Skip to content

Commit 9285bc0

Browse files
authored
Merge pull request #6 from lottilotte/curve_functionalities
Curve functionalities
2 parents 5469838 + f0b2319 commit 9285bc0

File tree

8 files changed

+159
-3
lines changed

8 files changed

+159
-3
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
* Added `compas_occ.brep.BRepLoop`.
1616
* Added `compas_occ.brep.BRepVertex`.
1717

18+
* Added `compas_occ.geometry.NurbsCurve.segment`.
19+
* Added `compas_occ.geometry.NurbsCurve.segmented`.
20+
* Added `compas_occ.geometry.NurbsCurve.closest_point`.
21+
1822
### Changed
1923

2024
### Removed
696 KB
Loading
679 KB
Loading
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from compas.geometry import Point, Polyline
2+
from compas_occ.geometry import NurbsCurve
3+
4+
from compas_view2.app import App
5+
6+
points = [Point(0, 0, 0), Point(3, 0, 2), Point(6, 0, -3), Point(8, 0, 0)]
7+
curve = NurbsCurve.from_interpolation(points)
8+
9+
projection_point = Point(2, -1, 0)
10+
11+
closest_point, t = curve.closest_point(projection_point, parameter=True)
12+
13+
print(curve.point_at(t) == closest_point)
14+
15+
# ==============================================================================
16+
# Visualisation
17+
# ==============================================================================
18+
19+
view = App()
20+
21+
view.add(Polyline(curve.locus()), linewidth=3)
22+
view.add(projection_point, pointcolor = (0, 0, 1))
23+
view.add(closest_point, pointcolor = (1, 0, 0))
24+
25+
view.run()
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
********************************************************************************
2+
Curve Closest Point
3+
********************************************************************************
4+
5+
.. figure:: /_images/example_curve_closest_point.png
6+
:figclass: figure
7+
:class: figure-img img-fluid
8+
9+
.. literalinclude:: curve_closest_point.py
10+
:language: python
11+
12+
.. code-block:: text
13+
14+
True
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from compas.geometry import Point, Polyline
2+
from compas_occ.geometry import NurbsCurve
3+
4+
from compas_view2.app import App
5+
6+
7+
pointsA = [Point(0, 0, 0), Point(3, 6, 0), Point(6, -3, 3), Point(10, 0, 0)]
8+
curveA = NurbsCurve.from_points(pointsA)
9+
10+
curveA.segment(u=0.2, v=0.5)
11+
12+
print(curveA.domain)
13+
14+
15+
pointsB = [Point(0, -1, 0), Point(3, 5, 0), Point(6, -4, 3), Point(10, -1, 0)]
16+
curveB = NurbsCurve.from_points(pointsB)
17+
18+
segment = curveB.segmented(u=0.2, v=0.5)
19+
20+
print(curveB.domain)
21+
print(segment.domain)
22+
23+
# ==============================================================================
24+
# Visualisation
25+
# ==============================================================================
26+
27+
view = App()
28+
29+
view.add(Polyline(curveA.locus()), linewidth=4, linecolor=(1, 0, 0))
30+
31+
view.add(Polyline(curveB.locus()), linewidth=1, linecolor=(0, 0, 0))
32+
view.add(Polyline(segment.locus()), linewidth=4, linecolor=(0, 1, 0))
33+
34+
view.run()
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
********************************************************************************
2+
Curve Segmentation
3+
********************************************************************************
4+
5+
.. figure:: /_images/example_segmentation.png
6+
:figclass: figure
7+
:class: figure-img img-fluid
8+
9+
.. literalinclude:: curve_segmentation.py
10+
:language: python
11+
12+
.. code-block:: text
13+
14+
(0.2, 0.5)
15+
16+
(0.0, 1.0)
17+
(0.2, 0.5)

src/compas_occ/geometry/curves/nurbs.py

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from OCC.Core.gp import gp_Vec
2525
from OCC.Core.Geom import Geom_BSplineCurve
2626
from OCC.Core.GeomAPI import GeomAPI_Interpolate
27+
from OCC.Core.GeomAPI import GeomAPI_ProjectPointOnCurve
2728
from OCC.Core.GeomAdaptor import GeomAdaptor_Curve
2829
from OCC.Core.GCPnts import GCPnts_AbscissaPoint_Length
2930
from OCC.Core.Bnd import Bnd_Box
@@ -612,9 +613,27 @@ def frame_at(self, t):
612613
self.occ_curve.D2(t, point, uvec, vvec)
613614
return Frame(Point.from_occ(point), Vector.from_occ(uvec), Vector.from_occ(vvec))
614615

615-
def closest_point(self, point, distance=None):
616-
"""Compute the closest point on the curve to a given point."""
617-
pass
616+
def closest_point(self, point: Point, parameter: bool = False) -> Point:
617+
"""Compute the closest point on the curve to a given point.
618+
619+
Parameters
620+
----------
621+
point : Point
622+
The point to project orthogonally to the curve.
623+
parameter : bool, optional
624+
Return the projected point as well as the curve parameter.
625+
626+
Returns
627+
-------
628+
Point or tuple
629+
The nearest point on the curve, if ``parameter`` is false.
630+
The nearest as (point, parameter) tuple, if ``parameter`` is true.
631+
"""
632+
projector = GeomAPI_ProjectPointOnCurve(point.to_occ(), self.occ_curve)
633+
point = Point.from_occ(projector.NearestPoint())
634+
if not parameter:
635+
return point
636+
return point, projector.LowerDistanceParameter()
618637

619638
def divide_by_count(self, count):
620639
"""Divide the curve into a specific number of equal length segments."""
@@ -642,3 +661,46 @@ def obb(self, precision: float = 0.0) -> Box:
642661
def length(self, precision: float = 1e-3) -> float:
643662
"""Compute the length of the curve."""
644663
return GCPnts_AbscissaPoint_Length(GeomAdaptor_Curve(self.occ_curve))
664+
665+
def segment(self, u: float, v: float, precision: float = 1e-3) -> None:
666+
"""Modifies this curve by segmenting it between the parameters u and v.
667+
668+
Parameters
669+
----------
670+
u: float
671+
v: float
672+
tol: float, optional
673+
default value is 1e-3
674+
675+
Returns
676+
-------
677+
None
678+
679+
"""
680+
if u > v:
681+
u, v = v, u
682+
s, e = self.domain
683+
if u < s or v > e:
684+
raise ValueError('At least one of the given parameters is outside the curve domain.')
685+
if u == v:
686+
raise ValueError('The given domain is zero length.')
687+
self.occ_curve.Segment(u, v, precision)
688+
689+
def segmented(self, u: float, v: float, precision: float = 1e-3) -> 'NurbsCurve':
690+
"""Returns a copy of this curve by segmenting it between the parameters u and v.
691+
692+
Parameters
693+
----------
694+
u: float
695+
v: float
696+
tol: float,optional
697+
default value is 1e-3
698+
699+
Returns
700+
-------
701+
NurbsCurve
702+
703+
"""
704+
copy = self.copy()
705+
copy.occ_curve.Segment(u, v, precision)
706+
return copy

0 commit comments

Comments
 (0)