Skip to content
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

Refinement of various scene functions #1385

Merged
merged 10 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
19 changes: 0 additions & 19 deletions src/compas/scene/meshobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand All @@ -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.

Expand Down
91 changes: 80 additions & 11 deletions src/compas/scene/scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@
# 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

Check warning on line 85 in src/compas/scene/scene.py

View check run for this annotation

Codecov / codecov/patch

src/compas/scene/scene.py#L82-L85

Added lines #L82 - L85 were not covered by tests

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.
Expand Down Expand Up @@ -108,32 +116,82 @@
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)

Check warning on line 144 in src/compas/scene/scene.py

View check run for this annotation

Codecov / codecov/patch

src/compas/scene/scene.py#L144

Added line #L144 was not covered by tests

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)

Check warning on line 179 in src/compas/scene/scene.py

View check run for this annotation

Codecov / codecov/patch

src/compas/scene/scene.py#L179

Added line #L179 was not covered by tests

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:
Expand All @@ -142,3 +200,14 @@
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()

Check warning on line 213 in src/compas/scene/scene.py

View check run for this annotation

Codecov / codecov/patch

src/compas/scene/scene.py#L212-L213

Added lines #L212 - L213 were not covered by tests
2 changes: 1 addition & 1 deletion src/compas/scene/sceneobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
53 changes: 30 additions & 23 deletions src/compas_ghpython/scene/meshobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand All @@ -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

Expand All @@ -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
3 changes: 2 additions & 1 deletion src/compas_rhino/conversions/breps.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion src/compas_rhino/conversions/meshes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
----------
Expand Down
1 change: 1 addition & 0 deletions src/compas_rhino/scene/graphobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading
Loading