feat: integrate SimpleCADAPI 2.0.2 CAD workflows
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
# Dimension
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class Dimension(length: int = 0, angle: int = 0)
|
||||
```
|
||||
|
||||
*Source: units.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import Dimension`
|
||||
|
||||
## Description
|
||||
|
||||
Physical dimension represented by integer length and angle exponents.
|
||||
@@ -0,0 +1,20 @@
|
||||
# DimensionTolerance
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class DimensionTolerance(lower_deviation: float, upper_deviation: float)
|
||||
```
|
||||
|
||||
*Source: expr.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import DimensionTolerance`
|
||||
|
||||
## Description
|
||||
|
||||
Permitted lower and upper deviations from a nominal dimension.
|
||||
|
||||
Deviations are signed: the lower deviation must be less than or equal to
|
||||
zero and the upper deviation must be greater than or equal to zero.
|
||||
@@ -21,6 +21,7 @@ Current design goals:
|
||||
- Translate only from the canonical low-level `graph` IR
|
||||
- Preserve node metadata and graph lineage as FreeCAD custom properties
|
||||
- Preserve `expression_graph` as explicit translator metadata
|
||||
- Preserve dimension tolerances and tolerance-chain requirements as metadata
|
||||
- Preserve exported assembly constraints as document metadata objects
|
||||
- Keep assembly metadata from the full model payload alongside the IR-driven
|
||||
geometry translation
|
||||
|
||||
@@ -16,16 +16,14 @@ class GraphSession(graph_id: Optional[str] = None)
|
||||
|
||||
Context manager that records CAD operations into a DAG.
|
||||
|
||||
with GraphSession() as session:
|
||||
n1 = record_operation(
|
||||
"make_line_redge", {"start": (0, 0, 0), "end": (1, 0, 0)}
|
||||
)
|
||||
n2 = record_operation(
|
||||
"make_line_redge", {"start": (1, 0, 0), "end": (1, 1, 0)}
|
||||
)
|
||||
record_operation(
|
||||
"make_wire_from_edges_rwire", {"edge_count": 2}, inputs=[n1, n2]
|
||||
)
|
||||
For new replayable model entry points, prefer `@scad.model`, which owns the
|
||||
session and returns a `ModelResult`. Use `GraphSession` directly for lower-level
|
||||
graph workflows. `result_node_ids` reports nodes selected by `capture_result`:
|
||||
|
||||
# Access the graph after the session
|
||||
print(session.graph.topological_order())
|
||||
```python
|
||||
with GraphSession(graph_id="demo") as session:
|
||||
body = make_box_rsolid(width=10.0, height=6.0, depth=2.0)
|
||||
session.capture_result(value=body)
|
||||
|
||||
print(session.result_node_ids)
|
||||
```
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# ModelResult
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
@dataclass(frozen=True)
|
||||
class ModelResult:
|
||||
value: Any
|
||||
session: GraphSession
|
||||
result_node_ids: Tuple[str, ...]
|
||||
model_json: str
|
||||
session_json: str
|
||||
artifact_paths: Mapping[str, Path] = field(default_factory=dict)
|
||||
```
|
||||
|
||||
*Source: graph.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import ModelResult`
|
||||
|
||||
## Description
|
||||
|
||||
Immutable result returned by a function decorated with `@scad.model`.
|
||||
It contains the ordinary Python return value, the completed graph session, the
|
||||
explicit result node ids, the model/session JSON artifacts, and any paths
|
||||
written by automatic artifact export.
|
||||
|
||||
```python
|
||||
result = build_model()
|
||||
rebuilt = result.replay()
|
||||
```
|
||||
|
||||
`replay()` replays `result.model_json`. Use `result.value` for the application
|
||||
return value and `result.result_node_ids` to inspect the captured graph outputs.
|
||||
Use `result.export_artifacts(output_dir=...)` to write one self-contained
|
||||
`<graph_id>.scene.zip` after the model runs. The package embeds model JSON,
|
||||
mapped project-relative Python sources, and render/selection assets; automatic
|
||||
export does not write adjacent model/session JSON, STEP, STL, or FCStd files.
|
||||
@@ -65,14 +65,38 @@ This index includes generated docs for the public SimpleCAD API surface, includi
|
||||
- [loft_rsolid](loft_rsolid.md) *(from operations.py)* `top-level`
|
||||
- [revolve_rsolid](revolve_rsolid.md) *(from operations.py)* `top-level`
|
||||
- [sweep_rsolid](sweep_rsolid.md) *(from operations.py)* `top-level`
|
||||
- [twisted_sweep_rsolid](twisted_sweep_rsolid.md) *(from operations.py)* `top-level`
|
||||
|
||||
## Tagging and Selection
|
||||
|
||||
- [apply_tag](apply_tag.md) *(from operations.py)* `top-level`
|
||||
- [apply_tag_rselection](apply_tag_rselection.md) *(from operations.py)* `top-level`
|
||||
- [explain_tag](explain_tag.md) *(from operations.py)* `top-level`
|
||||
- [list_tags](list_tags.md) *(from operations.py)* `top-level`
|
||||
- [select_edges_by_tag](select_edges_by_tag.md) *(from operations.py)* `top-level`
|
||||
- [select_faces_by_tag](select_faces_by_tag.md) *(from operations.py)* `top-level`
|
||||
|
||||
Creation-time topology-identity tags are supported by profile constructors and
|
||||
native feature primitives through `tag_prefix`. QL selectors also support `shared_boundary(...)`,
|
||||
`intersection(...)`, `incident_to(...)`, `incident_face_count(...)`, and
|
||||
`solids()` for relation-aware Edge selection.
|
||||
|
||||
## Unified Tag Contract
|
||||
|
||||
Topology identity and user semantics share one public tag model. Every binding
|
||||
is inspected with `list_tags(...)` and `explain_tag(...)` and queried with
|
||||
`ql.tag(...)`; one topology object may carry several tags for different uses.
|
||||
|
||||
`tag_prefix="housing"` creates topology-identity tags such as
|
||||
`housing.face.top` and `housing.solid`. These bindings carry `topology_name`
|
||||
evidence and project only when kernel history proves exact correspondence.
|
||||
|
||||
Role parameters such as `top_face_tag`, `side_faces_tag`, and
|
||||
`generated_faces_tag`, plus `result_tag`, create tags whose evidence identifies
|
||||
the kernel-proven role or result. Their tag text alone does not establish
|
||||
topology identity. There is one public tag parameter per target role, not a
|
||||
generic role-to-tag mapping.
|
||||
|
||||
## Boolean Operations
|
||||
|
||||
- [cut_rsolid](cut_rsolid.md) *(from operations.py)* `top-level`
|
||||
@@ -98,6 +122,10 @@ This index includes generated docs for the public SimpleCAD API surface, includi
|
||||
## Modeling Graph and Replay
|
||||
|
||||
- [GraphSession](GraphSession.md) *(from graph.py)* `top-level`
|
||||
- [ModelResult](ModelResult.md) *(from graph.py)* `top-level`
|
||||
- [capture_result](capture_result.md) *(from graph.py)* `top-level`
|
||||
- [model](model.md) *(from graph.py)* `top-level`
|
||||
- [requires_session](requires_session.md) *(from graph.py)* `top-level`
|
||||
- [export_graph_json](export_graph_json.md) *(from serializer.py)* `top-level`
|
||||
- [export_model_json](export_model_json.md) *(from serializer.py)* `top-level`
|
||||
- [export_session_json](export_session_json.md) *(from serializer.py)* `top-level`
|
||||
@@ -111,12 +139,34 @@ This index includes generated docs for the public SimpleCAD API surface, includi
|
||||
## Expressions and Parameters
|
||||
|
||||
- [Const](Const.md) *(from expr.py)* `top-level`
|
||||
- [DimensionTolerance](DimensionTolerance.md) *(from expr.py)* `top-level`
|
||||
- [Expr](Expr.md) *(from expr.py)* `top-level`
|
||||
- [ExpressionGraph](ExpressionGraph.md) *(from expr.py)* `top-level`
|
||||
- [ToleranceAnalysis](ToleranceAnalysis.md) *(from tolerance.py)* `top-level`
|
||||
- [ToleranceAnalysisError](ToleranceAnalysisError.md) *(from tolerance.py)* `top-level`
|
||||
- [ToleranceCheck](ToleranceCheck.md) *(from tolerance.py)* `top-level`
|
||||
- [ToleranceContribution](ToleranceContribution.md) *(from tolerance.py)* `top-level`
|
||||
- [ToleranceGraph](ToleranceGraph.md) *(from tolerance.py)* `top-level`
|
||||
- [ToleranceReport](ToleranceReport.md) *(from tolerance.py)* `top-level`
|
||||
- [ToleranceRequirement](ToleranceRequirement.md) *(from tolerance.py)* `top-level`
|
||||
- [ToleranceValidationError](ToleranceValidationError.md) *(from tolerance.py)* `top-level`
|
||||
- [Var](Var.md) *(from expr.py)* `top-level`
|
||||
- [analyze_tolerance](analyze_tolerance.md) *(from tolerance.py)* `top-level`
|
||||
- [check_tolerance](check_tolerance.md) *(from tolerance.py)* `top-level`
|
||||
- [const](const_function.md) *(from expr.py)* `top-level`
|
||||
- [var](var_function.md) *(from expr.py)* `top-level`
|
||||
|
||||
## Physical Units
|
||||
|
||||
- [Dimension](Dimension.md) *(from units.py)* `top-level`
|
||||
- [Unit](Unit.md) *(from units.py)* `top-level`
|
||||
- [UnitValidationError](UnitValidationError.md) *(from units.py)* `top-level`
|
||||
- [canonical_unit_for_dimension](canonical_unit_for_dimension.md) *(from units.py)* `top-level`
|
||||
- [convert_value](convert_value.md) *(from units.py)* `top-level`
|
||||
- [expression_uses_units](expression_uses_units.md) *(from units.py)* `top-level`
|
||||
- [get_unit](get_unit.md) *(from units.py)* `top-level`
|
||||
- [infer_dimension](infer_dimension.md) *(from units.py)* `top-level`
|
||||
|
||||
## Types and Errors
|
||||
|
||||
- [SimpleCADError](SimpleCADError.md) *(from errors.py)* `top-level`
|
||||
@@ -207,11 +257,15 @@ This index includes generated docs for the public SimpleCAD API surface, includi
|
||||
- [meta](meta.md) *(from ql.py)* `submodule:ql`
|
||||
- [not_](not_.md) *(from ql.py)* `submodule:ql`
|
||||
- [or_](or_.md) *(from ql.py)* `submodule:ql`
|
||||
- [output_role](output_role.md) *(from ql.py)* `submodule:ql`
|
||||
- [place_component_rassembly](place_component_rassembly.md) *(from operations.py)* `top-level`
|
||||
- [radial_pattern_rsolidlist](radial_pattern_rsolidlist.md) *(from operations.py)* `top-level`
|
||||
- [render_screenshot_rpath](render_screenshot_rpath.md) *(from operations.py)* `top-level`
|
||||
- [select](select.md) *(from ql.py)* `submodule:ql`
|
||||
- [solids](solids.md) *(from ql.py)* `submodule:ql`
|
||||
- [solve_assembly_constraints_rassembly](solve_assembly_constraints_rassembly.md) *(from operations.py)* `top-level`
|
||||
- [source_binding](source_binding.md) *(from ql.py)* `submodule:ql`
|
||||
- [source_topology](source_topology.md) *(from ql.py)* `submodule:ql`
|
||||
- [tag](tag.md) *(from ql.py)* `submodule:ql`
|
||||
- [unground_component_rassembly](unground_component_rassembly.md) *(from operations.py)* `top-level`
|
||||
- [value](value.md) *(from ql.py)* `submodule:ql`
|
||||
|
||||
@@ -22,3 +22,10 @@ Use `make_sketch_rsketch(...)`, `add_point_rsketch(...)`,
|
||||
profiles. Public sketch construction APIs are functional and return an
|
||||
updated `Sketch` document. The legacy `curves` constructor remains only for
|
||||
reading already-built wire/edge containers.
|
||||
|
||||
Entity IDs are creation-time local identifiers. Constrained profile promotion uses
|
||||
the ordered promotion map to bind each entity to exactly one generated Edge,
|
||||
creating `sketch.<sketch-name>.entity.<entity-id>` and
|
||||
`sketch.<sketch-name>.profile.<profile-id>` tags with `topology_name` evidence.
|
||||
Downstream features may project these tags only when their kernel history proves exact
|
||||
correspondence.
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# ToleranceAnalysis
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class ToleranceAnalysis(target_expr_id: str, method: ToleranceMethod, nominal: float, lower_bound: float, upper_bound: float, lower_deviation: float, upper_deviation: float, dimension: Dimension | None = None, unit: Unit | None = None, contributions: Tuple[ToleranceContribution, ...] = ())
|
||||
```
|
||||
|
||||
*Source: tolerance.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import ToleranceAnalysis`
|
||||
|
||||
## Description
|
||||
|
||||
Nominal value and propagated limits for an expression.
|
||||
@@ -0,0 +1,17 @@
|
||||
# ToleranceAnalysisError
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class ToleranceAnalysisError
|
||||
```
|
||||
|
||||
*Source: tolerance.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import ToleranceAnalysisError`
|
||||
|
||||
## Description
|
||||
|
||||
Raised when a tolerance chain cannot be propagated safely.
|
||||
@@ -0,0 +1,17 @@
|
||||
# ToleranceCheck
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class ToleranceCheck(requirement: ToleranceRequirement, analysis: ToleranceAnalysis, passed: bool, lower_margin: float, upper_margin: float)
|
||||
```
|
||||
|
||||
*Source: tolerance.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import ToleranceCheck`
|
||||
|
||||
## Description
|
||||
|
||||
Validation result for one tolerance requirement.
|
||||
@@ -0,0 +1,17 @@
|
||||
# ToleranceContribution
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class ToleranceContribution(variable_expr_id: str, variable_name: str, nominal: float, source_tolerance: DimensionTolerance, sensitivity: float | None, lower_deviation: float, upper_deviation: float, source_unit: Unit | None = None)
|
||||
```
|
||||
|
||||
*Source: tolerance.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import ToleranceContribution`
|
||||
|
||||
## Description
|
||||
|
||||
One source dimension's propagated contribution to a result.
|
||||
@@ -0,0 +1,17 @@
|
||||
# ToleranceGraph
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class ToleranceGraph(expression_graph: ExpressionGraph)
|
||||
```
|
||||
|
||||
*Source: tolerance.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import ToleranceGraph`
|
||||
|
||||
## Description
|
||||
|
||||
Tolerance requirements attached to one expression graph.
|
||||
@@ -0,0 +1,17 @@
|
||||
# ToleranceReport
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class ToleranceReport(checks: Tuple[ToleranceCheck, ...] = ())
|
||||
```
|
||||
|
||||
*Source: tolerance.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import ToleranceReport`
|
||||
|
||||
## Description
|
||||
|
||||
Validation report for every requirement in a tolerance graph.
|
||||
@@ -0,0 +1,17 @@
|
||||
# ToleranceRequirement
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class ToleranceRequirement(requirement_id: str, target_expr_id: str, tolerance: DimensionTolerance, method: ToleranceMethod = 'worst_case', name: str = '', tolerance_unit: Unit | None = None, target_dimension: Dimension | None = None)
|
||||
```
|
||||
|
||||
*Source: tolerance.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import ToleranceRequirement`
|
||||
|
||||
## Description
|
||||
|
||||
Permitted result deviations for one derived dimension.
|
||||
@@ -0,0 +1,17 @@
|
||||
# ToleranceValidationError
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class ToleranceValidationError(report: 'ToleranceReport')
|
||||
```
|
||||
|
||||
*Source: tolerance.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import ToleranceValidationError`
|
||||
|
||||
## Description
|
||||
|
||||
Raised when one or more declared tolerance requirements fail.
|
||||
@@ -0,0 +1,20 @@
|
||||
# Unit
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class Unit(symbol: str, dimension: Dimension, scale_to_canonical: float)
|
||||
```
|
||||
|
||||
*Source: units.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import Unit`
|
||||
|
||||
## Description
|
||||
|
||||
Named unit with a scale to SimpleCAD's canonical numeric units.
|
||||
|
||||
Custom units are supported and serialize their symbol, dimension, and scale.
|
||||
Registered built-in units serialize as compact symbols.
|
||||
@@ -0,0 +1,17 @@
|
||||
# UnitValidationError
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class UnitValidationError
|
||||
```
|
||||
|
||||
*Source: units.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import UnitValidationError`
|
||||
|
||||
## Description
|
||||
|
||||
Raised when units or expression dimensions are physically inconsistent.
|
||||
@@ -3,7 +3,7 @@
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class Var(name: str, default: float, comment: str | None = None, expr_id: str = field(default_factory=lambda : _make_expr_id('var')))
|
||||
class Var(name: str, default: float, comment: str | None = None, expr_id: str = field(default_factory=lambda : _make_expr_id('var')), tolerance: DimensionTolerance | None = None, unit: Unit | None = None, tolerance_unit: Unit | None = None)
|
||||
```
|
||||
|
||||
*Source: expr.py*
|
||||
@@ -14,4 +14,8 @@ class Var(name: str, default: float, comment: str | None = None, expr_id: str =
|
||||
|
||||
## Description
|
||||
|
||||
Named scalar parameter with a default fallback value.
|
||||
Named scalar parameter with optional physical-unit and tolerance intent.
|
||||
|
||||
``default`` and ``tolerance`` remain in their declared units. Evaluation,
|
||||
geometry parameters, and tolerance propagation convert them to SimpleCAD's
|
||||
canonical CAD units: millimeters for length and degrees for angle.
|
||||
|
||||
@@ -14,4 +14,6 @@ def add_line_rsketch(sketch: Sketch, entity_id: str, start: Union[SketchRef, str
|
||||
|
||||
## Description
|
||||
|
||||
Add a named line entity and return an updated sketch document.
|
||||
Add an identified line entity and return an updated sketch document. Its
|
||||
`entity_id` becomes the local segment of the canonical topology-identity tag for
|
||||
the corresponding promoted profile Edge.
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# analyze_tolerance
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def analyze_tolerance(value: ScalarLike, *, method: ToleranceMethod = 'worst_case') -> ToleranceAnalysis
|
||||
```
|
||||
|
||||
*Source: tolerance.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import analyze_tolerance`
|
||||
|
||||
## Description
|
||||
|
||||
Propagate source manufacturing tolerances through a scalar expression.
|
||||
|
||||
``worst_case`` returns guaranteed interval bounds. Affine chains are
|
||||
dependency-aware, so repeated variables such as ``x - x`` cancel exactly;
|
||||
nonlinear chains use conservative interval arithmetic. ``rss`` performs a
|
||||
first-order root-sum-square calculation using analytic sensitivities.
|
||||
|
||||
Unit-aware variables are converted to canonical CAD units before
|
||||
propagation. The returned analysis reports the inferred physical dimension
|
||||
and canonical result unit. Every variable in the expression must declare a
|
||||
source tolerance.
|
||||
@@ -14,10 +14,13 @@ def apply_tag(shape: AnyShape, tag: str) -> AnyShape
|
||||
|
||||
## Description
|
||||
|
||||
Attach a normalized tag to a shape using the standard propagation policy.
|
||||
Attach a normalized local user tag to a shape.
|
||||
|
||||
Tags must already be normalized lowercase tokens such as
|
||||
``role.mounting_surface`` or ``group.fasteners``. Propagation is intentionally
|
||||
not configurable from the public API; the default tag policy propagates
|
||||
semantic role/anchor/group tags downward and keeps topology-specific tags
|
||||
local.
|
||||
`role.mounting_surface` or `group.fasteners`. The default topology policy is
|
||||
`local` for every tag; token prefixes do not imply downward propagation.
|
||||
Lineage visibility is limited to proven continuation and fragment witnesses.
|
||||
|
||||
`apply_tag(...)` preserves its historical in-place wrapper behavior. Use
|
||||
`apply_tag_rselection(...)` when you need an independent semantic shape view,
|
||||
explicit topology propagation, or a replayable multi-entity assignment.
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# apply_tag_rselection
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def apply_tag_rselection(
|
||||
scope: AnyShape,
|
||||
targets: Union[ShapeSelector, Sequence[AnyShape]],
|
||||
tag: str,
|
||||
topology_propagation: str | TopologyPropagation = TopologyPropagation.LOCAL,
|
||||
lineage_policy: str | LineagePolicy = LineagePolicy.CONTINUATION_FRAGMENT,
|
||||
) -> AnyShape
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import apply_tag_rselection`
|
||||
|
||||
## Description
|
||||
|
||||
Return an independent semantic view over the same geometry with one canonical
|
||||
`TagBinding` attached to the selected entities. `targets` may be a serializable
|
||||
QL `ShapeSelector` or a non-empty sequence of topology objects belonging to
|
||||
`scope`.
|
||||
|
||||
Topology propagation defaults to `local`. Set `topology_propagation="downward"`
|
||||
only when descendants should inherit the binding. Lineage defaults to proven
|
||||
continuation and fragment derivations; it never makes lineage part of the
|
||||
`effective` scope.
|
||||
|
||||
Inside `GraphSession`, the operation records the complete binding, target intent,
|
||||
and selected-reference evidence. Replay re-resolves the target and checks that
|
||||
the evidence has not drifted. The semantic node does not replace geometry-owned
|
||||
topology references.
|
||||
@@ -0,0 +1,19 @@
|
||||
# canonical_unit_for_dimension
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def canonical_unit_for_dimension(dimension: Dimension) -> Unit
|
||||
```
|
||||
|
||||
*Source: units.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import canonical_unit_for_dimension`
|
||||
|
||||
## Description
|
||||
|
||||
Return the canonical unit used by CAD and tolerance calculations.
|
||||
|
||||
Length, area, volume, and angle use ``mm``, ``mm^2``, ``mm^3``, and ``deg``.
|
||||
@@ -0,0 +1,30 @@
|
||||
# capture_result
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def capture_result(*, value: Any) -> Any
|
||||
```
|
||||
|
||||
*Source: graph.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import capture_result`
|
||||
|
||||
## Description
|
||||
|
||||
Mark the graph nodes represented by `value` as explicit final outputs of the
|
||||
active model session. The value is returned unchanged. Explicit capture keeps
|
||||
intermediate/debug graph leaves out of `ModelResult.model_json`. Captured
|
||||
geometry/product values are also the inputs to automatic artifact export when
|
||||
the model decorator receives `export_dir=...`.
|
||||
|
||||
```python
|
||||
@scad.model(graph_id="demo")
|
||||
def build_demo():
|
||||
body = scad.make_box_rsolid(width=10.0, height=4.0, depth=2.0)
|
||||
return scad.capture_result(value=body)
|
||||
```
|
||||
|
||||
Call it inside `@model` or another active `GraphSession`.
|
||||
@@ -3,7 +3,14 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def chamfer_rsolid(solid: Solid, edges: Union[Sequence[Edge], ShapeSelector], distance: ScalarLike) -> Solid
|
||||
def chamfer_rsolid(
|
||||
solid: Solid,
|
||||
edges: Union[Sequence[Edge], ShapeSelector],
|
||||
distance: ScalarLike,
|
||||
*,
|
||||
result_tag: Optional[str] = None,
|
||||
generated_faces_tag: Optional[str] = None,
|
||||
) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -14,4 +21,10 @@ def chamfer_rsolid(solid: Solid, edges: Union[Sequence[Edge], ShapeSelector], di
|
||||
|
||||
## Description
|
||||
|
||||
Apply chamfers to selected solid edges.
|
||||
Apply chamfers to selected solid edges. `generated_faces_tag` targets every face
|
||||
with the kernel-proven `chamfer.patch` role. OCC contour expansion is included
|
||||
rather than treating only the seed edge as the feature boundary.
|
||||
|
||||
The operation fails if a requested patch role has no proven result. `result_tag`
|
||||
tags the resulting solid, and graph recording lowers assignments to replayable
|
||||
semantic nodes.
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# check_tolerance
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def check_tolerance(value: ScalarLike, tolerance: ToleranceLike, *, method: ToleranceMethod = 'worst_case', name: str | None = None, tolerance_unit: UnitLike | None = None) -> ToleranceCheck
|
||||
```
|
||||
|
||||
*Source: tolerance.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import check_tolerance`
|
||||
|
||||
## Description
|
||||
|
||||
Propagate and verify one Length or Angle requirement.
|
||||
|
||||
``tolerance_unit`` defaults to the target dimension's canonical unit. When
|
||||
provided, it must be dimensionally compatible and is converted before the
|
||||
comparison. Legacy unitless requirements remain supported when no unit is
|
||||
supplied.
|
||||
@@ -0,0 +1,17 @@
|
||||
# convert_value
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def convert_value(value: int | float, from_unit: UnitLike, to_unit: UnitLike) -> float
|
||||
```
|
||||
|
||||
*Source: units.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import convert_value`
|
||||
|
||||
## Description
|
||||
|
||||
Convert a finite numeric value between dimensionally compatible units.
|
||||
@@ -3,7 +3,11 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def cut_rsolid(*solids: Union[Solid, Sequence[Solid]], skip_non_intersecting: bool = True) -> Solid
|
||||
def cut_rsolid(
|
||||
*solids: Union[Solid, Sequence[Solid]],
|
||||
skip_non_intersecting: bool = True,
|
||||
tracking_policy: TrackingPolicy | str = TrackingPolicy.FULL,
|
||||
) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -29,6 +33,10 @@ sequences, and returns a single `Solid`.
|
||||
|
||||
- **Description**: When True, tools with no meaningful intersection are ignored for interactive convenience. Graph replay records this flag and should use False for strict diagnostic workflows.
|
||||
|
||||
### tracking_policy
|
||||
|
||||
- **Description**: `TrackingPolicy.FULL` computes topology history and lineage. `TrackingPolicy.GRAPH` preserves the canonical cut node, parameters, inputs, result topology references, and replay while omitting `TopoDelta` and history-derived topology lineage. Intersection validation and `skip_non_intersecting` behavior are unchanged.
|
||||
|
||||
## Returns
|
||||
|
||||
Solid: The cut result solid.
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# explain_tag
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def explain_tag(
|
||||
shape: AnyShape,
|
||||
tag: str,
|
||||
scope: str | TagScope = TagScope.EFFECTIVE,
|
||||
) -> List[Dict[str, Any]]
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import explain_tag`
|
||||
|
||||
## Description
|
||||
|
||||
Return every visible canonical binding that produces `tag` in the requested
|
||||
scope. Explanations preserve binding identity, producer, attachment, evidence,
|
||||
and policy-allowed lineage witnesses, so equal tag tokens from different
|
||||
producers remain distinguishable.
|
||||
|
||||
As with `list_tags(...)`, `effective` excludes lineage. A lineage explanation
|
||||
requires complete topology-history coverage.
|
||||
@@ -3,7 +3,12 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def export_model_json(session: 'GraphSession', indent: int = 2) -> str
|
||||
def export_model_json(
|
||||
session: 'GraphSession',
|
||||
indent: int = 2,
|
||||
*,
|
||||
result_node_ids: Optional[Sequence[str]] = None,
|
||||
) -> str
|
||||
```
|
||||
|
||||
*Source: serializer.py*
|
||||
@@ -20,3 +25,8 @@ Current Phase 1 scope uses the active session as the container of:
|
||||
- operation graph
|
||||
- expression graph
|
||||
- capabilities/schema metadata
|
||||
|
||||
When `result_node_ids` is omitted, explicitly captured session results are used
|
||||
when available; otherwise export falls back to graph leaves. New top-level model
|
||||
code normally reads `ModelResult.model_json` instead of calling this function
|
||||
directly.
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# expression_uses_units
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def expression_uses_units(value: 'ScalarLike') -> bool
|
||||
```
|
||||
|
||||
*Source: units.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import expression_uses_units`
|
||||
|
||||
## Description
|
||||
|
||||
Return whether an expression contains an explicit unit declaration.
|
||||
@@ -3,7 +3,17 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def extrude_rsolid(profile: Union[Wire, Face], direction: Tuple[float, float, float], distance: ScalarLike) -> Solid
|
||||
def extrude_rsolid(
|
||||
profile: Union[Wire, Face],
|
||||
direction: Tuple[float, float, float],
|
||||
distance: ScalarLike,
|
||||
*,
|
||||
tag_prefix: Optional[str] = None,
|
||||
result_tag: Optional[str] = None,
|
||||
start_face_tag: Optional[str] = None,
|
||||
end_face_tag: Optional[str] = None,
|
||||
side_faces_tag: Optional[str] = None,
|
||||
) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -14,4 +24,26 @@ def extrude_rsolid(profile: Union[Wire, Face], direction: Tuple[float, float, fl
|
||||
|
||||
## Description
|
||||
|
||||
Create a solid by extruding a profile.
|
||||
Create a solid by extruding a profile. Kernel history assigns the output roles
|
||||
`extrusion.start`, `extrusion.end`, and `extrusion.side`. Use the role tag
|
||||
arguments to attach tags to those exact role sets.
|
||||
|
||||
Use creation-time profile topology tags (`tag_prefix` and `edge_tags`) when
|
||||
durable feature Face/Edge identity is required. A tagged profile Edge yields a
|
||||
corresponding `<tag_prefix>.face.side.<edge_tag>` when OCC proves the generated
|
||||
side Face. Tagged Faces expose their effective tags to boundary Edge queries; use QL
|
||||
`incident_to(..., distinct=True)` or `shared_boundary(...)` to disambiguate
|
||||
an Edge by its two neighboring Faces.
|
||||
|
||||
Start and end roles require exactly one proven face. The side role requires one
|
||||
or more proven faces and tags all of them. Missing, ambiguous, or unsupported
|
||||
roles fail the whole operation instead of returning an untagged result.
|
||||
|
||||
`result_tag` attaches a local tag to the resulting solid. In a `GraphSession`,
|
||||
all requested tags lower to replayable `apply_tag_rselection` semantic nodes;
|
||||
they are not stored as geometry parameters.
|
||||
|
||||
Topology tags produced by `tag_prefix` remain directly queryable after
|
||||
booleans when OCC provides a complete preserved or modified Face history. The
|
||||
result binding retains the original semantic binding ID and source topology ID;
|
||||
new boundary faces do not inherit the topology tag.
|
||||
|
||||
@@ -3,7 +3,14 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def fillet_rsolid(solid: Solid, edges: Union[Sequence[Edge], ShapeSelector], radius: ScalarLike) -> Solid
|
||||
def fillet_rsolid(
|
||||
solid: Solid,
|
||||
edges: Union[Sequence[Edge], ShapeSelector],
|
||||
radius: ScalarLike,
|
||||
*,
|
||||
result_tag: Optional[str] = None,
|
||||
generated_faces_tag: Optional[str] = None,
|
||||
) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -14,4 +21,10 @@ def fillet_rsolid(solid: Solid, edges: Union[Sequence[Edge], ShapeSelector], rad
|
||||
|
||||
## Description
|
||||
|
||||
Apply fillets to selected solid edges.
|
||||
Apply fillets to selected solid edges. `generated_faces_tag` targets every face
|
||||
with the kernel-proven `fillet.patch` role. OCC contour expansion is included,
|
||||
so the role is not limited to the original seed edge.
|
||||
|
||||
The operation fails if a requested patch role has no proven result. `result_tag`
|
||||
tags the resulting solid. In a `GraphSession`, assignments are separate replayable
|
||||
semantic nodes with asserted user provenance.
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# get_unit
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def get_unit(value: UnitLike) -> Unit
|
||||
```
|
||||
|
||||
*Source: units.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import get_unit`
|
||||
|
||||
## Description
|
||||
|
||||
Resolve a built-in unit name/alias or return an existing ``Unit``.
|
||||
@@ -0,0 +1,21 @@
|
||||
# infer_dimension
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def infer_dimension(value: 'ScalarLike') -> Dimension | None
|
||||
```
|
||||
|
||||
*Source: units.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import infer_dimension`
|
||||
|
||||
## Description
|
||||
|
||||
Infer and validate an expression's result dimension.
|
||||
|
||||
``None`` means the expression uses only legacy variables without unit
|
||||
declarations. Expressions that contain explicit units are validated
|
||||
strictly and cannot mix in legacy variables.
|
||||
@@ -3,7 +3,10 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def list_tags(shape: AnyShape) -> List[str]
|
||||
def list_tags(
|
||||
shape: AnyShape,
|
||||
scope: str | TagScope = TagScope.EFFECTIVE,
|
||||
) -> List[str]
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -14,4 +17,12 @@ def list_tags(shape: AnyShape) -> List[str]
|
||||
|
||||
## Description
|
||||
|
||||
Return shape tags in deterministic sorted order.
|
||||
Return shape tags in deterministic sorted order for one semantic scope.
|
||||
|
||||
- `local`: bindings attached directly to the entity.
|
||||
- `inherited`: bindings visible through explicit downward topology propagation.
|
||||
- `effective`: local plus inherited bindings. Lineage is not included.
|
||||
- `lineage`: bindings visible through complete, policy-allowed topology history.
|
||||
|
||||
Lineage queries fail with a semantic capability error when complete topology
|
||||
history is unavailable; they do not guess from geometry or enumeration order.
|
||||
|
||||
@@ -3,7 +3,17 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def loft_rsolid(profiles: List[Wire], ruled: bool = False) -> Solid
|
||||
def loft_rsolid(
|
||||
profiles: List[Wire],
|
||||
ruled: bool = False,
|
||||
*,
|
||||
tracking_policy: TrackingPolicy | str = TrackingPolicy.FULL,
|
||||
tag_prefix: Optional[str] = None,
|
||||
result_tag: Optional[str] = None,
|
||||
start_face_tag: Optional[str] = None,
|
||||
end_face_tag: Optional[str] = None,
|
||||
side_faces_tag: Optional[str] = None,
|
||||
) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -14,4 +24,13 @@ def loft_rsolid(profiles: List[Wire], ruled: bool = False) -> Solid
|
||||
|
||||
## Description
|
||||
|
||||
Create a solid by lofting multiple profiles.
|
||||
Create a solid by lofting multiple profiles. Kernel history assigns
|
||||
`loft.start`, `loft.end`, and `loft.side` roles. Start and end tags require one
|
||||
proven face each; side tags apply to all proven side faces. `result_tag` targets
|
||||
the solid. Recorded assignments are replayable semantic nodes.
|
||||
|
||||
`TrackingPolicy.FULL` is the default and preserves complete kernel topology
|
||||
history. `TrackingPolicy.GRAPH` skips topology-history queries while still
|
||||
recording and replaying the `make_loft_rsolid` graph node. In `GRAPH` mode,
|
||||
`result_tag` remains available, but face-role tags and `tag_prefix` require
|
||||
`FULL` tracking.
|
||||
|
||||
@@ -3,7 +3,21 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_box_rsolid(width: ScalarLike, height: ScalarLike, depth: ScalarLike, bottom_face_center: Tuple[float, float, float] = (0, 0, 0)) -> Solid
|
||||
def make_box_rsolid(
|
||||
width: ScalarLike,
|
||||
height: ScalarLike,
|
||||
depth: ScalarLike,
|
||||
bottom_face_center: Tuple[float, float, float] = (0, 0, 0),
|
||||
*,
|
||||
tag_prefix: Optional[str] = None,
|
||||
result_tag: Optional[str] = None,
|
||||
bottom_face_tag: Optional[str] = None,
|
||||
top_face_tag: Optional[str] = None,
|
||||
front_face_tag: Optional[str] = None,
|
||||
back_face_tag: Optional[str] = None,
|
||||
left_face_tag: Optional[str] = None,
|
||||
right_face_tag: Optional[str] = None,
|
||||
) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -14,4 +28,12 @@ def make_box_rsolid(width: ScalarLike, height: ScalarLike, depth: ScalarLike, bo
|
||||
|
||||
## Description
|
||||
|
||||
Create a box solid.
|
||||
Create a native box with six exact kernel-backed Face roles: `box.bottom`,
|
||||
`box.top`, `box.front`, `box.back`, `box.left`, and `box.right`.
|
||||
`tag_prefix="housing"` creates `housing.solid` and corresponding
|
||||
`housing.face.<role>` topology-identity tags. Use the face tag arguments for
|
||||
role tags and `result_tag` for the Solid.
|
||||
|
||||
Box Edge roles are unsupported because the current OCP builder does not expose
|
||||
equivalent direct Edge witnesses. Select an exact Edge from two tagged incident
|
||||
Faces with QL `incident_to(..., distinct=True)` or `shared_boundary(...)`.
|
||||
|
||||
@@ -3,7 +3,13 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_circle_redge(center: Tuple[float, float, float], radius: ScalarLike, normal: Tuple[float, float, float] = (0, 0, 1)) -> Edge
|
||||
def make_circle_redge(
|
||||
center: Tuple[float, float, float],
|
||||
radius: ScalarLike,
|
||||
normal: Tuple[float, float, float] = (0, 0, 1),
|
||||
*,
|
||||
tag_prefix: Optional[str] = None,
|
||||
) -> Edge
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -14,4 +20,5 @@ def make_circle_redge(center: Tuple[float, float, float], radius: ScalarLike, no
|
||||
|
||||
## Description
|
||||
|
||||
Create a circular edge.
|
||||
Create a circular edge. `tag_prefix` optionally creates the topology tag
|
||||
`<tag_prefix>.edge`.
|
||||
|
||||
@@ -3,7 +3,14 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_circle_rface(center: Tuple[float, float, float], radius: ScalarLike, normal: Tuple[float, float, float] = (0, 0, 1)) -> Face
|
||||
def make_circle_rface(
|
||||
center: Tuple[float, float, float],
|
||||
radius: ScalarLike,
|
||||
normal: Tuple[float, float, float] = (0, 0, 1),
|
||||
*,
|
||||
tag_prefix: Optional[str] = None,
|
||||
edge_tag: Optional[str] = None,
|
||||
) -> Face
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -14,4 +21,8 @@ def make_circle_rface(center: Tuple[float, float, float], radius: ScalarLike, no
|
||||
|
||||
## Description
|
||||
|
||||
Create a circular face.
|
||||
Create a circular face. `tag_prefix` creates `<tag_prefix>.face`, while
|
||||
`edge_tag` supplies the final segment of `<tag_prefix>.edge.<edge_tag>` for its
|
||||
boundary Edge, or the complete Edge tag when `tag_prefix` is omitted. The Face
|
||||
topology tag is visible to effective boundary-Edge QL queries without copying
|
||||
arbitrary local Face tags to every Edge.
|
||||
|
||||
@@ -3,7 +3,14 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_circle_rwire(center: Tuple[float, float, float], radius: ScalarLike, normal: Tuple[float, float, float] = (0, 0, 1)) -> Wire
|
||||
def make_circle_rwire(
|
||||
center: Tuple[float, float, float],
|
||||
radius: ScalarLike,
|
||||
normal: Tuple[float, float, float] = (0, 0, 1),
|
||||
*,
|
||||
tag_prefix: Optional[str] = None,
|
||||
edge_tag: Optional[str] = None,
|
||||
) -> Wire
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -14,4 +21,8 @@ def make_circle_rwire(center: Tuple[float, float, float], radius: ScalarLike, no
|
||||
|
||||
## Description
|
||||
|
||||
Create a circular wire.
|
||||
Create a circular wire. `tag_prefix` creates `<tag_prefix>.wire`, while
|
||||
`edge_tag` supplies the final segment of `<tag_prefix>.edge.<edge_tag>` for its
|
||||
single circular Edge, or the complete Edge tag when `tag_prefix` is omitted.
|
||||
These topology tags are preserved only where a downstream operation has
|
||||
complete, kernel-proven correspondence.
|
||||
|
||||
@@ -3,7 +3,22 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_cone_rsolid(bottom_radius: ScalarLike, height: ScalarLike, top_radius: ScalarLike = 0.0, bottom_face_center: Tuple[float, float, float] = (0, 0, 0), axis: Tuple[float, float, float] = (0, 0, 1)) -> Solid
|
||||
def make_cone_rsolid(
|
||||
bottom_radius: ScalarLike,
|
||||
height: ScalarLike,
|
||||
top_radius: ScalarLike = 0.0,
|
||||
bottom_face_center: Tuple[float, float, float] = (0, 0, 0),
|
||||
axis: Tuple[float, float, float] = (0, 0, 1),
|
||||
*,
|
||||
tag_prefix: Optional[str] = None,
|
||||
result_tag: Optional[str] = None,
|
||||
start_face_tag: Optional[str] = None,
|
||||
end_face_tag: Optional[str] = None,
|
||||
side_face_tag: Optional[str] = None,
|
||||
start_edge_tag: Optional[str] = None,
|
||||
end_edge_tag: Optional[str] = None,
|
||||
seam_edge_tag: Optional[str] = None,
|
||||
) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -14,4 +29,12 @@ def make_cone_rsolid(bottom_radius: ScalarLike, height: ScalarLike, top_radius:
|
||||
|
||||
## Description
|
||||
|
||||
Create a cone or truncated cone solid.
|
||||
Create a native cone or frustum with direct kernel-backed `cone.start`,
|
||||
`cone.end`, and `cone.side` Face roles and `cone.start_boundary`,
|
||||
`cone.end_boundary`, and `cone.seam` Edge roles.
|
||||
|
||||
A pointed cone (`top_radius=0`) has no `cone.end` Face, so requesting that role
|
||||
fails. Its `cone.end_boundary` is the kernel's degenerate apex Edge. A frustum
|
||||
has all six roles. `tag_prefix="adapter"` creates corresponding
|
||||
`adapter.face.*` and `adapter.edge.*` topology-identity tags; use the role tag
|
||||
arguments for role assignments.
|
||||
|
||||
@@ -3,7 +3,21 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_cylinder_rsolid(radius: ScalarLike, height: ScalarLike, bottom_face_center: Tuple[float, float, float] = (0, 0, 0), axis: Tuple[float, float, float] = (0, 0, 1)) -> Solid
|
||||
def make_cylinder_rsolid(
|
||||
radius: ScalarLike,
|
||||
height: ScalarLike,
|
||||
bottom_face_center: Tuple[float, float, float] = (0, 0, 0),
|
||||
axis: Tuple[float, float, float] = (0, 0, 1),
|
||||
*,
|
||||
tag_prefix: Optional[str] = None,
|
||||
result_tag: Optional[str] = None,
|
||||
start_face_tag: Optional[str] = None,
|
||||
end_face_tag: Optional[str] = None,
|
||||
side_face_tag: Optional[str] = None,
|
||||
start_edge_tag: Optional[str] = None,
|
||||
end_edge_tag: Optional[str] = None,
|
||||
seam_edge_tag: Optional[str] = None,
|
||||
) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -14,4 +28,9 @@ def make_cylinder_rsolid(radius: ScalarLike, height: ScalarLike, bottom_face_cen
|
||||
|
||||
## Description
|
||||
|
||||
Create a cylinder solid.
|
||||
Create a native cylinder with kernel-backed topology tags. `tag_prefix="shaft"` produces
|
||||
`shaft.face.start`, `shaft.face.end`, `shaft.face.side`, and corresponding
|
||||
`shaft.edge.start`, `shaft.edge.end`, and `shaft.edge.seam` tags. Face tags
|
||||
are inherited by their boundary Edges. Role tags use the native
|
||||
`cylinder.*` Face/Edge roles. Use QL `incident_to(..., distinct=True)` or
|
||||
`shared_boundary(...)` to select an Edge from its two neighboring tagged Faces.
|
||||
|
||||
@@ -14,4 +14,12 @@ def make_face_from_sketch_rface(sketch: Sketch, profile: int | str = 0, *, requi
|
||||
|
||||
## Description
|
||||
|
||||
Promote a sketch profile to a concrete face, solving internally.
|
||||
Promote a sketch profile to a concrete face, solving internally. The Face and
|
||||
each boundary Edge receive canonical tags with `topology_name` evidence from the
|
||||
exact Sketch promotion map. For a Sketch with `name="rect"`, profile `bottom`,
|
||||
and entity ID `right`, the tags are `sketch.rect.profile.bottom` and
|
||||
`sketch.rect.entity.right`.
|
||||
|
||||
The bindings replay from the Sketch payload and promotion parameters. Legacy
|
||||
compatibility tags such as `sketch_entity.right` remain separate from the
|
||||
canonical topology-identity tags.
|
||||
|
||||
@@ -3,7 +3,12 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_face_from_wire_rface(wire: Wire, normal: Tuple[float, float, float] = (0, 0, 1)) -> Face
|
||||
def make_face_from_wire_rface(
|
||||
wire: Wire,
|
||||
normal: Tuple[float, float, float] = (0, 0, 1),
|
||||
*,
|
||||
tag_prefix: Optional[str] = None,
|
||||
) -> Face
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -14,4 +19,6 @@ def make_face_from_wire_rface(wire: Wire, normal: Tuple[float, float, float] = (
|
||||
|
||||
## Description
|
||||
|
||||
Create a face from a closed wire.
|
||||
Create a face from a closed wire. Existing proven Edge topology tags on the
|
||||
wire are copied to corresponding Face boundary Edges. `tag_prefix` optionally
|
||||
adds the Face tag `<tag_prefix>.face`.
|
||||
|
||||
@@ -3,7 +3,12 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_line_redge(start: Tuple[ScalarLike, ScalarLike, ScalarLike], end: Tuple[ScalarLike, ScalarLike, ScalarLike]) -> Edge
|
||||
def make_line_redge(
|
||||
start: Tuple[ScalarLike, ScalarLike, ScalarLike],
|
||||
end: Tuple[ScalarLike, ScalarLike, ScalarLike],
|
||||
*,
|
||||
tag_prefix: Optional[str] = None,
|
||||
) -> Edge
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -14,4 +19,7 @@ def make_line_redge(start: Tuple[ScalarLike, ScalarLike, ScalarLike], end: Tuple
|
||||
|
||||
## Description
|
||||
|
||||
Create a straight edge between two points.
|
||||
Create a straight edge between two points. When `tag_prefix` is provided, the
|
||||
edge receives the topology tag `<tag_prefix>.edge`. Downstream profile and
|
||||
feature operations may preserve that tag when kernel history proves the
|
||||
correspondence.
|
||||
|
||||
@@ -3,7 +3,15 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_rectangle_rface(width: ScalarLike, height: ScalarLike, center: Tuple[ScalarLike, ScalarLike, ScalarLike] = (0, 0, 0), normal: Tuple[ScalarLike, ScalarLike, ScalarLike] = (0, 0, 1)) -> Face
|
||||
def make_rectangle_rface(
|
||||
width: ScalarLike,
|
||||
height: ScalarLike,
|
||||
center: Tuple[ScalarLike, ScalarLike, ScalarLike] = (0, 0, 0),
|
||||
normal: Tuple[ScalarLike, ScalarLike, ScalarLike] = (0, 0, 1),
|
||||
*,
|
||||
tag_prefix: Optional[str] = None,
|
||||
edge_tags: Optional[Sequence[str]] = None,
|
||||
) -> Face
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -14,4 +22,9 @@ def make_rectangle_rface(width: ScalarLike, height: ScalarLike, center: Tuple[Sc
|
||||
|
||||
## Description
|
||||
|
||||
Create a rectangular face.
|
||||
Create a rectangular face. `tag_prefix` creates `<tag_prefix>.face`, and
|
||||
`edge_tags` supplies one tag for each of its four boundary Edges. With
|
||||
`tag_prefix`, each is a local segment under `<tag_prefix>.edge`; without it,
|
||||
each is a complete Edge tag. These topology tags can be projected to proven
|
||||
feature Faces and queried with the same `list_tags(...)` and `ql.tag(...)`
|
||||
surfaces as other tags.
|
||||
|
||||
@@ -3,7 +3,15 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_rectangle_rwire(width: ScalarLike, height: ScalarLike, center: Tuple[ScalarLike, ScalarLike, ScalarLike] = (0, 0, 0), normal: Tuple[ScalarLike, ScalarLike, ScalarLike] = (0, 0, 1)) -> Wire
|
||||
def make_rectangle_rwire(
|
||||
width: ScalarLike,
|
||||
height: ScalarLike,
|
||||
center: Tuple[ScalarLike, ScalarLike, ScalarLike] = (0, 0, 0),
|
||||
normal: Tuple[ScalarLike, ScalarLike, ScalarLike] = (0, 0, 1),
|
||||
*,
|
||||
tag_prefix: Optional[str] = None,
|
||||
edge_tags: Optional[Sequence[str]] = None,
|
||||
) -> Wire
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -14,4 +22,9 @@ def make_rectangle_rwire(width: ScalarLike, height: ScalarLike, center: Tuple[Sc
|
||||
|
||||
## Description
|
||||
|
||||
Create a rectangular wire.
|
||||
Create a rectangular wire. `tag_prefix` creates `<tag_prefix>.wire`.
|
||||
`edge_tags` must contain one tag for each generated profile Edge, in kernel
|
||||
construction order. With `tag_prefix`, each value is the local segment of
|
||||
`<tag_prefix>.edge.<edge_tag>`; without it, each value is the complete Edge tag.
|
||||
These topology tags are stable anchors for operations such as `extrude_rsolid`
|
||||
when correspondence is proven.
|
||||
|
||||
@@ -17,4 +17,5 @@ def make_sketch_rsketch(name: Optional[str] = None, *, plane: Any = 'XY', sketch
|
||||
Create an empty declarative sketch document.
|
||||
|
||||
Use this API, not concrete edge/wire constructors, when the intent is to
|
||||
build a sketch profile with constraints.
|
||||
build a sketch profile with constraints. The sketch name and explicit entity
|
||||
IDs are stable local identifiers used by constrained profile promotion.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_wire_from_edges_rwire(edges: List[Edge]) -> Wire
|
||||
def make_wire_from_edges_rwire(edges: List[Edge], *, tag_prefix: Optional[str] = None) -> Wire
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -14,4 +14,6 @@ def make_wire_from_edges_rwire(edges: List[Edge]) -> Wire
|
||||
|
||||
## Description
|
||||
|
||||
Create a wire from a list of connected edges.
|
||||
Create a wire from a list of connected edges. Existing proven Edge topology
|
||||
tags are preserved by exact topology identity; `tag_prefix` optionally adds
|
||||
`<tag_prefix>.wire` to the resulting wire.
|
||||
|
||||
@@ -14,4 +14,10 @@ def make_wire_from_sketch_rwire(sketch: Sketch, profile: int | str = 0, *, requi
|
||||
|
||||
## Description
|
||||
|
||||
Promote a sketch profile to a concrete wire, solving internally.
|
||||
Promote a sketch profile to a concrete wire, solving internally. The promotion
|
||||
map preserves exact ordered entity-to-Edge correspondence and creates tags with
|
||||
`topology_name` evidence, such as `sketch.rect.profile.bottom` and
|
||||
`sketch.rect.entity.right`.
|
||||
|
||||
Promotion fails when the generated Edge count does not match the promotion map;
|
||||
the SDK does not infer identity from geometry or enumeration heuristics.
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# model
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def model(
|
||||
func=None,
|
||||
*,
|
||||
graph_id: Optional[str] = None,
|
||||
export_dir: Optional[str | Path] = None,
|
||||
) -> Callable
|
||||
```
|
||||
|
||||
*Source: graph.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import model`
|
||||
|
||||
## Description
|
||||
|
||||
Decorate the single top-level entry point of a replayable model. Each invocation
|
||||
creates and owns exactly one `GraphSession`, activates it while the function
|
||||
runs, captures model/session JSON in memory, and returns a `ModelResult`. When
|
||||
`export_dir` is provided, explicitly captured geometry/product values produce
|
||||
one `<graph_id>.scene.zip` in that directory. The package embeds model JSON,
|
||||
mapped project-relative Python source files, and render/selection assets; it
|
||||
does not create adjacent model/session JSON, STEP, STL, or FCStd files.
|
||||
|
||||
```python
|
||||
@scad.model(graph_id="bracket")
|
||||
def build_bracket():
|
||||
body = scad.make_box_rsolid(width=20.0, height=10.0, depth=3.0)
|
||||
scad.capture_result(value=body)
|
||||
return body
|
||||
```
|
||||
|
||||
Use `result.artifact_paths["scene"]` to locate the package, or call
|
||||
`result.export_artifacts(output_dir=...)` after a model has run without an
|
||||
export directory. The explicit `export_dir` opt-in avoids unexpected filesystem
|
||||
writes for library callers and tests.
|
||||
|
||||
Do not nest `@model` functions or create another `GraphSession` inside a model
|
||||
function. Use `@requires_session` for child builders.
|
||||
@@ -0,0 +1,23 @@
|
||||
# output_role
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def output_role(role_name: str) -> SerializablePredicate
|
||||
```
|
||||
|
||||
*Source: ql.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- submodule: `from simplecadapi import ql`
|
||||
|
||||
## Description
|
||||
|
||||
Return a serializable predicate matching a kernel-proven operation output role
|
||||
in `metadata["track"]`. Role matching never falls back to face order, geometry,
|
||||
or flat tags. Use it with a typed selector, for example:
|
||||
|
||||
```python
|
||||
end = ql.faces().where(ql.output_role(role_name="extrusion.end")).exactly(1)
|
||||
```
|
||||
@@ -3,7 +3,7 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def render_screenshot_rpath(shapes: Union[Solid, Sequence[Solid]], output_path: str, highlight_tags: Optional[Sequence[str]] = None, tag_labels: Optional[Dict[str, str]] = None, image_size: Tuple[int, int] = (1400, 900), view: Union[Tuple[float, float], str] = 'auto', show_axes: bool = True, show_legend: bool = True, zoom: float = 4.0) -> str
|
||||
def render_screenshot_rpath(shapes: Union[Solid, Sequence[Solid]], output_path: str, highlight_tags: Optional[Sequence[str]] = None, tag_labels: Optional[Dict[str, str]] = None, image_size: Tuple[int, int] = (1400, 900), view: Union[Tuple[float, float], str] = 'auto', show_axes: bool = True, show_legend: bool = True, zoom: float = 4.0, show_callouts: bool = True) -> str
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -14,4 +14,6 @@ def render_screenshot_rpath(shapes: Union[Solid, Sequence[Solid]], output_path:
|
||||
|
||||
## Description
|
||||
|
||||
Render a screenshot of shapes and save it to a file.
|
||||
Render a screenshot of shapes and save it to a file. Set `show_callouts=False`
|
||||
to retain highlighted material colors and the legend without placing tag labels
|
||||
over the model.
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# requires_session
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def requires_session(func=None) -> Callable
|
||||
```
|
||||
|
||||
*Source: graph.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import requires_session`
|
||||
|
||||
## Description
|
||||
|
||||
Decorate a reusable graph-producing builder that must run inside the caller's
|
||||
active `GraphSession`. It reuses the session owned by the enclosing `@model`
|
||||
function and validates returned graph values against that session.
|
||||
|
||||
Calling a `@requires_session` builder without an active session raises
|
||||
`RuntimeError`. Builders must not create their own `GraphSession`.
|
||||
@@ -3,7 +3,18 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def revolve_rsolid(profile: Union[Wire, Face], axis: Tuple[float, float, float] = (0, 0, 1), angle: ScalarLike = 360, origin: Tuple[float, float, float] = (0, 0, 0)) -> Solid
|
||||
def revolve_rsolid(
|
||||
profile: Union[Wire, Face],
|
||||
axis: Tuple[float, float, float] = (0, 0, 1),
|
||||
angle: ScalarLike = 360,
|
||||
origin: Tuple[float, float, float] = (0, 0, 0),
|
||||
*,
|
||||
tag_prefix: Optional[str] = None,
|
||||
result_tag: Optional[str] = None,
|
||||
start_face_tag: Optional[str] = None,
|
||||
end_face_tag: Optional[str] = None,
|
||||
side_faces_tag: Optional[str] = None,
|
||||
) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -14,4 +25,10 @@ def revolve_rsolid(profile: Union[Wire, Face], axis: Tuple[float, float, float]
|
||||
|
||||
## Description
|
||||
|
||||
Create a solid by revolving a profile around an axis.
|
||||
Create a solid by revolving a profile around an axis. Kernel history assigns
|
||||
`revolution.start`, `revolution.end`, and `revolution.side` output roles.
|
||||
|
||||
Start and end roles require exactly one proven face; side tags apply to every
|
||||
proven side face. A full 360-degree revolve normally has no separate start or end
|
||||
face, so requesting either cap tag raises a capability error rather than guessing.
|
||||
`result_tag` tags the resulting solid. Role tags are replayable semantic nodes.
|
||||
|
||||
@@ -15,3 +15,15 @@ def select(items: Iterable[Any]) -> Query
|
||||
## Description
|
||||
|
||||
Start a QL query over a shape collection or selector scope.
|
||||
|
||||
For topology-aware selection, use `ql.faces()`, `ql.edges()`, `ql.wires()`,
|
||||
`ql.vertices()`, or `ql.solids()`. `ShapeSelector.intersection(other)` forms a
|
||||
serializable set intersection. `selector.shared_boundary(other,
|
||||
to_kind="edge")` intersects the boundaries of two selectors. Edge selectors
|
||||
also support `incident_to(face_selector, ..., distinct=True)` and
|
||||
`incident_face_count(exactly=2)` to select edges by exact incident Face
|
||||
witnesses and reject open or non-manifold edges.
|
||||
|
||||
These selectors resolve by topology identity, not enumeration order, area,
|
||||
normal, or position heuristics. Their `to_dict()` payloads can be restored with
|
||||
`ql.selector_from_dict(...)`.
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def select_edges_by_tag(shape: Union[Face, Solid], tag: str) -> List[Edge]
|
||||
def select_edges_by_tag(
|
||||
shape: Union[Face, Solid],
|
||||
tag: str,
|
||||
scope: str | TagScope = TagScope.EFFECTIVE,
|
||||
) -> List[Edge]
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -14,4 +18,6 @@ def select_edges_by_tag(shape: Union[Face, Solid], tag: str) -> List[Edge]
|
||||
|
||||
## Description
|
||||
|
||||
Select edges by tag.
|
||||
Select edges by an exact normalized tag in the requested semantic scope.
|
||||
`effective` does not include lineage; request `scope="lineage"` explicitly when
|
||||
selection depends on complete topology-history evidence.
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def select_faces_by_tag(solid: Solid, tag: str) -> List[Face]
|
||||
def select_faces_by_tag(
|
||||
solid: Solid,
|
||||
tag: str,
|
||||
scope: str | TagScope = TagScope.EFFECTIVE,
|
||||
) -> List[Face]
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -14,4 +18,6 @@ def select_faces_by_tag(solid: Solid, tag: str) -> List[Face]
|
||||
|
||||
## Description
|
||||
|
||||
Select faces by tag.
|
||||
Select faces by an exact normalized tag in the requested semantic scope.
|
||||
`effective` does not include lineage; request `scope="lineage"` explicitly when
|
||||
selection depends on complete topology-history evidence.
|
||||
|
||||
@@ -3,7 +3,17 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def shell_rsolid(solid: Solid, faces_to_remove: Union[Sequence[Face], ShapeSelector], thickness: ScalarLike) -> Solid
|
||||
def shell_rsolid(
|
||||
solid: Solid,
|
||||
faces_to_remove: Union[Sequence[Face], ShapeSelector],
|
||||
thickness: ScalarLike,
|
||||
*,
|
||||
result_tag: Optional[str] = None,
|
||||
body_faces_tag: Optional[str] = None,
|
||||
offset_faces_tag: Optional[str] = None,
|
||||
closing_faces_tag: Optional[str] = None,
|
||||
wall_edges_tag: Optional[str] = None,
|
||||
) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -14,4 +24,16 @@ def shell_rsolid(solid: Solid, faces_to_remove: Union[Sequence[Face], ShapeSelec
|
||||
|
||||
## Description
|
||||
|
||||
Shell a solid to create a hollow part.
|
||||
Shell a solid to create a hollow part. The operation can expose these exact
|
||||
kernel roles:
|
||||
|
||||
- `shell.body_face`: surviving or modified source body faces.
|
||||
- `shell.offset_face`: generated offset faces.
|
||||
- `shell.closing_descendant`: descendants of removed closing faces.
|
||||
- `shell.wall`: generated closing-boundary edges.
|
||||
|
||||
The named arguments map directly to those roles. A role is available only when
|
||||
OCC provides a complete witness;
|
||||
requesting an unavailable role fails instead of deriving one from enumeration or
|
||||
geometry. `result_tag` tags the resulting solid. Recorded assignments replay as
|
||||
semantic nodes and preserve face versus edge target kinds.
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# solids
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def solids() -> ShapeSelector
|
||||
```
|
||||
|
||||
*Source: ql.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- submodule: `from simplecadapi import ql`
|
||||
|
||||
## Description
|
||||
|
||||
Create a serializable selector over Solid topology. Solid selectors can be
|
||||
traversed to boundary Edges and combined with `intersection(...)` or
|
||||
`shared_boundary(...)` to find topology common to two named Solid selectors.
|
||||
@@ -0,0 +1,19 @@
|
||||
# source_binding
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def source_binding(binding_id: str) -> SerializablePredicate
|
||||
```
|
||||
|
||||
*Source: ql.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- submodule: `from simplecadapi import ql`
|
||||
|
||||
## Description
|
||||
|
||||
Match a local projected `TagBinding` whose topology-change evidence preserves the
|
||||
exact source `binding_id`. Objects without canonical local binding evidence raise
|
||||
an unsupported-query capability error instead of consulting flat tags.
|
||||
@@ -0,0 +1,19 @@
|
||||
# source_topology
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def source_topology(topo_id: str) -> SerializablePredicate
|
||||
```
|
||||
|
||||
*Source: ql.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- submodule: `from simplecadapi import ql`
|
||||
|
||||
## Description
|
||||
|
||||
Match a local projected `TagBinding` by the exact source topology identity stored
|
||||
in its kernel-history evidence. This predicate queries source-preserving evidence;
|
||||
it does not infer ancestry from geometry.
|
||||
@@ -3,7 +3,17 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def sweep_rsolid(profile: Face, path: Wire, is_frenet: bool = False) -> Solid
|
||||
def sweep_rsolid(
|
||||
profile: Face,
|
||||
path: Wire,
|
||||
is_frenet: bool = False,
|
||||
*,
|
||||
tag_prefix: Optional[str] = None,
|
||||
result_tag: Optional[str] = None,
|
||||
start_face_tag: Optional[str] = None,
|
||||
end_face_tag: Optional[str] = None,
|
||||
side_faces_tag: Optional[str] = None,
|
||||
) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -14,4 +24,10 @@ def sweep_rsolid(profile: Face, path: Wire, is_frenet: bool = False) -> Solid
|
||||
|
||||
## Description
|
||||
|
||||
Create a solid by sweeping a profile along a path.
|
||||
Create a solid by sweeping a profile along a path. Kernel history assigns
|
||||
`sweep.start`, `sweep.end`, and `sweep.side` roles. Start and end tags require one
|
||||
proven face each; side tags apply to all proven side faces. `result_tag` targets
|
||||
the solid, and recorded assignments are replayable semantic nodes.
|
||||
|
||||
Profiles with inner wires are rejected because the current PipeShell operation
|
||||
receives only the outer wire; silently dropping profile holes is not allowed.
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# twisted_sweep_rsolid
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def twisted_sweep_rsolid(
|
||||
profile: Face,
|
||||
distance: ScalarLike,
|
||||
twist_angle: ScalarLike,
|
||||
axis: Tuple[float, float, float] = (0.0, 0.0, 1.0),
|
||||
origin: Tuple[float, float, float] = (0.0, 0.0, 0.0),
|
||||
*,
|
||||
guide_radius: ScalarLike = 1.0,
|
||||
tag_prefix: Optional[str] = None,
|
||||
result_tag: Optional[str] = None,
|
||||
start_face_tag: Optional[str] = None,
|
||||
end_face_tag: Optional[str] = None,
|
||||
side_faces_tag: Optional[str] = None,
|
||||
) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- top-level: `from simplecadapi import twisted_sweep_rsolid`
|
||||
|
||||
## Description
|
||||
|
||||
Sweep a planar profile along a straight axis while rotating it linearly by the
|
||||
signed total `twist_angle` in degrees. The profile must lie at the sweep start,
|
||||
be planar, and be normal to `axis`.
|
||||
|
||||
The OCP implementation uses a one-edge straight spine and a one-edge
|
||||
cylindrical auxiliary spine. This normally creates one continuous side face per
|
||||
profile edge rather than splitting every side at intermediate loft sections.
|
||||
Profiles with inner wires are rejected.
|
||||
|
||||
Kernel history assigns `twisted_sweep.start`, `twisted_sweep.end`, and
|
||||
`twisted_sweep.side` roles. The operation records one canonical
|
||||
`make_twisted_sweep_rsolid` graph node containing `axis`, `origin`, `distance`,
|
||||
`twist_angle`, and `guide_radius`; strict replay invokes the same public
|
||||
operation with the recorded parameters.
|
||||
|
||||
`guide_radius` controls only the auxiliary orientation guide and must be a
|
||||
positive finite value. It does not set the swept profile radius.
|
||||
@@ -3,7 +3,13 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def union_rsolid(*solids: Union[Solid, Sequence[Solid]], clean: bool = True, glue: bool = _DEFAULT_UNION_GLUE, tol: Optional[float] = None) -> Solid
|
||||
def union_rsolid(
|
||||
*solids: Union[Solid, Sequence[Solid]],
|
||||
clean: bool = True,
|
||||
glue: bool = _DEFAULT_UNION_GLUE,
|
||||
tol: Optional[float] = None,
|
||||
tracking_policy: TrackingPolicy | str = TrackingPolicy.FULL,
|
||||
) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
@@ -40,6 +46,10 @@ returning multiple pieces.
|
||||
- **Type**: `Optional fuzzy-boolean tolerance used by the OCC union kernel. When`
|
||||
- **Description**: omitted, SimpleCAD chooses a conservative scale-aware tolerance.
|
||||
|
||||
### tracking_policy
|
||||
|
||||
- **Description**: `TrackingPolicy.FULL` computes topology history and lineage. `TrackingPolicy.GRAPH` preserves the canonical union node, parameters, inputs, result topology references, and replay while omitting `TopoDelta` and history-derived topology lineage. Geometry options `clean`, `glue`, and `tol` are unchanged.
|
||||
|
||||
## Returns
|
||||
|
||||
Solid: The merged union result.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def var(name: str, default: int | float, comment: str | None = None) -> Var
|
||||
def var(name: str, default: int | float, comment: str | None = None, tolerance: ToleranceLike | None = None, *, unit: UnitLike | None = None, tolerance_unit: UnitLike | None = None) -> Var
|
||||
```
|
||||
|
||||
*Source: expr.py*
|
||||
@@ -14,4 +14,13 @@ def var(name: str, default: int | float, comment: str | None = None) -> Var
|
||||
|
||||
## Description
|
||||
|
||||
Create a named variable node for v2 expression-driven parameters.
|
||||
Create a physical or legacy scalar variable.
|
||||
|
||||
``tolerance=0.1`` declares a symmetric ``+/-0.1`` tolerance. Use a
|
||||
``(lower_deviation, upper_deviation)`` pair for an asymmetric tolerance.
|
||||
``tolerance_unit`` defaults to ``unit`` when a nominal unit is declared.
|
||||
Values are converted to canonical CAD units only when evaluated, so the
|
||||
declaration and serialized expression node preserve the user's units.
|
||||
|
||||
Variables without ``unit`` retain legacy unitless behavior. A unit-aware
|
||||
expression cannot mix declared-unit variables with legacy variables.
|
||||
|
||||
+4
-4
@@ -33,7 +33,7 @@ SimpleCADAPI 的顶层 API 选择是正确的:它没有把用户拖进传统 C
|
||||
- historical scalar-field/SDF implementation, now removed from the active public/support surface
|
||||
- `docs/core/serialization/`
|
||||
- `docs/core/operation_graph_json_spec.md`
|
||||
- `examples/07_serialization_operation_tree.py`
|
||||
- retained replayable examples under `examples/`
|
||||
- `test/` 与 `tests/`
|
||||
|
||||
## 顶层 API 评价
|
||||
@@ -46,7 +46,7 @@ SimpleCADAPI 的顶层 API 选择是正确的:它没有把用户拖进传统 C
|
||||
|
||||
- `__init__.py` 明确导出建模函数、核心类型、graph/session、serializer、expression 和 `ql` 子模块,公开面可见性好。参考:`src/simplecadapi/__init__.py`。
|
||||
- 大多数核心创建函数遵守返回类型后缀,例如 `make_point_rvertex -> Vertex`、`make_line_redge -> Edge`、`make_circle_rwire -> Wire`、`make_box_rsolid -> Solid`。参考:`src/simplecadapi/operations.py:691`、`src/simplecadapi/operations.py:723`、`src/simplecadapi/operations.py:1465`。
|
||||
- composite convenience API 在 `GraphSession` 内降低为 canonical low-level graph,这个设计非常正确。用户调用 `make_box_rsolid`,图里记录 rectangle/profile/extrude,这是 CAD DSL 和 replay IR 分层的正确方式。参考:`src/simplecadapi/operations.py:1480`、`examples/07_serialization_operation_tree.py:52`、`examples/07_serialization_operation_tree.py:272`。
|
||||
- composite convenience API 在 `GraphSession` 内降低为 canonical low-level graph,这个设计非常正确。用户调用 `make_box_rsolid`,图里记录 rectangle/profile/extrude,这是 CAD DSL 和 replay IR 分层的正确方式。参考:`src/simplecadapi/operations.py` 与 `test/test_serialization.py`。
|
||||
- `union_rsolid(...)` 明确返回单个 `Solid`,失败就报错,不默默返回多个实体。这对机械 CAD 是正确的默认语义。参考:`src/simplecadapi/operations.py:2856`、`src/simplecadapi/operations.py:2876`。
|
||||
- `GraphSession` + `export_model_json` + `replay_model_json` 的主线是对的。它把脚本建模提升为可交换、可 replay 的模型记录。参考:`src/simplecadapi/graph.py:44`、`src/simplecadapi/serializer.py:416`、`src/simplecadapi/serializer.py:732`。
|
||||
- `ql` 被放到子模块而非全部塞进顶层,是正确的边界意识。参考:`src/simplecadapi/__init__.py`、`docs/api/README.md:5`。
|
||||
@@ -154,7 +154,7 @@ QL 是架构上最应该继续投资的部分。
|
||||
- `OperationGraph` 是 DAG,节点保存 op、params、param_exprs、inputs、context、semantic_delta、topo_delta、tags。结构完整。参考:`src/simplecadapi/topology.py:318`、`src/simplecadapi/topology.py:476`。
|
||||
- `export_model_json` 输出 graph、leaf_ids、expression_graph、frame_graph、registries、delta logs、canonical contract。方向很对。参考:`src/simplecadapi/serializer.py:627`。
|
||||
- `_assert_graph_is_canonical` 在 export model 前拒绝非 canonical ops,这是保持 IR 干净的正确动作。参考:`src/simplecadapi/serializer.py:246`、`src/simplecadapi/serializer.py:624`。
|
||||
- `examples/07_serialization_operation_tree.py` 非常有价值。它把 source API 到 operation tree 的映射讲清楚,是 SDK 最该保留和扩展的示例。参考:`examples/07_serialization_operation_tree.py:1`、`examples/07_serialization_operation_tree.py:217`。
|
||||
- `test/test_serialization.py` 系统覆盖 source API 到 operation tree 的映射,是该契约的主要回归入口。
|
||||
|
||||
严重问题:
|
||||
|
||||
@@ -164,7 +164,7 @@ QL 是架构上最应该继续投资的部分。
|
||||
- graph schema version 是 `1.0`,model schema 是 `2.0-draft`,canonical contract 是 `2.0-final-state`。这三个版本信号混在一起,会让外部消费者不知道哪个才是稳定承诺。参考:`src/simplecadapi/topology.py:19`、`src/simplecadapi/serializer.py:221`、`src/simplecadapi/serializer.py:628`。
|
||||
- node.context 和 frame_graph 被记录,但 replay 不恢复 frame/workplane;所有 ops 按当前 ambient world context 执行。参考:`src/simplecadapi/graph.py:132`、`src/simplecadapi/serializer.py:991`。
|
||||
- `leaf_ids` 自动来自 graph leaves,而不是用户显式声明的 final outputs。debug/intermediate 独立节点会成为 replay 输出。参考:`src/simplecadapi/serializer.py:624`、`src/simplecadapi/topology.py:563`。
|
||||
- expression replay 是 numeric snapshot,不是 parametric replay。这个可以接受,但必须在 SDK 主文档里反复强调。参考:`examples/07_serialization_operation_tree.py:40`、`examples/07_serialization_operation_tree.py:287`。
|
||||
- expression replay 是 numeric snapshot,不是 parametric replay。这个可以接受,但必须在 SDK 主文档里反复强调。参考:`test/test_serialization.py`。
|
||||
|
||||
建议:replay 应该默认 strict。所有 missing input、missing param、unknown op、leaf missing output、selection cardinality mismatch 都应该 hard failure。需要宽松模式时显式 `strict=False`。
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ SimpleWorkplane ← local modeling context
|
||||
- **Shape-first API**: users work with `Vertex`, `Edge`, `Wire`, `Face`, and `Solid`, not graph nodes.
|
||||
- **Functional modeling style**: public operations return new geometry values, e.g. `make_box_rsolid(...)`, `cut_rsolid(...)`, `fillet_rsolid(...)`.
|
||||
- **OCP-native runtime**: geometry construction, topology traversal, properties, booleans, transforms, and export use OCP/OpenCascade helpers.
|
||||
- **Replayable graph workflows**: `GraphSession` can record a canonical low-level operation graph and `export_model_json()` can serialize it for `replay_model_json()`.
|
||||
- **Replayable graph workflows**: `@scad.model` owns one `GraphSession` and returns a `ModelResult`; `@scad.requires_session` composes child builders, and `scad.capture_result()` selects canonical output nodes for replay and export.
|
||||
- **Tags and metadata**: tags are useful for lightweight semantics; structured numeric facts should be stored in metadata such as `metadata["geo"]`.
|
||||
- **Indexed topology access**: use plural methods such as `get_edges()` and `get_faces()` for enumeration, and pass an index to the same getter, such as `get_edges(index)` or `get_faces(index)`, for intentional indexed picks that should become graph selection nodes.
|
||||
|
||||
@@ -74,11 +74,14 @@ import simplecadapi as scad
|
||||
with scad.SimpleWorkplane(origin=(0, 0, 0)):
|
||||
box = scad.make_box_rsolid(width=5, height=3, depth=2)
|
||||
|
||||
scad.apply_tag(box, "role.bracket")
|
||||
scad.apply_tag(shape=box, tag="role.bracket")
|
||||
box.set_metadata("material", "6061-T6")
|
||||
box.auto_tag_faces("box")
|
||||
|
||||
top_faces = [face for face in box.get_faces() if "face.top" in scad.list_tags(face)]
|
||||
top_faces = [
|
||||
face for face in box.get_faces()
|
||||
if "face.top" in scad.list_tags(shape=face)
|
||||
]
|
||||
print(len(top_faces))
|
||||
```
|
||||
|
||||
@@ -87,13 +90,18 @@ print(len(top_faces))
|
||||
```python
|
||||
import simplecadapi as scad
|
||||
|
||||
with scad.GraphSession() as session:
|
||||
body = scad.make_box_rsolid(10, 10, 4)
|
||||
hole = scad.make_cylinder_rsolid(1.5, 8, bottom_face_center=(0, 0, -2))
|
||||
@scad.model(graph_id="drilled_block")
|
||||
def build_model():
|
||||
body = scad.make_box_rsolid(width=10, height=10, depth=4)
|
||||
hole = scad.make_cylinder_rsolid(
|
||||
radius=1.5, height=8, bottom_face_center=(0, 0, -2)
|
||||
)
|
||||
part = scad.cut_rsolid(body, hole)
|
||||
scad.capture_result(value=part)
|
||||
return part
|
||||
|
||||
payload = scad.export_model_json(session)
|
||||
rebuilt = scad.replay_model_json(payload)
|
||||
result = build_model()
|
||||
rebuilt = result.replay()
|
||||
print(len(rebuilt))
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# Dimension Tolerance Chains
|
||||
|
||||
SimpleCADAPI can attach manufacturing tolerances to declared dimension variables, propagate them through the expression DAG, and verify derived dimensions against design requirements.
|
||||
|
||||
This feature is separate from sketch-solver residual tolerances, boolean fuzzy tolerances, mesh resolution, and geometric fitting tolerances.
|
||||
|
||||
## Core Workflow
|
||||
|
||||
```python
|
||||
import simplecadapi as scad
|
||||
|
||||
housing = scad.var("housing", 100.0, unit="mm", tolerance=0.15)
|
||||
bearing = scad.var("bearing", 2.0, unit="cm", tolerance=(-0.04, 0.05), tolerance_unit="mm")
|
||||
spacer = scad.var("spacer", 79.4, unit="mm", tolerance=0.05)
|
||||
clearance = housing - bearing - spacer
|
||||
|
||||
worst_case = scad.analyze_tolerance(clearance, method="worst_case")
|
||||
rss = scad.analyze_tolerance(clearance, method="rss")
|
||||
|
||||
with scad.GraphSession() as session:
|
||||
body = scad.make_box_rsolid(housing, 10.0, 10.0)
|
||||
session.require_tolerance(
|
||||
clearance,
|
||||
(-0.25, 0.24),
|
||||
method="worst_case",
|
||||
name="axial_clearance",
|
||||
tolerance_unit="mm",
|
||||
)
|
||||
|
||||
report = session.validate_tolerances(raise_on_failure=True)
|
||||
model_json = scad.export_model_json(session)
|
||||
```
|
||||
|
||||
## Rules
|
||||
|
||||
- A scalar tolerance is symmetric: `0.1` means `-0.1/+0.1`.
|
||||
- A two-value sequence contains signed `(lower_deviation, upper_deviation)` values.
|
||||
- Every variable in a tolerance chain must declare a finite tolerance.
|
||||
- `tolerance_unit` defaults to `unit`; different nominal/tolerance units must
|
||||
have the same dimension.
|
||||
- Length and angle propagate in canonical `mm` and `deg` units.
|
||||
- Expression dimensions are inferred and validated before propagation.
|
||||
- Area and volume can be analyzed, while requirements currently accept final
|
||||
Length and Angle results only.
|
||||
- Tolerance sources are identified by `expr_id`, not by variable name.
|
||||
- `worst_case` uses exact dependency-aware affine propagation and conservative nonlinear interval propagation.
|
||||
- `rss` uses analytic first-order sensitivities, assumes distinct variables are independent, and combines repeated occurrences of the same variable before RSS.
|
||||
- Both methods reject a chain that is undefined anywhere inside its declared source intervals.
|
||||
- Export, import, replay, and FreeCAD translation validate stored requirements automatically.
|
||||
- Legacy model JSON without `tolerance_graph` imports with an empty graph.
|
||||
- Nominal replay continues to use numeric operation snapshots; tolerance analysis does not regenerate worst-case geometry.
|
||||
- Import recomputes dimensions and rejects tampered unit or target-dimension data.
|
||||
|
||||
Use `check_tolerance()` for a standalone non-raising check. Use `GraphSession.require_tolerance()` when the requirement must be serialized and enforced.
|
||||
|
||||
See the source-checkout guide at `docs/core/dimension-tolerance-chains.md` for propagation details, failure conditions, JSON shapes, and FreeCAD behavior.
|
||||
@@ -117,7 +117,7 @@ source API 名字不等于 canonical graph op 名字。Composite source API 可
|
||||
```json
|
||||
{
|
||||
"schema_version": "2.0",
|
||||
"producer_version": "2.0.1b1",
|
||||
"producer_version": "2.0.2",
|
||||
"capabilities": {
|
||||
"selection_ref_strategies": true,
|
||||
"geo_select_nodes": true,
|
||||
@@ -334,7 +334,7 @@ source API 名字不等于 canonical graph op 名字。Composite source API 可
|
||||
|
||||
- `make_*_face` 与 `make_face_from_wire` -> `Sketch`
|
||||
- `make_*_edge` / `make_*_wire` -> `Profile`
|
||||
- `make_extrude_rsolid` / `make_revolve_rsolid` / `make_loft_rsolid` / `make_sweep_rsolid` / `make_fillet_rsolid` / `make_chamfer_rsolid` / `make_shell_rsolid` / `make_cut_rsolid` / `make_union_rsolid` / `make_intersect_rsolid` -> `Feature`
|
||||
- `make_extrude_rsolid` / `make_revolve_rsolid` / `make_loft_rsolid` / `make_sweep_rsolid` / `make_twisted_sweep_rsolid` / `make_fillet_rsolid` / `make_chamfer_rsolid` / `make_shell_rsolid` / `make_cut_rsolid` / `make_union_rsolid` / `make_intersect_rsolid` -> `Feature`
|
||||
|
||||
## 7. Topology Delta Schema
|
||||
|
||||
@@ -936,6 +936,7 @@ New canonical profile nodes use the `make_*_r*` names listed in `canonical_contr
|
||||
- `make_revolve_rsolid`
|
||||
- `make_loft_rsolid`
|
||||
- `make_sweep_rsolid`
|
||||
- `make_twisted_sweep_rsolid`
|
||||
- `make_translate_rshape`
|
||||
- `make_rotate_rshape`
|
||||
- `make_mirror_rshape`
|
||||
@@ -1296,6 +1297,23 @@ Important:
|
||||
| --- | --- | --- |
|
||||
| `is_frenet` | `bool` | sweep orientation mode |
|
||||
|
||||
#### `make_twisted_sweep_rsolid`
|
||||
|
||||
- Inputs: 1 profile `Face`
|
||||
- Outputs: 1 `Solid`
|
||||
- Params:
|
||||
|
||||
| Key | Type | Meaning |
|
||||
| --- | --- | --- |
|
||||
| `axis` | 3-number array | sweep and rotation-axis direction in caller coordinates |
|
||||
| `origin` | 3-number array | sweep start and a point on the rotation axis |
|
||||
| `distance` | positive number | axial sweep distance |
|
||||
| `twist_angle` | number | signed total rotation in degrees |
|
||||
| `guide_radius` | positive number | internal auxiliary-spine radius |
|
||||
|
||||
Replay rebuilds the auxiliary spine deterministically inside the operation. It
|
||||
does not infer section counts or lower to transient loft nodes.
|
||||
|
||||
### 14.6 Boolean Ops
|
||||
|
||||
#### `make_union_rsolid`
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# Physical Units And Dimension Inference
|
||||
|
||||
Declare physical meaning on variables:
|
||||
|
||||
```python
|
||||
width = scad.var(
|
||||
"width",
|
||||
1.0,
|
||||
unit="in",
|
||||
tolerance=0.1,
|
||||
tolerance_unit="mm",
|
||||
)
|
||||
height = scad.var("height", 40.0, unit="mm", tolerance=0.2)
|
||||
diagonal = scad.sqrt(width**2 + height**2)
|
||||
```
|
||||
|
||||
## Canonical Rules
|
||||
|
||||
- Length, area, volume, and angle evaluate in `mm`, `mm^2`, `mm^3`, and `deg`.
|
||||
- Declaration values remain available on `Var.default` and `Var.tolerance`.
|
||||
- `tolerance_unit` defaults to `unit` and must have the same dimension.
|
||||
- Bindings use the variable's declaration unit.
|
||||
- Numeric constants are dimensionless coefficients; in addition/subtraction they
|
||||
are contextual offsets in the canonical result unit.
|
||||
|
||||
## Inference Rules
|
||||
|
||||
- Addition and subtraction require matching dimensions.
|
||||
- Multiplication/division add/subtract dimension exponents.
|
||||
- Constant integer powers multiply exponents.
|
||||
- Square root requires every exponent to be even.
|
||||
- `sin`, `cos`, and `tan` require Angle and return Dimensionless.
|
||||
- `asin`, `acos`, and `atan` require Dimensionless and return Angle.
|
||||
- `atan2` requires matching input dimensions and returns Angle.
|
||||
- Unit-declared variables cannot mix with legacy variables without units.
|
||||
|
||||
Built-in symbols are `1`, `%`, `mm`, `cm`, `m`, `in`, `ft`, their square/cubic
|
||||
forms, `deg`, and `rad`. Use `get_unit()`, `convert_value()`, and
|
||||
`infer_dimension()` for explicit inspection.
|
||||
|
||||
Area and volume can be inferred and analyzed. Persisted manufacturing requirements
|
||||
currently require a final Length or Angle result.
|
||||
|
||||
Read the source-checkout `docs/core/physical-units.md` for custom-unit JSON,
|
||||
complete operation tables, failure conditions, and legacy behavior.
|
||||
@@ -10,14 +10,19 @@ The long-form schema reference remains [`../operation_graph_json_spec.md`](../op
|
||||
import json
|
||||
import simplecadapi as scad
|
||||
|
||||
with scad.GraphSession() as session:
|
||||
body = scad.make_box_rsolid(10, 6, 2)
|
||||
hole = scad.make_cylinder_rsolid(1, 4, bottom_face_center=(0, 0, -1))
|
||||
@scad.model(graph_id="drilled_block")
|
||||
def build_model():
|
||||
body = scad.make_box_rsolid(width=10, height=6, depth=2)
|
||||
hole = scad.make_cylinder_rsolid(
|
||||
radius=1, height=4, bottom_face_center=(0, 0, -1)
|
||||
)
|
||||
result = scad.cut_rsolid(body, hole)
|
||||
scad.capture_result(value=result)
|
||||
return result
|
||||
|
||||
model_json = scad.export_model_json(session)
|
||||
payload = json.loads(model_json)
|
||||
rebuilt = scad.replay_model_json(model_json)
|
||||
model = build_model()
|
||||
payload = json.loads(model.model_json)
|
||||
rebuilt = model.replay()
|
||||
```
|
||||
|
||||
Inspect these fields:
|
||||
@@ -30,6 +35,16 @@ Inspect these fields:
|
||||
- `payload["leaf_ids"]`: explicit final result node ids.
|
||||
- `payload["expression_graph"]`: expression DAG used by expression-backed parameters.
|
||||
|
||||
For new top-level models, `ModelResult.model_json` is the preferred artifact
|
||||
accessor. Use `@scad.requires_session` for reusable builders and
|
||||
`scad.capture_result(...)` to keep intermediate graph leaves out of the model
|
||||
outputs. If a model invocation also needs durable CAD/viewer files, pass
|
||||
`export_dir=...` to `@scad.model`; its captured geometry/product values then
|
||||
produce one self-contained `<graph_id>.scene.zip`. It embeds model JSON, mapped
|
||||
project-relative Python sources, and evaluated render/selection assets; it does
|
||||
not create adjacent model/session JSON, STEP, STL, or FCStd files. No files are
|
||||
written when `export_dir` is omitted.
|
||||
|
||||
## Important rule: source API is not always graph API
|
||||
|
||||
Many user-facing functions are convenience APIs. During an active `GraphSession`, they lower to canonical low-level nodes:
|
||||
@@ -53,10 +68,10 @@ Many user-facing functions are convenience APIs. During an active `GraphSession`
|
||||
- [Features, booleans, transforms, patterns, and selectors](features-booleans-transforms.md)
|
||||
- [Expressions and replay behavior](expressions-and-replay.md)
|
||||
|
||||
## Example
|
||||
## Examples
|
||||
|
||||
See [`../../../examples/07_serialization_operation_tree.py`](../../../examples/07_serialization_operation_tree.py). It intentionally exercises every canonical core operation and writes:
|
||||
|
||||
- `examples/out/serialization_operation_tree.model.json`
|
||||
- `examples/out/serialization_operation_tree.summary.md`
|
||||
- `examples/out/serialization_operation_tree.step`
|
||||
The retained examples use the same model/session contract. See
|
||||
[`../../../examples/08_constrained_sketch.py`](../../../examples/08_constrained_sketch.py)
|
||||
for sketch promotion and replay, and
|
||||
[`../../../examples/10_part_assembly.py`](../../../examples/10_part_assembly.py)
|
||||
for product hierarchy and automatic artifact export.
|
||||
|
||||
+34
@@ -122,6 +122,40 @@ Replay effect:
|
||||
2. Replay path wire from input 1.
|
||||
3. Call `sweep_rsolid(profile, path, is_frenet=...)`.
|
||||
|
||||
## Twisted Sweep
|
||||
|
||||
Source:
|
||||
|
||||
```python
|
||||
profile = scad.make_rectangle_rface(width=2.0, height=1.0)
|
||||
solid = scad.twisted_sweep_rsolid(
|
||||
profile=profile,
|
||||
distance=8.0,
|
||||
twist_angle=30.0,
|
||||
)
|
||||
```
|
||||
|
||||
Serialized node:
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "make_twisted_sweep_rsolid",
|
||||
"params": {
|
||||
"axis": [0.0, 0.0, 1.0],
|
||||
"origin": [0.0, 0.0, 0.0],
|
||||
"distance": 8.0,
|
||||
"twist_angle": 30.0,
|
||||
"guide_radius": 1.0
|
||||
},
|
||||
"inputs": ["node_for_profile_face"],
|
||||
"output_count": 1
|
||||
}
|
||||
```
|
||||
|
||||
Replay reconstructs the continuous auxiliary-spine rotation law from the
|
||||
recorded parameters and invokes `twisted_sweep_rsolid(...)`. No sampled loft
|
||||
sections are stored or inferred.
|
||||
|
||||
## Helical sweep macro lowering
|
||||
|
||||
Source:
|
||||
|
||||
@@ -2,21 +2,24 @@
|
||||
|
||||
## Overview
|
||||
|
||||
`TaggedMixin` is the internal tag and metadata storage mixin used by `Vertex`, `Edge`, `Wire`, `Face`, and `Solid`. It owns the shared `_tags`, `_metadata`, and `_runtime` stores for topology wrappers.
|
||||
`TaggedMixin` is the internal semantic binding and metadata mixin used by topology wrappers. Canonical tag ownership is source-preserving `TagBinding` data. `_tags` is only an effective-scope compatibility cache; user code must not treat it as writable truth.
|
||||
|
||||
User code should not call member tag mutators. The public tag API is functional:
|
||||
|
||||
- `apply_tag(shape, tag)` attaches one normalized tag.
|
||||
- `list_tags(shape)` returns tags in deterministic sorted order.
|
||||
- `apply_tag_rselection(scope, targets, tag, ...)` returns an independent semantic view and exposes explicit propagation policies.
|
||||
- `list_tags(shape, scope=...)` returns tags in deterministic sorted order.
|
||||
- `explain_tag(shape, tag, scope=...)` preserves binding and producer evidence.
|
||||
- `select_faces_by_tag(...)`, `select_edges_by_tag(...)`, and QL predicates such as `ql.tag("role.*")` provide selection/query helpers.
|
||||
|
||||
## Tagging Mental Model
|
||||
|
||||
- Tags are normalized lowercase dot-separated semantic tokens.
|
||||
- Examples: `role.mounting_surface`, `anchor.datum.primary`, `group.fasteners`, `face.top`, `edge.boundary`, `wire.outer`, `solid.boolean.cut`.
|
||||
- `apply_tag(shape, tag)` does not expose propagation controls.
|
||||
- The standard policy propagates `role.*`, `anchor.*`, `group.*`, and a few legacy bare semantic tags downward.
|
||||
- Topology-specific tags such as `face.*`, `edge.*`, `wire.*`, `vertex.*`, and `solid.*` stay local.
|
||||
- New user assignments default to local topology propagation regardless of prefix.
|
||||
- Downward inheritance is explicit and computed dynamically; bindings are not copied into every child.
|
||||
- `effective` means local plus inherited and does not include lineage.
|
||||
- `lineage` requires complete topology history and only follows derivations allowed by the binding policy.
|
||||
- Numeric dimensions, measurements, and rich descriptive payloads belong in metadata, not tags.
|
||||
- Geometry builders store structured geometry facts under `metadata["geo"]`.
|
||||
|
||||
@@ -34,16 +37,29 @@ top_faces = [face for face in box.get_faces() if "face.top" in scad.list_tags(fa
|
||||
print(len(top_faces))
|
||||
```
|
||||
|
||||
## Propagation Example
|
||||
## Explicit Propagation Example
|
||||
|
||||
```python
|
||||
import simplecadapi as scad
|
||||
|
||||
body = scad.make_box_rsolid(10, 10, 2)
|
||||
scad.apply_tag(body, "role.mounting_plate")
|
||||
body = scad.make_box_rsolid(width=10, height=10, depth=2)
|
||||
tagged = scad.apply_tag_rselection(
|
||||
scope=body,
|
||||
targets=[body],
|
||||
tag="role.mounting_plate",
|
||||
topology_propagation=scad.TopologyPropagation.DOWNWARD,
|
||||
)
|
||||
|
||||
face_hits = [face for face in body.get_faces() if "role.mounting_plate" in scad.list_tags(face)]
|
||||
edge_hits = [edge for edge in body.get_edges() if "role.mounting_plate" in scad.list_tags(edge)]
|
||||
face_hits = scad.select_faces_by_tag(
|
||||
solid=tagged,
|
||||
tag="role.mounting_plate",
|
||||
scope=scad.TagScope.INHERITED,
|
||||
)
|
||||
edge_hits = scad.select_edges_by_tag(
|
||||
shape=tagged,
|
||||
tag="role.mounting_plate",
|
||||
scope=scad.TagScope.INHERITED,
|
||||
)
|
||||
|
||||
print(len(face_hits), len(edge_hits))
|
||||
```
|
||||
@@ -55,7 +71,13 @@ Primitives and modeling operations may attach normalized tags automatically:
|
||||
- Primitive tags such as `geom.primitive.box`, `geom.primitive.cylinder`, and `geom.primitive.sphere`.
|
||||
- Face tags from `auto_tag_faces(...)`, such as `face.top`, `face.bottom`, `face.side`, and `face.surface`.
|
||||
- Wire tags such as `wire.outer` and `wire.inner`.
|
||||
- Operation/tracking tags such as `solid.boolean.cut`, `op.cut.modified`, or `op.extrude.generated`.
|
||||
- Operation-level categorical tags such as `solid.boolean.cut` may remain local annotations.
|
||||
|
||||
Operation events and source roles are not tags. Proven `preserved`, `modified`,
|
||||
or `generated` events and `body`/`tool` origins live in typed
|
||||
`metadata["track"]`. Query them with `ql.operation_event(...)` and
|
||||
`ql.origin_role(...)`. Missing correspondence remains `coverage="partial"` and
|
||||
`status="unknown"`; it is never promoted to `generated` by default.
|
||||
|
||||
## Metadata Methods
|
||||
|
||||
@@ -84,7 +106,7 @@ scad.apply_tag(body, "role.mounting_plate")
|
||||
body.auto_tag_faces("box")
|
||||
|
||||
top_faces = Q.select(body.get_faces()).where(Q.tag("face.top")).all()
|
||||
role_faces = Q.select(body.get_faces()).where(Q.tag("role.*")).all()
|
||||
role_faces = Q.select(body.get_faces()).where(Q.tag("role.*", scope="effective")).all()
|
||||
|
||||
print(len(top_faces), len(role_faces))
|
||||
```
|
||||
|
||||
@@ -4,12 +4,12 @@ This index includes generated docs for standard part factory functions. Use thes
|
||||
|
||||
## Import Surfaces
|
||||
|
||||
- Recommended package-level module export: `import simplecadapi as scad`, then call functions through submodules such as `scad.std.gear.<function>(...)` and `scad.std.bearing.<function>(...)`.
|
||||
- Recommended package-level module export: `import simplecadapi as scad`, then call functions through submodules such as `scad.std.gear.<function>(...)`, `scad.std.fastener.<function>(...)`, and `scad.std.bearing.<function>(...)`.
|
||||
- Direct submodule import is also supported, for example `from simplecadapi.std.gear import make_spur_gear_rsolid` or `from simplecadapi.std.bearing import make_ball_bearing_rassembly`.
|
||||
|
||||
## Usage Guidance
|
||||
|
||||
- Prefer standard-library factories for standard bearings, gears, ring gears, and racks before hand-modeling profiles with core geometry APIs.
|
||||
- Prefer standard-library factories for standard bearings, fasteners, spur gears, straight bevel gears, ring gears, and racks before hand-modeling profiles with core geometry APIs.
|
||||
- Standard parts return normal SimpleCAD shapes or product assemblies, so they can be transformed, tagged, assembled, exported, or combined with core geometry operations.
|
||||
- Switch to core geometry APIs only when the requested standard part needs substantial custom geometry beyond the factory parameters.
|
||||
|
||||
@@ -23,6 +23,19 @@ This index includes generated docs for standard part factory functions. Use thes
|
||||
- [make_herringbone_gear_rsolid](make_herringbone_gear_rsolid.md) *(from std/gear.py)* `stdlib`
|
||||
- [make_spur_gear_rsolid](make_spur_gear_rsolid.md) *(from std/gear.py)* `stdlib`
|
||||
|
||||
## Bevel Gears
|
||||
|
||||
- [make_straight_bevel_gear_rsolid](make_straight_bevel_gear_rsolid.md) *(from std/gear.py)* `stdlib`
|
||||
|
||||
## Roller Chain
|
||||
|
||||
- [make_roller_chain_sprocket_rsolid](make_roller_chain_sprocket_rsolid.md) *(from std/chain.py)* `stdlib`
|
||||
|
||||
## Fasteners
|
||||
|
||||
- [make_bolt_rsolid](make_bolt_rsolid.md) *(from std/fastener.py)* `stdlib`
|
||||
- [make_nut_rsolid](make_nut_rsolid.md) *(from std/fastener.py)* `stdlib`
|
||||
|
||||
## Internal Ring Gears
|
||||
|
||||
- [make_helical_ring_gear_rsolid](make_helical_ring_gear_rsolid.md) *(from std/gear.py)* `stdlib`
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# make_bolt_rsolid
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_bolt_rsolid(
|
||||
diameter: float,
|
||||
length: float,
|
||||
head_style: str = "hex",
|
||||
thread_style: str = "auto",
|
||||
thread_detail: str = "modeled",
|
||||
thread_form: str = "v",
|
||||
thread_pitch: Optional[float] = None,
|
||||
thread_depth: Optional[float] = None,
|
||||
thread_length: Optional[float] = None,
|
||||
head_width: Optional[float] = None,
|
||||
head_height: Optional[float] = None,
|
||||
drive_style: str = "none",
|
||||
drive_size: Optional[float] = None,
|
||||
drive_depth: Optional[float] = None,
|
||||
underhead_fillet_radius: Optional[float] = None,
|
||||
) -> Solid
|
||||
```
|
||||
|
||||
*Source: std/fastener.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- standard library: `import simplecadapi as scad` then `scad.std.fastener.make_bolt_rsolid(...)`; direct submodule import: `from simplecadapi.std.fastener import make_bolt_rsolid`
|
||||
|
||||
## Description
|
||||
|
||||
Create a parameterized bolt along `+Z`, with the head underside on `Z=0`. Head styles are `hex`, `square`, `cylindrical`, `button`, and `countersunk`. Drive styles are `none`, `slot`, `cross`, and `hex_socket`.
|
||||
|
||||
Thread styles are `auto`, `full`, `partial`, and `none`. `auto` selects full thread for `length <= 3 * diameter`; otherwise it uses the ISO-style piecewise partial-thread length. The default `thread_detail="modeled"` creates a visible replayable helical `v` or `trapezoidal` thread. Set `thread_detail="cosmetic"` explicitly for a smooth shank with thread intent only in metadata. For partial threads, `thread_length` is measured back from the tip at `Z=length`.
|
||||
|
||||
For metric coarse-series diameters, the factory derives the default pitch from the standard series and records `d2 = d - 0.6495P` and `d1 = d - 1.0825P` in metadata. Hex-head defaults use catalog-like `S` and `k` values where available. The default underhead fillet is `0.06d`; `underhead_fillet_radius` can override it. The metadata also reports a minimum recommended mating-hole chamfer equal to the fillet radius.
|
||||
|
||||
Modeled threads may rotate the helical seam to an equivalent kernel-stable phase. The selected phase is recorded as `thread_phase_degrees` in `std.fastener.bolt` metadata and does not change thread dimensions or handedness.
|
||||
|
||||
Default dimensions are useful parametric proportions, not a claim of compliance with a specific ISO, DIN, ASME, or supplier catalog. Set catalog dimensions explicitly for released hardware.
|
||||
@@ -16,10 +16,9 @@ def make_helical_gear_rsolid(n_teeth: int, module: float, pressure_angle: float
|
||||
|
||||
Create an involute helical gear.
|
||||
|
||||
Non-zero helix angles are modeled as small-step ruled lofts through rotated
|
||||
copies of one profile. The small angular step keeps closed-wire section
|
||||
correspondence stable while ruled faces avoid smooth loft bulging in STEP
|
||||
exports.
|
||||
Non-zero helix angles use a continuous twisted sweep along the gear axis. An
|
||||
auxiliary-spine rotation law preserves the profile while generating one
|
||||
continuous side face per profile edge instead of one face per loft interval.
|
||||
|
||||
## Parameters
|
||||
|
||||
|
||||
+2
-4
@@ -16,10 +16,8 @@ def make_herringbone_gear_rsolid(n_teeth: int, module: float, pressure_angle: fl
|
||||
|
||||
Create an involute herringbone (double-helical) gear.
|
||||
|
||||
Each half is modeled as a small-step ruled loft through rotated copies of
|
||||
one profile, with a shared center section forming the herringbone ridge.
|
||||
This keeps closed-wire section correspondence stable while avoiding smooth
|
||||
loft bulging in STEP exports.
|
||||
Each half uses a continuous twisted sweep with opposite handedness. The two
|
||||
halves share the rotated center profile and are fused into one solid.
|
||||
|
||||
## Parameters
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# make_nut_rsolid
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_nut_rsolid(
|
||||
diameter: float,
|
||||
width: float,
|
||||
height: float,
|
||||
nut_style: str = "hex",
|
||||
hole_style: str = "through",
|
||||
thread_detail: str = "modeled",
|
||||
thread_form: str = "v",
|
||||
thread_pitch: Optional[float] = None,
|
||||
thread_depth: Optional[float] = None,
|
||||
hole_depth: Optional[float] = None,
|
||||
knurl_count: int = 24,
|
||||
) -> Solid
|
||||
```
|
||||
|
||||
*Source: std/fastener.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- standard library: `import simplecadapi as scad` then `scad.std.fastener.make_nut_rsolid(...)`; direct submodule import: `from simplecadapi.std.fastener import make_nut_rsolid`
|
||||
|
||||
## Description
|
||||
|
||||
Create a parameterized nut along `+Z`, with its bottom face on `Z=0`. Nut styles are `hex`, `square`, `round`, and `knurled`. Hole styles are `through` and `blind`; a blind hole opens from the top face and uses `hole_depth` as its axial depth.
|
||||
|
||||
The default `thread_detail="modeled"` creates replayable internal `v` or `trapezoidal` helical teeth. Set `thread_detail="cosmetic"` explicitly for a smooth major-diameter hole with thread intent only in metadata. For metric coarse-series diameters, the factory derives the default pitch and records `d2 = d - 0.6495P` and `d1 = d - 1.0825P` in metadata. `knurl_count` controls the lobe count of the printable knurled approximation.
|
||||
|
||||
Modeled threads may rotate the helical seam to an equivalent kernel-stable phase. The selected phase is recorded as `thread_phase_degrees` in `std.fastener.nut` metadata and does not change thread dimensions or handedness.
|
||||
|
||||
Default thread proportions are useful for parametric models, not a substitute for catalog tolerances, thread class, lead-in, prevailing-torque features, or manufacturing checks.
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
# make_roller_chain_sprocket_rsolid
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_roller_chain_sprocket_rsolid(n_teeth: int, chain_pitch: float, roller_diameter: float, sprocket_thickness: float, *, bore_radius: float = 0.0, roller_clearance: float = 0.15) -> Solid
|
||||
```
|
||||
|
||||
*Source: std/chain.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- standard library: `import simplecadapi as scad` then `scad.std.chain.make_roller_chain_sprocket_rsolid(...)`; direct submodule import: `from simplecadapi.std.chain import make_roller_chain_sprocket_rsolid`
|
||||
|
||||
## Description
|
||||
|
||||
Create a roller-chain sprocket from tooth count, chain pitch, roller diameter, and tooth-plate thickness. The pitch radius follows the regular pitch polygon. Circular roller seats are cut at every pitch point and opened through the engineering outside-diameter envelope.
|
||||
|
||||
The result preserves assembly-level engagement dimensions. Manufacturing release still requires the selected chain standard's permitted tooth-form range, tooth width, hub, material, heat treatment, runout, and supplier checks.
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
# make_straight_bevel_gear_rsolid
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_straight_bevel_gear_rsolid(n_teeth: int, module: float, pitch_angle: float = 45.0, pressure_angle: float = 20.0, face_width: float = 8.0, *, addendum_factor: float = 1.0, clearance_factor: float = 0.25, backlash: float = 0.0) -> Solid
|
||||
```
|
||||
|
||||
*Source: std/gear.py*
|
||||
|
||||
## Import Surface
|
||||
|
||||
- standard library: `import simplecadapi as scad` then `scad.std.gear.make_straight_bevel_gear_rsolid(...)`; direct submodule import: `from simplecadapi.std.gear import make_straight_bevel_gear_rsolid`
|
||||
|
||||
## Description
|
||||
|
||||
Create a straight bevel gear with standard metric tooth proportions. The large-end transverse section uses an analytic involute profile. A similar small-end section is placed on the pitch cone and connected with ruled straight tooth surfaces.
|
||||
|
||||
The returned solid contains nominal tooth geometry. Releasing a mating pair still requires mounting-distance, contact-pattern, backlash, material, heat-treatment, and strength checks.
|
||||
|
||||
## Parameters
|
||||
|
||||
- `n_teeth`: Number of teeth, at least 3.
|
||||
- `module`: Large-end transverse module in millimetres.
|
||||
- `pitch_angle`: Pitch-cone angle in degrees, greater than 0 and less than 90.
|
||||
- `pressure_angle`: Transverse pressure angle in degrees.
|
||||
- `face_width`: Tooth face width along the pitch-cone generator in millimetres; must be smaller than the outer pitch-cone distance.
|
||||
- `addendum_factor`: Large-end tooth addendum divided by module.
|
||||
- `clearance_factor`: Root clearance beyond the addendum divided by module.
|
||||
- `backlash`: Large-end circumferential tooth-thickness reduction at the pitch circle in millimetres.
|
||||
Reference in New Issue
Block a user