Skip to content

feat: nurbs sketching and surface support #2104

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
a4e285f
nurbs sketch wip
jacobrkerstetter Jul 2, 2025
79cd02f
sketch nurbs working with visualization
jacobrkerstetter Jul 3, 2025
f11312f
doc edits
jacobrkerstetter Jul 3, 2025
9636f22
added degree check for fitting from < 4 points
jacobrkerstetter Jul 3, 2025
8f8cc96
Merge branch 'main' of https://github.com/ansys/pyansys-geometry into…
jacobrkerstetter Jul 7, 2025
38b92b0
adding contains_point for nurbs edge
jacobrkerstetter Jul 8, 2025
a1ffa39
added test to create a surface from a combination of nurbs and segmen…
jacobrkerstetter Jul 9, 2025
e591136
add fill command
jacobrkerstetter Jul 10, 2025
8a3bab5
Merge branch 'main' of https://github.com/ansys/pyansys-geometry into…
jacobrkerstetter Jul 11, 2025
2ee3921
added test for creating surface from nurbs sketch + overhanging edges
jacobrkerstetter Jul 11, 2025
6d25316
chore: auto fixes from pre-commit hooks
pre-commit-ci[bot] Jul 11, 2025
2bca7ab
chore: adding changelog file 2104.added.md [dependabot-skip]
pyansys-ci-bot Jul 11, 2025
a7dc888
code cleanup
jacobrkerstetter Jul 11, 2025
6e600ea
Merge branch 'feat/nurbs_sketching' of https://github.com/ansys/pyans…
jacobrkerstetter Jul 11, 2025
9601df4
chore: auto fixes from pre-commit hooks
pre-commit-ci[bot] Jul 11, 2025
1b5f78e
more cleanup
jacobrkerstetter Jul 11, 2025
d00f558
Merge branch 'feat/nurbs_sketching' of https://github.com/ansys/pyans…
jacobrkerstetter Jul 11, 2025
c931225
Merge branch 'main' into feat/nurbs_sketching
jacobrkerstetter Jul 15, 2025
a8f91e6
bump protos version
jacobrkerstetter Jul 15, 2025
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 doc/changelog.d/2104.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Nurbs sketching and surface support
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ classifiers = [
]

dependencies = [
"ansys-api-geometry==0.4.65",
"ansys-api-geometry==0.4.66",
"ansys-tools-path>=0.3,<1",
"beartype>=0.11.0,<0.22",
"geomdl>=5,<6",
Expand Down
58 changes: 54 additions & 4 deletions src/ansys/geometry/core/_grpc/_services/v0/conversions.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
from ansys.geometry.core.sketch.edge import SketchEdge
from ansys.geometry.core.sketch.ellipse import SketchEllipse
from ansys.geometry.core.sketch.face import SketchFace
from ansys.geometry.core.sketch.nurbs import SketchNurbs
from ansys.geometry.core.sketch.polygon import Polygon
from ansys.geometry.core.sketch.segment import SketchSegment

Expand Down Expand Up @@ -404,6 +405,7 @@ def from_sketch_shapes_to_grpc_geometries(
converted_sketch_edges = from_sketch_edges_to_grpc_geometries(edges, plane)
geometries.lines.extend(converted_sketch_edges[0])
geometries.arcs.extend(converted_sketch_edges[1])
geometries.nurbs_curves.extend(converted_sketch_edges[2])

for face in faces:
if isinstance(face, SketchCircle):
Expand All @@ -429,6 +431,8 @@ def from_sketch_shapes_to_grpc_geometries(
one_curve_geometry.ellipses.append(geometries.ellipses[0])
elif len(geometries.polygons) > 0:
one_curve_geometry.polygons.append(geometries.polygons[0])
elif len(geometries.nurbs_curves) > 0:
one_curve_geometry.nurbs_curves.append(geometries.nurbs_curves[0])
return one_curve_geometry

else:
Expand All @@ -438,7 +442,7 @@ def from_sketch_shapes_to_grpc_geometries(
def from_sketch_edges_to_grpc_geometries(
edges: list["SketchEdge"],
plane: "Plane",
) -> tuple[list[GRPCLine], list[GRPCArc]]:
) -> tuple[list[GRPCLine], list[GRPCArc], list[GRPCNurbsCurve]]:
"""Convert a list of ``SketchEdge`` to a gRPC message.
Parameters
Expand All @@ -450,21 +454,25 @@ def from_sketch_edges_to_grpc_geometries(
Returns
-------
tuple[list[GRPCLine], list[GRPCArc]]
Geometry service gRPC line and arc messages. The unit is meters.
tuple[list[GRPCLine], list[GRPCArc], list[GRPCNurbsCurve]]
Geometry service gRPC line, arc, and NURBS curve messages. The unit is meters.
"""
from ansys.geometry.core.sketch.arc import Arc
from ansys.geometry.core.sketch.nurbs import SketchNurbs
from ansys.geometry.core.sketch.segment import SketchSegment

arcs = []
segments = []
nurbs_curves = []
for edge in edges:
if isinstance(edge, SketchSegment):
segments.append(from_sketch_segment_to_grpc_line(edge, plane))
elif isinstance(edge, Arc):
arcs.append(from_sketch_arc_to_grpc_arc(edge, plane))
elif isinstance(edge, SketchNurbs):
nurbs_curves.append(from_sketch_nurbs_to_grpc_nurbs_curve(edge, plane))

return (segments, arcs)
return (segments, arcs, nurbs_curves)


def from_sketch_arc_to_grpc_arc(arc: "Arc", plane: "Plane") -> GRPCArc:
Expand Down Expand Up @@ -496,6 +504,48 @@ def from_sketch_arc_to_grpc_arc(arc: "Arc", plane: "Plane") -> GRPCArc:
)


def from_sketch_nurbs_to_grpc_nurbs_curve(curve: "SketchNurbs", plane: "Plane") -> GRPCNurbsCurve:
"""Convert a ``SketchNurbs`` class to a NURBS curve gRPC message.
Parameters
----------
nurbs : SketchNurbs
Source NURBS data.
plane : Plane
Plane for positioning the NURBS curve.
Returns
-------
GRPCNurbsCurve
Geometry service gRPC NURBS curve message. The unit is meters.
"""
from ansys.api.geometry.v0.models_pb2 import (
ControlPoint as GRPCControlPoint,
NurbsData as GRPCNurbsData,
)

# Convert control points
control_points = [
GRPCControlPoint(
position=from_point2d_to_grpc_point(plane, pt),
weight=curve.weights[i],
)
for i, pt in enumerate(curve.control_points)
]

# Convert nurbs data
nurbs_data = GRPCNurbsData(
degree=curve.degree,
knots=from_knots_to_grpc_knots(curve.knots),
order=curve.degree + 1,
)

return GRPCNurbsCurve(
control_points=control_points,
nurbs_data=nurbs_data,
)


def from_sketch_ellipse_to_grpc_ellipse(ellipse: "SketchEllipse", plane: "Plane") -> GRPCEllipse:
"""Convert a ``SketchEllipse`` class to an ellipse gRPC message.
Expand Down
11 changes: 9 additions & 2 deletions src/ansys/geometry/core/connection/conversions.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ def sketch_shapes_to_grpc_geometries(
GRPCGeometries
Geometry service gRPC geometries message. The unit is meters.
"""
from ansys.geometry.core.sketch.circle import SketchCircle
from ansys.geometry.core.sketch.ellipse import SketchEllipse
from ansys.geometry.core.sketch.polygon import Polygon

geometries = GRPCGeometries()

converted_sketch_edges = sketch_edges_to_grpc_geometries(edges, plane)
Expand Down Expand Up @@ -197,8 +201,8 @@ def sketch_shapes_to_grpc_geometries(


def sketch_edges_to_grpc_geometries(
edges: list[SketchEdge],
plane: Plane,
edges: list["SketchEdge"],
plane: "Plane",
) -> tuple[list[GRPCLine], list[GRPCArc]]:
"""Convert a list of ``SketchEdge`` to a gRPC message.
Expand All @@ -214,6 +218,9 @@ def sketch_edges_to_grpc_geometries(
tuple[list[GRPCLine], list[GRPCArc]]
Geometry service gRPC line and arc messages. The unit is meters.
"""
from ansys.geometry.core.sketch.arc import Arc
from ansys.geometry.core.sketch.segment import SketchSegment

arcs = []
segments = []
for edge in edges:
Expand Down
1 change: 1 addition & 0 deletions src/ansys/geometry/core/designer/geometry_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
point3d_to_grpc_point,
unit_vector_to_grpc_direction,
)
from ansys.geometry.core.designer.body import Body
from ansys.geometry.core.designer.component import Component
from ansys.geometry.core.designer.mating_conditions import (
AlignCondition,
Expand Down
5 changes: 1 addition & 4 deletions src/ansys/geometry/core/shapes/curves/nurbs.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,7 @@ def fit_curve_from_points(
from geomdl import fitting

# Convert points to a format suitable for the fitting function
converted_points = []
for pt in points:
pt_raw = [*pt]
converted_points.append(pt_raw)
converted_points = [[*pt] for pt in points]

# Fit the curve to the points
curve = fitting.interpolate_curve(converted_points, degree)
Expand Down
6 changes: 5 additions & 1 deletion src/ansys/geometry/core/sketch/edge.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ def length(self) -> Quantity:
"""Length of the edge."""
raise NotImplementedError("Each edge must provide the length definition.")

def contains_point(self, point: Point2D, tol: float = 1e-6) -> bool:
"""Check if the edge contains the given point within a tolerance."""
raise NotImplementedError("Each edge must provide the contains_point method.")

@property
def visualization_polydata(self) -> "pv.PolyData":
"""VTK polydata representation for PyVista visualization.
Expand All @@ -76,7 +80,7 @@ def plane_change(self, plane: "Plane") -> None:
Notes
-----
This implies that their 3D definition might suffer changes. By default, this
metho does nothing. It is required to be implemented in child ``SketchEdge``
method does nothing. It is required to be implemented in child ``SketchEdge``
classes.
"""
pass
Loading
Loading