parasolid-kit 0.1.0.dev3__tar.gz → 0.1.0.dev4__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 (161) hide show
  1. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/Cargo.lock +2 -2
  2. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/Cargo.toml +1 -1
  3. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/PKG-INFO +21 -12
  4. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/README.md +20 -11
  5. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/brep/mapper.rs +249 -62
  6. parasolid_kit-0.1.0.dev4/crates/parasolid-core/src/brep/profile_roles.rs +680 -0
  7. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/error.rs +6 -0
  8. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/lib.rs +1 -1
  9. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/schema/mod.rs +3 -1
  10. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/schema/profile.rs +84 -6
  11. parasolid_kit-0.1.0.dev4/crates/parasolid-core/src/schema/profiles/mod.rs +10 -0
  12. parasolid_kit-0.1.0.dev4/crates/parasolid-core/src/schema/profiles/sch13006.rs +447 -0
  13. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/schema/profiles/sch30000.rs +42 -5
  14. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/schema/provider.rs +57 -0
  15. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/schema/registry.rs +3 -2
  16. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/text_document.rs +3 -3
  17. parasolid_kit-0.1.0.dev4/crates/parasolid-core/tests/builtin_sch13006.rs +147 -0
  18. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/tests/builtin_sch30000.rs +66 -7
  19. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/tests/builtin_sch30000_brep.rs +68 -3
  20. parasolid_kit-0.1.0.dev4/crates/parasolid-core/tests/embedded_base_membership.rs +303 -0
  21. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/tests/support/sch30000.rs +30 -4
  22. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/docs/api.md +18 -8
  23. parasolid_kit-0.1.0.dev4/docs/builtin-profiles.md +607 -0
  24. parasolid_kit-0.1.0.dev4/docs/doubly-periodic-surface-evidence.json +293 -0
  25. parasolid_kit-0.1.0.dev4/docs/doubly-periodic-surface-evidence.md +120 -0
  26. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/docs/format-support.md +78 -10
  27. parasolid_kit-0.1.0.dev4/docs/nurbs-surface-evidence.json +489 -0
  28. parasolid_kit-0.1.0.dev4/docs/nurbs-surface-evidence.md +136 -0
  29. parasolid_kit-0.1.0.dev4/docs/rational-periodic-surface-evidence.json +290 -0
  30. parasolid_kit-0.1.0.dev4/docs/rational-periodic-surface-evidence.md +113 -0
  31. parasolid_kit-0.1.0.dev4/docs/step-accuracy-data.json +266 -0
  32. parasolid_kit-0.1.0.dev4/docs/step-accuracy.md +176 -0
  33. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/fuzz/Cargo.lock +1 -1
  34. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/pyproject.toml +1 -1
  35. parasolid_kit-0.1.0.dev4/scripts/prepare_fuzz_corpus.py +169 -0
  36. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/scripts/verify_artifacts.py +3 -2
  37. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/scripts/verify_isolated_install.py +44 -15
  38. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/scripts/verify_optional_install.py +2 -2
  39. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/__init__.py +1 -1
  40. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/_native.py +10 -2
  41. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/schema/model.py +1 -2
  42. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_artifact_gate.py +5 -5
  43. parasolid_kit-0.1.0.dev4/tests/test_builtin_bspline_surface_runtime.py +160 -0
  44. parasolid_kit-0.1.0.dev4/tests/test_builtin_cone_runtime.py +102 -0
  45. parasolid_kit-0.1.0.dev4/tests/test_builtin_embedded_runtime.py +182 -0
  46. parasolid_kit-0.1.0.dev4/tests/test_builtin_intersection_data_runtime.py +191 -0
  47. parasolid_kit-0.1.0.dev4/tests/test_builtin_intersection_runtime.py +200 -0
  48. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_builtin_schema_runtime.py +3 -3
  49. parasolid_kit-0.1.0.dev4/tests/test_builtin_spcurve_runtime.py +378 -0
  50. parasolid_kit-0.1.0.dev4/tests/test_builtin_trimmed_runtime.py +212 -0
  51. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_distribution_runtime.py +26 -1
  52. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_public_contracts.py +2 -2
  53. parasolid_kit-0.1.0.dev3/crates/parasolid-core/src/brep/profile_roles.rs +0 -255
  54. parasolid_kit-0.1.0.dev3/crates/parasolid-core/src/schema/profiles/mod.rs +0 -5
  55. parasolid_kit-0.1.0.dev3/docs/builtin-profiles.md +0 -95
  56. parasolid_kit-0.1.0.dev3/scripts/prepare_fuzz_corpus.py +0 -74
  57. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/.gitattributes +0 -0
  58. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/LICENSE +0 -0
  59. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/corpus/README.md +0 -0
  60. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/corpus/manifest.schema.json +0 -0
  61. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/Cargo.toml +0 -0
  62. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/brep/metrics.rs +0 -0
  63. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/brep/mod.rs +0 -0
  64. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/brep/model.rs +0 -0
  65. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/comparison.rs +0 -0
  66. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/document.rs +0 -0
  67. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/header.rs +0 -0
  68. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/reader.rs +0 -0
  69. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/schema/catalog.rs +0 -0
  70. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/schema/coverage.rs +0 -0
  71. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/schema/decoder.rs +0 -0
  72. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/schema/model.rs +0 -0
  73. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/schema/wire.rs +0 -0
  74. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/text_reader.rs +0 -0
  75. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/src/user_fields.rs +0 -0
  76. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-core/tests/builtin_sch30000_holdout_values.rs +0 -0
  77. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-python/Cargo.toml +0 -0
  78. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-python/src/brep.rs +0 -0
  79. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/crates/parasolid-python/src/lib.rs +0 -0
  80. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/fuzz/Cargo.toml +0 -0
  81. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/fuzz/fuzz_targets/inspect.rs +0 -0
  82. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/fuzz/fuzz_targets/parse.rs +0 -0
  83. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/fuzz/fuzz_targets/schema_catalog.rs +0 -0
  84. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/scripts/verify_corpus.py +0 -0
  85. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/scripts/verify_optional_interop_i0.py +0 -0
  86. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/scripts/verify_optional_interop_i5.py +0 -0
  87. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/scripts/verify_optional_interop_i6.py +0 -0
  88. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/scripts/verify_optional_interop_i7.py +0 -0
  89. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/__main__.py +0 -0
  90. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/api.py +0 -0
  91. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/binary/__init__.py +0 -0
  92. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/binary/document.py +0 -0
  93. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/binary/header.py +0 -0
  94. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/brep/__init__.py +0 -0
  95. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/brep/_native.py +0 -0
  96. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/brep/geometry.py +0 -0
  97. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/brep/model.py +0 -0
  98. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/brep/topology.py +0 -0
  99. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/cli.py +0 -0
  100. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/diagnostics.py +0 -0
  101. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/errors.py +0 -0
  102. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/__init__.py +0 -0
  103. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/_typing.py +0 -0
  104. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/cadquery.py +0 -0
  105. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/dependency.py +0 -0
  106. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/errors.py +0 -0
  107. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/limits.py +0 -0
  108. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/occt/__init__.py +0 -0
  109. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/occt/conversion.py +0 -0
  110. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/occt/coverage.py +0 -0
  111. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/occt/geometry.py +0 -0
  112. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/occt/model.py +0 -0
  113. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/occt/options.py +0 -0
  114. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/occt/step.py +0 -0
  115. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/occt/topology.py +0 -0
  116. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/occt/validation.py +0 -0
  117. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/preview/__init__.py +0 -0
  118. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/preview/glb.py +0 -0
  119. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/preview/model.py +0 -0
  120. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/preview/server.py +0 -0
  121. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/preview/static/index.html +0 -0
  122. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/preview/static/viewer.css +0 -0
  123. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/preview/static/viewer.js +0 -0
  124. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/preview/tessellation.py +0 -0
  125. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/interop/preview/writer.py +0 -0
  126. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/limits.py +0 -0
  127. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/py.typed +0 -0
  128. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/schema/__init__.py +0 -0
  129. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/schema/api.py +0 -0
  130. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/schema/provider.py +0 -0
  131. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/summary.py +0 -0
  132. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/text/__init__.py +0 -0
  133. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/text/header.py +0 -0
  134. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/validation/__init__.py +0 -0
  135. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/src/parasolid_kit/validation/comparison.py +0 -0
  136. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/__init__.py +0 -0
  137. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/_occt_fixtures.py +0 -0
  138. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/support/__init__.py +0 -0
  139. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/support/parasolid_binary.py +0 -0
  140. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/support/parasolid_schema.py +0 -0
  141. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/support/parasolid_text.py +0 -0
  142. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_brep.py +0 -0
  143. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_cadquery_adapter.py +0 -0
  144. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_cli.py +0 -0
  145. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_corpus_gate.py +0 -0
  146. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_corpus_manifest.py +0 -0
  147. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_diagnostics.py +0 -0
  148. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_geometry_coverage.py +0 -0
  149. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_inspect_xb.py +0 -0
  150. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_interop.py +0 -0
  151. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_limits.py +0 -0
  152. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_occt_conversion.py +0 -0
  153. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_parse_xb.py +0 -0
  154. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_parse_xt.py +0 -0
  155. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_preview.py +0 -0
  156. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_schema_catalog.py +0 -0
  157. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_schema_resolution.py +0 -0
  158. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_step_export.py +0 -0
  159. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_summary.py +0 -0
  160. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_synthetic_binary.py +0 -0
  161. {parasolid_kit-0.1.0.dev3 → parasolid_kit-0.1.0.dev4}/tests/test_user_fields.py +0 -0
@@ -88,7 +88,7 @@ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
88
88
 
89
89
  [[package]]
90
90
  name = "parasolid-core"
91
- version = "0.1.0-dev3"
91
+ version = "0.1.0-dev4"
92
92
  dependencies = [
93
93
  "serde_json",
94
94
  "sha2",
@@ -96,7 +96,7 @@ dependencies = [
96
96
 
97
97
  [[package]]
98
98
  name = "parasolid-python"
99
- version = "0.1.0-dev3"
99
+ version = "0.1.0-dev4"
100
100
  dependencies = [
101
101
  "parasolid-core",
102
102
  "pyo3",
@@ -4,7 +4,7 @@ exclude = ["fuzz"]
4
4
  resolver = "3"
5
5
 
6
6
  [workspace.package]
7
- version = "0.1.0-dev3"
7
+ version = "0.1.0-dev4"
8
8
  edition = "2024"
9
9
  rust-version = "1.88"
10
10
  description = "Schema-aware parser core for Parasolid X_T and X_B data"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: parasolid-kit
3
- Version: 0.1.0.dev3
3
+ Version: 0.1.0.dev4
4
4
  Classifier: Development Status :: 2 - Pre-Alpha
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: Programming Language :: Python :: 3 :: Only
@@ -70,7 +70,7 @@ The `--pre` option is required while only development releases are available.
70
70
  To install the current release by exact version instead:
71
71
 
72
72
  ```bash
73
- python -m pip install "parasolid-kit==0.1.0.dev3"
73
+ python -m pip install "parasolid-kit==0.1.0.dev4"
74
74
  ```
75
75
 
76
76
  Stable releases, once available, can be installed without `--pre`:
@@ -82,14 +82,14 @@ python -m pip install parasolid-kit
82
82
  Alternatively, install a downloaded wheel directly:
83
83
 
84
84
  ```bash
85
- python -m pip install /path/to/parasolid_kit-0.1.0.dev3-cp310-abi3-PLATFORM.whl
85
+ python -m pip install /path/to/parasolid_kit-0.1.0.dev4-cp310-abi3-PLATFORM.whl
86
86
  ```
87
87
 
88
88
  Header inspection works immediately after installation. Complete parsing and
89
89
  B-Rep checking also work without a schema file for the built-in
90
90
  `SCH_3000000_30000` profile (`verified_subset`): Onshape V30 text/neutral binary
91
- exports of single solid boxes, prisms, cylinders, and through-holes with zero
92
- user fields. Other keys and uncovered types require an explicit provider; see
91
+ exports of single solid boxes, prisms, cylinders, through-holes, spheres, and
92
+ verified solids with elliptical edges, with zero user fields. Other keys and uncovered types require an explicit provider; see
93
93
  [Schema catalogs](#schema-catalogs). STEP, CadQuery, and preview operations also
94
94
  require an optional runtime and geometry supported by that adapter.
95
95
 
@@ -235,14 +235,23 @@ python -m pip install .
235
235
 
236
236
  ## Schema catalogs
237
237
 
238
- The built-in `onshape-sch30000-r1` profile (revision 1, `verified_subset`) is
239
- selected when neither a provider nor a schema directory is supplied and the
240
- internal key is exactly `SCH_3000000_30000`. It needs no catalog, network, or
241
- CAD installation at runtime. [Profile provenance and coverage](docs/builtin-profiles.md)
242
- records its source, canonical hash, and validation scope.
238
+ When neither a provider nor a schema directory is supplied, default parsing
239
+ selects one of these compiled profiles by the exact internal stream key:
243
240
 
244
- Use an external catalog for other keys, including embedded-base keys, or for
245
- types outside the built-in subset. An explicit provider is authoritative: an
241
+ | Internal schema key | Built-in profile | Reviewed base layout |
242
+ |---|---|---|
243
+ | `SCH_3000000_30000` | `onshape-sch30000-r2` | Onshape V30, 24 types / 202 field groups |
244
+ | `SCH_1300000_13006` | `onshape-sch13006-r6` | Onshape V13, 40 types / 327 field groups |
245
+ | `SCH_3000310_30000_13006` | `icad-sch30000-13006-r5` | 13006 base plus trimmed curve (133): 30 types / 240 groups; embedded edits and full type 204 |
246
+
247
+ All have `verified_subset` coverage and require zero user fields. They need no
248
+ catalog, network, or CAD installation at runtime.
249
+ [Profile provenance and coverage](docs/builtin-profiles.md) records their sources,
250
+ canonical hashes, and validation limits. The iCAD profile accepts extracted
251
+ Parasolid streams; `.icd` container parsing remains outside this package.
252
+
253
+ Use an external catalog for other keys or types outside these subsets.
254
+ An explicit provider is authoritative: an
246
255
  empty provider or missing exact catalog fails without switching to the built-in
247
256
  profile. `schema_provider=None` means default selection. Nearby versions and
248
257
  the human-readable X_T common-header `SCH` value are never used as substitutes.
@@ -46,7 +46,7 @@ The `--pre` option is required while only development releases are available.
46
46
  To install the current release by exact version instead:
47
47
 
48
48
  ```bash
49
- python -m pip install "parasolid-kit==0.1.0.dev3"
49
+ python -m pip install "parasolid-kit==0.1.0.dev4"
50
50
  ```
51
51
 
52
52
  Stable releases, once available, can be installed without `--pre`:
@@ -58,14 +58,14 @@ python -m pip install parasolid-kit
58
58
  Alternatively, install a downloaded wheel directly:
59
59
 
60
60
  ```bash
61
- python -m pip install /path/to/parasolid_kit-0.1.0.dev3-cp310-abi3-PLATFORM.whl
61
+ python -m pip install /path/to/parasolid_kit-0.1.0.dev4-cp310-abi3-PLATFORM.whl
62
62
  ```
63
63
 
64
64
  Header inspection works immediately after installation. Complete parsing and
65
65
  B-Rep checking also work without a schema file for the built-in
66
66
  `SCH_3000000_30000` profile (`verified_subset`): Onshape V30 text/neutral binary
67
- exports of single solid boxes, prisms, cylinders, and through-holes with zero
68
- user fields. Other keys and uncovered types require an explicit provider; see
67
+ exports of single solid boxes, prisms, cylinders, through-holes, spheres, and
68
+ verified solids with elliptical edges, with zero user fields. Other keys and uncovered types require an explicit provider; see
69
69
  [Schema catalogs](#schema-catalogs). STEP, CadQuery, and preview operations also
70
70
  require an optional runtime and geometry supported by that adapter.
71
71
 
@@ -211,14 +211,23 @@ python -m pip install .
211
211
 
212
212
  ## Schema catalogs
213
213
 
214
- The built-in `onshape-sch30000-r1` profile (revision 1, `verified_subset`) is
215
- selected when neither a provider nor a schema directory is supplied and the
216
- internal key is exactly `SCH_3000000_30000`. It needs no catalog, network, or
217
- CAD installation at runtime. [Profile provenance and coverage](docs/builtin-profiles.md)
218
- records its source, canonical hash, and validation scope.
214
+ When neither a provider nor a schema directory is supplied, default parsing
215
+ selects one of these compiled profiles by the exact internal stream key:
219
216
 
220
- Use an external catalog for other keys, including embedded-base keys, or for
221
- types outside the built-in subset. An explicit provider is authoritative: an
217
+ | Internal schema key | Built-in profile | Reviewed base layout |
218
+ |---|---|---|
219
+ | `SCH_3000000_30000` | `onshape-sch30000-r2` | Onshape V30, 24 types / 202 field groups |
220
+ | `SCH_1300000_13006` | `onshape-sch13006-r6` | Onshape V13, 40 types / 327 field groups |
221
+ | `SCH_3000310_30000_13006` | `icad-sch30000-13006-r5` | 13006 base plus trimmed curve (133): 30 types / 240 groups; embedded edits and full type 204 |
222
+
223
+ All have `verified_subset` coverage and require zero user fields. They need no
224
+ catalog, network, or CAD installation at runtime.
225
+ [Profile provenance and coverage](docs/builtin-profiles.md) records their sources,
226
+ canonical hashes, and validation limits. The iCAD profile accepts extracted
227
+ Parasolid streams; `.icd` container parsing remains outside this package.
228
+
229
+ Use an external catalog for other keys or types outside these subsets.
230
+ An explicit provider is authoritative: an
222
231
  empty provider or missing exact catalog fails without switching to the built-in
223
232
  profile. `schema_provider=None` means default selection. Nearby versions and
224
233
  the human-readable X_T common-header `SCH` value are never used as substitutes.
@@ -40,7 +40,11 @@ pub fn map_xb_brep_with_diagnostic_limit(
40
40
  BrepSourceFormat::Binary,
41
41
  document.schema_key.raw(),
42
42
  &document.nodes,
43
- RoleAccess::select(&document.schema_provider, document.schema_key.raw())?,
43
+ RoleAccess::select(
44
+ &document.schema_provider,
45
+ document.schema_key.raw(),
46
+ &document.schemas,
47
+ )?,
44
48
  )
45
49
  .with_max_diagnostics(max_diagnostics)
46
50
  .map()
@@ -71,7 +75,11 @@ pub fn map_xt_brep_with_diagnostic_limit(
71
75
  BrepSourceFormat::Text,
72
76
  document.schema_key.raw(),
73
77
  &document.nodes,
74
- RoleAccess::select(&document.schema_provider, document.schema_key.raw())?,
78
+ RoleAccess::select(
79
+ &document.schema_provider,
80
+ document.schema_key.raw(),
81
+ &document.schemas,
82
+ )?,
75
83
  )
76
84
  .with_max_diagnostics(max_diagnostics)
77
85
  .map()
@@ -472,40 +480,9 @@ impl<'a> Mapper<'a> {
472
480
  transverse_radius: self.positive_double(node, "transverse_radius")?,
473
481
  conjugate_radius: self.positive_double(node, "conjugate_radius")?,
474
482
  },
475
- "TRIMMED_CURVE" => CurveKind::Trimmed {
476
- basis_curve: self.required_id(
477
- node,
478
- "basis_curve",
479
- "curve geometry",
480
- &self.ids.curves,
481
- )?,
482
- start_point: self.vector(node, "point_1")?,
483
- end_point: self.vector(node, "point_2")?,
484
- start_parameter: self.double(node, "parm_1")?,
485
- end_parameter: self.double(node, "parm_2")?,
486
- },
483
+ "TRIMMED_CURVE" => self.trimmed_curve(node)?,
487
484
  "B_CURVE" => CurveKind::Nurbs(self.nurbs_curve(node)?),
488
- "SP_CURVE" => CurveKind::SurfaceParametric {
489
- surface: self.required_id(
490
- node,
491
- "surface",
492
- "surface geometry",
493
- &self.ids.surfaces,
494
- )?,
495
- parameter_curve: self.required_id(
496
- node,
497
- "b_curve",
498
- "curve geometry",
499
- &self.ids.curves,
500
- )?,
501
- original_curve: self.optional_id(
502
- node,
503
- "original",
504
- "curve geometry",
505
- &self.ids.curves,
506
- )?,
507
- tolerance_to_original: self.optional_double(node, "tolerance_to_original")?,
508
- },
485
+ "SP_CURVE" => self.surface_parametric_curve(node)?,
509
486
  "INTERSECTION" => self.intersection_curve(node)?,
510
487
  type_name => {
511
488
  let source = self.source(node);
@@ -659,7 +636,70 @@ impl<'a> Mapper<'a> {
659
636
  })
660
637
  }
661
638
 
639
+ #[allow(clippy::float_cmp)] // Equal parameters are forbidden, however short a valid trim is.
640
+ fn trimmed_curve(&self, node: &RawNode) -> Result<CurveKind, ParseError> {
641
+ let basis_curve =
642
+ self.required_id(node, "basis_curve", "curve geometry", &self.ids.curves)?;
643
+ let basis_index = self.required_pointer(node, "basis_curve")?;
644
+ if basis_index == node.index {
645
+ return Err(self.invalid_geometry(
646
+ node,
647
+ "basis_curve",
648
+ "trimmed curve references itself",
649
+ ));
650
+ }
651
+ if self.sense(node, "sense")? != Sense::Positive {
652
+ return Err(self.invalid_geometry(
653
+ node,
654
+ "sense",
655
+ "trimmed curve sense must be positive",
656
+ ));
657
+ }
658
+ let start_parameter = self.double(node, "parm_1")?;
659
+ let end_parameter = self.double(node, "parm_2")?;
660
+ let basis_sense = self.sense(self.node(basis_index)?, "sense")?;
661
+ if start_parameter == end_parameter
662
+ || (basis_sense == Sense::Positive && end_parameter < start_parameter)
663
+ || (basis_sense == Sense::Negative && end_parameter > start_parameter)
664
+ {
665
+ return Err(self.invalid_geometry(
666
+ node,
667
+ "parm_2",
668
+ "trim parameters must be distinct and follow the basis curve sense",
669
+ ));
670
+ }
671
+ Ok(CurveKind::Trimmed {
672
+ basis_curve,
673
+ start_point: self.vector(node, "point_1")?,
674
+ end_point: self.vector(node, "point_2")?,
675
+ start_parameter,
676
+ end_parameter,
677
+ })
678
+ }
679
+
662
680
  fn intersection_curve(&self, node: &RawNode) -> Result<CurveKind, ParseError> {
681
+ let chart = self.required_typed_node(node, "chart", "CHART")?;
682
+ self.double(chart, "base_parameter")?;
683
+ self.double(chart, "base_scale")?;
684
+ self.optional_double(chart, "chordal_error")?;
685
+ self.optional_double(chart, "angular_error")?;
686
+ let count = self.nonnegative_integer(chart, "chart_count")?;
687
+ self.intersection_points(chart, usize::try_from(count).unwrap_or(usize::MAX))?;
688
+ for field in ["start", "end"] {
689
+ let limit = self.required_typed_node(node, field, "LIMIT")?;
690
+ let count = match self.character(limit, "type")? {
691
+ b'H' | b'L' => 1,
692
+ b'T' => 2,
693
+ _ => {
694
+ return Err(self.invalid_geometry(
695
+ limit,
696
+ "type",
697
+ "invalid intersection limit kind",
698
+ ));
699
+ }
700
+ };
701
+ self.intersection_points(limit, count)?;
702
+ }
663
703
  let surface_indices = self.pointer_array(node, "surface")?;
664
704
  if surface_indices.len() != 2 {
665
705
  return Err(self.invalid_field(
@@ -684,12 +724,71 @@ impl<'a> Mapper<'a> {
684
724
  &self.ids.surfaces,
685
725
  )?,
686
726
  ];
727
+ let intersection_data = self.optional_source_if_field(node, "intersection_data")?;
728
+ if intersection_data.is_some() {
729
+ self.required_typed_node(node, "intersection_data", "INTERSECTION_DATA")?;
730
+ }
687
731
  Ok(CurveKind::Intersection {
688
732
  surfaces,
689
733
  chart: self.required_source(node, "chart")?,
690
734
  start: self.required_source(node, "start")?,
691
735
  end: self.required_source(node, "end")?,
692
- intersection_data: self.optional_source_if_field(node, "intersection_data")?,
736
+ intersection_data,
737
+ })
738
+ }
739
+
740
+ fn intersection_points(&self, node: &RawNode, count: usize) -> Result<(), ParseError> {
741
+ let values = &self.field(node, "hvec")?.values;
742
+ if count == 0 || values.len() != count {
743
+ return Err(self.invalid_geometry(node, "hvec", "intersection point count mismatch"));
744
+ }
745
+ for value in values {
746
+ match value {
747
+ FieldValue::IntersectionPoint([Some(x), Some(y), Some(z)])
748
+ if x.is_finite() && y.is_finite() && z.is_finite() => {}
749
+ _ => {
750
+ return Err(self.invalid_geometry(
751
+ node,
752
+ "hvec",
753
+ "intersection point is null or non-finite",
754
+ ));
755
+ }
756
+ }
757
+ }
758
+ Ok(())
759
+ }
760
+
761
+ fn surface_parametric_curve(&self, node: &RawNode) -> Result<CurveKind, ParseError> {
762
+ let parameter_curve = self.required_typed_node(node, "b_curve", "B_CURVE")?;
763
+ let nurbs = self.required_typed_node(parameter_curve, "nurbs", "NURBS_CURVE")?;
764
+ let rational = self.logical(nurbs, "rational")?;
765
+ let dimension = self.positive_short(nurbs, "vertex_dim")?;
766
+ if dimension != if rational { 3 } else { 2 } {
767
+ return Err(self.invalid_geometry(
768
+ nurbs,
769
+ "vertex_dim",
770
+ "surface parameter curve must have two coordinates plus an optional weight",
771
+ ));
772
+ }
773
+ let tolerance_to_original = self.optional_double(node, "tolerance_to_original")?;
774
+ if tolerance_to_original.is_some_and(|value| value < 0.0) {
775
+ return Err(self.invalid_geometry(node, "tolerance_to_original", "negative tolerance"));
776
+ }
777
+ Ok(CurveKind::SurfaceParametric {
778
+ surface: self.required_id(node, "surface", "surface geometry", &self.ids.surfaces)?,
779
+ parameter_curve: self.required_id(
780
+ node,
781
+ "b_curve",
782
+ "curve geometry",
783
+ &self.ids.curves,
784
+ )?,
785
+ original_curve: self.optional_id(
786
+ node,
787
+ "original",
788
+ "curve geometry",
789
+ &self.ids.curves,
790
+ )?,
791
+ tolerance_to_original,
693
792
  })
694
793
  }
695
794
 
@@ -720,28 +819,42 @@ impl<'a> Mapper<'a> {
720
819
  flat_vertices.len(),
721
820
  )?;
722
821
  let degree = self.positive_short(nurbs, "degree")?;
723
- let expected_expanded = u64::from(control_count) + u64::from(degree) + 1;
724
- let actual_expanded = multiplicities
725
- .iter()
726
- .map(|value| u64::from(*value))
727
- .sum::<u64>();
728
- if actual_expanded != expected_expanded {
822
+ let rational = self.logical(nurbs, "rational")?;
823
+ let periodic = self.logical(nurbs, "periodic")?;
824
+ let closed = self.logical(nurbs, "closed")?;
825
+ if control_count <= u32::from(degree)
826
+ || !(if rational { 3..=4 } else { 2..=3 }).contains(&vertex_dimension)
827
+ || (periodic && !closed)
828
+ {
729
829
  return Err(self.invalid_geometry(
730
830
  nurbs,
731
- "knot_mult",
732
- format!(
733
- "expanded knot count {actual_expanded} does not equal vertices + degree + 1 ({expected_expanded})"
734
- ),
831
+ "n_vertices",
832
+ "invalid NURBS dimensions or closure",
735
833
  ));
736
834
  }
835
+ if rational
836
+ && flat_vertices
837
+ .chunks(usize::from(vertex_dimension))
838
+ .any(|vertex| vertex.last().is_none_or(|weight| *weight <= 0.0))
839
+ {
840
+ return Err(self.invalid_geometry(nurbs, "vertices", "NURBS weights must be positive"));
841
+ }
842
+ self.validate_nurbs_knots(
843
+ nurbs,
844
+ "knots",
845
+ &knots,
846
+ &multiplicities,
847
+ control_count,
848
+ degree,
849
+ )?;
737
850
  Ok(NurbsCurve {
738
851
  degree,
739
852
  control_vertex_count: control_count,
740
853
  vertex_dimension,
741
854
  knot_type: self.byte(nurbs, "knot_type")?,
742
- periodic: self.logical(nurbs, "periodic")?,
743
- closed: self.logical(nurbs, "closed")?,
744
- rational: self.logical(nurbs, "rational")?,
855
+ periodic,
856
+ closed,
857
+ rational,
745
858
  curve_form: self.byte(nurbs, "curve_form")?,
746
859
  control_vertices: flat_vertices
747
860
  .chunks(usize::from(vertex_dimension))
@@ -792,8 +905,46 @@ impl<'a> Mapper<'a> {
792
905
  )?;
793
906
  let u_degree = self.positive_short(nurbs, "u_degree")?;
794
907
  let v_degree = self.positive_short(nurbs, "v_degree")?;
795
- self.validate_expanded_knots(nurbs, "u_knot_mult", &u_multiplicities, u_count, u_degree)?;
796
- self.validate_expanded_knots(nurbs, "v_knot_mult", &v_multiplicities, v_count, v_degree)?;
908
+ self.validate_nurbs_knots(
909
+ nurbs,
910
+ "u_knots",
911
+ &u_knots,
912
+ &u_multiplicities,
913
+ u_count,
914
+ u_degree,
915
+ )?;
916
+ self.validate_nurbs_knots(
917
+ nurbs,
918
+ "v_knots",
919
+ &v_knots,
920
+ &v_multiplicities,
921
+ v_count,
922
+ v_degree,
923
+ )?;
924
+ let rational = self.logical(nurbs, "rational")?;
925
+ let u_periodic = self.logical(nurbs, "u_periodic")?;
926
+ let v_periodic = self.logical(nurbs, "v_periodic")?;
927
+ let u_closed = self.logical(nurbs, "u_closed")?;
928
+ let v_closed = self.logical(nurbs, "v_closed")?;
929
+ if u_count <= u32::from(u_degree)
930
+ || v_count <= u32::from(v_degree)
931
+ || vertex_dimension != if rational { 4 } else { 3 }
932
+ || (u_periodic && !u_closed)
933
+ || (v_periodic && !v_closed)
934
+ {
935
+ return Err(self.invalid_geometry(
936
+ nurbs,
937
+ "vertices",
938
+ "invalid NURBS surface dimensions or closure",
939
+ ));
940
+ }
941
+ if rational
942
+ && flat_vertices
943
+ .chunks(usize::from(vertex_dimension))
944
+ .any(|vertex| vertex.last().is_none_or(|weight| *weight <= 0.0))
945
+ {
946
+ return Err(self.invalid_geometry(nurbs, "vertices", "NURBS weights must be positive"));
947
+ }
797
948
  Ok(NurbsSurface {
798
949
  u_degree,
799
950
  v_degree,
@@ -802,11 +953,11 @@ impl<'a> Mapper<'a> {
802
953
  vertex_dimension,
803
954
  u_knot_type: self.byte(nurbs, "u_knot_type")?,
804
955
  v_knot_type: self.byte(nurbs, "v_knot_type")?,
805
- u_periodic: self.logical(nurbs, "u_periodic")?,
806
- v_periodic: self.logical(nurbs, "v_periodic")?,
807
- u_closed: self.logical(nurbs, "u_closed")?,
808
- v_closed: self.logical(nurbs, "v_closed")?,
809
- rational: self.logical(nurbs, "rational")?,
956
+ u_periodic,
957
+ v_periodic,
958
+ u_closed,
959
+ v_closed,
960
+ rational,
810
961
  surface_form: self.byte(nurbs, "surface_form")?,
811
962
  control_vertices: flat_vertices
812
963
  .chunks(usize::from(vertex_dimension))
@@ -827,24 +978,60 @@ impl<'a> Mapper<'a> {
827
978
  })
828
979
  }
829
980
 
830
- fn validate_expanded_knots(
981
+ #[allow(clippy::too_many_arguments)]
982
+ fn validate_nurbs_knots(
831
983
  &self,
832
984
  node: &RawNode,
833
985
  field: &'static str,
986
+ knots: &[f64],
834
987
  multiplicities: &[u16],
835
- vertex_count: u32,
988
+ control_count: u32,
836
989
  degree: u16,
837
990
  ) -> Result<(), ParseError> {
838
- let expected = u64::from(vertex_count) + u64::from(degree) + 1;
839
- let actual = multiplicities
991
+ if knots.len() < 2
992
+ || knots.windows(2).any(|pair| pair[0] >= pair[1])
993
+ || multiplicities.iter().any(|&count| count > degree + 1)
994
+ || multiplicities[1..multiplicities.len() - 1]
995
+ .iter()
996
+ .any(|&count| count > degree)
997
+ {
998
+ return Err(self.invalid_geometry(
999
+ node,
1000
+ field,
1001
+ "invalid distinct knots or multiplicities",
1002
+ ));
1003
+ }
1004
+ let expected_expanded = u64::from(control_count) + u64::from(degree) + 1;
1005
+ let actual_expanded = multiplicities
840
1006
  .iter()
841
1007
  .map(|value| u64::from(*value))
842
1008
  .sum::<u64>();
843
- if actual != expected {
1009
+ if actual_expanded != expected_expanded {
1010
+ return Err(self.invalid_geometry(
1011
+ node,
1012
+ field,
1013
+ format!(
1014
+ "expanded knot count {actual_expanded} does not equal vertices + degree + 1 ({expected_expanded})"
1015
+ ),
1016
+ ));
1017
+ }
1018
+ // Only [t_degree, t_control_count] is fully defined. Find these knots
1019
+ // without allocating the expanded vector, including imaginary knots.
1020
+ let knot_at = |index: u64| {
1021
+ let mut end = 0_u64;
1022
+ knots
1023
+ .iter()
1024
+ .zip(multiplicities)
1025
+ .find_map(|(&knot, &count)| {
1026
+ end += u64::from(count);
1027
+ (index < end).then_some(knot)
1028
+ })
1029
+ };
1030
+ if knot_at(u64::from(degree)) >= knot_at(u64::from(control_count)) {
844
1031
  return Err(self.invalid_geometry(
845
1032
  node,
846
1033
  field,
847
- format!("expanded knot count {actual} does not equal {expected}"),
1034
+ "NURBS active parameter range must have positive length",
848
1035
  ));
849
1036
  }
850
1037
  Ok(())