python-cad-tools 0.1.0rc1__py3-none-any.whl

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 (236) hide show
  1. python_cad_tools/__init__.py +10 -0
  2. python_cad_tools/_locking.py +63 -0
  3. python_cad_tools/_project_loader.py +273 -0
  4. python_cad_tools/_project_state.py +65 -0
  5. python_cad_tools/_scaffold.py +66 -0
  6. python_cad_tools/_transaction.py +208 -0
  7. python_cad_tools/_viewer_static/app.webmanifest +18 -0
  8. python_cad_tools/_viewer_static/asset-manifest.json +1 -0
  9. python_cad_tools/_viewer_static/assets/animationGroup-Sd34ntWg.js +1 -0
  10. python_cad_tools/_viewer_static/assets/bakedVertexAnimation-Cd_pRdrn.js +316 -0
  11. python_cad_tools/_viewer_static/assets/basisTextureLoader-DWgvS_1G.js +1 -0
  12. python_cad_tools/_viewer_static/assets/brdfTextureTools-CWGiG1W2.js +2 -0
  13. python_cad_tools/_viewer_static/assets/bumpFragment-CWULheF1.js +127 -0
  14. python_cad_tools/_viewer_static/assets/bumpVertex-DqzFZayE.js +6 -0
  15. python_cad_tools/_viewer_static/assets/bumpVertexDeclaration-C5EFyTio.js +6 -0
  16. python_cad_tools/_viewer_static/assets/clipPlaneFragment-BXRtt4jj.js +46 -0
  17. python_cad_tools/_viewer_static/assets/clipPlaneVertex-B_VhD1oQ.js +37 -0
  18. python_cad_tools/_viewer_static/assets/cubemapToSphericalPolynomial-DRpaukza.js +1 -0
  19. python_cad_tools/_viewer_static/assets/dds-Dkb2Cx2N.js +1 -0
  20. python_cad_tools/_viewer_static/assets/dds.pure-DtvJsKvY.js +1 -0
  21. python_cad_tools/_viewer_static/assets/ddsTextureLoader-H78xux1g.js +1 -0
  22. python_cad_tools/_viewer_static/assets/default.fragment-oSQ-tWhe.js +469 -0
  23. python_cad_tools/_viewer_static/assets/default.vertex-DhFjw_Da.js +209 -0
  24. python_cad_tools/_viewer_static/assets/defaultUboDeclaration-BMDHNppx.js +5 -0
  25. python_cad_tools/_viewer_static/assets/dumpTools-BaNZPjUX.js +2 -0
  26. python_cad_tools/_viewer_static/assets/envTextureLoader-V0YnLcIM.js +1 -0
  27. python_cad_tools/_viewer_static/assets/exrTextureLoader-CjGyIK3x.js +1 -0
  28. python_cad_tools/_viewer_static/assets/flowGraphApplyForceBlock-CW5nuobR.js +1 -0
  29. python_cad_tools/_viewer_static/assets/flowGraphApplyImpulseBlock-LFqYIkhj.js +1 -0
  30. python_cad_tools/_viewer_static/assets/flowGraphArrayIndexBlock-BMOHUbBm.js +1 -0
  31. python_cad_tools/_viewer_static/assets/flowGraphBezierCurveEasingBlock-knmTiuEk.js +1 -0
  32. python_cad_tools/_viewer_static/assets/flowGraphBinaryOperationBlock-DFLuCAKJ.js +1 -0
  33. python_cad_tools/_viewer_static/assets/flowGraphBranchBlock-BJRHMXMo.js +1 -0
  34. python_cad_tools/_viewer_static/assets/flowGraphCachedOperationBlock-Dng-6jE0.js +1 -0
  35. python_cad_tools/_viewer_static/assets/flowGraphCancelDelayBlock-CgbK3CQZ.js +1 -0
  36. python_cad_tools/_viewer_static/assets/flowGraphCodeExecutionBlock-8zMtq9tg.js +1 -0
  37. python_cad_tools/_viewer_static/assets/flowGraphConditionalDataBlock-BiLr2rkI.js +1 -0
  38. python_cad_tools/_viewer_static/assets/flowGraphConsoleLogBlock-J1vcCtNP.js +1 -0
  39. python_cad_tools/_viewer_static/assets/flowGraphConstantBlock-CFb5nuaV.js +1 -0
  40. python_cad_tools/_viewer_static/assets/flowGraphContextBlock-DAeGVzhC.js +1 -0
  41. python_cad_tools/_viewer_static/assets/flowGraphCounterBlock-lZlAQiJd.js +1 -0
  42. python_cad_tools/_viewer_static/assets/flowGraphDataSwitchBlock-BIzvmbVy.js +1 -0
  43. python_cad_tools/_viewer_static/assets/flowGraphDebounceBlock-DvNuMVwy.js +1 -0
  44. python_cad_tools/_viewer_static/assets/flowGraphDebugBlock-Dymd2VEE.js +1 -0
  45. python_cad_tools/_viewer_static/assets/flowGraphDoNBlock-v68aoNLH.js +1 -0
  46. python_cad_tools/_viewer_static/assets/flowGraphEasingBlock-CTnGv92q.js +1 -0
  47. python_cad_tools/_viewer_static/assets/flowGraphFlipFlopBlock-DXsO0Chw.js +1 -0
  48. python_cad_tools/_viewer_static/assets/flowGraphForLoopBlock-CPpe9T75.js +1 -0
  49. python_cad_tools/_viewer_static/assets/flowGraphFunctionReferenceBlock-DjDev4Pq.js +1 -0
  50. python_cad_tools/_viewer_static/assets/flowGraphGLTFDataProvider-GIYCz4AQ.js +1 -0
  51. python_cad_tools/_viewer_static/assets/flowGraphGetAngularVelocityBlock-BStkOVeL.js +1 -0
  52. python_cad_tools/_viewer_static/assets/flowGraphGetAssetBlock-Eiqoivbz.js +1 -0
  53. python_cad_tools/_viewer_static/assets/flowGraphGetLinearVelocityBlock-B0cpV5ws.js +1 -0
  54. python_cad_tools/_viewer_static/assets/flowGraphGetPhysicsMassPropertiesBlock-B-H64yaI.js +1 -0
  55. python_cad_tools/_viewer_static/assets/flowGraphGetPropertyBlock-CUyOsPw0.js +1 -0
  56. python_cad_tools/_viewer_static/assets/flowGraphGetSoundVolumeBlock-C0FjrsUz.js +1 -0
  57. python_cad_tools/_viewer_static/assets/flowGraphGetVariableBlock-O4597zBl.js +1 -0
  58. python_cad_tools/_viewer_static/assets/flowGraphIndexOfBlock-E00Z3KXM.js +1 -0
  59. python_cad_tools/_viewer_static/assets/flowGraphInterpolationBlock-DnkA9MNH.js +1 -0
  60. python_cad_tools/_viewer_static/assets/flowGraphIsKeyPressedBlock-DDfeuAyf.js +1 -0
  61. python_cad_tools/_viewer_static/assets/flowGraphIsSoundPlayingBlock-DMXg8jdM.js +1 -0
  62. python_cad_tools/_viewer_static/assets/flowGraphJsonPointerParserBlock-OmCorK0y.js +1 -0
  63. python_cad_tools/_viewer_static/assets/flowGraphKeyDownEventBlock-RuVtj6g2.js +1 -0
  64. python_cad_tools/_viewer_static/assets/flowGraphKeyUpEventBlock-DO4pAfHi.js +1 -0
  65. python_cad_tools/_viewer_static/assets/flowGraphKeyboardEventBlock-Bx3pAzzh.js +1 -0
  66. python_cad_tools/_viewer_static/assets/flowGraphMathBlocks-7W1XYOC8.js +1 -0
  67. python_cad_tools/_viewer_static/assets/flowGraphMathCombineExtractBlocks-a4f0Kv6e.js +1 -0
  68. python_cad_tools/_viewer_static/assets/flowGraphMatrixMathBlocks-C5Z-y1Au.js +1 -0
  69. python_cad_tools/_viewer_static/assets/flowGraphMeshPickEventBlock-D4m-I4CS.js +1 -0
  70. python_cad_tools/_viewer_static/assets/flowGraphMultiGateBlock-BISVSMw6.js +1 -0
  71. python_cad_tools/_viewer_static/assets/flowGraphPauseAnimationBlock-BYP2Z-HG.js +1 -0
  72. python_cad_tools/_viewer_static/assets/flowGraphPauseSoundBlock-Drh1EeW6.js +1 -0
  73. python_cad_tools/_viewer_static/assets/flowGraphPhysicsCollisionEventBlock-BoziAQ75.js +1 -0
  74. python_cad_tools/_viewer_static/assets/flowGraphPlayAnimationBlock-Clry-68M.js +1 -0
  75. python_cad_tools/_viewer_static/assets/flowGraphPlaySoundBlock-LbBVxGBE.js +1 -0
  76. python_cad_tools/_viewer_static/assets/flowGraphPointerDownEventBlock-DIVXCSHh.js +1 -0
  77. python_cad_tools/_viewer_static/assets/flowGraphPointerMoveEventBlock-BNNOzY2W.js +1 -0
  78. python_cad_tools/_viewer_static/assets/flowGraphPointerOutEventBlock-B8f2dOFT.js +1 -0
  79. python_cad_tools/_viewer_static/assets/flowGraphPointerOverEventBlock-CQr8LCee.js +1 -0
  80. python_cad_tools/_viewer_static/assets/flowGraphPointerUpEventBlock-DMg_iEHl.js +1 -0
  81. python_cad_tools/_viewer_static/assets/flowGraphReceiveCustomEventBlock-B8GJm9Ai.js +1 -0
  82. python_cad_tools/_viewer_static/assets/flowGraphSceneReadyEventBlock-Dcdb29K2.js +1 -0
  83. python_cad_tools/_viewer_static/assets/flowGraphSceneTickEventBlock-BLE5p4mi.js +1 -0
  84. python_cad_tools/_viewer_static/assets/flowGraphSendCustomEventBlock-BMzDItQ5.js +1 -0
  85. python_cad_tools/_viewer_static/assets/flowGraphSequenceBlock-DRURovjn.js +1 -0
  86. python_cad_tools/_viewer_static/assets/flowGraphSetAngularVelocityBlock-Ba8dhkLM.js +1 -0
  87. python_cad_tools/_viewer_static/assets/flowGraphSetDelayBlock-CnQR--Gn.js +1 -0
  88. python_cad_tools/_viewer_static/assets/flowGraphSetLinearVelocityBlock-Dm1r3vkh.js +1 -0
  89. python_cad_tools/_viewer_static/assets/flowGraphSetPhysicsMotionTypeBlock-BNtMLimi.js +1 -0
  90. python_cad_tools/_viewer_static/assets/flowGraphSetPropertyBlock-C-uv2jhP.js +1 -0
  91. python_cad_tools/_viewer_static/assets/flowGraphSetSoundVolumeBlock-D9LDx3lg.js +1 -0
  92. python_cad_tools/_viewer_static/assets/flowGraphSetVariableBlock-DszzRB5X.js +1 -0
  93. python_cad_tools/_viewer_static/assets/flowGraphSoundEndedEventBlock-DeTDSudb.js +1 -0
  94. python_cad_tools/_viewer_static/assets/flowGraphStopAnimationBlock-DWuwoFWr.js +1 -0
  95. python_cad_tools/_viewer_static/assets/flowGraphStopSoundBlock-DEnj2cQw.js +1 -0
  96. python_cad_tools/_viewer_static/assets/flowGraphSwitchBlock-zs8Fp9ky.js +1 -0
  97. python_cad_tools/_viewer_static/assets/flowGraphThrottleBlock-BYAKUQXd.js +1 -0
  98. python_cad_tools/_viewer_static/assets/flowGraphTransformCoordinatesSystemBlock-DYP54m5P.js +1 -0
  99. python_cad_tools/_viewer_static/assets/flowGraphTypeToTypeBlocks-vFwn79bS.js +1 -0
  100. python_cad_tools/_viewer_static/assets/flowGraphUnaryOperationBlock-CaRh4Ef0.js +1 -0
  101. python_cad_tools/_viewer_static/assets/flowGraphVectorMathBlocks-BEIhMs-A.js +1 -0
  102. python_cad_tools/_viewer_static/assets/flowGraphWaitAllBlock-DFbELP_j.js +1 -0
  103. python_cad_tools/_viewer_static/assets/flowGraphWhileLoopBlock-C1OKQ-_F.js +1 -0
  104. python_cad_tools/_viewer_static/assets/fogFragment-DtDlQBnz.js +25 -0
  105. python_cad_tools/_viewer_static/assets/gaussianSplatting-Cfigjb15.js +242 -0
  106. python_cad_tools/_viewer_static/assets/gaussianSplatting-DIn8R8oF.js +296 -0
  107. python_cad_tools/_viewer_static/assets/gaussianSplatting.fragment-BxeRJ2k-.js +38 -0
  108. python_cad_tools/_viewer_static/assets/gaussianSplatting.fragment-DNDmvRs-.js +37 -0
  109. python_cad_tools/_viewer_static/assets/gaussianSplatting.vertex-D92z5jYn.js +86 -0
  110. python_cad_tools/_viewer_static/assets/gaussianSplatting.vertex-DSWfFnKQ.js +86 -0
  111. python_cad_tools/_viewer_static/assets/gaussianSplattingVoxel.fragment-DBQE2gDk.js +12 -0
  112. python_cad_tools/_viewer_static/assets/gaussianSplattingVoxel.fragment-qwx5ntqJ.js +17 -0
  113. python_cad_tools/_viewer_static/assets/gaussianSplattingVoxel.vertex-CP2kd3jU.js +30 -0
  114. python_cad_tools/_viewer_static/assets/gaussianSplattingVoxel.vertex-Dr-fEg_W.js +19 -0
  115. python_cad_tools/_viewer_static/assets/geometry.fragment-CsJIm2H0.js +242 -0
  116. python_cad_tools/_viewer_static/assets/geometry.fragment-Dph3muZt.js +262 -0
  117. python_cad_tools/_viewer_static/assets/geometry.vertex-BXGMTA_J.js +212 -0
  118. python_cad_tools/_viewer_static/assets/geometry.vertex-Bm9Wg3Gi.js +214 -0
  119. python_cad_tools/_viewer_static/assets/glowBlurPostProcess.fragment-BJSwKIJD.js +7 -0
  120. python_cad_tools/_viewer_static/assets/glowMapGeneration.fragment-DcyS1cTF.js +63 -0
  121. python_cad_tools/_viewer_static/assets/glowMapGeneration.vertex-BGq1Fakj.js +75 -0
  122. python_cad_tools/_viewer_static/assets/glowMapMerge.fragment-DxcneU_F.js +25 -0
  123. python_cad_tools/_viewer_static/assets/glowMapMerge.vertex-DaJJKqMV.js +8 -0
  124. python_cad_tools/_viewer_static/assets/harmonicsFunctions-DzJcvNWG.js +16 -0
  125. python_cad_tools/_viewer_static/assets/hdrTextureLoader-BGQvgh0h.js +2 -0
  126. python_cad_tools/_viewer_static/assets/helperFunctions-Cugfp1Fz.js +87 -0
  127. python_cad_tools/_viewer_static/assets/iesTextureLoader-CO4G0p2M.js +2 -0
  128. python_cad_tools/_viewer_static/assets/index-B2zBEWsq.js +7007 -0
  129. python_cad_tools/_viewer_static/assets/index-DcTTrc-8.css +1 -0
  130. python_cad_tools/_viewer_static/assets/kernelBlur.fragment-B6CTXTxY.js +57 -0
  131. python_cad_tools/_viewer_static/assets/kernelBlur.fragment-BkKbudbW.js +57 -0
  132. python_cad_tools/_viewer_static/assets/kernelBlur.vertex-BYwFU5IS.js +11 -0
  133. python_cad_tools/_viewer_static/assets/kernelBlur.vertex-D371zUfT.js +11 -0
  134. python_cad_tools/_viewer_static/assets/kernelBlurVaryingDeclaration-CgBGaCHg.js +1 -0
  135. python_cad_tools/_viewer_static/assets/kernelBlurVaryingDeclaration-YS8JpqdO.js +1 -0
  136. python_cad_tools/_viewer_static/assets/ktxTextureLoader-ncMSJTjl.js +1 -0
  137. python_cad_tools/_viewer_static/assets/lightFragment-skHiFbKB.js +398 -0
  138. python_cad_tools/_viewer_static/assets/logDepthDeclaration-BR8XRz9F.js +4 -0
  139. python_cad_tools/_viewer_static/assets/logDepthVertex-UKrhEIvg.js +14 -0
  140. python_cad_tools/_viewer_static/assets/mainUVVaryingDeclaration-BxQWGFcG.js +4 -0
  141. python_cad_tools/_viewer_static/assets/mesh.vertexData.functions-ayNlGC5K.js +1 -0
  142. python_cad_tools/_viewer_static/assets/meshUboDeclaration-BnX046aQ.js +4 -0
  143. python_cad_tools/_viewer_static/assets/oitFragment-CTMCOfNP.js +605 -0
  144. python_cad_tools/_viewer_static/assets/openpbr.fragment-DPJPqi6p.js +1530 -0
  145. python_cad_tools/_viewer_static/assets/openpbr.fragment-Dv6qTVVi.js +1384 -0
  146. python_cad_tools/_viewer_static/assets/openpbr.vertex-Bn2wrdGL.js +364 -0
  147. python_cad_tools/_viewer_static/assets/openpbr.vertex-CHESW3gB.js +251 -0
  148. python_cad_tools/_viewer_static/assets/openpbrMaterial.pure-BdK7VgO_.js +3 -0
  149. python_cad_tools/_viewer_static/assets/openpbrMaterialLoadingAdapter-Bm_AHDUN.js +4 -0
  150. python_cad_tools/_viewer_static/assets/openpbrTransmissionLayerData-BDa9qEoN.js +339 -0
  151. python_cad_tools/_viewer_static/assets/openpbrTransmissionLayerData-Ba_ly4LE.js +343 -0
  152. python_cad_tools/_viewer_static/assets/openpbrUboDeclaration-BY2lSH2C.js +5 -0
  153. python_cad_tools/_viewer_static/assets/openpbrUboDeclaration-DajhikDS.js +6 -0
  154. python_cad_tools/_viewer_static/assets/packingFunctions-BHPwcGG2.js +4 -0
  155. python_cad_tools/_viewer_static/assets/packingFunctions-DTwzBRBx.js +4 -0
  156. python_cad_tools/_viewer_static/assets/pass.fragment-9oKU4pI3.js +4 -0
  157. python_cad_tools/_viewer_static/assets/passCube.fragment-CpAeiYx1.js +23 -0
  158. python_cad_tools/_viewer_static/assets/passCube.fragment-DHtSWVRF.js +23 -0
  159. python_cad_tools/_viewer_static/assets/pbr.fragment-cIhMKwLs.js +2560 -0
  160. python_cad_tools/_viewer_static/assets/pbr.vertex-BfdONsf2.js +255 -0
  161. python_cad_tools/_viewer_static/assets/pbrBRDFFunctions-B_ppOzPO.js +173 -0
  162. python_cad_tools/_viewer_static/assets/pbrDebug-DDdwL5_t.js +693 -0
  163. python_cad_tools/_viewer_static/assets/pbrIBLFunctions-b1zdpzOP.js +43 -0
  164. python_cad_tools/_viewer_static/assets/pbrMaterial.pure-BZA7yVhu.js +2 -0
  165. python_cad_tools/_viewer_static/assets/pbrMaterialLoadingAdapter-BRvKRnjl.js +1 -0
  166. python_cad_tools/_viewer_static/assets/pbrUboDeclaration-CMZp25k3.js +5 -0
  167. python_cad_tools/_viewer_static/assets/postprocess.vertex-DzdUpNbW.js +9 -0
  168. python_cad_tools/_viewer_static/assets/procedural.vertex-BnbuQ-1f.js +7 -0
  169. python_cad_tools/_viewer_static/assets/procedural.vertex-Dp-Ng7Pc.js +8 -0
  170. python_cad_tools/_viewer_static/assets/reflectionFunction-DBlr3oDs.js +83 -0
  171. python_cad_tools/_viewer_static/assets/rgbdDecode.fragment-CyvnZFOJ.js +5 -0
  172. python_cad_tools/_viewer_static/assets/rgbdEncode.fragment-Dz4zLnoD.js +5 -0
  173. python_cad_tools/_viewer_static/assets/rgbdEncode.fragment-iscgpA1_.js +5 -0
  174. python_cad_tools/_viewer_static/assets/sceneUboDeclaration-BzFIc4mb.js +11 -0
  175. python_cad_tools/_viewer_static/assets/textureProcessor.fragment-C9D0rd-M.js +151 -0
  176. python_cad_tools/_viewer_static/assets/textureProcessor.fragment-JMKyWZng.js +146 -0
  177. python_cad_tools/_viewer_static/assets/tgaTextureLoader-oxTElIQJ.js +1 -0
  178. python_cad_tools/_viewer_static/assets/vertexColorMixing-mdC9xZe1.js +418 -0
  179. python_cad_tools/_viewer_static/favicon.svg +10 -0
  180. python_cad_tools/_viewer_static/index.html +18 -0
  181. python_cad_tools/_viewer_static/sw.js +61 -0
  182. python_cad_tools/artifacts.py +546 -0
  183. python_cad_tools/build-info.json +1 -0
  184. python_cad_tools/build.py +270 -0
  185. python_cad_tools/cli.py +212 -0
  186. python_cad_tools/context.py +67 -0
  187. python_cad_tools/determinism.py +66 -0
  188. python_cad_tools/drawings/__init__.py +124 -0
  189. python_cad_tools/drawings/_catalog.py +79 -0
  190. python_cad_tools/drawings/_validation.py +238 -0
  191. python_cad_tools/elements.py +121 -0
  192. python_cad_tools/exceptions.py +45 -0
  193. python_cad_tools/exporters/__init__.py +8 -0
  194. python_cad_tools/exporters/base.py +24 -0
  195. python_cad_tools/exporters/drawings/__init__.py +5 -0
  196. python_cad_tools/exporters/drawings/annotations.py +444 -0
  197. python_cad_tools/exporters/drawings/dxf.py +110 -0
  198. python_cad_tools/exporters/drawings/model.py +70 -0
  199. python_cad_tools/exporters/drawings/pdf.py +119 -0
  200. python_cad_tools/exporters/drawings/primitives.py +93 -0
  201. python_cad_tools/exporters/drawings/projection.py +64 -0
  202. python_cad_tools/exporters/drawings/svg.py +135 -0
  203. python_cad_tools/exporters/glb.py +100 -0
  204. python_cad_tools/exporters/ifc.py +229 -0
  205. python_cad_tools/exporters/quantities.py +136 -0
  206. python_cad_tools/exporters/step.py +117 -0
  207. python_cad_tools/geometry.py +187 -0
  208. python_cad_tools/manifests.py +338 -0
  209. python_cad_tools/project_config.py +174 -0
  210. python_cad_tools/py.typed +1 -0
  211. python_cad_tools/runtime_sync.py +126 -0
  212. python_cad_tools/schemas/build-manifest-1.json +1 -0
  213. python_cad_tools/schemas/design-manifest-1.json +1 -0
  214. python_cad_tools/schemas/download-manifest-2.json +46 -0
  215. python_cad_tools/schemas/drawing-annotations-1.json +66 -0
  216. python_cad_tools/schemas/glb-manifest-1.json +1 -0
  217. python_cad_tools/schemas/ifc-validation-1.json +1 -0
  218. python_cad_tools/schemas/quantities-1.json +1 -0
  219. python_cad_tools/schemas/run-metadata-1.json +1 -0
  220. python_cad_tools/schemas/site-metadata-1.json +34 -0
  221. python_cad_tools/schemas/step-validation-1.json +1 -0
  222. python_cad_tools/site.py +676 -0
  223. python_cad_tools/templates/project/.gitignore +9 -0
  224. python_cad_tools/templates/project/README.md +38 -0
  225. python_cad_tools/templates/project/config.py +36 -0
  226. python_cad_tools/templates/project/drawing_annotations.py +117 -0
  227. python_cad_tools/templates/project/model.py +87 -0
  228. python_cad_tools/templates/project/pyproject.toml +27 -0
  229. python_cad_tools/units.py +207 -0
  230. python_cad_tools/validation.py +153 -0
  231. python_cad_tools-0.1.0rc1.dist-info/METADATA +149 -0
  232. python_cad_tools-0.1.0rc1.dist-info/RECORD +236 -0
  233. python_cad_tools-0.1.0rc1.dist-info/WHEEL +5 -0
  234. python_cad_tools-0.1.0rc1.dist-info/entry_points.txt +2 -0
  235. python_cad_tools-0.1.0rc1.dist-info/licenses/LICENSE +679 -0
  236. python_cad_tools-0.1.0rc1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,124 @@
1
+ """Public format-neutral drawing annotation contracts."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Literal
7
+
8
+ type Point2 = tuple[float, float]
9
+ type Direction = Literal["up", "down", "left", "right"]
10
+ type DrawingStyleToken = Literal[
11
+ "annotation-line",
12
+ "annotation-text",
13
+ "annotation-section",
14
+ "annotation-elevation",
15
+ "annotation-table",
16
+ ]
17
+
18
+
19
+ @dataclass(frozen=True, slots=True)
20
+ class DrawingSheet:
21
+ sheet_id: str
22
+ view_id: Literal["plan", "front", "side", "section"]
23
+ origin_mm: tuple[float, float, float]
24
+ x_axis: tuple[float, float, float]
25
+ y_axis: tuple[float, float, float]
26
+ projected_bounds_mm: tuple[float, float, float, float]
27
+
28
+
29
+ @dataclass(frozen=True, slots=True)
30
+ class Line:
31
+ id: str
32
+ sheet_id: str
33
+ start: Point2
34
+ end: Point2
35
+ style: DrawingStyleToken
36
+
37
+
38
+ @dataclass(frozen=True, slots=True)
39
+ class Polyline:
40
+ id: str
41
+ sheet_id: str
42
+ points: tuple[Point2, ...]
43
+ closed: bool
44
+ style: DrawingStyleToken
45
+
46
+
47
+ @dataclass(frozen=True, slots=True)
48
+ class Text:
49
+ id: str
50
+ sheet_id: str
51
+ position: Point2
52
+ value: str
53
+ height_mm: float
54
+ rotation_degrees: float
55
+ style: DrawingStyleToken
56
+
57
+
58
+ @dataclass(frozen=True, slots=True)
59
+ class SectionCallout:
60
+ id: str
61
+ sheet_id: str
62
+ start: Point2
63
+ end: Point2
64
+ reference_sheet_id: str
65
+ label: str
66
+ view_direction: Direction
67
+
68
+
69
+ @dataclass(frozen=True, slots=True)
70
+ class ElevationMarker:
71
+ id: str
72
+ sheet_id: str
73
+ position: Point2
74
+ reference_sheet_id: str
75
+ label: str
76
+ direction: Direction
77
+
78
+
79
+ @dataclass(frozen=True, slots=True)
80
+ class TableRow:
81
+ id: str
82
+ cells: tuple[str, ...]
83
+
84
+
85
+ @dataclass(frozen=True, slots=True)
86
+ class Table:
87
+ id: str
88
+ sheet_id: str
89
+ position: Point2
90
+ title: str
91
+ columns: tuple[str, ...]
92
+ rows: tuple[TableRow, ...]
93
+
94
+
95
+ type AnnotationPrimitive = Line | Polyline | Text | SectionCallout | ElevationMarker | Table
96
+
97
+
98
+ @dataclass(frozen=True, slots=True)
99
+ class SheetAnnotations:
100
+ sheet_id: str
101
+ annotations: tuple[AnnotationPrimitive, ...]
102
+
103
+
104
+ @dataclass(frozen=True, slots=True)
105
+ class DrawingAnnotationSet:
106
+ provider_id: str
107
+ sheets: tuple[SheetAnnotations, ...]
108
+
109
+
110
+ __all__ = [
111
+ "Direction",
112
+ "DrawingAnnotationSet",
113
+ "DrawingSheet",
114
+ "DrawingStyleToken",
115
+ "ElevationMarker",
116
+ "Line",
117
+ "Point2",
118
+ "Polyline",
119
+ "SectionCallout",
120
+ "SheetAnnotations",
121
+ "Table",
122
+ "TableRow",
123
+ "Text",
124
+ ]
@@ -0,0 +1,79 @@
1
+ """Standard drawing sheet catalog and orthographic projection helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from itertools import product
6
+ from math import isfinite
7
+ from typing import Any, Literal, cast
8
+
9
+ from ..exceptions import ModelValidationError
10
+ from ..geometry import bounds
11
+ from . import DrawingSheet
12
+
13
+ _SHEET_BASES: tuple[
14
+ tuple[
15
+ str,
16
+ Literal["plan", "front", "side", "section"],
17
+ tuple[float, float, float],
18
+ tuple[float, float, float],
19
+ tuple[float, float, float],
20
+ ],
21
+ ...,
22
+ ] = (
23
+ ("A-101", "plan", (0.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.0, 1.0, 0.0)),
24
+ ("A-201", "front", (0.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.0, 0.0, 1.0)),
25
+ ("A-202", "side", (0.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)),
26
+ ("A-301", "section", (0.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.0, 0.0, 1.0)),
27
+ )
28
+
29
+
30
+ def standard_sheet_catalog(model: Any) -> tuple[DrawingSheet, ...]:
31
+ physical = [
32
+ element
33
+ for element in model.walk()
34
+ if element.physical and element.visible and "drawings" in element.export_formats
35
+ ]
36
+ if not physical:
37
+ raise ModelValidationError("Drawing-inclusive operations require at least one visible physical drawing element")
38
+ sheets = []
39
+ for sheet_id, view_id, origin, x_axis, y_axis in _SHEET_BASES:
40
+ rectangles = [project_bounds(bounds(element.geometry), origin, x_axis, y_axis) for element in physical]
41
+ projected = (
42
+ min(item[0] for item in rectangles),
43
+ min(item[1] for item in rectangles),
44
+ max(item[2] for item in rectangles),
45
+ max(item[3] for item in rectangles),
46
+ )
47
+ if (
48
+ not all(isfinite(value) for value in projected)
49
+ or projected[0] >= projected[2]
50
+ or projected[1] >= projected[3]
51
+ ):
52
+ raise ModelValidationError(f"Drawing sheet {sheet_id!r} has invalid projected geometry bounds")
53
+ sheets.append(DrawingSheet(sheet_id, view_id, origin, x_axis, y_axis, projected))
54
+ return tuple(sheets)
55
+
56
+
57
+ def project_bounds(
58
+ bounds_mm: tuple[float, float, float, float, float, float],
59
+ origin: tuple[float, float, float],
60
+ x_axis: tuple[float, float, float],
61
+ y_axis: tuple[float, float, float],
62
+ ) -> tuple[float, float, float, float]:
63
+ projected = []
64
+ for point in product(*((bounds_mm[index], bounds_mm[index + 3]) for index in range(3))):
65
+ relative = cast(tuple[float, float, float], tuple(point[index] - origin[index] for index in range(3)))
66
+ projected.append((dot(relative, x_axis), dot(relative, y_axis)))
67
+ return (
68
+ min(point[0] for point in projected),
69
+ min(point[1] for point in projected),
70
+ max(point[0] for point in projected),
71
+ max(point[1] for point in projected),
72
+ )
73
+
74
+
75
+ def dot(left: tuple[float, float, float], right: tuple[float, float, float]) -> float:
76
+ return sum(left[index] * right[index] for index in range(3))
77
+
78
+
79
+ __all__: list[str] = []
@@ -0,0 +1,238 @@
1
+ """Strict validation for public neutral drawing values."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from math import isclose, isfinite
7
+ from typing import Any
8
+
9
+ from ..exceptions import ModelValidationError
10
+ from . import (
11
+ DrawingAnnotationSet,
12
+ DrawingSheet,
13
+ ElevationMarker,
14
+ Line,
15
+ Polyline,
16
+ SectionCallout,
17
+ SheetAnnotations,
18
+ Table,
19
+ TableRow,
20
+ Text,
21
+ )
22
+
23
+ _STABLE_ID = re.compile(r"[A-Za-z0-9._-]+\Z", re.ASCII)
24
+ _STYLE_TOKENS = {
25
+ "annotation-line",
26
+ "annotation-text",
27
+ "annotation-section",
28
+ "annotation-elevation",
29
+ "annotation-table",
30
+ }
31
+ _DIRECTIONS = {"up", "down", "left", "right"}
32
+ _PRIMITIVE_TYPES = (Line, Polyline, Text, SectionCallout, ElevationMarker, Table)
33
+
34
+
35
+ def validate_annotation_set(value: Any, sheets: tuple[DrawingSheet, ...]) -> DrawingAnnotationSet:
36
+ if not isinstance(value, DrawingAnnotationSet):
37
+ raise ModelValidationError(
38
+ f"Drawing annotation provider returned {type(value).__name__}; expected DrawingAnnotationSet"
39
+ )
40
+ _stable(value.provider_id, "annotation provider ID")
41
+ if not isinstance(value.sheets, tuple):
42
+ raise ModelValidationError("DrawingAnnotationSet.sheets must be a tuple")
43
+ sheet_catalog = {sheet.sheet_id: sheet for sheet in sheets}
44
+ _validate_sheet_catalog(sheets)
45
+ seen_sheet_records: set[str] = set()
46
+ seen_ids: set[str] = set()
47
+ for sheet_record in value.sheets:
48
+ if not isinstance(sheet_record, SheetAnnotations):
49
+ raise ModelValidationError("DrawingAnnotationSet.sheets must contain only SheetAnnotations values")
50
+ if sheet_record.sheet_id not in sheet_catalog:
51
+ raise ModelValidationError(f"Annotations reference unknown sheet: {sheet_record.sheet_id!r}")
52
+ if sheet_record.sheet_id in seen_sheet_records:
53
+ raise ModelValidationError(f"Duplicate SheetAnnotations record: {sheet_record.sheet_id!r}")
54
+ seen_sheet_records.add(sheet_record.sheet_id)
55
+ if not isinstance(sheet_record.annotations, tuple):
56
+ raise ModelValidationError("SheetAnnotations.annotations must be a tuple")
57
+ for annotation in sheet_record.annotations:
58
+ if not isinstance(annotation, _PRIMITIVE_TYPES):
59
+ raise ModelValidationError(f"Unsupported drawing annotation primitive: {type(annotation).__name__}")
60
+ _validate_annotation(annotation, sheet_record.sheet_id, sheet_catalog, seen_ids)
61
+ for child_id in _compound_child_ids(annotation):
62
+ if child_id in seen_ids:
63
+ raise ModelValidationError(f"Duplicate annotation or compound child ID: {child_id!r}")
64
+ seen_ids.add(child_id)
65
+ return value
66
+
67
+
68
+ def _validate_sheet_catalog(sheets: tuple[DrawingSheet, ...]) -> None:
69
+ if not isinstance(sheets, tuple) or not sheets:
70
+ raise ModelValidationError("DrawingContext.sheets must be a nonempty tuple")
71
+ seen: set[str] = set()
72
+ for sheet in sheets:
73
+ if not isinstance(sheet, DrawingSheet):
74
+ raise ModelValidationError("DrawingContext.sheets contains a non-DrawingSheet value")
75
+ _stable(sheet.sheet_id, "drawing sheet ID")
76
+ if sheet.sheet_id in seen:
77
+ raise ModelValidationError(f"Duplicate drawing sheet ID: {sheet.sheet_id!r}")
78
+ seen.add(sheet.sheet_id)
79
+ if sheet.view_id not in {"plan", "front", "side", "section"}:
80
+ raise ModelValidationError(f"Unsupported drawing view ID: {sheet.view_id!r}")
81
+ for label, vector in (("origin", sheet.origin_mm), ("x_axis", sheet.x_axis), ("y_axis", sheet.y_axis)):
82
+ if not _finite_tuple(vector, 3):
83
+ raise ModelValidationError(f"Drawing sheet {sheet.sheet_id!r} has invalid {label}")
84
+ x_length = sum(value * value for value in sheet.x_axis) ** 0.5
85
+ y_length = sum(value * value for value in sheet.y_axis) ** 0.5
86
+ product = sum(left * right for left, right in zip(sheet.x_axis, sheet.y_axis, strict=True))
87
+ if not isclose(x_length, 1.0, abs_tol=1e-9) or not isclose(y_length, 1.0, abs_tol=1e-9):
88
+ raise ModelValidationError(f"Drawing sheet {sheet.sheet_id!r} axes must be unit vectors")
89
+ if not isclose(product, 0.0, abs_tol=1e-9):
90
+ raise ModelValidationError(f"Drawing sheet {sheet.sheet_id!r} axes must be orthogonal")
91
+ if not _finite_tuple(sheet.projected_bounds_mm, 4):
92
+ raise ModelValidationError(f"Drawing sheet {sheet.sheet_id!r} has invalid projected bounds")
93
+ min_x, min_y, max_x, max_y = sheet.projected_bounds_mm
94
+ if min_x >= max_x or min_y >= max_y:
95
+ raise ModelValidationError(f"Drawing sheet {sheet.sheet_id!r} projected bounds must have positive area")
96
+
97
+
98
+ def _validate_annotation(
99
+ annotation: Any,
100
+ containing_sheet: str,
101
+ sheets: dict[str, DrawingSheet],
102
+ seen_ids: set[str],
103
+ ) -> None:
104
+ _stable(annotation.id, "annotation ID")
105
+ if annotation.id in seen_ids:
106
+ raise ModelValidationError(f"Duplicate annotation ID: {annotation.id!r}")
107
+ seen_ids.add(annotation.id)
108
+ if annotation.sheet_id != containing_sheet:
109
+ raise ModelValidationError(
110
+ f"Annotation {annotation.id!r} sheet_id {annotation.sheet_id!r} does not match its container"
111
+ )
112
+ if annotation.sheet_id not in sheets:
113
+ raise ModelValidationError(f"Annotation {annotation.id!r} references unknown sheet {annotation.sheet_id!r}")
114
+ if isinstance(annotation, Line):
115
+ _point(annotation.start, annotation.id)
116
+ _point(annotation.end, annotation.id)
117
+ if annotation.start == annotation.end:
118
+ raise ModelValidationError(f"Line annotation {annotation.id!r} must be nonzero")
119
+ _style(annotation.style, annotation.id)
120
+ elif isinstance(annotation, Polyline):
121
+ if not isinstance(annotation.points, tuple) or len(annotation.points) < (3 if annotation.closed else 2):
122
+ raise ModelValidationError(f"Polyline annotation {annotation.id!r} has too few points")
123
+ for point in annotation.points:
124
+ _point(point, annotation.id)
125
+ if len(set(annotation.points)) < 2:
126
+ raise ModelValidationError(f"Polyline annotation {annotation.id!r} must contain distinct points")
127
+ if type(annotation.closed) is not bool:
128
+ raise ModelValidationError(f"Polyline annotation {annotation.id!r} closed must be bool")
129
+ _style(annotation.style, annotation.id)
130
+ elif isinstance(annotation, Text):
131
+ _point(annotation.position, annotation.id)
132
+ _text(annotation.value, annotation.id)
133
+ if not _finite_number(annotation.height_mm) or annotation.height_mm <= 0:
134
+ raise ModelValidationError(f"Text annotation {annotation.id!r} height_mm must be finite and positive")
135
+ if not _finite_number(annotation.rotation_degrees):
136
+ raise ModelValidationError(f"Text annotation {annotation.id!r} rotation_degrees must be finite")
137
+ _style(annotation.style, annotation.id)
138
+ elif isinstance(annotation, SectionCallout):
139
+ _point(annotation.start, annotation.id)
140
+ _point(annotation.end, annotation.id)
141
+ if annotation.start == annotation.end:
142
+ raise ModelValidationError(f"Section callout {annotation.id!r} must have a nonzero line")
143
+ _reference(annotation.reference_sheet_id, annotation.id, sheets)
144
+ _text(annotation.label, annotation.id)
145
+ _direction(annotation.view_direction, annotation.id)
146
+ dx = annotation.end[0] - annotation.start[0]
147
+ dy = annotation.end[1] - annotation.start[1]
148
+ if annotation.view_direction in {"left", "right"} and not isclose(dx, 0.0, abs_tol=1e-9):
149
+ raise ModelValidationError(f"Section callout {annotation.id!r} must be orthogonal to its view_direction")
150
+ if annotation.view_direction in {"up", "down"} and not isclose(dy, 0.0, abs_tol=1e-9):
151
+ raise ModelValidationError(f"Section callout {annotation.id!r} must be orthogonal to its view_direction")
152
+ elif isinstance(annotation, ElevationMarker):
153
+ _point(annotation.position, annotation.id)
154
+ _reference(annotation.reference_sheet_id, annotation.id, sheets)
155
+ _text(annotation.label, annotation.id)
156
+ _direction(annotation.direction, annotation.id)
157
+ elif isinstance(annotation, Table):
158
+ _point(annotation.position, annotation.id)
159
+ _text(annotation.title, annotation.id)
160
+ if not isinstance(annotation.columns, tuple) or not annotation.columns:
161
+ raise ModelValidationError(f"Table annotation {annotation.id!r} must have columns")
162
+ for column in annotation.columns:
163
+ _text(column, annotation.id)
164
+ if not isinstance(annotation.rows, tuple):
165
+ raise ModelValidationError(f"Table annotation {annotation.id!r} rows must be a tuple")
166
+ row_ids: set[str] = set()
167
+ for row in annotation.rows:
168
+ if not isinstance(row, TableRow):
169
+ raise ModelValidationError(f"Table annotation {annotation.id!r} contains a non-TableRow value")
170
+ _stable(row.id, f"row ID in table {annotation.id!r}")
171
+ if row.id in row_ids:
172
+ raise ModelValidationError(f"Duplicate drawing table row ID: {row.id!r}")
173
+ row_ids.add(row.id)
174
+ if not isinstance(row.cells, tuple) or len(row.cells) != len(annotation.columns):
175
+ raise ModelValidationError(f"Table row {row.id!r} cell count does not match its columns")
176
+ for cell in row.cells:
177
+ _text(cell, row.id)
178
+
179
+
180
+ def _stable(value: object, label: str) -> None:
181
+ if not isinstance(value, str) or _STABLE_ID.fullmatch(value) is None:
182
+ raise ModelValidationError(f"{label} must match [A-Za-z0-9._-]+")
183
+
184
+
185
+ def _style(value: object, annotation_id: str) -> None:
186
+ if value not in _STYLE_TOKENS:
187
+ raise ModelValidationError(f"Annotation {annotation_id!r} has unsupported style token {value!r}")
188
+
189
+
190
+ def _direction(value: object, annotation_id: str) -> None:
191
+ if value not in _DIRECTIONS:
192
+ raise ModelValidationError(f"Annotation {annotation_id!r} has unsupported direction {value!r}")
193
+
194
+
195
+ def _reference(value: object, annotation_id: str, sheets: dict[str, DrawingSheet]) -> None:
196
+ if not isinstance(value, str) or value not in sheets:
197
+ raise ModelValidationError(f"Annotation {annotation_id!r} references unknown sheet {value!r}")
198
+
199
+
200
+ def _point(value: object, annotation_id: str) -> None:
201
+ if not _finite_tuple(value, 2):
202
+ raise ModelValidationError(f"Annotation {annotation_id!r} contains an invalid point")
203
+
204
+
205
+ def _text(value: object, annotation_id: str) -> None:
206
+ if (
207
+ not isinstance(value, str)
208
+ or not value.strip()
209
+ or any(ord(character) < 32 or ord(character) == 127 for character in value)
210
+ ):
211
+ raise ModelValidationError(f"Annotation {annotation_id!r} contains invalid text")
212
+
213
+
214
+ def _finite_tuple(value: object, length: int) -> bool:
215
+ return isinstance(value, tuple) and len(value) == length and all(_finite_number(item) for item in value)
216
+
217
+
218
+ def _finite_number(value: object) -> bool:
219
+ return isinstance(value, (int, float)) and not isinstance(value, bool) and isfinite(value)
220
+
221
+
222
+ def _compound_child_ids(annotation: Any) -> tuple[str, ...]:
223
+ if isinstance(annotation, SectionCallout):
224
+ suffixes = ("line", "arrow.start", "arrow.end", "label.start", "label.end")
225
+ return tuple(f"{annotation.id}.{suffix}" for suffix in suffixes)
226
+ if isinstance(annotation, ElevationMarker):
227
+ return tuple(f"{annotation.id}.{suffix}" for suffix in ("outline", "pointer", "label"))
228
+ if isinstance(annotation, Table):
229
+ ids = [f"{annotation.id}.title", f"{annotation.id}.border"]
230
+ ids.extend(f"{annotation.id}.header.{index}" for index in range(len(annotation.columns)))
231
+ for row in annotation.rows:
232
+ ids.extend(f"{annotation.id}.row.{row.id}.cell.{index}" for index in range(len(annotation.columns)))
233
+ ids.append(f"{annotation.id}.row.{row.id}.separator")
234
+ return tuple(ids)
235
+ return ()
236
+
237
+
238
+ __all__: list[str] = []
@@ -0,0 +1,121 @@
1
+ """Typed intermediate model shared by Python CAD Tools exporters."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterator
6
+ from dataclasses import dataclass, field
7
+ from typing import Any, Literal
8
+
9
+ Color = tuple[float, float, float]
10
+ GeometryKind = Literal["solid", "shell", "wire", "edge", "reference"]
11
+
12
+
13
+ @dataclass(frozen=True, slots=True)
14
+ class MaterialSpec:
15
+ id: str
16
+ name: str
17
+ category: str
18
+ color_rgb: Color
19
+ density_kg_m3: float | None = None
20
+ properties: dict[str, Any] = field(default_factory=dict)
21
+
22
+
23
+ @dataclass(frozen=True, slots=True)
24
+ class Placement:
25
+ translation_mm: tuple[float, float, float] = (0.0, 0.0, 0.0)
26
+ rotation_deg_xyz: tuple[float, float, float] = (0.0, 0.0, 0.0)
27
+
28
+
29
+ @dataclass(frozen=True, slots=True)
30
+ class Dimensions:
31
+ length_mm: float | None = None
32
+ width_mm: float | None = None
33
+ height_mm: float | None = None
34
+ radius_mm: float | None = None
35
+ extras: dict[str, float] = field(default_factory=dict)
36
+
37
+ def values(self) -> tuple[float, ...]:
38
+ primary = (self.length_mm, self.width_mm, self.height_mm, self.radius_mm)
39
+ return tuple(value for value in primary if value is not None) + tuple(
40
+ self.extras[key] for key in sorted(self.extras)
41
+ )
42
+
43
+
44
+ @dataclass(frozen=True, slots=True)
45
+ class IfcMapping:
46
+ ifc_class: str | None
47
+ predefined_type: str | None = None
48
+ exclude_reason: str | None = None
49
+
50
+
51
+ @dataclass(slots=True)
52
+ class DesignElement:
53
+ id: str
54
+ name: str
55
+ category: str
56
+ geometry: Any
57
+ geometry_kind: GeometryKind
58
+ placement: Placement = field(default_factory=Placement)
59
+ dimensions: Dimensions = field(default_factory=Dimensions)
60
+ material: MaterialSpec | None = None
61
+ color_rgb: Color | None = None
62
+ ifc_mapping: IfcMapping = field(default_factory=lambda: IfcMapping(None, exclude_reason="not mapped"))
63
+ storey: str | None = None
64
+ tags: set[str] = field(default_factory=set)
65
+ properties: dict[str, Any] = field(default_factory=dict)
66
+ children: list[DesignElement] = field(default_factory=list)
67
+ visible: bool = True
68
+ export_formats: set[str] = field(default_factory=lambda: {"step", "ifc", "glb", "drawings", "quantities"})
69
+ source_module: str = "model"
70
+ physical: bool = True
71
+ parent_id: str | None = None
72
+
73
+ def walk(self) -> Iterator[DesignElement]:
74
+ yield self
75
+ for child in sorted(self.children, key=lambda item: item.id):
76
+ yield from child.walk()
77
+
78
+
79
+ @dataclass(slots=True)
80
+ class DesignModel:
81
+ id: str
82
+ name: str
83
+ artifact_stem: str
84
+ elements: list[DesignElement]
85
+ coordinate_system: str = "right-handed; X right, Y depth/away, Z up; millimetres"
86
+ metadata: dict[str, Any] = field(default_factory=dict)
87
+ validators: list[Any] = field(default_factory=list)
88
+
89
+ def walk(self) -> Iterator[DesignElement]:
90
+ for element in sorted(self.elements, key=lambda item: item.id):
91
+ yield from element.walk()
92
+
93
+ def get(self, stable_id: str) -> DesignElement:
94
+ for element in self.walk():
95
+ if element.id == stable_id:
96
+ return element
97
+ raise KeyError(stable_id)
98
+
99
+
100
+ @dataclass(frozen=True, slots=True)
101
+ class QuantityRecord:
102
+ element_id: str
103
+ category: str
104
+ material_id: str | None
105
+ count: int
106
+ area_mm2: float | None
107
+ volume_mm3: float | None
108
+ mass_kg: float | None
109
+ provenance: str
110
+
111
+
112
+ __all__ = [
113
+ "Color",
114
+ "DesignElement",
115
+ "DesignModel",
116
+ "Dimensions",
117
+ "GeometryKind",
118
+ "IfcMapping",
119
+ "MaterialSpec",
120
+ "Placement",
121
+ ]
@@ -0,0 +1,45 @@
1
+ """Public exception hierarchy for Python CAD Tools."""
2
+
3
+
4
+ class CadToolsError(RuntimeError):
5
+ """Base class for actionable Python CAD Tools failures."""
6
+
7
+
8
+ class ProjectConfigError(CadToolsError):
9
+ """Project configuration or entry-point loading is invalid."""
10
+
11
+
12
+ class ModelValidationError(CadToolsError):
13
+ """The project model violates a public model contract."""
14
+
15
+
16
+ class BuildExportError(CadToolsError):
17
+ """An exporter failed to create or validate an artifact."""
18
+
19
+
20
+ class ArtifactIntegrityError(CadToolsError):
21
+ """Generated artifact bytes do not match their inventory."""
22
+
23
+
24
+ class UnsafePathError(CadToolsError):
25
+ """A configured or requested path escapes the project boundary."""
26
+
27
+
28
+ class BuildLockedError(CadToolsError):
29
+ """A project operation cannot run because execution is already active."""
30
+
31
+
32
+ class SiteError(CadToolsError):
33
+ """Viewer-site preparation or serving failed."""
34
+
35
+
36
+ __all__ = [
37
+ "ArtifactIntegrityError",
38
+ "BuildExportError",
39
+ "BuildLockedError",
40
+ "CadToolsError",
41
+ "ModelValidationError",
42
+ "ProjectConfigError",
43
+ "SiteError",
44
+ "UnsafePathError",
45
+ ]
@@ -0,0 +1,8 @@
1
+ """Format exporters for the shared Python CAD Tools design model."""
2
+
3
+ from .glb import GlbExporter
4
+ from .ifc import IfcExporter
5
+ from .quantities import QuantityExporter
6
+ from .step import StepExporter
7
+
8
+ __all__ = ["GlbExporter", "IfcExporter", "QuantityExporter", "StepExporter"]
@@ -0,0 +1,24 @@
1
+ """Common Python CAD Tools exporter protocol and helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Protocol
7
+
8
+ from ..elements import DesignElement, DesignModel
9
+
10
+
11
+ class Exporter(Protocol):
12
+ name: str
13
+
14
+ def export(self, model: DesignModel, output_dir: Path) -> list[Path]: ...
15
+
16
+
17
+ def selected_elements(model: DesignModel, format_name: str, *, physical_only: bool = False) -> list[DesignElement]:
18
+ return [
19
+ element
20
+ for element in model.walk()
21
+ if element.visible
22
+ and format_name in element.export_formats
23
+ and (element.physical or not physical_only)
24
+ ]
@@ -0,0 +1,5 @@
1
+ """Python CAD Tools conceptual drawing scenes and vector writers."""
2
+
3
+ from .model import DrawingExporter
4
+
5
+ __all__ = ["DrawingExporter"]