33from functools import partial
44
55import compas_blender
6+ from compas .geometry import add_vectors
7+ from compas .geometry import centroid_points
8+ from compas .geometry import scale_vector
69
710from compas_blender .artists ._artist import BaseArtist
811from compas .utilities import color_to_colordict
@@ -50,9 +53,19 @@ def __init__(self, mesh):
5053 self ._vertexcollection = None
5154 self ._edgecollection = None
5255 self ._facecollection = None
56+ self ._vertexnormalcollection = None
57+ self ._facenormalcollection = None
58+ self ._vertexlabelcollection = None
59+ self ._edgelabelcollection = None
60+ self ._facelabelcollection = None
5361 self ._object_vertex = {}
5462 self ._object_edge = {}
5563 self ._object_face = {}
64+ self ._object_vertexnormal = {}
65+ self ._object_facenormal = {}
66+ self ._object_vertexlabel = {}
67+ self ._object_edgelabel = {}
68+ self ._object_facelabel = {}
5669 self .color_vertices = (1.0 , 1.0 , 1.0 )
5770 self .color_edges = (0.0 , 0.0 , 0.0 )
5871 self .color_faces = (0.7 , 0.7 , 0.7 )
@@ -88,6 +101,41 @@ def facecollection(self):
88101 self ._facecollection = compas_blender .create_collections_from_path (path )[1 ]
89102 return self ._facecollection
90103
104+ @property
105+ def vertexnormalcollection (self ):
106+ path = f"{ self .mesh .name } ::VertexNormals"
107+ if not self ._vertexnormalcollection :
108+ self ._vertexnormalcollection = compas_blender .create_collections_from_path (path )[1 ]
109+ return self ._vertexnormalcollection
110+
111+ @property
112+ def facenormalcollection (self ):
113+ path = f"{ self .mesh .name } ::FaceNormals"
114+ if not self ._facenormalcollection :
115+ self ._facenormalcollection = compas_blender .create_collections_from_path (path )[1 ]
116+ return self ._facenormalcollection
117+
118+ @property
119+ def vertexlabelcollection (self ):
120+ path = f"{ self .mesh .name } ::VertexLabels"
121+ if not self ._vertexlabelcollection :
122+ self ._vertexlabelcollection = compas_blender .create_collections_from_path (path )[1 ]
123+ return self ._vertexlabelcollection
124+
125+ @property
126+ def edgelabelcollection (self ):
127+ path = f"{ self .mesh .name } ::EdgeLabels"
128+ if not self ._edgelabelcollection :
129+ self ._edgelabelcollection = compas_blender .create_collections_from_path (path )[1 ]
130+ return self ._edgelabelcollection
131+
132+ @property
133+ def facelabelcollection (self ):
134+ path = f"{ self .mesh .name } ::FaceLabels"
135+ if not self ._facelabelcollection :
136+ self ._facelabelcollection = compas_blender .create_collections_from_path (path )[1 ]
137+ return self ._facelabelcollection
138+
91139 @property
92140 def object_vertex (self ):
93141 """Map between Blender object objects and mesh vertex identifiers."""
@@ -115,6 +163,51 @@ def object_face(self):
115163 def object_face (self , values ):
116164 self ._object_face = dict (values )
117165
166+ @property
167+ def object_vertexnormal (self ):
168+ """Map between Blender object objects and mesh vertex normal identifiers."""
169+ return self ._object_vertexnormal
170+
171+ @object_vertexnormal .setter
172+ def object_vertexnormal (self , values ):
173+ self ._object_vertexnormal = dict (values )
174+
175+ @property
176+ def object_facenormal (self ):
177+ """Map between Blender object objects and mesh face normal identifiers."""
178+ return self ._object_facenormal
179+
180+ @object_facenormal .setter
181+ def object_facenormal (self , values ):
182+ self ._object_facenormal = dict (values )
183+
184+ @property
185+ def object_vertexlabel (self ):
186+ """Map between Blender object objects and mesh vertex label identifiers."""
187+ return self ._object_vertexlabel
188+
189+ @object_vertexlabel .setter
190+ def object_vertexlabel (self , values ):
191+ self ._object_vertexlabel = dict (values )
192+
193+ @property
194+ def object_edgelabel (self ):
195+ """Map between Blender object objects and mesh edge label identifiers."""
196+ return self ._object_edgelabel
197+
198+ @object_edgelabel .setter
199+ def object_edgelabel (self , values ):
200+ self ._object_edgelabel = dict (values )
201+
202+ @property
203+ def object_facelabel (self ):
204+ """Map between Blender object objects and mesh face label identifiers."""
205+ return self ._object_facelabel
206+
207+ @object_facelabel .setter
208+ def object_facelabel (self , values ):
209+ self ._object_facelabel = dict (values )
210+
118211 # ==========================================================================
119212 # clear
120213 # ==========================================================================
@@ -126,10 +219,20 @@ def clear(self):
126219 objects += list (self .object_vertex )
127220 objects += list (self .object_edge )
128221 objects += list (self .object_face )
222+ objects += list (self .object_vertexnormal )
223+ objects += list (self .object_facenormal )
224+ objects += list (self .object_vertexlabel )
225+ objects += list (self .object_edgelabel )
226+ objects += list (self .object_facelabel )
129227 compas_blender .delete_objects (objects , purge_data = True )
130228 self ._object_vertex = {}
131229 self ._object_edge = {}
132230 self ._object_face = {}
231+ self ._object_vertexnormal = {}
232+ self ._object_facenormal = {}
233+ self ._object_vertexlabel = {}
234+ self ._object_edgelabel = {}
235+ self ._object_facelabel = {}
133236
134237 # ==========================================================================
135238 # components
@@ -246,3 +349,190 @@ def draw_edges(self, edges=None, color=None):
246349 objects = compas_blender .draw_lines (lines , self .edgecollection )
247350 self .object_edge = zip (objects , edges )
248351 return objects
352+
353+ # ==========================================================================
354+ # draw normals
355+ # ==========================================================================
356+
357+ def draw_vertexnormals (self , vertices = None , color = (0. , 1. , 0. ), scale = 1.0 ):
358+ """Draw the normals at the vertices of the mesh.
359+
360+ Parameters
361+ ----------
362+ vertices : list, optional
363+ A selection of vertex normals to draw.
364+ Default is to draw all vertex normals.
365+ color : tuple, optional
366+ The color specification of the normal vectors.
367+ The default color is green, ``(0., 1., 0.)``.
368+ scale : float, optional
369+ Scale factor for the vertex normals.
370+ Default is ``1.0``.
371+
372+ Returns
373+ -------
374+ list of :class:`bpy.types.Object`
375+ """
376+ vertices = vertices or list (self .mesh .vertices ())
377+ lines = []
378+ for vertex in vertices :
379+ a = self .mesh .vertex_coordinates (vertex )
380+ n = self .mesh .vertex_normal (vertex )
381+ b = add_vectors (a , scale_vector (n , scale ))
382+ lines .append ({
383+ 'start' : a ,
384+ 'end' : b ,
385+ 'color' : color ,
386+ 'name' : "{}.vertexnormal.{}" .format (self .mesh .name , vertex )
387+ })
388+ objects = compas_blender .draw_lines (lines , collection = self .vertexnormalcollection )
389+ self .object_vertexnormal = zip (objects , vertices )
390+ return objects
391+
392+ def draw_facenormals (self , faces = None , color = (0. , 1. , 1. ), scale = 1.0 ):
393+ """Draw the normals of the faces.
394+
395+ Parameters
396+ ----------
397+ faces : list, optional
398+ A selection of face normals to draw.
399+ Default is to draw all face normals.
400+ color : tuple, optional
401+ The color specification of the normal vectors.
402+ The default color is cyan, ``(0., 1., 1.)``.
403+ scale : float, optional
404+ Scale factor for the face normals.
405+ Default is ``1.0``.
406+
407+ Returns
408+ -------
409+ list of :class:`bpy.types.Object`
410+ """
411+ faces = faces or list (self .mesh .faces ())
412+ lines = []
413+ for face in faces :
414+ a = centroid_points (
415+ [self .mesh .vertex_coordinates (vertex ) for vertex in self .mesh .face_vertices (face )]
416+ )
417+ n = self .mesh .face_normal (face )
418+ b = add_vectors (a , scale_vector (n , scale ))
419+ lines .append ({
420+ 'start' : a ,
421+ 'end' : b ,
422+ 'name' : "{}.facenormal.{}" .format (self .mesh .name , face ),
423+ 'color' : color
424+ })
425+ objects = compas_blender .draw_lines (lines , collection = self .facenormalcollection )
426+ self .object_facenormal = zip (objects , faces )
427+ return objects
428+
429+ # ==========================================================================
430+ # draw labels
431+ # ==========================================================================
432+
433+ def draw_vertexlabels (self , text = None , color = None ):
434+ """Draw labels for a selection vertices.
435+
436+ Parameters
437+ ----------
438+ text : dict, optional
439+ A dictionary of vertex labels as vertex-text pairs.
440+ The default value is ``None``, in which case every vertex will be labelled with its key.
441+ color : tuple or dict of tuple, optional
442+ The color specification of the labels.
443+ The default color is the same as the default vertex color.
444+
445+ Returns
446+ -------
447+ list of :class:`bpy.types.Object`
448+ """
449+ if not text or text == 'key' :
450+ vertex_text = {vertex : str (vertex ) for vertex in self .mesh .vertices ()}
451+ elif text == 'index' :
452+ vertex_text = {vertex : str (index ) for index , vertex in enumerate (self .mesh .vertices ())}
453+ elif isinstance (text , dict ):
454+ vertex_text = text
455+ else :
456+ raise NotImplementedError
457+ vertex_color = color or self .color_vertices
458+ labels = []
459+ for vertex in vertex_text :
460+ labels .append ({
461+ 'pos' : self .mesh .vertex_coordinates (vertex ),
462+ 'name' : "{}.vertexlabel.{}" .format (self .mesh .name , vertex ),
463+ 'text' : vertex_text [vertex ]})
464+ objects = compas_blender .draw_texts (labels , collection = self .vertexlabelcollection , color = vertex_color )
465+ self .object_vertexlabel = zip (objects , vertex_text )
466+ return objects
467+
468+ def draw_edgelabels (self , text = None , color = None ):
469+ """Draw labels for a selection of edges.
470+
471+ Parameters
472+ ----------
473+ text : dict, optional
474+ A dictionary of edge labels as edge-text pairs.
475+ The default value is ``None``, in which case every edge will be labelled with its key.
476+ color : tuple or dict of tuple, optional
477+ The color specification of the labels.
478+ The default color is the same as the default color for edges.
479+
480+ Returns
481+ -------
482+ list of :class:`bpy.types.Object`
483+ """
484+ if text is None :
485+ edge_text = {(u , v ): "{}-{}" .format (u , v ) for u , v in self .mesh .edges ()}
486+ elif isinstance (text , dict ):
487+ edge_text = text
488+ else :
489+ raise NotImplementedError
490+ edge_color = color or self .color_edges
491+ labels = []
492+ for edge in edge_text :
493+ labels .append ({
494+ 'pos' : centroid_points (
495+ [self .mesh .vertex_coordinates (edge [0 ]), self .mesh .vertex_coordinates (edge [1 ])]
496+ ),
497+ 'name' : "{}.edgelabel.{}-{}" .format (self .mesh .name , * edge ),
498+ 'text' : edge_text [edge ]})
499+ objects = compas_blender .draw_texts (labels , collection = self .edgelabelcollection , color = edge_color )
500+ self .object_edgelabel = zip (objects , edge_text )
501+ return objects
502+
503+ def draw_facelabels (self , text = None , color = None ):
504+ """Draw labels for a selection of faces.
505+
506+ Parameters
507+ ----------
508+ text : dict, optional
509+ A dictionary of face labels as face-text pairs.
510+ The default value is ``None``, in which case every face will be labelled with its key.
511+ color : tuple or dict of tuple, optional
512+ The color specification of the labels.
513+ The default color is the same as the default face color.
514+
515+ Returns
516+ -------
517+ list of :class:`bpy.types.Object`
518+ """
519+ if not text or text == 'key' :
520+ face_text = {face : str (face ) for face in self .mesh .faces ()}
521+ elif text == 'index' :
522+ face_text = {face : str (index ) for index , face in enumerate (self .mesh .faces ())}
523+ elif isinstance (text , dict ):
524+ face_text = text
525+ else :
526+ raise NotImplementedError
527+ face_color = color or self .color_faces
528+ labels = []
529+ for face in face_text :
530+ labels .append ({
531+ 'pos' : centroid_points (
532+ [self .mesh .vertex_coordinates (vertex ) for vertex in self .mesh .face_vertices (face )]
533+ ),
534+ 'name' : "{}.facelabel.{}" .format (self .mesh .name , face ),
535+ 'text' : face_text [face ]})
536+ objects = compas_blender .draw_texts (labels , collection = self .collection , color = face_color )
537+ self .object_facelabel = zip (objects , face_text )
538+ return objects
0 commit comments