first commit
This commit is contained in:
+119
@@ -0,0 +1,119 @@
|
||||
# SimpleCAD API Index
|
||||
|
||||
This index includes API docs generated from `operations.py`, `evolve.py`, `constraints.py`, and `ql.py`.
|
||||
|
||||
## Basic Creation
|
||||
|
||||
- [make_angle_arc_redge](make_angle_arc_redge.md) *(from operations.py)*
|
||||
- [make_angle_arc_rwire](make_angle_arc_rwire.md) *(from operations.py)*
|
||||
- [make_box_rscalarfield](make_box_rscalarfield.md) *(from field.py)*
|
||||
- [make_box_rsolid](make_box_rsolid.md) *(from operations.py)*
|
||||
- [make_capsule_rscalarfield](make_capsule_rscalarfield.md) *(from field.py)*
|
||||
- [make_circle_redge](make_circle_redge.md) *(from operations.py)*
|
||||
- [make_circle_rface](make_circle_rface.md) *(from operations.py)*
|
||||
- [make_circle_rwire](make_circle_rwire.md) *(from operations.py)*
|
||||
- [make_cone_rsolid](make_cone_rsolid.md) *(from operations.py)*
|
||||
- [make_cylinder_rsolid](make_cylinder_rsolid.md) *(from operations.py)*
|
||||
- [make_ellipsoid_rscalarfield](make_ellipsoid_rscalarfield.md) *(from field.py)*
|
||||
- [make_face_from_wire_rface](make_face_from_wire_rface.md) *(from operations.py)*
|
||||
- [make_field_surface_rsolid](make_field_surface_rsolid.md) *(from operations.py)*
|
||||
- [make_helix_redge](make_helix_redge.md) *(from operations.py)*
|
||||
- [make_helix_rwire](make_helix_rwire.md) *(from operations.py)*
|
||||
- [make_line_redge](make_line_redge.md) *(from operations.py)*
|
||||
- [make_point_rvertex](make_point_rvertex.md) *(from operations.py)*
|
||||
- [make_polyline_rwire](make_polyline_rwire.md) *(from operations.py)*
|
||||
- [make_rectangle_rface](make_rectangle_rface.md) *(from operations.py)*
|
||||
- [make_rectangle_rwire](make_rectangle_rwire.md) *(from operations.py)*
|
||||
- [make_segment_redge](make_segment_redge.md) *(from operations.py)*
|
||||
- [make_segment_rwire](make_segment_rwire.md) *(from operations.py)*
|
||||
- [make_sphere_rscalarfield](make_sphere_rscalarfield.md) *(from field.py)*
|
||||
- [make_sphere_rsolid](make_sphere_rsolid.md) *(from operations.py)*
|
||||
- [make_spline_redge](make_spline_redge.md) *(from operations.py)*
|
||||
- [make_spline_rwire](make_spline_rwire.md) *(from operations.py)*
|
||||
- [make_three_point_arc_redge](make_three_point_arc_redge.md) *(from operations.py)*
|
||||
- [make_three_point_arc_rwire](make_three_point_arc_rwire.md) *(from operations.py)*
|
||||
- [make_wire_from_edges_rwire](make_wire_from_edges_rwire.md) *(from operations.py)*
|
||||
|
||||
## Transforms
|
||||
|
||||
- [mirror_shape](mirror_shape.md) *(from operations.py)*
|
||||
- [rotate_rscalarfield](rotate_rscalarfield.md) *(from field.py)*
|
||||
- [rotate_shape](rotate_shape.md) *(from operations.py)*
|
||||
- [translate_rscalarfield](translate_rscalarfield.md) *(from field.py)*
|
||||
- [translate_shape](translate_shape.md) *(from operations.py)*
|
||||
|
||||
## 3D Operations
|
||||
|
||||
- [extrude_rsolid](extrude_rsolid.md) *(from operations.py)*
|
||||
- [loft_rsolid](loft_rsolid.md) *(from operations.py)*
|
||||
- [revolve_rsolid](revolve_rsolid.md) *(from operations.py)*
|
||||
- [sweep_rsolid](sweep_rsolid.md) *(from operations.py)*
|
||||
|
||||
## Tagging and Selection
|
||||
|
||||
- [select_edges_by_tag](select_edges_by_tag.md) *(from operations.py)*
|
||||
- [select_faces_by_tag](select_faces_by_tag.md) *(from operations.py)*
|
||||
- [set_tag](set_tag.md) *(from operations.py)*
|
||||
|
||||
## Boolean Operations
|
||||
|
||||
- [cut_rsolidlist](cut_rsolidlist.md) *(from operations.py)*
|
||||
- [intersect_rscalarfield](intersect_rscalarfield.md) *(from field.py)*
|
||||
- [intersect_rsolidlist](intersect_rsolidlist.md) *(from operations.py)*
|
||||
- [union_rscalarfield](union_rscalarfield.md) *(from field.py)*
|
||||
- [union_rsolidlist](union_rsolidlist.md) *(from operations.py)*
|
||||
|
||||
## Export
|
||||
|
||||
- [export_step](export_step.md) *(from operations.py)*
|
||||
- [export_stl](export_stl.md) *(from operations.py)*
|
||||
|
||||
## Advanced Features
|
||||
|
||||
- [chamfer_rsolid](chamfer_rsolid.md) *(from operations.py)*
|
||||
- [fillet_rsolid](fillet_rsolid.md) *(from operations.py)*
|
||||
- [helical_sweep_rsolid](helical_sweep_rsolid.md) *(from operations.py)*
|
||||
- [shell_rsolid](shell_rsolid.md) *(from operations.py)*
|
||||
|
||||
## Evolve
|
||||
|
||||
- [make_n_hole_flange_rsolid](make_n_hole_flange_rsolid.md) *(from evolve.py)*
|
||||
- [make_naca_propeller_blade_rsolid](make_naca_propeller_blade_rsolid.md) *(from evolve.py)*
|
||||
- [make_threaded_rod_rsolid](make_threaded_rod_rsolid.md) *(from evolve.py)*
|
||||
|
||||
## Assembly Constraints
|
||||
|
||||
- [add_part_rassembly](add_part_rassembly.md) *(from constraints.py)*
|
||||
- [clear_constraints_rassembly](clear_constraints_rassembly.md) *(from constraints.py)*
|
||||
- [clone_assembly_rassembly](clone_assembly_rassembly.md) *(from constraints.py)*
|
||||
- [constrain_coincident_rassembly](constrain_coincident_rassembly.md) *(from constraints.py)*
|
||||
- [constrain_concentric_rassembly](constrain_concentric_rassembly.md) *(from constraints.py)*
|
||||
- [constrain_distance_rassembly](constrain_distance_rassembly.md) *(from constraints.py)*
|
||||
- [constrain_offset_rassembly](constrain_offset_rassembly.md) *(from constraints.py)*
|
||||
- [make_assembly_rassembly](make_assembly_rassembly.md) *(from constraints.py)*
|
||||
- [rotate_part_rassembly](rotate_part_rassembly.md) *(from constraints.py)*
|
||||
- [solve_assembly_rresult](solve_assembly_rresult.md) *(from constraints.py)*
|
||||
- [stack](stack.md) *(from constraints.py)*
|
||||
- [stack_rassembly](stack_rassembly.md) *(from constraints.py)*
|
||||
- [translate_part_rassembly](translate_part_rassembly.md) *(from constraints.py)*
|
||||
|
||||
## Other
|
||||
|
||||
- [and_](and_.md) *(from ql.py)*
|
||||
- [bounds_rbbox](bounds_rbbox.md) *(from field.py)*
|
||||
- [eval_rarray](eval_rarray.md) *(from field.py)*
|
||||
- [eval_rscalar](eval_rscalar.md) *(from field.py)*
|
||||
- [geo](geo.md) *(from ql.py)*
|
||||
- [linear_pattern_rsolidlist](linear_pattern_rsolidlist.md) *(from operations.py)*
|
||||
- [meta](meta.md) *(from ql.py)*
|
||||
- [not_](not_.md) *(from ql.py)*
|
||||
- [or_](or_.md) *(from ql.py)*
|
||||
- [radial_pattern_rsolidlist](radial_pattern_rsolidlist.md) *(from operations.py)*
|
||||
- [render_screenshot_rpath](render_screenshot_rpath.md) *(from operations.py)*
|
||||
- [scale_rscalarfield](scale_rscalarfield.md) *(from field.py)*
|
||||
- [select](select.md) *(from ql.py)*
|
||||
- [smooth_subtract_rscalarfield](smooth_subtract_rscalarfield.md) *(from field.py)*
|
||||
- [smooth_union_rscalarfield](smooth_union_rscalarfield.md) *(from field.py)*
|
||||
- [subtract_rscalarfield](subtract_rscalarfield.md) *(from field.py)*
|
||||
- [tag](tag.md) *(from ql.py)*
|
||||
- [value](value.md) *(from ql.py)*
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# add_part_rassembly
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def add_part_rassembly(assembly: Assembly, name: str, solid: Solid, parent: Optional[Union[str, PartHandle]] = None, local_transform: Optional[Union[np.ndarray, Sequence[Sequence[float]]]] = None) -> Assembly
|
||||
```
|
||||
|
||||
*Source: constraints.py*
|
||||
|
||||
## Description
|
||||
|
||||
Type-2 mapping: add a part in assembly space and return a new assembly.
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
# and_
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def and_(*predicates: Predicate) -> Predicate
|
||||
```
|
||||
|
||||
*Source: ql.py*
|
||||
|
||||
## Description
|
||||
|
||||
Build an AND-composed predicate.
|
||||
|
||||
Q.and_(Q.tag("face.top"), Q.tag("role.mounting_surface"))
|
||||
|
||||
## Parameters
|
||||
|
||||
### *predicates
|
||||
|
||||
- **Description**: Any number of predicates.
|
||||
|
||||
## Returns
|
||||
|
||||
Callable[[Any], bool]: Combined predicate.
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
# bounds_rbbox
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def bounds_rbbox(field: ScalarField) -> Tuple[Tuple[float, float, float], Tuple[float, float, float]]
|
||||
```
|
||||
|
||||
*Source: field.py*
|
||||
|
||||
## Description
|
||||
|
||||
Compute the axis-aligned bounding box of a scalar field.
|
||||
|
||||
## Parameters
|
||||
|
||||
### field
|
||||
|
||||
- **Description**: Scalar field.
|
||||
|
||||
## Returns
|
||||
|
||||
Tuple[min_xyz, max_xyz]: Bounding box.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# chamfer_rsolid
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def chamfer_rsolid(solid: Solid, edges: List[Edge], distance: float) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Apply chamfers to selected solid edges.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# clear_constraints_rassembly
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def clear_constraints_rassembly(assembly: Assembly) -> Assembly
|
||||
```
|
||||
|
||||
*Source: constraints.py*
|
||||
|
||||
## Description
|
||||
|
||||
Type-2 mapping: clear constraints and return a new assembly.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# clone_assembly_rassembly
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def clone_assembly_rassembly(assembly: Assembly) -> Assembly
|
||||
```
|
||||
|
||||
*Source: constraints.py*
|
||||
|
||||
## Description
|
||||
|
||||
Type-2 mapping: clone one assembly object into another.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# constrain_coincident_rassembly
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def constrain_coincident_rassembly(assembly: Assembly, reference: PointAnchor, moving: PointAnchor) -> Assembly
|
||||
```
|
||||
|
||||
*Source: constraints.py*
|
||||
|
||||
## Description
|
||||
|
||||
Type-2 mapping: add a coincident constraint and return a new assembly.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# constrain_concentric_rassembly
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def constrain_concentric_rassembly(assembly: Assembly, reference: AxisAnchor, moving: AxisAnchor, same_direction: bool = False) -> Assembly
|
||||
```
|
||||
|
||||
*Source: constraints.py*
|
||||
|
||||
## Description
|
||||
|
||||
Type-2 mapping: add a concentric constraint and return a new assembly.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# constrain_distance_rassembly
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def constrain_distance_rassembly(assembly: Assembly, reference: PointAnchor, moving: PointAnchor, distance: float, fallback_axis: AxisLike = 'x') -> Assembly
|
||||
```
|
||||
|
||||
*Source: constraints.py*
|
||||
|
||||
## Description
|
||||
|
||||
Type-2 mapping: add a point-distance constraint and return a new assembly.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# constrain_offset_rassembly
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def constrain_offset_rassembly(assembly: Assembly, reference: PointAnchor, moving: PointAnchor, distance: float, axis: AxisLike = 'z') -> Assembly
|
||||
```
|
||||
|
||||
*Source: constraints.py*
|
||||
|
||||
## Description
|
||||
|
||||
Type-2 mapping: add an axial offset constraint and return a new assembly.
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
# cut_rsolidlist
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def cut_rsolidlist(*solids: Union[Solid, Sequence[Solid]]) -> List[Solid]
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Compute the boolean difference of solids.
|
||||
|
||||
All boolean operations (union/cut/intersect) accept a mix of Solid and
|
||||
sequences; results are always returned as a list of Solid.
|
||||
`cut_rsolidlist(base, [tool_a, tool_b])` is valid input.
|
||||
If an earlier union returned multiple solids, keep that list and process each
|
||||
solid intentionally instead of collapsing it to `result[0]` without proof.
|
||||
If a later step truly requires one solid, first verify `len(results) == 1`.
|
||||
When a preceding union produced multiple tangent-only solids, adjust the part
|
||||
placement so the intended bodies overlap slightly, re-run the union, and only
|
||||
then unwrap the single result.
|
||||
|
||||
## Parameters
|
||||
|
||||
### solids
|
||||
|
||||
- **Description**: One or more Solid objects or sequences of Solid. Nested sequences are flattened before processing; the first solid is the base, the rest are subtracted in order.
|
||||
|
||||
## Returns
|
||||
|
||||
List[Solid]: A list containing the cut result solid, or an empty list when
|
||||
there is no valid input. The result is returned as a list for consistency
|
||||
with other boolean operations.
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1
|
||||
```python
|
||||
body = make_box_rsolid(12, 4, 4, bottom_face_center=(0, 0, 0))
|
||||
slot = make_box_rsolid(2, 2, 6, bottom_face_center=(2, 1, -1))
|
||||
relief = make_cylinder_rsolid(radius=0.8, height=6, center=(8, 2, 2))
|
||||
```
|
||||
|
||||
### Example 2
|
||||
```python
|
||||
results = cut_rsolidlist(body, [slot, relief])
|
||||
print(f"Cut result count: {len(results)}")
|
||||
```
|
||||
|
||||
### Example 3
|
||||
```python
|
||||
# If a previous union returned multiple solids, keep the list and cut each part.
|
||||
tangent_parts = union_rsolidlist(
|
||||
body,
|
||||
[
|
||||
make_sphere_rsolid(2.0, center=(-2.0, 2.0, 2.0)),
|
||||
make_sphere_rsolid(2.0, center=(14.0, 2.0, 2.0)),
|
||||
],
|
||||
)
|
||||
trimmed_parts = []
|
||||
for part in tangent_parts:
|
||||
trimmed_parts.extend(cut_rsolidlist(part, [slot, relief]))
|
||||
```
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
# eval_rarray
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def eval_rarray(field: ScalarField, xs: np.ndarray, ys: np.ndarray, zs: np.ndarray) -> np.ndarray
|
||||
```
|
||||
|
||||
*Source: field.py*
|
||||
|
||||
## Description
|
||||
|
||||
Evaluate a scalar field on arrays of points.
|
||||
|
||||
## Parameters
|
||||
|
||||
### field
|
||||
|
||||
- **Description**: Scalar field.
|
||||
|
||||
### xs
|
||||
|
||||
- **Description**: Array of X coordinates.
|
||||
|
||||
### ys
|
||||
|
||||
- **Description**: Array of Y coordinates.
|
||||
|
||||
### zs
|
||||
|
||||
- **Description**: Array of Z coordinates.
|
||||
|
||||
## Returns
|
||||
|
||||
np.ndarray: Array of field values.
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
# eval_rscalar
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def eval_rscalar(field: ScalarField, x: float, y: float, z: float) -> float
|
||||
```
|
||||
|
||||
*Source: field.py*
|
||||
|
||||
## Description
|
||||
|
||||
Evaluate a scalar field at a single point.
|
||||
|
||||
## Parameters
|
||||
|
||||
### field
|
||||
|
||||
- **Description**: Scalar field.
|
||||
|
||||
### x
|
||||
|
||||
- **Description**: X coordinate.
|
||||
|
||||
### y
|
||||
|
||||
- **Description**: Y coordinate.
|
||||
|
||||
### z
|
||||
|
||||
- **Description**: Z coordinate.
|
||||
|
||||
## Returns
|
||||
|
||||
float: Field value.
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
# export_step
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def export_step(shapes: Union[AnyShape, Sequence[AnyShape]], filename: str) -> None
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Export shapes to STEP.
|
||||
|
||||
Use this function when you want to export one shape or many shapes into the
|
||||
same STEP file. Passing `List[Solid]` is valid and often preferred when a
|
||||
previous boolean operation returned multiple solids.
|
||||
|
||||
## Parameters
|
||||
|
||||
### shapes
|
||||
|
||||
- **Description**: A single exportable shape or any nested sequence of exportable shapes. Lists of Solid are supported directly, including list results returned by boolean operations.
|
||||
|
||||
### filename
|
||||
|
||||
- **Description**: Output STEP file path.
|
||||
|
||||
## Returns
|
||||
|
||||
None: Writes the provided shapes into one STEP file.
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1
|
||||
```python
|
||||
main_body = make_box_rsolid(10, 4, 4, bottom_face_center=(0, 0, 0))
|
||||
left_cap = make_sphere_rsolid(2.0, center=(-2.0, 2.0, 2.0))
|
||||
right_cap = make_sphere_rsolid(2.0, center=(12.0, 2.0, 2.0))
|
||||
body_parts = union_rsolidlist(main_body, [left_cap, right_cap])
|
||||
```
|
||||
|
||||
### Example 2
|
||||
```python
|
||||
# Export the full list directly; no need to collapse to body_parts[0].
|
||||
export_step(body_parts, "rounded_bar.step")
|
||||
```
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
# export_stl
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def export_stl(shapes: Union[AnyShape, Sequence[AnyShape]], filename: str) -> None
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Export shapes to STL.
|
||||
|
||||
Use this function when you want to export one solid or many solids/faces into
|
||||
the same STL file. Passing `List[Solid]` is valid and often preferred when a
|
||||
previous boolean operation returned multiple solids.
|
||||
|
||||
## Parameters
|
||||
|
||||
### shapes
|
||||
|
||||
- **Description**: A single Solid or Face, or any nested sequence of Solid/Face. Lists of Solid are supported directly, including list results returned by boolean operations.
|
||||
|
||||
### filename
|
||||
|
||||
- **Description**: Output STL file path.
|
||||
|
||||
## Returns
|
||||
|
||||
None: Writes the provided shapes into one STL file.
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1
|
||||
```python
|
||||
main_body = make_box_rsolid(10, 4, 4, bottom_face_center=(0, 0, 0))
|
||||
left_cap = make_sphere_rsolid(2.0, center=(-2.0, 2.0, 2.0))
|
||||
right_cap = make_sphere_rsolid(2.0, center=(12.0, 2.0, 2.0))
|
||||
body_parts = union_rsolidlist(main_body, [left_cap, right_cap])
|
||||
```
|
||||
|
||||
### Example 2
|
||||
```python
|
||||
# Export the list result directly.
|
||||
export_stl(body_parts, "rounded_bar.stl")
|
||||
```
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# extrude_rsolid
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def extrude_rsolid(profile: Union[Wire, Face], direction: Tuple[float, float, float], distance: float) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a solid by extruding a profile.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# fillet_rsolid
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def fillet_rsolid(solid: Solid, edges: List[Edge], radius: float) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Apply fillets to selected solid edges.
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
# geo
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def geo(field: str, default: Any = None) -> KeyFn
|
||||
```
|
||||
|
||||
*Source: ql.py*
|
||||
|
||||
## Description
|
||||
|
||||
Convenience builder for a `geo` metadata getter.
|
||||
|
||||
Q.select(items).order_by(Q.geo("height"))
|
||||
|
||||
## Parameters
|
||||
|
||||
### field
|
||||
|
||||
- **Description**: `geo` field name, such as `type` or `height`.
|
||||
|
||||
### default
|
||||
|
||||
- **Description**: Default value when lookup fails.
|
||||
|
||||
## Returns
|
||||
|
||||
Callable[[Any], Any]: Getter function.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# helical_sweep_rsolid
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def helical_sweep_rsolid(profile: Wire, pitch: float, height: float, radius: float, center: Tuple[float, float, float] = (0, 0, 0), dir: Tuple[float, float, float] = (0, 0, 1)) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a solid by sweeping a profile along a helical path.
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
# intersect_rscalarfield
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def intersect_rscalarfield(*fields: ScalarField) -> ScalarField
|
||||
```
|
||||
|
||||
*Source: field.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create an intersection scalar field.
|
||||
|
||||
## Parameters
|
||||
|
||||
### *fields
|
||||
|
||||
- **Description**: Input scalar fields.
|
||||
|
||||
## Returns
|
||||
|
||||
ScalarField: Intersection scalar field.
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
# intersect_rsolidlist
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def intersect_rsolidlist(*solids: Union[Solid, Sequence[Solid]]) -> List[Solid]
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Compute the boolean intersection of solids.
|
||||
|
||||
All boolean operations (union/cut/intersect) accept a mix of Solid and
|
||||
sequences; results are always returned as a list of Solid.
|
||||
`intersect_rsolidlist(body, [clip_a, clip_b])` is valid input.
|
||||
If an earlier union returned multiple solids, keep that list and intersect
|
||||
each solid intentionally instead of collapsing it to `result[0]`.
|
||||
If a later step truly requires one solid, first verify `len(results) == 1`.
|
||||
When a preceding union produced multiple tangent-only solids, adjust the part
|
||||
placement so the intended bodies overlap slightly, re-run the union, and only
|
||||
then unwrap the single result.
|
||||
|
||||
## Parameters
|
||||
|
||||
### solids
|
||||
|
||||
- **Description**: One or more Solid objects or sequences of Solid. Nested sequences are flattened before processing.
|
||||
|
||||
## Returns
|
||||
|
||||
List[Solid]: A list containing the intersection result, or an empty list if
|
||||
the solids do not overlap. The result is returned as a list for
|
||||
consistency with other boolean operations.
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1
|
||||
```python
|
||||
body = make_box_rsolid(12, 4, 4, bottom_face_center=(0, 0, 0))
|
||||
clip_a = make_box_rsolid(8, 4, 4, bottom_face_center=(2, 0, 0))
|
||||
clip_b = make_box_rsolid(6, 6, 6, bottom_face_center=(3, -1, -1))
|
||||
```
|
||||
|
||||
### Example 2
|
||||
```python
|
||||
results = intersect_rsolidlist(body, [clip_a, clip_b])
|
||||
print(f"Intersect result count: {len(results)}")
|
||||
```
|
||||
|
||||
### Example 3
|
||||
```python
|
||||
# A previous union may return multiple solids; keep the list and intersect each part.
|
||||
tangent_parts = union_rsolidlist(
|
||||
body,
|
||||
[
|
||||
make_sphere_rsolid(2.0, center=(-2.0, 2.0, 2.0)),
|
||||
make_sphere_rsolid(2.0, center=(14.0, 2.0, 2.0)),
|
||||
],
|
||||
)
|
||||
clipped_parts = []
|
||||
for part in tangent_parts:
|
||||
clipped_parts.extend(intersect_rsolidlist(part, clip_a))
|
||||
```
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# linear_pattern_rsolidlist
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def linear_pattern_rsolidlist(shape: AnyShape, direction: Tuple[float, float, float], count: int, spacing: float) -> List[Solid]
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a linear pattern of solids.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# loft_rsolid
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def loft_rsolid(profiles: List[Wire], ruled: bool = False) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a solid by lofting multiple profiles.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_angle_arc_redge
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_angle_arc_redge(center: Tuple[float, float, float], radius: float, start_angle: float, end_angle: float, normal: Tuple[float, float, float] = (0, 0, 1)) -> Edge
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create an arc edge from a center, radius, and angle range.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_angle_arc_rwire
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_angle_arc_rwire(center: Tuple[float, float, float], radius: float, start_angle: float, end_angle: float, normal: Tuple[float, float, float] = (0, 0, 1)) -> Wire
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a wire containing an arc defined by a center, radius, and angle range.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_assembly_rassembly
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_assembly_rassembly(parts: Sequence[Tuple[str, Solid]], name: str = 'assembly', parents: Optional[Dict[str, str]] = None, local_transforms: Optional[Dict[str, Union[np.ndarray, Sequence[Sequence[float]]]]] = None) -> Assembly
|
||||
```
|
||||
|
||||
*Source: constraints.py*
|
||||
|
||||
## Description
|
||||
|
||||
Type-1 mapping: lift a parameter description into an assembly object.
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
# make_box_rscalarfield
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_box_rscalarfield(center: Tuple[float, float, float], size: Tuple[float, float, float]) -> ScalarField
|
||||
```
|
||||
|
||||
*Source: field.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create an axis-aligned box scalar field.
|
||||
|
||||
## Parameters
|
||||
|
||||
### center
|
||||
|
||||
- **Description**: Box center coordinates `(x, y, z)`.
|
||||
|
||||
### size
|
||||
|
||||
- **Description**: Box size `(sx, sy, sz)`.
|
||||
|
||||
## Returns
|
||||
|
||||
ScalarField: Box scalar field.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_box_rsolid
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_box_rsolid(width: float, height: float, depth: float, bottom_face_center: Tuple[float, float, float] = (0, 0, 0)) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a box solid.
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
# make_capsule_rscalarfield
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_capsule_rscalarfield(p0: Tuple[float, float, float], p1: Tuple[float, float, float], radius: float) -> ScalarField
|
||||
```
|
||||
|
||||
*Source: field.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a capsule scalar field.
|
||||
|
||||
## Parameters
|
||||
|
||||
### p0
|
||||
|
||||
- **Description**: First endpoint coordinates.
|
||||
|
||||
### p1
|
||||
|
||||
- **Description**: Second endpoint coordinates.
|
||||
|
||||
### radius
|
||||
|
||||
- **Description**: Capsule radius.
|
||||
|
||||
## Returns
|
||||
|
||||
ScalarField: Capsule scalar field.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_circle_redge
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_circle_redge(center: Tuple[float, float, float], radius: float, normal: Tuple[float, float, float] = (0, 0, 1)) -> Edge
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a circular edge.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_circle_rface
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_circle_rface(center: Tuple[float, float, float], radius: float, normal: Tuple[float, float, float] = (0, 0, 1)) -> Face
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a circular face.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_circle_rwire
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_circle_rwire(center: Tuple[float, float, float], radius: float, normal: Tuple[float, float, float] = (0, 0, 1)) -> Wire
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a circular wire.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_cone_rsolid
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_cone_rsolid(bottom_radius: float, height: float, top_radius: float = 0.0, bottom_face_center: Tuple[float, float, float] = (0, 0, 0), axis: Tuple[float, float, float] = (0, 0, 1)) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a cone or truncated cone solid.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_cylinder_rsolid
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_cylinder_rsolid(radius: float, height: float, bottom_face_center: Tuple[float, float, float] = (0, 0, 0), axis: Tuple[float, float, float] = (0, 0, 1)) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a cylinder solid.
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
# make_ellipsoid_rscalarfield
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_ellipsoid_rscalarfield(center: Tuple[float, float, float], radii: Tuple[float, float, float]) -> ScalarField
|
||||
```
|
||||
|
||||
*Source: field.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create an ellipsoid scalar field.
|
||||
|
||||
## Parameters
|
||||
|
||||
### center
|
||||
|
||||
- **Description**: Ellipsoid center coordinates `(x, y, z)`.
|
||||
|
||||
### radii
|
||||
|
||||
- **Description**: Radii `(rx, ry, rz)`.
|
||||
|
||||
## Returns
|
||||
|
||||
ScalarField: Ellipsoid scalar field.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_face_from_wire_rface
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_face_from_wire_rface(wire: Wire, normal: Tuple[float, float, float] = (0, 0, 1)) -> Face
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a face from a closed wire.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_field_surface_rsolid
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_field_surface_rsolid(field, bounds: Optional[Tuple[Tuple[float, float, float], Tuple[float, float, float]]] = None, resolution: Tuple[int, int, int] = (24, 24, 24), iso: float = 0.0, cap_bounds: bool = True) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Build a closed solid from a scalar field isosurface.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_helix_redge
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_helix_redge(pitch: float, height: float, radius: float, center: Tuple[float, float, float] = (0, 0, 0), dir: Tuple[float, float, float] = (0, 0, 1)) -> Edge
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a helix edge.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_helix_rwire
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_helix_rwire(pitch: float, height: float, radius: float, center: Tuple[float, float, float] = (0, 0, 0), dir: Tuple[float, float, float] = (0, 0, 1)) -> Wire
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a helix wire.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_line_redge
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_line_redge(start: Tuple[float, float, float], end: Tuple[float, float, float]) -> Edge
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a straight edge between two points.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_n_hole_flange_rsolid
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_n_hole_flange_rsolid(flange_outer_diameter = 120.0, flange_inner_diameter = 60.0, flange_thickness = 15.0, boss_outer_diameter = 80.0, boss_height = 5.0, hole_diameter = 8.0, hole_circle_diameter = 100.0, hole_count = 8, chamfer_size = 1.0) -> Solid
|
||||
```
|
||||
|
||||
*Source: evolve.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create an n-hole flange with a raised boss ring and optional chamfers. The center of the bottom face is placed at the origin.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_naca_propeller_blade_rsolid
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_naca_propeller_blade_rsolid(blade_length = 5.0, root_chord = 1.5, tip_chord = 0.3, total_twist_angle = 45.0, num_sections = 7, t_c = 0.16) -> Solid
|
||||
```
|
||||
|
||||
*Source: evolve.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a single propeller blade solid from a twisted NACA 0016 profile. The blade root starts at the origin and extends along +Z.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_point_rvertex
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_point_rvertex(x: float, y: float, z: float) -> Vertex
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a point in 3D space and return it as a vertex.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_polyline_rwire
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_polyline_rwire(points: List[Tuple[float, float, float]], closed: bool = False) -> Wire
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a polyline wire from a point list.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_rectangle_rface
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_rectangle_rface(width: float, height: float, center: Tuple[float, float, float] = (0, 0, 0), normal: Tuple[float, float, float] = (0, 0, 1)) -> Face
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a rectangular face.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_rectangle_rwire
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_rectangle_rwire(width: float, height: float, center: Tuple[float, float, float] = (0, 0, 0), normal: Tuple[float, float, float] = (0, 0, 1)) -> Wire
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a rectangular wire.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_segment_redge
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_segment_redge(start: Tuple[float, float, float], end: Tuple[float, float, float]) -> Edge
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Alias of `make_line_redge` that returns a straight edge.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_segment_rwire
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_segment_rwire(start: Tuple[float, float, float], end: Tuple[float, float, float]) -> Wire
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a wire containing a single straight segment.
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
# make_sphere_rscalarfield
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_sphere_rscalarfield(center: Tuple[float, float, float], radius: float) -> ScalarField
|
||||
```
|
||||
|
||||
*Source: field.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a spherical scalar field.
|
||||
|
||||
## Parameters
|
||||
|
||||
### center
|
||||
|
||||
- **Description**: Sphere center coordinates `(x, y, z)`.
|
||||
|
||||
### radius
|
||||
|
||||
- **Description**: Sphere radius.
|
||||
|
||||
## Returns
|
||||
|
||||
ScalarField: Sphere scalar field.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_sphere_rsolid
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_sphere_rsolid(radius: float, center: Tuple[float, float, float] = (0, 0, 0)) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a sphere solid.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_spline_redge
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_spline_redge(points: List[Tuple[float, float, float]], tangents: Optional[List[Tuple[float, float, float]]] = None) -> Edge
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a spline edge through control points.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_spline_rwire
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_spline_rwire(points: List[Tuple[float, float, float]], tangents: Optional[List[Tuple[float, float, float]]] = None, closed: bool = False) -> Wire
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a spline wire through control points.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_threaded_rod_rsolid
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_threaded_rod_rsolid(thread_diameter = 8.0, thread_length = 20.0, total_length = 30.0, thread_pitch = 1.25, thread_start_position = 0.0, chamfer_size = 0.5) -> Solid
|
||||
```
|
||||
|
||||
*Source: evolve.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a threaded rod with configurable rod length, thread span, and pitch. The top center is placed at the origin and the rod extends in -Z.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_three_point_arc_redge
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_three_point_arc_redge(start: Tuple[float, float, float], middle: Tuple[float, float, float], end: Tuple[float, float, float]) -> Edge
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create an arc edge from three points.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_three_point_arc_rwire
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_three_point_arc_rwire(start: Tuple[float, float, float], middle: Tuple[float, float, float], end: Tuple[float, float, float]) -> Wire
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a wire containing an arc defined by three points.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# make_wire_from_edges_rwire
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def make_wire_from_edges_rwire(edges: List[Edge]) -> Wire
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a wire from a list of connected edges.
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
# meta
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def meta(path: str, op: str, value: Any) -> Predicate
|
||||
```
|
||||
|
||||
*Source: ql.py*
|
||||
|
||||
## Description
|
||||
|
||||
Build a metadata-based predicate.
|
||||
|
||||
Q.meta("geo.type", "==", "box")
|
||||
|
||||
## Parameters
|
||||
|
||||
### path
|
||||
|
||||
- **Description**: Metadata path, for example `geo.type`.
|
||||
|
||||
### op
|
||||
|
||||
- **Description**: Comparison operator. Supports `==`, `!=`, `>`, `>=`, `<`, and `<=`.
|
||||
|
||||
### value
|
||||
|
||||
- **Description**: Comparison target value.
|
||||
|
||||
## Returns
|
||||
|
||||
Callable[[Any], bool]: Predicate function.
|
||||
|
||||
## Raises
|
||||
|
||||
- **TypeError**: If op is not a string.
|
||||
- **ValueError**: If the operator is unsupported.
|
||||
|
||||
## Examples
|
||||
|
||||
```python
|
||||
pred = Q.meta("geo.size.x", ">", 1.0)
|
||||
matched = pred(obj)
|
||||
```
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# mirror_shape
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def mirror_shape(shape: AnyShape, plane_origin: Tuple[float, float, float], plane_normal: Tuple[float, float, float]) -> AnyShape
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Mirror a shape across a plane.
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
# not_
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def not_(predicate: Predicate) -> Predicate
|
||||
```
|
||||
|
||||
*Source: ql.py*
|
||||
|
||||
## Description
|
||||
|
||||
Build a NOT predicate.
|
||||
|
||||
Q.not_(Q.tag("state.*"))
|
||||
|
||||
## Parameters
|
||||
|
||||
### predicate
|
||||
|
||||
- **Description**: A single predicate.
|
||||
|
||||
## Returns
|
||||
|
||||
Callable[[Any], bool]: Negated predicate.
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
# or_
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def or_(*predicates: Predicate) -> Predicate
|
||||
```
|
||||
|
||||
*Source: ql.py*
|
||||
|
||||
## Description
|
||||
|
||||
Build an OR-composed predicate.
|
||||
|
||||
Q.or_(Q.tag("face.top"), Q.tag("face.bottom"))
|
||||
|
||||
## Parameters
|
||||
|
||||
### *predicates
|
||||
|
||||
- **Description**: Any number of predicates.
|
||||
|
||||
## Returns
|
||||
|
||||
Callable[[Any], bool]: Combined predicate.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# radial_pattern_rsolidlist
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def radial_pattern_rsolidlist(shape: AnyShape, center: Tuple[float, float, float], axis: Tuple[float, float, float], count: int, total_rotation_angle: float) -> List[Solid]
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a radial pattern of solids.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# render_screenshot_rpath
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Render a screenshot of shapes and save it to a file.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# revolve_rsolid
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def revolve_rsolid(profile: Union[Wire, Face], axis: Tuple[float, float, float] = (0, 0, 1), angle: float = 360, origin: Tuple[float, float, float] = (0, 0, 0)) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a solid by revolving a profile around an axis.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# rotate_part_rassembly
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def rotate_part_rassembly(assembly: Assembly, part: Union[str, PartHandle], angle_deg: float, axis: AxisLike = 'z', origin: Vec3Like = (0.0, 0.0, 0.0), frame: Literal['world', 'local'] = 'world') -> Assembly
|
||||
```
|
||||
|
||||
*Source: constraints.py*
|
||||
|
||||
## Description
|
||||
|
||||
Type-2 mapping: rotate a part and return a new assembly.
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
# rotate_rscalarfield
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def rotate_rscalarfield(field: ScalarField, axis: Tuple[float, float, float], angle_degrees: float) -> ScalarField
|
||||
```
|
||||
|
||||
*Source: field.py*
|
||||
|
||||
## Description
|
||||
|
||||
Rotate a scalar field around the origin.
|
||||
|
||||
## Parameters
|
||||
|
||||
### field
|
||||
|
||||
- **Description**: Input scalar field.
|
||||
|
||||
### axis
|
||||
|
||||
- **Description**: Rotation axis vector `(x, y, z)`.
|
||||
|
||||
### angle_degrees
|
||||
|
||||
- **Description**: Rotation angle in degrees.
|
||||
|
||||
## Returns
|
||||
|
||||
ScalarField: Rotated scalar field.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# rotate_shape
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def rotate_shape(shape: AnyShape, angle: float, axis: Tuple[float, float, float] = (0, 0, 1), origin: Tuple[float, float, float] = (0, 0, 0)) -> AnyShape
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Rotate a shape around an axis.
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
# scale_rscalarfield
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def scale_rscalarfield(field: ScalarField, factors: Tuple[float, float, float]) -> ScalarField
|
||||
```
|
||||
|
||||
*Source: field.py*
|
||||
|
||||
## Description
|
||||
|
||||
Scale a scalar field around the origin.
|
||||
|
||||
## Parameters
|
||||
|
||||
### field
|
||||
|
||||
- **Description**: Input scalar field.
|
||||
|
||||
### factors
|
||||
|
||||
- **Description**: Scale factors `(sx, sy, sz)`.
|
||||
|
||||
## Returns
|
||||
|
||||
ScalarField: Scaled scalar field.
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
# select
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def select(items: Iterable[Any]) -> Query
|
||||
```
|
||||
|
||||
*Source: ql.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a query object.
|
||||
|
||||
Q.select(items).where(Q.tag("face.top")).first()
|
||||
|
||||
## Parameters
|
||||
|
||||
### items
|
||||
|
||||
- **Description**: Any iterable.
|
||||
|
||||
## Returns
|
||||
|
||||
Query: Query object.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# select_edges_by_tag
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def select_edges_by_tag(shape: Union[Face, Solid], tag: str) -> List[Edge]
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Select edges by tag.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# select_faces_by_tag
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def select_faces_by_tag(solid: Solid, tag: str) -> List[Face]
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Select faces by tag.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# set_tag
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def set_tag(shape: AnyShape, tag: str) -> AnyShape
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Attach a tag to a shape.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# shell_rsolid
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def shell_rsolid(solid: Solid, faces_to_remove: List[Face], thickness: float) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Shell a solid to create a hollow part.
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
# smooth_subtract_rscalarfield
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def smooth_subtract_rscalarfield(a: ScalarField, b: ScalarField, k: float) -> ScalarField
|
||||
```
|
||||
|
||||
*Source: field.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a smooth subtraction scalar field.
|
||||
|
||||
## Parameters
|
||||
|
||||
### a
|
||||
|
||||
- **Description**: Minuend scalar field.
|
||||
|
||||
### b
|
||||
|
||||
- **Description**: Subtrahend scalar field.
|
||||
|
||||
### k
|
||||
|
||||
- **Description**: Smoothing factor, which must be positive.
|
||||
|
||||
## Returns
|
||||
|
||||
ScalarField: Smooth subtraction scalar field.
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
# smooth_union_rscalarfield
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def smooth_union_rscalarfield(a: ScalarField, b: ScalarField, k: float) -> ScalarField
|
||||
```
|
||||
|
||||
*Source: field.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a smooth union scalar field.
|
||||
|
||||
## Parameters
|
||||
|
||||
### a
|
||||
|
||||
- **Description**: Scalar field A.
|
||||
|
||||
### b
|
||||
|
||||
- **Description**: Scalar field B.
|
||||
|
||||
### k
|
||||
|
||||
- **Description**: Smoothing factor, which must be positive.
|
||||
|
||||
## Returns
|
||||
|
||||
ScalarField: Smooth union scalar field.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# solve_assembly_rresult
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def solve_assembly_rresult(assembly: Assembly, max_iterations: int = 30, tolerance: float = 1e-06) -> AssemblyResult
|
||||
```
|
||||
|
||||
*Source: constraints.py*
|
||||
|
||||
## Description
|
||||
|
||||
Type-2 mapping: map an assembly to a solve result without mutating it.
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
# stack
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def stack(assembly: Assembly, parts: Sequence[Union[str, PartHandle]], axis: str = 'z', gap: float = 0.0, align: Literal['center', 'start', 'end'] = 'center', justify: Literal['start', 'center', 'end', 'space-between'] = 'start', bounds: Optional[Tuple[PointAnchor, PointAnchor]] = None) -> Assembly
|
||||
```
|
||||
|
||||
*Source: constraints.py*
|
||||
|
||||
## Description
|
||||
|
||||
Declaratively stack multiple parts along the specified axis.
|
||||
|
||||
Semantics:
|
||||
- sequential stacking: part i is placed after part i-1 with the given gap
|
||||
- cross-axis alignment: the other two axes are aligned according to `align`
|
||||
- main-axis distribution: `justify` controls how the whole stack is placed
|
||||
within the bounds
|
||||
|
||||
BBox-first note:
|
||||
- This function uses axis-aligned bounding-box (AABB) anchors such as
|
||||
`bbox.top` and `bbox.bottom` to approximate Flexbox-like box semantics.
|
||||
- For parts with large rotations, the AABB changes with pose, so the layout
|
||||
result changes as well. This is expected in the current MVP stage.
|
||||
|
||||
Note:
|
||||
- This is container-level sugar that compiles into a set of `offset(...)`
|
||||
constraints internally.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# stack_rassembly
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def stack_rassembly(assembly: Assembly, parts: Sequence[Union[str, PartHandle]], axis: str = 'z', gap: float = 0.0, align: Literal['center', 'start', 'end'] = 'center', justify: Literal['start', 'center', 'end', 'space-between'] = 'start', bounds: Optional[Tuple[PointAnchor, PointAnchor]] = None) -> Assembly
|
||||
```
|
||||
|
||||
*Source: constraints.py*
|
||||
|
||||
## Description
|
||||
|
||||
Type-2 mapping: apply a stack layout and return a new assembly.
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
# subtract_rscalarfield
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def subtract_rscalarfield(a: ScalarField, b: ScalarField) -> ScalarField
|
||||
```
|
||||
|
||||
*Source: field.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a subtraction scalar field.
|
||||
|
||||
## Parameters
|
||||
|
||||
### a
|
||||
|
||||
- **Description**: Minuend scalar field.
|
||||
|
||||
### b
|
||||
|
||||
- **Description**: Subtrahend scalar field.
|
||||
|
||||
## Returns
|
||||
|
||||
ScalarField: Subtraction scalar field.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# sweep_rsolid
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def sweep_rsolid(profile: Face, path: Wire, is_frenet: bool = False) -> Solid
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a solid by sweeping a profile along a path.
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
# tag
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def tag(pattern: str) -> Predicate
|
||||
```
|
||||
|
||||
*Source: ql.py*
|
||||
|
||||
## Description
|
||||
|
||||
Build a tag-based predicate.
|
||||
|
||||
`Q.tag("face.top")` or `Q.tag("role.*")`.
|
||||
|
||||
## Parameters
|
||||
|
||||
### pattern
|
||||
|
||||
- **Description**: Tag matching pattern. Supports a trailing `*` wildcard.
|
||||
|
||||
## Returns
|
||||
|
||||
Callable[[Any], bool]: Predicate function.
|
||||
|
||||
## Raises
|
||||
|
||||
- **TypeError**: If pattern is not a string.
|
||||
- **ValueError**: If the wildcard position is invalid.
|
||||
|
||||
## Examples
|
||||
|
||||
```python
|
||||
pred = Q.tag("role.*")
|
||||
matched = pred(obj)
|
||||
```
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# translate_part_rassembly
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def translate_part_rassembly(assembly: Assembly, part: Union[str, PartHandle], vector: Vec3Like, frame: Literal['world', 'local'] = 'world') -> Assembly
|
||||
```
|
||||
|
||||
*Source: constraints.py*
|
||||
|
||||
## Description
|
||||
|
||||
Type-2 mapping: translate a part and return a new assembly.
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
# translate_rscalarfield
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def translate_rscalarfield(field: ScalarField, offset: Tuple[float, float, float]) -> ScalarField
|
||||
```
|
||||
|
||||
*Source: field.py*
|
||||
|
||||
## Description
|
||||
|
||||
Translate a scalar field.
|
||||
|
||||
## Parameters
|
||||
|
||||
### field
|
||||
|
||||
- **Description**: Input scalar field.
|
||||
|
||||
### offset
|
||||
|
||||
- **Description**: Translation vector `(dx, dy, dz)`.
|
||||
|
||||
## Returns
|
||||
|
||||
ScalarField: Translated scalar field.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# translate_shape
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def translate_shape(shape: AnyShape, vector: Tuple[float, float, float]) -> AnyShape
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Translate a shape by an offset vector.
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
# union_rscalarfield
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def union_rscalarfield(*fields: ScalarField) -> ScalarField
|
||||
```
|
||||
|
||||
*Source: field.py*
|
||||
|
||||
## Description
|
||||
|
||||
Create a union scalar field.
|
||||
|
||||
## Parameters
|
||||
|
||||
### *fields
|
||||
|
||||
- **Description**: Input scalar fields.
|
||||
|
||||
## Returns
|
||||
|
||||
ScalarField: Union scalar field.
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
# union_rsolidlist
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def union_rsolidlist(
|
||||
*solids: Union[Solid, Sequence[Solid]],
|
||||
clean: bool = True,
|
||||
glue: bool = True,
|
||||
tol: Optional[float] = None,
|
||||
) -> List[Solid]
|
||||
```
|
||||
|
||||
*Source: operations.py*
|
||||
|
||||
## Description
|
||||
|
||||
Compute the boolean union of one or more solids.
|
||||
|
||||
All boolean operations (union/cut/intersect) accept a mix of Solid and
|
||||
sequences; results are always returned as a list of Solid.
|
||||
Keep the list result unless you have explicitly verified `len(result) == 1`.
|
||||
SimpleCAD enables glue mode by default and applies a conservative internal fuzzy
|
||||
tolerance, so normal modeling code does not need to tune boolean parameters.
|
||||
By default this follows CadQuery's union flow: perform one OCC fuse across the
|
||||
input solids, then call `clean()` to unify same-domain faces when possible.
|
||||
Touching-but-not-intersecting inputs can legitimately return multiple solids.
|
||||
If that happens, keep using the list: pass it directly into later union calls,
|
||||
or iterate over the solids for later cut/intersect steps.
|
||||
When the returned solids are still separated by more than the active tolerance,
|
||||
SimpleCAD prints a stdout warning explaining that the objects do not touch and
|
||||
that their gap exceeds `tol`.
|
||||
If you truly need exactly one merged solid, you must check the list length
|
||||
before using `result[0]`.
|
||||
|
||||
## Parameters
|
||||
|
||||
### solids
|
||||
|
||||
- **Description**: One or more Solid objects or sequences of Solid. Nested sequences are flattened before processing.
|
||||
|
||||
### clean
|
||||
|
||||
- **Description**: Call CadQuery's `clean()` after the union to remove splitter edges and unify same-domain faces when possible.
|
||||
|
||||
### glue
|
||||
|
||||
- **Description**: Enable OCC glue mode for touching or partially overlapping inputs. Defaults to `True` for SimpleCAD's standard union behavior.
|
||||
|
||||
### tol
|
||||
|
||||
- **Description**: Optional fuzzy-boolean tolerance passed to the OCC union kernel. When omitted, SimpleCAD derives a conservative scale-aware value automatically.
|
||||
|
||||
## Returns
|
||||
|
||||
List[Solid]: Resulting solids after union attempts. Solids that can be fused
|
||||
are merged; disjoint or tangent-only contacts remain separate, so the
|
||||
list may contain multiple solids.
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1
|
||||
```python
|
||||
# Rounded-bar style input: end caps only touch the center body.
|
||||
main_body = make_box_rsolid(10, 4, 4, bottom_face_center=(0, 0, 0))
|
||||
left_cap = make_sphere_rsolid(2.0, center=(-2.0, 2.0, 2.0))
|
||||
right_cap = make_sphere_rsolid(2.0, center=(12.0, 2.0, 2.0))
|
||||
```
|
||||
|
||||
### Example 2
|
||||
```python
|
||||
body_parts = union_rsolidlist(main_body, [left_cap, right_cap])
|
||||
print(f"Union result count: {len(body_parts)}")
|
||||
# This is acceptable: tangent-only contact can stay as multiple solids.
|
||||
for solid in body_parts:
|
||||
print(f"- volume: {solid.get_volume():.6f}")
|
||||
```
|
||||
|
||||
### Example 3
|
||||
```python
|
||||
# Keep using the returned list in later boolean steps.
|
||||
rib = make_box_rsolid(2, 4, 4, bottom_face_center=(4, 0, 0))
|
||||
combined_parts = union_rsolidlist(body_parts, rib)
|
||||
print(f"Combined result count: {len(combined_parts)}")
|
||||
```
|
||||
|
||||
### Example 4
|
||||
```python
|
||||
# Only unwrap to one solid after an explicit length check.
|
||||
left_cap_embedded = make_sphere_rsolid(2.0, center=(-1.8, 2.0, 2.0))
|
||||
right_cap_embedded = make_sphere_rsolid(2.0, center=(11.8, 2.0, 2.0))
|
||||
merged = union_rsolidlist(main_body, [left_cap_embedded, right_cap_embedded])
|
||||
if len(merged) != 1:
|
||||
raise ValueError(
|
||||
"Adjust part placement so each cap overlaps the body slightly before "
|
||||
"using merged[0]."
|
||||
)
|
||||
final_body = merged[0]
|
||||
```
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
# value
|
||||
|
||||
## API Definition
|
||||
|
||||
```python
|
||||
def value(path: str, default: Any = None) -> KeyFn
|
||||
```
|
||||
|
||||
*Source: ql.py*
|
||||
|
||||
## Description
|
||||
|
||||
Build a value getter for sorting or projection.
|
||||
|
||||
Q.select(items).order_by(Q.value("geo.height"))
|
||||
|
||||
## Parameters
|
||||
|
||||
### path
|
||||
|
||||
- **Description**: Metadata path, for example `geo.height`.
|
||||
|
||||
### default
|
||||
|
||||
- **Description**: Default value when lookup fails.
|
||||
|
||||
## Returns
|
||||
|
||||
Callable[[Any], Any]: Getter function.
|
||||
|
||||
## Examples
|
||||
|
||||
```python
|
||||
key = Q.value("geo.height", 0.0)
|
||||
height = key(obj)
|
||||
```
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
# SimpleCAD API Core Classes Documentation
|
||||
|
||||
This directory contains detailed documentation for all core classes of SimpleCAD API.
|
||||
|
||||
## Core Classes Overview
|
||||
|
||||
SimpleCAD API provides a complete set of geometric modeling classes, from basic points, lines, and surfaces to complex solids and compounds. Each class has rich functionality and a flexible tag management system.
|
||||
|
||||
### Basic Classes
|
||||
|
||||
#### [CoordinateSystem - Coordinate System](coordinate_system.md)
|
||||
A 3D coordinate system class for defining and managing local coordinate systems, supporting coordinate transformations and integration with CADQuery.
|
||||
|
||||
**Main Features:**
|
||||
- Define local coordinate systems
|
||||
- Coordinate and vector transformations
|
||||
- Conversion with CADQuery coordinate systems
|
||||
|
||||
#### [SimpleWorkplane - Workplane](simple_workplane.md)
|
||||
A workplane context manager that provides a local coordinate system environment, supporting nested usage.
|
||||
|
||||
**Main Features:**
|
||||
- Define workplanes
|
||||
- Context manager support
|
||||
- Nested coordinate system management
|
||||
|
||||
#### [TaggedMixin - Tag Mixin Class](tagged_mixin.md)
|
||||
A mixin class that provides unified tag and metadata management functionality for all geometry classes.
|
||||
|
||||
**Main Features:**
|
||||
- Tag management (add, remove, query)
|
||||
- Metadata storage and retrieval
|
||||
- Geometry classification and query support
|
||||
|
||||
### Geometry Classes
|
||||
|
||||
#### 0D Geometry
|
||||
|
||||
##### [Vertex - Vertex](vertex.md)
|
||||
Represents a point in 3D space, the fundamental element of all geometries.
|
||||
|
||||
**Main Features:**
|
||||
- Store 3D coordinates
|
||||
- Tag and metadata management
|
||||
- Coordinate queries
|
||||
|
||||
#### 1D Geometry
|
||||
|
||||
##### [Edge - Edge](edge.md)
|
||||
Represents a 1D geometric element connecting two vertices, which can be lines, arcs, splines, etc.
|
||||
|
||||
**Main Features:**
|
||||
- Length calculation
|
||||
- Endpoint queries
|
||||
- Geometry type identification
|
||||
|
||||
##### [Wire - Wire](wire.md)
|
||||
A 1D geometric path composed of multiple connected edges, which can be open or closed.
|
||||
|
||||
**Main Features:**
|
||||
- Edge collection management
|
||||
- Closure checking
|
||||
- Path analysis
|
||||
|
||||
#### 2D Geometry
|
||||
|
||||
##### [Face - Face](face.md)
|
||||
Represents 2D surface geometry, enclosed by one or more wires, and can contain holes.
|
||||
|
||||
**Main Features:**
|
||||
- Area calculation
|
||||
- Normal vector queries
|
||||
- Boundary wire management
|
||||
|
||||
##### [Shell - Shell](shell.md)
|
||||
A surface collection composed of multiple faces, which can be open or closed.
|
||||
|
||||
**Main Features:**
|
||||
- Face collection management
|
||||
- Surface analysis
|
||||
- Thin-walled structure support
|
||||
|
||||
#### 3D Geometry
|
||||
|
||||
##### [Solid - Solid](solid.md)
|
||||
Represents a 3D closed geometry with volume, the core object of CAD modeling.
|
||||
|
||||
**Main Features:**
|
||||
- Volume calculation
|
||||
- Face and edge queries
|
||||
- Automatic face tagging
|
||||
- Boolean operation support
|
||||
|
||||
##### [Compound - Compound](compound.md)
|
||||
A collection composed of multiple geometry objects, used for managing complex assemblies.
|
||||
|
||||
**Main Features:**
|
||||
- Multi-solid management
|
||||
- Hierarchy support
|
||||
- Batch operations
|
||||
|
||||
## Class Relationship Diagram
|
||||
|
||||
```
|
||||
TaggedMixin
|
||||
├── Vertex (0D)
|
||||
├── Edge (1D)
|
||||
├── Wire (1D) ← composed of Edges
|
||||
├── Face (2D) ← bounded by Wires
|
||||
├── Shell (2D) ← collection of Faces
|
||||
├── Solid (3D) ← bounded by Shells/Faces
|
||||
└── Compound (3D) ← collection of Solids
|
||||
|
||||
CoordinateSystem ← independent utility class
|
||||
SimpleWorkplane ← uses CoordinateSystem
|
||||
```
|
||||
|
||||
## Inheritance Relationships
|
||||
|
||||
All geometry classes inherit from `TaggedMixin`, obtaining unified tag and metadata management functionality:
|
||||
|
||||
- **Tag System**: Add string tags to geometries, supporting classification and queries
|
||||
- **Metadata System**: Store key-value pairs, supporting complex attribute management
|
||||
- **Query Support**: Efficient querying and filtering based on tags and metadata
|
||||
|
||||
## Coordinate System
|
||||
|
||||
SimpleCAD uses a unified coordinate system:
|
||||
|
||||
- **Global Coordinate System**: Z-up right-handed coordinate system
|
||||
- **Local Coordinate Systems**: Defined through `CoordinateSystem` and `SimpleWorkplane`
|
||||
- **CADQuery Compatible**: Automatically handles conversion with CADQuery coordinate systems
|
||||
|
||||
## Design Principles
|
||||
|
||||
### Consistency
|
||||
All classes follow the same design patterns and naming conventions, providing a consistent user experience.
|
||||
|
||||
### Extensibility
|
||||
Through the tag and metadata system, users can add custom information to geometries, supporting complex application scenarios.
|
||||
|
||||
### Interoperability
|
||||
Seamlessly integrates with CADQuery, fully utilizing CADQuery's powerful functionality.
|
||||
|
||||
### Ease of Use
|
||||
Provides intuitive APIs and rich examples, reducing learning costs.
|
||||
|
||||
## Usage Guide
|
||||
|
||||
### Basic Usage Flow
|
||||
|
||||
1. **Create Geometries**: Use `make_*` functions to create basic geometries
|
||||
2. **Add Tags**: Use `add_tag()` to add identifiers to geometries
|
||||
3. **Set Metadata**: Use `set_metadata()` to store attribute information
|
||||
4. **Combine Operations**: Use boolean operations, transformations, etc. to create complex geometries
|
||||
5. **Query and Filter**: Query desired geometries based on tags and metadata
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. **Tag Naming**: Use consistent naming conventions, such as `category.subcategory.detail`
|
||||
2. **Metadata Organization**: Use structured data to organize related information
|
||||
3. **Coordinate System Management**: Use workplanes appropriately to simplify complex geometry creation
|
||||
4. **Performance Considerations**: Avoid excessive tags and large metadata that may impact performance
|
||||
|
||||
## Example Code
|
||||
|
||||
```python
|
||||
from simplecadapi import *
|
||||
|
||||
# 创建工作平面
|
||||
with SimpleWorkplane(origin=(0, 0, 0)) as wp:
|
||||
# 创建基础几何体
|
||||
box = make_box_rsolid(width=5, height=3, depth=2)
|
||||
|
||||
# 添加标签和元数据
|
||||
box.add_tag("structural")
|
||||
box.add_tag("aluminum")
|
||||
box.set_metadata("material", "6061-T6")
|
||||
box.set_metadata("density", 2.7)
|
||||
|
||||
# 自动标记面
|
||||
box.auto_tag_faces("box")
|
||||
|
||||
# 查询特定面
|
||||
top_faces = [f for f in box.get_faces() if f.has_tag("top")]
|
||||
```
|
||||
|
||||
## Extension Development
|
||||
|
||||
If you need to create custom geometry classes:
|
||||
|
||||
1. Inherit from `TaggedMixin` to get tag functionality
|
||||
2. Wrap the corresponding CADQuery object
|
||||
3. Implement necessary geometry query methods
|
||||
4. Provide appropriate string representation methods
|
||||
|
||||
```python
|
||||
class CustomGeometry(TaggedMixin):
|
||||
def __init__(self, cq_object):
|
||||
TaggedMixin.__init__(self)
|
||||
self.cq_object = cq_object
|
||||
|
||||
def get_custom_property(self):
|
||||
# 实现自定义功能
|
||||
pass
|
||||
```
|
||||
|
||||
## More Resources
|
||||
|
||||
- [API Reference Documentation](../api/)
|
||||
- [Example Code](../../examples.py)
|
||||
- [User Guide](../../README.md)
|
||||
- [Declarative Constraint Layout Design Draft](declarative_constraints.md)
|
||||
+635
@@ -0,0 +1,635 @@
|
||||
# Compound
|
||||
|
||||
## Overview
|
||||
|
||||
`Compound` is the compound class in the SimpleCAD API, representing a collection of multiple geometry objects. A compound can contain multiple solids, faces, edges, and other types of geometric objects, making it an important tool for handling complex assemblies and multi-body geometry. It wraps the CADQuery Compound object and adds tagging functionality.
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class Compound(TaggedMixin):
|
||||
"""复合体类,包装CADQuery的Compound,添加标签功能"""
|
||||
```
|
||||
|
||||
## Inheritance
|
||||
|
||||
- Inherits from `TaggedMixin`, providing tag and metadata functionality
|
||||
|
||||
## Usage
|
||||
|
||||
- Manage collections of multiple geometry objects
|
||||
- Create complex assembly structures
|
||||
- Batch process multiple geometry objects
|
||||
- Organize and classify geometry objects
|
||||
- Implement hierarchical geometry structures
|
||||
|
||||
## Constructor
|
||||
|
||||
### `__init__(cq_compound)`
|
||||
|
||||
Initializes a compound object.
|
||||
|
||||
**Parameters:**
|
||||
- `cq_compound` (cadquery.Compound): A CADQuery compound object
|
||||
|
||||
**Raises:**
|
||||
- `ValueError`: When the input compound object is invalid
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import (
|
||||
make_box_rsolid,
|
||||
make_cylinder_rsolid,
|
||||
make_sphere_rsolid,
|
||||
union_rsolidlist
|
||||
)
|
||||
|
||||
# 创建多个实体
|
||||
box = make_box_rsolid(width=2, height=2, depth=2)
|
||||
cylinder = make_cylinder_rsolid(center=(3, 0, 0), radius=1, height=2)
|
||||
sphere = make_sphere_rsolid(center=(0, 3, 0), radius=1)
|
||||
|
||||
# 通过布尔运算可能产生复合体
|
||||
# 注意:实际的复合体创建方式可能因 API 实现而异
|
||||
```
|
||||
|
||||
## Main Properties
|
||||
|
||||
- `cq_compound`: The underlying CADQuery compound object
|
||||
- `_tags`: Tag set (inherited from TaggedMixin)
|
||||
- `_metadata`: Metadata dictionary (inherited from TaggedMixin)
|
||||
|
||||
## Common Methods
|
||||
|
||||
### `get_solids()`
|
||||
|
||||
Get all solids that make up the compound.
|
||||
|
||||
**Returns:**
|
||||
- `List[Solid]`: List of solid objects
|
||||
|
||||
**Raises:**
|
||||
- `ValueError`: When solid list retrieval fails
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
# 假设有一个复合体对象
|
||||
# compound = ... 某个复合体
|
||||
|
||||
solids = compound.get_solids()
|
||||
print(f"复合体包含 {len(solids)} 个实体")
|
||||
|
||||
for i, solid in enumerate(solids):
|
||||
volume = solid.get_volume()
|
||||
print(f"实体 {i}: 体积 {volume:.3f}")
|
||||
```
|
||||
|
||||
### Tag Management Methods
|
||||
|
||||
Methods inherited from `TaggedMixin`:
|
||||
|
||||
#### `add_tag(tag)`, `has_tag(tag)`, `get_tags()`, `remove_tag(tag)`
|
||||
#### `set_metadata(key, value)`, `get_metadata(key, default=None)`
|
||||
|
||||
Usage is similar to Vertex; see [Vertex documentation](vertex.md) for details.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Creating and Managing Compounds
|
||||
|
||||
```python
|
||||
from simplecadapi import (
|
||||
make_box_rsolid,
|
||||
make_cylinder_rsolid,
|
||||
make_sphere_rsolid,
|
||||
translate_shape,
|
||||
rotate_shape
|
||||
)
|
||||
|
||||
def create_compound_assembly():
|
||||
"""创建复合体装配"""
|
||||
|
||||
# 创建基础零件
|
||||
parts = []
|
||||
|
||||
# 主体零件
|
||||
main_body = make_box_rsolid(width=8, height=6, depth=4)
|
||||
main_body.add_tag("main_body")
|
||||
main_body.add_tag("structural")
|
||||
main_body.set_metadata("part_id", "MB001")
|
||||
main_body.set_metadata("material", "steel")
|
||||
parts.append(main_body)
|
||||
|
||||
# 圆柱形零件
|
||||
for i in range(3):
|
||||
cylinder = make_cylinder_rsolid(center=(0, 0, 0), radius=0.5, height=2)
|
||||
cylinder = translate_shape(cylinder, offset=(2 + i*2, 1, 4))
|
||||
cylinder.add_tag(f"cylinder_{i}")
|
||||
cylinder.add_tag("fastener")
|
||||
cylinder.set_metadata("part_id", f"CY{i:03d}")
|
||||
cylinder.set_metadata("material", "brass")
|
||||
parts.append(cylinder)
|
||||
|
||||
# 球形零件
|
||||
for i in range(2):
|
||||
sphere = make_sphere_rsolid(center=(0, 0, 0), radius=0.8)
|
||||
sphere = translate_shape(sphere, offset=(1 + i*6, 5, 2))
|
||||
sphere.add_tag(f"sphere_{i}")
|
||||
sphere.add_tag("decorative")
|
||||
sphere.set_metadata("part_id", f"SP{i:03d}")
|
||||
sphere.set_metadata("material", "aluminum")
|
||||
parts.append(sphere)
|
||||
|
||||
# 分析装配体
|
||||
print(f"装配体分析:")
|
||||
print(f" 零件总数: {len(parts)}")
|
||||
|
||||
# 按类型分类
|
||||
structural_parts = [p for p in parts if p.has_tag("structural")]
|
||||
fastener_parts = [p for p in parts if p.has_tag("fastener")]
|
||||
decorative_parts = [p for p in parts if p.has_tag("decorative")]
|
||||
|
||||
print(f" 结构件: {len(structural_parts)}")
|
||||
print(f" 紧固件: {len(fastener_parts)}")
|
||||
print(f" 装饰件: {len(decorative_parts)}")
|
||||
|
||||
# 按材料分类
|
||||
materials = {}
|
||||
for part in parts:
|
||||
material = part.get_metadata("material", "unknown")
|
||||
if material not in materials:
|
||||
materials[material] = []
|
||||
materials[material].append(part)
|
||||
|
||||
print(f" 材料分布:")
|
||||
for material, part_list in materials.items():
|
||||
total_volume = sum(p.get_volume() for p in part_list)
|
||||
print(f" {material}: {len(part_list)} 件, 总体积: {total_volume:.3f}")
|
||||
|
||||
return parts
|
||||
|
||||
assembly_parts = create_compound_assembly()
|
||||
```
|
||||
|
||||
### Hierarchical Compound Structure
|
||||
|
||||
```python
|
||||
from simplecadapi import make_box_rsolid, make_cylinder_rsolid, translate_shape
|
||||
|
||||
def create_hierarchical_compound():
|
||||
"""创建层次化复合体"""
|
||||
|
||||
# 创建子装配1:螺栓组件
|
||||
bolt_assembly = []
|
||||
|
||||
# 螺栓主体
|
||||
bolt_body = make_cylinder_rsolid(center=(0, 0, 0), radius=0.3, height=3)
|
||||
bolt_body.add_tag("bolt_body")
|
||||
bolt_body.add_tag("threaded")
|
||||
bolt_body.set_metadata("assembly", "bolt_assembly")
|
||||
bolt_body.set_metadata("function", "fastening")
|
||||
bolt_assembly.append(bolt_body)
|
||||
|
||||
# 螺栓头
|
||||
bolt_head = make_cylinder_rsolid(center=(0, 0, 3), radius=0.5, height=0.5)
|
||||
bolt_head.add_tag("bolt_head")
|
||||
bolt_head.add_tag("hex_head")
|
||||
bolt_head.set_metadata("assembly", "bolt_assembly")
|
||||
bolt_head.set_metadata("function", "driving")
|
||||
bolt_assembly.append(bolt_head)
|
||||
|
||||
# 创建子装配2:支架组件
|
||||
bracket_assembly = []
|
||||
|
||||
# 支架主体
|
||||
bracket_main = make_box_rsolid(width=4, height=1, depth=2)
|
||||
bracket_main.add_tag("bracket_main")
|
||||
bracket_main.add_tag("mounting")
|
||||
bracket_main.set_metadata("assembly", "bracket_assembly")
|
||||
bracket_main.set_metadata("function", "support")
|
||||
bracket_assembly.append(bracket_main)
|
||||
|
||||
# 支架臂
|
||||
for i in range(2):
|
||||
arm = make_box_rsolid(width=0.5, height=2, depth=2)
|
||||
arm = translate_shape(arm, offset=(0.75 + i*2.5, 1, 0))
|
||||
arm.add_tag(f"bracket_arm_{i}")
|
||||
arm.add_tag("support_arm")
|
||||
arm.set_metadata("assembly", "bracket_assembly")
|
||||
arm.set_metadata("function", "support")
|
||||
bracket_assembly.append(arm)
|
||||
|
||||
# 创建主装配
|
||||
main_assembly = []
|
||||
|
||||
# 添加子装配
|
||||
main_assembly.extend(bolt_assembly)
|
||||
main_assembly.extend(bracket_assembly)
|
||||
|
||||
# 添加主体零件
|
||||
main_body = make_box_rsolid(width=8, height=6, depth=4)
|
||||
main_body.add_tag("main_body")
|
||||
main_body.add_tag("primary_structure")
|
||||
main_body.set_metadata("assembly", "main_assembly")
|
||||
main_body.set_metadata("function", "housing")
|
||||
main_assembly.append(main_body)
|
||||
|
||||
# 分析层次结构
|
||||
print(f"层次化装配体分析:")
|
||||
|
||||
# 按装配分组
|
||||
assemblies = {}
|
||||
for part in main_assembly:
|
||||
assembly_name = part.get_metadata("assembly", "unknown")
|
||||
if assembly_name not in assemblies:
|
||||
assemblies[assembly_name] = []
|
||||
assemblies[assembly_name].append(part)
|
||||
|
||||
for assembly_name, parts in assemblies.items():
|
||||
print(f" {assembly_name}:")
|
||||
print(f" 零件数: {len(parts)}")
|
||||
|
||||
# 按功能分类
|
||||
functions = {}
|
||||
for part in parts:
|
||||
function = part.get_metadata("function", "unknown")
|
||||
if function not in functions:
|
||||
functions[function] = []
|
||||
functions[function].append(part)
|
||||
|
||||
for function, func_parts in functions.items():
|
||||
total_volume = sum(p.get_volume() for p in func_parts)
|
||||
print(f" {function}: {len(func_parts)} 件, 体积: {total_volume:.3f}")
|
||||
|
||||
return main_assembly, assemblies
|
||||
|
||||
main_assembly, assemblies = create_hierarchical_compound()
|
||||
```
|
||||
|
||||
### Batch Operations on Compounds
|
||||
|
||||
```python
|
||||
from simplecadapi import (
|
||||
make_box_rsolid,
|
||||
make_cylinder_rsolid,
|
||||
translate_shape,
|
||||
rotate_shape
|
||||
)
|
||||
|
||||
def batch_operations_on_compound():
|
||||
"""对复合体进行批量操作"""
|
||||
|
||||
# 创建一系列相似零件
|
||||
parts = []
|
||||
|
||||
# 创建网格排列的零件
|
||||
for i in range(3):
|
||||
for j in range(3):
|
||||
# 基础几何体
|
||||
if (i + j) % 2 == 0:
|
||||
part = make_box_rsolid(width=1, height=1, depth=1)
|
||||
part.add_tag("box_part")
|
||||
part.add_tag("cubic")
|
||||
else:
|
||||
part = make_cylinder_rsolid(center=(0, 0, 0), radius=0.5, height=1)
|
||||
part.add_tag("cylinder_part")
|
||||
part.add_tag("circular")
|
||||
|
||||
# 定位
|
||||
part = translate_shape(part, offset=(i*2, j*2, 0))
|
||||
|
||||
# 添加位置信息
|
||||
part.add_tag(f"pos_{i}_{j}")
|
||||
part.add_tag("grid_item")
|
||||
part.set_metadata("grid_position", (i, j))
|
||||
part.set_metadata("grid_index", i*3 + j)
|
||||
|
||||
parts.append(part)
|
||||
|
||||
# 批量分析
|
||||
print(f"批量操作分析:")
|
||||
print(f" 总零件数: {len(parts)}")
|
||||
|
||||
# 按类型统计
|
||||
box_parts = [p for p in parts if p.has_tag("box_part")]
|
||||
cylinder_parts = [p for p in parts if p.has_tag("cylinder_part")]
|
||||
|
||||
print(f" 盒子零件: {len(box_parts)}")
|
||||
print(f" 圆柱零件: {len(cylinder_parts)}")
|
||||
|
||||
# 批量体积计算
|
||||
total_volume = sum(p.get_volume() for p in parts)
|
||||
box_volume = sum(p.get_volume() for p in box_parts)
|
||||
cylinder_volume = sum(p.get_volume() for p in cylinder_parts)
|
||||
|
||||
print(f" 总体积: {total_volume:.3f}")
|
||||
print(f" 盒子体积: {box_volume:.3f}")
|
||||
print(f" 圆柱体积: {cylinder_volume:.3f}")
|
||||
|
||||
# 批量属性设置
|
||||
for part in parts:
|
||||
volume = part.get_volume()
|
||||
grid_pos = part.get_metadata("grid_position")
|
||||
|
||||
# 根据体积分类
|
||||
if volume < 0.5:
|
||||
part.add_tag("small_part")
|
||||
elif volume < 1.5:
|
||||
part.add_tag("medium_part")
|
||||
else:
|
||||
part.add_tag("large_part")
|
||||
|
||||
# 根据位置分类
|
||||
if grid_pos[0] == 0:
|
||||
part.add_tag("left_column")
|
||||
elif grid_pos[0] == 2:
|
||||
part.add_tag("right_column")
|
||||
else:
|
||||
part.add_tag("center_column")
|
||||
|
||||
if grid_pos[1] == 0:
|
||||
part.add_tag("bottom_row")
|
||||
elif grid_pos[1] == 2:
|
||||
part.add_tag("top_row")
|
||||
else:
|
||||
part.add_tag("center_row")
|
||||
|
||||
# 设置材料属性
|
||||
if part.has_tag("box_part"):
|
||||
part.set_metadata("material", "aluminum")
|
||||
part.set_metadata("density", 2.7)
|
||||
else:
|
||||
part.set_metadata("material", "steel")
|
||||
part.set_metadata("density", 7.8)
|
||||
|
||||
# 计算质量
|
||||
density = part.get_metadata("density")
|
||||
mass = volume * density
|
||||
part.set_metadata("mass", mass)
|
||||
|
||||
# 批量质量分析
|
||||
total_mass = sum(p.get_metadata("mass") for p in parts)
|
||||
aluminum_mass = sum(p.get_metadata("mass") for p in parts if p.get_metadata("material") == "aluminum")
|
||||
steel_mass = sum(p.get_metadata("mass") for p in parts if p.get_metadata("material") == "steel")
|
||||
|
||||
print(f" 总质量: {total_mass:.3f}")
|
||||
print(f" 铝质量: {aluminum_mass:.3f}")
|
||||
print(f" 钢质量: {steel_mass:.3f}")
|
||||
|
||||
# 位置统计
|
||||
position_stats = {}
|
||||
for part in parts:
|
||||
pos = part.get_metadata("grid_position")
|
||||
if pos not in position_stats:
|
||||
position_stats[pos] = {"count": 0, "volume": 0, "mass": 0}
|
||||
|
||||
position_stats[pos]["count"] += 1
|
||||
position_stats[pos]["volume"] += part.get_volume()
|
||||
position_stats[pos]["mass"] += part.get_metadata("mass")
|
||||
|
||||
print(f" 位置统计:")
|
||||
for pos, stats in position_stats.items():
|
||||
print(f" 位置 {pos}: {stats['count']} 件, 体积: {stats['volume']:.3f}, 质量: {stats['mass']:.3f}")
|
||||
|
||||
return parts
|
||||
|
||||
batch_parts = batch_operations_on_compound()
|
||||
```
|
||||
|
||||
### Query and Filter Compound
|
||||
|
||||
```python
|
||||
from simplecadapi import make_box_rsolid, make_cylinder_rsolid, make_sphere_rsolid
|
||||
|
||||
def query_and_filter_compound():
|
||||
"""查询和筛选复合体"""
|
||||
|
||||
# 创建多样化的零件集合
|
||||
parts = []
|
||||
|
||||
# 创建不同类型的零件
|
||||
geometries = [
|
||||
("small_box", make_box_rsolid(width=1, height=1, depth=1)),
|
||||
("large_box", make_box_rsolid(width=3, height=2, depth=2)),
|
||||
("thin_cylinder", make_cylinder_rsolid(center=(0, 0, 0), radius=0.5, height=4)),
|
||||
("wide_cylinder", make_cylinder_rsolid(center=(0, 0, 0), radius=2, height=1)),
|
||||
("small_sphere", make_sphere_rsolid(center=(0, 0, 0), radius=0.8)),
|
||||
("large_sphere", make_sphere_rsolid(center=(0, 0, 0), radius=1.5))
|
||||
]
|
||||
|
||||
# 为每个零件添加详细信息
|
||||
for name, part in geometries:
|
||||
part.add_tag(name)
|
||||
|
||||
# 几何类型标签
|
||||
if "box" in name:
|
||||
part.add_tag("rectangular")
|
||||
part.add_tag("prismatic")
|
||||
elif "cylinder" in name:
|
||||
part.add_tag("cylindrical")
|
||||
part.add_tag("rotational")
|
||||
elif "sphere" in name:
|
||||
part.add_tag("spherical")
|
||||
part.add_tag("rotational")
|
||||
|
||||
# 尺寸标签
|
||||
volume = part.get_volume()
|
||||
if volume < 2:
|
||||
part.add_tag("small")
|
||||
elif volume < 10:
|
||||
part.add_tag("medium")
|
||||
else:
|
||||
part.add_tag("large")
|
||||
|
||||
# 应用标签
|
||||
if "thin" in name:
|
||||
part.add_tag("structural")
|
||||
part.set_metadata("application", "support")
|
||||
elif "wide" in name:
|
||||
part.add_tag("base")
|
||||
part.set_metadata("application", "foundation")
|
||||
else:
|
||||
part.add_tag("general")
|
||||
part.set_metadata("application", "multipurpose")
|
||||
|
||||
# 材料属性
|
||||
if part.has_tag("small"):
|
||||
part.set_metadata("material", "aluminum")
|
||||
part.set_metadata("cost_per_unit", 2.5)
|
||||
elif part.has_tag("medium"):
|
||||
part.set_metadata("material", "steel")
|
||||
part.set_metadata("cost_per_unit", 1.8)
|
||||
else:
|
||||
part.set_metadata("material", "cast_iron")
|
||||
part.set_metadata("cost_per_unit", 3.2)
|
||||
|
||||
part.set_metadata("volume", volume)
|
||||
part.set_metadata("name", name)
|
||||
|
||||
parts.append(part)
|
||||
|
||||
# 查询和筛选示例
|
||||
print(f"复合体查询和筛选:")
|
||||
print(f" 总零件数: {len(parts)}")
|
||||
|
||||
# 1. 按标签查询
|
||||
print(f"\n1. 按标签查询:")
|
||||
small_parts = [p for p in parts if p.has_tag("small")]
|
||||
cylindrical_parts = [p for p in parts if p.has_tag("cylindrical")]
|
||||
structural_parts = [p for p in parts if p.has_tag("structural")]
|
||||
|
||||
print(f" 小型零件: {len(small_parts)}")
|
||||
print(f" 圆柱形零件: {len(cylindrical_parts)}")
|
||||
print(f" 结构零件: {len(structural_parts)}")
|
||||
|
||||
# 2. 按体积范围查询
|
||||
print(f"\n2. 按体积范围查询:")
|
||||
volume_ranges = [
|
||||
("超小", 0, 1),
|
||||
("小", 1, 5),
|
||||
("中", 5, 15),
|
||||
("大", 15, float('inf'))
|
||||
]
|
||||
|
||||
for range_name, min_vol, max_vol in volume_ranges:
|
||||
range_parts = [p for p in parts if min_vol <= p.get_volume() < max_vol]
|
||||
if range_parts:
|
||||
print(f" {range_name}体积 ({min_vol}-{max_vol}): {len(range_parts)} 件")
|
||||
|
||||
# 3. 按材料查询
|
||||
print(f"\n3. 按材料查询:")
|
||||
materials = set(p.get_metadata("material") for p in parts)
|
||||
for material in materials:
|
||||
material_parts = [p for p in parts if p.get_metadata("material") == material]
|
||||
total_volume = sum(p.get_volume() for p in material_parts)
|
||||
total_cost = sum(p.get_metadata("cost_per_unit", 0) for p in material_parts)
|
||||
print(f" {material}: {len(material_parts)} 件, 总体积: {total_volume:.3f}, 总成本: {total_cost:.2f}")
|
||||
|
||||
# 4. 复合查询
|
||||
print(f"\n4. 复合查询:")
|
||||
|
||||
# 查询小型圆柱形零件
|
||||
small_cylinders = [p for p in parts if p.has_tag("small") and p.has_tag("cylindrical")]
|
||||
print(f" 小型圆柱形零件: {len(small_cylinders)}")
|
||||
|
||||
# 查询铝制零件
|
||||
aluminum_parts = [p for p in parts if p.get_metadata("material") == "aluminum"]
|
||||
print(f" 铝制零件: {len(aluminum_parts)}")
|
||||
|
||||
# 查询高成本零件
|
||||
high_cost_parts = [p for p in parts if p.get_metadata("cost_per_unit", 0) > 3.0]
|
||||
print(f" 高成本零件: {len(high_cost_parts)}")
|
||||
|
||||
# 5. 统计分析
|
||||
print(f"\n5. 统计分析:")
|
||||
|
||||
# 按几何类型统计
|
||||
geometric_types = ["rectangular", "cylindrical", "spherical"]
|
||||
for geo_type in geometric_types:
|
||||
type_parts = [p for p in parts if p.has_tag(geo_type)]
|
||||
if type_parts:
|
||||
avg_volume = sum(p.get_volume() for p in type_parts) / len(type_parts)
|
||||
print(f" {geo_type}: {len(type_parts)} 件, 平均体积: {avg_volume:.3f}")
|
||||
|
||||
# 成本效率分析
|
||||
print(f"\n6. 成本效率分析:")
|
||||
for part in parts:
|
||||
volume = part.get_volume()
|
||||
cost = part.get_metadata("cost_per_unit", 0)
|
||||
if cost > 0:
|
||||
efficiency = volume / cost
|
||||
part.set_metadata("volume_cost_efficiency", efficiency)
|
||||
|
||||
# 按效率排序
|
||||
sorted_parts = sorted(parts, key=lambda p: p.get_metadata("volume_cost_efficiency", 0), reverse=True)
|
||||
print(f" 最高效率零件: {sorted_parts[0].get_metadata('name')}, 效率: {sorted_parts[0].get_metadata('volume_cost_efficiency'):.3f}")
|
||||
print(f" 最低效率零件: {sorted_parts[-1].get_metadata('name')}, 效率: {sorted_parts[-1].get_metadata('volume_cost_efficiency'):.3f}")
|
||||
|
||||
return parts
|
||||
|
||||
filtered_parts = query_and_filter_compound()
|
||||
```
|
||||
|
||||
## String Representation
|
||||
|
||||
```python
|
||||
# 假设有一个复合体对象
|
||||
# compound = ... 某个复合体
|
||||
|
||||
compound.add_tag("assembly")
|
||||
compound.set_metadata("part_count", 5)
|
||||
compound.set_metadata("total_volume", 150.0)
|
||||
|
||||
print(compound)
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
Compound:
|
||||
solid_count: 5
|
||||
solids:
|
||||
solid_0:
|
||||
volume: 30.000
|
||||
face_count: 6
|
||||
edge_count: 12
|
||||
solid_1:
|
||||
volume: 25.000
|
||||
face_count: 8
|
||||
edge_count: 16
|
||||
solid_2:
|
||||
volume: 40.000
|
||||
face_count: 6
|
||||
edge_count: 12
|
||||
solid_3:
|
||||
volume: 35.000
|
||||
face_count: 10
|
||||
edge_count: 20
|
||||
solid_4:
|
||||
volume: 20.000
|
||||
face_count: 4
|
||||
edge_count: 8
|
||||
tags: [assembly]
|
||||
metadata:
|
||||
part_count: 5
|
||||
total_volume: 150.0
|
||||
```
|
||||
|
||||
## Relationships with Other Geometry
|
||||
|
||||
- **Solid (Solid)**: Primary components of a compound
|
||||
- **Face (Face)**: Indirectly associated through solids
|
||||
- **Edge (Edge)**: Indirectly associated through solids and faces
|
||||
|
||||
## Application Scenarios
|
||||
|
||||
- **Assembly modeling**: Complex mechanical assemblies
|
||||
- **Architectural design**: Building group modeling
|
||||
- **Product design**: Multi-component products
|
||||
- **Manufacturing planning**: Batch production management
|
||||
- **Simulation analysis**: Multi-body system analysis
|
||||
|
||||
## Management Strategies
|
||||
|
||||
### Hierarchical Management
|
||||
- Use tags and metadata to establish hierarchical structures
|
||||
- Classify by function, material, process, etc.
|
||||
- Enable fast querying and batch operations
|
||||
|
||||
### Performance Optimization
|
||||
- Organize compound structures reasonably
|
||||
- Avoid excessively deep nesting levels
|
||||
- Optimize query and filtering algorithms
|
||||
|
||||
### Data Consistency
|
||||
- Ensure metadata accuracy
|
||||
- Maintain relationships between geometry objects
|
||||
- Update statistics in a timely manner
|
||||
|
||||
## Notes
|
||||
|
||||
- Compounds may contain many geometry objects; be mindful of performance
|
||||
- Modifications to geometry objects do not automatically update compound statistics
|
||||
- Tag and metadata management requires establishing unified naming conventions
|
||||
- Complex hierarchical structures may lead to decreased query efficiency
|
||||
- Reasonable data structure design is needed to support efficient batch operations
|
||||
- When performing geometric operations, consider the interrelationships between objects in the compound
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
# CoordinateSystem
|
||||
|
||||
## Overview
|
||||
|
||||
`CoordinateSystem` is the 3D coordinate system class in SimpleCAD API, used for defining and managing coordinate systems in 3D space. SimpleCAD uses a Z-up right-handed coordinate system with origin at (0, 0, 0), X-axis forward, Y-axis right, and Z-axis up.
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class CoordinateSystem:
|
||||
"""三维坐标系
|
||||
|
||||
SimpleCAD使用Z向上的右手坐标系,原点在(0, 0, 0),X轴向前,Y轴向右,Z轴向上
|
||||
"""
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
- Define local coordinate systems
|
||||
- Coordinate transformation (local to global coordinates)
|
||||
- Conversion with CADQuery coordinate systems
|
||||
- Foundation for geometric transformations
|
||||
|
||||
## Constructor
|
||||
|
||||
### `__init__(origin, x_axis, y_axis)`
|
||||
|
||||
Initialize a coordinate system.
|
||||
|
||||
**Parameters:**
|
||||
- `origin` (Tuple[float, float, float], optional): Coordinate system origin, default (0, 0, 0)
|
||||
- `x_axis` (Tuple[float, float, float], optional): X-axis direction vector, default (1, 0, 0)
|
||||
- `y_axis` (Tuple[float, float, float], optional): Y-axis direction vector, default (0, 1, 0)
|
||||
|
||||
**Exceptions:**
|
||||
- `ValueError`: Raised when input coordinates or direction vectors are invalid
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import CoordinateSystem
|
||||
|
||||
# 默认坐标系(世界坐标系)
|
||||
world_cs = CoordinateSystem()
|
||||
|
||||
# 自定义坐标系
|
||||
custom_cs = CoordinateSystem(
|
||||
origin=(1, 2, 3),
|
||||
x_axis=(1, 0, 0),
|
||||
y_axis=(0, 1, 0)
|
||||
)
|
||||
|
||||
# 旋转的坐标系
|
||||
rotated_cs = CoordinateSystem(
|
||||
origin=(0, 0, 0),
|
||||
x_axis=(0.707, 0.707, 0), # 绕Z轴旋转45度
|
||||
y_axis=(-0.707, 0.707, 0)
|
||||
)
|
||||
```
|
||||
|
||||
## Main Properties
|
||||
|
||||
- `origin`: Coordinate system origin (numpy.ndarray)
|
||||
- `x_axis`: X-axis direction vector (numpy.ndarray)
|
||||
- `y_axis`: Y-axis direction vector (numpy.ndarray)
|
||||
- `z_axis`: Z-axis direction vector (numpy.ndarray, automatically calculated)
|
||||
|
||||
## Common Methods
|
||||
|
||||
### `transform_point(point)`
|
||||
|
||||
Transform local coordinates to global coordinates.
|
||||
|
||||
**Parameters:**
|
||||
- `point` (numpy.ndarray): Local coordinate point
|
||||
|
||||
**Returns:**
|
||||
- `numpy.ndarray`: Global coordinate point
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
import numpy as np
|
||||
from simplecadapi import CoordinateSystem
|
||||
|
||||
cs = CoordinateSystem(origin=(1, 0, 0))
|
||||
local_point = np.array([1, 0, 0])
|
||||
global_point = cs.transform_point(local_point)
|
||||
print(global_point) # [2. 0. 0.]
|
||||
```
|
||||
|
||||
### `transform_vector(vector)`
|
||||
|
||||
Transform local direction vectors to global direction vectors (excluding translation).
|
||||
|
||||
**Parameters:**
|
||||
- `vector` (numpy.ndarray): Local direction vector
|
||||
|
||||
**Returns:**
|
||||
- `numpy.ndarray`: Global direction vector
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
import numpy as np
|
||||
from simplecadapi import CoordinateSystem
|
||||
|
||||
cs = CoordinateSystem(
|
||||
origin=(0, 0, 0),
|
||||
x_axis=(0, 1, 0), # X轴指向Y方向
|
||||
y_axis=(1, 0, 0) # Y轴指向X方向
|
||||
)
|
||||
|
||||
local_vector = np.array([1, 0, 0]) # 局部X方向
|
||||
global_vector = cs.transform_vector(local_vector)
|
||||
print(global_vector) # [0. 1. 0.] (全局Y方向)
|
||||
```
|
||||
|
||||
### `to_cq_plane()`
|
||||
|
||||
Convert to CADQuery's Plane object.
|
||||
|
||||
**Returns:**
|
||||
- `cadquery.Plane`: CADQuery plane object
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import CoordinateSystem
|
||||
|
||||
cs = CoordinateSystem(origin=(0, 0, 1))
|
||||
cq_plane = cs.to_cq_plane()
|
||||
```
|
||||
|
||||
## Coordinate System Transformation
|
||||
|
||||
SimpleCAD uses Z-up coordinate system, while CADQuery uses Y-up coordinate system. The conversion rules are:
|
||||
|
||||
- SimpleCAD's X-axis (forward) → CADQuery's Z-axis (forward)
|
||||
- SimpleCAD's Y-axis (right) → CADQuery's X-axis (right)
|
||||
- SimpleCAD's Z-axis (up) → CADQuery's Y-axis (up)
|
||||
|
||||
## Global Coordinate System
|
||||
|
||||
SimpleCAD provides a global world coordinate system:
|
||||
|
||||
```python
|
||||
from simplecadapi import WORLD_CS
|
||||
|
||||
print(WORLD_CS.origin) # [0. 0. 0.]
|
||||
print(WORLD_CS.x_axis) # [1. 0. 0.]
|
||||
print(WORLD_CS.y_axis) # [0. 1. 0.]
|
||||
print(WORLD_CS.z_axis) # [0. 0. 1.]
|
||||
```
|
||||
|
||||
## String Representation
|
||||
|
||||
```python
|
||||
from simplecadapi import CoordinateSystem
|
||||
|
||||
cs = CoordinateSystem(origin=(1, 2, 3))
|
||||
print(cs)
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
CoordinateSystem:
|
||||
origin: [1.000, 2.000, 3.000]
|
||||
x_axis: [1.000, 0.000, 0.000]
|
||||
y_axis: [0.000, 1.000, 0.000]
|
||||
z_axis: [0.000, 0.000, 1.000]
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Input direction vectors are automatically normalized
|
||||
- Z-axis is automatically calculated via the cross product of X-axis and Y-axis
|
||||
- If a zero vector is input, a ValueError will be raised
|
||||
- Coordinate systems should maintain right-handed characteristics
|
||||
+306
@@ -0,0 +1,306 @@
|
||||
# Declarative Constraints Layout Design Draft
|
||||
|
||||
## Background and Goals
|
||||
|
||||
The current SimpleCADAPI is primarily imperative modeling: developers need to manually provide specific coordinates, rotation angles, and offsets. For assemblies, this approach is costly in the following scenarios:
|
||||
|
||||
1. Geometric relationships are stable but dimensions change frequently (repeated position recalculation after parameter changes).
|
||||
2. Dependencies between multiple parts are complex (one part change cascades to affect multiple parts).
|
||||
3. Need to express "relationships" rather than "values" (e.g., coaxial, fit, equidistant distribution).
|
||||
|
||||
Web layout (such as HTML/CSS Flexbox) has proven an effective direction:
|
||||
|
||||
- Users declare constraints (alignment, distribution, spacing).
|
||||
- Solvers propagate constraints in the layout tree and compute final geometric values.
|
||||
|
||||
This proposal aims to migrate this approach to the CAD SDK:
|
||||
|
||||
- Retain existing imperative APIs;
|
||||
- Add an optional declarative assembly layer;
|
||||
- Let users describe assembly relationships, with the SDK computing each part's final pose.
|
||||
|
||||
## Current Implementation Status (feat/declarative-constraints-layout)
|
||||
|
||||
The current branch provides a runnable MVP with the following core capabilities:
|
||||
|
||||
- New module: `simplecadapi.constraints`
|
||||
- New objects: `Assembly`, `PartHandle`, `PointAnchor`, `AxisAnchor`, `AssemblyResult`
|
||||
- Supports mixed paradigm:
|
||||
- First imperative pre-positioning (`translate_part` / `rotate_part`)
|
||||
- Then declarative constraint solving (`coincident` / `concentric` / `offset` / `distance`)
|
||||
- Supports 1D container syntax sugar: `stack(...)`
|
||||
- `stack(...)` adds main axis distribution parameter: `justify=start|center|end|space-between`
|
||||
- When `justify=center/end/space-between` is needed, use `bounds=(start_anchor, end_anchor)` to specify the container's main axis boundary.
|
||||
|
||||
Current layout implementation uses a **BBox-first** strategy:
|
||||
|
||||
- Alignment and distribution are primarily based on `bbox.*` anchors (AABB).
|
||||
- This is consistent with Flexbox's box model thinking, but is approximate in 3D:
|
||||
When parts rotate, AABB changes and layout results change accordingly.
|
||||
- OBB/feature face anchors can be added later to reduce approximation errors from rotation.
|
||||
- Supports assembly tree parent-child relationships and local/world transform propagation.
|
||||
|
||||
Framework-style constraints (functional) have been explicitly categorized into two types of mappings:
|
||||
|
||||
1. **Type-1 lifting mappings** (parameter space -> CAD object space)
|
||||
- E.g.: `make_*_rsolid`, `make_assembly_rassembly`
|
||||
2. **Type-2 algebraic transform mappings** (CAD object space -> CAD object space/result space)
|
||||
- E.g.: `translate_part_rassembly`, `constrain_offset_rassembly`, `stack_rassembly`
|
||||
- Solving uses `solve_assembly_rresult`, ensuring input assembly objects are not modified
|
||||
|
||||
Corresponding functional APIs (do not modify input) include:
|
||||
|
||||
- `make_assembly_rassembly`
|
||||
- `clone_assembly_rassembly`
|
||||
- `add_part_rassembly`
|
||||
- `translate_part_rassembly` / `rotate_part_rassembly`
|
||||
- `constrain_coincident_rassembly` / `constrain_concentric_rassembly`
|
||||
- `constrain_offset_rassembly` / `constrain_distance_rassembly`
|
||||
- `stack_rassembly`
|
||||
- `solve_assembly_rresult`
|
||||
|
||||
Functional pipeline example:
|
||||
|
||||
```python
|
||||
import simplecadapi as scad
|
||||
|
||||
asm0 = scad.make_assembly_rassembly([
|
||||
("sleeve", sleeve_solid),
|
||||
("rod", rod_solid),
|
||||
])
|
||||
|
||||
asm1 = scad.translate_part_rassembly(asm0, "rod", (3.0, -2.0, 4.0))
|
||||
asm2 = scad.constrain_concentric_rassembly(
|
||||
asm1,
|
||||
asm1.part("sleeve").axis("z"),
|
||||
asm1.part("rod").axis("z"),
|
||||
)
|
||||
asm3 = scad.constrain_offset_rassembly(
|
||||
asm2,
|
||||
asm2.part("sleeve").anchor("bbox.bottom"),
|
||||
asm2.part("rod").anchor("bbox.bottom"),
|
||||
3.0,
|
||||
axis="z",
|
||||
)
|
||||
|
||||
result = scad.solve_assembly_rresult(asm3)
|
||||
```
|
||||
|
||||
Example (mixed usage):
|
||||
|
||||
```python
|
||||
import simplecadapi as scad
|
||||
|
||||
asm = scad.Assembly("demo")
|
||||
sleeve = asm.add_part("sleeve", sleeve_solid)
|
||||
rod = asm.add_part("rod", rod_solid)
|
||||
|
||||
# 命令式预定位
|
||||
asm.translate_part("rod", (3.0, -2.0, 4.0), frame="world")
|
||||
|
||||
# 声明式约束
|
||||
asm.concentric(sleeve.axis("z"), rod.axis("z"))
|
||||
asm.offset(sleeve.anchor("bbox.bottom"), rod.anchor("bbox.bottom"), 3.0, axis="z")
|
||||
|
||||
result = asm.solve()
|
||||
scad.export_step(result.solids(), "assembly.step")
|
||||
```
|
||||
|
||||
## Scope
|
||||
|
||||
### In Scope (Phase 1)
|
||||
|
||||
- Assembly pose solving (rigid body 6DOF, no part topology modification).
|
||||
- Basic constraint types:
|
||||
- `coincident` (point/plane/axis coincidence)
|
||||
- `concentric` (coaxial)
|
||||
- `parallel` / `perpendicular` (directional relationships)
|
||||
- `distance` (spacing)
|
||||
- `offset` (offset along normal or axis direction)
|
||||
- "Flex-like" 1D layout containers:
|
||||
- `stack(axis="x|y|z")`
|
||||
- `gap`
|
||||
- `justify` (start/center/end/space-between)
|
||||
- `align` (start/center/end/stretch*)
|
||||
|
||||
`stretch` in CAD does not perform geometric stretching; it only means aligning to the alignment baseline.
|
||||
|
||||
### Out of Scope (Phase 1)
|
||||
|
||||
- Parameter-driven topology rebuilding (e.g., automatic hole diameter changes, chamfer regeneration).
|
||||
- General nonlinear symbolic solvers (CAS level).
|
||||
- Complete sketch constraint system (2D sketch solver).
|
||||
|
||||
## Core Abstractions
|
||||
|
||||
### 1) Assembly Node
|
||||
|
||||
Each node contains:
|
||||
|
||||
- `name`
|
||||
- `solid`
|
||||
- `local frame` (node local coordinate system)
|
||||
- `current transform` (variables to be solved)
|
||||
- `anchors` (anchor points that can be referenced by constraints)
|
||||
|
||||
### 2) Anchor
|
||||
|
||||
Anchors are used to extract constrainable objects from geometry:
|
||||
|
||||
- `point`: 3D point
|
||||
- `axis`: Directed line (point + direction)
|
||||
- `plane`: Plane (point + normal)
|
||||
- `frame`: Local coordinate system
|
||||
|
||||
Suggested built-in anchor sources:
|
||||
|
||||
- Bounding box: `bbox.min/max/center`
|
||||
- Principal axes: `axis.x/y/z`
|
||||
- Named faces: `face("top")`, `face("bottom")` (reuse existing tag mechanism)
|
||||
|
||||
### 3) Constraint
|
||||
|
||||
Constraints consist of:
|
||||
|
||||
- `type`
|
||||
- `lhs anchor` / `rhs anchor`
|
||||
- `value` (optional, e.g., distance)
|
||||
- `priority` (hard/soft)
|
||||
- `weight` (soft constraint weight)
|
||||
|
||||
### 4) Layout Container
|
||||
|
||||
Containers are constraint syntax sugar, compiled into a set of basic constraints:
|
||||
|
||||
- E.g., `stack([A,B,C], axis="z", gap=8)`
|
||||
- `B.min_z = A.max_z + 8`
|
||||
- `C.min_z = B.max_z + 8`
|
||||
- Plus alignment constraints (e.g., XY centering)
|
||||
|
||||
## API Draft
|
||||
|
||||
```python
|
||||
import simplecadapi as scad
|
||||
from simplecadapi.constraints import Assembly, stack
|
||||
|
||||
asm = Assembly(name="shock_absorber")
|
||||
|
||||
sleeve = asm.add_part("sleeve", sleeve_solid)
|
||||
rod = asm.add_part("rod", rod_solid)
|
||||
spring = asm.add_part("spring", spring_solid)
|
||||
|
||||
asm.concentric(rod.axis("z"), sleeve.axis("z"))
|
||||
asm.offset(rod.anchor("bottom"), sleeve.anchor("bottom"), 10.0)
|
||||
asm.distance(rod.anchor("top"), sleeve.anchor("top"), min_value=5.0)
|
||||
|
||||
stack(
|
||||
asm,
|
||||
parts=[spring],
|
||||
axis="z",
|
||||
relative_to=sleeve,
|
||||
align="center",
|
||||
justify="start",
|
||||
gap=8.0,
|
||||
)
|
||||
|
||||
result = asm.solve()
|
||||
solids = result.solids()
|
||||
scad.export_step(solids, "shock_absorber_assembly.step")
|
||||
```
|
||||
|
||||
## Solving Strategy (Layered)
|
||||
|
||||
### Layer A: Parsing and Normalization
|
||||
|
||||
- Convert constraints and container rules into residual equations.
|
||||
- Map anchor references to real-time geometric query functions.
|
||||
|
||||
### Layer B: Graph Constraint Propagation (Fast Path)
|
||||
|
||||
- First perform topological solving for directly propagatable rigid constraints:
|
||||
- Coaxial + offset + alignment can directly derive partial poses.
|
||||
- Build dependency graph and perform incremental updates (dirty propagation).
|
||||
|
||||
### Layer C: Numerical Solving (Fallback)
|
||||
|
||||
- Use least squares solving for remaining degrees of freedom (hard constraints have highest priority).
|
||||
- For over-constrained or contradictory constraints, output diagnostic reports:
|
||||
- Conflicting constraint pairs
|
||||
- Constraints with highest residuals
|
||||
- Recommended relaxation items (downgrade from hard to soft)
|
||||
|
||||
## Result Model
|
||||
|
||||
`solve()` returns an object that should contain:
|
||||
|
||||
- `transforms`: Final pose for each part
|
||||
- `solids()`: List of solids with poses applied
|
||||
- `report`: Solving information
|
||||
- Whether converged
|
||||
- Number of iterations
|
||||
- Maximum residual
|
||||
- Conflict/over-constraint description
|
||||
|
||||
## Compatibility Strategy with Existing API
|
||||
|
||||
1. No changes to existing `operations.py` imperative interfaces.
|
||||
2. New independent module (suggested `simplecadapi.constraints`).
|
||||
3. Export still reuses `export_step` / `export_stl`, can directly export `result.solids()`.
|
||||
|
||||
## Milestone Plan
|
||||
|
||||
### M0 - Design and Feasibility Verification (Current Phase)
|
||||
|
||||
- Output this design document.
|
||||
- Define minimum API surface.
|
||||
- Select first batch of constraint types and diagnostic formats.
|
||||
|
||||
### M1 - Minimum Viable Prototype (MVP)
|
||||
|
||||
- `Assembly.add_part()`
|
||||
- Anchors: `bbox` + `axis` + `face tag`
|
||||
- Constraints: `concentric` + `offset` + `distance`
|
||||
- Solving: Support single-chain assemblies (no loops)
|
||||
|
||||
### M2 - Flex-like Layout Containers
|
||||
|
||||
- `stack(axis, gap, align, justify)`
|
||||
- Compile container rules into constraints
|
||||
- Support simple incremental updates
|
||||
|
||||
### M3 - Diagnostics and Engineering
|
||||
|
||||
- Conflict localization and interpretable error messages
|
||||
- Solving logs and visual output (text reports)
|
||||
- Unit test coverage for typical assembly scenarios
|
||||
|
||||
### M4 - Advanced Capabilities
|
||||
|
||||
- Soft constraint weight system
|
||||
- Complex constraint loops
|
||||
- Performance optimization (caching, partitioned solving)
|
||||
|
||||
## Testing Recommendations
|
||||
|
||||
At least cover the following scenarios:
|
||||
|
||||
1. **Basic convergence**: Coaxial + offset + alignment, unique result.
|
||||
2. **Under-constrained**: Clear warning when degrees of freedom are not locked.
|
||||
3. **Over-constrained**: Conflict diagnostics when constraints are contradictory.
|
||||
4. **Incremental updates**: Modifying a single parameter triggers only local recalculation.
|
||||
5. **Export consistency**: `result.solids()` can directly export STEP/STL.
|
||||
|
||||
## Risks and Mitigations
|
||||
|
||||
- **Risk:** Constraint semantics too abstract, making the API hard to use.
|
||||
**Mitigation:** Start with high-frequency assembly scenarios, providing only a few strongly semantic constraints.
|
||||
|
||||
- **Risk:** Numerical solving is unstable.
|
||||
**Mitigation:** First do graph propagation fast path; numerical solving only handles remaining degrees of freedom.
|
||||
|
||||
- **Risk:** Conflict with existing user code.
|
||||
**Mitigation:** New module isolation, disabled by default, strictly maintain backward compatibility.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Migrating the Flexbox-style "declare relationships -> solve geometry" paradigm to the CAD SDK is feasible and can significantly improve assembly modeling efficiency and maintainability. It is recommended to use "assembly pose constraints + 1D container layout" as the Phase 1 entry point, deliver an MVP first, and then gradually enhance solving capabilities and diagnostic experience.
|
||||
+389
@@ -0,0 +1,389 @@
|
||||
# Edge
|
||||
|
||||
## Overview
|
||||
|
||||
`Edge` is the edge class in SimpleCAD API, representing a 1D geometric element connecting two vertices. Edges can be lines, arcs, splines, and other types of curves. It wraps CADQuery's Edge object and adds tag functionality.
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class Edge(TaggedMixin):
|
||||
"""边类,包装CADQuery的Edge,添加标签功能"""
|
||||
```
|
||||
|
||||
## Inheritance Relationships
|
||||
|
||||
- Inherits from `TaggedMixin`, with tag and metadata functionality
|
||||
|
||||
## Usage
|
||||
|
||||
- Represent connections between two points
|
||||
- Fundamental elements composing Wires and Faces
|
||||
- Provide geometric information (length, vertices, etc.)
|
||||
- Support tag management and queries
|
||||
|
||||
## Constructor
|
||||
|
||||
### `__init__(cq_edge)`
|
||||
|
||||
Initialize an edge object.
|
||||
|
||||
**Parameters:**
|
||||
- `cq_edge` (cadquery.Edge): CADQuery edge object
|
||||
|
||||
**Exceptions:**
|
||||
- `ValueError`: Raised when the input edge object is invalid
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_line_redge, make_circle_redge
|
||||
|
||||
# 通过 SimpleCAD 函数创建边
|
||||
line_edge = make_line_redge(start=(0, 0, 0), end=(1, 1, 0))
|
||||
circle_edge = make_circle_redge(center=(0, 0, 0), radius=1.0)
|
||||
```
|
||||
|
||||
## Main Properties
|
||||
|
||||
- `cq_edge`: Underlying CADQuery edge object
|
||||
- `_tags`: Tag set (inherited from TaggedMixin)
|
||||
- `_metadata`: Metadata dictionary (inherited from TaggedMixin)
|
||||
|
||||
## Common Methods
|
||||
|
||||
### `get_length()`
|
||||
|
||||
Get the length of the edge.
|
||||
|
||||
**Returns:**
|
||||
- `float`: Edge length
|
||||
|
||||
**Exceptions:**
|
||||
- `ValueError`: Raised when length retrieval fails
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_line_redge, make_circle_redge
|
||||
import math
|
||||
|
||||
# 直线边
|
||||
line = make_line_redge(start=(0, 0, 0), end=(3, 4, 0))
|
||||
line_length = line.get_length()
|
||||
print(f"直线长度: {line_length}") # 5.0
|
||||
|
||||
# 圆形边
|
||||
circle = make_circle_redge(center=(0, 0, 0), radius=2.0)
|
||||
circle_length = circle.get_length()
|
||||
print(f"圆形周长: {circle_length}") # 约 12.566 (2π * 2)
|
||||
```
|
||||
|
||||
### `get_start_vertex()`
|
||||
|
||||
Get the start vertex of the edge.
|
||||
|
||||
**Returns:**
|
||||
- `Vertex`: Start vertex object
|
||||
|
||||
**Exceptions:**
|
||||
- `ValueError`: Raised when vertex retrieval fails
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_line_redge
|
||||
|
||||
line = make_line_redge(start=(1, 2, 3), end=(4, 5, 6))
|
||||
start_vertex = line.get_start_vertex()
|
||||
start_coords = start_vertex.get_coordinates()
|
||||
print(f"起始点坐标: {start_coords}") # (1.0, 2.0, 3.0)
|
||||
```
|
||||
|
||||
### `get_end_vertex()`
|
||||
|
||||
Get the end vertex of the edge.
|
||||
|
||||
**Returns:**
|
||||
- `Vertex`: End vertex object
|
||||
|
||||
**Exceptions:**
|
||||
- `ValueError`: Raised when vertex retrieval fails
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_line_redge
|
||||
|
||||
line = make_line_redge(start=(1, 2, 3), end=(4, 5, 6))
|
||||
end_vertex = line.get_end_vertex()
|
||||
end_coords = end_vertex.get_coordinates()
|
||||
print(f"结束点坐标: {end_coords}") # (4.0, 5.0, 6.0)
|
||||
```
|
||||
|
||||
### Tag Management Methods
|
||||
|
||||
Methods inherited from `TaggedMixin`:
|
||||
|
||||
#### `add_tag(tag)`, `has_tag(tag)`, `get_tags()`, `remove_tag(tag)`
|
||||
#### `set_metadata(key, value)`, `get_metadata(key, default=None)`
|
||||
|
||||
Usage is similar to Vertex; see [Vertex documentation](vertex.md) for details.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Creating Different Types of Edges
|
||||
|
||||
```python
|
||||
from simplecadapi import (
|
||||
make_line_redge,
|
||||
make_circle_redge,
|
||||
make_three_point_arc_redge,
|
||||
make_spline_redge
|
||||
)
|
||||
|
||||
# 直线边
|
||||
line = make_line_redge(start=(0, 0, 0), end=(5, 0, 0))
|
||||
line.add_tag("base_line")
|
||||
|
||||
# 圆形边
|
||||
circle = make_circle_redge(center=(0, 0, 0), radius=2.0)
|
||||
circle.add_tag("full_circle")
|
||||
|
||||
# 三点圆弧边
|
||||
arc = make_three_point_arc_redge(
|
||||
start=(0, 0, 0),
|
||||
mid=(1, 1, 0),
|
||||
end=(2, 0, 0)
|
||||
)
|
||||
arc.add_tag("arc_segment")
|
||||
|
||||
# 样条边
|
||||
spline = make_spline_redge(points=[(0, 0, 0), (1, 1, 0), (2, 0, 0), (3, 1, 0)])
|
||||
spline.add_tag("smooth_curve")
|
||||
|
||||
# 打印边的信息
|
||||
edges = [line, circle, arc, spline]
|
||||
for edge in edges:
|
||||
print(f"边标签: {edge.get_tags()}, 长度: {edge.get_length():.3f}")
|
||||
```
|
||||
|
||||
### Edge Analysis and Classification
|
||||
|
||||
```python
|
||||
from simplecadapi import make_line_redge
|
||||
import math
|
||||
|
||||
def analyze_edge_collection():
|
||||
"""分析边的集合"""
|
||||
|
||||
# 创建多条边
|
||||
edges = [
|
||||
make_line_redge(start=(0, 0, 0), end=(1, 0, 0)), # 水平线
|
||||
make_line_redge(start=(0, 0, 0), end=(0, 1, 0)), # 垂直线
|
||||
make_line_redge(start=(0, 0, 0), end=(1, 1, 0)), # 对角线
|
||||
make_line_redge(start=(0, 0, 0), end=(2, 0, 0)), # 长水平线
|
||||
make_line_redge(start=(0, 0, 0), end=(0, 2, 0)), # 长垂直线
|
||||
]
|
||||
|
||||
# 分析每条边
|
||||
for i, edge in enumerate(edges):
|
||||
length = edge.get_length()
|
||||
start_coords = edge.get_start_vertex().get_coordinates()
|
||||
end_coords = edge.get_end_vertex().get_coordinates()
|
||||
|
||||
# 计算方向向量
|
||||
direction = (
|
||||
end_coords[0] - start_coords[0],
|
||||
end_coords[1] - start_coords[1],
|
||||
end_coords[2] - start_coords[2]
|
||||
)
|
||||
|
||||
# 分类边
|
||||
if abs(direction[0]) > 0 and abs(direction[1]) == 0:
|
||||
edge.add_tag("horizontal")
|
||||
elif abs(direction[0]) == 0 and abs(direction[1]) > 0:
|
||||
edge.add_tag("vertical")
|
||||
elif abs(direction[0]) > 0 and abs(direction[1]) > 0:
|
||||
edge.add_tag("diagonal")
|
||||
|
||||
# 根据长度分类
|
||||
if length < 1.5:
|
||||
edge.add_tag("short")
|
||||
else:
|
||||
edge.add_tag("long")
|
||||
|
||||
# 添加元数据
|
||||
edge.set_metadata("length", length)
|
||||
edge.set_metadata("direction", direction)
|
||||
edge.set_metadata("index", i)
|
||||
|
||||
print(f"边 {i}: 长度={length:.3f}, 标签={edge.get_tags()}")
|
||||
|
||||
analyze_edge_collection()
|
||||
```
|
||||
|
||||
### Building Edge Networks
|
||||
|
||||
```python
|
||||
from simplecadapi import make_line_redge
|
||||
|
||||
def create_edge_network():
|
||||
"""创建边的网络结构"""
|
||||
|
||||
# 定义节点
|
||||
nodes = [
|
||||
(0, 0, 0), # A
|
||||
(2, 0, 0), # B
|
||||
(2, 2, 0), # C
|
||||
(0, 2, 0), # D
|
||||
(1, 1, 0), # E (中心点)
|
||||
]
|
||||
|
||||
# 定义连接关系
|
||||
connections = [
|
||||
(0, 1), # A-B
|
||||
(1, 2), # B-C
|
||||
(2, 3), # C-D
|
||||
(3, 0), # D-A
|
||||
(0, 4), # A-E
|
||||
(1, 4), # B-E
|
||||
(2, 4), # C-E
|
||||
(3, 4), # D-E
|
||||
]
|
||||
|
||||
edges = []
|
||||
|
||||
for i, (start_idx, end_idx) in enumerate(connections):
|
||||
start_point = nodes[start_idx]
|
||||
end_point = nodes[end_idx]
|
||||
|
||||
edge = make_line_redge(start=start_point, end=end_point)
|
||||
|
||||
# 添加连接信息
|
||||
edge.add_tag(f"connection_{chr(65+start_idx)}{chr(65+end_idx)}")
|
||||
|
||||
# 分类边
|
||||
if start_idx < 4 and end_idx < 4:
|
||||
edge.add_tag("perimeter")
|
||||
else:
|
||||
edge.add_tag("internal")
|
||||
|
||||
# 添加元数据
|
||||
edge.set_metadata("start_node", chr(65+start_idx))
|
||||
edge.set_metadata("end_node", chr(65+end_idx))
|
||||
edge.set_metadata("connection_index", i)
|
||||
|
||||
edges.append(edge)
|
||||
|
||||
return edges
|
||||
|
||||
# 创建网络
|
||||
network_edges = create_edge_network()
|
||||
|
||||
# 分析网络
|
||||
perimeter_edges = [e for e in network_edges if e.has_tag("perimeter")]
|
||||
internal_edges = [e for e in network_edges if e.has_tag("internal")]
|
||||
|
||||
print(f"周边边数: {len(perimeter_edges)}")
|
||||
print(f"内部边数: {len(internal_edges)}")
|
||||
|
||||
# 计算总长度
|
||||
total_length = sum(edge.get_length() for edge in network_edges)
|
||||
print(f"网络总长度: {total_length:.3f}")
|
||||
```
|
||||
|
||||
### Edge Geometric Calculations
|
||||
|
||||
```python
|
||||
from simplecadapi import make_line_redge, make_circle_redge
|
||||
import math
|
||||
|
||||
def calculate_edge_properties():
|
||||
"""计算边的几何属性"""
|
||||
|
||||
# 创建不同类型的边
|
||||
line = make_line_redge(start=(0, 0, 0), end=(3, 4, 0))
|
||||
circle = make_circle_redge(center=(0, 0, 0), radius=5.0)
|
||||
|
||||
# 直线属性
|
||||
line_length = line.get_length()
|
||||
line_start = line.get_start_vertex().get_coordinates()
|
||||
line_end = line.get_end_vertex().get_coordinates()
|
||||
|
||||
# 计算直线的中点
|
||||
line_midpoint = (
|
||||
(line_start[0] + line_end[0]) / 2,
|
||||
(line_start[1] + line_end[1]) / 2,
|
||||
(line_start[2] + line_end[2]) / 2
|
||||
)
|
||||
|
||||
# 计算直线的方向向量
|
||||
line_direction = (
|
||||
line_end[0] - line_start[0],
|
||||
line_end[1] - line_start[1],
|
||||
line_end[2] - line_start[2]
|
||||
)
|
||||
|
||||
# 归一化方向向量
|
||||
line_dir_length = math.sqrt(sum(x*x for x in line_direction))
|
||||
line_unit_direction = tuple(x / line_dir_length for x in line_direction)
|
||||
|
||||
# 圆形属性
|
||||
circle_length = circle.get_length() # 周长
|
||||
circle_radius = circle_length / (2 * math.pi)
|
||||
|
||||
# 存储计算结果
|
||||
line.set_metadata("midpoint", line_midpoint)
|
||||
line.set_metadata("direction", line_direction)
|
||||
line.set_metadata("unit_direction", line_unit_direction)
|
||||
line.add_tag("calculated")
|
||||
|
||||
circle.set_metadata("radius", circle_radius)
|
||||
circle.set_metadata("circumference", circle_length)
|
||||
circle.add_tag("calculated")
|
||||
|
||||
print(f"直线长度: {line_length:.3f}")
|
||||
print(f"直线中点: {line_midpoint}")
|
||||
print(f"直线单位方向: {line_unit_direction}")
|
||||
print(f"圆形周长: {circle_length:.3f}")
|
||||
print(f"圆形半径: {circle_radius:.3f}")
|
||||
|
||||
calculate_edge_properties()
|
||||
```
|
||||
|
||||
## String Representation
|
||||
|
||||
```python
|
||||
from simplecadapi import make_line_redge
|
||||
|
||||
edge = make_line_redge(start=(0, 0, 0), end=(3, 4, 0))
|
||||
edge.add_tag("example_edge")
|
||||
edge.set_metadata("type", "line")
|
||||
|
||||
print(edge)
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
Edge:
|
||||
length: 5.000
|
||||
vertices:
|
||||
start: (0.0, 0.0, 0.0)
|
||||
end: (3.0, 4.0, 0.0)
|
||||
tags: [example_edge]
|
||||
metadata:
|
||||
type: line
|
||||
```
|
||||
|
||||
## Relationships with Other Geometries
|
||||
|
||||
- **Vertex**: Endpoints of edges
|
||||
- **Wire**: Composed of multiple connected edges
|
||||
- **Face**: Boundary defined by edges (via wires)
|
||||
- **Solid**: Ultimately composed of faces formed by edges
|
||||
|
||||
## Notes
|
||||
|
||||
- Edge length is determined by its geometry and cannot be directly modified
|
||||
- Circular edges are complete circles with identical start and end vertices
|
||||
- Spline edge lengths are approximate values and may have precision errors
|
||||
- Edge directionality may affect certain operations
|
||||
- Tags and metadata do not affect edge geometry properties
|
||||
- When retrieving vertices, for closed edges like circular edges, start and end vertices may be identical
|
||||
+550
@@ -0,0 +1,550 @@
|
||||
# Face
|
||||
|
||||
## Overview
|
||||
|
||||
`Face` is the face class in the SimpleCAD API, representing 2D surface geometry. A face is bounded by one or more wires, including an outer boundary and possibly inner boundaries (holes). It wraps the CADQuery Face object and adds tagging functionality.
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class Face(TaggedMixin):
|
||||
"""面类,包装CADQuery的Face,添加标签功能"""
|
||||
```
|
||||
|
||||
## Inheritance
|
||||
|
||||
- Inherits from `TaggedMixin`, providing tag and metadata functionality
|
||||
|
||||
## Usage
|
||||
|
||||
- Represent 2D surface areas
|
||||
- Form the boundary of solids (Solid)
|
||||
- Define cross-sections for sweep, extrude, and other operations
|
||||
- Calculate geometric properties such as area and normal vectors
|
||||
|
||||
## Constructor
|
||||
|
||||
### `__init__(cq_face)`
|
||||
|
||||
Initializes a face object.
|
||||
|
||||
**Parameters:**
|
||||
- `cq_face` (cadquery.Face): A CADQuery face object
|
||||
|
||||
**Raises:**
|
||||
- `ValueError`: When the input face object is invalid
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import (
|
||||
make_rectangle_rface,
|
||||
make_circle_rface,
|
||||
make_face_from_wire_rface,
|
||||
make_rectangle_rwire
|
||||
)
|
||||
|
||||
# 通过 SimpleCAD 函数创建面
|
||||
rectangle = make_rectangle_rface(width=5, height=3)
|
||||
circle = make_circle_rface(center=(0, 0, 0), radius=2.0)
|
||||
|
||||
# 从线创建面
|
||||
wire = make_rectangle_rwire(width=4, height=4)
|
||||
face_from_wire = make_face_from_wire_rface(wire)
|
||||
```
|
||||
|
||||
## Main Properties
|
||||
|
||||
- `cq_face`: The underlying CADQuery face object
|
||||
- `_tags`: Tag set (inherited from TaggedMixin)
|
||||
- `_metadata`: Metadata dictionary (inherited from TaggedMixin)
|
||||
|
||||
## Common Methods
|
||||
|
||||
### `get_area()`
|
||||
|
||||
Get the area of the face.
|
||||
|
||||
**Returns:**
|
||||
- `float`: The area of the face
|
||||
|
||||
**Raises:**
|
||||
- `ValueError`: When area retrieval fails
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_rectangle_rface, make_circle_rface
|
||||
import math
|
||||
|
||||
# 矩形面
|
||||
rectangle = make_rectangle_rface(width=5, height=3)
|
||||
rect_area = rectangle.get_area()
|
||||
print(f"矩形面积: {rect_area}") # 15.0
|
||||
|
||||
# 圆形面
|
||||
circle = make_circle_rface(center=(0, 0, 0), radius=2.0)
|
||||
circle_area = circle.get_area()
|
||||
expected_area = math.pi * 2.0 * 2.0
|
||||
print(f"圆形面积: {circle_area:.3f}, 期望: {expected_area:.3f}")
|
||||
```
|
||||
|
||||
### `get_normal_at(u, v)`
|
||||
|
||||
Get the normal vector of the face at the specified parameter position.
|
||||
|
||||
**Parameters:**
|
||||
- `u` (float, optional): U parameter, default 0.5
|
||||
- `v` (float, optional): V parameter, default 0.5
|
||||
|
||||
**Returns:**
|
||||
- `cadquery.Vector`: Normal vector
|
||||
|
||||
**Raises:**
|
||||
- `ValueError`: When normal vector retrieval fails
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_rectangle_rface
|
||||
|
||||
rectangle = make_rectangle_rface(width=5, height=3)
|
||||
normal = rectangle.get_normal_at()
|
||||
print(f"法向量: ({normal.x:.3f}, {normal.y:.3f}, {normal.z:.3f})")
|
||||
```
|
||||
|
||||
### `get_outer_wire()`
|
||||
|
||||
Get the outer boundary wire of the face.
|
||||
|
||||
**Returns:**
|
||||
- `Wire`: Outer boundary wire object
|
||||
|
||||
**Raises:**
|
||||
- `ValueError`: When outer boundary wire retrieval fails
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_rectangle_rface
|
||||
|
||||
rectangle = make_rectangle_rface(width=5, height=3)
|
||||
outer_wire = rectangle.get_outer_wire()
|
||||
edges = outer_wire.get_edges()
|
||||
print(f"外边界由 {len(edges)} 条边组成")
|
||||
```
|
||||
|
||||
### Tag Management Methods
|
||||
|
||||
Methods inherited from `TaggedMixin`:
|
||||
|
||||
#### `add_tag(tag)`, `has_tag(tag)`, `get_tags()`, `remove_tag(tag)`
|
||||
#### `set_metadata(key, value)`, `get_metadata(key, default=None)`
|
||||
|
||||
Usage is similar to Vertex; see [Vertex documentation](vertex.md) for details.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Creating Different Types of Faces
|
||||
|
||||
```python
|
||||
from simplecadapi import (
|
||||
make_rectangle_rface,
|
||||
make_circle_rface,
|
||||
make_face_from_wire_rface,
|
||||
make_polyline_rwire
|
||||
)
|
||||
|
||||
# 矩形面
|
||||
rectangle = make_rectangle_rface(width=10, height=6)
|
||||
rectangle.add_tag("rectangle")
|
||||
rectangle.add_tag("quadrilateral")
|
||||
|
||||
# 圆形面
|
||||
circle = make_circle_rface(center=(0, 0, 0), radius=3.0)
|
||||
circle.add_tag("circle")
|
||||
circle.add_tag("curved")
|
||||
|
||||
# 复杂多边形面
|
||||
points = [
|
||||
(0, 0, 0), (4, 0, 0), (4, 3, 0), (2, 5, 0), (0, 3, 0), (0, 0, 0)
|
||||
]
|
||||
polygon_wire = make_polyline_rwire(points=points)
|
||||
polygon = make_face_from_wire_rface(polygon_wire)
|
||||
polygon.add_tag("polygon")
|
||||
polygon.add_tag("complex")
|
||||
|
||||
# 分析面的属性
|
||||
faces = [rectangle, circle, polygon]
|
||||
for face in faces:
|
||||
area = face.get_area()
|
||||
normal = face.get_normal_at()
|
||||
outer_wire = face.get_outer_wire()
|
||||
edges = outer_wire.get_edges()
|
||||
tags = face.get_tags()
|
||||
|
||||
print(f"面类型: {tags}")
|
||||
print(f" 面积: {area:.3f}")
|
||||
print(f" 法向量: ({normal.x:.3f}, {normal.y:.3f}, {normal.z:.3f})")
|
||||
print(f" 边数: {len(edges)}")
|
||||
print()
|
||||
```
|
||||
|
||||
### Geometric Analysis of Faces
|
||||
|
||||
```python
|
||||
from simplecadapi import make_rectangle_rface, make_circle_rface
|
||||
import math
|
||||
|
||||
def analyze_face_geometry():
|
||||
"""分析面的几何属性"""
|
||||
|
||||
# 创建不同尺寸的矩形
|
||||
rectangles = [
|
||||
make_rectangle_rface(width=2, height=3),
|
||||
make_rectangle_rface(width=4, height=4),
|
||||
make_rectangle_rface(width=6, height=2)
|
||||
]
|
||||
|
||||
# 创建不同半径的圆
|
||||
circles = [
|
||||
make_circle_rface(center=(0, 0, 0), radius=1.0),
|
||||
make_circle_rface(center=(0, 0, 0), radius=2.0),
|
||||
make_circle_rface(center=(0, 0, 0), radius=3.0)
|
||||
]
|
||||
|
||||
# 分析矩形
|
||||
for i, rect in enumerate(rectangles):
|
||||
area = rect.get_area()
|
||||
outer_wire = rect.get_outer_wire()
|
||||
edges = outer_wire.get_edges()
|
||||
|
||||
# 计算周长
|
||||
perimeter = sum(edge.get_length() for edge in edges)
|
||||
|
||||
# 计算长宽比
|
||||
lengths = [edge.get_length() for edge in edges]
|
||||
lengths.sort()
|
||||
aspect_ratio = lengths[1] / lengths[0] if lengths[0] > 0 else 1.0
|
||||
|
||||
rect.add_tag(f"rectangle_{i}")
|
||||
rect.set_metadata("area", area)
|
||||
rect.set_metadata("perimeter", perimeter)
|
||||
rect.set_metadata("aspect_ratio", aspect_ratio)
|
||||
|
||||
if aspect_ratio == 1.0:
|
||||
rect.add_tag("square")
|
||||
elif aspect_ratio > 2.0:
|
||||
rect.add_tag("elongated")
|
||||
|
||||
print(f"矩形 {i}: 面积={area:.3f}, 周长={perimeter:.3f}, 长宽比={aspect_ratio:.3f}")
|
||||
|
||||
# 分析圆形
|
||||
for i, circle in enumerate(circles):
|
||||
area = circle.get_area()
|
||||
outer_wire = circle.get_outer_wire()
|
||||
edges = outer_wire.get_edges()
|
||||
|
||||
# 计算周长(圆周长)
|
||||
perimeter = sum(edge.get_length() for edge in edges)
|
||||
|
||||
# 从面积计算半径
|
||||
radius_from_area = math.sqrt(area / math.pi)
|
||||
|
||||
# 从周长计算半径
|
||||
radius_from_perimeter = perimeter / (2 * math.pi)
|
||||
|
||||
circle.add_tag(f"circle_{i}")
|
||||
circle.set_metadata("area", area)
|
||||
circle.set_metadata("perimeter", perimeter)
|
||||
circle.set_metadata("radius_from_area", radius_from_area)
|
||||
circle.set_metadata("radius_from_perimeter", radius_from_perimeter)
|
||||
|
||||
if radius_from_area < 1.5:
|
||||
circle.add_tag("small")
|
||||
elif radius_from_area > 2.5:
|
||||
circle.add_tag("large")
|
||||
else:
|
||||
circle.add_tag("medium")
|
||||
|
||||
print(f"圆形 {i}: 面积={area:.3f}, 周长={perimeter:.3f}, 半径={radius_from_area:.3f}")
|
||||
|
||||
analyze_face_geometry()
|
||||
```
|
||||
|
||||
### Faces with Holes
|
||||
|
||||
```python
|
||||
from simplecadapi import (
|
||||
make_rectangle_rface,
|
||||
make_circle_rface,
|
||||
make_face_from_wire_rface,
|
||||
make_rectangle_rwire,
|
||||
make_circle_rwire
|
||||
)
|
||||
|
||||
def create_face_with_holes():
|
||||
"""创建带孔的面(概念示例)"""
|
||||
|
||||
# 创建外边界
|
||||
outer_boundary = make_rectangle_rwire(width=10, height=8)
|
||||
|
||||
# 创建内边界(孔)
|
||||
hole1 = make_circle_rwire(center=(3, 2, 0), radius=1.0)
|
||||
hole2 = make_circle_rwire(center=(7, 6, 0), radius=1.5)
|
||||
|
||||
# 注意:SimpleCAD 当前版本可能不直接支持多边界面
|
||||
# 这里展示概念和标签使用
|
||||
|
||||
# 主面
|
||||
main_face = make_rectangle_rface(width=10, height=8)
|
||||
main_face.add_tag("main_surface")
|
||||
main_face.add_tag("with_holes")
|
||||
|
||||
# 孔面(用于布尔运算)
|
||||
hole_face1 = make_circle_rface(center=(3, 2, 0), radius=1.0)
|
||||
hole_face1.add_tag("hole")
|
||||
hole_face1.add_tag("circular")
|
||||
hole_face1.set_metadata("hole_id", 1)
|
||||
hole_face1.set_metadata("center", (3, 2, 0))
|
||||
hole_face1.set_metadata("radius", 1.0)
|
||||
|
||||
hole_face2 = make_circle_rface(center=(7, 6, 0), radius=1.5)
|
||||
hole_face2.add_tag("hole")
|
||||
hole_face2.add_tag("circular")
|
||||
hole_face2.set_metadata("hole_id", 2)
|
||||
hole_face2.set_metadata("center", (7, 6, 0))
|
||||
hole_face2.set_metadata("radius", 1.5)
|
||||
|
||||
# 计算有效面积
|
||||
main_area = main_face.get_area()
|
||||
hole1_area = hole_face1.get_area()
|
||||
hole2_area = hole_face2.get_area()
|
||||
effective_area = main_area - hole1_area - hole2_area
|
||||
|
||||
main_face.set_metadata("total_area", main_area)
|
||||
main_face.set_metadata("hole_area", hole1_area + hole2_area)
|
||||
main_face.set_metadata("effective_area", effective_area)
|
||||
|
||||
print(f"主面面积: {main_area:.3f}")
|
||||
print(f"孔面积总和: {hole1_area + hole2_area:.3f}")
|
||||
print(f"有效面积: {effective_area:.3f}")
|
||||
|
||||
return main_face, [hole_face1, hole_face2]
|
||||
|
||||
main_face, holes = create_face_with_holes()
|
||||
```
|
||||
|
||||
### Face Transformation and Operations
|
||||
|
||||
```python
|
||||
from simplecadapi import (
|
||||
make_rectangle_rface,
|
||||
translate_shape,
|
||||
rotate_shape
|
||||
)
|
||||
|
||||
def transform_faces():
|
||||
"""变换面的操作"""
|
||||
|
||||
# 创建基础面
|
||||
base_face = make_rectangle_rface(width=4, height=3)
|
||||
base_face.add_tag("base")
|
||||
base_face.add_tag("original")
|
||||
|
||||
# 应用变换
|
||||
translated_face = translate_shape(base_face, offset=(6, 0, 0))
|
||||
translated_face.add_tag("translated")
|
||||
|
||||
rotated_face = rotate_shape(base_face, axis=(0, 0, 1), angle=45)
|
||||
rotated_face.add_tag("rotated")
|
||||
|
||||
elevated_face = translate_shape(base_face, offset=(0, 0, 2))
|
||||
elevated_face.add_tag("elevated")
|
||||
|
||||
# 收集所有面
|
||||
all_faces = [base_face, translated_face, rotated_face, elevated_face]
|
||||
|
||||
# 分析变换结果
|
||||
for face in all_faces:
|
||||
area = face.get_area()
|
||||
normal = face.get_normal_at()
|
||||
outer_wire = face.get_outer_wire()
|
||||
edges = outer_wire.get_edges()
|
||||
|
||||
# 计算边界框
|
||||
all_coords = []
|
||||
for edge in edges:
|
||||
start_coords = edge.get_start_vertex().get_coordinates()
|
||||
end_coords = edge.get_end_vertex().get_coordinates()
|
||||
all_coords.extend([start_coords, end_coords])
|
||||
|
||||
if all_coords:
|
||||
min_x = min(coord[0] for coord in all_coords)
|
||||
max_x = max(coord[0] for coord in all_coords)
|
||||
min_y = min(coord[1] for coord in all_coords)
|
||||
max_y = max(coord[1] for coord in all_coords)
|
||||
min_z = min(coord[2] for coord in all_coords)
|
||||
max_z = max(coord[2] for coord in all_coords)
|
||||
|
||||
face.set_metadata("bbox_min", (min_x, min_y, min_z))
|
||||
face.set_metadata("bbox_max", (max_x, max_y, max_z))
|
||||
|
||||
face.set_metadata("area", area)
|
||||
face.set_metadata("normal", (normal.x, normal.y, normal.z))
|
||||
|
||||
print(f"面标签: {face.get_tags()}")
|
||||
print(f" 面积: {area:.3f}")
|
||||
print(f" 法向量: ({normal.x:.3f}, {normal.y:.3f}, {normal.z:.3f})")
|
||||
if face.get_metadata("bbox_min"):
|
||||
print(f" 边界框: {face.get_metadata('bbox_min')} 到 {face.get_metadata('bbox_max')}")
|
||||
print()
|
||||
|
||||
transform_faces()
|
||||
```
|
||||
|
||||
### Face Classification and Filtering
|
||||
|
||||
```python
|
||||
from simplecadapi import make_rectangle_rface, make_circle_rface
|
||||
|
||||
def classify_faces():
|
||||
"""分类和筛选面"""
|
||||
|
||||
# 创建不同类型的面
|
||||
faces = []
|
||||
|
||||
# 小矩形
|
||||
small_rects = [
|
||||
make_rectangle_rface(width=1, height=1),
|
||||
make_rectangle_rface(width=2, height=1),
|
||||
make_rectangle_rface(width=1, height=2)
|
||||
]
|
||||
|
||||
# 大矩形
|
||||
large_rects = [
|
||||
make_rectangle_rface(width=5, height=4),
|
||||
make_rectangle_rface(width=6, height=3),
|
||||
make_rectangle_rface(width=4, height=6)
|
||||
]
|
||||
|
||||
# 圆形
|
||||
circles = [
|
||||
make_circle_rface(center=(0, 0, 0), radius=1.0),
|
||||
make_circle_rface(center=(0, 0, 0), radius=2.0),
|
||||
make_circle_rface(center=(0, 0, 0), radius=3.0)
|
||||
]
|
||||
|
||||
# 标记面
|
||||
for i, face in enumerate(small_rects):
|
||||
face.add_tag("rectangle")
|
||||
face.add_tag("small")
|
||||
face.set_metadata("size_category", "small")
|
||||
face.set_metadata("shape_type", "rectangle")
|
||||
faces.append(face)
|
||||
|
||||
for i, face in enumerate(large_rects):
|
||||
face.add_tag("rectangle")
|
||||
face.add_tag("large")
|
||||
face.set_metadata("size_category", "large")
|
||||
face.set_metadata("shape_type", "rectangle")
|
||||
faces.append(face)
|
||||
|
||||
for i, face in enumerate(circles):
|
||||
face.add_tag("circle")
|
||||
area = face.get_area()
|
||||
if area < 10:
|
||||
face.add_tag("small")
|
||||
face.set_metadata("size_category", "small")
|
||||
elif area > 20:
|
||||
face.add_tag("large")
|
||||
face.set_metadata("size_category", "large")
|
||||
else:
|
||||
face.add_tag("medium")
|
||||
face.set_metadata("size_category", "medium")
|
||||
face.set_metadata("shape_type", "circle")
|
||||
faces.append(face)
|
||||
|
||||
# 分类统计
|
||||
rectangles = [f for f in faces if f.has_tag("rectangle")]
|
||||
circles = [f for f in faces if f.has_tag("circle")]
|
||||
small_faces = [f for f in faces if f.has_tag("small")]
|
||||
large_faces = [f for f in faces if f.has_tag("large")]
|
||||
|
||||
print(f"总面数: {len(faces)}")
|
||||
print(f"矩形面: {len(rectangles)}")
|
||||
print(f"圆形面: {len(circles)}")
|
||||
print(f"小面: {len(small_faces)}")
|
||||
print(f"大面: {len(large_faces)}")
|
||||
|
||||
# 计算统计信息
|
||||
total_area = sum(f.get_area() for f in faces)
|
||||
avg_area = total_area / len(faces)
|
||||
|
||||
print(f"总面积: {total_area:.3f}")
|
||||
print(f"平均面积: {avg_area:.3f}")
|
||||
|
||||
return faces
|
||||
|
||||
classified_faces = classify_faces()
|
||||
```
|
||||
|
||||
## String Representation
|
||||
|
||||
```python
|
||||
from simplecadapi import make_rectangle_rface
|
||||
|
||||
face = make_rectangle_rface(width=5, height=3)
|
||||
face.add_tag("example_face")
|
||||
face.set_metadata("material", "steel")
|
||||
|
||||
print(face)
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
Face:
|
||||
area: 15.000
|
||||
normal: [0.000, 0.000, 1.000]
|
||||
outer_wire:
|
||||
Wire:
|
||||
edge_count: 4
|
||||
closed: True
|
||||
edges:
|
||||
edge_0:
|
||||
length: 5.000
|
||||
vertices:
|
||||
start: (0.0, 0.0, 0.0)
|
||||
end: (5.0, 0.0, 0.0)
|
||||
edge_1:
|
||||
length: 3.000
|
||||
vertices:
|
||||
start: (5.0, 0.0, 0.0)
|
||||
end: (5.0, 3.0, 0.0)
|
||||
edge_2:
|
||||
length: 5.000
|
||||
vertices:
|
||||
start: (5.0, 3.0, 0.0)
|
||||
end: (0.0, 3.0, 0.0)
|
||||
edge_3:
|
||||
length: 3.000
|
||||
vertices:
|
||||
start: (0.0, 3.0, 0.0)
|
||||
end: (0.0, 0.0, 0.0)
|
||||
tags: [example_face]
|
||||
metadata:
|
||||
material: steel
|
||||
```
|
||||
|
||||
## Relationships with Other Geometry
|
||||
|
||||
- **Wire (Wire)**: Boundary of the face
|
||||
- **Edge (Edge)**: Indirectly associated through wires
|
||||
- **Solid (Solid)**: Faces form the surfaces of a solid
|
||||
- **Shell (Shell)**: A collection of surfaces composed of multiple faces
|
||||
|
||||
## Notes
|
||||
|
||||
- Faces must be closed, bounded by closed wires
|
||||
- The face normal direction follows the right-hand rule
|
||||
- Area calculation includes all regions bounded by the boundary
|
||||
- Faces with holes require special treatment (outer boundary + inner boundary)
|
||||
- Face orientation affects subsequent solid operations
|
||||
- Complex faces may have self-intersection or degenerate cases
|
||||
- The u, v parameter range is typically [0, 1]
|
||||
+518
@@ -0,0 +1,518 @@
|
||||
# Shell
|
||||
|
||||
## Overview
|
||||
|
||||
`Shell` is the shell class in the SimpleCAD API, representing an open or closed surface collection composed of multiple faces. A shell is a geometry type between faces and solids, useful for creating complex surface structures. It wraps the CADQuery Shell object and adds tagging functionality.
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class Shell(TaggedMixin):
|
||||
"""壳类,包装CADQuery的Shell,添加标签功能"""
|
||||
```
|
||||
|
||||
## Inheritance
|
||||
|
||||
- Inherits from `TaggedMixin`, providing tag and metadata functionality
|
||||
|
||||
## Usage
|
||||
|
||||
- Represent complex surface structures
|
||||
- Serve as an intermediate state of solids
|
||||
- Create open surface geometry
|
||||
- Represent thin-walled structures
|
||||
|
||||
## Constructor
|
||||
|
||||
### `__init__(cq_shell)`
|
||||
|
||||
Initializes a shell object.
|
||||
|
||||
**Parameters:**
|
||||
- `cq_shell` (Union[cadquery.Shell, Any]): A CADQuery shell object or other Shape object
|
||||
|
||||
**Raises:**
|
||||
- `ValueError`: When the input shell object is invalid
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_box_rsolid, shell_rsolid
|
||||
|
||||
# 创建一个实体,然后制作壳
|
||||
box = make_box_rsolid(width=5, height=3, depth=2)
|
||||
# 选择要移除的面创建壳
|
||||
shell = shell_rsolid(box, thickness=0.2) # 创建薄壁壳
|
||||
```
|
||||
|
||||
## Main Properties
|
||||
|
||||
- `cq_shell`: The underlying CADQuery shell object
|
||||
- `_tags`: Tag set (inherited from TaggedMixin)
|
||||
- `_metadata`: Metadata dictionary (inherited from TaggedMixin)
|
||||
|
||||
## Common Methods
|
||||
|
||||
### `get_faces()`
|
||||
|
||||
Get all faces that make up the shell.
|
||||
|
||||
**Returns:**
|
||||
- `List[Face]`: List of face objects
|
||||
|
||||
**Raises:**
|
||||
- `ValueError`: When face list retrieval fails
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_box_rsolid, shell_rsolid
|
||||
|
||||
# 创建壳
|
||||
box = make_box_rsolid(width=4, height=3, depth=2)
|
||||
shell = shell_rsolid(box, thickness=0.1)
|
||||
|
||||
# 获取面
|
||||
faces = shell.get_faces()
|
||||
print(f"壳由 {len(faces)} 个面组成")
|
||||
|
||||
for i, face in enumerate(faces):
|
||||
area = face.get_area()
|
||||
print(f"面 {i}: 面积 {area:.3f}")
|
||||
```
|
||||
|
||||
### Tag Management Methods
|
||||
|
||||
Methods inherited from `TaggedMixin`:
|
||||
|
||||
#### `add_tag(tag)`, `has_tag(tag)`, `get_tags()`, `remove_tag(tag)`
|
||||
#### `set_metadata(key, value)`, `get_metadata(key, default=None)`
|
||||
|
||||
Usage is similar to Vertex; see [Vertex documentation](vertex.md) for details.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Creating and Analyzing Shells
|
||||
|
||||
```python
|
||||
from simplecadapi import make_box_rsolid, make_cylinder_rsolid, shell_rsolid
|
||||
|
||||
def create_and_analyze_shells():
|
||||
"""创建和分析不同类型的壳"""
|
||||
|
||||
# 创建盒子壳
|
||||
box = make_box_rsolid(width=6, height=4, depth=3)
|
||||
box_shell = shell_rsolid(box, thickness=0.2)
|
||||
box_shell.add_tag("box_shell")
|
||||
box_shell.add_tag("rectangular")
|
||||
|
||||
# 创建圆柱壳
|
||||
cylinder = make_cylinder_rsolid(center=(0, 0, 0), radius=2, height=5)
|
||||
cylinder_shell = shell_rsolid(cylinder, thickness=0.15)
|
||||
cylinder_shell.add_tag("cylinder_shell")
|
||||
cylinder_shell.add_tag("cylindrical")
|
||||
|
||||
# 分析壳
|
||||
shells = [box_shell, cylinder_shell]
|
||||
|
||||
for shell in shells:
|
||||
faces = shell.get_faces()
|
||||
total_area = sum(face.get_area() for face in faces)
|
||||
|
||||
# 分析面的类型
|
||||
face_areas = [face.get_area() for face in faces]
|
||||
min_area = min(face_areas)
|
||||
max_area = max(face_areas)
|
||||
avg_area = sum(face_areas) / len(face_areas)
|
||||
|
||||
shell.set_metadata("face_count", len(faces))
|
||||
shell.set_metadata("total_surface_area", total_area)
|
||||
shell.set_metadata("min_face_area", min_area)
|
||||
shell.set_metadata("max_face_area", max_area)
|
||||
shell.set_metadata("avg_face_area", avg_area)
|
||||
|
||||
# 标记面的大小
|
||||
for i, face in enumerate(faces):
|
||||
area = face.get_area()
|
||||
face.add_tag(f"shell_face_{i}")
|
||||
|
||||
if area < avg_area * 0.5:
|
||||
face.add_tag("small_face")
|
||||
elif area > avg_area * 1.5:
|
||||
face.add_tag("large_face")
|
||||
else:
|
||||
face.add_tag("medium_face")
|
||||
|
||||
print(f"壳类型: {shell.get_tags()}")
|
||||
print(f" 面数: {len(faces)}")
|
||||
print(f" 总表面积: {total_area:.3f}")
|
||||
print(f" 平均面积: {avg_area:.3f}")
|
||||
print(f" 面积范围: {min_area:.3f} - {max_area:.3f}")
|
||||
print()
|
||||
|
||||
create_and_analyze_shells()
|
||||
```
|
||||
|
||||
### Creating Complex Shells
|
||||
|
||||
```python
|
||||
from simplecadapi import (
|
||||
make_box_rsolid,
|
||||
make_cylinder_rsolid,
|
||||
union_rsolidlist,
|
||||
shell_rsolid
|
||||
)
|
||||
|
||||
def create_complex_shell():
|
||||
"""创建复杂的壳结构"""
|
||||
|
||||
# 创建复合几何体
|
||||
main_body = make_box_rsolid(width=8, height=6, depth=4)
|
||||
main_body.add_tag("main_body")
|
||||
|
||||
# 添加圆柱形突出部分
|
||||
cylinder1 = make_cylinder_rsolid(center=(2, 1, 4), radius=1, height=2)
|
||||
cylinder1.add_tag("protrusion_1")
|
||||
|
||||
cylinder2 = make_cylinder_rsolid(center=(6, 5, 4), radius=0.8, height=1.5)
|
||||
cylinder2.add_tag("protrusion_2")
|
||||
|
||||
# 合并几何体
|
||||
combined = union_rsolidlist([main_body, cylinder1])[0]
|
||||
combined = union_rsolidlist([combined, cylinder2])[0]
|
||||
combined.add_tag("combined_solid")
|
||||
|
||||
# 创建壳
|
||||
complex_shell = shell_rsolid(combined, thickness=0.3)
|
||||
complex_shell.add_tag("complex_shell")
|
||||
complex_shell.add_tag("multi_feature")
|
||||
|
||||
# 分析复杂壳
|
||||
faces = complex_shell.get_faces()
|
||||
|
||||
# 按面积分类
|
||||
large_faces = []
|
||||
medium_faces = []
|
||||
small_faces = []
|
||||
|
||||
total_area = sum(face.get_area() for face in faces)
|
||||
avg_area = total_area / len(faces)
|
||||
|
||||
for i, face in enumerate(faces):
|
||||
area = face.get_area()
|
||||
face.add_tag(f"complex_face_{i}")
|
||||
face.set_metadata("area", area)
|
||||
face.set_metadata("area_ratio", area / avg_area)
|
||||
|
||||
if area > avg_area * 2:
|
||||
large_faces.append(face)
|
||||
face.add_tag("large")
|
||||
elif area > avg_area * 0.5:
|
||||
medium_faces.append(face)
|
||||
face.add_tag("medium")
|
||||
else:
|
||||
small_faces.append(face)
|
||||
face.add_tag("small")
|
||||
|
||||
complex_shell.set_metadata("large_face_count", len(large_faces))
|
||||
complex_shell.set_metadata("medium_face_count", len(medium_faces))
|
||||
complex_shell.set_metadata("small_face_count", len(small_faces))
|
||||
complex_shell.set_metadata("total_surface_area", total_area)
|
||||
|
||||
print(f"复杂壳分析:")
|
||||
print(f" 总面数: {len(faces)}")
|
||||
print(f" 大面数: {len(large_faces)}")
|
||||
print(f" 中面数: {len(medium_faces)}")
|
||||
print(f" 小面数: {len(small_faces)}")
|
||||
print(f" 总表面积: {total_area:.3f}")
|
||||
|
||||
return complex_shell
|
||||
|
||||
complex_shell = create_complex_shell()
|
||||
```
|
||||
|
||||
### Shell Quality Check
|
||||
|
||||
```python
|
||||
from simplecadapi import make_box_rsolid, shell_rsolid
|
||||
|
||||
def check_shell_quality():
|
||||
"""检查壳的质量"""
|
||||
|
||||
# 创建测试壳
|
||||
test_solids = [
|
||||
make_box_rsolid(width=5, height=5, depth=5), # 立方体
|
||||
make_box_rsolid(width=10, height=2, depth=1), # 薄板
|
||||
make_box_rsolid(width=1, height=1, depth=10) # 长条
|
||||
]
|
||||
|
||||
test_names = ["cube", "plate", "rod"]
|
||||
|
||||
for i, solid in enumerate(test_solids):
|
||||
name = test_names[i]
|
||||
solid.add_tag(name)
|
||||
|
||||
# 创建不同厚度的壳
|
||||
thicknesses = [0.1, 0.2, 0.5]
|
||||
|
||||
for thickness in thicknesses:
|
||||
try:
|
||||
shell = shell_rsolid(solid, thickness=thickness)
|
||||
shell.add_tag(f"{name}_shell")
|
||||
shell.add_tag(f"thickness_{thickness}")
|
||||
|
||||
faces = shell.get_faces()
|
||||
|
||||
# 检查面的连续性
|
||||
edge_count = 0
|
||||
total_area = 0
|
||||
|
||||
for face in faces:
|
||||
area = face.get_area()
|
||||
total_area += area
|
||||
|
||||
outer_wire = face.get_outer_wire()
|
||||
edges = outer_wire.get_edges()
|
||||
edge_count += len(edges)
|
||||
|
||||
# 检查面的有效性
|
||||
if area < 1e-6:
|
||||
face.add_tag("degenerate")
|
||||
elif area > 1000:
|
||||
face.add_tag("oversized")
|
||||
else:
|
||||
face.add_tag("normal")
|
||||
|
||||
# 计算质量指标
|
||||
face_count = len(faces)
|
||||
avg_face_area = total_area / face_count if face_count > 0 else 0
|
||||
edge_to_face_ratio = edge_count / face_count if face_count > 0 else 0
|
||||
|
||||
shell.set_metadata("thickness", thickness)
|
||||
shell.set_metadata("face_count", face_count)
|
||||
shell.set_metadata("edge_count", edge_count)
|
||||
shell.set_metadata("total_area", total_area)
|
||||
shell.set_metadata("avg_face_area", avg_face_area)
|
||||
shell.set_metadata("edge_to_face_ratio", edge_to_face_ratio)
|
||||
|
||||
# 质量评估
|
||||
if face_count < 20 and edge_to_face_ratio < 10:
|
||||
shell.add_tag("good_quality")
|
||||
elif face_count < 50 and edge_to_face_ratio < 20:
|
||||
shell.add_tag("acceptable_quality")
|
||||
else:
|
||||
shell.add_tag("complex_geometry")
|
||||
|
||||
print(f"{name} 壳 (厚度 {thickness}):")
|
||||
print(f" 面数: {face_count}")
|
||||
print(f" 边数: {edge_count}")
|
||||
print(f" 总面积: {total_area:.3f}")
|
||||
print(f" 边面比: {edge_to_face_ratio:.2f}")
|
||||
print(f" 质量: {shell.get_tags()}")
|
||||
print()
|
||||
|
||||
except Exception as e:
|
||||
print(f"创建 {name} 壳 (厚度 {thickness}) 失败: {e}")
|
||||
print()
|
||||
|
||||
check_shell_quality()
|
||||
```
|
||||
|
||||
### Shell Repair and Optimization
|
||||
|
||||
```python
|
||||
from simplecadapi import make_box_rsolid, shell_rsolid
|
||||
|
||||
def optimize_shell():
|
||||
"""优化壳的结构"""
|
||||
|
||||
# 创建基础壳
|
||||
base_solid = make_box_rsolid(width=6, height=4, depth=3)
|
||||
shell = shell_rsolid(base_solid, thickness=0.2)
|
||||
shell.add_tag("original_shell")
|
||||
|
||||
faces = shell.get_faces()
|
||||
|
||||
# 分析和标记面
|
||||
optimized_faces = []
|
||||
removed_faces = []
|
||||
|
||||
total_area = sum(face.get_area() for face in faces)
|
||||
avg_area = total_area / len(faces)
|
||||
|
||||
for i, face in enumerate(faces):
|
||||
area = face.get_area()
|
||||
face.add_tag(f"face_{i}")
|
||||
face.set_metadata("original_area", area)
|
||||
|
||||
# 检查面的质量
|
||||
if area < avg_area * 0.01: # 极小面
|
||||
face.add_tag("tiny_face")
|
||||
face.add_tag("candidate_for_removal")
|
||||
removed_faces.append(face)
|
||||
elif area > avg_area * 10: # 极大面
|
||||
face.add_tag("oversized_face")
|
||||
face.add_tag("needs_subdivision")
|
||||
optimized_faces.append(face)
|
||||
else:
|
||||
face.add_tag("normal_face")
|
||||
optimized_faces.append(face)
|
||||
|
||||
# 创建优化信息
|
||||
shell.set_metadata("original_face_count", len(faces))
|
||||
shell.set_metadata("optimized_face_count", len(optimized_faces))
|
||||
shell.set_metadata("removed_face_count", len(removed_faces))
|
||||
shell.set_metadata("total_area", total_area)
|
||||
|
||||
# 标记优化结果
|
||||
if len(removed_faces) > 0:
|
||||
shell.add_tag("has_removable_faces")
|
||||
|
||||
if len(optimized_faces) == len(faces):
|
||||
shell.add_tag("well_formed")
|
||||
else:
|
||||
shell.add_tag("needs_optimization")
|
||||
|
||||
print(f"壳优化分析:")
|
||||
print(f" 原始面数: {len(faces)}")
|
||||
print(f" 优化后面数: {len(optimized_faces)}")
|
||||
print(f" 移除面数: {len(removed_faces)}")
|
||||
print(f" 总面积: {total_area:.3f}")
|
||||
print(f" 平均面积: {avg_area:.3f}")
|
||||
print(f" 优化标签: {shell.get_tags()}")
|
||||
|
||||
return shell, optimized_faces, removed_faces
|
||||
|
||||
shell, optimized_faces, removed_faces = optimize_shell()
|
||||
```
|
||||
|
||||
### Shell Comparison and Analysis
|
||||
|
||||
```python
|
||||
from simplecadapi import make_box_rsolid, make_cylinder_rsolid, shell_rsolid
|
||||
|
||||
def compare_shells():
|
||||
"""比较不同类型的壳"""
|
||||
|
||||
# 创建不同的基础几何体
|
||||
geometries = [
|
||||
("box", make_box_rsolid(width=4, height=4, depth=4)),
|
||||
("cylinder", make_cylinder_rsolid(center=(0, 0, 0), radius=2, height=4)),
|
||||
]
|
||||
|
||||
shells = []
|
||||
|
||||
for name, geometry in geometries:
|
||||
# 创建不同厚度的壳
|
||||
for thickness in [0.1, 0.2, 0.3]:
|
||||
shell = shell_rsolid(geometry, thickness=thickness)
|
||||
shell.add_tag(f"{name}_shell")
|
||||
shell.add_tag(f"thickness_{thickness}")
|
||||
|
||||
faces = shell.get_faces()
|
||||
|
||||
# 计算几何属性
|
||||
total_area = sum(face.get_area() for face in faces)
|
||||
face_count = len(faces)
|
||||
|
||||
# 计算复杂度指标
|
||||
edge_count = sum(len(face.get_outer_wire().get_edges()) for face in faces)
|
||||
complexity = edge_count / face_count if face_count > 0 else 0
|
||||
|
||||
shell.set_metadata("base_geometry", name)
|
||||
shell.set_metadata("thickness", thickness)
|
||||
shell.set_metadata("face_count", face_count)
|
||||
shell.set_metadata("edge_count", edge_count)
|
||||
shell.set_metadata("total_area", total_area)
|
||||
shell.set_metadata("complexity", complexity)
|
||||
|
||||
# 分类复杂度
|
||||
if complexity < 5:
|
||||
shell.add_tag("simple")
|
||||
elif complexity < 8:
|
||||
shell.add_tag("moderate")
|
||||
else:
|
||||
shell.add_tag("complex")
|
||||
|
||||
shells.append(shell)
|
||||
|
||||
# 比较分析
|
||||
print("壳比较分析:")
|
||||
print("=" * 50)
|
||||
|
||||
for shell in shells:
|
||||
base_geo = shell.get_metadata("base_geometry")
|
||||
thickness = shell.get_metadata("thickness")
|
||||
face_count = shell.get_metadata("face_count")
|
||||
total_area = shell.get_metadata("total_area")
|
||||
complexity = shell.get_metadata("complexity")
|
||||
|
||||
print(f"{base_geo} 壳 (厚度 {thickness}):")
|
||||
print(f" 面数: {face_count}")
|
||||
print(f" 总面积: {total_area:.3f}")
|
||||
print(f" 复杂度: {complexity:.2f}")
|
||||
print(f" 分类: {[tag for tag in shell.get_tags() if tag in ['simple', 'moderate', 'complex']]}")
|
||||
print()
|
||||
|
||||
return shells
|
||||
|
||||
shell_comparison = compare_shells()
|
||||
```
|
||||
|
||||
## String Representation
|
||||
|
||||
```python
|
||||
from simplecadapi import make_box_rsolid, shell_rsolid
|
||||
|
||||
box = make_box_rsolid(width=5, height=3, depth=2)
|
||||
shell = shell_rsolid(box, thickness=0.2)
|
||||
shell.add_tag("example_shell")
|
||||
shell.set_metadata("thickness", 0.2)
|
||||
|
||||
print(shell)
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
Shell:
|
||||
face_count: 10
|
||||
faces:
|
||||
face_0:
|
||||
area: 6.000
|
||||
normal: [0.000, 0.000, 1.000]
|
||||
outer_wire:
|
||||
Wire:
|
||||
edge_count: 4
|
||||
closed: True
|
||||
face_1:
|
||||
area: 6.000
|
||||
normal: [0.000, 0.000, -1.000]
|
||||
outer_wire:
|
||||
Wire:
|
||||
edge_count: 4
|
||||
closed: True
|
||||
...
|
||||
tags: [example_shell]
|
||||
metadata:
|
||||
thickness: 0.2
|
||||
```
|
||||
|
||||
## Relationships with Other Geometry
|
||||
|
||||
- **Face (Face)**: Components of a shell
|
||||
- **Solid (Solid)**: Closed shells can form solids
|
||||
- **Compound (Compound)**: Multiple shells can form a compound
|
||||
|
||||
## Application Scenarios
|
||||
|
||||
- **Thin-walled structures**: Containers, housings, etc.
|
||||
- **Architectural surfaces**: Building facades
|
||||
- **Industrial design**: Product exterior design
|
||||
- **Finite element analysis**: Mesh generation for thin-walled structures
|
||||
|
||||
## Notes
|
||||
|
||||
- Shells can be open or closed
|
||||
- Closed shells can form solids
|
||||
- Shell thickness affects the final geometry
|
||||
- Complex shells may contain many small faces, affecting performance
|
||||
- Shell quality directly affects the success rate of subsequent operations
|
||||
- Certain operations may cause changes in shell topology
|
||||
+278
@@ -0,0 +1,278 @@
|
||||
# SimpleWorkplane
|
||||
|
||||
## Overview
|
||||
|
||||
`SimpleWorkplane` is the workplane class in SimpleCAD API, used for defining local coordinate systems and workplanes for geometric modeling. It supports nested usage and can be used as a context manager, providing a local coordinate system environment for geometric operations.
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class SimpleWorkplane:
|
||||
"""工作平面上下文管理器
|
||||
|
||||
用于定义局部坐标系,支持嵌套使用
|
||||
"""
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
- Define local workplanes
|
||||
- Provide context manager functionality, using with statements to automatically manage coordinate systems
|
||||
- Support nested coordinate systems
|
||||
- Conversion with CADQuery Workplane
|
||||
- Simplify complex geometric modeling
|
||||
|
||||
## Constructor
|
||||
|
||||
### `__init__(origin, normal, x_dir)`
|
||||
|
||||
Initialize a workplane.
|
||||
|
||||
**Parameters:**
|
||||
- `origin` (Tuple[float, float, float], optional): Workplane origin, default (0, 0, 0)
|
||||
- `normal` (Tuple[float, float, float], optional): Workplane normal vector, default (0, 0, 1)
|
||||
- `x_dir` (Tuple[float, float, float], optional): Workplane X-axis direction, default (1, 0, 0)
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import SimpleWorkplane
|
||||
|
||||
# 默认工作平面(XY平面)
|
||||
wp = SimpleWorkplane()
|
||||
|
||||
# 自定义工作平面
|
||||
wp = SimpleWorkplane(
|
||||
origin=(0, 0, 5),
|
||||
normal=(0, 0, 1),
|
||||
x_dir=(1, 0, 0)
|
||||
)
|
||||
|
||||
# 倾斜的工作平面
|
||||
wp = SimpleWorkplane(
|
||||
origin=(0, 0, 0),
|
||||
normal=(0, 1, 1), # 45度倾斜
|
||||
x_dir=(1, 0, 0)
|
||||
)
|
||||
```
|
||||
|
||||
## Main Properties
|
||||
|
||||
- `cs`: Corresponding coordinate system of the workplane (CoordinateSystem)
|
||||
- `cq_workplane`: CADQuery workplane object (lazy loaded)
|
||||
|
||||
## Common Methods
|
||||
|
||||
### Context Manager Methods
|
||||
|
||||
SimpleWorkplane supports `with` statements to automatically manage the current coordinate system within the context.
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import SimpleWorkplane, make_box_rsolid
|
||||
|
||||
# 在全局坐标系中创建盒子
|
||||
box1 = make_box_rsolid(1, 1, 1)
|
||||
|
||||
# 在局部坐标系中创建盒子
|
||||
with SimpleWorkplane(origin=(2, 0, 0)) as wp:
|
||||
box2 = make_box_rsolid(1, 1, 1) # 实际位置在 (2, 0, 0)
|
||||
```
|
||||
|
||||
|
||||
### `to_cq_workplane()`
|
||||
|
||||
Convert to CADQuery's Workplane object.
|
||||
|
||||
**Returns:**
|
||||
- `cadquery.Workplane`: CADQuery workplane object
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import SimpleWorkplane
|
||||
|
||||
wp = SimpleWorkplane(origin=(0, 0, 1))
|
||||
cq_wp = wp.to_cq_workplane()
|
||||
```
|
||||
## Usage Examples
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```python
|
||||
from simplecadapi import SimpleWorkplane, make_circle_rface, extrude_rsolid
|
||||
|
||||
# 创建工作平面
|
||||
wp = SimpleWorkplane(origin=(0, 0, 0), normal=(0, 0, 1))
|
||||
|
||||
# 使用工作平面
|
||||
with wp:
|
||||
# 在工作平面上创建圆形面
|
||||
circle = make_circle_rface(radius=1.0)
|
||||
|
||||
# 拉伸成圆柱体
|
||||
cylinder = extrude_rsolid(circle, height=2.0)
|
||||
```
|
||||
|
||||
### Nested Workplanes
|
||||
|
||||
```python
|
||||
from simplecadapi import SimpleWorkplane, make_box_rsolid
|
||||
|
||||
# 第一层工作平面
|
||||
with SimpleWorkplane(origin=(1, 0, 0)) as wp1:
|
||||
box1 = make_box_rsolid(1, 1, 1) # 位置在 (1, 0, 0)
|
||||
|
||||
# 第二层工作平面(相对于第一层)
|
||||
with SimpleWorkplane(origin=(0, 1, 0)) as wp2:
|
||||
box2 = make_box_rsolid(1, 1, 1) # 位置在 (1, 1, 0)
|
||||
|
||||
# 第三层工作平面(相对于第二层)
|
||||
with SimpleWorkplane(origin=(0, 0, 1)) as wp3:
|
||||
box3 = make_box_rsolid(1, 1, 1) # 位置在 (1, 1, 1)
|
||||
```
|
||||
|
||||
### Creating Complex Geometry
|
||||
|
||||
```python
|
||||
from simplecadapi import SimpleWorkplane, make_rectangle_rface, extrude_rsolid
|
||||
|
||||
# 创建一个带有多个特征的零件
|
||||
def create_complex_part():
|
||||
parts = []
|
||||
|
||||
# 主体
|
||||
with SimpleWorkplane(origin=(0, 0, 0)) as wp:
|
||||
base = make_rectangle_rface(width=10, height=5)
|
||||
main_body = extrude_rsolid(base, height=2)
|
||||
parts.append(main_body)
|
||||
|
||||
# 左侧支架
|
||||
with SimpleWorkplane(origin=(-3, 0, 2), normal=(1, 0, 0)) as wp:
|
||||
bracket = make_rectangle_rface(width=3, height=2)
|
||||
left_bracket = extrude_rsolid(bracket, height=1)
|
||||
parts.append(left_bracket)
|
||||
|
||||
# 右侧支架
|
||||
with SimpleWorkplane(origin=(3, 0, 2), normal=(-1, 0, 0)) as wp:
|
||||
bracket = make_rectangle_rface(width=3, height=2)
|
||||
right_bracket = extrude_rsolid(bracket, height=1)
|
||||
parts.append(right_bracket)
|
||||
|
||||
return parts
|
||||
|
||||
parts = create_complex_part()
|
||||
```
|
||||
|
||||
### Rotated Workplanes
|
||||
|
||||
```python
|
||||
import math
|
||||
from simplecadapi import SimpleWorkplane, make_circle_rface, extrude_rsolid
|
||||
|
||||
# 创建多个旋转的工作平面
|
||||
def create_rotated_cylinders():
|
||||
cylinders = []
|
||||
|
||||
for i in range(6):
|
||||
angle = i * math.pi / 3 # 每60度一个
|
||||
|
||||
# 创建旋转的工作平面
|
||||
with SimpleWorkplane(
|
||||
origin=(0, 0, 0),
|
||||
normal=(math.cos(angle), math.sin(angle), 0),
|
||||
x_dir=(0, 0, 1)
|
||||
) as wp:
|
||||
circle = make_circle_rface(radius=0.5)
|
||||
cylinder = extrude_rsolid(circle, height=3)
|
||||
cylinders.append(cylinder)
|
||||
|
||||
return cylinders
|
||||
|
||||
cylinders = create_rotated_cylinders()
|
||||
```
|
||||
|
||||
### Creating Workplanes Along a Path
|
||||
|
||||
```python
|
||||
from simplecadapi import SimpleWorkplane, make_box_rsolid
|
||||
|
||||
def create_path_features():
|
||||
# 定义路径点
|
||||
path_points = [
|
||||
(0, 0, 0),
|
||||
(1, 0, 0),
|
||||
(2, 1, 0),
|
||||
(3, 1, 1),
|
||||
(4, 0, 1)
|
||||
]
|
||||
|
||||
features = []
|
||||
|
||||
for i, point in enumerate(path_points):
|
||||
# 计算朝向下一个点的方向
|
||||
if i < len(path_points) - 1:
|
||||
next_point = path_points[i + 1]
|
||||
direction = (
|
||||
next_point[0] - point[0],
|
||||
next_point[1] - point[1],
|
||||
next_point[2] - point[2]
|
||||
)
|
||||
else:
|
||||
direction = (1, 0, 0) # 最后一个点使用默认方向
|
||||
|
||||
with SimpleWorkplane(origin=point, x_dir=direction) as wp:
|
||||
feature = make_box_rsolid(0.2, 0.2, 0.2)
|
||||
features.append(feature)
|
||||
|
||||
return features
|
||||
|
||||
features = create_path_features()
|
||||
```
|
||||
|
||||
## Coordinate System Transformation
|
||||
|
||||
SimpleWorkplane automatically handles coordinate system transformations:
|
||||
|
||||
- Input coordinates and direction vectors are defined in the current coordinate system
|
||||
- Internally converted to global coordinate system
|
||||
- Automatically handles orthogonalization to ensure coordinate system correctness
|
||||
|
||||
## String Representation
|
||||
|
||||
```python
|
||||
from simplecadapi import SimpleWorkplane
|
||||
|
||||
wp = SimpleWorkplane(origin=(1, 2, 3))
|
||||
print(wp)
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
SimpleWorkplane:
|
||||
coordinate_system:
|
||||
CoordinateSystem:
|
||||
origin: [1.000, 2.000, 3.000]
|
||||
x_axis: [1.000, 0.000, 0.000]
|
||||
y_axis: [0.000, 1.000, 0.000]
|
||||
z_axis: [0.000, 0.000, 1.000]
|
||||
```
|
||||
|
||||
## CADQuery Compatibility
|
||||
|
||||
SimpleWorkplane can be seamlessly converted to CADQuery's Workplane:
|
||||
|
||||
```python
|
||||
from simplecadapi import SimpleWorkplane
|
||||
|
||||
wp = SimpleWorkplane(origin=(0, 0, 1))
|
||||
cq_wp = wp.to_cq_workplane()
|
||||
|
||||
# 可以在 CADQuery 工作平面上进行操作
|
||||
cq_result = cq_wp.box(1, 1, 1)
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- When using `with` statements, the workplane automatically manages the coordinate system stack
|
||||
- Coordinates in nested workplanes are relative to the parent workplane
|
||||
- If the normal vector and X-axis direction are parallel, the system automatically selects an appropriate Y-axis direction
|
||||
- Coordinate systems are automatically orthogonalized to ensure correctness
|
||||
+697
@@ -0,0 +1,697 @@
|
||||
# Solid
|
||||
|
||||
## Overview
|
||||
|
||||
`Solid` is the solid class in the SimpleCAD API, representing a 3D closed geometry. Solids have volume and are one of the most important geometry types in CAD modeling. It wraps the CADQuery Solid object and adds tagging functionality and automatic face tagging capability.
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class Solid(TaggedMixin):
|
||||
"""实体类,包装CADQuery的Solid,添加标签功能"""
|
||||
```
|
||||
|
||||
## Inheritance
|
||||
|
||||
- Inherits from `TaggedMixin`, providing tag and metadata functionality
|
||||
|
||||
## Usage
|
||||
|
||||
- Represent 3D solid objects
|
||||
- Perform boolean operations (union, intersection, difference)
|
||||
- Apply feature operations (fillets, chamfers, etc.)
|
||||
- Calculate physical properties such as volume and surface area
|
||||
- Generate manufacturing data
|
||||
|
||||
## Constructor
|
||||
|
||||
### `__init__(cq_solid)`
|
||||
|
||||
Initializes a solid object.
|
||||
|
||||
**Parameters:**
|
||||
- `cq_solid` (Union[cadquery.Solid, Any]): A CADQuery solid object or other Shape object
|
||||
|
||||
**Raises:**
|
||||
- `ValueError`: When the input solid object is invalid
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import (
|
||||
make_box_rsolid,
|
||||
make_cylinder_rsolid,
|
||||
make_sphere_rsolid
|
||||
)
|
||||
|
||||
# 通过 SimpleCAD 函数创建实体
|
||||
box = make_box_rsolid(width=5, height=3, depth=2)
|
||||
cylinder = make_cylinder_rsolid(center=(0, 0, 0), radius=2, height=4)
|
||||
sphere = make_sphere_rsolid(center=(0, 0, 0), radius=1.5)
|
||||
```
|
||||
|
||||
## Main Properties
|
||||
|
||||
- `cq_solid`: The underlying CADQuery solid object
|
||||
- `_tags`: Tag set (inherited from TaggedMixin)
|
||||
- `_metadata`: Metadata dictionary (inherited from TaggedMixin)
|
||||
- `_face_tags`: Face tag dictionary (internal use)
|
||||
|
||||
## Common Methods
|
||||
|
||||
### `get_volume()`
|
||||
|
||||
Get the volume of the solid.
|
||||
|
||||
**Returns:**
|
||||
- `float`: The volume of the solid
|
||||
|
||||
**Raises:**
|
||||
- `ValueError`: When volume retrieval fails
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_box_rsolid, make_cylinder_rsolid, make_sphere_rsolid
|
||||
import math
|
||||
|
||||
# 立方体体积
|
||||
box = make_box_rsolid(width=2, height=3, depth=4)
|
||||
box_volume = box.get_volume()
|
||||
print(f"立方体体积: {box_volume}") # 24.0
|
||||
|
||||
# 圆柱体体积
|
||||
cylinder = make_cylinder_rsolid(center=(0, 0, 0), radius=2, height=5)
|
||||
cylinder_volume = cylinder.get_volume()
|
||||
expected_volume = math.pi * 2**2 * 5
|
||||
print(f"圆柱体体积: {cylinder_volume:.3f}, 期望: {expected_volume:.3f}")
|
||||
|
||||
# 球体体积
|
||||
sphere = make_sphere_rsolid(center=(0, 0, 0), radius=1.5)
|
||||
sphere_volume = sphere.get_volume()
|
||||
expected_volume = (4/3) * math.pi * 1.5**3
|
||||
print(f"球体体积: {sphere_volume:.3f}, 期望: {expected_volume:.3f}")
|
||||
```
|
||||
|
||||
### `get_faces()`
|
||||
|
||||
Get all faces that make up the solid.
|
||||
|
||||
**Returns:**
|
||||
- `List[Face]`: List of face objects
|
||||
|
||||
**Raises:**
|
||||
- `ValueError`: When face list retrieval fails
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_box_rsolid
|
||||
|
||||
box = make_box_rsolid(width=4, height=3, depth=2)
|
||||
faces = box.get_faces()
|
||||
|
||||
print(f"立方体有 {len(faces)} 个面")
|
||||
for i, face in enumerate(faces):
|
||||
area = face.get_area()
|
||||
print(f"面 {i}: 面积 {area:.3f}")
|
||||
```
|
||||
|
||||
### `get_edges()`
|
||||
|
||||
Get all edges that make up the solid.
|
||||
|
||||
**Returns:**
|
||||
- `List[Edge]`: List of edge objects
|
||||
|
||||
**Raises:**
|
||||
- `ValueError`: When edge list retrieval fails
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_box_rsolid
|
||||
|
||||
box = make_box_rsolid(width=4, height=3, depth=2)
|
||||
edges = box.get_edges()
|
||||
|
||||
print(f"立方体有 {len(edges)} 条边")
|
||||
for i, edge in enumerate(edges):
|
||||
length = edge.get_length()
|
||||
print(f"边 {i}: 长度 {length:.3f}")
|
||||
```
|
||||
|
||||
### `auto_tag_faces(geometry_type)`
|
||||
|
||||
Automatically add tags to faces.
|
||||
|
||||
**Parameters:**
|
||||
- `geometry_type` (str): Geometry type ("box", "cylinder", "sphere", "unknown")
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_box_rsolid, make_cylinder_rsolid
|
||||
|
||||
# 立方体面标记
|
||||
box = make_box_rsolid(width=4, height=3, depth=2)
|
||||
box.auto_tag_faces("box")
|
||||
|
||||
faces = box.get_faces()
|
||||
for face in faces:
|
||||
print(f"面标签: {face.get_tags()}")
|
||||
|
||||
# 圆柱体面标记
|
||||
cylinder = make_cylinder_rsolid(center=(0, 0, 0), radius=2, height=4)
|
||||
cylinder.auto_tag_faces("cylinder")
|
||||
|
||||
faces = cylinder.get_faces()
|
||||
for face in faces:
|
||||
print(f"面标签: {face.get_tags()}")
|
||||
```
|
||||
|
||||
### Tag Management Methods
|
||||
|
||||
Methods inherited from `TaggedMixin`:
|
||||
|
||||
#### `add_tag(tag)`, `has_tag(tag)`, `get_tags()`, `remove_tag(tag)`
|
||||
#### `set_metadata(key, value)`, `get_metadata(key, default=None)`
|
||||
|
||||
Usage is similar to Vertex; see [Vertex documentation](vertex.md) for details.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Creating and Analyzing Basic Solids
|
||||
|
||||
```python
|
||||
from simplecadapi import (
|
||||
make_box_rsolid,
|
||||
make_cylinder_rsolid,
|
||||
make_sphere_rsolid
|
||||
)
|
||||
|
||||
def create_basic_solids():
|
||||
"""创建和分析基础实体"""
|
||||
|
||||
# 创建不同类型的实体
|
||||
solids = [
|
||||
("box", make_box_rsolid(width=4, height=3, depth=2)),
|
||||
("cylinder", make_cylinder_rsolid(center=(0, 0, 0), radius=2, height=4)),
|
||||
("sphere", make_sphere_rsolid(center=(0, 0, 0), radius=1.5))
|
||||
]
|
||||
|
||||
for name, solid in solids:
|
||||
# 添加基本标签
|
||||
solid.add_tag(name)
|
||||
solid.add_tag("basic_geometry")
|
||||
|
||||
# 自动标记面
|
||||
solid.auto_tag_faces(name)
|
||||
|
||||
# 获取几何属性
|
||||
volume = solid.get_volume()
|
||||
faces = solid.get_faces()
|
||||
edges = solid.get_edges()
|
||||
|
||||
# 计算表面积
|
||||
total_surface_area = sum(face.get_area() for face in faces)
|
||||
|
||||
# 分析面的分布
|
||||
face_areas = [face.get_area() for face in faces]
|
||||
min_face_area = min(face_areas)
|
||||
max_face_area = max(face_areas)
|
||||
avg_face_area = sum(face_areas) / len(face_areas)
|
||||
|
||||
# 存储元数据
|
||||
solid.set_metadata("volume", volume)
|
||||
solid.set_metadata("surface_area", total_surface_area)
|
||||
solid.set_metadata("face_count", len(faces))
|
||||
solid.set_metadata("edge_count", len(edges))
|
||||
solid.set_metadata("min_face_area", min_face_area)
|
||||
solid.set_metadata("max_face_area", max_face_area)
|
||||
solid.set_metadata("avg_face_area", avg_face_area)
|
||||
|
||||
# 计算体积效率(体积/表面积比)
|
||||
volume_efficiency = volume / total_surface_area if total_surface_area > 0 else 0
|
||||
solid.set_metadata("volume_efficiency", volume_efficiency)
|
||||
|
||||
print(f"{name.upper()} 实体分析:")
|
||||
print(f" 体积: {volume:.3f}")
|
||||
print(f" 表面积: {total_surface_area:.3f}")
|
||||
print(f" 面数: {len(faces)}")
|
||||
print(f" 边数: {len(edges)}")
|
||||
print(f" 体积效率: {volume_efficiency:.3f}")
|
||||
print(f" 面积范围: {min_face_area:.3f} - {max_face_area:.3f}")
|
||||
print()
|
||||
|
||||
create_basic_solids()
|
||||
```
|
||||
|
||||
### Boolean Operations Example
|
||||
|
||||
```python
|
||||
from simplecadapi import (
|
||||
make_box_rsolid,
|
||||
make_cylinder_rsolid,
|
||||
union_rsolidlist,
|
||||
cut_rsolidlist,
|
||||
intersect_rsolidlist
|
||||
)
|
||||
|
||||
def boolean_operations_example():
|
||||
"""布尔运算示例"""
|
||||
|
||||
# 创建基础几何体
|
||||
box = make_box_rsolid(width=6, height=4, depth=3)
|
||||
box.add_tag("base_box")
|
||||
|
||||
cylinder = make_cylinder_rsolid(center=(3, 2, 0), radius=1, height=5)
|
||||
cylinder.add_tag("cutting_cylinder")
|
||||
|
||||
# 并集运算
|
||||
union_result = union_rsolidlist([box, cylinder])[0]
|
||||
union_result.add_tag("union_result")
|
||||
union_result.add_tag("combined_geometry")
|
||||
|
||||
# 差集运算(从盒子中减去圆柱)
|
||||
cut_result = cut_rsolidlist(box, cylinder)[0]
|
||||
cut_result.add_tag("cut_result")
|
||||
cut_result.add_tag("with_hole")
|
||||
|
||||
# 交集运算
|
||||
intersect_result = intersect_rsolidlist(box, cylinder)[0]
|
||||
intersect_result.add_tag("intersect_result")
|
||||
intersect_result.add_tag("common_volume")
|
||||
|
||||
# 分析结果
|
||||
operations = [
|
||||
("原始盒子", box),
|
||||
("原始圆柱", cylinder),
|
||||
("并集", union_result),
|
||||
("差集", cut_result),
|
||||
("交集", intersect_result)
|
||||
]
|
||||
|
||||
for name, solid in operations:
|
||||
volume = solid.get_volume()
|
||||
faces = solid.get_faces()
|
||||
surface_area = sum(face.get_area() for face in faces)
|
||||
|
||||
solid.set_metadata("operation_type", name)
|
||||
solid.set_metadata("volume", volume)
|
||||
solid.set_metadata("surface_area", surface_area)
|
||||
|
||||
print(f"{name}:")
|
||||
print(f" 体积: {volume:.3f}")
|
||||
print(f" 表面积: {surface_area:.3f}")
|
||||
print(f" 面数: {len(faces)}")
|
||||
print(f" 标签: {solid.get_tags()}")
|
||||
print()
|
||||
|
||||
boolean_operations_example()
|
||||
```
|
||||
|
||||
### Feature Operations Example
|
||||
|
||||
```python
|
||||
from simplecadapi import (
|
||||
make_box_rsolid,
|
||||
fillet_rsolid,
|
||||
chamfer_rsolid,
|
||||
select_edges_by_tag
|
||||
)
|
||||
|
||||
def feature_operations_example():
|
||||
"""特征操作示例"""
|
||||
|
||||
# 创建基础盒子
|
||||
base_box = make_box_rsolid(width=8, height=6, depth=4)
|
||||
base_box.add_tag("base_geometry")
|
||||
base_box.auto_tag_faces("box")
|
||||
|
||||
# 分析原始几何体
|
||||
original_volume = base_box.get_volume()
|
||||
original_faces = base_box.get_faces()
|
||||
original_edges = base_box.get_edges()
|
||||
|
||||
print(f"原始几何体:")
|
||||
print(f" 体积: {original_volume:.3f}")
|
||||
print(f" 面数: {len(original_faces)}")
|
||||
print(f" 边数: {len(original_edges)}")
|
||||
print()
|
||||
|
||||
# 圆角操作
|
||||
try:
|
||||
filleted_box = fillet_rsolid(base_box, radius=0.5)
|
||||
filleted_box.add_tag("filleted")
|
||||
filleted_box.add_tag("rounded_edges")
|
||||
|
||||
filleted_volume = filleted_box.get_volume()
|
||||
filleted_faces = filleted_box.get_faces()
|
||||
filleted_edges = filleted_box.get_edges()
|
||||
|
||||
filleted_box.set_metadata("original_volume", original_volume)
|
||||
filleted_box.set_metadata("volume_change", filleted_volume - original_volume)
|
||||
filleted_box.set_metadata("volume_ratio", filleted_volume / original_volume)
|
||||
|
||||
print(f"圆角后几何体:")
|
||||
print(f" 体积: {filleted_volume:.3f}")
|
||||
print(f" 体积变化: {filleted_volume - original_volume:.3f}")
|
||||
print(f" 面数: {len(filleted_faces)}")
|
||||
print(f" 边数: {len(filleted_edges)}")
|
||||
print()
|
||||
|
||||
except Exception as e:
|
||||
print(f"圆角操作失败: {e}")
|
||||
filleted_box = None
|
||||
|
||||
# 倒角操作
|
||||
try:
|
||||
chamfered_box = chamfer_rsolid(base_box, distance=0.3)
|
||||
chamfered_box.add_tag("chamfered")
|
||||
chamfered_box.add_tag("beveled_edges")
|
||||
|
||||
chamfered_volume = chamfered_box.get_volume()
|
||||
chamfered_faces = chamfered_box.get_faces()
|
||||
chamfered_edges = chamfered_box.get_edges()
|
||||
|
||||
chamfered_box.set_metadata("original_volume", original_volume)
|
||||
chamfered_box.set_metadata("volume_change", chamfered_volume - original_volume)
|
||||
chamfered_box.set_metadata("volume_ratio", chamfered_volume / original_volume)
|
||||
|
||||
print(f"倒角后几何体:")
|
||||
print(f" 体积: {chamfered_volume:.3f}")
|
||||
print(f" 体积变化: {chamfered_volume - original_volume:.3f}")
|
||||
print(f" 面数: {len(chamfered_faces)}")
|
||||
print(f" 边数: {len(chamfered_edges)}")
|
||||
print()
|
||||
|
||||
except Exception as e:
|
||||
print(f"倒角操作失败: {e}")
|
||||
chamfered_box = None
|
||||
|
||||
# 比较结果
|
||||
results = [("原始", base_box)]
|
||||
if filleted_box:
|
||||
results.append(("圆角", filleted_box))
|
||||
if chamfered_box:
|
||||
results.append(("倒角", chamfered_box))
|
||||
|
||||
print("特征操作比较:")
|
||||
for name, solid in results:
|
||||
volume = solid.get_volume()
|
||||
faces = solid.get_faces()
|
||||
tags = solid.get_tags()
|
||||
print(f" {name}: 体积={volume:.3f}, 面数={len(faces)}, 标签={tags}")
|
||||
|
||||
feature_operations_example()
|
||||
```
|
||||
|
||||
### Creating Complex Geometry
|
||||
|
||||
```python
|
||||
from simplecadapi import (
|
||||
make_box_rsolid,
|
||||
make_cylinder_rsolid,
|
||||
make_sphere_rsolid,
|
||||
union_rsolidlist,
|
||||
cut_rsolidlist,
|
||||
translate_shape,
|
||||
rotate_shape
|
||||
)
|
||||
|
||||
def create_complex_geometry():
|
||||
"""创建复杂几何体"""
|
||||
|
||||
# 创建主体
|
||||
main_body = make_box_rsolid(width=12, height=8, depth=6)
|
||||
main_body.add_tag("main_body")
|
||||
main_body.add_tag("base_structure")
|
||||
|
||||
# 创建圆柱形孔
|
||||
holes = []
|
||||
hole_positions = [(3, 2, 0), (9, 2, 0), (3, 6, 0), (9, 6, 0)]
|
||||
|
||||
for i, (x, y, z) in enumerate(hole_positions):
|
||||
hole = make_cylinder_rsolid(center=(x, y, z), radius=0.8, height=8)
|
||||
hole.add_tag(f"hole_{i}")
|
||||
hole.add_tag("mounting_hole")
|
||||
holes.append(hole)
|
||||
|
||||
# 创建球形特征
|
||||
sphere_feature = make_sphere_rsolid(center=(6, 4, 6), radius=2)
|
||||
sphere_feature.add_tag("sphere_feature")
|
||||
sphere_feature.add_tag("decorative")
|
||||
|
||||
# 创建圆柱形支柱
|
||||
support_cylinder = make_cylinder_rsolid(center=(6, 4, 0), radius=1, height=4)
|
||||
support_cylinder.add_tag("support_cylinder")
|
||||
support_cylinder.add_tag("structural")
|
||||
|
||||
# 组合几何体
|
||||
complex_solid = union_rsolidlist([main_body, sphere_feature])[0]
|
||||
complex_solid = union_rsolidlist([complex_solid, support_cylinder])[0]
|
||||
|
||||
# 减去孔
|
||||
for hole in holes:
|
||||
complex_solid = cut_rsolidlist(complex_solid, hole)[0]
|
||||
|
||||
# 标记复杂几何体
|
||||
complex_solid.add_tag("complex_geometry")
|
||||
complex_solid.add_tag("multi_feature")
|
||||
complex_solid.add_tag("machined_part")
|
||||
|
||||
# 分析复杂几何体
|
||||
volume = complex_solid.get_volume()
|
||||
faces = complex_solid.get_faces()
|
||||
edges = complex_solid.get_edges()
|
||||
|
||||
# 计算几何复杂度
|
||||
face_count = len(faces)
|
||||
edge_count = len(edges)
|
||||
complexity_ratio = edge_count / face_count if face_count > 0 else 0
|
||||
|
||||
# 分析面的分布
|
||||
face_areas = [face.get_area() for face in faces]
|
||||
total_surface_area = sum(face_areas)
|
||||
|
||||
# 分类面
|
||||
small_faces = [f for f in faces if f.get_area() < 1.0]
|
||||
large_faces = [f for f in faces if f.get_area() > 10.0]
|
||||
medium_faces = [f for f in faces if 1.0 <= f.get_area() <= 10.0]
|
||||
|
||||
# 存储分析结果
|
||||
complex_solid.set_metadata("volume", volume)
|
||||
complex_solid.set_metadata("surface_area", total_surface_area)
|
||||
complex_solid.set_metadata("face_count", face_count)
|
||||
complex_solid.set_metadata("edge_count", edge_count)
|
||||
complex_solid.set_metadata("complexity_ratio", complexity_ratio)
|
||||
complex_solid.set_metadata("small_face_count", len(small_faces))
|
||||
complex_solid.set_metadata("medium_face_count", len(medium_faces))
|
||||
complex_solid.set_metadata("large_face_count", len(large_faces))
|
||||
|
||||
# 根据复杂度添加标签
|
||||
if complexity_ratio < 5:
|
||||
complex_solid.add_tag("simple_topology")
|
||||
elif complexity_ratio < 10:
|
||||
complex_solid.add_tag("moderate_topology")
|
||||
else:
|
||||
complex_solid.add_tag("complex_topology")
|
||||
|
||||
print(f"复杂几何体分析:")
|
||||
print(f" 体积: {volume:.3f}")
|
||||
print(f" 表面积: {total_surface_area:.3f}")
|
||||
print(f" 面数: {face_count}")
|
||||
print(f" 边数: {edge_count}")
|
||||
print(f" 复杂度比: {complexity_ratio:.2f}")
|
||||
print(f" 面分布 - 小:{len(small_faces)}, 中:{len(medium_faces)}, 大:{len(large_faces)}")
|
||||
print(f" 标签: {complex_solid.get_tags()}")
|
||||
|
||||
return complex_solid
|
||||
|
||||
complex_geometry = create_complex_geometry()
|
||||
```
|
||||
|
||||
### Solid Quality Analysis
|
||||
|
||||
```python
|
||||
from simplecadapi import make_box_rsolid, make_cylinder_rsolid, make_sphere_rsolid
|
||||
|
||||
def analyze_solid_quality():
|
||||
"""分析实体质量"""
|
||||
|
||||
# 创建测试实体
|
||||
test_solids = [
|
||||
("small_box", make_box_rsolid(width=1, height=1, depth=1)),
|
||||
("large_box", make_box_rsolid(width=10, height=10, depth=10)),
|
||||
("thin_box", make_box_rsolid(width=10, height=10, depth=0.1)),
|
||||
("cylinder", make_cylinder_rsolid(center=(0, 0, 0), radius=2, height=5)),
|
||||
("sphere", make_sphere_rsolid(center=(0, 0, 0), radius=2))
|
||||
]
|
||||
|
||||
for name, solid in test_solids:
|
||||
solid.add_tag(name)
|
||||
solid.add_tag("test_geometry")
|
||||
|
||||
# 基本几何属性
|
||||
volume = solid.get_volume()
|
||||
faces = solid.get_faces()
|
||||
edges = solid.get_edges()
|
||||
|
||||
# 计算质量指标
|
||||
surface_area = sum(face.get_area() for face in faces)
|
||||
volume_to_surface_ratio = volume / surface_area if surface_area > 0 else 0
|
||||
|
||||
# 分析拓扑复杂度
|
||||
face_count = len(faces)
|
||||
edge_count = len(edges)
|
||||
euler_characteristic = None # 简化版本不计算欧拉特征数
|
||||
|
||||
# 面积分布分析
|
||||
face_areas = [face.get_area() for face in faces]
|
||||
if face_areas:
|
||||
min_area = min(face_areas)
|
||||
max_area = max(face_areas)
|
||||
area_ratio = max_area / min_area if min_area > 0 else float('inf')
|
||||
else:
|
||||
min_area = max_area = area_ratio = 0
|
||||
|
||||
# 质量评估
|
||||
quality_score = 0
|
||||
quality_issues = []
|
||||
|
||||
# 体积检查
|
||||
if volume > 1e-6:
|
||||
quality_score += 20
|
||||
else:
|
||||
quality_issues.append("极小体积")
|
||||
|
||||
# 面数检查
|
||||
if 4 <= face_count <= 100:
|
||||
quality_score += 20
|
||||
elif face_count > 100:
|
||||
quality_issues.append("面数过多")
|
||||
else:
|
||||
quality_issues.append("面数异常")
|
||||
|
||||
# 面积比检查
|
||||
if area_ratio < 1000:
|
||||
quality_score += 20
|
||||
else:
|
||||
quality_issues.append("面积差异过大")
|
||||
|
||||
# 体积效率检查
|
||||
if volume_to_surface_ratio > 0.1:
|
||||
quality_score += 20
|
||||
else:
|
||||
quality_issues.append("体积效率低")
|
||||
|
||||
# 边数合理性检查
|
||||
if edge_count < face_count * 10:
|
||||
quality_score += 20
|
||||
else:
|
||||
quality_issues.append("边数过多")
|
||||
|
||||
# 存储质量数据
|
||||
solid.set_metadata("volume", volume)
|
||||
solid.set_metadata("surface_area", surface_area)
|
||||
solid.set_metadata("face_count", face_count)
|
||||
solid.set_metadata("edge_count", edge_count)
|
||||
solid.set_metadata("volume_to_surface_ratio", volume_to_surface_ratio)
|
||||
solid.set_metadata("area_ratio", area_ratio)
|
||||
solid.set_metadata("quality_score", quality_score)
|
||||
solid.set_metadata("quality_issues", quality_issues)
|
||||
|
||||
# 质量标签
|
||||
if quality_score >= 80:
|
||||
solid.add_tag("high_quality")
|
||||
elif quality_score >= 60:
|
||||
solid.add_tag("good_quality")
|
||||
elif quality_score >= 40:
|
||||
solid.add_tag("acceptable_quality")
|
||||
else:
|
||||
solid.add_tag("poor_quality")
|
||||
|
||||
print(f"{name.upper()} 质量分析:")
|
||||
print(f" 体积: {volume:.6f}")
|
||||
print(f" 表面积: {surface_area:.3f}")
|
||||
print(f" 面数: {face_count}")
|
||||
print(f" 边数: {edge_count}")
|
||||
print(f" 体积效率: {volume_to_surface_ratio:.3f}")
|
||||
print(f" 面积比: {area_ratio:.2f}")
|
||||
print(f" 质量分数: {quality_score}/100")
|
||||
if quality_issues:
|
||||
print(f" 质量问题: {', '.join(quality_issues)}")
|
||||
print(f" 质量等级: {[tag for tag in solid.get_tags() if 'quality' in tag]}")
|
||||
print()
|
||||
|
||||
analyze_solid_quality()
|
||||
```
|
||||
|
||||
## String Representation
|
||||
|
||||
```python
|
||||
from simplecadapi import make_box_rsolid
|
||||
|
||||
box = make_box_rsolid(width=5, height=3, depth=2)
|
||||
box.add_tag("example_box")
|
||||
box.auto_tag_faces("box")
|
||||
box.set_metadata("material", "aluminum")
|
||||
|
||||
print(box)
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
Solid:
|
||||
volume: 30.000
|
||||
face_count: 6
|
||||
edge_count: 12
|
||||
faces:
|
||||
face_0:
|
||||
area: 15.000
|
||||
normal: [0.000, 0.000, 1.000]
|
||||
tags: [top]
|
||||
face_1:
|
||||
area: 15.000
|
||||
normal: [0.000, 0.000, -1.000]
|
||||
tags: [bottom]
|
||||
face_2:
|
||||
area: 10.000
|
||||
normal: [0.000, 1.000, 0.000]
|
||||
tags: [front]
|
||||
face_3:
|
||||
area: 10.000
|
||||
normal: [0.000, -1.000, 0.000]
|
||||
tags: [back]
|
||||
face_4:
|
||||
area: 6.000
|
||||
normal: [1.000, 0.000, 0.000]
|
||||
tags: [right]
|
||||
face_5:
|
||||
area: 6.000
|
||||
normal: [-1.000, 0.000, 0.000]
|
||||
tags: [left]
|
||||
tags: [example_box]
|
||||
metadata:
|
||||
material: aluminum
|
||||
```
|
||||
|
||||
## Relationships with Other Geometry
|
||||
|
||||
- **Face (Face)**: Boundary surfaces of a solid
|
||||
- **Edge (Edge)**: Boundaries of faces
|
||||
- **Shell (Shell)**: Solids can be decomposed into shells
|
||||
- **Compound (Compound)**: Multiple solids can form a compound
|
||||
|
||||
## Application Scenarios
|
||||
|
||||
- **Mechanical design**: Part modeling
|
||||
- **Architectural design**: Building components
|
||||
- **Product design**: Industrial products
|
||||
- **3D printing**: Prototype manufacturing
|
||||
- **Simulation analysis**: Finite element analysis
|
||||
|
||||
## Notes
|
||||
|
||||
- Solids must be closed, valid geometry
|
||||
- Boolean operations may change the solid's topology
|
||||
- Complex solids may contain many faces and edges
|
||||
- Feature operations may fail and require appropriate error handling
|
||||
- Automatic face tagging depends on geometry regularity
|
||||
- Solid quality directly affects the success rate and performance of subsequent operations
|
||||
+692
@@ -0,0 +1,692 @@
|
||||
# TaggedMixin
|
||||
|
||||
## Overview
|
||||
|
||||
`TaggedMixin` is the tag mixin class in SimpleCAD API, providing unified tag and metadata management functionality for all geometry classes. It is a mixin class inherited by all core geometry classes, implementing the core functionality of the tag system.
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class TaggedMixin:
|
||||
"""标签混入类,为几何体提供标签功能"""
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
- Add identification tags to geometries
|
||||
- Store and manage metadata
|
||||
- Implement geometry classification and queries
|
||||
- Support complex filtering and batch operations
|
||||
- Provide semantic information for geometries
|
||||
|
||||
## Initialization
|
||||
|
||||
### `__init__()`
|
||||
|
||||
Initialize the tag mixin class.
|
||||
|
||||
**Description:**
|
||||
- Creates an empty tag set `_tags`
|
||||
- Creates an empty metadata dictionary `_metadata`
|
||||
- Usually called in the `__init__` method of inheriting classes
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
class CustomGeometry(TaggedMixin):
|
||||
def __init__(self):
|
||||
TaggedMixin.__init__(self)
|
||||
# 其他初始化代码
|
||||
```
|
||||
|
||||
## Main Properties
|
||||
|
||||
- `_tags`: Tag set (Set[str]), storing geometry tags
|
||||
- `_metadata`: Metadata dictionary (Dict[str, Any]), storing key-value pairs
|
||||
|
||||
## Tag Management Methods
|
||||
|
||||
### `add_tag(tag)`
|
||||
|
||||
Add a tag to the geometry.
|
||||
|
||||
**Parameters:**
|
||||
- `tag` (str): Name of the tag to add
|
||||
|
||||
**Exceptions:**
|
||||
- `TypeError`: Raised when the tag is not a string type
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_box_rsolid
|
||||
|
||||
box = make_box_rsolid(width=5, height=3, depth=2)
|
||||
box.add_tag("structural")
|
||||
box.add_tag("main_component")
|
||||
box.add_tag("steel_part")
|
||||
|
||||
print(box.get_tags()) # {'structural', 'main_component', 'steel_part'}
|
||||
```
|
||||
|
||||
### `remove_tag(tag)`
|
||||
|
||||
Remove a specified tag from the geometry.
|
||||
|
||||
**Parameters:**
|
||||
- `tag` (str): Name of the tag to remove
|
||||
|
||||
**Description:**
|
||||
- If the tag does not exist, no exception will be raised
|
||||
- Uses the `discard` method for safe removal
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_box_rsolid
|
||||
|
||||
box = make_box_rsolid(width=5, height=3, depth=2)
|
||||
box.add_tag("temporary")
|
||||
box.add_tag("structural")
|
||||
|
||||
box.remove_tag("temporary")
|
||||
print(box.get_tags()) # {'structural'}
|
||||
|
||||
# 移除不存在的标签不会出错
|
||||
box.remove_tag("nonexistent")
|
||||
```
|
||||
|
||||
### `has_tag(tag)`
|
||||
|
||||
Check if the geometry has a specified tag.
|
||||
|
||||
**Parameters:**
|
||||
- `tag` (str): Name of the tag to check
|
||||
|
||||
**Returns:**
|
||||
- `bool`: Returns True if the tag exists, False otherwise
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_cylinder_rsolid
|
||||
|
||||
cylinder = make_cylinder_rsolid(center=(0, 0, 0), radius=2, height=5)
|
||||
cylinder.add_tag("rotational")
|
||||
cylinder.add_tag("hollow_ready")
|
||||
|
||||
if cylinder.has_tag("rotational"):
|
||||
print("这是一个旋转体")
|
||||
|
||||
if not cylinder.has_tag("solid"):
|
||||
cylinder.add_tag("solid")
|
||||
```
|
||||
|
||||
### `get_tags()`
|
||||
|
||||
Get all tags of the geometry.
|
||||
|
||||
**Returns:**
|
||||
- `Set[str]`: A copy of the tag set
|
||||
|
||||
**Description:**
|
||||
- Returns a copy of the tag set; modifying the return value will not affect the original tags
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_sphere_rsolid
|
||||
|
||||
sphere = make_sphere_rsolid(center=(0, 0, 0), radius=1.5)
|
||||
sphere.add_tag("curved")
|
||||
sphere.add_tag("symmetric")
|
||||
sphere.add_tag("smooth")
|
||||
|
||||
tags = sphere.get_tags()
|
||||
print(f"球体标签: {sorted(tags)}") # ['curved', 'smooth', 'symmetric']
|
||||
|
||||
# 修改返回的集合不会影响原始标签
|
||||
tags.add("new_tag")
|
||||
print(f"原始标签仍然是: {sorted(sphere.get_tags())}")
|
||||
```
|
||||
|
||||
## Metadata Management Methods
|
||||
|
||||
### `set_metadata(key, value)`
|
||||
|
||||
Set a metadata key-value pair.
|
||||
|
||||
**Parameters:**
|
||||
- `key` (str): Metadata key name
|
||||
- `value` (Any): Metadata value, can be any type
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_box_rsolid
|
||||
|
||||
box = make_box_rsolid(width=5, height=3, depth=2)
|
||||
|
||||
# 设置各种类型的元数据
|
||||
box.set_metadata("material", "aluminum")
|
||||
box.set_metadata("density", 2.7)
|
||||
box.set_metadata("cost", 45.50)
|
||||
box.set_metadata("dimensions", [5, 3, 2])
|
||||
box.set_metadata("manufactured_date", "2024-01-15")
|
||||
box.set_metadata("is_finished", True)
|
||||
box.set_metadata("supplier", {"name": "ABC Corp", "contact": "info@abc.com"})
|
||||
```
|
||||
|
||||
### `get_metadata(key, default=None)`
|
||||
|
||||
Get the metadata value for a specified key.
|
||||
|
||||
**Parameters:**
|
||||
- `key` (str): Metadata key name
|
||||
- `default` (Any, optional): Default value, returned when the key does not exist
|
||||
|
||||
**Returns:**
|
||||
- `Any`: Metadata value or default value
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_cylinder_rsolid
|
||||
|
||||
cylinder = make_cylinder_rsolid(center=(0, 0, 0), radius=2, height=5)
|
||||
cylinder.set_metadata("material", "steel")
|
||||
cylinder.set_metadata("weight", 12.5)
|
||||
|
||||
# 获取存在的元数据
|
||||
material = cylinder.get_metadata("material")
|
||||
print(f"材料: {material}") # 材料: steel
|
||||
|
||||
weight = cylinder.get_metadata("weight")
|
||||
print(f"重量: {weight}") # 重量: 12.5
|
||||
|
||||
# 获取不存在的元数据
|
||||
color = cylinder.get_metadata("color", "default_color")
|
||||
print(f"颜色: {color}") # 颜色: default_color
|
||||
|
||||
# 不提供默认值时返回 None
|
||||
surface_finish = cylinder.get_metadata("surface_finish")
|
||||
print(f"表面处理: {surface_finish}") # 表面处理: None
|
||||
```
|
||||
|
||||
## Internal Helper Methods
|
||||
|
||||
### `_format_tags_and_metadata(indent=0)`
|
||||
|
||||
Format the string representation of tags and metadata.
|
||||
|
||||
**Parameters:**
|
||||
- `indent` (int): Indentation level
|
||||
|
||||
**Returns:**
|
||||
- `str`: Formatted string
|
||||
|
||||
**Description:**
|
||||
- Internal method, used for implementing `__str__` method
|
||||
- Generates aesthetically pleasing tag and metadata display format
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic Tag Usage
|
||||
|
||||
```python
|
||||
from simplecadapi import make_box_rsolid, make_cylinder_rsolid, make_sphere_rsolid
|
||||
|
||||
def basic_tagging_example():
|
||||
"""基础标签使用示例"""
|
||||
|
||||
# 创建几何体
|
||||
box = make_box_rsolid(width=4, height=3, depth=2)
|
||||
cylinder = make_cylinder_rsolid(center=(0, 0, 0), radius=1.5, height=4)
|
||||
sphere = make_sphere_rsolid(center=(0, 0, 0), radius=1)
|
||||
|
||||
# 添加几何类型标签
|
||||
box.add_tag("rectangular")
|
||||
box.add_tag("prismatic")
|
||||
|
||||
cylinder.add_tag("cylindrical")
|
||||
cylinder.add_tag("rotational")
|
||||
|
||||
sphere.add_tag("spherical")
|
||||
sphere.add_tag("curved")
|
||||
|
||||
# 添加功能标签
|
||||
box.add_tag("structural")
|
||||
cylinder.add_tag("pipe")
|
||||
sphere.add_tag("bearing")
|
||||
|
||||
# 添加材料标签
|
||||
for geom in [box, cylinder, sphere]:
|
||||
geom.add_tag("metal")
|
||||
geom.add_tag("machinable")
|
||||
|
||||
# 检查和使用标签
|
||||
geometries = [("盒子", box), ("圆柱", cylinder), ("球体", sphere)]
|
||||
|
||||
for name, geom in geometries:
|
||||
print(f"{name} 标签: {sorted(geom.get_tags())}")
|
||||
|
||||
if geom.has_tag("rotational"):
|
||||
print(f" -> {name} 是旋转对称的")
|
||||
|
||||
if geom.has_tag("structural"):
|
||||
print(f" -> {name} 用于结构支撑")
|
||||
|
||||
basic_tagging_example()
|
||||
```
|
||||
|
||||
### Advanced Metadata Management
|
||||
|
||||
```python
|
||||
from simplecadapi import make_box_rsolid
|
||||
import datetime
|
||||
|
||||
def advanced_metadata_example():
|
||||
"""高级元数据管理示例"""
|
||||
|
||||
# 创建零件
|
||||
part = make_box_rsolid(width=10, height=8, depth=5)
|
||||
part.add_tag("machined_part")
|
||||
part.add_tag("aluminum")
|
||||
|
||||
# 设置基本属性
|
||||
part.set_metadata("part_number", "MP-001-A")
|
||||
part.set_metadata("revision", "Rev-C")
|
||||
part.set_metadata("material", "6061-T6 Aluminum")
|
||||
part.set_metadata("density", 2.7) # g/cm³
|
||||
|
||||
# 设置尺寸信息
|
||||
dimensions = {
|
||||
"length": 10,
|
||||
"width": 8,
|
||||
"height": 5,
|
||||
"tolerance": "±0.1"
|
||||
}
|
||||
part.set_metadata("dimensions", dimensions)
|
||||
|
||||
# 设置制造信息
|
||||
manufacturing = {
|
||||
"process": "CNC Milling",
|
||||
"machine": "Haas VF-2",
|
||||
"operator": "John Doe",
|
||||
"setup_time": 45, # minutes
|
||||
"cycle_time": 23 # minutes
|
||||
}
|
||||
part.set_metadata("manufacturing", manufacturing)
|
||||
|
||||
# 设置质量控制信息
|
||||
quality = {
|
||||
"inspection_level": "Level II",
|
||||
"critical_dimensions": ["length", "width"],
|
||||
"surface_finish": "Ra 1.6 μm",
|
||||
"hardness": "HB 95-105"
|
||||
}
|
||||
part.set_metadata("quality", quality)
|
||||
|
||||
# 设置成本信息
|
||||
cost_breakdown = {
|
||||
"material_cost": 15.50,
|
||||
"machining_cost": 45.00,
|
||||
"setup_cost": 25.00,
|
||||
"overhead": 12.50,
|
||||
"total": 98.00
|
||||
}
|
||||
part.set_metadata("cost", cost_breakdown)
|
||||
|
||||
# 设置时间信息
|
||||
part.set_metadata("created_date", datetime.datetime.now().isoformat())
|
||||
part.set_metadata("due_date", "2024-02-15")
|
||||
part.set_metadata("estimated_completion", "2024-02-10")
|
||||
|
||||
# 计算衍生属性
|
||||
volume = part.get_volume()
|
||||
density = part.get_metadata("density")
|
||||
weight = volume * density / 1000 # 转换为kg(假设体积单位为cm³)
|
||||
|
||||
part.set_metadata("calculated_weight", weight)
|
||||
part.set_metadata("weight_unit", "kg")
|
||||
|
||||
# 生成报告
|
||||
print("零件详细信息报告:")
|
||||
print("=" * 50)
|
||||
|
||||
# 基本信息
|
||||
print(f"零件号: {part.get_metadata('part_number')}")
|
||||
print(f"版本: {part.get_metadata('revision')}")
|
||||
print(f"材料: {part.get_metadata('material')}")
|
||||
print(f"重量: {weight:.3f} kg")
|
||||
|
||||
# 尺寸信息
|
||||
dims = part.get_metadata("dimensions")
|
||||
print(f"\n尺寸:")
|
||||
print(f" 长x宽x高: {dims['length']} x {dims['width']} x {dims['height']}")
|
||||
print(f" 公差: {dims['tolerance']}")
|
||||
|
||||
# 制造信息
|
||||
mfg = part.get_metadata("manufacturing")
|
||||
print(f"\n制造:")
|
||||
print(f" 工艺: {mfg['process']}")
|
||||
print(f" 设备: {mfg['machine']}")
|
||||
print(f" 周期时间: {mfg['cycle_time']} 分钟")
|
||||
|
||||
# 成本信息
|
||||
cost = part.get_metadata("cost")
|
||||
print(f"\n成本:")
|
||||
print(f" 材料: ${cost['material_cost']:.2f}")
|
||||
print(f" 加工: ${cost['machining_cost']:.2f}")
|
||||
print(f" 总计: ${cost['total']:.2f}")
|
||||
|
||||
return part
|
||||
|
||||
advanced_part = advanced_metadata_example()
|
||||
```
|
||||
|
||||
### Batch Tag and Metadata Operations
|
||||
|
||||
```python
|
||||
from simplecadapi import make_box_rsolid, make_cylinder_rsolid
|
||||
|
||||
def batch_tagging_example():
|
||||
"""批量标签和元数据操作示例"""
|
||||
|
||||
# 创建零件集合
|
||||
parts = []
|
||||
|
||||
# 创建不同类型的零件
|
||||
part_specs = [
|
||||
("housing", "box", (10, 8, 6), "aluminum"),
|
||||
("shaft", "cylinder", (1, 15), "steel"),
|
||||
("bracket", "box", (5, 3, 2), "aluminum"),
|
||||
("bushing", "cylinder", (0.5, 3), "bronze"),
|
||||
("plate", "box", (8, 6, 0.5), "steel")
|
||||
]
|
||||
|
||||
for name, shape_type, dimensions, material in part_specs:
|
||||
if shape_type == "box":
|
||||
part = make_box_rsolid(width=dimensions[0], height=dimensions[1], depth=dimensions[2])
|
||||
else: # cylinder
|
||||
part = make_cylinder_rsolid(center=(0, 0, 0), radius=dimensions[0], height=dimensions[1])
|
||||
|
||||
# 基本标签
|
||||
part.add_tag(name)
|
||||
part.add_tag(shape_type)
|
||||
part.add_tag(material)
|
||||
|
||||
# 基本元数据
|
||||
part.set_metadata("name", name)
|
||||
part.set_metadata("material", material)
|
||||
part.set_metadata("shape_type", shape_type)
|
||||
|
||||
parts.append(part)
|
||||
|
||||
# 批量添加通用标签
|
||||
for part in parts:
|
||||
part.add_tag("mechanical_part")
|
||||
part.add_tag("machined")
|
||||
part.set_metadata("project", "Assembly_001")
|
||||
part.set_metadata("status", "design")
|
||||
|
||||
# 根据材料批量设置属性
|
||||
material_properties = {
|
||||
"aluminum": {"density": 2.7, "cost_per_kg": 3.50, "color": "silver"},
|
||||
"steel": {"density": 7.8, "cost_per_kg": 1.20, "color": "gray"},
|
||||
"bronze": {"density": 8.9, "cost_per_kg": 8.50, "color": "bronze"}
|
||||
}
|
||||
|
||||
for part in parts:
|
||||
material = part.get_metadata("material")
|
||||
if material in material_properties:
|
||||
props = material_properties[material]
|
||||
for prop_name, prop_value in props.items():
|
||||
part.set_metadata(prop_name, prop_value)
|
||||
|
||||
# 根据尺寸批量分类
|
||||
for part in parts:
|
||||
volume = part.get_volume()
|
||||
|
||||
if volume < 10:
|
||||
part.add_tag("small_part")
|
||||
part.set_metadata("size_category", "small")
|
||||
elif volume < 100:
|
||||
part.add_tag("medium_part")
|
||||
part.set_metadata("size_category", "medium")
|
||||
else:
|
||||
part.add_tag("large_part")
|
||||
part.set_metadata("size_category", "large")
|
||||
|
||||
# 批量计算成本
|
||||
for part in parts:
|
||||
volume = part.get_volume()
|
||||
density = part.get_metadata("density", 1.0)
|
||||
cost_per_kg = part.get_metadata("cost_per_kg", 2.0)
|
||||
|
||||
weight = volume * density / 1000 # 转换为kg
|
||||
material_cost = weight * cost_per_kg
|
||||
|
||||
part.set_metadata("weight", weight)
|
||||
part.set_metadata("material_cost", material_cost)
|
||||
|
||||
# 批量查询和统计
|
||||
print("批量操作结果:")
|
||||
print("=" * 40)
|
||||
|
||||
# 按材料统计
|
||||
materials = set(p.get_metadata("material") for p in parts)
|
||||
for material in materials:
|
||||
material_parts = [p for p in parts if p.get_metadata("material") == material]
|
||||
total_weight = sum(p.get_metadata("weight", 0) for p in material_parts)
|
||||
total_cost = sum(p.get_metadata("material_cost", 0) for p in material_parts)
|
||||
|
||||
print(f"{material.upper()} 零件:")
|
||||
print(f" 数量: {len(material_parts)}")
|
||||
print(f" 总重量: {total_weight:.3f} kg")
|
||||
print(f" 材料成本: ${total_cost:.2f}")
|
||||
print()
|
||||
|
||||
# 按尺寸分类统计
|
||||
size_categories = ["small", "medium", "large"]
|
||||
for size in size_categories:
|
||||
size_parts = [p for p in parts if p.has_tag(f"{size}_part")]
|
||||
if size_parts:
|
||||
avg_volume = sum(p.get_volume() for p in size_parts) / len(size_parts)
|
||||
print(f"{size.upper()} 零件: {len(size_parts)} 个, 平均体积: {avg_volume:.3f}")
|
||||
|
||||
return parts
|
||||
|
||||
batch_parts = batch_tagging_example()
|
||||
```
|
||||
|
||||
### Advanced Query and Filtering
|
||||
|
||||
```python
|
||||
from simplecadapi import make_box_rsolid, make_cylinder_rsolid, make_sphere_rsolid
|
||||
|
||||
def advanced_query_example():
|
||||
"""高级查询和筛选示例"""
|
||||
|
||||
# 创建复杂的零件库
|
||||
parts_library = []
|
||||
|
||||
# 定义零件类型
|
||||
part_definitions = [
|
||||
# (name, shape, dimensions, material, application, priority)
|
||||
("main_frame", "box", (20, 15, 10), "steel", "structural", "high"),
|
||||
("support_beam", "box", (15, 5, 5), "aluminum", "structural", "medium"),
|
||||
("motor_shaft", "cylinder", (2, 25), "steel", "rotating", "high"),
|
||||
("bearing_housing", "cylinder", (3, 8), "aluminum", "support", "medium"),
|
||||
("control_sphere", "sphere", (2,), "plastic", "interface", "low"),
|
||||
("sensor_mount", "box", (3, 3, 2), "aluminum", "mounting", "medium"),
|
||||
("drive_wheel", "cylinder", (5, 3), "rubber", "motion", "high"),
|
||||
("safety_cover", "box", (12, 8, 1), "plastic", "protection", "low")
|
||||
]
|
||||
|
||||
for name, shape, dims, material, application, priority in part_definitions:
|
||||
# 创建几何体
|
||||
if shape == "box":
|
||||
part = make_box_rsolid(width=dims[0], height=dims[1], depth=dims[2])
|
||||
elif shape == "cylinder":
|
||||
part = make_cylinder_rsolid(center=(0, 0, 0), radius=dims[0], height=dims[1])
|
||||
else: # sphere
|
||||
part = make_sphere_rsolid(center=(0, 0, 0), radius=dims[0])
|
||||
|
||||
# 添加标签
|
||||
part.add_tag(name)
|
||||
part.add_tag(shape)
|
||||
part.add_tag(material)
|
||||
part.add_tag(application)
|
||||
part.add_tag(priority)
|
||||
|
||||
# 设置详细元数据
|
||||
part.set_metadata("name", name)
|
||||
part.set_metadata("shape", shape)
|
||||
part.set_metadata("material", material)
|
||||
part.set_metadata("application", application)
|
||||
part.set_metadata("priority", priority)
|
||||
part.set_metadata("volume", part.get_volume())
|
||||
|
||||
# 根据应用设置其他属性
|
||||
if application == "structural":
|
||||
part.add_tag("load_bearing")
|
||||
part.set_metadata("safety_factor", 2.5)
|
||||
elif application == "rotating":
|
||||
part.add_tag("dynamic")
|
||||
part.set_metadata("max_rpm", 3600)
|
||||
elif application == "interface":
|
||||
part.add_tag("user_contact")
|
||||
part.set_metadata("ergonomic", True)
|
||||
|
||||
# 根据材料设置属性
|
||||
material_db = {
|
||||
"steel": {"density": 7.8, "strength": "high", "cost": 1.2},
|
||||
"aluminum": {"density": 2.7, "strength": "medium", "cost": 3.5},
|
||||
"plastic": {"density": 1.2, "strength": "low", "cost": 0.8},
|
||||
"rubber": {"density": 1.5, "strength": "flexible", "cost": 2.0}
|
||||
}
|
||||
|
||||
if material in material_db:
|
||||
for prop, value in material_db[material].items():
|
||||
part.set_metadata(prop, value)
|
||||
|
||||
parts_library.append(part)
|
||||
|
||||
# 定义查询函数
|
||||
def query_parts(parts, **criteria):
|
||||
"""根据条件查询零件"""
|
||||
result = parts
|
||||
|
||||
for key, value in criteria.items():
|
||||
if key == "has_tag":
|
||||
result = [p for p in result if p.has_tag(value)]
|
||||
elif key == "has_any_tag":
|
||||
result = [p for p in result if any(p.has_tag(tag) for tag in value)]
|
||||
elif key == "has_all_tags":
|
||||
result = [p for p in result if all(p.has_tag(tag) for tag in value)]
|
||||
elif key == "metadata_equals":
|
||||
meta_key, meta_value = value
|
||||
result = [p for p in result if p.get_metadata(meta_key) == meta_value]
|
||||
elif key == "metadata_greater":
|
||||
meta_key, threshold = value
|
||||
result = [p for p in result if p.get_metadata(meta_key, 0) > threshold]
|
||||
elif key == "metadata_less":
|
||||
meta_key, threshold = value
|
||||
result = [p for p in result if p.get_metadata(meta_key, float('inf')) < threshold]
|
||||
elif key == "volume_range":
|
||||
min_vol, max_vol = value
|
||||
result = [p for p in result if min_vol <= p.get_volume() <= max_vol]
|
||||
|
||||
return result
|
||||
|
||||
# 执行各种查询
|
||||
print("高级查询示例:")
|
||||
print("=" * 50)
|
||||
|
||||
# 1. 简单标签查询
|
||||
steel_parts = query_parts(parts_library, has_tag="steel")
|
||||
print(f"1. 钢制零件: {len(steel_parts)} 个")
|
||||
for part in steel_parts:
|
||||
print(f" - {part.get_metadata('name')}")
|
||||
|
||||
# 2. 多标签查询
|
||||
structural_aluminum = query_parts(parts_library, has_all_tags=["aluminum", "structural"])
|
||||
print(f"\n2. 铝制结构件: {len(structural_aluminum)} 个")
|
||||
|
||||
# 3. 元数据查询
|
||||
high_priority = query_parts(parts_library, metadata_equals=("priority", "high"))
|
||||
print(f"\n3. 高优先级零件: {len(high_priority)} 个")
|
||||
for part in high_priority:
|
||||
print(f" - {part.get_metadata('name')}: {part.get_metadata('application')}")
|
||||
|
||||
# 4. 范围查询
|
||||
large_parts = query_parts(parts_library, volume_range=(100, 5000))
|
||||
print(f"\n4. 大型零件 (体积 100-5000): {len(large_parts)} 个")
|
||||
|
||||
# 5. 复合查询
|
||||
critical_metal_parts = query_parts(
|
||||
parts_library,
|
||||
has_any_tag=["steel", "aluminum"],
|
||||
metadata_equals=("priority", "high")
|
||||
)
|
||||
print(f"\n5. 关键金属零件: {len(critical_metal_parts)} 个")
|
||||
|
||||
# 6. 性能分析查询
|
||||
load_bearing = query_parts(parts_library, has_tag="load_bearing")
|
||||
if load_bearing:
|
||||
avg_safety_factor = sum(p.get_metadata("safety_factor", 1) for p in load_bearing) / len(load_bearing)
|
||||
print(f"\n6. 承载零件平均安全系数: {avg_safety_factor:.2f}")
|
||||
|
||||
# 7. 成本分析
|
||||
total_cost = 0
|
||||
cost_by_material = {}
|
||||
|
||||
for part in parts_library:
|
||||
volume = part.get_volume()
|
||||
density = part.get_metadata("density", 1)
|
||||
cost_per_kg = part.get_metadata("cost", 1)
|
||||
|
||||
weight = volume * density / 1000
|
||||
cost = weight * cost_per_kg
|
||||
total_cost += cost
|
||||
|
||||
material = part.get_metadata("material")
|
||||
if material not in cost_by_material:
|
||||
cost_by_material[material] = 0
|
||||
cost_by_material[material] += cost
|
||||
|
||||
print(f"\n7. 成本分析:")
|
||||
print(f" 总成本: ${total_cost:.2f}")
|
||||
for material, cost in cost_by_material.items():
|
||||
print(f" {material}: ${cost:.2f}")
|
||||
|
||||
return parts_library
|
||||
|
||||
parts_db = advanced_query_example()
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Tag Naming Conventions
|
||||
|
||||
1. **Consistency**: Use consistent naming styles
|
||||
2. **Hierarchy**: Use dot-separated hierarchical structures (e.g., `material.metal.steel`)
|
||||
3. **Descriptive**: Tag names should be descriptive
|
||||
4. **Avoid Spaces**: Use underscores instead of spaces
|
||||
|
||||
### Metadata Organization
|
||||
|
||||
1. **Structured**: Use dictionaries to organize related metadata
|
||||
2. **Type Consistency**: Use the same data type for metadata with similar purposes
|
||||
3. **Clear Units**: Numerical metadata should specify units
|
||||
4. **Version Control**: Important metadata should include version information
|
||||
|
||||
### Performance Considerations
|
||||
|
||||
1. **Tag Quantity**: Avoid excessive tags that may impact query performance
|
||||
2. **Metadata Size**: Avoid storing large data objects
|
||||
3. **Query Optimization**: Organize tags and metadata appropriately to support efficient queries
|
||||
4. **Memory Management**: Clean up unnecessary tags and metadata in a timely manner
|
||||
|
||||
## Notes
|
||||
|
||||
- Tags must be of string type
|
||||
- Tags are case-sensitive
|
||||
- Metadata values can be of any type, but serializable types are recommended
|
||||
- Tag and metadata modifications do not trigger geometry updates
|
||||
- Special handling is required for tags and metadata during deep copying
|
||||
- Tags and metadata do not participate in geometric operations; they are only used for management and queries
|
||||
+320
@@ -0,0 +1,320 @@
|
||||
# Vertex
|
||||
|
||||
## Overview
|
||||
|
||||
`Vertex` is the vertex class in SimpleCAD API, representing a point in 3D space. It wraps CADQuery's Vertex object and adds tag functionality for identifying and managing specific vertices in geometries.
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class Vertex(TaggedMixin):
|
||||
"""顶点类,包装CADQuery的Vertex,添加标签功能"""
|
||||
```
|
||||
|
||||
## Inheritance Relationships
|
||||
|
||||
- Inherits from `TaggedMixin`, with tag and metadata functionality
|
||||
|
||||
## Usage
|
||||
|
||||
- Represent points in 3D space
|
||||
- Serve as building elements for edges, wires, faces, and other geometries
|
||||
- Provide vertex coordinate information
|
||||
- Support tag management and queries
|
||||
|
||||
## Constructor
|
||||
|
||||
### `__init__(cq_vertex)`
|
||||
|
||||
Initialize a vertex object.
|
||||
|
||||
**Parameters:**
|
||||
- `cq_vertex` (cadquery.Vertex): CADQuery vertex object
|
||||
|
||||
**Exceptions:**
|
||||
- `ValueError`: Raised when the input vertex object is invalid
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_point_rvertex
|
||||
|
||||
# 通过 SimpleCAD 函数创建顶点
|
||||
vertex = make_point_rvertex(1.0, 2.0, 3.0)
|
||||
```
|
||||
|
||||
## Main Properties
|
||||
|
||||
- `cq_vertex`: Underlying CADQuery vertex object
|
||||
- `_tags`: Tag set (inherited from TaggedMixin)
|
||||
- `_metadata`: Metadata dictionary (inherited from TaggedMixin)
|
||||
|
||||
## Common Methods
|
||||
|
||||
### `get_coordinates()`
|
||||
|
||||
Get the coordinates of the vertex.
|
||||
|
||||
**Returns:**
|
||||
- `Tuple[float, float, float]`: Vertex coordinates (x, y, z)
|
||||
|
||||
**Exceptions:**
|
||||
- `ValueError`: Raised when coordinate retrieval fails
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_point_rvertex
|
||||
|
||||
vertex = make_point_rvertex(1.0, 2.0, 3.0)
|
||||
coords = vertex.get_coordinates()
|
||||
print(coords) # (1.0, 2.0, 3.0)
|
||||
```
|
||||
|
||||
### Tag Management Methods
|
||||
|
||||
Methods inherited from `TaggedMixin`:
|
||||
|
||||
#### `add_tag(tag)`
|
||||
Add a tag.
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
vertex = make_point_rvertex(0, 0, 0)
|
||||
vertex.add_tag("origin")
|
||||
vertex.add_tag("reference_point")
|
||||
```
|
||||
|
||||
#### `has_tag(tag)`
|
||||
Check if a specified tag exists.
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
vertex = make_point_rvertex(0, 0, 0)
|
||||
vertex.add_tag("origin")
|
||||
|
||||
if vertex.has_tag("origin"):
|
||||
print("这是原点")
|
||||
```
|
||||
|
||||
#### `get_tags()`
|
||||
Get all tags.
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
vertex = make_point_rvertex(0, 0, 0)
|
||||
vertex.add_tag("origin")
|
||||
vertex.add_tag("reference")
|
||||
|
||||
tags = vertex.get_tags()
|
||||
print(tags) # {'origin', 'reference'}
|
||||
```
|
||||
|
||||
#### `remove_tag(tag)`
|
||||
Remove a tag.
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
vertex = make_point_rvertex(0, 0, 0)
|
||||
vertex.add_tag("temp")
|
||||
vertex.remove_tag("temp")
|
||||
```
|
||||
|
||||
### Metadata Management Methods
|
||||
|
||||
#### `set_metadata(key, value)`
|
||||
Set metadata.
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
vertex = make_point_rvertex(0, 0, 0)
|
||||
vertex.set_metadata("created_by", "user_input")
|
||||
vertex.set_metadata("importance", "high")
|
||||
```
|
||||
|
||||
#### `get_metadata(key, default=None)`
|
||||
Get metadata.
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
vertex = make_point_rvertex(0, 0, 0)
|
||||
vertex.set_metadata("created_by", "user_input")
|
||||
|
||||
creator = vertex.get_metadata("created_by")
|
||||
print(creator) # "user_input"
|
||||
|
||||
unknown = vertex.get_metadata("unknown_key", "default_value")
|
||||
print(unknown) # "default_value"
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Creating and Using Vertices
|
||||
|
||||
```python
|
||||
from simplecadapi import make_point_rvertex
|
||||
|
||||
# 创建顶点
|
||||
vertex1 = make_point_rvertex(0, 0, 0)
|
||||
vertex2 = make_point_rvertex(1, 1, 1)
|
||||
|
||||
# 获取坐标
|
||||
coords1 = vertex1.get_coordinates()
|
||||
coords2 = vertex2.get_coordinates()
|
||||
|
||||
print(f"顶点1坐标: {coords1}") # 顶点1坐标: (0.0, 0.0, 0.0)
|
||||
print(f"顶点2坐标: {coords2}") # 顶点2坐标: (1.0, 1.0, 1.0)
|
||||
```
|
||||
|
||||
### Vertex Tag Management
|
||||
|
||||
```python
|
||||
from simplecadapi import make_point_rvertex
|
||||
|
||||
# 创建关键点
|
||||
origin = make_point_rvertex(0, 0, 0)
|
||||
corner1 = make_point_rvertex(10, 0, 0)
|
||||
corner2 = make_point_rvertex(10, 10, 0)
|
||||
corner3 = make_point_rvertex(0, 10, 0)
|
||||
|
||||
# 添加标签
|
||||
origin.add_tag("origin")
|
||||
origin.add_tag("reference")
|
||||
|
||||
corner1.add_tag("corner")
|
||||
corner1.add_tag("x_axis")
|
||||
|
||||
corner2.add_tag("corner")
|
||||
corner2.add_tag("diagonal")
|
||||
|
||||
corner3.add_tag("corner")
|
||||
corner3.add_tag("y_axis")
|
||||
|
||||
# 查找所有角点
|
||||
vertices = [origin, corner1, corner2, corner3]
|
||||
corners = [v for v in vertices if v.has_tag("corner")]
|
||||
|
||||
print(f"找到 {len(corners)} 个角点")
|
||||
```
|
||||
|
||||
### Vertex Classification and Management
|
||||
|
||||
```python
|
||||
from simplecadapi import make_point_rvertex
|
||||
|
||||
def create_grid_vertices(width, height, spacing):
|
||||
"""创建网格顶点"""
|
||||
vertices = []
|
||||
|
||||
for i in range(width + 1):
|
||||
for j in range(height + 1):
|
||||
x = i * spacing
|
||||
y = j * spacing
|
||||
z = 0
|
||||
|
||||
vertex = make_point_rvertex(x, y, z)
|
||||
|
||||
# 添加位置标签
|
||||
if i == 0 and j == 0:
|
||||
vertex.add_tag("origin")
|
||||
elif i == 0:
|
||||
vertex.add_tag("left_edge")
|
||||
elif i == width:
|
||||
vertex.add_tag("right_edge")
|
||||
|
||||
if j == 0:
|
||||
vertex.add_tag("bottom_edge")
|
||||
elif j == height:
|
||||
vertex.add_tag("top_edge")
|
||||
|
||||
# 添加角点标签
|
||||
if (i == 0 or i == width) and (j == 0 or j == height):
|
||||
vertex.add_tag("corner")
|
||||
|
||||
# 添加元数据
|
||||
vertex.set_metadata("grid_position", (i, j))
|
||||
vertex.set_metadata("distance_from_origin", (x*x + y*y)**0.5)
|
||||
|
||||
vertices.append(vertex)
|
||||
|
||||
return vertices
|
||||
|
||||
# 创建 5x3 网格
|
||||
vertices = create_grid_vertices(5, 3, 1.0)
|
||||
|
||||
# 查找特定顶点
|
||||
corners = [v for v in vertices if v.has_tag("corner")]
|
||||
origin = [v for v in vertices if v.has_tag("origin")][0]
|
||||
|
||||
print(f"网格顶点总数: {len(vertices)}")
|
||||
print(f"角点数量: {len(corners)}")
|
||||
print(f"原点坐标: {origin.get_coordinates()}")
|
||||
```
|
||||
|
||||
### Vertex Distance Calculation
|
||||
|
||||
```python
|
||||
import math
|
||||
from simplecadapi import make_point_rvertex
|
||||
|
||||
def calculate_distance(vertex1, vertex2):
|
||||
"""计算两个顶点之间的距离"""
|
||||
coords1 = vertex1.get_coordinates()
|
||||
coords2 = vertex2.get_coordinates()
|
||||
|
||||
dx = coords2[0] - coords1[0]
|
||||
dy = coords2[1] - coords1[1]
|
||||
dz = coords2[2] - coords1[2]
|
||||
|
||||
return math.sqrt(dx*dx + dy*dy + dz*dz)
|
||||
|
||||
# 创建顶点
|
||||
v1 = make_point_rvertex(0, 0, 0)
|
||||
v2 = make_point_rvertex(3, 4, 0)
|
||||
v3 = make_point_rvertex(0, 0, 5)
|
||||
|
||||
# 计算距离
|
||||
dist12 = calculate_distance(v1, v2)
|
||||
dist13 = calculate_distance(v1, v3)
|
||||
dist23 = calculate_distance(v2, v3)
|
||||
|
||||
print(f"v1 到 v2 的距离: {dist12}") # 5.0
|
||||
print(f"v1 到 v3 的距离: {dist13}") # 5.0
|
||||
print(f"v2 到 v3 的距离: {dist23}") # 约 7.07
|
||||
```
|
||||
|
||||
## String Representation
|
||||
|
||||
```python
|
||||
from simplecadapi import make_point_rvertex
|
||||
|
||||
vertex = make_point_rvertex(1.234, 5.678, 9.012)
|
||||
vertex.add_tag("test_point")
|
||||
vertex.set_metadata("created_by", "example")
|
||||
|
||||
print(vertex)
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
Vertex:
|
||||
coordinates: [1.234, 5.678, 9.012]
|
||||
tags: [test_point]
|
||||
metadata:
|
||||
created_by: example
|
||||
```
|
||||
|
||||
## Relationships with Other Geometries
|
||||
|
||||
Vertices are the fundamental elements that compose more complex geometries:
|
||||
|
||||
- **Edge**: Defined by two vertices
|
||||
- **Wire**: Composed of multiple connected edges, containing multiple vertices
|
||||
- **Face**: Boundary defined by vertices
|
||||
- **Solid**: Ultimately composed of vertices
|
||||
|
||||
## Notes
|
||||
|
||||
- Vertex objects wrap CADQuery's underlying vertices; do not modify coordinates directly
|
||||
- Tags are of string type and are case-sensitive
|
||||
- Metadata can store values of any type
|
||||
- Vertex coordinates are read-only; to modify positions, create new vertices
|
||||
- Floating-point coordinates may have precision issues; consider tolerance when comparing
|
||||
+452
@@ -0,0 +1,452 @@
|
||||
# Wire
|
||||
|
||||
## Overview
|
||||
|
||||
`Wire` is the wire class in the SimpleCAD API, representing a 1D geometric path formed by connecting multiple edges. A wire can be open (different start and end points) or closed (forming a closed path). It wraps the CADQuery Wire object and adds tagging functionality.
|
||||
|
||||
## Class Definition
|
||||
|
||||
```python
|
||||
class Wire(TaggedMixin):
|
||||
"""线类,包装CADQuery的Wire,添加标签功能"""
|
||||
```
|
||||
|
||||
## Inheritance
|
||||
|
||||
- Inherits from `TaggedMixin`, providing tag and metadata functionality
|
||||
|
||||
## Usage
|
||||
|
||||
- Represent continuous paths or contours
|
||||
- Form the boundary of faces (Face)
|
||||
- Define paths for sweep, extrude, and other operations
|
||||
- Create complex geometric contours
|
||||
|
||||
## Constructor
|
||||
|
||||
### `__init__(cq_wire)`
|
||||
|
||||
Initializes a wire object.
|
||||
|
||||
**Parameters:**
|
||||
- `cq_wire` (cadquery.Wire): A CADQuery wire object
|
||||
|
||||
**Raises:**
|
||||
- `ValueError`: When the input wire object is invalid
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import (
|
||||
make_rectangle_rwire,
|
||||
make_circle_rwire,
|
||||
make_polyline_rwire
|
||||
)
|
||||
|
||||
# 通过 SimpleCAD 函数创建线
|
||||
rectangle = make_rectangle_rwire(width=5, height=3)
|
||||
circle = make_circle_rwire(center=(0, 0, 0), radius=2.0)
|
||||
polyline = make_polyline_rwire(points=[(0, 0, 0), (1, 1, 0), (2, 0, 0)])
|
||||
```
|
||||
|
||||
## Main Properties
|
||||
|
||||
- `cq_wire`: The underlying CADQuery wire object
|
||||
- `_tags`: Tag set (inherited from TaggedMixin)
|
||||
- `_metadata`: Metadata dictionary (inherited from TaggedMixin)
|
||||
|
||||
## Common Methods
|
||||
|
||||
### `get_edges()`
|
||||
|
||||
Get all edges that make up the wire.
|
||||
|
||||
**Returns:**
|
||||
- `List[Edge]`: List of edge objects
|
||||
|
||||
**Raises:**
|
||||
- `ValueError`: When edge list retrieval fails
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_rectangle_rwire
|
||||
|
||||
rectangle = make_rectangle_rwire(width=4, height=3)
|
||||
edges = rectangle.get_edges()
|
||||
|
||||
print(f"矩形由 {len(edges)} 条边组成")
|
||||
for i, edge in enumerate(edges):
|
||||
print(f"边 {i}: 长度 {edge.get_length():.3f}")
|
||||
```
|
||||
|
||||
### `is_closed()`
|
||||
|
||||
Check if the wire is closed.
|
||||
|
||||
**Returns:**
|
||||
- `bool`: Returns True if the wire is closed, False otherwise
|
||||
|
||||
**Raises:**
|
||||
- `ValueError`: When closure check fails
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from simplecadapi import make_rectangle_rwire, make_polyline_rwire
|
||||
|
||||
# 闭合线
|
||||
rectangle = make_rectangle_rwire(width=5, height=3)
|
||||
print(f"矩形是否闭合: {rectangle.is_closed()}") # True
|
||||
|
||||
# 开放线
|
||||
polyline = make_polyline_rwire(points=[(0, 0, 0), (1, 1, 0), (2, 0, 0)])
|
||||
print(f"折线是否闭合: {polyline.is_closed()}") # False
|
||||
```
|
||||
|
||||
### Tag Management Methods
|
||||
|
||||
Methods inherited from `TaggedMixin`:
|
||||
|
||||
#### `add_tag(tag)`, `has_tag(tag)`, `get_tags()`, `remove_tag(tag)`
|
||||
#### `set_metadata(key, value)`, `get_metadata(key, default=None)`
|
||||
|
||||
Usage is similar to Vertex; see [Vertex documentation](vertex.md) for details.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Creating Different Types of Wires
|
||||
|
||||
```python
|
||||
from simplecadapi import (
|
||||
make_rectangle_rwire,
|
||||
make_circle_rwire,
|
||||
make_polyline_rwire,
|
||||
make_spline_rwire
|
||||
)
|
||||
|
||||
# 矩形线
|
||||
rectangle = make_rectangle_rwire(width=10, height=6)
|
||||
rectangle.add_tag("rectangle")
|
||||
rectangle.add_tag("closed")
|
||||
|
||||
# 圆形线
|
||||
circle = make_circle_rwire(center=(0, 0, 0), radius=3.0)
|
||||
circle.add_tag("circle")
|
||||
circle.add_tag("closed")
|
||||
|
||||
# 折线
|
||||
polyline = make_polyline_rwire(points=[
|
||||
(0, 0, 0), (2, 0, 0), (2, 2, 0), (1, 3, 0), (0, 2, 0)
|
||||
])
|
||||
polyline.add_tag("polyline")
|
||||
polyline.add_tag("open")
|
||||
|
||||
# 样条线
|
||||
spline = make_spline_rwire(points=[
|
||||
(0, 0, 0), (1, 2, 0), (3, 2, 0), (4, 0, 0)
|
||||
])
|
||||
spline.add_tag("spline")
|
||||
spline.add_tag("smooth")
|
||||
|
||||
# 分析线的属性
|
||||
wires = [rectangle, circle, polyline, spline]
|
||||
for wire in wires:
|
||||
edges = wire.get_edges()
|
||||
closed = wire.is_closed()
|
||||
tags = wire.get_tags()
|
||||
|
||||
print(f"线类型: {tags}, 边数: {len(edges)}, 闭合: {closed}")
|
||||
```
|
||||
|
||||
### Creating Complex Contours
|
||||
|
||||
```python
|
||||
from simplecadapi import make_polyline_rwire
|
||||
|
||||
def create_complex_profile():
|
||||
"""创建复杂的轮廓线"""
|
||||
|
||||
# 定义轮廓点
|
||||
points = [
|
||||
(0, 0, 0), # 起点
|
||||
(10, 0, 0), # 底边
|
||||
(10, 2, 0), # 右下
|
||||
(8, 2, 0), # 内凹1
|
||||
(8, 4, 0), #
|
||||
(10, 4, 0), # 右上
|
||||
(10, 6, 0), # 顶边右
|
||||
(0, 6, 0), # 顶边左
|
||||
(0, 4, 0), # 左上
|
||||
(2, 4, 0), # 内凹2
|
||||
(2, 2, 0), #
|
||||
(0, 2, 0), # 左下
|
||||
(0, 0, 0) # 闭合回起点
|
||||
]
|
||||
|
||||
profile = make_polyline_rwire(points=points)
|
||||
profile.add_tag("complex_profile")
|
||||
profile.add_tag("symmetric")
|
||||
|
||||
# 添加几何信息
|
||||
edges = profile.get_edges()
|
||||
total_length = sum(edge.get_length() for edge in edges)
|
||||
|
||||
profile.set_metadata("total_length", total_length)
|
||||
profile.set_metadata("point_count", len(points))
|
||||
profile.set_metadata("edge_count", len(edges))
|
||||
|
||||
return profile
|
||||
|
||||
profile = create_complex_profile()
|
||||
print(f"复杂轮廓: {profile.get_tags()}")
|
||||
print(f"总长度: {profile.get_metadata('total_length'):.3f}")
|
||||
print(f"边数: {profile.get_metadata('edge_count')}")
|
||||
```
|
||||
|
||||
### Wire Analysis and Processing
|
||||
|
||||
```python
|
||||
from simplecadapi import make_rectangle_rwire, make_circle_rwire
|
||||
|
||||
def analyze_wire_properties():
|
||||
"""分析线的属性"""
|
||||
|
||||
# 创建不同的线
|
||||
rectangle = make_rectangle_rwire(width=6, height=4)
|
||||
circle = make_circle_rwire(center=(0, 0, 0), radius=2.0)
|
||||
|
||||
wires = [rectangle, circle]
|
||||
|
||||
for i, wire in enumerate(wires):
|
||||
# 基本属性
|
||||
edges = wire.get_edges()
|
||||
is_closed = wire.is_closed()
|
||||
|
||||
# 计算总长度
|
||||
total_length = sum(edge.get_length() for edge in edges)
|
||||
|
||||
# 分析边
|
||||
edge_lengths = [edge.get_length() for edge in edges]
|
||||
min_edge_length = min(edge_lengths)
|
||||
max_edge_length = max(edge_lengths)
|
||||
avg_edge_length = sum(edge_lengths) / len(edge_lengths)
|
||||
|
||||
# 添加标签和元数据
|
||||
wire.add_tag(f"wire_{i}")
|
||||
wire.add_tag("analyzed")
|
||||
|
||||
if is_closed:
|
||||
wire.add_tag("closed")
|
||||
else:
|
||||
wire.add_tag("open")
|
||||
|
||||
wire.set_metadata("total_length", total_length)
|
||||
wire.set_metadata("edge_count", len(edges))
|
||||
wire.set_metadata("min_edge_length", min_edge_length)
|
||||
wire.set_metadata("max_edge_length", max_edge_length)
|
||||
wire.set_metadata("avg_edge_length", avg_edge_length)
|
||||
|
||||
# 分类边
|
||||
for j, edge in enumerate(edges):
|
||||
edge.add_tag(f"wire_{i}_edge_{j}")
|
||||
edge.set_metadata("parent_wire", i)
|
||||
edge.set_metadata("position_in_wire", j)
|
||||
|
||||
print(f"线 {i}:")
|
||||
print(f" 总长度: {total_length:.3f}")
|
||||
print(f" 边数: {len(edges)}")
|
||||
print(f" 闭合: {is_closed}")
|
||||
print(f" 最短边: {min_edge_length:.3f}")
|
||||
print(f" 最长边: {max_edge_length:.3f}")
|
||||
print(f" 平均边长: {avg_edge_length:.3f}")
|
||||
print()
|
||||
|
||||
analyze_wire_properties()
|
||||
```
|
||||
|
||||
### Wire Transformation and Operations
|
||||
|
||||
```python
|
||||
from simplecadapi import make_rectangle_rwire, translate_shape, rotate_shape
|
||||
|
||||
def transform_wires():
|
||||
"""变换线的操作"""
|
||||
|
||||
# 创建基础矩形
|
||||
base_rect = make_rectangle_rwire(width=4, height=2)
|
||||
base_rect.add_tag("base")
|
||||
base_rect.add_tag("original")
|
||||
|
||||
# 创建变换后的线
|
||||
translated_rect = translate_shape(base_rect, offset=(5, 0, 0))
|
||||
translated_rect.add_tag("translated")
|
||||
|
||||
rotated_rect = rotate_shape(base_rect, axis=(0, 0, 1), angle=45)
|
||||
rotated_rect.add_tag("rotated")
|
||||
|
||||
# 收集所有线
|
||||
all_wires = [base_rect, translated_rect, rotated_rect]
|
||||
|
||||
# 分析变换结果
|
||||
for wire in all_wires:
|
||||
edges = wire.get_edges()
|
||||
total_length = sum(edge.get_length() for edge in edges)
|
||||
|
||||
# 计算边界框(简化版)
|
||||
all_coords = []
|
||||
for edge in edges:
|
||||
start_coords = edge.get_start_vertex().get_coordinates()
|
||||
end_coords = edge.get_end_vertex().get_coordinates()
|
||||
all_coords.extend([start_coords, end_coords])
|
||||
|
||||
if all_coords:
|
||||
min_x = min(coord[0] for coord in all_coords)
|
||||
max_x = max(coord[0] for coord in all_coords)
|
||||
min_y = min(coord[1] for coord in all_coords)
|
||||
max_y = max(coord[1] for coord in all_coords)
|
||||
|
||||
wire.set_metadata("bbox_min", (min_x, min_y))
|
||||
wire.set_metadata("bbox_max", (max_x, max_y))
|
||||
wire.set_metadata("bbox_width", max_x - min_x)
|
||||
wire.set_metadata("bbox_height", max_y - min_y)
|
||||
|
||||
wire.set_metadata("total_length", total_length)
|
||||
|
||||
print(f"线标签: {wire.get_tags()}")
|
||||
print(f" 总长度: {total_length:.3f}")
|
||||
if wire.get_metadata("bbox_min"):
|
||||
print(f" 边界框: {wire.get_metadata('bbox_min')} 到 {wire.get_metadata('bbox_max')}")
|
||||
print()
|
||||
|
||||
transform_wires()
|
||||
```
|
||||
|
||||
### Building Wire Sequences
|
||||
|
||||
```python
|
||||
from simplecadapi import make_segment_rwire
|
||||
|
||||
def create_wire_sequence():
|
||||
"""创建线的序列"""
|
||||
|
||||
# 创建连续的线段
|
||||
segments = []
|
||||
|
||||
# 定义路径点
|
||||
waypoints = [
|
||||
(0, 0, 0),
|
||||
(2, 0, 0),
|
||||
(2, 2, 0),
|
||||
(0, 2, 0),
|
||||
(0, 4, 0),
|
||||
(4, 4, 0),
|
||||
(4, 0, 0),
|
||||
(6, 0, 0)
|
||||
]
|
||||
|
||||
# 创建连续的线段
|
||||
for i in range(len(waypoints) - 1):
|
||||
start = waypoints[i]
|
||||
end = waypoints[i + 1]
|
||||
|
||||
segment = make_segment_rwire(start=start, end=end)
|
||||
segment.add_tag(f"segment_{i}")
|
||||
segment.add_tag("path_segment")
|
||||
|
||||
# 添加方向信息
|
||||
direction = (
|
||||
end[0] - start[0],
|
||||
end[1] - start[1],
|
||||
end[2] - start[2]
|
||||
)
|
||||
|
||||
if direction[0] > 0:
|
||||
segment.add_tag("eastward")
|
||||
elif direction[0] < 0:
|
||||
segment.add_tag("westward")
|
||||
|
||||
if direction[1] > 0:
|
||||
segment.add_tag("northward")
|
||||
elif direction[1] < 0:
|
||||
segment.add_tag("southward")
|
||||
|
||||
segment.set_metadata("start_point", start)
|
||||
segment.set_metadata("end_point", end)
|
||||
segment.set_metadata("direction", direction)
|
||||
segment.set_metadata("sequence_index", i)
|
||||
|
||||
segments.append(segment)
|
||||
|
||||
# 分析序列
|
||||
total_path_length = sum(seg.get_edges()[0].get_length() for seg in segments)
|
||||
|
||||
print(f"路径段数: {len(segments)}")
|
||||
print(f"总路径长度: {total_path_length:.3f}")
|
||||
|
||||
# 按方向分类
|
||||
eastward = [s for s in segments if s.has_tag("eastward")]
|
||||
northward = [s for s in segments if s.has_tag("northward")]
|
||||
|
||||
print(f"向东段数: {len(eastward)}")
|
||||
print(f"向北段数: {len(northward)}")
|
||||
|
||||
return segments
|
||||
|
||||
sequence = create_wire_sequence()
|
||||
```
|
||||
|
||||
## String Representation
|
||||
|
||||
```python
|
||||
from simplecadapi import make_rectangle_rwire
|
||||
|
||||
wire = make_rectangle_rwire(width=5, height=3)
|
||||
wire.add_tag("example_rectangle")
|
||||
wire.set_metadata("area", 15.0)
|
||||
|
||||
print(wire)
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
Wire:
|
||||
edge_count: 4
|
||||
closed: True
|
||||
edges:
|
||||
edge_0:
|
||||
length: 5.000
|
||||
vertices:
|
||||
start: (0.0, 0.0, 0.0)
|
||||
end: (5.0, 0.0, 0.0)
|
||||
edge_1:
|
||||
length: 3.000
|
||||
vertices:
|
||||
start: (5.0, 0.0, 0.0)
|
||||
end: (5.0, 3.0, 0.0)
|
||||
edge_2:
|
||||
length: 5.000
|
||||
vertices:
|
||||
start: (5.0, 3.0, 0.0)
|
||||
end: (0.0, 3.0, 0.0)
|
||||
edge_3:
|
||||
length: 3.000
|
||||
vertices:
|
||||
start: (0.0, 3.0, 0.0)
|
||||
end: (0.0, 0.0, 0.0)
|
||||
tags: [example_rectangle]
|
||||
metadata:
|
||||
area: 15.0
|
||||
```
|
||||
|
||||
## Relationships with Other Geometry
|
||||
|
||||
- **Edge (Edge)**: Components of a wire
|
||||
- **Face (Face)**: Closed wires can define face boundaries
|
||||
- **Solid (Solid)**: Can be created by sweeping or extruding wires
|
||||
|
||||
## Notes
|
||||
|
||||
- Wire edges must be continuous; endpoints of adjacent edges must coincide
|
||||
- The start and end points of a closed wire must coincide
|
||||
- Wire orientation affects certain operations (such as face normal direction)
|
||||
- Complex wires may contain self-intersections and require special handling
|
||||
- Wire length equals the sum of all edge lengths
|
||||
- When creating faces, outer boundary wires should be counterclockwise; inner boundary wires should be clockwise
|
||||
Reference in New Issue
Block a user