Skip to content

Commit f3f1fda

Browse files
authored
Merge pull request #1385 from compas-dev/scene-tweaks
Refinement of various scene functions
2 parents 82af17e + 6310194 commit f3f1fda

File tree

11 files changed

+239
-184
lines changed

11 files changed

+239
-184
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
* Added `compas.scene.Scene.redraw`.
13+
* Added `compas.scene.Scene.context_objects` representing all objects drawn in the visualisation context by the scene.
14+
* Added `compas.scene.Scene.clear_context` with optional `guids` to clear some or all objects from the visualisation context.
15+
* 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).
16+
* Added `compas_rhino.conversions.extrusion_to_compas_box` as direct conversion of extrusion breps.
17+
1218
### Changed
1319

1420
* 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.
1521
* 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.
1622
* Fixed bug in `Pointcloud.from_box()`.
23+
* Changed `compas.scene.MeshObject` to not use vertex coordinate caching because it is too fragile.
24+
* Changed `compas_rhino.scene.RhinoMeshObject` to keep track of element-guid pairs in dicts.
25+
* Changed `compas.scene.Scene._guids` to a default value of `[]`.
26+
* Fixed bug due to missing import in `compas_rhino.scene.graphobject`.
27+
* Changed `compas_rhino.scene.RhinoMeshObject.draw_vertexnormals` to use the same selection of vertices as `draw_vertices`.
28+
* Changed `compas_rhino.scene.RhinoMeshObject.draw_vertexnormals` to use the corresponding vertex color if no color is specified.
29+
* Changed `compas_rhino.scene.RhinoMeshObject.draw_facenormals` to use the same selection of vertices as `draw_faces`.
30+
* Changed `compas_rhino.scene.RhinoMeshObject.draw_facenormals` to use the corresponding face color if no color is specified.
1731

1832
### Removed
1933

src/compas/scene/meshobject.py

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import compas.colors # noqa: F401
66
import compas.datastructures # noqa: F401
77
import compas.geometry # noqa: F401
8-
from compas.geometry import transform_points
98

109
from .descriptors.colordict import ColorDictAttribute
1110
from .sceneobject import SceneObject
@@ -71,7 +70,6 @@ def __init__(
7170
): # fmt: skip
7271
# type: (...) -> None
7372
super(MeshObject, self).__init__(**kwargs) # type: ignore
74-
self._vertex_xyz = None
7573
self.show_vertices = show_vertices
7674
self.show_edges = show_edges
7775
self.show_faces = show_faces
@@ -107,7 +105,6 @@ def mesh(self, mesh):
107105
# type: (compas.datastructures.Mesh) -> None
108106
self._item = mesh
109107
self._transformation = None
110-
self._vertex_xyz = None
111108

112109
@property
113110
def transformation(self):
@@ -117,24 +114,8 @@ def transformation(self):
117114
@transformation.setter
118115
def transformation(self, transformation):
119116
# type: (compas.geometry.Transformation) -> None
120-
self._vertex_xyz = None
121117
self._transformation = transformation
122118

123-
@property
124-
def vertex_xyz(self):
125-
# type: () -> dict[int, list[float]]
126-
if self.mesh:
127-
if self._vertex_xyz is None:
128-
points = self.mesh.vertices_attributes("xyz")
129-
points = transform_points(points, self.worldtransformation)
130-
self._vertex_xyz = dict(zip(self.mesh.vertices(), points))
131-
return self._vertex_xyz # type: ignore
132-
133-
@vertex_xyz.setter
134-
def vertex_xyz(self, vertex_xyz):
135-
# type: (dict[int, list[float]]) -> None
136-
self._vertex_xyz = vertex_xyz
137-
138119
def draw_vertices(self):
139120
"""Draw the vertices of the mesh.
140121

src/compas/scene/scene.py

Lines changed: 80 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,14 @@ def objects(self):
7676
# type: () -> list[SceneObject]
7777
return [node for node in self.nodes if not node.is_root] # type: ignore
7878

79+
@property
80+
def context_objects(self):
81+
# type: () -> list
82+
guids = []
83+
for obj in self.objects:
84+
guids += obj.guids
85+
return guids
86+
7987
def add(self, item, parent=None, **kwargs):
8088
# type: (compas.geometry.Geometry | compas.datastructures.Datastructure, SceneObject | TreeNode | None, dict) -> SceneObject
8189
"""Add an item to the scene.
@@ -108,32 +116,82 @@ def add(self, item, parent=None, **kwargs):
108116
super(Scene, self).add(sceneobject, parent=parent)
109117
return sceneobject
110118

111-
def clear(self):
112-
# type: () -> None
113-
"""Clear everything from the current context of the scene."""
119+
def clear_context(self, guids=None):
120+
# type: (list | None) -> None
121+
"""Clear the visualisation context.
122+
123+
Parameters
124+
----------
125+
guids : list, optional
126+
The identifiers of the objects in the visualisation context.
127+
128+
Returns
129+
-------
130+
None
131+
132+
Notes
133+
-----
134+
If `guids=None`, this will clear all objects from the visualisation context.
135+
For example, when used in Rhino, it will remove everything from the Rhino model.
136+
This is equivalent to `compas_rhino.clear()`.
137+
138+
If `guids` is a list, only those objects in the list will be removed.
139+
140+
The method is used by `Scene.clear` to remove all objects previously drawn by the scene,
141+
without removing other model objects.
142+
143+
"""
144+
clear(guids)
145+
146+
def clear(self, clear_scene=True, clear_context=True):
147+
# type: (bool, bool) -> None
148+
"""Clear the scene.
149+
150+
Parameters
151+
----------
152+
clear_scene : bool, optional
153+
If True, all scene objects will be removed from the scene tree.
154+
clear_context : bool, optional
155+
If True, all objects drawn by the scene in the visualisation context will be removed.
156+
157+
Returns
158+
-------
159+
None
114160
115-
clear()
161+
Notes
162+
-----
163+
To redraw the scene, without modifying any of the other objects in the visualisation context:
116164
117-
def clear_objects(self):
118-
# type: () -> None
119-
"""Clear all objects inside the scene."""
165+
>>> scene.clear(clear_scene=False, clear_context=True)
166+
>>> scene.draw()
120167
168+
"""
121169
guids = []
170+
122171
for sceneobject in self.objects:
123172
guids += sceneobject.guids
124173
sceneobject._guids = None
125-
clear(guids=guids)
174+
175+
if clear_scene:
176+
self.remove(sceneobject)
177+
178+
if clear_context:
179+
self.clear_context(guids)
126180

127181
def draw(self):
128-
"""Draw the scene."""
182+
"""Draw the scene.
183+
184+
This will just draw all scene objects in the scene tree,
185+
without making any modifications to the visualisation context.
186+
For example, it will not remove any of the previously drawn objects.
187+
188+
"""
129189

130190
if not self.context:
131191
raise ValueError("No context detected.")
132192

133193
before_draw()
134194

135-
self.clear_objects()
136-
137195
drawn_objects = []
138196
for sceneobject in self.objects:
139197
if sceneobject.show:
@@ -142,3 +200,14 @@ def draw(self):
142200
after_draw(drawn_objects)
143201

144202
return drawn_objects
203+
204+
def redraw(self):
205+
"""Redraw the scene.
206+
207+
This removes all previously drawn objects from the visualisation context,
208+
before drawing all scene objects in the scene tree.
209+
210+
"""
211+
212+
self.clear(clear_scene=False, clear_context=True)
213+
self.draw()

src/compas/scene/sceneobject.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def __init__(
111111
# which means that adding child objects will be added in context "None"
112112
self.context = context
113113
self._item = item
114-
self._guids = None
114+
self._guids = []
115115
self._node = None
116116
self._frame = frame
117117
self._transformation = transformation

src/compas_ghpython/scene/meshobject.py

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -50,45 +50,38 @@ def draw(self):
5050
self._guids = []
5151

5252
if self.show_faces is True:
53+
5354
vertexcolors = []
54-
if len(self.vertexcolor):
55-
vertexcolors = [self.vertexcolor[vertex] for vertex in self.mesh.vertices()]
55+
if len(self.vertexcolor): # type: ignore
56+
vertexcolors = [self.vertexcolor[vertex] for vertex in self.mesh.vertices()] # type: ignore
5657

5758
facecolors = []
58-
if len(self.facecolor):
59-
facecolors = [self.facecolor[face] for face in self.mesh.faces()]
60-
61-
color = None
62-
if not vertexcolors and not facecolors:
63-
color = self.color
59+
if len(self.facecolor): # type: ignore
60+
facecolors = [self.facecolor[face] for face in self.mesh.faces()] # type: ignore
6461

6562
vertex_index = self.mesh.vertex_index()
66-
vertex_xyz = self.vertex_xyz
6763

68-
vertices = [vertex_xyz[vertex] for vertex in self.mesh.vertices()]
64+
vertices = [self.mesh.vertex_attributes(vertex, "xyz") for vertex in self.mesh.vertices()]
6965
faces = [[vertex_index[vertex] for vertex in self.mesh.face_vertices(face)] for face in self.mesh.faces()]
7066

7167
geometry = conversions.vertices_and_faces_to_rhino(
7268
vertices,
7369
faces,
74-
color=color,
70+
color=self.color,
7571
vertexcolors=vertexcolors,
7672
facecolors=facecolors,
7773
disjoint=self.disjoint,
7874
)
7975

80-
# geometry.Transform(conversions.transformation_to_rhino(self.worldtransformation))
76+
geometry.Transform(conversions.transformation_to_rhino(self.worldtransformation))
8177

8278
self._guids.append(geometry)
8379

8480
elif self.show_faces:
85-
self._guids += self.draw_faces()
81+
self.draw_faces()
8682

87-
if self.show_vertices:
88-
self._guids += self.draw_vertices()
89-
90-
if self.show_edges:
91-
self._guids += self.draw_edges()
83+
self.draw_vertices()
84+
self.draw_edges()
9285

9386
return self.guids
9487

@@ -104,9 +97,13 @@ def draw_vertices(self):
10497

10598
vertices = list(self.mesh.vertices()) if self.show_vertices is True else self.show_vertices or []
10699

100+
transformation = conversions.transformation_to_rhino(self.worldtransformation)
101+
107102
if vertices:
108103
for vertex in vertices:
109-
points.append(conversions.point_to_rhino(self.vertex_xyz[vertex]))
104+
geometry = conversions.point_to_rhino(self.mesh.vertex_attributes(vertex, "xyz"))
105+
geometry.Transform(transformation)
106+
points.append(geometry)
110107

111108
return points
112109

@@ -122,9 +119,14 @@ def draw_edges(self):
122119

123120
edges = list(self.mesh.edges()) if self.show_edges is True else self.show_edges or []
124121

122+
transformation = conversions.transformation_to_rhino(self.worldtransformation)
123+
125124
if edges:
126125
for edge in edges:
127-
lines.append(conversions.line_to_rhino((self.vertex_xyz[edge[0]], self.vertex_xyz[edge[1]])))
126+
line = self.mesh.edge_line(edge)
127+
geometry = conversions.line_to_rhino(line)
128+
geometry.Transform(transformation)
129+
lines.append(geometry)
128130

129131
return lines
130132

@@ -140,12 +142,17 @@ def draw_faces(self):
140142

141143
faces = list(self.mesh.faces()) if self.show_faces is True else self.show_faces or []
142144

145+
transformation = conversions.transformation_to_rhino(self.worldtransformation)
146+
143147
if faces:
144148
for face in faces:
145-
color = self.facecolor[face]
146-
vertices = [self.vertex_xyz[vertex] for vertex in self.mesh.face_vertices(face)]
149+
color = self.facecolor[face] # type: ignore
150+
vertices = [self.mesh.vertex_attributes(vertex, "xyz") for vertex in self.mesh.face_vertices(face)] # type: ignore
147151
facet = ngon(len(vertices))
152+
148153
if facet:
149-
meshes.append(conversions.vertices_and_faces_to_rhino(vertices, [facet], color=color))
154+
geometry = conversions.vertices_and_faces_to_rhino(vertices, [facet], color=color)
155+
geometry.Transform(transformation)
156+
meshes.append(geometry)
150157

151158
return meshes

src/compas_rhino/conversions/breps.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from compas.tolerance import TOL
1313

1414
from .exceptions import ConversionError
15+
from .extrusions import extrusion_to_compas_box
1516
from .geometry import point_to_compas
1617
from .shapes import cone_to_compas
1718
from .shapes import cylinder_to_compas
@@ -82,7 +83,7 @@ def brep_to_compas_box(brep):
8283
:class:`compas.geometry.Box`
8384
8485
"""
85-
raise NotImplementedError
86+
return extrusion_to_compas_box(brep.Geometry)
8687

8788

8889
def brep_to_compas_cone(brep):

src/compas_rhino/conversions/meshes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ def face_callback(face):
231231

232232

233233
def mesh_to_compas(rhinomesh, cls=None):
234-
"""Convert a Rhino mesh object to a COMPAS mesh.
234+
"""Convert a Rhino mesh to a COMPAS mesh.
235235
236236
Parameters
237237
----------

src/compas_rhino/scene/graphobject.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import scriptcontext as sc # type: ignore
77

88
import compas_rhino
9+
import compas_rhino.objects
910
from compas.geometry import Line
1011
from compas.scene import GraphObject
1112
from compas_rhino.conversions import line_to_rhino

0 commit comments

Comments
 (0)