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,119 @@
1
+ """Python CAD Tools vector PDF sheet-set writer."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ from reportlab.lib.pagesizes import landscape, letter
8
+ from reportlab.pdfgen.canvas import Canvas
9
+
10
+ from .primitives import DrawingScene
11
+
12
+
13
+ def write_pdf(scenes: list[DrawingScene], path: Path) -> Path:
14
+ path.parent.mkdir(parents=True, exist_ok=True)
15
+ page_size = landscape(letter)
16
+ canvas = Canvas(str(path), pagesize=page_size, pageCompression=0, invariant=1)
17
+ for scene in scenes:
18
+ _draw_scene(canvas, scene, page_size)
19
+ canvas.showPage()
20
+ canvas.save()
21
+ return path
22
+
23
+
24
+ def _draw_scene(canvas: Canvas, scene: DrawingScene, page_size: tuple[float, float]) -> None:
25
+ page_width, page_height = page_size
26
+ margin = 42
27
+ canvas.setFillColorRGB(1, 1, 1)
28
+ canvas.rect(0, 0, page_width, page_height, stroke=0, fill=1)
29
+ canvas.setFillColorRGB(0, 0, 0)
30
+ canvas.setStrokeColorRGB(0.2, 0.2, 0.2)
31
+ canvas.setLineWidth(0.6)
32
+ canvas.rect(24, 24, page_width - 48, page_height - 48, stroke=1, fill=0)
33
+ x0, y0, x1, y1 = scene.viewport.bounds
34
+ scale = min((page_width - 2 * margin) / (x1 - x0), (page_height - 3 * margin) / (y1 - y0))
35
+
36
+ def point(value: tuple[float, float]) -> tuple[float, float]:
37
+ return margin + (value[0] - x0) * scale, margin * 1.5 + (value[1] - y0) * scale
38
+
39
+ canvas.setStrokeColorRGB(0.05, 0.05, 0.05)
40
+ canvas.setLineWidth(1.0)
41
+ for polyline in scene.polylines:
42
+ path = canvas.beginPath()
43
+ start = point(polyline.points[0])
44
+ path.moveTo(*start)
45
+ for source in polyline.points[1:]:
46
+ path.lineTo(*point(source))
47
+ if polyline.closed:
48
+ path.close()
49
+ canvas.drawPath(path)
50
+ canvas.saveState()
51
+ canvas.setStrokeColorRGB(0.1, 0.35, 0.55)
52
+ canvas.setDash(7, 3)
53
+ canvas.setLineWidth(0.8)
54
+ for line in scene.lines:
55
+ canvas.line(*point(line.start), *point(line.end))
56
+ canvas.restoreState()
57
+ canvas.saveState()
58
+ canvas.setStrokeColorRGB(0.25, 0.25, 0.25)
59
+ canvas.setFillColorRGB(0.15, 0.15, 0.15)
60
+ canvas.setLineWidth(0.65)
61
+ canvas.setFont("Helvetica", 7)
62
+ for dimension in scene.dimensions:
63
+ dx = dimension.end[0] - dimension.start[0]
64
+ dy = dimension.end[1] - dimension.start[1]
65
+ if abs(dx) >= abs(dy):
66
+ start = (dimension.start[0], dimension.start[1] + dimension.offset)
67
+ end = (dimension.end[0], dimension.end[1] + dimension.offset)
68
+ else:
69
+ start = (dimension.start[0] + dimension.offset, dimension.start[1])
70
+ end = (dimension.end[0] + dimension.offset, dimension.end[1])
71
+ start_page = point(start)
72
+ end_page = point(end)
73
+ canvas.line(*start_page, *end_page)
74
+ canvas.line(start_page[0] - 3, start_page[1] - 3, start_page[0] + 3, start_page[1] + 3)
75
+ canvas.line(end_page[0] - 3, end_page[1] - 3, end_page[0] + 3, end_page[1] + 3)
76
+ canvas.drawCentredString(
77
+ (start_page[0] + end_page[0]) / 2,
78
+ (start_page[1] + end_page[1]) / 2 + 4,
79
+ dimension.label,
80
+ )
81
+ canvas.restoreState()
82
+ canvas.setFont("Helvetica", 7)
83
+ for text in scene.texts:
84
+ canvas.drawString(*point(text.position), text.value)
85
+ for entity in scene.annotation_entities:
86
+ canvas.saveState()
87
+ canvas.setStrokeColorRGB(0.48, 0.12, 0.12)
88
+ canvas.setFillColorRGB(0.48, 0.12, 0.12)
89
+ canvas.setLineWidth(0.8)
90
+ if entity.kind == "line":
91
+ canvas.line(*point(entity.points[0]), *point(entity.points[1]))
92
+ elif entity.kind == "polyline":
93
+ annotation_path = canvas.beginPath()
94
+ annotation_path.moveTo(*point(entity.points[0]))
95
+ for source in entity.points[1:]:
96
+ annotation_path.lineTo(*point(source))
97
+ if entity.closed:
98
+ annotation_path.close()
99
+ canvas.drawPath(annotation_path)
100
+ elif entity.kind == "circle" and entity.center is not None and entity.radius is not None:
101
+ center = point(entity.center)
102
+ canvas.circle(center[0], center[1], entity.radius * scale, stroke=1, fill=0)
103
+ elif entity.kind == "text" and entity.text is not None:
104
+ x, y = point(entity.points[0])
105
+ canvas.translate(x, y)
106
+ canvas.rotate(entity.rotation_degrees)
107
+ canvas.setFont("Helvetica", max(entity.height_mm * scale, 5.0))
108
+ canvas.drawString(0, 0, entity.text)
109
+ canvas.restoreState()
110
+ canvas.setFont("Helvetica-Bold", 12)
111
+ canvas.drawString(margin, page_height - margin, scene.title_block.sheet_title)
112
+ canvas.setFont("Helvetica", 8)
113
+ canvas.drawString(margin, margin * 0.72, scene.title_block.project_name)
114
+ canvas.drawRightString(page_width - margin, page_height - margin, f"{scene.title_block.sheet_number} Rev {scene.title_block.revision}")
115
+ canvas.line(margin, margin * 1.05, page_width - margin, margin * 1.05)
116
+ canvas.setFillColorRGB(0.65, 0.0, 0.0)
117
+ canvas.setFont("Helvetica-Bold", 11)
118
+ canvas.drawCentredString(page_width / 2, margin * 0.72, scene.title_block.notice)
119
+ canvas.setFillColorRGB(0, 0, 0)
@@ -0,0 +1,93 @@
1
+ """Format-neutral Python CAD Tools drawing primitives."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ from .annotations import AnnotationEntity
10
+
11
+
12
+ @dataclass(frozen=True, slots=True)
13
+ class Line:
14
+ start: tuple[float, float]
15
+ end: tuple[float, float]
16
+ layer: str = "VISIBLE"
17
+ source_id: str | None = None
18
+
19
+
20
+ @dataclass(frozen=True, slots=True)
21
+ class Polyline:
22
+ points: tuple[tuple[float, float], ...]
23
+ closed: bool = False
24
+ layer: str = "VISIBLE"
25
+ source_id: str | None = None
26
+
27
+
28
+ @dataclass(frozen=True, slots=True)
29
+ class Arc:
30
+ center: tuple[float, float]
31
+ radius: float
32
+ start_deg: float
33
+ end_deg: float
34
+ layer: str = "VISIBLE"
35
+
36
+
37
+ @dataclass(frozen=True, slots=True)
38
+ class Text:
39
+ position: tuple[float, float]
40
+ value: str
41
+ height: float = 3.0
42
+ layer: str = "TEXT"
43
+
44
+
45
+ @dataclass(frozen=True, slots=True)
46
+ class Dimension:
47
+ start: tuple[float, float]
48
+ end: tuple[float, float]
49
+ offset: float
50
+ label: str
51
+ source_id: str | None = None
52
+
53
+
54
+ @dataclass(frozen=True, slots=True)
55
+ class Leader:
56
+ points: tuple[tuple[float, float], ...]
57
+ label: str
58
+
59
+
60
+ @dataclass(frozen=True, slots=True)
61
+ class Hatch:
62
+ boundary: tuple[tuple[float, float], ...]
63
+ pattern: str = "ANSI31"
64
+
65
+
66
+ @dataclass(frozen=True, slots=True)
67
+ class Viewport:
68
+ name: str
69
+ bounds: tuple[float, float, float, float]
70
+ scale: float
71
+
72
+
73
+ @dataclass(frozen=True, slots=True)
74
+ class TitleBlock:
75
+ project_name: str
76
+ sheet_title: str
77
+ sheet_number: str
78
+ revision: str
79
+ notice: str = "Conceptual — not for construction or permitting"
80
+
81
+
82
+ @dataclass(slots=True)
83
+ class DrawingScene:
84
+ title_block: TitleBlock
85
+ viewport: Viewport
86
+ lines: list[Line] = field(default_factory=list)
87
+ polylines: list[Polyline] = field(default_factory=list)
88
+ arcs: list[Arc] = field(default_factory=list)
89
+ texts: list[Text] = field(default_factory=list)
90
+ dimensions: list[Dimension] = field(default_factory=list)
91
+ leaders: list[Leader] = field(default_factory=list)
92
+ hatches: list[Hatch] = field(default_factory=list)
93
+ annotation_entities: tuple[AnnotationEntity, ...] = ()
@@ -0,0 +1,64 @@
1
+ """Orthographic shared-model projection into Python CAD Tools primitives."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ...drawings import AnnotationPrimitive, DrawingSheet
6
+ from ...drawings._catalog import project_bounds
7
+ from ...geometry import bounds
8
+ from ...units import format_feet_inches
9
+ from ..base import selected_elements
10
+ from .annotations import annotation_bounds, flatten_annotations
11
+ from .primitives import Dimension, DrawingScene, Line, Polyline, Text, TitleBlock, Viewport
12
+
13
+
14
+ def project_model(
15
+ model,
16
+ sheet: DrawingSheet,
17
+ annotations: tuple[AnnotationPrimitive, ...] = (),
18
+ revision: str = "WORKING",
19
+ ) -> DrawingScene:
20
+ physical = selected_elements(model, "drawings", physical_only=True)
21
+ rectangles = []
22
+ for element in physical:
23
+ rectangle = project_bounds(bounds(element.geometry), sheet.origin_mm, sheet.x_axis, sheet.y_axis)
24
+ rectangles.append((element, rectangle))
25
+ geometry_bounds = sheet.projected_bounds_mm
26
+ min_x, min_y, max_x, max_y = geometry_bounds
27
+ annotation_entities = flatten_annotations(annotations)
28
+ annotation_rectangle = annotation_bounds(annotation_entities)
29
+ if annotation_rectangle is not None:
30
+ min_x = min(min_x, annotation_rectangle[0])
31
+ min_y = min(min_y, annotation_rectangle[1])
32
+ max_x = max(max_x, annotation_rectangle[2])
33
+ max_y = max(max_y, annotation_rectangle[3])
34
+ padding = max(max_x - min_x, max_y - min_y) * 0.12
35
+ scene = DrawingScene(
36
+ title_block=TitleBlock(model.name, f"Overall {sheet.view_id.title()}", sheet.sheet_id, revision),
37
+ viewport=Viewport(sheet.view_id, (min_x - padding, min_y - padding, max_x + padding, max_y + padding), 1.0),
38
+ annotation_entities=annotation_entities,
39
+ )
40
+ label_all = len(rectangles) <= 20
41
+ for element, (x0, y0, x1, y1) in rectangles:
42
+ scene.polylines.append(
43
+ Polyline(((x0, y0), (x1, y0), (x1, y1), (x0, y1)), True, "VISIBLE", element.id)
44
+ )
45
+ if label_all or element.properties.get("drawing_label", False):
46
+ scene.texts.append(
47
+ Text((x0, y1 + padding * 0.12), f"{element.name} [{element.id}]", max(padding * 0.035, 3))
48
+ )
49
+ for element in selected_elements(model, "drawings"):
50
+ if element.physical:
51
+ continue
52
+ projected = project_bounds(bounds(element.geometry), sheet.origin_mm, sheet.x_axis, sheet.y_axis)
53
+ start, end = (projected[0], projected[1]), (projected[2], projected[3])
54
+ if start != end:
55
+ scene.lines.append(Line(start, end, "REFERENCE", element.id))
56
+ if rectangles:
57
+ x0, y0, x1, y1 = geometry_bounds
58
+ scene.dimensions.extend(
59
+ [
60
+ Dimension((x0, y0), (x1, y0), -padding * 0.35, format_feet_inches(x1 - x0), model.id),
61
+ Dimension((x0, y0), (x0, y1), -padding * 0.35, format_feet_inches(y1 - y0), model.id),
62
+ ]
63
+ )
64
+ return scene
@@ -0,0 +1,135 @@
1
+ """SVG writer for the shared Python CAD Tools drawing scene."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from xml.etree import ElementTree as ET
7
+
8
+ from .primitives import DrawingScene
9
+
10
+ SVG_NS = "http://www.w3.org/2000/svg"
11
+ ET.register_namespace("", SVG_NS)
12
+
13
+
14
+ def write_svg(scene: DrawingScene, path: Path) -> Path:
15
+ x0, y0, x1, y1 = scene.viewport.bounds
16
+ width = x1 - x0
17
+ height = y1 - y0
18
+ root = ET.Element(
19
+ f"{{{SVG_NS}}}svg",
20
+ {
21
+ "viewBox": f"{x0:g} {y0:g} {width:g} {height:g}",
22
+ "data-sheet": scene.title_block.sheet_number,
23
+ },
24
+ )
25
+ ET.SubElement(root, f"{{{SVG_NS}}}title").text = scene.title_block.sheet_title
26
+ group = ET.SubElement(root, f"{{{SVG_NS}}}g", {"transform": f"translate(0 {y0 + y1:g}) scale(1 -1)"})
27
+ for polyline in scene.polylines:
28
+ points = " ".join(f"{x:g},{y:g}" for x, y in polyline.points)
29
+ ET.SubElement(
30
+ group,
31
+ f"{{{SVG_NS}}}{'polygon' if polyline.closed else 'polyline'}",
32
+ {
33
+ "points": points,
34
+ "fill": "none",
35
+ "stroke": "#111111",
36
+ "stroke-width": "2",
37
+ "vector-effect": "non-scaling-stroke",
38
+ "data-layer": polyline.layer,
39
+ "data-source-id": polyline.source_id or "",
40
+ },
41
+ )
42
+ for line in scene.lines:
43
+ ET.SubElement(
44
+ group,
45
+ f"{{{SVG_NS}}}line",
46
+ {
47
+ "x1": f"{line.start[0]:g}", "y1": f"{line.start[1]:g}",
48
+ "x2": f"{line.end[0]:g}", "y2": f"{line.end[1]:g}",
49
+ "stroke": "#111111", "data-layer": line.layer,
50
+ },
51
+ )
52
+ text_group = ET.SubElement(root, f"{{{SVG_NS}}}g", {"fill": "#111111", "font-family": "sans-serif"})
53
+ for item in scene.texts:
54
+ ET.SubElement(
55
+ text_group,
56
+ f"{{{SVG_NS}}}text",
57
+ {"x": f"{item.position[0]:g}", "y": f"{y0 + y1 - item.position[1]:g}", "font-size": f"{item.height:g}"},
58
+ ).text = item.value
59
+ for dimension in scene.dimensions:
60
+ dx = dimension.end[0] - dimension.start[0]
61
+ dy = dimension.end[1] - dimension.start[1]
62
+ if abs(dx) >= abs(dy):
63
+ a = (dimension.start[0], dimension.start[1] + dimension.offset)
64
+ b = (dimension.end[0], dimension.end[1] + dimension.offset)
65
+ else:
66
+ a = (dimension.start[0] + dimension.offset, dimension.start[1])
67
+ b = (dimension.end[0] + dimension.offset, dimension.end[1])
68
+ ET.SubElement(group, f"{{{SVG_NS}}}line", {"x1": f"{a[0]:g}", "y1": f"{a[1]:g}", "x2": f"{b[0]:g}", "y2": f"{b[1]:g}", "stroke": "#333333", "data-layer": "DIMENSIONS"})
69
+ ET.SubElement(text_group, f"{{{SVG_NS}}}text", {"x": f"{(a[0] + b[0]) / 2:g}", "y": f"{y0 + y1 - (a[1] + b[1]) / 2:g}", "font-size": "12", "text-anchor": "middle"}).text = dimension.label
70
+ annotation_group = ET.SubElement(group, f"{{{SVG_NS}}}g", {"data-layer": "ANNOTATIONS"})
71
+ annotation_text_group = ET.SubElement(
72
+ root,
73
+ f"{{{SVG_NS}}}g",
74
+ {"data-layer": "ANNOTATIONS", "fill": "#111111", "font-family": "sans-serif"},
75
+ )
76
+ for entity in scene.annotation_entities:
77
+ attributes = {"data-annotation-id": entity.id, "data-style": entity.style}
78
+ if entity.kind == "line":
79
+ start, end = entity.points
80
+ ET.SubElement(
81
+ annotation_group,
82
+ f"{{{SVG_NS}}}line",
83
+ attributes
84
+ | {
85
+ "x1": f"{start[0]:g}",
86
+ "y1": f"{start[1]:g}",
87
+ "x2": f"{end[0]:g}",
88
+ "y2": f"{end[1]:g}",
89
+ "fill": "none",
90
+ "stroke": "#7a1f1f",
91
+ "vector-effect": "non-scaling-stroke",
92
+ },
93
+ )
94
+ elif entity.kind == "polyline":
95
+ ET.SubElement(
96
+ annotation_group,
97
+ f"{{{SVG_NS}}}{'polygon' if entity.closed else 'polyline'}",
98
+ attributes
99
+ | {
100
+ "points": " ".join(f"{x:g},{y:g}" for x, y in entity.points),
101
+ "fill": "none",
102
+ "stroke": "#7a1f1f",
103
+ "vector-effect": "non-scaling-stroke",
104
+ },
105
+ )
106
+ elif entity.kind == "circle" and entity.center is not None and entity.radius is not None:
107
+ ET.SubElement(
108
+ annotation_group,
109
+ f"{{{SVG_NS}}}circle",
110
+ attributes
111
+ | {
112
+ "cx": f"{entity.center[0]:g}",
113
+ "cy": f"{entity.center[1]:g}",
114
+ "r": f"{entity.radius:g}",
115
+ "fill": "none",
116
+ "stroke": "#7a1f1f",
117
+ "vector-effect": "non-scaling-stroke",
118
+ },
119
+ )
120
+ elif entity.kind == "text" and entity.text is not None:
121
+ x, y = entity.points[0]
122
+ text_attributes = attributes | {
123
+ "x": f"{x:g}",
124
+ "y": f"{y0 + y1 - y:g}",
125
+ "font-size": f"{entity.height_mm:g}",
126
+ }
127
+ if entity.rotation_degrees:
128
+ text_attributes["transform"] = f"rotate({-entity.rotation_degrees:g} {x:g} {y0 + y1 - y:g})"
129
+ ET.SubElement(annotation_text_group, f"{{{SVG_NS}}}text", text_attributes).text = entity.text
130
+ notice = ET.SubElement(root, f"{{{SVG_NS}}}text", {"x": f"{x0 + width * 0.02:g}", "y": f"{y1 - height * 0.02:g}", "font-size": f"{max(height * 0.025, 10):g}", "font-weight": "bold"})
131
+ notice.text = scene.title_block.notice
132
+ path.parent.mkdir(parents=True, exist_ok=True)
133
+ ET.ElementTree(root).write(path, encoding="utf-8", xml_declaration=True)
134
+ ET.parse(path)
135
+ return path
@@ -0,0 +1,100 @@
1
+ """OCP tessellation and glTF 2.0 binary scene export."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import struct
7
+ from pathlib import Path
8
+
9
+ import numpy as np
10
+ import trimesh
11
+
12
+ from ..determinism import write_json
13
+ from ..exceptions import BuildExportError
14
+ from ..geometry import combined_bounds, tessellate
15
+ from .base import selected_elements
16
+
17
+
18
+ class GlbExporter:
19
+ name = "glb"
20
+
21
+ def __init__(self, linear_deflection: float = 0.5, angular_deflection: float = 0.25) -> None:
22
+ self.linear_deflection = linear_deflection
23
+ self.angular_deflection = angular_deflection
24
+
25
+ def export(self, model, output_dir: Path) -> list[Path]:
26
+ target = output_dir / "glb"
27
+ target.mkdir(parents=True, exist_ok=True)
28
+ scene = trimesh.Scene(base_frame="model")
29
+ expected_ids: list[str] = []
30
+ triangle_counts: dict[str, int] = {}
31
+ for element in selected_elements(model, "glb", physical_only=True):
32
+ vertices, faces = tessellate(element.geometry, self.linear_deflection, self.angular_deflection)
33
+ # CAD Z-up (x,y,z) -> glTF Y-up (x,z,-y), a right-handed rotation.
34
+ converted = np.asarray([[x, z, -y] for x, y, z in vertices], dtype=np.float64)
35
+ material = trimesh.visual.material.PBRMaterial(
36
+ name=element.material.name if element.material else element.id,
37
+ baseColorFactor=[
38
+ int(round(channel * 255)) for channel in (element.color_rgb or (0.7, 0.7, 0.7))
39
+ ]
40
+ + [255],
41
+ metallicFactor=0.0,
42
+ roughnessFactor=0.8,
43
+ )
44
+ mesh = trimesh.Trimesh(vertices=converted, faces=np.asarray(faces), process=False, validate=True)
45
+ mesh.visual = trimesh.visual.TextureVisuals(material=material)
46
+ extras = {
47
+ "stable_id": element.id,
48
+ "category": element.category,
49
+ "source_module": element.source_module,
50
+ "geometry_kind": element.geometry_kind,
51
+ "units": "millimetres",
52
+ }
53
+ scene.add_geometry(mesh, geom_name=element.id, node_name=element.id, metadata=extras)
54
+ expected_ids.append(element.id)
55
+ triangle_counts[element.id] = len(faces)
56
+ path = target / f"{model.artifact_stem}.glb"
57
+ path.write_bytes(trimesh.exchange.gltf.export_glb(scene, include_normals=True))
58
+ document = glb_json(path)
59
+ found_ids = sorted(
60
+ node.get("extras", {}).get("stable_id")
61
+ for node in document.get("nodes", [])
62
+ if node.get("extras", {}).get("stable_id")
63
+ )
64
+ if found_ids != sorted(expected_ids):
65
+ raise BuildExportError(f"GLB node extras missing stable IDs: {found_ids}")
66
+ loaded = trimesh.load(path, force="scene", process=False)
67
+ if not isinstance(loaded, trimesh.Scene) or len(loaded.geometry) != len(expected_ids):
68
+ raise BuildExportError("Independent GLB reload did not preserve element geometry count")
69
+ manifest = write_json(
70
+ target / "manifest.json",
71
+ {
72
+ "schema_id": "urn:python-cad-tools:schema:glb-manifest:1",
73
+ "schema_version": 1,
74
+ "coordinate_transform": "CAD (x,y,z) to glTF (x,z,-y), right-handed",
75
+ "elements": sorted(expected_ids),
76
+ "triangle_counts": triangle_counts,
77
+ "bounds_cad_mm": [
78
+ round(value, 6)
79
+ for value in combined_bounds(
80
+ [element.geometry for element in selected_elements(model, "glb", physical_only=True)]
81
+ )
82
+ ],
83
+ "bounds_y_up_mm": np.asarray(loaded.bounds).round(6).tolist(),
84
+ "file_size": path.stat().st_size,
85
+ },
86
+ )
87
+ return [path, manifest]
88
+
89
+
90
+ def glb_json(path: Path) -> dict:
91
+ data = path.read_bytes()
92
+ if data[:4] != b"glTF":
93
+ raise BuildExportError("Invalid GLB magic")
94
+ _, version, _ = struct.unpack_from("<4sII", data, 0)
95
+ if version != 2:
96
+ raise BuildExportError(f"Expected glTF 2.0, got {version}")
97
+ chunk_length, chunk_type = struct.unpack_from("<II", data, 12)
98
+ if chunk_type != 0x4E4F534A:
99
+ raise BuildExportError("First GLB chunk is not JSON")
100
+ return json.loads(data[20 : 20 + chunk_length].decode("utf-8").rstrip(" \t\r\n\0"))