From 5f9b52b2a1f6c1e549a9412ee720941f1e9b204d Mon Sep 17 00:00:00 2001 From: nikeftekhar <43377174+nikeftekhar@users.noreply.github.com> Date: Wed, 24 Jun 2020 13:36:23 +0200 Subject: [PATCH 01/13] new functions for compas geometry line divide by count polyline shorten polyline rebuild polyline divide by count polyline tween transformations extend line transformations extend polyline intersection_mesh_line intersection_mesh_plane split polyline_plane split mesh_plane --- .../_intersections/mesh_intersections.py | 102 ++++++++++++++++ src/compas/geometry/_primitives/line.py | 28 ++++- src/compas/geometry/_primitives/polyline.py | 102 ++++++++++++++++ .../geometry/_transformations/extend.py | 97 +++++++++++++++ src/compas/geometry/splits/__init__.py | 7 ++ src/compas/geometry/splits/splits.py | 113 ++++++++++++++++++ 6 files changed, 448 insertions(+), 1 deletion(-) create mode 100644 src/compas/geometry/_intersections/mesh_intersections.py create mode 100644 src/compas/geometry/_transformations/extend.py create mode 100644 src/compas/geometry/splits/__init__.py create mode 100644 src/compas/geometry/splits/splits.py diff --git a/src/compas/geometry/_intersections/mesh_intersections.py b/src/compas/geometry/_intersections/mesh_intersections.py new file mode 100644 index 000000000000..63d091239145 --- /dev/null +++ b/src/compas/geometry/_intersections/mesh_intersections.py @@ -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 + """ + for fkey in list(mesh.faces()): + 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 + 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 + + Parameters + ---------- + mesh : compas.datastructures.Mesh + plane : compas.geometry.Plane + + Returns + ------- + intersections: list of points as keys from mesh + """ + + intersections = [] + for u, v in list(mesh.edges()): + a = mesh.vertex_attributes(u,'xyz') + b = mesh.vertex_attributes(v,'xyz') + inters = intersection_segment_plane((a,b), plane) + 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 + if t>= 1.0: + 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): + """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] diff --git a/src/compas/geometry/_primitives/line.py b/src/compas/geometry/_primitives/line.py index ebf9a6e1d36e..c6a4ee2a65f2 100644 --- a/src/compas/geometry/_primitives/line.py +++ b/src/compas/geometry/_primitives/line.py @@ -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" + __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): + """Return list of points from dividing the line by specific number of divisions + + Parameters + ---------- + number : integer + number of divisions + includeEnds : boolean + True if including start and end point in division points + False if not including start and end point in division points + + Returns + ------- + list of: compas.geometry.Point // Point as sequence of values xyz) + 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]] + """ + 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] + # ============================================================================== # Main # ============================================================================== diff --git a/src/compas/geometry/_primitives/polyline.py b/src/compas/geometry/_primitives/polyline.py index 7ba57703a12a..8d381fb97730 100644 --- a/src/compas/geometry/_primitives/polyline.py +++ b/src/compas/geometry/_primitives/polyline.py @@ -54,6 +54,8 @@ class Polyline(Primitive): 1.0 """ + __module__ = "compas.geometry" + __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. + + Parameters + ---------- + start_distance : float. + distance to shorten from the starting point of the polyline + 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 + return points + return self + + 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 + Parameters + ---------- + number : integer. + number of divisions + includeEnds : boolean + 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)] + 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 + + Parameters + ---------- + polyline_two : compas.geometry.Polyline + polyline to create the tween polyline + number : number of points of the tween polyline + + 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 diff --git a/src/compas/geometry/_transformations/extend.py b/src/compas/geometry/_transformations/extend.py new file mode 100644 index 000000000000..e2dd7a968317 --- /dev/null +++ b/src/compas/geometry/_transformations/extend.py @@ -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', +] + + +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 + 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 + 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) + Line([-1.0,0.0,0.0],[2.0,0.0,0.0]) + """ + 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 + + Parameters + ---------- + polyline : list + 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) + + 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) + 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) diff --git a/src/compas/geometry/splits/__init__.py b/src/compas/geometry/splits/__init__.py new file mode 100644 index 000000000000..7a69d31f2fc7 --- /dev/null +++ b/src/compas/geometry/splits/__init__.py @@ -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('_')] \ No newline at end of file diff --git a/src/compas/geometry/splits/splits.py b/src/compas/geometry/splits/splits.py new file mode 100644 index 000000000000..b2ac47206e4a --- /dev/null +++ b/src/compas/geometry/splits/splits.py @@ -0,0 +1,113 @@ +from __future__ import print_function +from __future__ import absolute_import +from __future__ import division + +from compas.utilities import pairwise + +from compas.datastructures import Mesh +from compas.geometry import Polyline +from compas.geometry import intersection_segment_plane +from compas.geometry import intersection_mesh_plane +from compas.geometry import subtract_vectors +from compas.geometry import dot_vectors + +__all__ = [ + 'split_polyline_plane', + 'split_mesh_plane', +] + + +def split_polyline_plane(polyline, plane): + """Split a polyline by a plane, returns a list of polylines, if there has been intersections + Returns list of the splitted polylines + + Parameters + ---------- + polyline : compas.datastructures.Polyline + plane : compas.geometry.Plane + + Returns + ------- + polylines : list of compas.geometry.Polyline + """ + splitted_polylines_points = [] + sublist = [] + for i, segment in enumerate(pairwise(polyline)): + sublist.append(segment.start) + temp_intersection = intersection_segment_plane(segment, plane) + if temp_intersection: + sublist.append(temp_intersection) + splitted_polylines_points.append(sublist) + sublist = [] + sublist.append(temp_intersection) + if i == len(pairwise(polyline))-1: + sublist.append(segment.end) + splitted_polylines_points.append(sublist) + + return [Polyline(sublist) for sublist in splitted_polylines_points] + + +def split_mesh_plane(mesh, plane, open=True): # compas + """ Calculate all the intersections between edges of the mesh and cutting plane, + and splits every mesh edge at the intersection point, if it exists. + Returns a list of the resulting splitted meshes. + + Parameters + ---------- + mesh : compas.datastructures.Mesh + plane : compas.geometry.Plane + + Returns + ------- + splitted_meshes : list of compas.datastructures.Mesh + """ + + def mesh_from_split(self, mesh, v_keys, f_keys, intersections, open=True): # compas + """ Return a mesh from the positive or negative verts, faces and the intersection verts of a splitted mesh + open = true for open splits, open= False for closed splits + """ + vertices = {key: mesh.vertex_coordinates(key) for key in v_keys + intersections} + faces = [mesh.face_vertices(f_key) for f_key in f_keys] + final_mesh = Mesh.from_vertices_and_faces(vertices, faces) + if not open: + final_mesh.add_face(final_mesh.vertices_on_boundary(True)) + return final_mesh + + intersections = intersection_mesh_plane(mesh, plane) + + if len(intersections) > 2: + for f_key in list(mesh.faces()): + split = [v_key for v_key in mesh.face_vertices(f_key) if v_key in intersections] + if len(split) == 2: + mesh.split_face(f_key, split[0], split[1]) + + pos_verts = [] + neg_verts = [] + for v_key in mesh.vertices(): + if v_key in intersections: + continue + vert_a = mesh.vertex_attributes(v_key, 'xyz') + ori_vert_a = subtract_vectors(vert_a, plane.point) + similarity = dot_vectors(plane.normal, ori_vert_a) + if similarity > 0.0: + pos_verts.append(v_key) + elif similarity < 0.0: + neg_verts.append(v_key) + + pos_faces = [] + for key in pos_verts: + pos_faces += mesh.vertex_faces(key) + pos_faces = list(set(pos_faces)) + + neg_faces = [] + for key in neg_verts: + neg_faces += mesh.vertex_faces(key) + neg_faces = list(set(neg_faces)) + + pos_mesh = mesh_from_split(mesh, pos_verts, pos_faces, intersections, open) + neg_mesh = mesh_from_split(mesh, neg_verts, neg_faces, intersections, open) + + splitted_meshes = [pos_mesh, neg_mesh] + return splitted_meshes + else: + return None From 6408ef7acf1e5e843b40f8f121cb528c487e652a Mon Sep 17 00:00:00 2001 From: nikeftekhar <43377174+nikeftekhar@users.noreply.github.com> Date: Thu, 25 Jun 2020 09:50:06 +0200 Subject: [PATCH 02/13] Update src/compas/geometry/_intersections/mesh_intersections.py Co-authored-by: Gonzalo Casas --- .../_intersections/mesh_intersections.py | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/compas/geometry/_intersections/mesh_intersections.py b/src/compas/geometry/_intersections/mesh_intersections.py index 63d091239145..985897afb7e3 100644 --- a/src/compas/geometry/_intersections/mesh_intersections.py +++ b/src/compas/geometry/_intersections/mesh_intersections.py @@ -37,18 +37,19 @@ def intersection_mesh_line(mesh, line): 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: + if len(vertex_keys) not in (3, 4): continue + + 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: return None From 0e3420468e58546a294abd044029d0e3de64b708 Mon Sep 17 00:00:00 2001 From: nikeftekhar <43377174+nikeftekhar@users.noreply.github.com> Date: Thu, 25 Jun 2020 09:50:26 +0200 Subject: [PATCH 03/13] Update src/compas/geometry/_intersections/mesh_intersections.py Co-authored-by: Gonzalo Casas --- src/compas/geometry/_intersections/mesh_intersections.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compas/geometry/_intersections/mesh_intersections.py b/src/compas/geometry/_intersections/mesh_intersections.py index 985897afb7e3..3e652c5af27d 100644 --- a/src/compas/geometry/_intersections/mesh_intersections.py +++ b/src/compas/geometry/_intersections/mesh_intersections.py @@ -76,7 +76,7 @@ def intersection_mesh_plane(mesh, plane, tol=0.0001): 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 - if t>= 1.0: + if t >= 1.0: t = 1 - tol elif t <= 0.0: t = tol From 401a7e280bd9245f7e668138ed194d45cf8930e9 Mon Sep 17 00:00:00 2001 From: nikeftekhar <43377174+nikeftekhar@users.noreply.github.com> Date: Thu, 25 Jun 2020 09:53:19 +0200 Subject: [PATCH 04/13] Update src/compas/geometry/_intersections/mesh_intersections.py Co-authored-by: Gonzalo Casas --- src/compas/geometry/_intersections/mesh_intersections.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/compas/geometry/_intersections/mesh_intersections.py b/src/compas/geometry/_intersections/mesh_intersections.py index 3e652c5af27d..06d89e599c26 100644 --- a/src/compas/geometry/_intersections/mesh_intersections.py +++ b/src/compas/geometry/_intersections/mesh_intersections.py @@ -99,5 +99,4 @@ def mesh_vertices_to_points(mesh, v_keys): ------- 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] + return [Point(*mesh.vertex_attributes(v_key, 'xyz')) for v_key in v_keys] From 2df9dcf7f5fe96960674ebd0523f32393c58b4fc Mon Sep 17 00:00:00 2001 From: nikeftekhar <43377174+nikeftekhar@users.noreply.github.com> Date: Thu, 25 Jun 2020 09:58:20 +0200 Subject: [PATCH 05/13] Update src/compas/geometry/_primitives/line.py Co-authored-by: Gonzalo Casas --- src/compas/geometry/_primitives/line.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compas/geometry/_primitives/line.py b/src/compas/geometry/_primitives/line.py index c6a4ee2a65f2..42816b244fde 100644 --- a/src/compas/geometry/_primitives/line.py +++ b/src/compas/geometry/_primitives/line.py @@ -357,7 +357,7 @@ def divide_by_count(self, number=10, include_ends=False): -------- >>> 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]] + [[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]] """ if include_ends: return [self.point(i * float(1 / number)) for i in range(int(number)+1)] From 77549aa609d2d57c577a53eb4e68632607cbe8f3 Mon Sep 17 00:00:00 2001 From: nikeftekhar <43377174+nikeftekhar@users.noreply.github.com> Date: Thu, 25 Jun 2020 09:59:41 +0200 Subject: [PATCH 06/13] Update src/compas/geometry/splits/splits.py Co-authored-by: Gonzalo Casas --- src/compas/geometry/splits/splits.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compas/geometry/splits/splits.py b/src/compas/geometry/splits/splits.py index b2ac47206e4a..9c25cd5665f0 100644 --- a/src/compas/geometry/splits/splits.py +++ b/src/compas/geometry/splits/splits.py @@ -40,7 +40,7 @@ def split_polyline_plane(polyline, plane): splitted_polylines_points.append(sublist) sublist = [] sublist.append(temp_intersection) - if i == len(pairwise(polyline))-1: + if i == len(pairwise(polyline)) - 1: sublist.append(segment.end) splitted_polylines_points.append(sublist) From 829696c018af9aaab318873557979b656bb77a27 Mon Sep 17 00:00:00 2001 From: nikeftekhar <43377174+nikeftekhar@users.noreply.github.com> Date: Thu, 25 Jun 2020 10:00:10 +0200 Subject: [PATCH 07/13] Update src/compas/geometry/_primitives/line.py Co-authored-by: Gonzalo Casas --- src/compas/geometry/_primitives/line.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/compas/geometry/_primitives/line.py b/src/compas/geometry/_primitives/line.py index 42816b244fde..58dbad3bdb35 100644 --- a/src/compas/geometry/_primitives/line.py +++ b/src/compas/geometry/_primitives/line.py @@ -351,7 +351,8 @@ def divide_by_count(self, number=10, include_ends=False): Returns ------- - list of: compas.geometry.Point // Point as sequence of values xyz) + list of :class:`compas.geometry.Point` + Point as sequence of values xyz Example -------- From ad06707457f9e28e484c5e3a4957bbbacd009640 Mon Sep 17 00:00:00 2001 From: nikeftekhar <43377174+nikeftekhar@users.noreply.github.com> Date: Thu, 25 Jun 2020 10:00:28 +0200 Subject: [PATCH 08/13] Update src/compas/geometry/_primitives/line.py Co-authored-by: Gonzalo Casas --- src/compas/geometry/_primitives/line.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compas/geometry/_primitives/line.py b/src/compas/geometry/_primitives/line.py index 58dbad3bdb35..703492a0180c 100644 --- a/src/compas/geometry/_primitives/line.py +++ b/src/compas/geometry/_primitives/line.py @@ -363,7 +363,7 @@ def divide_by_count(self, number=10, include_ends=False): 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] + return [self.point(i * float(1.0 / number)) for i in range(int(number) + 1) if i != 0 or i != number] # ============================================================================== # Main From f29a933a0c225514cd9728138f8bf4a53bd7aa1e Mon Sep 17 00:00:00 2001 From: nikeftekhar <43377174+nikeftekhar@users.noreply.github.com> Date: Thu, 25 Jun 2020 10:00:44 +0200 Subject: [PATCH 09/13] Update src/compas/geometry/_primitives/polyline.py Co-authored-by: Gonzalo Casas --- src/compas/geometry/_primitives/polyline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compas/geometry/_primitives/polyline.py b/src/compas/geometry/_primitives/polyline.py index 8d381fb97730..ae748b988896 100644 --- a/src/compas/geometry/_primitives/polyline.py +++ b/src/compas/geometry/_primitives/polyline.py @@ -397,7 +397,7 @@ def divide_by_count(self, number=10, include_ends=False): ------- points : list of points resulting from dividing the polyline """ - points = [self.point(i * float(1 / number)) for i in range(number)] + points = [self.point(i * float(1.0 / number)) for i in range(number)] if include_ends: points.append(self.point(1)) else: From 4eafb739285cfe05c110fcc2647752fbc4a84f10 Mon Sep 17 00:00:00 2001 From: nikeftekhar <43377174+nikeftekhar@users.noreply.github.com> Date: Thu, 25 Jun 2020 10:01:10 +0200 Subject: [PATCH 10/13] Update src/compas/geometry/_transformations/extend.py Co-authored-by: Gonzalo Casas --- src/compas/geometry/_transformations/extend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compas/geometry/_transformations/extend.py b/src/compas/geometry/_transformations/extend.py index e2dd7a968317..b678331294ca 100644 --- a/src/compas/geometry/_transformations/extend.py +++ b/src/compas/geometry/_transformations/extend.py @@ -35,7 +35,7 @@ def extend_line(line, start_extension=0, end_extension=0): -------- >>> line = Line([0.0,0.0,0.0],[1.0,0.0,0.0]) >>> extended_line = extend_line(line, 1, 1) - Line([-1.0,0.0,0.0],[2.0,0.0,0.0]) + Line([-1.0, 0.0, 0.0], [2.0, 0.0, 0.0]) """ def calculate_translation(line, distance): vector = line.direction.copy() From faf7bc8200b29d7a735f5b7fb0fca37bb1c65e29 Mon Sep 17 00:00:00 2001 From: nikeftekhar <43377174+nikeftekhar@users.noreply.github.com> Date: Thu, 25 Jun 2020 11:13:47 +0200 Subject: [PATCH 11/13] all changes and tests corrected errors invoke lint passed invoke test passed --- .../_intersections/mesh_intersections.py | 26 ++-- src/compas/geometry/_primitives/line.py | 8 +- src/compas/geometry/_primitives/polyline.py | 21 ++-- .../geometry/_transformations/extend.py | 10 +- src/compas/geometry/splits/__init__.py | 4 +- src/compas/geometry/splits/splits.py | 118 +++++++++--------- 6 files changed, 88 insertions(+), 99 deletions(-) diff --git a/src/compas/geometry/_intersections/mesh_intersections.py b/src/compas/geometry/_intersections/mesh_intersections.py index 06d89e599c26..4112abdd31b3 100644 --- a/src/compas/geometry/_intersections/mesh_intersections.py +++ b/src/compas/geometry/_intersections/mesh_intersections.py @@ -2,7 +2,6 @@ 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 @@ -17,11 +16,7 @@ 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. + """Compute intersection between mesh faces and line. After one single intersection, stops searching for more. Parameters ---------- @@ -53,6 +48,7 @@ def intersection_mesh_line(mesh, line): 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 @@ -65,24 +61,22 @@ def intersection_mesh_plane(mesh, plane, tol=0.0001): ------- intersections: list of points as keys from mesh """ - - intersections = [] + intersections = [] for u, v in list(mesh.edges()): - a = mesh.vertex_attributes(u,'xyz') - b = mesh.vertex_attributes(v,'xyz') - inters = intersection_segment_plane((a,b), plane) - if not inters: + a = mesh.vertex_attributes(u, 'xyz') + b = mesh.vertex_attributes(v, 'xyz') + intersection = intersection_segment_plane((a, b), plane) + if not intersection: continue - len_a_inters = length_vector(subtract_vectors(inters, a)) + len_a_inters = length_vector(subtract_vectors(intersection, a)) len_a_b = length_vector(subtract_vectors(b, a)) t = len_a_inters / len_a_b - if t >= 1.0: + if t >= 1.0: 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 @@ -97,6 +91,6 @@ def mesh_vertices_to_points(mesh, v_keys): Returns ------- - list of compas.geometry.Point + list of compas.geometry.Point """ return [Point(*mesh.vertex_attributes(v_key, 'xyz')) for v_key in v_keys] diff --git a/src/compas/geometry/_primitives/line.py b/src/compas/geometry/_primitives/line.py index 703492a0180c..cb4ed493aa6a 100644 --- a/src/compas/geometry/_primitives/line.py +++ b/src/compas/geometry/_primitives/line.py @@ -55,8 +55,6 @@ class Line(Primitive): True """ - __module__ = "compas.geometry" - __slots__ = ['_start', '_end'] def __init__(self, p1, p2): @@ -356,19 +354,19 @@ def divide_by_count(self, number=10, include_ends=False): Example -------- - >>> line = Line([0.0,0.0,0.0],[5.0,0.0,0.0]) + >>> 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]] + [Point(0.000, 0.000, 0.000), Point(1.000, 0.000, 0.000), Point(2.000, 0.000, 0.000), Point(3.000, 0.000, 0.000), Point(4.000, 0.000, 0.000), Point(5.000, 0.000, 0.000)] """ if include_ends: return [self.point(i * float(1 / number)) for i in range(int(number)+1)] else: return [self.point(i * float(1.0 / number)) for i in range(int(number) + 1) if i != 0 or i != number] - # ============================================================================== # Main # ============================================================================== + if __name__ == '__main__': import doctest diff --git a/src/compas/geometry/_primitives/polyline.py b/src/compas/geometry/_primitives/polyline.py index ae748b988896..fac053ac1768 100644 --- a/src/compas/geometry/_primitives/polyline.py +++ b/src/compas/geometry/_primitives/polyline.py @@ -54,8 +54,6 @@ class Polyline(Primitive): 1.0 """ - __module__ = "compas.geometry" - __slots__ = ["_points", "_lines"] def __init__(self, points): @@ -320,10 +318,10 @@ 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. - + Parameters ---------- start_distance : float. @@ -373,13 +371,13 @@ def rebuild(self, number=20): Returns ------- - list of equally spaced points on the polyline + :class: 'compas.geometry.Polyline' + the rebuilt copy """ - 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 + points = [self.point(i * float(1.0 / number)) for i in range(number)] + points.append(self.point(1)) + rebuilt_polyline.points = [Point(x, y, z) for x, y, z in points] return rebuilt_polyline def divide_by_count(self, number=10, include_ends=False): @@ -395,7 +393,7 @@ def divide_by_count(self, number=10, include_ends=False): Returns ------- - points : list of points resulting from dividing the polyline + list of :class: 'compas.geometry.Point' """ points = [self.point(i * float(1.0 / number)) for i in range(number)] if include_ends: @@ -415,7 +413,7 @@ def tween(self, polyline_two, number=50): Returns ------- - list of compas.geometry.Point + list of :class: 'compas.geometry.Point' """ rebuilt_polyline_one = self.rebuild(number) rebuilt_polyline_two = polyline_two.rebuild(number) @@ -426,6 +424,7 @@ def tween(self, polyline_two, number=50): # Main # ============================================================================== + if __name__ == '__main__': import doctest diff --git a/src/compas/geometry/_transformations/extend.py b/src/compas/geometry/_transformations/extend.py index b678331294ca..515bfcc4d8e9 100644 --- a/src/compas/geometry/_transformations/extend.py +++ b/src/compas/geometry/_transformations/extend.py @@ -3,10 +3,8 @@ 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', @@ -29,11 +27,11 @@ def extend_line(line, start_extension=0, end_extension=0): Returns ------- extended line : tuple - Two points defining the offset line. + Two points defining the extended line. Examples -------- - >>> line = Line([0.0,0.0,0.0],[1.0,0.0,0.0]) + >>> line = Line([0.0, 0.0, 0.0], [1.0, 0.0, 0.0]) >>> extended_line = extend_line(line, 1, 1) Line([-1.0, 0.0, 0.0], [2.0, 0.0, 0.0]) """ @@ -70,9 +68,9 @@ def extend_polyline(polyline, start_extension=0, end_extension=0): 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]) + >>> 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) - 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]) + 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() diff --git a/src/compas/geometry/splits/__init__.py b/src/compas/geometry/splits/__init__.py index 7a69d31f2fc7..327f2c7d1a57 100644 --- a/src/compas/geometry/splits/__init__.py +++ b/src/compas/geometry/splits/__init__.py @@ -2,6 +2,4 @@ from __future__ import absolute_import from __future__ import division -from .splits import * - -__all__ = [name for name in dir() if not name.startswith('_')] \ No newline at end of file +__all__ = [name for name in dir() if not name.startswith('_')] diff --git a/src/compas/geometry/splits/splits.py b/src/compas/geometry/splits/splits.py index 9c25cd5665f0..95eb6bb2888d 100644 --- a/src/compas/geometry/splits/splits.py +++ b/src/compas/geometry/splits/splits.py @@ -7,7 +7,7 @@ from compas.datastructures import Mesh from compas.geometry import Polyline from compas.geometry import intersection_segment_plane -from compas.geometry import intersection_mesh_plane +from compas.geometry._intersections.mesh_intersections import intersection_mesh_plane from compas.geometry import subtract_vectors from compas.geometry import dot_vectors @@ -23,31 +23,43 @@ def split_polyline_plane(polyline, plane): Parameters ---------- - polyline : compas.datastructures.Polyline + polyline : compas.geometry.Polyline plane : compas.geometry.Plane Returns ------- - polylines : list of compas.geometry.Polyline + list of :class: 'compas.geometry.Polyline' """ - splitted_polylines_points = [] + points_from_split_polylines = [] sublist = [] for i, segment in enumerate(pairwise(polyline)): sublist.append(segment.start) - temp_intersection = intersection_segment_plane(segment, plane) - if temp_intersection: - sublist.append(temp_intersection) - splitted_polylines_points.append(sublist) + intersection = intersection_segment_plane(segment, plane) + if intersection: + sublist.append(intersection) + points_from_split_polylines.append(sublist) sublist = [] - sublist.append(temp_intersection) + sublist.append(intersection) if i == len(pairwise(polyline)) - 1: sublist.append(segment.end) - splitted_polylines_points.append(sublist) + points_from_split_polylines.append(sublist) - return [Polyline(sublist) for sublist in splitted_polylines_points] + return [Polyline(sublist) for sublist in points_from_split_polylines] -def split_mesh_plane(mesh, plane, open=True): # compas +def _mesh_from_split(mesh, v_keys, f_keys, intersections, open=True): + """ Return a mesh from the positive or negative verts, faces and the intersection verts of a splitted mesh + open = true for open splits, open= False for closed splits + """ + vertices = {key: mesh.vertex_coordinates(key) for key in v_keys + intersections} + faces = [mesh.face_vertices(f_key) for f_key in f_keys] + final_mesh = Mesh.from_vertices_and_faces(vertices, faces) + if not open: + final_mesh.add_face(final_mesh.vertices_on_boundary(True)) + return final_mesh + + +def split_mesh_plane(mesh, plane, open=True): """ Calculate all the intersections between edges of the mesh and cutting plane, and splits every mesh edge at the intersection point, if it exists. Returns a list of the resulting splitted meshes. @@ -62,52 +74,42 @@ def split_mesh_plane(mesh, plane, open=True): # compas splitted_meshes : list of compas.datastructures.Mesh """ - def mesh_from_split(self, mesh, v_keys, f_keys, intersections, open=True): # compas - """ Return a mesh from the positive or negative verts, faces and the intersection verts of a splitted mesh - open = true for open splits, open= False for closed splits - """ - vertices = {key: mesh.vertex_coordinates(key) for key in v_keys + intersections} - faces = [mesh.face_vertices(f_key) for f_key in f_keys] - final_mesh = Mesh.from_vertices_and_faces(vertices, faces) - if not open: - final_mesh.add_face(final_mesh.vertices_on_boundary(True)) - return final_mesh - intersections = intersection_mesh_plane(mesh, plane) - if len(intersections) > 2: - for f_key in list(mesh.faces()): - split = [v_key for v_key in mesh.face_vertices(f_key) if v_key in intersections] - if len(split) == 2: - mesh.split_face(f_key, split[0], split[1]) - - pos_verts = [] - neg_verts = [] - for v_key in mesh.vertices(): - if v_key in intersections: - continue - vert_a = mesh.vertex_attributes(v_key, 'xyz') - ori_vert_a = subtract_vectors(vert_a, plane.point) - similarity = dot_vectors(plane.normal, ori_vert_a) - if similarity > 0.0: - pos_verts.append(v_key) - elif similarity < 0.0: - neg_verts.append(v_key) - - pos_faces = [] - for key in pos_verts: - pos_faces += mesh.vertex_faces(key) - pos_faces = list(set(pos_faces)) - - neg_faces = [] - for key in neg_verts: - neg_faces += mesh.vertex_faces(key) - neg_faces = list(set(neg_faces)) - - pos_mesh = mesh_from_split(mesh, pos_verts, pos_faces, intersections, open) - neg_mesh = mesh_from_split(mesh, neg_verts, neg_faces, intersections, open) - - splitted_meshes = [pos_mesh, neg_mesh] - return splitted_meshes - else: + if len(intersections) < 3: return None + + for f_key in list(mesh.faces()): + split = [v_key for v_key in mesh.face_vertices(f_key) if v_key in intersections] + if len(split) == 2: + mesh.split_face(f_key, split[0], split[1]) + + positive_vertices = [] + negative_vertices = [] + for v_key in mesh.vertices(): + if v_key in intersections: + continue + vert_a = mesh.vertex_attributes(v_key, 'xyz') + ori_vert_a = subtract_vectors(vert_a, plane.point) + similarity = dot_vectors(plane.normal, ori_vert_a) + if similarity > 0.0: + positive_vertices.append(v_key) + elif similarity < 0.0: + negative_vertices.append(v_key) + + positive_faces = [] + for key in positive_vertices: + positive_faces += mesh.vertex_faces(key) + positive_faces = list(set(positive_faces)) + + negative_faces = [] + for key in negative_vertices: + negative_faces += mesh.vertex_faces(key) + negative_faces = list(set(negative_faces)) + + positive_mesh = _mesh_from_split(mesh, positive_vertices, positive_faces, intersections, open) + negative_mesh = _mesh_from_split(mesh, negative_vertices, negative_faces, intersections, open) + + splitted_meshes = [positive_mesh, negative_mesh] + return splitted_meshes + \ No newline at end of file From e9209c81f5a1210a4285c0d19d6e3a80404679b3 Mon Sep 17 00:00:00 2001 From: nikeftekhar <43377174+nikeftekhar@users.noreply.github.com> Date: Mon, 29 Jun 2020 14:08:32 +0200 Subject: [PATCH 12/13] some fixes --- .../_intersections/mesh_intersections.py | 4 ++-- .../geometry/_transformations/extend.py | 11 +++++----- src/compas/geometry/splits/splits.py | 20 ++++++++++++++++--- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/compas/geometry/_intersections/mesh_intersections.py b/src/compas/geometry/_intersections/mesh_intersections.py index 4112abdd31b3..646344cfd7a8 100644 --- a/src/compas/geometry/_intersections/mesh_intersections.py +++ b/src/compas/geometry/_intersections/mesh_intersections.py @@ -45,8 +45,8 @@ def intersection_mesh_line(mesh, line): intersection_2 = intersection_line_triangle(line, triangle_2) if intersection_2: return Point(intersection_2[0], intersection_2[1], intersection_2[2]) - else: - return None + + return None def intersection_mesh_plane(mesh, plane, tol=0.0001): diff --git a/src/compas/geometry/_transformations/extend.py b/src/compas/geometry/_transformations/extend.py index 515bfcc4d8e9..bf120f992ae8 100644 --- a/src/compas/geometry/_transformations/extend.py +++ b/src/compas/geometry/_transformations/extend.py @@ -72,24 +72,23 @@ def extend_polyline(polyline, start_extension=0, end_extension=0): >>> extended_polyline = extend_polyline(polyline, 1, 1) 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): + def move_point(point, vector, distance): vector.unitize() vector.scale(distance) - return Translation(vector) + move = Translation(vector) + return point.transformed(move) 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) + new_point_start = move_point(point_start, vec, start_extension) 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) + new_point_end = move_point(point_end, vec_end, end_extension) points.append(new_point_end) return Polyline(points) diff --git a/src/compas/geometry/splits/splits.py b/src/compas/geometry/splits/splits.py index 95eb6bb2888d..8b91ec3793c2 100644 --- a/src/compas/geometry/splits/splits.py +++ b/src/compas/geometry/splits/splits.py @@ -48,8 +48,22 @@ def split_polyline_plane(polyline, plane): def _mesh_from_split(mesh, v_keys, f_keys, intersections, open=True): - """ Return a mesh from the positive or negative verts, faces and the intersection verts of a splitted mesh - open = true for open splits, open= False for closed splits + """Return a mesh from the positive or negative verts, faces and the intersection verts of a splitted mesh + + Parameters + ---------- + v_keys : list of integers + list of keys from vertices of the mesh as integers + f_keys : list of faces + list of face keys(list of vertices forming the face) + intersections : list of integers + list of keys from the intersection of a mesh with a plane + open : boolean + True if the result remains open, False if the resulting mesh is closed + + Returns + ------- + compas.datastructures.Mesh """ vertices = {key: mesh.vertex_coordinates(key) for key in v_keys + intersections} faces = [mesh.face_vertices(f_key) for f_key in f_keys] @@ -60,7 +74,7 @@ def _mesh_from_split(mesh, v_keys, f_keys, intersections, open=True): def split_mesh_plane(mesh, plane, open=True): - """ Calculate all the intersections between edges of the mesh and cutting plane, + """Calculate all the intersections between edges of the mesh and cutting plane, and splits every mesh edge at the intersection point, if it exists. Returns a list of the resulting splitted meshes. From 8b750117e4b7b3889a18f238a37ab4cd986d65dd Mon Sep 17 00:00:00 2001 From: nikeftekhar <43377174+nikeftekhar@users.noreply.github.com> Date: Thu, 2 Jul 2020 17:16:56 +0200 Subject: [PATCH 13/13] slight change --- src/compas/geometry/_primitives/line.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compas/geometry/_primitives/line.py b/src/compas/geometry/_primitives/line.py index cb4ed493aa6a..1d5aa5a32c6d 100644 --- a/src/compas/geometry/_primitives/line.py +++ b/src/compas/geometry/_primitives/line.py @@ -359,9 +359,9 @@ def divide_by_count(self, number=10, include_ends=False): [Point(0.000, 0.000, 0.000), Point(1.000, 0.000, 0.000), Point(2.000, 0.000, 0.000), Point(3.000, 0.000, 0.000), Point(4.000, 0.000, 0.000), Point(5.000, 0.000, 0.000)] """ if include_ends: - return [self.point(i * float(1 / number)) for i in range(int(number)+1)] + return [self.point(i * float(1.0 / number)) for i in range(int(number) + 1)] else: - return [self.point(i * float(1.0 / number)) for i in range(int(number) + 1) if i != 0 or i != number] + return [self.point(i * float(1.0 / number)) for i in range(int(number)) if i != 0] # ============================================================================== # Main # ==============================================================================