Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ tools/research/out/
tools/research/histjson/out/
tools/research/whucad/out/
tools/research/cadqueryftc/out/
tools/research/build123dftc/out/
.venv-cq/

# Source screen recordings stay local; compressed MP4/GIF derivatives are tracked
Expand Down
39 changes: 39 additions & 0 deletions docs/skill/references/docs/api/draft_rsolid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# draft_rsolid

```python
def draft_rsolid(
solid: Solid,
faces_to_draft: Sequence[Face] | ShapeSelector,
angle: ScalarLike,
plane_origin: Sequence[float] = (0, 0, 0),
plane_normal: Sequence[float] = (0, 0, 1),
) -> Solid
```

*Source: operators/draft.py*

Import with `from simplecadapi import draft_rsolid`.

Apply a signed draft angle in degrees to selected planar, cylindrical, or
conical faces. The neutral plane retains its original section while the selected
faces lean toward or away from it. Its origin and normal use the current
coordinate system. The input solid is preserved, and the output must be one
valid solid. A zero angle records an unchanged result.

```python
import simplecadapi as scad
from simplecadapi import ql

body = scad.make_box_rsolid(10, 10, 10)
right = ql.faces().where(ql.prop("geom.normal.x", ">", 0.9)).exactly(1)
drafted = scad.draft_rsolid(body, right, angle=10)
```

Selections may be explicit faces from the body or a QL selector. Empty
selections, faces from another body, unsupported surface families, a zero plane
normal, and nonfinite parameters are rejected. The angle must be strictly
between -90 and 90 degrees. Kernel failures and invalid topology raise errors.

The operation `make_draft_rsolid` records the body and selected faces as ordered
inputs. Parameters preserve the angle, neutral plane, and face selection nodes
for strict model JSON replay.
13 changes: 13 additions & 0 deletions docs/skill/references/docs/api/extrude_taper_rsolid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# extrude_taper_rsolid

```python
def extrude_taper_rsolid(profile: Face, vector: Sequence[float],
taper: float, flip_inner: bool = True) -> Solid
```

Extrude a planar sketch face by a nonzero vector, in current coordinate-system
coordinates. The finite taper angle is in degrees and must lie strictly between
-90 and 90. Positive taper shrinks the outer section; inner loops taper in the
opposite direction when `flip_inner=True`. Uses an analytic draft prism where
applicable and exact offset sections with lofts otherwise. Raises on collapsed
loops or invalid topology. Records `make_extrude_taper_rsolid` for replay.
53 changes: 53 additions & 0 deletions docs/skill/references/docs/api/extrude_until_rsolid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# extrude_until_rsolid

```python
def extrude_until_rsolid(
profile: Face,
target: Solid | Sequence[Solid],
direction: Sequence[float] = (0, 0, 1),
until: str = "NEXT",
) -> Solid
```

*Source: operators/limited_extrusion.py*

Import with `from simplecadapi import extrude_until_rsolid`.

Extrude a planar face until it reaches an actual boundary on one or more target
solids. Planar and curved termination surfaces are supported. The direction is
interpreted in the current coordinate system and normalized before construction.

| Mode | Direction | Termination |
| --- | --- | --- |
| `NEXT` | Forward | Nearest intersecting target surface |
| `LAST` | Forward | Farthest intersecting target surface |
| `PREVIOUS` | Reverse | Nearest intersecting target surface |
| `FIRST` | Reverse | Farthest intersecting target surface |

The implementation intersects a temporary prism with the target, obtains target
boundary faces from kernel history, sews the contact faces into surfaces, and
splits the prism on the selected surface. Bounding boxes only determine a
sufficient temporary prism length; they do not define the termination surface.
Side walls parallel to the direction do not join separate near and far limits.

```python
import simplecadapi as scad

profile = scad.make_rectangle_rface(2, 2)
ceiling = scad.make_box_rsolid(10, 10, 2, bottom_face_center=(0, 0, 5))
near = scad.extrude_until_rsolid(profile, ceiling, until="NEXT") # Volume 20
far = scad.extrude_until_rsolid(profile, ceiling, until="LAST") # Volume 28

sphere = scad.make_sphere_rsolid(5, center=(0, 0, 10))
curved = scad.extrude_until_rsolid(profile, sphere)
```

Targets must cover the profile at the requested contact. Missing contact,
insufficient coverage, an invalid mode, a zero/nonfinite direction, and invalid
kernel output raise errors. An empty target sequence is rejected. A successful
result is one valid solid; inputs are preserved.

The recorded operation is `make_extrude_until_rsolid`. Its ordered inputs are the
profile followed by every target solid. Parameters retain `direction`, `until`,
and `target_count`, so model JSON replay recomputes the boundary when a target
changes. No final BRep is embedded in the feature.
15 changes: 15 additions & 0 deletions docs/skill/references/docs/api/offset_rsolid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# offset_rsolid

```python
def offset_rsolid(solid: Solid, amount: ScalarLike,
openings: Sequence[Face] | ShapeSelector = (), *,
tolerance: float = 1e-4, kind: str = "ARC") -> Solid
```

Positive `amount` offsets outward; negative offsets inward. The amount must
be finite and nonzero. QL-selected `openings` form an open shell. With no
openings, produce a closed offset solid. Join `kind` is `ARC` or
`INTERSECTION`. Invalid kernel results raise an error. Records a replayable
`make_offset_rsolid` node with explicit face selections; topology tag lineage
is conservatively partial. `shell_rsolid` retains its existing positive
inward wall-thickness contract.
11 changes: 11 additions & 0 deletions docs/skill/references/docs/api/scale_rsolid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# scale_rsolid

```python
def scale_rsolid(solid: Solid, factors: Sequence[float], center: Sequence[float] = (0, 0, 0)) -> Solid
```

Scale a solid along the current coordinate system's axes about `center`.
The three factors must be finite and nonzero. Uniform scaling preserves
analytic curves and surfaces; nonuniform scaling uses the CAD kernel's
general affine transform. The source stays unchanged. Records a replayable
`make_scale_rsolid` operation. Topology tag lineage is conservatively partial.
12 changes: 12 additions & 0 deletions docs/skill/references/docs/api/sweep_multisection_rsolid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# sweep_multisection_rsolid

```python
def sweep_multisection_rsolid(profiles: Sequence[Face | Wire], path: Wire,
is_frenet: bool = False, binormal: Sequence[float] | Wire | None = None) -> Solid
```

Sweep through at least two ordered sections along a continuous path. Face
sections supply their outer wire, matching build123d's multi-section sweep
contract. Binormal is an optional direction or auxiliary spine. The kernel
must produce one valid solid. Every section and path is recorded as an input
of `make_sweep_multisection_rsolid`, so model JSON preserves the construction.
10 changes: 10 additions & 0 deletions docs/skill/references/docs/api/transform_rsolid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# transform_rsolid

```python
def transform_rsolid(solid: Solid, matrix: Sequence[Sequence[float]]) -> Solid
```

Apply a finite nonsingular 4x4 affine matrix in the current coordinate system,
including shear, reflection and nonuniform scale. Reject perspective and
singular matrices. Input geometry stays unchanged. Records a replayable
`make_transform_rsolid` operation; topology tag lineage is partial.
12 changes: 12 additions & 0 deletions src/simplecadapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@
get_current_cs,
)
from .errors import SimpleCADError
from .operators.deformation import scale_rsolid, offset_rsolid, transform_rsolid, boolean_rsolidlist
from .operators.section_features import extrude_taper_rsolid, sweep_multisection_rsolid
from .operators.limited_extrusion import extrude_until_rsolid
from .operators.draft import draft_rsolid
from .params.expr import (
Const,
DimensionTolerance,
Expand Down Expand Up @@ -433,6 +437,14 @@ def notebook_id() -> str:


__all__ = [
"scale_rsolid",
"boolean_rsolidlist",
"transform_rsolid",
"offset_rsolid",
"extrude_taper_rsolid",
"sweep_multisection_rsolid",
"extrude_until_rsolid",
"draft_rsolid",
"exporter",
# 核心类
"CoordinateSystem",
Expand Down
6 changes: 4 additions & 2 deletions src/simplecadapi/kernel/ocp_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@ def surface_area(face: TopoDS_Face) -> float:

def volume(solid: TopoDS_Solid) -> float:
props = GProp_GProps()
BRepGProp.VolumeProperties_s(solid, props)
# Integrate NURBS by knot spans. The fixed-order routine both loses
# precision on rational curves and can crash on affine transformed trims.
BRepGProp.VolumePropertiesGK_s(solid, props, 1e-9, True, True)
return float(props.Mass())


Expand Down Expand Up @@ -146,7 +148,7 @@ def center_of_mass(shape: TopoDS_Shape) -> Vec3:
if kind in (TopAbs_SOLID, TopAbs_COMPSOLID, TopAbs_COMPOUND):
props = GProp_GProps()
try:
BRepGProp.VolumeProperties_s(shape, props)
BRepGProp.VolumePropertiesGK_s(shape, props, 1e-9, True, True)
point = _props_mass_point(props)
if point is not None:
return point
Expand Down
10 changes: 9 additions & 1 deletion src/simplecadapi/operators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import sys as _sys
from types import ModuleType as _ModuleType

from . import _support, geometry, transform, boolean, product, sketch, selection, features
from . import _support, geometry, transform, boolean, product, sketch, selection, features, deformation, section_features, limited_extrusion, draft
from ._support import *
from .geometry import *
from .transform import *
Expand All @@ -25,6 +25,10 @@
from .sketch import *
from .selection import *
from .features import *
from .deformation import *
from .section_features import *
from .limited_extrusion import *
from .draft import *

_IMPLEMENTATION_MODULES = (
_support,
Expand All @@ -35,6 +39,10 @@
sketch,
selection,
features,
deformation,
section_features,
limited_extrusion,
draft,
)

__all__ = tuple(
Expand Down
1 change: 1 addition & 0 deletions src/simplecadapi/operators/_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -1245,6 +1245,7 @@ def _record_geo_selection_nodes(
op=op,
params={
"target_kind": kind,
"source_output_slot": int(source_shape._get_runtime("graph.output_slot", 0)),
"geo_selector": _make_geo_selector(
selected,
source_shape=source_shape,
Expand Down
Loading
Loading