diff --git a/CHANGELOG.md b/CHANGELOG.md index 88a4ecdfeb2..50be02b4b14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +* Added `compas.scene.Scene.redraw`. +* Added `compas.scene.Scene.context_objects` representing all objects drawn in the visualisation context by the scene. +* Added `compas.scene.Scene.clear_context` with optional `guids` to clear some or all objects from the visualisation context. +* Added `clear_scene` and `clear_context` parameters to `compas.scene.Scene.clear` to differentiate between removing objects from the scene internally or removing corresponding objects from the viz context, or both (default). +* Added `compas_rhino.conversions.extrusion_to_compas_box` as direct conversion of extrusion breps. + ### Changed * Changed the `__str__` of `compas.geometry.Frame`, `compas.geometry.Plane`, `compas.geometry.Polygon`, `compas.geometry.Polyhedron`, `compas.geometry.Quaternion` to use a limited number of decimals (determined by `Tolerance.PRECISION`). Note: `__repr__` will instead maintain full precision. * Changed the `__str__` of `compas.geometry.Pointcloud` to print total number of points instead of the long list of points. Note: `__repr__` will still print all the points with full precision. * Fixed bug in `Pointcloud.from_box()`. +* Changed `compas.scene.MeshObject` to not use vertex coordinate caching because it is too fragile. +* Changed `compas_rhino.scene.RhinoMeshObject` to keep track of element-guid pairs in dicts. +* Changed `compas.scene.Scene._guids` to a default value of `[]`. +* Fixed bug due to missing import in `compas_rhino.scene.graphobject`. +* Changed `compas_rhino.scene.RhinoMeshObject.draw_vertexnormals` to use the same selection of vertices as `draw_vertices`. +* Changed `compas_rhino.scene.RhinoMeshObject.draw_vertexnormals` to use the corresponding vertex color if no color is specified. +* Changed `compas_rhino.scene.RhinoMeshObject.draw_facenormals` to use the same selection of vertices as `draw_faces`. +* Changed `compas_rhino.scene.RhinoMeshObject.draw_facenormals` to use the corresponding face color if no color is specified. ### Removed diff --git a/src/compas/scene/meshobject.py b/src/compas/scene/meshobject.py index 33069b8edd2..c6904ef3cd3 100644 --- a/src/compas/scene/meshobject.py +++ b/src/compas/scene/meshobject.py @@ -5,7 +5,6 @@ import compas.colors # noqa: F401 import compas.datastructures # noqa: F401 import compas.geometry # noqa: F401 -from compas.geometry import transform_points from .descriptors.colordict import ColorDictAttribute from .sceneobject import SceneObject @@ -71,7 +70,6 @@ def __init__( ): # fmt: skip # type: (...) -> None super(MeshObject, self).__init__(**kwargs) # type: ignore - self._vertex_xyz = None self.show_vertices = show_vertices self.show_edges = show_edges self.show_faces = show_faces @@ -107,7 +105,6 @@ def mesh(self, mesh): # type: (compas.datastructures.Mesh) -> None self._item = mesh self._transformation = None - self._vertex_xyz = None @property def transformation(self): @@ -117,24 +114,8 @@ def transformation(self): @transformation.setter def transformation(self, transformation): # type: (compas.geometry.Transformation) -> None - self._vertex_xyz = None self._transformation = transformation - @property - def vertex_xyz(self): - # type: () -> dict[int, list[float]] - if self.mesh: - if self._vertex_xyz is None: - points = self.mesh.vertices_attributes("xyz") - points = transform_points(points, self.worldtransformation) - self._vertex_xyz = dict(zip(self.mesh.vertices(), points)) - return self._vertex_xyz # type: ignore - - @vertex_xyz.setter - def vertex_xyz(self, vertex_xyz): - # type: (dict[int, list[float]]) -> None - self._vertex_xyz = vertex_xyz - def draw_vertices(self): """Draw the vertices of the mesh. diff --git a/src/compas/scene/scene.py b/src/compas/scene/scene.py index f9b0861a2b6..2e4dc253086 100644 --- a/src/compas/scene/scene.py +++ b/src/compas/scene/scene.py @@ -76,6 +76,14 @@ def objects(self): # type: () -> list[SceneObject] return [node for node in self.nodes if not node.is_root] # type: ignore + @property + def context_objects(self): + # type: () -> list + guids = [] + for obj in self.objects: + guids += obj.guids + return guids + def add(self, item, parent=None, **kwargs): # type: (compas.geometry.Geometry | compas.datastructures.Datastructure, SceneObject | TreeNode | None, dict) -> SceneObject """Add an item to the scene. @@ -108,32 +116,82 @@ def add(self, item, parent=None, **kwargs): super(Scene, self).add(sceneobject, parent=parent) return sceneobject - def clear(self): - # type: () -> None - """Clear everything from the current context of the scene.""" + def clear_context(self, guids=None): + # type: (list | None) -> None + """Clear the visualisation context. + + Parameters + ---------- + guids : list, optional + The identifiers of the objects in the visualisation context. + + Returns + ------- + None + + Notes + ----- + If `guids=None`, this will clear all objects from the visualisation context. + For example, when used in Rhino, it will remove everything from the Rhino model. + This is equivalent to `compas_rhino.clear()`. + + If `guids` is a list, only those objects in the list will be removed. + + The method is used by `Scene.clear` to remove all objects previously drawn by the scene, + without removing other model objects. + + """ + clear(guids) + + def clear(self, clear_scene=True, clear_context=True): + # type: (bool, bool) -> None + """Clear the scene. + + Parameters + ---------- + clear_scene : bool, optional + If True, all scene objects will be removed from the scene tree. + clear_context : bool, optional + If True, all objects drawn by the scene in the visualisation context will be removed. + + Returns + ------- + None - clear() + Notes + ----- + To redraw the scene, without modifying any of the other objects in the visualisation context: - def clear_objects(self): - # type: () -> None - """Clear all objects inside the scene.""" + >>> scene.clear(clear_scene=False, clear_context=True) + >>> scene.draw() + """ guids = [] + for sceneobject in self.objects: guids += sceneobject.guids sceneobject._guids = None - clear(guids=guids) + + if clear_scene: + self.remove(sceneobject) + + if clear_context: + self.clear_context(guids) def draw(self): - """Draw the scene.""" + """Draw the scene. + + This will just draw all scene objects in the scene tree, + without making any modifications to the visualisation context. + For example, it will not remove any of the previously drawn objects. + + """ if not self.context: raise ValueError("No context detected.") before_draw() - self.clear_objects() - drawn_objects = [] for sceneobject in self.objects: if sceneobject.show: @@ -142,3 +200,14 @@ def draw(self): after_draw(drawn_objects) return drawn_objects + + def redraw(self): + """Redraw the scene. + + This removes all previously drawn objects from the visualisation context, + before drawing all scene objects in the scene tree. + + """ + + self.clear(clear_scene=False, clear_context=True) + self.draw() diff --git a/src/compas/scene/sceneobject.py b/src/compas/scene/sceneobject.py index 2a0f4c30383..3ca5f081da2 100644 --- a/src/compas/scene/sceneobject.py +++ b/src/compas/scene/sceneobject.py @@ -111,7 +111,7 @@ def __init__( # which means that adding child objects will be added in context "None" self.context = context self._item = item - self._guids = None + self._guids = [] self._node = None self._frame = frame self._transformation = transformation diff --git a/src/compas_ghpython/scene/meshobject.py b/src/compas_ghpython/scene/meshobject.py index 46f4aada794..bb36ccc70d7 100644 --- a/src/compas_ghpython/scene/meshobject.py +++ b/src/compas_ghpython/scene/meshobject.py @@ -50,45 +50,38 @@ def draw(self): self._guids = [] if self.show_faces is True: + vertexcolors = [] - if len(self.vertexcolor): - vertexcolors = [self.vertexcolor[vertex] for vertex in self.mesh.vertices()] + if len(self.vertexcolor): # type: ignore + vertexcolors = [self.vertexcolor[vertex] for vertex in self.mesh.vertices()] # type: ignore facecolors = [] - if len(self.facecolor): - facecolors = [self.facecolor[face] for face in self.mesh.faces()] - - color = None - if not vertexcolors and not facecolors: - color = self.color + if len(self.facecolor): # type: ignore + facecolors = [self.facecolor[face] for face in self.mesh.faces()] # type: ignore vertex_index = self.mesh.vertex_index() - vertex_xyz = self.vertex_xyz - vertices = [vertex_xyz[vertex] for vertex in self.mesh.vertices()] + vertices = [self.mesh.vertex_attributes(vertex, "xyz") for vertex in self.mesh.vertices()] faces = [[vertex_index[vertex] for vertex in self.mesh.face_vertices(face)] for face in self.mesh.faces()] geometry = conversions.vertices_and_faces_to_rhino( vertices, faces, - color=color, + color=self.color, vertexcolors=vertexcolors, facecolors=facecolors, disjoint=self.disjoint, ) - # geometry.Transform(conversions.transformation_to_rhino(self.worldtransformation)) + geometry.Transform(conversions.transformation_to_rhino(self.worldtransformation)) self._guids.append(geometry) elif self.show_faces: - self._guids += self.draw_faces() + self.draw_faces() - if self.show_vertices: - self._guids += self.draw_vertices() - - if self.show_edges: - self._guids += self.draw_edges() + self.draw_vertices() + self.draw_edges() return self.guids @@ -104,9 +97,13 @@ def draw_vertices(self): vertices = list(self.mesh.vertices()) if self.show_vertices is True else self.show_vertices or [] + transformation = conversions.transformation_to_rhino(self.worldtransformation) + if vertices: for vertex in vertices: - points.append(conversions.point_to_rhino(self.vertex_xyz[vertex])) + geometry = conversions.point_to_rhino(self.mesh.vertex_attributes(vertex, "xyz")) + geometry.Transform(transformation) + points.append(geometry) return points @@ -122,9 +119,14 @@ def draw_edges(self): edges = list(self.mesh.edges()) if self.show_edges is True else self.show_edges or [] + transformation = conversions.transformation_to_rhino(self.worldtransformation) + if edges: for edge in edges: - lines.append(conversions.line_to_rhino((self.vertex_xyz[edge[0]], self.vertex_xyz[edge[1]]))) + line = self.mesh.edge_line(edge) + geometry = conversions.line_to_rhino(line) + geometry.Transform(transformation) + lines.append(geometry) return lines @@ -140,12 +142,17 @@ def draw_faces(self): faces = list(self.mesh.faces()) if self.show_faces is True else self.show_faces or [] + transformation = conversions.transformation_to_rhino(self.worldtransformation) + if faces: for face in faces: - color = self.facecolor[face] - vertices = [self.vertex_xyz[vertex] for vertex in self.mesh.face_vertices(face)] + color = self.facecolor[face] # type: ignore + vertices = [self.mesh.vertex_attributes(vertex, "xyz") for vertex in self.mesh.face_vertices(face)] # type: ignore facet = ngon(len(vertices)) + if facet: - meshes.append(conversions.vertices_and_faces_to_rhino(vertices, [facet], color=color)) + geometry = conversions.vertices_and_faces_to_rhino(vertices, [facet], color=color) + geometry.Transform(transformation) + meshes.append(geometry) return meshes diff --git a/src/compas_rhino/conversions/breps.py b/src/compas_rhino/conversions/breps.py index 51ee2714e4f..3be86027198 100644 --- a/src/compas_rhino/conversions/breps.py +++ b/src/compas_rhino/conversions/breps.py @@ -12,6 +12,7 @@ from compas.tolerance import TOL from .exceptions import ConversionError +from .extrusions import extrusion_to_compas_box from .geometry import point_to_compas from .shapes import cone_to_compas from .shapes import cylinder_to_compas @@ -82,7 +83,7 @@ def brep_to_compas_box(brep): :class:`compas.geometry.Box` """ - raise NotImplementedError + return extrusion_to_compas_box(brep.Geometry) def brep_to_compas_cone(brep): diff --git a/src/compas_rhino/conversions/meshes.py b/src/compas_rhino/conversions/meshes.py index 82c89ed4ea5..deff5233ae0 100644 --- a/src/compas_rhino/conversions/meshes.py +++ b/src/compas_rhino/conversions/meshes.py @@ -231,7 +231,7 @@ def face_callback(face): def mesh_to_compas(rhinomesh, cls=None): - """Convert a Rhino mesh object to a COMPAS mesh. + """Convert a Rhino mesh to a COMPAS mesh. Parameters ---------- diff --git a/src/compas_rhino/scene/graphobject.py b/src/compas_rhino/scene/graphobject.py index 88f1e409966..a761a559e36 100644 --- a/src/compas_rhino/scene/graphobject.py +++ b/src/compas_rhino/scene/graphobject.py @@ -6,6 +6,7 @@ import scriptcontext as sc # type: ignore import compas_rhino +import compas_rhino.objects from compas.geometry import Line from compas.scene import GraphObject from compas_rhino.conversions import line_to_rhino diff --git a/src/compas_rhino/scene/meshobject.py b/src/compas_rhino/scene/meshobject.py index db04e6c9eb2..3f4a0c9f316 100644 --- a/src/compas_rhino/scene/meshobject.py +++ b/src/compas_rhino/scene/meshobject.py @@ -8,13 +8,11 @@ import compas_rhino.objects from compas.colors import Color from compas.geometry import Line -from compas.geometry import Point from compas.geometry import centroid_points from compas.scene import MeshObject from compas_rhino.conversions import line_to_rhino from compas_rhino.conversions import point_to_rhino - -# from compas_rhino.conversions import transformation_to_rhino +from compas_rhino.conversions import transformation_to_rhino from compas_rhino.conversions import vertices_and_faces_to_rhino from .helpers import ngon @@ -56,16 +54,9 @@ def __init__(self, disjoint=False, vertexgroup=None, edgegroup=None, facegroup=N super(RhinoMeshObject, self).__init__(**kwargs) self.disjoint = disjoint self._guid_mesh = None - self._guids_faces = None - self._guids_edges = None - self._guids_vertices = None - self._guids_vertexnormals = None - self._guids_facenormals = None - self._guids_vertexlabels = None - self._guids_edgelabels = None - self._guids_facelabels = None - self._guids_spheres = None - self._guids_pipes = None + self._guid_face = {} + self._guid_edge = {} + self._guid_vertex = {} self.vertexgroup = vertexgroup self.edgegroup = edgegroup self.facegroup = facegroup @@ -92,7 +83,7 @@ def clear_vertices(self): None """ - compas_rhino.objects.delete_objects(self._guids_vertices, purge=True) + compas_rhino.objects.delete_objects(self._guid_vertex, purge=True) def clear_edges(self): """Delete all edges drawn by this scene object. @@ -102,7 +93,7 @@ def clear_edges(self): None """ - compas_rhino.objects.delete_objects(self._guids_edges, purge=True) + compas_rhino.objects.delete_objects(self._guid_edge, purge=True) def clear_faces(self): """Delete all faces drawn by this scene object. @@ -112,57 +103,7 @@ def clear_faces(self): None """ - compas_rhino.objects.delete_objects(self._guids_faces, purge=True) - - def clear_vertexnormals(self): - """Delete all vertex normals drawn by this scene object. - - Returns - ------- - None - - """ - compas_rhino.objects.delete_objects(self._guids_vertexnormals, purge=True) - - def clear_facenormals(self): - """Delete all face normals drawn by this scene object. - - Returns - ------- - None - - """ - compas_rhino.objects.delete_objects(self._guids_facenormals, purge=True) - - def clear_vertexlabels(self): - """Delete all vertex labels drawn by this scene object. - - Returns - ------- - None - - """ - compas_rhino.objects.delete_objects(self._guids_vertexlabels, purge=True) - - def clear_edgelabels(self): - """Delete all edge labels drawn by this scene object. - - Returns - ------- - None - - """ - compas_rhino.objects.delete_objects(self._guids_edgelabels, purge=True) - - def clear_facelabels(self): - """Delete all face labels drawn by this scene object. - - Returns - ------- - None - - """ - compas_rhino.objects.delete_objects(self._guids_facelabels, purge=True) + compas_rhino.objects.delete_objects(self._guid_face, purge=True) # ========================================================================== # draw @@ -193,17 +134,16 @@ def draw(self): attr = self.compile_attributes() vertexcolors = [] - if len(self.vertexcolor): - vertexcolors = [self.vertexcolor[vertex] for vertex in self.mesh.vertices()] + if len(self.vertexcolor): # type: ignore + vertexcolors = [self.vertexcolor[vertex] for vertex in self.mesh.vertices()] # type: ignore facecolors = [] - if len(self.facecolor): - facecolors = [self.facecolor[face] for face in self.mesh.faces()] + if len(self.facecolor): # type: ignore + facecolors = [self.facecolor[face] for face in self.mesh.faces()] # type: ignore vertex_index = self.mesh.vertex_index() - vertex_xyz = self.vertex_xyz - vertices = [vertex_xyz[vertex] for vertex in self.mesh.vertices()] + vertices = [self.mesh.vertex_attributes(vertex, "xyz") for vertex in self.mesh.vertices()] faces = [[vertex_index[vertex] for vertex in self.mesh.face_vertices(face)] for face in self.mesh.faces()] geometry = vertices_and_faces_to_rhino( @@ -215,7 +155,7 @@ def draw(self): disjoint=self.disjoint, ) - # geometry.Transform(transformation_to_rhino(self.worldtransformation)) + geometry.Transform(transformation_to_rhino(self.worldtransformation)) self._guid_mesh = sc.doc.Objects.AddMesh(geometry, attr) if self.group: @@ -224,13 +164,10 @@ def draw(self): self._guids.append(self._guid_mesh) elif self.show_faces: - self._guids += self.draw_faces() + self.draw_faces() - if self.show_vertices: - self._guids += self.draw_vertices() - - if self.show_edges: - self._guids += self.draw_edges() + self.draw_vertices() + self.draw_edges() return self.guids @@ -247,15 +184,18 @@ def draw_vertices(self): vertices = list(self.mesh.vertices()) if self.show_vertices is True else self.show_vertices or [] + transformation = transformation_to_rhino(self.worldtransformation) + if vertices: for vertex in vertices: name = "{}.vertex.{}".format(self.mesh.name, vertex) - color = self.vertexcolor[vertex] + color = self.vertexcolor[vertex] # type: ignore attr = self.compile_attributes(name=name, color=color) - point = point_to_rhino(self.vertex_xyz[vertex]) + geometry = point_to_rhino(self.mesh.vertex_attributes(vertex, "xyz")) + geometry.Transform(transformation) - guid = sc.doc.Objects.AddPoint(point, attr) + guid = sc.doc.Objects.AddPoint(geometry, attr) guids.append(guid) if guids: @@ -264,7 +204,9 @@ def draw_vertices(self): elif self.group: self.add_to_group(self.group, guids) - self._guids_vertices = guids + self._guid_vertex = dict(zip(guids, vertices)) + + self._guids += guids return guids def draw_edges(self): @@ -280,15 +222,20 @@ def draw_edges(self): edges = list(self.mesh.edges()) if self.show_edges is True else self.show_edges or [] + transformation = transformation_to_rhino(self.worldtransformation) + if edges: for edge in edges: name = "{}.edge.{}-{}".format(self.mesh.name, *edge) - color = self.edgecolor[edge] + color = self.edgecolor[edge] # type: ignore attr = self.compile_attributes(name=name, color=color) - line = Line(self.vertex_xyz[edge[0]], self.vertex_xyz[edge[1]]) + line = self.mesh.edge_line(edge) - guid = sc.doc.Objects.AddLine(line_to_rhino(line), attr) + geometry = line_to_rhino(line) + geometry.Transform(transformation) + + guid = sc.doc.Objects.AddLine(geometry, attr) guids.append(guid) if guids: @@ -297,7 +244,9 @@ def draw_edges(self): elif self.group: self.add_to_group(self.group, guids) - self._guids_edges = guids + self._guid_edge = dict(zip(guids, edges)) + + self._guids += guids return guids def draw_faces(self): @@ -313,17 +262,22 @@ def draw_faces(self): faces = list(self.mesh.faces()) if self.show_faces is True else self.show_faces or [] + transformation = transformation_to_rhino(self.worldtransformation) + if faces: for face in faces: name = "{}.face.{}".format(self.mesh.name, face) - color = self.facecolor[face] + color = self.facecolor[face] # type: ignore attr = self.compile_attributes(name=name, color=color) - vertices = [self.vertex_xyz[vertex] for vertex in self.mesh.face_vertices(face)] # type: ignore + vertices = [self.mesh.vertex_attributes(vertex, "xyz") for vertex in self.mesh.face_vertices(face)] # type: ignore facet = ngon(len(vertices)) if facet: - guid = sc.doc.Objects.AddMesh(vertices_and_faces_to_rhino(vertices, [facet]), attr) + geometry = vertices_and_faces_to_rhino(vertices, [facet]) + geometry.Transform(transformation) + + guid = sc.doc.Objects.AddMesh(geometry, attr) guids.append(guid) if guids: @@ -332,7 +286,9 @@ def draw_faces(self): elif self.group: self.add_to_group(self.group, guids) - self._guids_faces = guids + self._guid_face = dict(zip(guids, faces)) + + self._guids += guids return guids # ========================================================================== @@ -365,14 +321,17 @@ def draw_vertexlabels(self, text, color=None, group=None, fontheight=10, fontfac self.vertexcolor = color + transformation = transformation_to_rhino(self.worldtransformation) + for vertex in text: name = "{}.vertex.{}.label".format(self.mesh.name, vertex) # type: ignore - color = self.vertexcolor[vertex] + color = self.vertexcolor[vertex] # type: ignore attr = self.compile_attributes(name=name, color=color) - point = point_to_rhino(self.vertex_xyz[vertex]) + location = point_to_rhino(self.mesh.vertex_attributes(vertex, "xyz")) + location.Transform(transformation) - dot = Rhino.Geometry.TextDot(str(text[vertex]), point) # type: ignore + dot = Rhino.Geometry.TextDot(str(text[vertex]), location) # type: ignore dot.FontHeight = fontheight dot.FontFace = fontface @@ -383,7 +342,7 @@ def draw_vertexlabels(self, text, color=None, group=None, fontheight=10, fontfac self.add_to_group(group, guids) self._guids_vertexlabels = guids - + self._guids += guids return guids def draw_edgelabels(self, text, color=None, group=None, fontheight=10, fontface="Arial Regular"): @@ -412,15 +371,18 @@ def draw_edgelabels(self, text, color=None, group=None, fontheight=10, fontface= self.edgecolor = color + transformation = transformation_to_rhino(self.worldtransformation) + for edge in text: name = "{}.edge.{}-{}".format(self.mesh.name, *edge) # type: ignore - color = self.edgecolor[edge] + color = self.edgecolor[edge] # type: ignore attr = self.compile_attributes(name="{}.label".format(name), color=color) - line = Line(self.vertex_xyz[edge[0]], self.vertex_xyz[edge[1]]) - point = point_to_rhino(line.midpoint) + line = self.mesh.edge_line(edge) + location = point_to_rhino(line.midpoint) + location.Transform(transformation) - dot = Rhino.Geometry.TextDot(str(text[edge]), point) # type: ignore + dot = Rhino.Geometry.TextDot(str(text[edge]), location) # type: ignore dot.FontHeight = fontheight dot.FontFace = fontface @@ -431,7 +393,7 @@ def draw_edgelabels(self, text, color=None, group=None, fontheight=10, fontface= self.add_to_group(group, guids) self._guids_edgelabels = guids - + self._guids += guids return guids def draw_facelabels(self, text, color=None, group=None, fontheight=10, fontface="Arial Regular"): @@ -458,15 +420,18 @@ def draw_facelabels(self, text, color=None, group=None, fontheight=10, fontface= """ guids = [] + transformation = transformation_to_rhino(self.worldtransformation) + for face in text: name = "{}.face.{}.label".format(self.mesh.name, face) # type: ignore - color = self.facecolor[face] + color = self.facecolor[face] # type: ignore attr = self.compile_attributes(name=name, color=color) - points = [self.vertex_xyz[vertex] for vertex in self.mesh.face_vertices(face)] # type: ignore - point = point_to_rhino(centroid_points(points)) + points = [self.mesh.vertex_attributes(vertex, "xyz") for vertex in self.mesh.face_vertices(face)] # type: ignore + location = point_to_rhino(centroid_points(points)) + location.Transform(transformation) - dot = Rhino.Geometry.TextDot(str(text[face]), point) # type: ignore + dot = Rhino.Geometry.TextDot(str(text[face]), location) # type: ignore dot.FontHeight = fontheight dot.FontFace = fontface @@ -477,23 +442,21 @@ def draw_facelabels(self, text, color=None, group=None, fontheight=10, fontface= self.add_to_group(group, guids) self._guids_facelabels = guids - + self._guids += guids return guids # ========================================================================== # draw normals # ========================================================================== - def draw_vertexnormals(self, vertices=None, color=(0, 255, 0), scale=1.0, group=None): + def draw_vertexnormals(self, color=None, scale=1.0, group=None): """Draw the normals at the vertices of the mesh. Parameters ---------- - vertices : list[int], optional - A selection of vertex normals to draw. - Default is to draw all vertex normals. color : tuple[int, int, int] | tuple[float, float, float] | :class:`compas.colors.Color`, optional The color specification of the normal vectors. + If no color is specified, the color of the corresponding vertex is used. scale : float, optional Scale factor for the vertex normals. group : str, optional @@ -507,35 +470,40 @@ def draw_vertexnormals(self, vertices=None, color=(0, 255, 0), scale=1.0, group= """ guids = [] + vertices = list(self.mesh.vertices()) if self.show_vertices is True else self.show_vertices or [] + transformation = transformation_to_rhino(self.worldtransformation) + color = Color.coerce(color) - for vertex in vertices or self.mesh.vertices(): # type: ignore + for vertex in vertices: name = "{}.vertex.{}.normal".format(self.mesh.name, vertex) # type: ignore - attr = self.compile_attributes(name=name, color=color) + attr = self.compile_attributes(name=name, color=color or self.vertexcolor[vertex]) # type: ignore - point = Point(*self.vertex_xyz[vertex]) + point = self.mesh.vertex_point(vertex) normal = self.mesh.vertex_normal(vertex) # type: ignore + line = Line.from_point_and_vector(point, normal) + + geometry = line_to_rhino(line) + geometry.Transform(transformation) - guid = sc.doc.Objects.AddLine(point_to_rhino(point), point_to_rhino(point + normal * scale), attr) + guid = sc.doc.Objects.AddLine(geometry, attr) guids.append(guid) if group: self.add_to_group(group, guids) self._guids_vertexnormals = guids - + self._guids += guids return guids - def draw_facenormals(self, faces=None, color=(0, 255, 255), scale=1.0, group=None): + def draw_facenormals(self, color=None, scale=1.0, group=None): """Draw the normals of the faces. Parameters ---------- - faces : list[int], optional - A selection of face normals to draw. - Default is to draw all face normals. color : tuple[int, int, int] | tuple[float, float, float] | :class:`compas.colors.Color`, optional The color specification of the normal vectors. + If no color is specified, the color of the corresponding face is used. scale : float, optional Scale factor for the face normals. group : str, optional @@ -549,16 +517,23 @@ def draw_facenormals(self, faces=None, color=(0, 255, 255), scale=1.0, group=Non """ guids = [] + faces = list(self.mesh.faces()) if self.show_faces is True else self.show_faces or [] + transformation = transformation_to_rhino(self.worldtransformation) + color = Color.coerce(color) - for face in faces or self.mesh.faces(): # type: ignore + for face in faces: name = "{}.face.{}.normal".format(self.mesh.name, face) # type: ignore - attr = self.compile_attributes(name=name, color=color) + attr = self.compile_attributes(name=name, color=color or self.facecolor[face]) # type: ignore + + point = self.mesh.face_centroid(face) + normal = self.mesh.face_normal(face) + line = Line.from_point_and_vector(point, normal) - point = Point(*centroid_points([self.vertex_xyz[vertex] for vertex in self.mesh.face_vertices(face)])) # type: ignore - normal = self.mesh.face_normal(face) # type: ignore + geometry = line_to_rhino(line) + geometry.Transform(transformation) - guid = sc.doc.Objects.AddLine(point_to_rhino(point), point_to_rhino(point + normal * scale), attr) + guid = sc.doc.Objects.AddLine(geometry, attr) guids.append(guid) if group: diff --git a/tests/compas/geometry/test_polyhedron.py b/tests/compas/geometry/test_polyhedron.py index 18d098b88f8..8b673012421 100644 --- a/tests/compas/geometry/test_polyhedron.py +++ b/tests/compas/geometry/test_polyhedron.py @@ -1,8 +1,3 @@ -import pytest -import json -import compas -from random import random -from compas.geometry import Point from compas.geometry import Polyhedron from compas.itertools import pairwise diff --git a/tests/compas/scene/test_scene.py b/tests/compas/scene/test_scene.py index 678e4c3f235..29016749c36 100644 --- a/tests/compas/scene/test_scene.py +++ b/tests/compas/scene/test_scene.py @@ -99,3 +99,15 @@ def test_sceneobject_transform(): sceneobj3.worldtransformation == sceneobj1.frame.to_transformation() * sceneobj2.frame.to_transformation() * sceneobj3.frame.to_transformation() * sceneobj3.transformation ) + + def test_scene_clear(): + scene = Scene() + sceneobj1 = scene.add(Box()) + sceneobj2 = scene.add(Box(), parent=sceneobj1) + sceneobj3 = scene.add(Box(), parent=sceneobj2) # noqa: F841 + + assert len(scene.objects) == 3 + + scene.clear(clear_context=False, clear_scene=True) + + assert len(scene.objects) == 0