python-cad-tools 0.1.17__tar.gz → 0.1.18__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/CHANGELOG.md +24 -0
  2. {python_cad_tools-0.1.17/src/python_cad_tools.egg-info → python_cad_tools-0.1.18}/PKG-INFO +1 -1
  3. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/docs/public-api.md +9 -7
  4. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/docs/spikes/curved-geometry-ifc-bounds.md +18 -1
  5. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/artifacts.py +12 -8
  6. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/build.py +3 -2
  7. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/exporters/ifc.py +3 -3
  8. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/geometry.py +81 -15
  9. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/manifests.py +38 -3
  10. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/validation.py +16 -4
  11. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18/src/python_cad_tools.egg-info}/PKG-INFO +1 -1
  12. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools.egg-info/scm_file_list.json +2 -0
  13. python_cad_tools-0.1.18/src/python_cad_tools.egg-info/scm_version.json +8 -0
  14. python_cad_tools-0.1.17/src/python_cad_tools.egg-info/scm_version.json +0 -8
  15. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/.gitattributes +0 -0
  16. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/.githooks/pre-push +0 -0
  17. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/.github/workflows/ci.yml +0 -0
  18. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/.github/workflows/downstream-compatibility.yml +0 -0
  19. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/.github/workflows/release-candidate.yml +0 -0
  20. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/.github/workflows/release.yml +0 -0
  21. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/.gitignore +0 -0
  22. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/.opencode/commands/autocommit.md +0 -0
  23. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/.opencode/tools/specrepo-autocommit.js +0 -0
  24. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/.opencode/tools/specrepo-autocommit.py +0 -0
  25. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/AGENTS.md +0 -0
  26. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/CLAUDE.md +0 -0
  27. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/LICENSE +0 -0
  28. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/MANIFEST.in +0 -0
  29. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/README.md +0 -0
  30. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/docs/build-semantics.md +0 -0
  31. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/docs/drawing-extensions.md +0 -0
  32. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/docs/manifests.md +0 -0
  33. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/docs/neutral-model.md +0 -0
  34. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/docs/project-configuration.md +0 -0
  35. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/docs/spikes/format-feasibility.md +0 -0
  36. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/docs/toolchain.md +0 -0
  37. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/docs/two-tier-contract.md +0 -0
  38. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/docs/viewer-delivery.md +0 -0
  39. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/pyproject.toml +0 -0
  40. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/setup.cfg +0 -0
  41. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/__init__.py +0 -0
  42. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/__main__.py +0 -0
  43. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_locking.py +0 -0
  44. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_project_loader.py +0 -0
  45. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_project_state.py +0 -0
  46. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_scaffold.py +0 -0
  47. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_transaction.py +0 -0
  48. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/app.webmanifest +0 -0
  49. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/asset-manifest.json +0 -0
  50. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/animationGroup-CMgMiFPo.js +0 -0
  51. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/bakedVertexAnimation-BlpwZEna.js +0 -0
  52. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/basisTextureLoader-pxgVvwE7.js +0 -0
  53. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/brdfTextureTools-CL1Nnrm1.js +0 -0
  54. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/bumpFragment-BNBjNOnF.js +0 -0
  55. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/bumpVertex-BTawYPw8.js +0 -0
  56. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/bumpVertexDeclaration-B_fffQFY.js +0 -0
  57. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/clipPlaneFragment-D1ZAafqO.js +0 -0
  58. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/clipPlaneVertex-BC_wnvpq.js +0 -0
  59. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/cubemapToSphericalPolynomial-DeA9LJ87.js +0 -0
  60. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/dds-C5zm4fz8.js +0 -0
  61. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/dds.pure-B_7lJeYI.js +0 -0
  62. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/ddsTextureLoader-DsS5rL5j.js +0 -0
  63. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/default.fragment-BUHDudIn.js +0 -0
  64. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/default.vertex-B80Wq2Ab.js +0 -0
  65. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/defaultUboDeclaration-DuGcl3VN.js +0 -0
  66. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/dumpTools-1F1Rx0a0.js +0 -0
  67. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/envTextureLoader-DqI1ceC5.js +0 -0
  68. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/exrTextureLoader-DmNwF2B5.js +0 -0
  69. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphApplyForceBlock-DmvfeucL.js +0 -0
  70. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphApplyImpulseBlock-ChgIpPqh.js +0 -0
  71. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphArrayIndexBlock-_JwXSrlj.js +0 -0
  72. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphBezierCurveEasingBlock-B-T0qVRw.js +0 -0
  73. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphBinaryOperationBlock-TpZm5BfV.js +0 -0
  74. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphBranchBlock-CNwAs8cP.js +0 -0
  75. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphCachedOperationBlock-DaEYpjja.js +0 -0
  76. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphCancelDelayBlock-Dc4RKYSf.js +0 -0
  77. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphCodeExecutionBlock-DCxeE8wr.js +0 -0
  78. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphConditionalDataBlock-TckZ2SGl.js +0 -0
  79. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphConsoleLogBlock-B5e69wX5.js +0 -0
  80. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphConstantBlock-3ktOI1lC.js +0 -0
  81. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphContextBlock-XTBtETD_.js +0 -0
  82. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphCounterBlock-Pb64y9OD.js +0 -0
  83. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphDataSwitchBlock-DsG4QDCF.js +0 -0
  84. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphDebounceBlock-B_XjPDoZ.js +0 -0
  85. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphDebugBlock-CD6lTvpS.js +0 -0
  86. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphDoNBlock-0NvER8W0.js +0 -0
  87. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphEasingBlock-B6hapywt.js +0 -0
  88. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphFlipFlopBlock-D1uyBOpq.js +0 -0
  89. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphForLoopBlock-BYD81Gof.js +0 -0
  90. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphFunctionReferenceBlock-zABkEq-X.js +0 -0
  91. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphGLTFDataProvider-B0ofoVrD.js +0 -0
  92. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphGetAngularVelocityBlock-DPTUNnTb.js +0 -0
  93. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphGetAssetBlock-B8_99mSz.js +0 -0
  94. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphGetLinearVelocityBlock-DFYXgPrA.js +0 -0
  95. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphGetPhysicsMassPropertiesBlock-DA6AqPR7.js +0 -0
  96. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphGetPropertyBlock-Bp4o9QmW.js +0 -0
  97. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphGetSoundVolumeBlock-BAmdegmU.js +0 -0
  98. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphGetVariableBlock-Cd8Y6leR.js +0 -0
  99. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphIndexOfBlock-CGYxKZZY.js +0 -0
  100. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphInterpolationBlock-CwjeWPr7.js +0 -0
  101. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphIsKeyPressedBlock-rNAEqrST.js +0 -0
  102. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphIsSoundPlayingBlock-BTztAQ_0.js +0 -0
  103. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphJsonPointerParserBlock-DoLWKXUb.js +0 -0
  104. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphKeyDownEventBlock-DPIi37WK.js +0 -0
  105. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphKeyUpEventBlock-ZV1Tim4d.js +0 -0
  106. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphKeyboardEventBlock-ClUSFYDa.js +0 -0
  107. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphMathBlocks-BEiYY5fC.js +0 -0
  108. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphMathCombineExtractBlocks-CVfFjT4m.js +0 -0
  109. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphMatrixMathBlocks-B6l0dCDc.js +0 -0
  110. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphMeshPickEventBlock-kdpuo1mU.js +0 -0
  111. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphMultiGateBlock-n99NMOON.js +0 -0
  112. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphPauseAnimationBlock-DXKPP4nK.js +0 -0
  113. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphPauseSoundBlock-e2k4W8Bc.js +0 -0
  114. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphPhysicsCollisionEventBlock-ClgKwXfJ.js +0 -0
  115. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphPlayAnimationBlock-BZj6MNgX.js +0 -0
  116. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphPlaySoundBlock-BnzlrpjK.js +0 -0
  117. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphPointerDownEventBlock-CQiX1RZi.js +0 -0
  118. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphPointerMoveEventBlock-B_JzBfd9.js +0 -0
  119. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphPointerOutEventBlock-B-xKCRP6.js +0 -0
  120. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphPointerOverEventBlock-DPC5GKft.js +0 -0
  121. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphPointerUpEventBlock-hCR14WZY.js +0 -0
  122. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphReceiveCustomEventBlock-EOc5SmbK.js +0 -0
  123. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphSceneReadyEventBlock-BAOJ-S5R.js +0 -0
  124. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphSceneTickEventBlock-BJla1nDw.js +0 -0
  125. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphSendCustomEventBlock-CbJzCBSw.js +0 -0
  126. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphSequenceBlock-C0E5Vhwk.js +0 -0
  127. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphSetAngularVelocityBlock-Crx1sltA.js +0 -0
  128. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphSetDelayBlock-t-D4JM8I.js +0 -0
  129. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphSetLinearVelocityBlock-DKNa-lhN.js +0 -0
  130. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphSetPhysicsMotionTypeBlock-BIeMWJwi.js +0 -0
  131. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphSetPropertyBlock-CFERwfo5.js +0 -0
  132. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphSetSoundVolumeBlock-B8fGBso9.js +0 -0
  133. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphSetVariableBlock-D3NUXLsU.js +0 -0
  134. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphSoundEndedEventBlock-BsojQIIQ.js +0 -0
  135. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphStopAnimationBlock-Bhvi4mzM.js +0 -0
  136. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphStopSoundBlock-BL-vDlPP.js +0 -0
  137. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphSwitchBlock-q5uox2Ll.js +0 -0
  138. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphThrottleBlock-ZvIobaF-.js +0 -0
  139. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphTransformCoordinatesSystemBlock-DC7IZTrj.js +0 -0
  140. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphTypeToTypeBlocks-DsmLuQmd.js +0 -0
  141. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphUnaryOperationBlock-CeFOvM8N.js +0 -0
  142. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphVectorMathBlocks-O_o0PB3R.js +0 -0
  143. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphWaitAllBlock-CS8S_sto.js +0 -0
  144. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/flowGraphWhileLoopBlock-BogzY-TI.js +0 -0
  145. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/fogFragment-5iY5mDF9.js +0 -0
  146. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/gaussianSplatting-CzELCfrg.js +0 -0
  147. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/gaussianSplatting-Dm4qQcCl.js +0 -0
  148. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/gaussianSplatting.fragment-Dl5Knagi.js +0 -0
  149. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/gaussianSplatting.fragment-gV6NfBr2.js +0 -0
  150. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/gaussianSplatting.vertex-BU7N8GWm.js +0 -0
  151. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/gaussianSplatting.vertex-BYh0LKwp.js +0 -0
  152. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/gaussianSplattingVoxel.fragment-DGCqlu5B.js +0 -0
  153. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/gaussianSplattingVoxel.fragment-DPRpU6I6.js +0 -0
  154. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/gaussianSplattingVoxel.vertex-D6831_Db.js +0 -0
  155. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/gaussianSplattingVoxel.vertex-DVVqYQkj.js +0 -0
  156. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/geometry.fragment-CJ5P29rW.js +0 -0
  157. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/geometry.fragment-eMPAGW-C.js +0 -0
  158. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/geometry.vertex-Cfmd-eH1.js +0 -0
  159. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/geometry.vertex-D8aj2PHd.js +0 -0
  160. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/glowBlurPostProcess.fragment-BNUj6Ok4.js +0 -0
  161. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/glowMapGeneration.fragment-BMfCbDSO.js +0 -0
  162. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/glowMapGeneration.vertex-HFoIGtvW.js +0 -0
  163. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/glowMapMerge.fragment-DcbXX8O5.js +0 -0
  164. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/glowMapMerge.vertex-C6kfp5Df.js +0 -0
  165. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/harmonicsFunctions-B_Rux4W4.js +0 -0
  166. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/hdrTextureLoader-BGQvgh0h.js +0 -0
  167. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/helperFunctions-DfEEUVrK.js +0 -0
  168. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/iesTextureLoader-DRIv8q-c.js +0 -0
  169. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/index-Bi_hQlRi.css +0 -0
  170. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/index-QUv88KPb.js +0 -0
  171. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/kernelBlur.fragment-AIxPvzg7.js +0 -0
  172. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/kernelBlur.fragment-B-fzpNXl.js +0 -0
  173. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/kernelBlur.vertex-BeCpz_iB.js +0 -0
  174. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/kernelBlur.vertex-LF_ZsFNn.js +0 -0
  175. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/kernelBlurVaryingDeclaration-0MaBFn29.js +0 -0
  176. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/kernelBlurVaryingDeclaration-CKf1P4fb.js +0 -0
  177. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/ktxTextureLoader-tKPsUacI.js +0 -0
  178. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/lightFragment-k2nRX45x.js +0 -0
  179. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/logDepthDeclaration-BGXXcOUJ.js +0 -0
  180. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/logDepthVertex-BC72KyzN.js +0 -0
  181. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/mainUVVaryingDeclaration-BCMZuxRE.js +0 -0
  182. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/mesh.vertexData.functions-ayNlGC5K.js +0 -0
  183. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/meshUboDeclaration-Cxk75_zo.js +0 -0
  184. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/oitFragment-BtwWIihL.js +0 -0
  185. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/openpbr.fragment-8rnUKL54.js +0 -0
  186. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/openpbr.fragment-C6yWZfQI.js +0 -0
  187. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/openpbr.vertex-DGh-kC4B.js +0 -0
  188. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/openpbr.vertex-DbnVIs8Y.js +0 -0
  189. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/openpbrMaterial.pure-CFHhabYh.js +0 -0
  190. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/openpbrMaterialLoadingAdapter-BQGAj-f6.js +0 -0
  191. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/openpbrTransmissionLayerData-BMK7l2Ba.js +0 -0
  192. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/openpbrTransmissionLayerData-DNsGu5u0.js +0 -0
  193. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/openpbrUboDeclaration-Cd1JAAAL.js +0 -0
  194. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/openpbrUboDeclaration-rBzbf9n3.js +0 -0
  195. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/packingFunctions-Dji1Rr-b.js +0 -0
  196. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/packingFunctions-_goA4OEm.js +0 -0
  197. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/pass.fragment-CgGToehX.js +0 -0
  198. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/passCube.fragment-CqqQ20XF.js +0 -0
  199. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/passCube.fragment-D93dlgjG.js +0 -0
  200. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/pbr.fragment-YSvcDJjz.js +0 -0
  201. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/pbr.vertex-BOhSAeHX.js +0 -0
  202. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/pbrBRDFFunctions-DTEwurLZ.js +0 -0
  203. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/pbrDebug-B4IzJZHZ.js +0 -0
  204. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/pbrIBLFunctions-gJ0KErMb.js +0 -0
  205. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/pbrMaterial.pure-BRIxFz02.js +0 -0
  206. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/pbrMaterialLoadingAdapter--NRrZRBU.js +0 -0
  207. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/pbrUboDeclaration-6dxbcrtA.js +0 -0
  208. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/postprocess.vertex-ZxthID8t.js +0 -0
  209. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/procedural.vertex-BSVnGss-.js +0 -0
  210. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/procedural.vertex-DzIg_d-k.js +0 -0
  211. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/reflectionFunction-CgKY-0t6.js +0 -0
  212. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/rgbdDecode.fragment-CAfeJDpQ.js +0 -0
  213. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/rgbdEncode.fragment-D7xEO0B-.js +0 -0
  214. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/rgbdEncode.fragment-DIzz0LC4.js +0 -0
  215. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/sceneUboDeclaration-kGrRDglr.js +0 -0
  216. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/textureProcessor.fragment-CgQTw0M7.js +0 -0
  217. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/textureProcessor.fragment-DnEEx4Gs.js +0 -0
  218. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/tgaTextureLoader-CwaQqeYn.js +0 -0
  219. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/assets/vertexColorMixing-C2cK_vck.js +0 -0
  220. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/favicon.svg +0 -0
  221. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/index.html +0 -0
  222. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/_viewer_static/sw.js +0 -0
  223. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/build-info.json +0 -0
  224. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/cli.py +0 -0
  225. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/context.py +0 -0
  226. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/determinism.py +0 -0
  227. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/drawings/__init__.py +0 -0
  228. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/drawings/_catalog.py +0 -0
  229. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/drawings/_validation.py +0 -0
  230. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/elements.py +0 -0
  231. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/exceptions.py +0 -0
  232. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/exporters/__init__.py +0 -0
  233. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/exporters/base.py +0 -0
  234. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/exporters/drawings/__init__.py +0 -0
  235. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/exporters/drawings/annotations.py +0 -0
  236. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/exporters/drawings/dxf.py +0 -0
  237. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/exporters/drawings/model.py +0 -0
  238. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/exporters/drawings/pdf.py +0 -0
  239. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/exporters/drawings/primitives.py +0 -0
  240. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/exporters/drawings/projection.py +0 -0
  241. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/exporters/drawings/svg.py +0 -0
  242. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/exporters/glb.py +0 -0
  243. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/exporters/quantities.py +0 -0
  244. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/exporters/step.py +0 -0
  245. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/project_config.py +0 -0
  246. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/py.typed +0 -0
  247. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/recipes.py +0 -0
  248. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/runtime_sync.py +0 -0
  249. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/schemas/build-manifest-1.json +0 -0
  250. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/schemas/design-manifest-1.json +0 -0
  251. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/schemas/download-manifest-2.json +0 -0
  252. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/schemas/drawing-annotations-1.json +0 -0
  253. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/schemas/glb-manifest-1.json +0 -0
  254. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/schemas/ifc-validation-1.json +0 -0
  255. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/schemas/quantities-1.json +0 -0
  256. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/schemas/run-metadata-1.json +0 -0
  257. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/schemas/site-metadata-1.json +0 -0
  258. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/schemas/step-validation-1.json +0 -0
  259. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/site.py +0 -0
  260. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/templates/project/.gitignore +0 -0
  261. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/templates/project/README.md +0 -0
  262. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/templates/project/config.py +0 -0
  263. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/templates/project/drawing_annotations.py +0 -0
  264. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/templates/project/generated/.gitkeep +0 -0
  265. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/templates/project/model.py +0 -0
  266. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/templates/project/pyproject.toml +0 -0
  267. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools/units.py +0 -0
  268. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools.egg-info/SOURCES.txt +0 -0
  269. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools.egg-info/dependency_links.txt +0 -0
  270. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools.egg-info/entry_points.txt +0 -0
  271. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools.egg-info/requires.txt +0 -0
  272. {python_cad_tools-0.1.17 → python_cad_tools-0.1.18}/src/python_cad_tools.egg-info/top_level.txt +0 -0
@@ -7,6 +7,30 @@ stable release is published.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.18] - 2026-10-08
11
+
12
+ ### Fixed
13
+
14
+ - Curved B-rep solids (spheres, `cylinder_between` cylinders and other curved
15
+ shapes) no longer fail `build` and `verify` with `artifact_integrity:
16
+ IFC/design bounds mismatch`. The design manifest records each element's
17
+ `body_bounds_mm` (the bounds of the body IFC actually writes: the mesh of a
18
+ tessellated solid, the exact shape for recipes), and the IFC check compares
19
+ against it at the same `1e-6` tolerance. Manifests without the field fall
20
+ back to `bounds_mm`. `body_bounds_mm` describes the export, not the design,
21
+ so it is left out of `semantic_hash`: design hashes are unchanged, and
22
+ `validate` does not tessellate.
23
+ - `validate` reports a build123d `Part` (or compound) that holds one solid as
24
+ `solid-wrapped` with the fix (`.solid()`), instead of a bare
25
+ `solid-invalid`.
26
+
27
+ ### Changed
28
+
29
+ - A build tessellates each element once and shares the mesh between the
30
+ design manifest, IFC and GLB (`geometry.shared_tessellation`), instead of
31
+ IFC and GLB each meshing it. `benge-property-cad`'s full build went from
32
+ 106 s to 67 s on the owner's Mac.
33
+
10
34
  ## [0.1.16] - 2026-09-22
11
35
 
12
36
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-cad-tools
3
- Version: 0.1.17
3
+ Version: 0.1.18
4
4
  Summary: Typed, headless Python tooling for CAD/BIM models and artifacts
5
5
  Author: Brandon Benge
6
6
  License: AGPL-3.0-only
@@ -16,13 +16,15 @@ of optional runtime imports.
16
16
  `cubic_yards`, `metres`, and `square_metres`.
17
17
  - `python_cad_tools.geometry`: `box`, `reference_line`, `cylinder_between`,
18
18
  `prism_between`, `sloped_pool`, `bounds`, `combined_bounds`, `tessellate`,
19
- `is_valid_solid`, and `diagonal`. **Curved geometry that must pass the
20
- build pipeline's `artifact_integrity` IFC/design-bounds check should not
21
- use `cylinder_between`** -- its tessellated B-rep output cannot exactly
22
- match analytic design-side bounds. Use `python_cad_tools.recipes`'
23
- `CircleProfile` + `ExtrudedProfile` instead; see
24
- [`cylinder_between`'s docstring](../src/python_cad_tools/geometry.py) and
25
- `docs/spikes/curved-geometry-ifc-bounds.md` for the confirmed mechanism.
19
+ `is_valid_solid`, and `diagonal`. Curved B-rep solids (`cylinder_between`,
20
+ build123d spheres and other curved shapes) build and verify: IFC writes
21
+ them as triangle meshes, and the IFC bounds check compares that mesh with
22
+ the same tessellation recorded in the design manifest as `body_bounds_mm`
23
+ (0.1.18). Use `python_cad_tools.recipes`' `CircleProfile` +
24
+ `ExtrudedProfile` where an analytic IFC body is wanted instead of a mesh.
25
+ Pass a `Solid`: build123d algebra-mode shapes such as `Sphere(...)` are a
26
+ `Part` wrapping one solid, which `validate` reports as `solid-wrapped`
27
+ (use `.solid()`).
26
28
  - `python_cad_tools.drawings`: `Point2`, `Direction`, `DrawingStyleToken`,
27
29
  `DrawingSheet`, `Line`, `Polyline`, `Text`, `Dimension`, `SectionCallout`,
28
30
  `ElevationMarker`, `TableRow`, `Table`, `SheetAnnotations`, and
@@ -1,4 +1,21 @@
1
- # Curved geometry vs. the IFC/design bounds integrity check (mechanism confirmed empirically)
1
+ # Curved geometry vs. the IFC/design bounds integrity check
2
+
3
+ **Resolved in 0.1.18 (2026-10-08).** The mechanism is confirmed in source:
4
+ elements without a recipe are written to IFC as an `IfcTriangulatedFaceSet`
5
+ from `tessellate(geometry, 0.5, 0.25)`, the IFC report's bounds are read back
6
+ from those mesh coordinates, and the manifest recorded only the exact B-rep
7
+ bounds; `_reconcile_formats` compared the two at `1e-6`. The fix is candidate
8
+ direction 1 below ("compare like with like"): the design manifest now records
9
+ `body_bounds_mm`, the bounds of the same tessellation
10
+ (`geometry.IFC_TESSELLATION`, shared with the exporter), and the IFC check
11
+ compares against it, still exactly. A live hosted run on 2026-10-08 ("change
12
+ the cube to a sphere", `Sphere(152.4).solid()`) failed with x/y short by
13
+ 0.58 mm before the fix; `tests/integration/test_curved_geometry.py` covers a
14
+ sphere, a `cylinder_between` cylinder, a box, and a tampered IFC report.
15
+ Approval: `specrepo/approved/2026-10-08-curved-geometry-ifc-bounds/` (workspace).
16
+
17
+ The history below is kept as it was written.
18
+
2
19
 
3
20
  Status: **mechanism confirmed, fix known — not yet verified against this
4
21
  repo's own source.** A later sandbox-agent attempt on the same project (see
@@ -14,7 +14,7 @@ from xml.etree import ElementTree
14
14
 
15
15
  from .determinism import semantic_hash, sha256_file
16
16
  from .exceptions import ArtifactIntegrityError, BuildExportError
17
- from .manifests import load_json_document, validate_json_document
17
+ from .manifests import design_semantic_projection, load_json_document, validate_json_document
18
18
  from .validation import ValidationIssue
19
19
 
20
20
  BUILD_MANIFEST_RELATIVE = "manifests/build-manifest.json"
@@ -153,9 +153,8 @@ def verify_output_root(output_root: Path, project_root: Path | None = None) -> V
153
153
  _verify_sources(project_root, document)
154
154
  selected = _selected_formats(document)
155
155
  design = load_design_manifest(output_root / "manifests/design-manifest.json")
156
- design_without_hash = dict(design)
157
- recorded_design_hash = design_without_hash.pop("semantic_hash", None)
158
- if recorded_design_hash != semantic_hash(design_without_hash):
156
+ recorded_design_hash = design.get("semantic_hash")
157
+ if recorded_design_hash != semantic_hash(design_semantic_projection(design)):
159
158
  raise ArtifactIntegrityError("Design manifest semantic_hash does not reproduce")
160
159
  if document["design_semantic_hash"] != design["semantic_hash"]:
161
160
  raise ArtifactIntegrityError("Build and design manifest semantic hashes disagree")
@@ -386,7 +385,11 @@ def _reconcile_formats(root: Path, formats: tuple[str, ...]) -> None:
386
385
  for item in elements
387
386
  if item["physical"] and "ifc" in item["export_formats"] and item["ifc_class"] is not None
388
387
  ]
389
- _require_matching_bounds("IFC", report["bounds_mm"], _combined_manifest_bounds(expected_elements))
388
+ # Tessellated IFC bodies are meshes; compare them with the same mesh, recorded in the
389
+ # manifest as body_bounds_mm (0.1.18). Older manifests fall back to the exact bounds.
390
+ _require_matching_bounds(
391
+ "IFC", report["bounds_mm"], _combined_manifest_bounds(expected_elements, key="body_bounds_mm")
392
+ )
390
393
  _reconcile_ifc_extensions(root, formats, report, expected_elements)
391
394
  if "glb" in formats:
392
395
  from .exporters.glb import glb_json
@@ -536,14 +539,15 @@ def _normalized_text(value: str) -> str:
536
539
  return " ".join(value.split())
537
540
 
538
541
 
539
- def _combined_manifest_bounds(elements: list[dict[str, Any]]) -> list[float]:
542
+ def _combined_manifest_bounds(elements: list[dict[str, Any]], key: str = "bounds_mm") -> list[float]:
540
543
  if not elements:
541
544
  return [0.0] * 6
545
+ values = [item.get(key) or item["bounds_mm"] for item in elements]
542
546
  return [
543
- round(min(float(item["bounds_mm"][index]) for item in elements), 6)
547
+ round(min(float(value[index]) for value in values), 6)
544
548
  for index in range(3)
545
549
  ] + [
546
- round(max(float(item["bounds_mm"][index]) for item in elements), 6)
550
+ round(max(float(value[index]) for value in values), 6)
547
551
  for index in range(3, 6)
548
552
  ]
549
553
 
@@ -22,6 +22,7 @@ from .determinism import semantic_hash, sha256_file, write_json
22
22
  from .exceptions import BuildExportError, ModelValidationError, ProjectConfigError, UnsafePathError
23
23
  from .exporters import GlbExporter, IfcExporter, QuantityExporter, StepExporter
24
24
  from .exporters.drawings import DrawingExporter
25
+ from .geometry import shared_tessellation
25
26
  from .manifests import build_manifest_document, design_manifest, write_preinventory_manifests
26
27
  from .project_config import FORMAT_ORDER, load_project_config, output_path, resolve_project_root
27
28
  from .validation import ValidationIssue, fatal_issues
@@ -82,7 +83,7 @@ def build_project(options: BuildOptions) -> BuildResult:
82
83
  raise TypeError("build_project expects BuildOptions")
83
84
  requested = _validate_requested_formats(options.formats)
84
85
  root = resolve_project_root(options.project_root)
85
- with project_execution():
86
+ with project_execution(), shared_tessellation():
86
87
  with project_lock(root):
87
88
  config = load_project_config(root)
88
89
  formats = config.default_formats if requested is None else requested
@@ -173,7 +174,7 @@ def validate_project(options: ValidationOptions) -> ValidationReport:
173
174
  formats=formats,
174
175
  issues=loaded.issues,
175
176
  source_set_hash=source_set_hash,
176
- design_semantic_hash=design_manifest(loaded.model)["semantic_hash"],
177
+ design_semantic_hash=design_manifest(loaded.model, include_body_bounds=False)["semantic_hash"],
177
178
  annotation_provider_id=(
178
179
  None if loaded.annotations is None else loaded.annotations.provider_id
179
180
  ),
@@ -17,7 +17,7 @@ from ifcopenshell import ifcopenshell_wrapper
17
17
  from ..determinism import normalized_build_epoch, write_json
18
18
  from ..elements import DesignElement, Opening
19
19
  from ..exceptions import BuildExportError, ModelValidationError
20
- from ..geometry import bounds, tessellate
20
+ from ..geometry import IFC_TESSELLATION, bounds, tessellate
21
21
  from ..recipes import (
22
22
  CircleProfile,
23
23
  DifferenceRecipe,
@@ -326,7 +326,7 @@ def _build_representation(file, context, element: DesignElement) -> tuple[Any, s
326
326
  BREP instead.
327
327
  """
328
328
  if element.recipe is None:
329
- vertices, triangles = tessellate(element.geometry, 0.5, 0.25)
329
+ vertices, triangles = tessellate(element.geometry, *IFC_TESSELLATION)
330
330
  points = file.createIfcCartesianPointList3D(vertices)
331
331
  face_set = file.createIfcTriangulatedFaceSet(
332
332
  points,
@@ -482,7 +482,7 @@ def _add_opening(file, context, host_product, storey, element, opening: Opening)
482
482
  The host element's ``geometry`` stays authoritative for every other
483
483
  format; the framework does not boolean-subtract the opening.
484
484
  """
485
- vertices, triangles = tessellate(opening.geometry, 0.5, 0.25)
485
+ vertices, triangles = tessellate(opening.geometry, *IFC_TESSELLATION)
486
486
  points = file.createIfcCartesianPointList3D(vertices)
487
487
  face_set = file.createIfcTriangulatedFaceSet(
488
488
  points, None, True, [[index + 1 for index in triangle] for triangle in triangles], None
@@ -2,6 +2,9 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from collections.abc import Iterator
6
+ from contextlib import contextmanager
7
+ from contextvars import ContextVar
5
8
  from math import isfinite, sqrt
6
9
  from typing import Any
7
10
 
@@ -51,20 +54,14 @@ def cylinder_between(
51
54
  end: tuple[Length, Length, Length],
52
55
  radius: Length,
53
56
  ) -> Solid:
54
- """Build a tessellated B-rep cylinder between two points.
55
-
56
- The returned solid is a polygonal approximation of a true cylinder
57
- (~51-gon), not exact analytic geometry. Its IFC export bounds will not
58
- exactly match the analytic design-side bounds computed from ``start``/
59
- ``end``/``radius`` -- this reliably fails the build pipeline's
60
- ``artifact_integrity`` IFC/design-bounds check (see
61
- ``docs/spikes/curved-geometry-ifc-bounds.md``). For curved geometry that
62
- must survive that check, author it with ``python_cad_tools.recipes``
63
- (``CircleProfile`` + ``ExtrudedProfile``) instead, which exports exact
64
- analytic IFC geometry and does not have this problem. Prefer
65
- ``cylinder_between`` only where an approximate B-rep solid is acceptable
66
- (e.g. it is never exported to IFC, or exact bounds agreement is not
67
- checked).
57
+ """Build a B-rep cylinder between two points.
58
+
59
+ IFC exports it as a triangle mesh; the build's IFC bounds check compares
60
+ that mesh with the same tessellation recorded in the design manifest
61
+ (``body_bounds_mm``), so it builds and verifies like any other solid
62
+ (0.1.18; see ``docs/spikes/curved-geometry-ifc-bounds.md``). Where an
63
+ analytic IFC body is wanted instead of a mesh, author it with
64
+ ``python_cad_tools.recipes`` (``CircleProfile`` + ``ExtrudedProfile``).
68
65
  """
69
66
  _positive_lengths((radius,), "Cylinder radius")
70
67
  start_mm = _point(start, "Cylinder start")
@@ -152,11 +149,80 @@ def combined_bounds(shapes: list[Any]) -> tuple[float, float, float, float, floa
152
149
  )
153
150
 
154
151
 
152
+ #: The (linear, angular) deflection of every tessellated IFC body. The design manifest's
153
+ #: ``body_bounds_mm`` uses the same values, so the IFC bounds check compares the exported
154
+ #: mesh with that same mesh (2026-10-08 approval, curved-geometry-ifc-bounds).
155
+ IFC_TESSELLATION: tuple[float, float] = (0.5, 0.25)
156
+
157
+
158
+ #: One build's tessellations, keyed by shape identity and deflections. The design manifest
159
+ #: (``body_bounds_mm``), IFC and GLB all mesh each element with the same deflections, so a
160
+ #: build meshes each shape once instead of up to three times. The value keeps the shape alive,
161
+ #: so its ``id`` cannot be reused while the cache exists. Results are shared: callers must not
162
+ #: mutate them.
163
+ _TESSELLATIONS: ContextVar[dict[tuple[int, float, float], tuple[Any, Any]] | None] = ContextVar(
164
+ "python_cad_tools_tessellations", default=None
165
+ )
166
+
167
+
168
+ @contextmanager
169
+ def shared_tessellation() -> Iterator[None]:
170
+ """Reuse each shape's tessellation for the duration of one build."""
171
+ token = _TESSELLATIONS.set({})
172
+ try:
173
+ yield
174
+ finally:
175
+ _TESSELLATIONS.reset(token)
176
+
177
+
155
178
  def tessellate(shape: Any, linear_deflection: float, angular_deflection: float) -> tuple[list[list[float]], list[list[int]]]:
179
+ cache = _TESSELLATIONS.get()
180
+ key = (id(shape), float(linear_deflection), float(angular_deflection))
181
+ if cache is not None:
182
+ hit = cache.get(key)
183
+ if hit is not None and hit[0] is shape:
184
+ result: tuple[list[list[float]], list[list[int]]] = hit[1]
185
+ return result
156
186
  vertices, triangles = shape.tessellate(linear_deflection, angular_deflection)
157
187
  points = [[float(vertex.X), float(vertex.Y), float(vertex.Z)] for vertex in vertices]
158
188
  faces = [[int(index) for index in triangle] for triangle in triangles]
159
- return points, faces
189
+ computed = (points, faces)
190
+ if cache is not None:
191
+ cache[key] = (shape, computed)
192
+ return computed
193
+
194
+
195
+ def mesh_bounds(
196
+ shape: Any, linear_deflection: float, angular_deflection: float
197
+ ) -> tuple[float, float, float, float, float, float]:
198
+ """Bounds of ``shape``'s tessellation: what a mesh export of it actually contains.
199
+
200
+ A curved surface's mesh lies inside the true surface, so these can be tighter than
201
+ :func:`bounds`; for a flat-faced solid they are equal.
202
+ """
203
+ vertices, _ = tessellate(shape, linear_deflection, angular_deflection)
204
+ if not vertices:
205
+ return bounds(shape)
206
+ return (
207
+ min(vertex[0] for vertex in vertices),
208
+ min(vertex[1] for vertex in vertices),
209
+ min(vertex[2] for vertex in vertices),
210
+ max(vertex[0] for vertex in vertices),
211
+ max(vertex[1] for vertex in vertices),
212
+ max(vertex[2] for vertex in vertices),
213
+ )
214
+
215
+
216
+ def single_wrapped_solid(shape: Any) -> Solid | None:
217
+ """The one valid solid inside a compound (such as build123d's algebra-mode ``Part``), or
218
+ ``None`` when ``shape`` is not such a wrapper."""
219
+ if isinstance(shape, Solid) or not hasattr(shape, "solids"):
220
+ return None
221
+ try:
222
+ solids = list(shape.solids())
223
+ except Exception: # noqa: BLE001 -- any shape that cannot list solids is not a wrapper
224
+ return None
225
+ return solids[0] if len(solids) == 1 and is_valid_solid(solids[0]) else None
160
226
 
161
227
 
162
228
  def is_valid_solid(shape: Any) -> bool:
@@ -13,7 +13,7 @@ from typing import Any
13
13
  from . import __version__
14
14
  from .determinism import normalized_epoch_iso, semantic_hash, write_json
15
15
  from .exceptions import ArtifactIntegrityError
16
- from .geometry import bounds
16
+ from .geometry import IFC_TESSELLATION, bounds, mesh_bounds
17
17
  from .recipes import DifferenceRecipe, ExtrudedProfile
18
18
 
19
19
  SCHEMAS: dict[str, tuple[int, frozenset[str]]] = {
@@ -68,7 +68,37 @@ _SNAKE_CASE = re.compile(r"[a-z][a-z0-9]*(?:_[a-z0-9]+)*\Z")
68
68
  _DRAWING_ID = re.compile(r"[A-Za-z0-9._-]+\Z", re.ASCII)
69
69
 
70
70
 
71
- def design_manifest(model: Any) -> dict[str, Any]:
71
+ def _body_bounds(element: Any) -> tuple[float, float, float, float, float, float]:
72
+ """Bounds of the body the IFC exporter writes: the mesh of a tessellated solid, the exact
73
+ shape otherwise (recipes are exported analytically; non-solids are not meshed)."""
74
+ if element.recipe is None and element.geometry_kind == "solid":
75
+ return mesh_bounds(element.geometry, *IFC_TESSELLATION)
76
+ return bounds(element.geometry)
77
+
78
+
79
+ #: Per-element manifest keys that describe an export (not the design) and so are left out of
80
+ #: ``semantic_hash``: a project's design hash is the same with or without them, and
81
+ #: ``validate`` (which reports the hash) need not tessellate every element.
82
+ _EXPORT_ONLY_ELEMENT_KEYS = frozenset({"body_bounds_mm"})
83
+
84
+
85
+ def design_semantic_projection(manifest: dict[str, Any]) -> dict[str, Any]:
86
+ """The design manifest as hashed: without ``semantic_hash`` and export-only element keys."""
87
+ projected = {key: value for key, value in manifest.items() if key != "semantic_hash"}
88
+ elements = projected.get("elements")
89
+ if isinstance(elements, list):
90
+ projected["elements"] = [
91
+ {key: value for key, value in item.items() if key not in _EXPORT_ONLY_ELEMENT_KEYS}
92
+ if isinstance(item, dict)
93
+ else item
94
+ for item in elements
95
+ ]
96
+ return projected
97
+
98
+
99
+ def design_manifest(model: Any, *, include_body_bounds: bool = True) -> dict[str, Any]:
100
+ """The design manifest. ``include_body_bounds=False`` skips ``body_bounds_mm`` (and its
101
+ tessellation) when only the semantic hash is needed; the hash is the same either way."""
72
102
  elements = []
73
103
  for element in model.walk():
74
104
  elements.append(
@@ -79,6 +109,11 @@ def design_manifest(model: Any) -> dict[str, Any]:
79
109
  "geometry_kind": element.geometry_kind,
80
110
  "physical": element.physical,
81
111
  "bounds_mm": [round(value, 6) for value in bounds(element.geometry)],
112
+ **(
113
+ {"body_bounds_mm": [round(value, 6) for value in _body_bounds(element)]}
114
+ if include_body_bounds
115
+ else {}
116
+ ),
82
117
  "placement": {
83
118
  "translation_mm": element.placement.translation_mm,
84
119
  "rotation_deg_xyz": element.placement.rotation_deg_xyz,
@@ -119,7 +154,7 @@ def design_manifest(model: Any) -> dict[str, Any]:
119
154
  "glb": {"site_path": f"artifacts/glb/{model.artifact_stem}.glb"},
120
155
  "elements": elements,
121
156
  }
122
- value["semantic_hash"] = semantic_hash(value)
157
+ value["semantic_hash"] = semantic_hash(design_semantic_projection(value))
123
158
  return value
124
159
 
125
160
 
@@ -15,7 +15,7 @@ from build123d import (
15
15
 
16
16
  from .elements import DesignElement, DesignModel, MaterialSpec, Opening, Placement
17
17
  from .exceptions import ModelValidationError
18
- from .geometry import bounds, is_valid_solid
18
+ from .geometry import bounds, is_valid_solid, single_wrapped_solid
19
19
  from .recipes import (
20
20
  ROTATION_X_RANGE,
21
21
  ROTATION_Y_RANGE,
@@ -108,9 +108,21 @@ def _validate_element(
108
108
  if element.physical and element.geometry_kind != "solid":
109
109
  issues.append(ValidationIssue("physical-kind", "error", "Physical elements must be solids", element_id=element.id))
110
110
  if element.physical and element.geometry is not None and not is_valid_solid(element.geometry):
111
- issues.append(
112
- ValidationIssue("solid-invalid", "error", "Physical geometry must be a valid positive-volume solid", element_id=element.id)
113
- )
111
+ if single_wrapped_solid(element.geometry) is not None:
112
+ issues.append(
113
+ ValidationIssue(
114
+ "solid-wrapped",
115
+ "error",
116
+ "Geometry is a Part/Compound containing one solid; pass `.solid()` (the single Solid) instead",
117
+ element_id=element.id,
118
+ )
119
+ )
120
+ else:
121
+ issues.append(
122
+ ValidationIssue(
123
+ "solid-invalid", "error", "Physical geometry must be a valid positive-volume solid", element_id=element.id
124
+ )
125
+ )
114
126
  if not element.physical and element.geometry_kind == "solid":
115
127
  issues.append(
116
128
  ValidationIssue(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-cad-tools
3
- Version: 0.1.17
3
+ Version: 0.1.18
4
4
  Summary: Typed, headless Python tooling for CAD/BIM models and artifacts
5
5
  Author: Brandon Benge
6
6
  License: AGPL-3.0-only
@@ -263,6 +263,7 @@
263
263
  "src/python_cad_tools/validation.py",
264
264
  "tests/conftest.py",
265
265
  "tests/integration/test_build_transactions.py",
266
+ "tests/integration/test_curved_geometry.py",
266
267
  "tests/integration/test_drawing_extensions.py",
267
268
  "tests/integration/test_exports.py",
268
269
  "tests/integration/test_project_loading.py",
@@ -271,6 +272,7 @@
271
272
  "tests/packaging/test_toolchain.py",
272
273
  "tests/typing/public_project.py",
273
274
  "tests/typing/test_external_typing.py",
275
+ "tests/unit/test_curved_geometry_units.py",
274
276
  "tests/unit/test_drawings.py",
275
277
  "tests/unit/test_manifests.py",
276
278
  "tests/unit/test_model_contract.py",
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "0.1.18",
3
+ "distance": 0,
4
+ "node": "ge909d6f9394ae6f2a5ee80d99f5a8cac772d09de",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-10-09"
8
+ }
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "0.1.17",
3
- "distance": 0,
4
- "node": "g96a733187ecf50a799b65f04f064a2f9a7fd3867",
5
- "dirty": false,
6
- "branch": "HEAD",
7
- "node_date": "2026-10-07"
8
- }