1 # SPDX-FileCopyrightText: 2017 Alessandro Zomparelli
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 #-------------------------- COLORS / GROUPS EXCHANGER -------------------------#
7 # Vertex Color to Vertex Group allow you to convert colors channles to weight #
9 # The main purpose is to use vertex colors to store information when importing #
10 # files from other softwares. The script works with the active vertex color #
12 # For use the command "Vertex Clors to Vertex Groups" use the search bar #
15 # (c) Alessandro Zomparelli #
18 # http://www.co-de-it.com/ #
20 ################################################################################
24 import math
, timeit
, time
26 from mathutils
import Vector
29 from bpy
.types
import (
35 from bpy
.props
import (
48 def anim_contour_curves(self
, context
):
50 props
= ob
.tissue_contour_curves
51 if not (ob
.tissue
.bool_lock
or ob
.tissue
.bool_hold
):
54 bpy
.ops
.object.tissue_update_contour_curves()
57 class tissue_contour_curves_prop(PropertyGroup
):
58 object : PointerProperty(
59 type=bpy
.types
.Object
,
61 description
="Source object",
62 update
= anim_contour_curves
65 use_modifiers
: BoolProperty(
66 name
="Use Modifiers", default
=True,
67 description
="Apply all the modifiers",
68 update
= anim_contour_curves
71 variable_bevel
: BoolProperty(
72 name
="Variable Bevel", default
=False,
73 description
="Variable Bevel",
74 update
= anim_contour_curves
77 min_value
: FloatProperty(
78 name
="Offset Value", default
=0., #soft_min=0, soft_max=1,
79 description
="Offset contouring values",
80 update
= anim_contour_curves
83 range_value
: FloatProperty(
84 name
="Range Values", default
=100, #soft_min=0, soft_max=1,
85 description
="Maximum range of contouring values",
86 update
= anim_contour_curves
89 n_curves
: IntProperty(
90 name
="Curves", default
=1000, soft_min
=1, soft_max
=200,
91 description
="Number of Contour Curves",
92 update
= anim_contour_curves
95 in_displace
: FloatProperty(
96 name
="Displace A", default
=0, soft_min
=-10, soft_max
=10,
97 description
="Pattern displace strength",
98 update
= anim_contour_curves
101 out_displace
: FloatProperty(
102 name
="Displace B", default
=2, soft_min
=-10, soft_max
=10,
103 description
="Pattern displace strength",
104 update
= anim_contour_curves
107 in_steps
: IntProperty(
108 name
="Steps A", default
=1, min=0, soft_max
=10,
109 description
="Number of layers to move inwards",
110 update
= anim_contour_curves
113 out_steps
: IntProperty(
114 name
="Steps B", default
=1, min=0, soft_max
=10,
115 description
="Number of layers to move outwards",
116 update
= anim_contour_curves
119 displace_x
: BoolProperty(
120 name
="Use X", default
=True,
121 description
="Displace along X axis",
122 update
= anim_contour_curves
125 displace_y
: BoolProperty(
126 name
="Use Y", default
=True,
127 description
="Displace along Y axis",
128 update
= anim_contour_curves
131 displace_z
: BoolProperty(
132 name
="Use Z", default
=True,
133 description
="Displace along Z axis",
134 update
= anim_contour_curves
137 merge
: BoolProperty(
138 name
="Merge Vertices", default
=True,
139 description
="Merge points",
140 update
= anim_contour_curves
143 merge_thres
: FloatProperty(
144 name
="Merge Threshold", default
=0.01, min=0, soft_max
=1,
145 description
="Minimum Curve Radius",
146 update
= anim_contour_curves
149 bevel_depth
: FloatProperty(
150 name
="Bevel Depth", default
=0, min=0, soft_max
=1,
152 update
= anim_contour_curves
155 min_bevel_depth
: FloatProperty(
156 name
="Min Bevel Depth", default
=0.05, min=0, soft_max
=1,
158 update
= anim_contour_curves
161 max_bevel_depth
: FloatProperty(
162 name
="Max Bevel Depth", default
=0.20, min=0, soft_max
=1,
164 update
= anim_contour_curves
167 remove_open_curves
: BoolProperty(
168 name
="Remove Open Curves", default
=False,
169 description
="Remove Open Curves",
170 update
= anim_contour_curves
173 vertex_group_pattern
: StringProperty(
174 name
="Displace", default
='',
175 description
="Vertex Group used for pattern displace",
176 update
= anim_contour_curves
179 vertex_group_bevel
: StringProperty(
180 name
="Bevel", default
='',
181 description
="Variable Bevel depth",
182 update
= anim_contour_curves
185 object_name
: StringProperty(
186 name
="Active Object", default
='',
188 update
= anim_contour_curves
191 vertex_group_contour
: StringProperty(
192 name
="Contour", default
="",
193 description
="Vertex Group used for contouring",
194 update
= anim_contour_curves
197 clean_distance
: FloatProperty(
198 name
="Clean Distance", default
=0.005, min=0, soft_max
=10,
199 description
="Remove short segments",
200 update
= anim_contour_curves
203 spiralized
: BoolProperty(
204 name
='Spiralized', default
=False,
205 description
='Create a Spiral Contour. Works better with dense meshes.',
206 update
= anim_contour_curves
209 spiral_axis
: FloatVectorProperty(
210 name
="Spiral Axis", default
=(0,0,1),
211 description
="Axis of the Spiral (in local coordinates)",
212 update
= anim_contour_curves
215 spiral_rotation
: FloatProperty(
216 name
="Spiral Rotation", default
=0, min=0, max=2*pi
,
218 update
= anim_contour_curves
221 contour_mode
: EnumProperty(
223 ('VECTOR', "Vector", "Orient the Contour to a given vector starting from the origin of the object"),
224 ('OBJECT', "Object", "Orient the Contour to a target object's Z"),
225 ('WEIGHT', "Weight", "Contour based on a Vertex Group"),
226 ('ATTRIBUTE', "Attribute", "Contour based on an Attribute (Vertex > Float)"),
227 ('GEODESIC', "Geodesic Distance", "Contour based on the geodesic distance from the chosen vertices"),
228 ('TOPOLOGY', "Topology Distance", "Contour based on the topology distance from the chosen vertices")
231 name
="Mode used for the Contour Curves",
232 update
= anim_contour_curves
235 contour_vector
: FloatVectorProperty(
236 name
='Vector', description
='Constant Vector', default
=(0.0, 0.0, 1.0),
237 update
= anim_contour_curves
240 contour_vector_object
: PointerProperty(
241 type=bpy
.types
.Object
,
243 description
="Target Object",
244 update
= anim_contour_curves
247 contour_offset
: FloatProperty(
248 name
="Offset", default
=0.05, min=0.000001, soft_min
=0.01, soft_max
=10,
249 description
="Contour offset along the Vector",
250 update
= anim_contour_curves
253 seeds_mode
: EnumProperty(
255 ('BOUND', "Boundary Edges", "Compute the distance starting from the boundary edges"),
256 ('SHARP', "Sharp Edges", "Compute the distance starting from the sharp edges"),
257 ('WEIGHT', "Weight", "Compute the distance starting from the selected vertex group")
260 name
="Seeds used for computing the distance",
261 update
= anim_contour_curves
264 vertex_group_seed
: StringProperty(
265 name
="Seeds", default
="",
266 description
="Vertex Group used for computing the distance",
267 update
= anim_contour_curves
270 spline_type
: EnumProperty(
272 ('POLY', "Poly", "Generate Poly curves"),
273 ('NURBS', "NURBS", "Generate NURBS curves")
277 update
= anim_contour_curves
280 contour_attribute
: StringProperty(
281 name
="Contour Attribute", default
='',
282 description
="Vertex > Float attribute used for contouring",
283 update
= anim_contour_curves
287 class tissue_weight_contour_curves_pattern(Operator
):
288 bl_idname
= "object.tissue_weight_contour_curves_pattern"
289 bl_label
= "Contour Curves"
290 bl_description
= ("")
291 bl_options
= {'REGISTER', 'UNDO'}
293 object : StringProperty(
295 description
="Source object",
299 use_modifiers
: BoolProperty(
300 name
="Use Modifiers", default
=True,
301 description
="Apply all the modifiers"
304 variable_bevel
: BoolProperty(
305 name
="Variable Bevel", default
=False,
306 description
="Variable Bevel"
309 min_value
: FloatProperty(
310 name
="Offset Value", default
=0.,
311 description
="Offset contouring values"
314 range_value
: FloatProperty(
315 name
="Range Values", default
=100,
316 description
="Maximum range of contouring values"
319 n_curves
: IntProperty(
320 name
="Curves", default
=1000, soft_min
=1, soft_max
=200,
321 description
="Number of Contour Curves"
327 in_displace
: FloatProperty(
328 name
="Displace A", default
=0, soft_min
=-10, soft_max
=10,
329 description
="Pattern displace strength"
332 out_displace
: FloatProperty(
333 name
="Displace B", default
=2, soft_min
=-10, soft_max
=10,
334 description
="Pattern displace strength"
337 in_steps
: IntProperty(
338 name
="Steps A", default
=1, min=0, soft_max
=10,
339 description
="Number of layers to move inwards"
342 out_steps
: IntProperty(
343 name
="Steps B", default
=1, min=0, soft_max
=10,
344 description
="Number of layers to move outwards"
347 displace_x
: BoolProperty(
348 name
="Use X", default
=True,
349 description
="Displace along X axis"
352 displace_y
: BoolProperty(
353 name
="Use Y", default
=True,
354 description
="Displace along Y axis"
357 displace_z
: BoolProperty(
358 name
="Use Z", default
=True,
359 description
="Displace along Z axis"
362 merge
: BoolProperty(
363 name
="Merge Vertices", default
=True,
364 description
="Merge points"
367 merge_thres
: FloatProperty(
368 name
="Merge Threshold", default
=0.01, min=0, soft_max
=1,
369 description
="Minimum Curve Radius"
372 bevel_depth
: FloatProperty(
373 name
="Bevel Depth", default
=0, min=0, soft_max
=1,
377 min_bevel_depth
: FloatProperty(
378 name
="Min Bevel Depth", default
=0.05, min=0, soft_max
=1,
382 max_bevel_depth
: FloatProperty(
383 name
="Max Bevel Depth", default
=0.20, min=0, soft_max
=1,
387 remove_open_curves
: BoolProperty(
388 name
="Remove Open Curves", default
=False,
389 description
="Remove Open Curves"
392 vertex_group_pattern
: StringProperty(
393 name
="Displace", default
='',
394 description
="Vertex Group used for pattern displace"
397 vertex_group_bevel
: StringProperty(
398 name
="Bevel", default
='',
399 description
="Variable Bevel depth"
402 object_name
: StringProperty(
403 name
="Active Object", default
='',
407 contour_attribute
: StringProperty(
408 name
="Contour Attribute", default
='',
409 description
="Vertex > Float attribute used for contouring"
412 try: vg_name
= bpy
.context
.object.vertex_groups
.active
.name
415 vertex_group_contour
: StringProperty(
416 name
="Contour", default
=vg_name
,
417 description
="Vertex Group used for contouring"
420 clean_distance
: FloatProperty(
421 name
="Clean Distance", default
=0.005, min=0, soft_max
=10,
422 description
="Remove short segments"
425 spiralized
: BoolProperty(
426 name
='Spiralized', default
=False,
427 description
='Create a Spiral Contour. Works better with dense meshes.'
430 spiral_axis
: FloatVectorProperty(
431 name
="Spiral Axis", default
=(0,0,1),
432 description
="Axis of the Spiral (in local coordinates)"
435 spiral_rotation
: FloatProperty(
436 name
="Spiral Rotation", default
=0, min=0, max=2*pi
,
440 bool_hold
: BoolProperty(
442 description
="Wait...",
446 contour_mode
: EnumProperty(
448 ('VECTOR', "Vector", "Orient the Contour to a given vector starting from the origin of the object"),
449 ('OBJECT', "Object", "Orient the Contour to a target object's Z"),
450 ('WEIGHT', "Weight", "Contour based on a Vertex Group"),
451 ('ATTRIBUTE', "Attribute", "Contour based on an Attribute (Vertex > Float)"),
452 ('GEODESIC', "Geodesic Distance", "Contour based on the geodesic distance from the chosen vertices"),
453 ('TOPOLOGY', "Topology Distance", "Contour based on the topology distance from the chosen vertices")
456 name
="Mode used for the Contour Curves"
459 contour_vector
: FloatVectorProperty(
460 name
='Vector', description
='Constant Vector', default
=(0.0, 0.0, 1.0)
463 contour_vector_object
: StringProperty(
465 description
="Target object",
469 contour_offset
: FloatProperty(
470 name
="Offset", default
=0.05, min=0.000001, soft_min
=0.01, soft_max
=10,
471 description
="Contour offset along the Vector"
474 seeds_mode
: EnumProperty(
476 ('BOUND', "Boundary Edges", "Compute the distance starting from the boundary edges"),
477 ('SHARP', "Sharp Edges", "Compute the distance starting from the sharp edges"),
478 ('WEIGHT', "Weight", "Compute the distance starting from the selected vertex group")
481 name
="Seeds used for computing the distance"
484 vertex_group_seed
: StringProperty(
485 name
="Seeds", default
=vg_name
,
486 description
="Vertex Group used for computing the distance"
489 spline_type
: EnumProperty(
491 ('POLY', "Poly", "Generate Poly curves"),
492 ('NURBS', "NURBS", "Generate NURBS curves")
498 def invoke(self
, context
, event
):
499 self
.object = context
.object.name
500 return context
.window_manager
.invoke_props_dialog(self
, width
=250)
502 def draw(self
, context
):
504 ob0
= bpy
.data
.objects
[self
.object]
506 if self
.contour_mode
== 'WEIGHT':
508 if self
.vertex_group_contour
not in [vg
.name
for vg
in ob
.vertex_groups
]:
509 self
.vertex_group_contour
= ob
.vertex_groups
.active
.name
511 self
.contour_mode
== 'VECTOR'
513 if not self
.bool_hold
:
514 self
.object = ob
.name
515 self
.bool_hold
= True
518 col
= layout
.column(align
=True)
519 col
.prop(self
, "use_modifiers")
520 col
.label(text
="Contour Curves:")
523 row
.prop(self
, "spline_type", icon
='NONE', expand
=True,
524 slider
=True, toggle
=False, icon_only
=False, event
=False,
525 full_event
=False, emboss
=True, index
=-1)
527 col
.prop(self
, "contour_mode", text
="Mode")
529 if self
.contour_mode
== 'VECTOR':
531 row
.prop(self
,'contour_vector')
532 elif self
.contour_mode
== 'WEIGHT':
533 col
.prop_search(self
, 'vertex_group_contour', ob
, "vertex_groups", text
='Group')
534 elif self
.contour_mode
== 'ATTRIBUTE':
535 col
.prop_search(self
, 'contour_attribute', ob0
.data
, "attributes", text
='Attribute')
537 if self
.contour_attribute
in ob0
.data
.attributes
:
538 attr
= ob0
.data
.attributes
[self
.contour_attribute
]
539 is_attribute
= attr
.data_type
== 'FLOAT' and attr
.domain
== 'POINT'
543 col
.label(text
="Please select a (Vertex > Float) Attribute for contouring.", icon
='ERROR')
544 elif self
.contour_mode
in ('TOPOLOGY','GEODESIC'):
545 col
.prop(self
, "seeds_mode", text
="Seeds")
546 if self
.seeds_mode
== 'WEIGHT':
547 col
.prop_search(self
, 'vertex_group_seed', ob
, "vertex_groups", text
='Group')
548 elif self
.contour_mode
== 'OBJECT':
549 col
.prop_search(self
, "contour_vector_object", context
.scene
, "objects", text
='Object')
552 if self
.contour_mode
== 'OBJECT':
553 col
.prop(self
,'contour_offset')
554 col
.prop(self
,'n_curves', text
='Max Curves')
555 elif self
.contour_mode
in ('VECTOR', 'GEODESIC', 'ATTRIBUTE'):
556 col
.prop(self
,'contour_offset')
557 row
= col
.row(align
=True)
558 row
.prop(self
,'min_value')
559 row
.prop(self
,'range_value')
560 col
.prop(self
,'n_curves', text
='Max Curves')
561 elif self
.contour_mode
in ('TOPOLOGY', 'WEIGHT'):
562 row
= col
.row(align
=True)
563 row
.prop(self
,'min_value')
564 row
.prop(self
,'range_value')
565 col
.prop(self
,'n_curves')
568 col
.label(text
='Curves Bevel:')
569 col
.prop(self
,'variable_bevel')
570 row
= col
.row(align
=True)
571 row
.prop(self
,'min_bevel_depth')
572 row
.prop(self
,'max_bevel_depth')
573 row2
= col
.row(align
=True)
574 row2
.prop_search(self
, 'vertex_group_bevel', ob
, "vertex_groups", text
='')
575 if not self
.variable_bevel
:
576 row
.enabled
= row2
.enabled
= False
579 col
.label(text
="Displace Pattern:")
580 col
.prop_search(self
, 'vertex_group_pattern', ob
, "vertex_groups", text
='')
581 if self
.vertex_group_pattern
!= '':
583 row
= col
.row(align
=True)
584 row
.prop(self
,'in_steps')
585 row
.prop(self
,'out_steps')
586 row
= col
.row(align
=True)
587 row
.prop(self
,'in_displace')
588 row
.prop(self
,'out_displace')
590 row
= col
.row(align
=True)
591 row
.label(text
="Axis")
592 row
.prop(self
,'displace_x', text
="X", toggle
=1)
593 row
.prop(self
,'displace_y', text
="Y", toggle
=1)
594 row
.prop(self
,'displace_z', text
="Z", toggle
=1)
597 col
.label(text
='Clean Curves:')
598 col
.prop(self
,'clean_distance')
599 col
.prop(self
,'remove_open_curves')
601 def execute(self
, context
):
602 ob0
= bpy
.context
.object
604 self
.object_name
= "Contour Curves"
605 # Check if existing object with same name
606 names
= [o
.name
for o
in bpy
.data
.objects
]
607 if self
.object_name
in names
:
610 test_name
= self
.object_name
+ '.{:03d}'.format(count_name
)
611 if not (test_name
in names
):
612 self
.object_name
= test_name
616 if bpy
.ops
.object.select_all
.poll():
617 bpy
.ops
.object.select_all(action
='DESELECT')
618 bpy
.ops
.object.mode_set(mode
='OBJECT')
621 if context
.object == ob0
:
622 auto_layer_collection()
623 curve
= bpy
.data
.curves
.new(self
.object_name
,'CURVE')
624 new_ob
= bpy
.data
.objects
.new(self
.object_name
,curve
)
625 bpy
.context
.collection
.objects
.link(new_ob
)
626 bpy
.context
.view_layer
.objects
.active
= new_ob
627 if bpy
.ops
.object.select_all
.poll():
628 bpy
.ops
.object.select_all(action
='DESELECT')
629 bpy
.ops
.object.mode_set(mode
='OBJECT')
630 new_ob
.select_set(True)
632 new_ob
= context
.object
636 props
= new_ob
.tissue_contour_curves
637 new_ob
.tissue
.bool_hold
= True
638 if self
.object in bpy
.data
.objects
.keys():
639 props
.object = bpy
.data
.objects
[self
.object]
640 props
.use_modifiers
= self
.use_modifiers
641 props
.variable_bevel
= self
.variable_bevel
642 props
.min_value
= self
.min_value
643 props
.range_value
= self
.range_value
644 props
.n_curves
= self
.n_curves
645 props
.in_displace
= self
.in_displace
646 props
.out_displace
= self
.out_displace
647 props
.in_steps
= self
.in_steps
648 props
.out_steps
= self
.out_steps
649 props
.displace_x
= self
.displace_x
650 props
.displace_y
= self
.displace_y
651 props
.displace_z
= self
.displace_z
652 props
.merge
= self
.merge
653 props
.merge_thres
= self
.merge_thres
654 props
.bevel_depth
= self
.bevel_depth
655 props
.min_bevel_depth
= self
.min_bevel_depth
656 props
.max_bevel_depth
= self
.max_bevel_depth
657 props
.remove_open_curves
= self
.remove_open_curves
658 props
.vertex_group_pattern
= self
.vertex_group_pattern
659 props
.vertex_group_bevel
= self
.vertex_group_bevel
660 props
.object_name
= self
.object_name
661 props
.vertex_group_contour
= self
.vertex_group_contour
662 props
.clean_distance
= self
.clean_distance
663 props
.spiralized
= self
.spiralized
664 props
.spiral_axis
= self
.spiral_axis
665 props
.spiral_rotation
= self
.spiral_rotation
666 props
.contour_mode
= self
.contour_mode
667 if self
.contour_vector_object
in bpy
.data
.objects
.keys():
668 props
.contour_vector_object
= bpy
.data
.objects
[self
.contour_vector_object
]
669 props
.contour_vector
= self
.contour_vector
670 props
.contour_offset
= self
.contour_offset
671 props
.seeds_mode
= self
.seeds_mode
672 props
.vertex_group_seed
= self
.vertex_group_seed
673 props
.spline_type
= self
.spline_type
674 props
.contour_attribute
= self
.contour_attribute
675 new_ob
.tissue
.bool_hold
= False
677 new_ob
.tissue
.tissue_type
= 'CONTOUR_CURVES'
678 try: bpy
.ops
.object.tissue_update_contour_curves()
679 except RuntimeError as e
:
681 bpy
.data
.objects
.remove(new_ob
)
682 remove_temp_objects()
683 self
.report({'ERROR'}, str(e
))
686 self
.object_name
= new_ob
.name
687 #self.working_on = self.object_name
688 new_ob
.location
= ob0
.location
689 new_ob
.matrix_world
= ob0
.matrix_world
691 # Assign collection of the base object
692 old_coll
= new_ob
.users_collection
693 if old_coll
!= ob0
.users_collection
:
695 c
.objects
.unlink(new_ob
)
696 for c
in ob0
.users_collection
:
697 c
.objects
.link(new_ob
)
698 context
.view_layer
.objects
.active
= new_ob
702 class tissue_update_contour_curves(Operator
):
703 bl_idname
= "object.tissue_update_contour_curves"
704 bl_label
= "Update Contour Curves"
705 bl_description
= ("Update a previously generated Contour Curves object")
706 bl_options
= {'REGISTER', 'UNDO'}
708 def execute(self
, context
):
710 props
= ob
.tissue_contour_curves
712 n_curves
= props
.n_curves
715 tissue_time(None,'Tissue: Contour Curves of "{}"...'.format(ob
.name
), levels
=0)
717 ob0
= convert_object_to_mesh(_ob0
, apply_modifiers
=props
.use_modifiers
)
718 ob0
.name
= "_tissue_tmp_ob0"
724 n_verts
= len(bm
.verts
)
725 vertices
, normals
= get_vertices_and_normals_numpy(me0
)
727 if props
.contour_mode
== 'OBJECT':
729 vec_ob
= props
.contour_vector_object
730 vec_ob_name
= vec_ob
.name
733 bpy
.data
.objects
.remove(ob0
)
734 self
.report({'ERROR'}, "Please select an target Object")
737 tt1
= tissue_time(tt1
, "Load objects", levels
=1)
739 # store weight values
740 if props
.contour_mode
in ('VECTOR','OBJECT'):
741 ob0_matrix
= np
.matrix(ob0
.matrix_world
.to_3x3().transposed())
742 global_verts
= np
.matmul(vertices
,ob0_matrix
)
743 global_verts
+= np
.array(ob0
.matrix_world
.translation
)
744 if props
.contour_mode
== 'OBJECT' and props
.contour_vector_object
:
745 vec_ob
= props
.contour_vector_object
746 global_verts
-= np
.array(vec_ob
.matrix_world
.translation
)
747 vec_ob_matrix
= np
.matrix(vec_ob
.matrix_world
.to_3x3().inverted().transposed())
748 global_verts
= np
.matmul(global_verts
,vec_ob_matrix
)
749 weight
= global_verts
[:,2].A1
750 elif props
.contour_mode
== 'VECTOR':
751 vec
= np
.array(props
.contour_vector
)
752 vec_len
= np
.linalg
.norm(vec
)
754 vec
= np
.array((0,0,1))
759 global_verts
= global_verts
.A
760 projected_verts
= global_verts
* vec
761 projected_verts
= np
.sum(projected_verts
,axis
=1)[:,np
.newaxis
]
762 weight
= projected_verts
.reshape((-1))
763 elif props
.contour_mode
== 'WEIGHT':
765 weight
= get_weight_numpy(ob0
.vertex_groups
[props
.vertex_group_contour
], len(me0
.vertices
))
768 bpy
.data
.objects
.remove(ob0
)
769 self
.report({'ERROR'}, "Please select a Vertex Group for contouring")
771 elif props
.contour_mode
== 'ATTRIBUTE':
772 if props
.contour_attribute
in me0
.attributes
:
774 me0
.attributes
[props
.contour_attribute
].data
.foreach_get('value',weight
)
775 weight
= np
.array(weight
)
778 bpy
.data
.objects
.remove(ob0
)
779 self
.report({'ERROR'}, "Please select a (Vertex > Float) Attribute for contouring")
781 elif props
.contour_mode
in ('GEODESIC','TOPOLOGY'):
783 weight
= [None]*n_verts
785 bm
.verts
.ensure_lookup_table()
786 if props
.seeds_mode
== 'BOUND':
791 if props
.seeds_mode
== 'SHARP':
792 for e
, bme
in zip(me0
.edges
, bm
.edges
):
794 seed_verts
.append(bme
.verts
[0])
795 seed_verts
.append(bme
.verts
[1])
796 seed_verts
= list(set(seed_verts
))
797 if len(seed_verts
) == 0: cancel
= True
798 for i
in [v
.index
for v
in seed_verts
]:
800 if props
.seeds_mode
== 'WEIGHT':
802 seeds
= get_weight_numpy(ob0
.vertex_groups
[props
.vertex_group_seed
], len(me0
.vertices
))
805 bpy
.data
.objects
.remove(ob0
)
806 self
.report({'ERROR'}, "Please select a Vertex Group as seed")
808 for i
,v
in enumerate(bm
.verts
):
809 if seeds
[i
]>0.999999:
812 if cancel
or len(seed_verts
)==0:
814 bpy
.data
.objects
.remove(ob0
)
815 self
.report({'ERROR'}, "No seed vertices found")
818 weight
= fill_neighbors_attribute(seed_verts
, weight
, props
.contour_mode
)
819 weight
= np
.array(weight
)
820 print(weight
[weight
==None])
821 weight
[weight
==None] = 0
822 print(weight
[weight
==None])
825 pattern_weight
= get_weight_numpy(ob0
.vertex_groups
[props
.vertex_group_pattern
], len(me0
.vertices
))
827 #self.report({'WARNING'}, "There is no Vertex Group assigned to the pattern displace")
828 pattern_weight
= np
.zeros(len(me0
.vertices
))
831 if props
.variable_bevel
:
833 bevel_weight
= get_weight_numpy(ob0
.vertex_groups
[props
.vertex_group_bevel
], len(me0
.vertices
))
836 bevel_weight
= np
.ones(len(me0
.vertices
))
838 bevel_weight
= np
.ones(len(me0
.vertices
))
840 total_verts
= np
.zeros((0,3))
841 total_radii
= np
.zeros((0,1))
842 total_edges_index
= np
.zeros((0)).astype('int')
843 total_segments
= []# np.array([])
846 tt1
= tissue_time(tt1
, "Compute values", levels
=1)
848 # start iterate contours levels
849 filtered_edges
= get_edges_id_numpy(me0
)
851 min_value
= props
.min_value
852 max_value
= props
.min_value
+ props
.range_value
854 if props
.contour_mode
in ('VECTOR','OBJECT','GEODESIC','ATTRIBUTE'):
855 delta_iso
= props
.contour_offset
856 n_curves
= min(int((np
.max(weight
)-props
.min_value
)/delta_iso
)+1, props
.n_curves
)
859 delta_iso
= props
.range_value
/2
861 delta_iso
= props
.range_value
/(n_curves
-1)
862 if props
.contour_mode
== 'TOPOLOGY':
863 weight
= weight
/np
.max(weight
)
866 edges_verts
= get_attribute_numpy(me0
.edges
,"vertices",mult
=2).astype('int')
867 edges_vec
= vertices
[edges_verts
[:,0]]-vertices
[edges_verts
[:,1]]
868 #edges_vec = global_verts[edges_verts[:,0]]-global_verts[edges_verts[:,1]]
869 edges_length
= np
.linalg
.norm(edges_vec
,axis
=1)
870 edges_vec
/= edges_length
[:,np
.newaxis
]
871 edges_dw
= np
.abs(weight
[edges_verts
[:,0]]-weight
[edges_verts
[:,1]])
872 edges_bevel
= delta_iso
*edges_length
/edges_dw
/2*0 + 1
876 faces_n_verts = get_attribute_numpy(me0.polygons, attribute='loop_total').astype('int')
877 faces_verts = get_attribute_numpy(me0.polygons, attribute='vertices', size=np.sum(faces_n_verts)).astype('int')
878 faces_weight = weight[faces_verts]
879 faces_weight = np.split(faces_weight, np.cumsum(faces_n_verts)[:-1])
881 faces_weight
= [np
.array([weight
[v
] for v
in p
.vertices
]) for p
in me0
.polygons
]
883 fw_min
= np
.min(faces_weight
, axis
=1)
884 fw_max
= np
.max(faces_weight
, axis
=1)
886 # necessary for irregular meshes
887 fw_min
= np
.array([min(fw
) for fw
in faces_weight
])
888 fw_max
= np
.array([max(fw
) for fw
in faces_weight
])
889 bm_faces
= np
.array(bm
.faces
)
891 tt1
= tissue_time(tt1
, "Compute face values", levels
=1)
892 for c
in range(n_curves
):
894 iso_val
= c
*delta_iso
+ min_value
896 iso_val
= min_value
+ range_value
/2
897 if iso_val
> max_value
: break
899 # remove passed faces
900 bool_mask
= iso_val
<= fw_max
901 bm_faces
= bm_faces
[bool_mask
]
902 fw_min
= fw_min
[bool_mask
]
903 fw_max
= fw_max
[bool_mask
]
906 bool_mask
= fw_min
<= iso_val
907 faces_mask
= bm_faces
[bool_mask
]
909 count
= len(total_verts
)
911 if not weight_bevel
and props
.variable_bevel
:
912 bevel_weight
= np
.full(n_verts
, c
/n_curves
)
913 new_filtered_edges
, edges_index
, verts
, bevel
= contour_edges_pattern(props
, c
, len(total_verts
), iso_val
, vertices
, normals
, filtered_edges
, weight
, pattern_weight
, bevel_weight
)
914 #bevel = edges_bevel[edges_index][:,np.newaxis]
916 if len(edges_index
) > 0:
917 if props
.variable_bevel
and props
.max_bevel_depth
!= props
.min_bevel_depth
and False:
918 #min_radius = min(props.min_bevel_depth, props.max_bevel_depth)
919 #max_radius = max(props.min_bevel_depth, props.max_bevel_depth)
920 min_radius
= props
.min_bevel_depth
921 max_radius
= props
.max_bevel_depth
922 min_radius
= min_radius
/ max(0.0001,max_radius
)
923 radii
= min_radius
+ bevel
*(1 - min_radius
)
929 if verts
[0,0] == None: continue
930 else: filtered_edges
= new_filtered_edges
932 for i
, id in enumerate(edges_index
): edges_id
[id] = i
+ count
934 if len(verts
) == 0: continue
942 #seg.append(new_ids[np.where(edges_index == e.index)[0][0]])
943 seg
.append(edges_id
[e
.index
])
949 total_segments
= total_segments
+ segments
950 total_verts
= np
.concatenate((total_verts
, verts
))
951 total_radii
= np
.concatenate((total_radii
, radii
))
952 total_edges_index
= np
.concatenate((total_edges_index
, edges_index
))
953 tt1
= tissue_time(tt1
, "Compute curves", levels
=1)
955 if len(total_segments
) > 0:
956 ordered_points
, ordered_points_edge_id
= find_curves_attribute(total_segments
, len(total_verts
), total_edges_index
)
958 total_tangents
= np
.zeros((len(total_verts
),3))
959 for curve
in ordered_points
:
960 np_curve
= np
.array(curve
).astype('int')
961 curve_pts
= np
.array(total_verts
[np_curve
], dtype
=np
.float64
)
962 tangents
= np
.roll(curve_pts
,1) - np
.roll(curve_pts
,-1)
963 tangents
/= np
.linalg
.norm(tangents
,axis
=1)[:,np
.newaxis
]
964 total_tangents
[curve
] = tangents
966 step_time
= timeit
.default_timer()
967 ob
.data
.splines
.clear()
968 if props
.variable_bevel
:# and not weight_bevel:
969 total_radii
= np
.interp(total_radii
, (total_radii
.min(), total_radii
.max()), (props
.min_bevel_depth
, props
.max_bevel_depth
))
970 ob
.data
= curve_from_pydata(total_verts
, total_radii
, ordered_points
, ob0
.name
+ '_ContourCurves', props
.remove_open_curves
, merge_distance
=props
.clean_distance
, only_data
=True, curve
=ob
.data
, spline_type
=props
.spline_type
)
971 #context.view_layer.objects.active = crv
972 if props
.variable_bevel
:
974 ob
.data
.bevel_depth
= 1
976 ob
.data
.bevel_depth
= max(props
.max_bevel_depth
, props
.min_bevel_depth
)
977 tt1
= tissue_time(tt1
, "Store curves data", levels
=1)
979 ob
.data
.splines
.clear()
982 for o
in bpy
.data
.objects
:
983 if '_tissue_tmp_' in o
.name
:
984 bpy
.data
.objects
.remove(o
)
986 tt0
= tissue_time(tt0
, "Contour Curves", levels
=0)
990 class TISSUE_PT_contour_curves(Panel
):
991 bl_space_type
= 'PROPERTIES'
992 bl_region_type
= 'WINDOW'
994 bl_label
= "Tissue Contour Curves"
995 bl_options
= {'DEFAULT_CLOSED'}
998 def poll(cls
, context
):
1000 #bool_curve = context.object.tissue_to_curve.object != None
1002 return ob
.type == 'CURVE' and ob
.tissue
.tissue_type
== 'CONTOUR_CURVES'
1006 def draw(self
, context
):
1008 props
= ob
.tissue_contour_curves
1009 ob0
= bpy
.data
.objects
[props
.object.name
]
1011 layout
= self
.layout
1012 #layout.use_property_split = True
1013 #layout.use_property_decorate = False
1014 col
= layout
.column(align
=True)
1015 row
= col
.row(align
=True)
1016 #col.operator("object.tissue_update_convert_to_curve", icon='FILE_REFRESH', text='Refresh')
1017 row
.operator("object.tissue_update_tessellate_deps", icon
='FILE_REFRESH', text
='Refresh') ####
1018 lock_icon
= 'LOCKED' if ob
.tissue
.bool_lock
else 'UNLOCKED'
1019 #lock_icon = 'PINNED' if props.bool_lock else 'UNPINNED'
1020 deps_icon
= 'LINKED' if ob
.tissue
.bool_dependencies
else 'UNLINKED'
1021 row
.prop(ob
.tissue
, "bool_dependencies", text
="", icon
=deps_icon
)
1022 row
.prop(ob
.tissue
, "bool_lock", text
="", icon
=lock_icon
)
1023 col2
= row
.column(align
=True)
1024 col2
.prop(ob
.tissue
, "bool_run", text
="",icon
='TIME')
1025 col2
.enabled
= not ob
.tissue
.bool_lock
1026 col2
= row
.column(align
=True)
1027 col2
.operator("mesh.tissue_remove", text
="", icon
='X')
1030 row
= col
.row(align
=True)
1031 row
.prop_search(props
, "object", context
.scene
, "objects", text
="")
1032 row
.prop(props
, "use_modifiers", icon
='MODIFIER', text
='')
1034 col
.label(text
="Contour Curves:")
1036 row
.prop(props
, "spline_type", icon
='NONE', expand
=True,
1037 slider
=True, toggle
=False, icon_only
=False, event
=False,
1038 full_event
=False, emboss
=True, index
=-1)
1040 col
.prop(props
, "contour_mode", text
="Mode")
1042 if props
.contour_mode
== 'VECTOR':
1044 row
.prop(props
,'contour_vector')
1045 elif props
.contour_mode
== 'WEIGHT':
1046 col
.prop_search(props
, 'vertex_group_contour', ob0
, "vertex_groups", text
='Group')
1047 elif props
.contour_mode
== 'ATTRIBUTE':
1048 col
.prop_search(props
, 'contour_attribute', ob0
.data
, "attributes", text
='Attribute')
1050 if props
.contour_attribute
in ob0
.data
.attributes
:
1051 attr
= ob0
.data
.attributes
[props
.contour_attribute
]
1052 is_attribute
= attr
.data_type
== 'FLOAT' and attr
.domain
== 'POINT'
1054 is_attribute
= False
1055 if not is_attribute
:
1056 col
.label(text
="Please select a (Vertex > Float) Attribute for contouring.", icon
='ERROR')
1057 elif props
.contour_mode
in ('TOPOLOGY','GEODESIC'):
1058 col
.prop(props
, "seeds_mode", text
="Seeds")
1059 if props
.seeds_mode
== 'WEIGHT':
1060 col
.prop_search(props
, 'vertex_group_seed', ob0
, "vertex_groups", text
='Group')
1061 elif props
.contour_mode
== 'OBJECT':
1062 col
.prop_search(props
, "contour_vector_object", context
.scene
, "objects", text
='Object')
1065 if props
.contour_mode
== 'OBJECT':
1066 col
.prop(props
,'contour_offset')
1067 col
.prop(props
,'n_curves', text
='Max Curves')
1068 elif props
.contour_mode
in ('VECTOR','GEODESIC','ATTRIBUTE'):
1069 col
.prop(props
,'contour_offset')
1070 row
= col
.row(align
=True)
1071 row
.prop(props
,'min_value')
1072 row
.prop(props
,'range_value')
1073 col
.prop(props
,'n_curves', text
='Max Curves')
1074 elif props
.contour_mode
in ('TOPOLOGY', 'WEIGHT'):
1075 row
= col
.row(align
=True)
1076 row
.prop(props
,'min_value')
1077 row
.prop(props
,'range_value')
1078 col
.prop(props
,'n_curves')
1081 col
.label(text
='Curves Bevel:')
1082 col
.prop(props
,'variable_bevel')
1083 row
= col
.row(align
=True)
1084 row
.prop(props
,'min_bevel_depth')
1085 row
.prop(props
,'max_bevel_depth')
1086 row2
= col
.row(align
=True)
1087 row2
.prop_search(props
, 'vertex_group_bevel', ob0
, "vertex_groups", text
='')
1088 if not props
.variable_bevel
:
1089 row
.enabled
= row2
.enabled
= False
1092 col
.label(text
="Displace Pattern:")
1093 col
.prop_search(props
, 'vertex_group_pattern', ob0
, "vertex_groups", text
='')
1094 if props
.vertex_group_pattern
!= '':
1096 row
= col
.row(align
=True)
1097 row
.prop(props
,'in_steps')
1098 row
.prop(props
,'out_steps')
1099 row
= col
.row(align
=True)
1100 row
.prop(props
,'in_displace')
1101 row
.prop(props
,'out_displace')
1103 row
= col
.row(align
=True)
1104 row
.label(text
="Axis")
1105 row
.prop(props
,'displace_x', text
="X", toggle
=1)
1106 row
.prop(props
,'displace_y', text
="Y", toggle
=1)
1107 row
.prop(props
,'displace_z', text
="Z", toggle
=1)
1109 row
=col
.row(align
=True)
1111 col
.label(text
='Clean Curves:')
1112 col
.prop(props
,'clean_distance')
1113 col
.prop(props
,'remove_open_curves')
1115 def contour_edges_pattern(operator
, c
, verts_count
, iso_val
, vertices
, normals
, filtered_edges
, weight
, pattern_weight
, bevel_weight
):
1117 id0
= filtered_edges
[:,0]
1118 id1
= filtered_edges
[:,1]
1123 bool_w0
= w0
<= iso_val
1124 bool_w1
= w1
<= iso_val
1126 # mask all edges that have one weight value below the iso value
1127 mask_new_verts
= np
.logical_xor(bool_w0
, bool_w1
)
1128 if not mask_new_verts
.any():
1129 return np
.array([[None]]), {}, np
.array([[None]]), np
.array([[None]])
1131 id0
= id0
[mask_new_verts
]
1132 id1
= id1
[mask_new_verts
]
1138 w0
= w0
[mask_new_verts
]
1139 w1
= w1
[mask_new_verts
]
1140 pattern0
= pattern_weight
[id0
]
1141 pattern1
= pattern_weight
[id1
]
1143 bevel0
= bevel_weight
[id0
]
1144 bevel1
= bevel_weight
[id1
]
1147 param
= (iso_val
- w0
)/(w1
-w0
)
1148 if c
%(operator
.in_steps
+ operator
.out_steps
) < operator
.in_steps
:
1149 mult
= operator
.in_displace
1151 mult
= operator
.out_displace
1152 pattern_value
= pattern0
+ (pattern1
-pattern0
)*param
1154 bevel_value
= bevel0
+ (bevel1
-bevel0
)*param
1155 bevel_value
= np
.expand_dims(bevel_value
,axis
=1)
1156 except: bevel_value
= None
1157 disp
= pattern_value
* mult
1159 param
= np
.expand_dims(param
,axis
=1)
1160 disp
= np
.expand_dims(disp
,axis
=1)
1161 verts
= v0
+ (v1
-v0
)*param
1162 norm
= n0
+ (n1
-n0
)*param
1163 axis
= np
.array((operator
.displace_x
, operator
.displace_y
, operator
.displace_z
))
1165 verts
= verts
+ norm
*disp
1167 # indexes of edges with new vertices
1168 edges_index
= filtered_edges
[mask_new_verts
][:,2]
1170 # remove all edges completely below the iso value
1171 #mask_edges = np.logical_not(np.logical_and(bool_w0, bool_w1))
1172 #filtered_edges = filtered_edges[mask_edges]
1173 return filtered_edges
.astype("int"), edges_index
, verts
, bevel_value