- 
                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 | ||
| 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()): | ||
| 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 | ||
| 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. 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 | ||
| 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()): | ||
| 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 | ||
| 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): | ||
| 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): | ||
| 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 | ||
| 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 | ||
| 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 | ||
| 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. | ||
| 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
    
   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
    
   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 | ||
| 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 | ||
| 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
    
   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
    
   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 | ||
| 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 | ||
| 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 | ||
| 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', | ||
| 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 | ||
| 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 | ||
| 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) | ||
| 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 | ||
| 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 | ||
| 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) | ||
| 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) | ||
| 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.