-
Notifications
You must be signed in to change notification settings - Fork 113
new functions for compas geometry #569
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
base: main
Are you sure you want to change the base?
Changes from 1 commit
5f9b52b
6408ef7
0e34204
401a7e2
2df9dcf
77549aa
829696c
ad06707
f29a933
4eafb73
faf7bc8
e9209c8
8b75011
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| from __future__ import print_function | ||
| from __future__ import absolute_import | ||
| from __future__ import division | ||
|
|
||
| from compas.datastructures import Mesh | ||
| from compas.geometry import Point | ||
| from compas.geometry import length_vector | ||
| from compas.geometry import subtract_vectors | ||
| from compas.geometry import intersection_line_triangle | ||
| from compas.geometry import intersection_segment_plane | ||
|
|
||
| __all__ = [ | ||
| 'intersection_mesh_line', | ||
| 'intersection_mesh_plane', | ||
| 'mesh_vertices_to_points', | ||
| ] | ||
|
|
||
|
|
||
| def intersection_mesh_line(mesh, line): | ||
| """Compute intersection between mesh faces and line | ||
| First extracts faces from the mesh and computes the intersection between | ||
| a triangular face and a line, or two triangles of a quad face and a line. | ||
| After one single intersection, stops searching for more. | ||
| Returns one point from line-mesh intersection if intersection occurs. | ||
| Parameters | ||
| ---------- | ||
| mesh : compas.datastructures.Mesh | ||
| line : compas.geometry.Line | ||
| Returns | ||
| ------- | ||
| Point : compas.geometry.Point | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| """ | ||
| for fkey in list(mesh.faces()): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I understand it |
||
| vertex_keys = mesh.face_vertices(fkey) | ||
| if not vertex_keys: | ||
| continue | ||
| vertices = [mesh.vertex_attributes(vkey, 'xyz') for vkey in vertex_keys] | ||
| if len(vertex_keys) == 3 or len(vertex_keys) == 4: | ||
| triangle = [vertices[0], vertices[1], vertices[2]] | ||
| intersection = intersection_line_triangle(line, triangle) | ||
| if intersection: | ||
| return Point(intersection[0], intersection[1], intersection[2]) | ||
| if len(vertex_keys) == 4: | ||
| triangle_2 = [vertices[2], vertices[3], vertices[0]] | ||
| intersection_2 = intersection_line_triangle(line, triangle_2) | ||
| if intersection_2: | ||
| return Point(intersection_2[0], intersection_2[1], intersection_2[2]) | ||
| else: | ||
| continue | ||
nikeftekhar marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| else: | ||
|
||
| return None | ||
|
|
||
| def intersection_mesh_plane(mesh, plane, tol=0.0001): | ||
| """Calculate the keys of the points of the intersection of a mesh with a plane | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a full stop at the end of the sentence.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function not only returns the intersection points, but modifies the mesh structure, splitting any edge that cross the plane. I think that should be noted in the docstring. |
||
| Parameters | ||
| ---------- | ||
| mesh : compas.datastructures.Mesh | ||
| plane : compas.geometry.Plane | ||
| Returns | ||
| ------- | ||
| intersections: list of points as keys from mesh | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| """ | ||
|
|
||
| intersections = [] | ||
| for u, v in list(mesh.edges()): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, for efficiency |
||
| a = mesh.vertex_attributes(u,'xyz') | ||
| b = mesh.vertex_attributes(v,'xyz') | ||
| inters = intersection_segment_plane((a,b), plane) | ||
nikeftekhar marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if not inters: | ||
| continue | ||
| len_a_inters = length_vector(subtract_vectors(inters, a)) | ||
| len_a_b = length_vector(subtract_vectors(b, a)) | ||
| t = len_a_inters / len_a_b | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you sure that |
||
| if t>= 1.0: | ||
nikeftekhar marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| t = 1 - tol | ||
| elif t <= 0.0: | ||
| t = tol | ||
| intersection_key = mesh.split_edge(u, v, t=t, allow_boundary=True) | ||
| intersections.append(intersection_key) | ||
|
|
||
| return intersections | ||
|
|
||
|
|
||
| def mesh_vertices_to_points(mesh, v_keys): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function doesn't have much functionality, and I don't see much point of adding it to the library. |
||
| """Compute compas points from vertex keys from specific mesh | ||
| Returns list of compas points from a list of indexes of the vertexes of a mesh | ||
| Parameters | ||
| ---------- | ||
| mesh : compas.datastructures.Mesh | ||
| v_keys : list of vertex indexes of a mesh | ||
| Returns | ||
| ------- | ||
| list of compas.geometry.Point | ||
| """ | ||
| coordinates = [mesh.vertex_attributes(v_key, 'xyz') for v_key in v_keys] | ||
| return [Point(x, y, z) for x, y, z in coordinates] | ||
nikeftekhar marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,7 +5,6 @@ | |
| from compas.geometry._primitives import Primitive | ||
| from compas.geometry._primitives import Point | ||
|
|
||
|
|
||
| __all__ = ['Line'] | ||
|
|
||
|
|
||
|
|
@@ -56,6 +55,8 @@ class Line(Primitive): | |
| True | ||
| """ | ||
|
|
||
| __module__ = "compas.geometry" | ||
nikeftekhar marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| __slots__ = ['_start', '_end'] | ||
|
|
||
| def __init__(self, p1, p2): | ||
|
|
@@ -337,7 +338,32 @@ def transformed(self, T): | |
| line.transform(T) | ||
| return line | ||
|
|
||
| def divide_by_count(self, number=10, include_ends=False): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I find it a little confusing that the name of the function is |
||
| """Return list of points from dividing the line by specific number of divisions | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps |
||
|
|
||
| Parameters | ||
| ---------- | ||
| number : integer | ||
| number of divisions | ||
| includeEnds : boolean | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be |
||
| True if including start and end point in division points | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Double ` around True and False would make the documentation look a little more consistent, that is: |
||
| False if not including start and end point in division points | ||
|
|
||
| Returns | ||
| ------- | ||
| list of: compas.geometry.Point // Point as sequence of values xyz) | ||
nikeftekhar marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Example | ||
| -------- | ||
| >>> line = Line([0.0,0.0,0.0],[5.0,0.0,0.0]) | ||
| >>> line.divide_by_count(5, True) | ||
| [[0.0,0.0,0.0],[1.0,0.0,0.0],[2.0,0.0,0.0],[3.0,0.0,0.0],[4.0,0.0,0.0],[5.0,0.0,0.0]] | ||
nikeftekhar marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| """ | ||
| if include_ends: | ||
| return [self.point(i * float(1 / number)) for i in range(int(number)+1)] | ||
| else: | ||
| return [self.point(i * float(1 / number)) for i in range(int(number)+1) if i != 0 or i != number] | ||
nikeftekhar marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| # ============================================================================== | ||
| # Main | ||
| # ============================================================================== | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -54,6 +54,8 @@ class Polyline(Primitive): | |
| 1.0 | ||
| """ | ||
|
|
||
| __module__ = "compas.geometry" | ||
nikeftekhar marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| __slots__ = ["_points", "_lines"] | ||
|
|
||
| def __init__(self, points): | ||
|
|
@@ -318,7 +320,107 @@ def transformed(self, T): | |
| polyline = self.copy() | ||
| polyline.transform(T) | ||
| return polyline | ||
|
|
||
| def shorten(self, start_distance=0, end_distance=0): | ||
| """Return a new polyline which is shorter than the original in one end side, other or both by a given distance. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about something like "Return a new polyline truncated by the given distances on either side."? |
||
|
|
||
| Parameters | ||
| ---------- | ||
| start_distance : float. | ||
| distance to shorten from the starting point of the polyline | ||
|
Comment on lines
+327
to
+328
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and similarly below |
||
| end_distance : float. | ||
| distance to shorten from the ending point of the polyline | ||
|
|
||
| Returns | ||
| ------- | ||
| :class:`compas.geometry.Polyline` | ||
| The transformed copy. | ||
| """ | ||
| if start_distance != 0 or end_distance != 0: | ||
| points = [] | ||
| acum_length = 0 | ||
| switch = True | ||
| for i, line in enumerate(self.lines): | ||
| acum_length += line.length | ||
| if acum_length < start_distance: | ||
| continue | ||
| elif acum_length > start_distance and switch: | ||
| if start_distance == 0: | ||
| points.append(line.start) | ||
| else: | ||
| points.append(self.point(start_distance/self.length)) | ||
| switch = False | ||
| else: | ||
| points.append(line.start) | ||
| if end_distance == 0: | ||
| if i == len(self.lines)-1: | ||
| points.append(line.end) | ||
| else: | ||
| if acum_length >= (self.length - end_distance): | ||
| points.append(self.point(1-(end_distance/self.length))) | ||
| break | ||
|
Comment on lines
+338
to
+359
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a little worried about edge cases. Let's say you take |
||
| return points | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a list, not a polyline as indicated in the docstring. So either the docstring should be changed or something along the lines of (maybe there's a better way): |
||
| return self | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not a copy as might be indicated by the docstring. So either, the docstring should be changed or |
||
|
|
||
| def rebuild(self, number=20): | ||
| """Reconstruct a polyline with evenly spaced points based on a number of interpolations | ||
| Returns new rebuilt polyline | ||
|
|
||
| Parameters | ||
| ---------- | ||
| number : integer. | ||
| number of points for the amount of definition of the polyline | ||
|
|
||
| Returns | ||
| ------- | ||
| list of equally spaced points on the polyline | ||
| """ | ||
| points = [self.point(i * float(1 / number)) for i in range(number)] | ||
| points.append(self.point(1)) | ||
| new_points = [Point(x, y, z) for x, y, z in points] | ||
| rebuilt_polyline = self.copy() | ||
| rebuilt_polyline.points = new_points | ||
| return rebuilt_polyline | ||
|
|
||
| def divide_by_count(self, number=10, include_ends=False): | ||
| """Divide a polyline by count. Returns list of Points from the division | ||
|
Comment on lines
+383
to
+384
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, I find the count/number discrepancy confusing. Whatever you choose to do with the |
||
|
|
||
| Parameters | ||
| ---------- | ||
| number : integer. | ||
| number of divisions | ||
|
Comment on lines
+388
to
+389
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| includeEnds : boolean | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| True if including start and ending points. | ||
| False if not including start and ending points. | ||
|
|
||
| Returns | ||
| ------- | ||
| points : list of points resulting from dividing the polyline | ||
| """ | ||
| points = [self.point(i * float(1 / number)) for i in range(number)] | ||
nikeftekhar marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if include_ends: | ||
| points.append(self.point(1)) | ||
| else: | ||
| points.pop(0) | ||
| return points | ||
|
|
||
| def tween(self, polyline_two, number=50): | ||
| """Create an average polyline between two polylines interpolating their points | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mean to create a polyline or a list of points? |
||
|
|
||
| Parameters | ||
| ---------- | ||
| polyline_two : compas.geometry.Polyline | ||
| polyline to create the tween polyline | ||
| number : number of points of the tween polyline | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| Returns | ||
| ------- | ||
| list of compas.geometry.Point | ||
| """ | ||
| rebuilt_polyline_one = self.rebuild(number) | ||
| rebuilt_polyline_two = polyline_two.rebuild(number) | ||
| lines = [Line(point_one, point_two) for point_one, point_two in zip(rebuilt_polyline_one, rebuilt_polyline_two)] | ||
| return [line.midpoint for line in lines] | ||
|
|
||
| # ============================================================================== | ||
| # Main | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| from __future__ import print_function | ||
| from __future__ import absolute_import | ||
| from __future__ import division | ||
|
|
||
| from compas.geometry import Translation | ||
| from compas.geometry import Line | ||
| from compas.geometry import Polyline | ||
| from compas.geometry import Vector | ||
| from compas.geometry import Point | ||
|
|
||
| __all__ = [ | ||
| 'extend_line', | ||
| 'extend_polyline', | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have two comments about these two functions. First, |
||
| ] | ||
|
|
||
|
|
||
| def extend_line(line, start_extension=0, end_extension=0): | ||
| """Extend the given line from one end or the other, or both, depending on the given values | ||
| Parameters | ||
| ---------- | ||
| line : tuple | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tuple or Line or both? |
||
| Two points defining the line. | ||
| start_extension : float | ||
| The extension distance at the start of the line as float. | ||
| end_extension : float | ||
| The extension distance at the end of the line as float. | ||
| Returns | ||
| ------- | ||
| extended line : tuple | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tuple or Line? |
||
| Two points defining the offset line. | ||
| Examples | ||
| -------- | ||
| >>> line = Line([0.0,0.0,0.0],[1.0,0.0,0.0]) | ||
| >>> extended_line = extend_line(line, 1, 1) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be simply |
||
| Line([-1.0,0.0,0.0],[2.0,0.0,0.0]) | ||
nikeftekhar marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| """ | ||
| def calculate_translation(line, distance): | ||
| vector = line.direction.copy() | ||
| vector.scale(distance) | ||
| return Translation(vector) | ||
|
|
||
| if start_extension != 0: | ||
| translation = calculate_translation(line, -start_extension) | ||
| line.start.transform(translation) | ||
| if end_extension != 0: | ||
| translation = calculate_translation(line, end_extension) | ||
| line.end.transform(translation) | ||
|
|
||
| return line | ||
|
|
||
|
|
||
| def extend_polyline(polyline, start_extension=0, end_extension=0): | ||
| """Extend a polyline by line from the vectors on segments at extreme sides | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe mention that this function returns a new polyline with 2 more points than was in the original polyline |
||
| Parameters | ||
| ---------- | ||
| polyline : list | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. list or Polyline or both? |
||
| list of points defining the polyline. | ||
| start_extension : float | ||
| The extension distance at the start of the polyline as float. | ||
| end_extension : float | ||
| The extension distance at the end of the polyline as float. | ||
| Returns | ||
| ------- | ||
| extended polyline : compas.geometry.Polyline(points) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the documentation doesn't need to know what you called the variable internally. |
||
| Examples | ||
| -------- | ||
| >>> polyline = Polyline([0.0,0.0,0.0],[1.0,0.0,0.0],[2.0,1.0,0.0],[3.0,1.0,0.0],[4.0,0.0,0.0],[5.0,0.0,0.0]) | ||
| >>> extended_polyline = extend_polyline(polyline, 1, 1) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment as in example above. |
||
| Polyline([-1.0,0.0,0.0],[0.0,0.0,0.0],[1.0,0.0,0.0],[2.0,1.0,0.0],[3.0,1.0,0.0],[4.0,0.0,0.0],[5.0,0.0,0.0],[6.0,0.0,0.0]) | ||
| """ | ||
| def calculate_translation_vector(vector, distance): | ||
| vector.unitize() | ||
| vector.scale(distance) | ||
| return Translation(vector) | ||
|
|
||
| points = polyline.points | ||
| if start_extension != 0: | ||
| point_start = polyline.points[0] | ||
| vec = Vector.from_start_end(polyline.points[1], point_start) | ||
| translation = calculate_translation_vector(vec, start_extension) | ||
| new_point_start = point_start.transformed(translation) | ||
| points.insert(0, new_point_start) | ||
|
|
||
| if end_extension != 0: | ||
| point_end = polyline.points[-1] | ||
| vec_end = Vector.from_start_end(polyline.points[-2], point_end) | ||
| translation = calculate_translation_vector(vec_end, end_extension) | ||
| new_point_end = point_end.transformed(translation) | ||
| points.append(new_point_end) | ||
|
|
||
| return Polyline(points) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| from __future__ import print_function | ||
| from __future__ import absolute_import | ||
| from __future__ import division | ||
|
|
||
| from .splits import * | ||
|
|
||
| __all__ = [name for name in dir() if not name.startswith('_')] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To make the documentation reference-able, this should read
and similarly for all references in this PR.