Skip to content

Commit

Permalink
Merge pull request #1338 from compas-dev/feature-some-geometry-methods
Browse files Browse the repository at this point in the history
Add some convenience geometry methods
  • Loading branch information
yck011522 authored Apr 27, 2024
2 parents f28cdb8 + 458aa25 commit c2516e9
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 3 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

* Added `compas.geometry.Line.point_from_start` and `compas.geometry.Line.point_from_end`.
* Added `compas.geometry.Line.flip` and `compas.geometry.Line.flipped`.
* Added an `compas.geometry.Frame.interpolate_frame(s)` method
* Added `compas.colors.Color.contrast`.
* Added `compas.geometry.Brep.from_plane`.
Expand Down
86 changes: 83 additions & 3 deletions src/compas/geometry/curves/line.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,17 +297,19 @@ def transform(self, T):
# ==========================================================================

def point_at(self, t):
"""Construct a point at a specific location along the line.
"""Construct a point along the line at a fractional position.
Parameters
----------
t : float
The location along the line.
The relative position along the line as a fraction of the length of the line.
0.0 corresponds to the start point and 1.0 corresponds to the end point.
Numbers outside of this range are also valid and correspond to points beyond the start and end point.
Returns
-------
:class:`compas.geometry.Point`
The point at the specified location.
The point at the specified position.
See Also
--------
Expand All @@ -323,6 +325,44 @@ def point_at(self, t):
point = self.point + self.vector * t
return point

def point_from_start(self, distance):
"""Construct a point along the line at a distance from the start point.
Parameters
----------
distance : float
The distance along the line from the start point towards the end point.
If the distance is negative, the point is constructed in the opposite direction of the end point.
If the distance is larger than the length of the line, the point is constructed beyond the end point.
Returns
-------
:class:`compas.geometry.Point`
The point at the specified distance.
"""
point = self.point + self.direction * distance
return point

def point_from_end(self, distance):
"""Construct a point along the line at a distance from the end point.
Parameters
----------
distance : float
The distance along the line from the end point towards the start point.
If the distance is negative, the point is constructed in the opposite direction of the start point.
If the distance is larger than the length of the line, the point is constructed beyond the start point.
Returns
-------
:class:`compas.geometry.Point`
The point at the specified distance.
"""
point = self.end - self.direction * distance
return point

def closest_point(self, point, return_parameter=False):
"""Compute the closest point on the line to a given point.
Expand All @@ -349,3 +389,43 @@ def closest_point(self, point, return_parameter=False):
if return_parameter:
return closest, t
return closest

def flip(self):
"""Flip the direction of the line.
Returns
-------
None
Examples
--------
>>> line = Line([0, 0, 0], [1, 2, 3])
>>> line
Line(Point(x=0.0, y=0.0, z=0.0), Point(x=1.0, y=2.0, z=3.0))
>>> line.flip()
>>> line
Line(Point(x=1.0, y=2.0, z=3.0), Point(x=0.0, y=0.0, z=0.0))
"""
new_vector = self.vector.inverted()
self.start = self.end
self.vector = new_vector

def flipped(self):
"""Return a new line with the direction flipped.
Returns
-------
:class:`Line`
A new line.
Examples
--------
>>> line = Line([0, 0, 0], [1, 2, 3])
>>> line
Line(Point(x=0.0, y=0.0, z=0.0), Point(x=1.0, y=2.0, z=3.0))
>>> line.flipped()
Line(Point(x=1.0, y=2.0, z=3.0), Point(x=0.0, y=0.0, z=0.0))
"""
return Line(self.end, self.start)
66 changes: 66 additions & 0 deletions tests/compas/geometry/test_curves_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from compas.geometry import Vector
from compas.geometry import Frame
from compas.geometry import Line
from compas.tolerance import TOL


@pytest.mark.parametrize(
Expand Down Expand Up @@ -216,3 +217,68 @@ def test_line_accessors(p1, p2):
# =============================================================================
# Other Methods
# =============================================================================


@pytest.mark.parametrize(
"p1,p2",
[
([0, 0, 0], [1, 0, 0]),
([0, 0, 0], [1, 2, 3]),
([1, 2, 3], [-1, -2, -3]),
([-11.1, 22.2, 33.3], [1.1, -2.2, -3.3]),
],
)
def test_line_point_from_start(p1, p2):
distances = [0, 1, 4, -9, 3.3, 0.00001, -0.00001]
for distance in distances:
line = Line(p1, p2)
point = line.point_from_start(distance)
distance_to_start = distance_point_point(point, p1)
distance_to_end = distance_point_point(point, p2)
# Check that the distance is correct
assert TOL.is_close(distance_to_start, abs(distance))
# Check that negative distance gives a point far away from end
if distance < 0:
assert distance_to_end > line.length


@pytest.mark.parametrize(
"p1,p2",
[
([0, 0, 0], [1, 0, 0]),
([0, 0, 0], [1, 2, 3]),
([1, 2, 3], [-1, -2, -3]),
([-11.1, 22.2, 33.3], [1.1, -2.2, -3.3]),
],
)
def test_line_point_from_end(p1, p2):
distances = [0, 1, 4, -9, 3.3, 0.00001, -0.00001]
for distance in distances:
line = Line(p1, p2)
point = line.point_from_end(distance)
distance_to_start = distance_point_point(point, p1)
distance_to_end = distance_point_point(point, p2)
# Check that the distance is correct
assert TOL.is_close(distance_to_end, abs(distance))
# Check that negative distance gives a point far away from start
if distance < 0:
assert distance_to_start > line.length


@pytest.mark.parametrize(
"p1,p2",
[
([0, 0, 0], [1, 0, 0]),
([0, 0, 0], [1, 2, 3]),
([1, 2, 3], [-1, -2, -3]),
([-11.1, 22.2, 33.3], [1.1, -2.2, -3.3]),
],
)
def test_line_flip(p1, p2):
line = Line(p1, p2)
line.flip()
assert TOL.is_zero(distance_point_point(line.start, p2))
assert TOL.is_zero(distance_point_point(line.end, p1))
flipped_line = Line(p1, p2).flipped()
assert TOL.is_zero(distance_point_point(flipped_line.start, p2))
assert TOL.is_zero(distance_point_point(flipped_line.end, p1))

0 comments on commit c2516e9

Please sign in to comment.