1 # SPDX-FileCopyrightText: 2017 Alessandro Zomparelli
3 # SPDX-License-Identifier: GPL-2.0-or-later
6 # (c) Alessandro Zomparelli #
9 # http://www.co-de-it.com/ #
11 # ############################################################################ #
15 from bpy
.types
import Operator
16 from bpy
.props
import (
24 from bpy
.types
import (
31 from mathutils
import Vector
35 update_curve_from_pydata
,
37 convert_object_to_mesh
,
46 def anim_curve_active(self
, context
):
48 props
= ob
.tissue_to_curve
51 if not ob
.tissue
.bool_lock
:
52 bpy
.ops
.object.tissue_update_convert_to_curve()
56 class tissue_to_curve_prop(PropertyGroup
):
57 object : PointerProperty(
58 type=bpy
.types
.Object
,
60 description
="Source object",
61 update
= anim_curve_active
63 bool_smooth
: BoolProperty(
64 name
="Smooth Shading",
66 description
="Output faces with smooth shading rather than flat shaded",
67 update
= anim_curve_active
69 bool_lock
: BoolProperty(
71 description
="Prevent automatic update on settings changes or if other objects have it in the hierarchy.",
73 update
= anim_curve_active
75 bool_dependencies
: BoolProperty(
76 name
="Update Dependencies",
77 description
="Automatically updates source object as well, when possible",
79 update
= anim_curve_active
81 bool_run
: BoolProperty(
82 name
="Animatable Curve",
83 description
="Automatically recompute the conversion when the frame is changed.",
86 use_modifiers
: BoolProperty(
89 description
="Automatically apply Modifiers and Shape Keys",
90 update
= anim_curve_active
92 subdivision_mode
: EnumProperty(
96 ('INNER', "Inner", "")
99 name
="Subdivided Edges",
100 update
= anim_curve_active
102 use_endpoint_u
: BoolProperty(
105 description
="Make all open nurbs curve meet the endpoints",
106 update
= anim_curve_active
108 clean_distance
: FloatProperty(
109 name
="Merge Distance", default
=0, min=0, soft_max
=10,
110 description
="Merge Distance",
111 update
= anim_curve_active
113 nurbs_order
: IntProperty(
114 name
="Order", default
=4, min=2, max=6,
115 description
="Nurbs order",
116 update
= anim_curve_active
118 system
: IntProperty(
119 name
="System", default
=0, min=0,
120 description
="Particle system index",
121 update
= anim_curve_active
123 bounds_selection
: EnumProperty(
126 ('BOUNDS', "Boundaries", ""),
127 ('INNER', "Inner", "")
130 name
="Boundary Selection",
131 update
= anim_curve_active
133 periodic_selection
: EnumProperty(
136 ('OPEN', "Open", ""),
137 ('CLOSED', "Closed", "")
140 name
="Periodic Selection",
141 update
= anim_curve_active
143 spline_type
: EnumProperty(
145 ('POLY', "Poly", ""),
146 ('BEZIER', "Bezier", ""),
147 ('NURBS', "NURBS", "")
151 update
= anim_curve_active
155 ('LOOPS', "Loops", ""),
156 ('EDGES', "Edges", ""),
157 ('PARTICLES', "Particles", "")
160 name
="Conversion Mode",
161 update
= anim_curve_active
163 vertex_group
: StringProperty(
164 name
="Radius", default
='',
165 description
="Vertex Group used for variable radius",
166 update
= anim_curve_active
168 invert_vertex_group
: BoolProperty(default
=False,
169 description
='Inverte the value of the Vertex Group',
170 update
= anim_curve_active
172 vertex_group_factor
: FloatProperty(
177 description
="Depth bevel factor to use for zero vertex group influence",
178 update
= anim_curve_active
180 only_sharp
: BoolProperty(
182 name
="Only Sharp Edges",
183 description
='Convert only Sharp edges',
184 update
= anim_curve_active
186 pattern_depth
: FloatProperty(
191 description
="Displacement pattern depth",
192 update
= anim_curve_active
194 pattern_offset
: FloatProperty(
199 description
="Displacement pattern offset",
200 update
= anim_curve_active
202 pattern0
: IntProperty(
207 description
="Pattern step 0",
208 update
= anim_curve_active
210 pattern1
: IntProperty(
215 description
="Pattern step 1",
216 update
= anim_curve_active
219 class tissue_convert_to_curve(Operator
):
220 bl_idname
= "object.tissue_convert_to_curve"
221 bl_label
= "Tissue Convert to Curve"
222 bl_description
= "Convert selected mesh to Curve object"
223 bl_options
= {'REGISTER', 'UNDO'}
225 object : StringProperty(
227 description
="Source object",
230 bool_smooth
: BoolProperty(
231 name
="Smooth Shading",
233 description
="Output faces with smooth shading rather than flat shaded"
235 use_modifiers
: BoolProperty(
236 name
="Use Modifiers",
238 description
="Automatically apply Modifiers and Shape Keys"
240 subdivision_mode
: EnumProperty(
243 ('CAGE', "Cage", ""),
244 ('INNER', "Inner", "")
247 name
="Subdivided Edges"
249 use_endpoint_u
: BoolProperty(
252 description
="Make all open nurbs curve meet the endpoints"
254 nurbs_order
: IntProperty(
255 name
="Order", default
=4, min=2, max=6,
256 description
="Nurbs order"
258 system
: IntProperty(
259 name
="System", default
=0, min=0,
260 description
="Particle system index"
262 clean_distance
: FloatProperty(
263 name
="Merge Distance", default
=0, min=0, soft_max
=10,
264 description
="Merge Distance"
266 spline_type
: EnumProperty(
268 ('POLY', "Poly", ""),
269 ('BEZIER', "Bezier", ""),
270 ('NURBS', "NURBS", "")
275 bounds_selection
: EnumProperty(
278 ('BOUNDS', "Boundaries", ""),
279 ('INNER', "Inner", "")
282 name
="Boundary Selection"
284 periodic_selection
: EnumProperty(
287 ('OPEN', "Open", ""),
288 ('CLOSED', "Closed", "")
291 name
="Periodic Selection"
295 ('LOOPS', "Loops", ""),
296 ('EDGES', "Edges", ""),
297 ('PARTICLES', "Particles", "")
300 name
="Conversion Mode"
302 vertex_group
: StringProperty(
303 name
="Radius", default
='',
304 description
="Vertex Group used for variable radius"
306 invert_vertex_group
: BoolProperty(default
=False,
307 description
='Inverte the value of the Vertex Group'
309 vertex_group_factor
: FloatProperty(
314 description
="Depth bevel factor to use for zero vertex group influence"
316 only_sharp
: BoolProperty(
318 name
="Only Sharp Edges",
319 description
='Convert only Sharp edges'
321 pattern_depth
: FloatProperty(
326 description
="Displacement pattern depth"
328 pattern_offset
: FloatProperty(
333 description
="Displacement pattern offset"
335 pattern0
: IntProperty(
340 description
="Pattern step 0"
342 pattern1
: IntProperty(
347 description
="Pattern step 1"
351 def poll(cls
, context
):
353 #bool_tessellated = context.object.tissue_tessellate.generator != None
355 return ob
.type in ('MESH','CURVE','SURFACE','FONT') and ob
.mode
== 'OBJECT'# and bool_tessellated
359 def invoke(self
, context
, event
):
360 self
.object = context
.object.name
361 return context
.window_manager
.invoke_props_dialog(self
)
363 def draw(self
, context
):
365 ob0
= bpy
.data
.objects
[self
.object]
366 #props = ob.tissue_to_curve
368 col
= layout
.column(align
=True)
369 row
= col
.row(align
=True)
370 #row.label(text='Object: ' + self.object)
371 #row.prop_search(self, "object", context.scene, "objects")
372 #row.prop(self, "use_modifiers")#, icon='MODIFIER', text='')
374 col
.label(text
='Conversion Mode:')
375 row
= col
.row(align
=True)
377 self
, "mode", text
="Conversion Mode", icon
='NONE', expand
=True,
378 slider
=False, toggle
=False, icon_only
=False, event
=False,
379 full_event
=False, emboss
=True, index
=-1)
380 if self
.mode
== 'PARTICLES':
382 col
.prop(self
, "system")
384 if self
.mode
in ('LOOPS', 'EDGES'):
385 row
= col
.row(align
=True)
386 row
.prop(self
, "use_modifiers")
387 col2
= row
.column(align
=True)
388 if self
.use_modifiers
:
389 col2
.prop(self
, "subdivision_mode", text
='', icon
='NONE', expand
=False,
390 slider
=True, toggle
=False, icon_only
=False, event
=False,
391 full_event
=False, emboss
=True, index
=-1)
393 for m
in bpy
.data
.objects
[self
.object].modifiers
:
394 if m
.type in ('SUBSURF','MULTIRES'): col2
.enabled
= True
396 row
= col
.row(align
=True)
397 row
.label(text
='Filter Edges:')
398 col2
= row
.column(align
=True)
399 col2
.prop(self
, "bounds_selection", text
='', icon
='NONE', expand
=False,
400 slider
=True, toggle
=False, icon_only
=False, event
=False,
401 full_event
=False, emboss
=True, index
=-1)
402 col2
.prop(self
, 'only_sharp')
404 if self
.mode
== 'LOOPS':
405 row
= col
.row(align
=True)
406 row
.label(text
='Filter Loops:')
407 row
.prop(self
, "periodic_selection", text
='', icon
='NONE', expand
=False,
408 slider
=True, toggle
=False, icon_only
=False, event
=False,
409 full_event
=False, emboss
=True, index
=-1)
411 col
.label(text
='Spline Type:')
412 row
= col
.row(align
=True)
414 self
, "spline_type", text
="Spline Type", icon
='NONE', expand
=True,
415 slider
=False, toggle
=False, icon_only
=False, event
=False,
416 full_event
=False, emboss
=True, index
=-1)
417 if self
.spline_type
== 'NURBS':
419 col
.label(text
='Nurbs splines:')
420 row
= col
.row(align
=True)
421 row
.prop(self
, "use_endpoint_u")
422 row
.prop(self
, "nurbs_order")
424 col
.prop(self
, "bool_smooth")
425 if ob0
.type == 'MESH' and self
.mode
!= 'PARTICLES':
427 col
.label(text
='Variable Radius:')
428 row
= col
.row(align
=True)
429 row
.prop_search(self
, 'vertex_group', ob0
, "vertex_groups", text
='')
430 row
.prop(self
, "invert_vertex_group", text
="", toggle
=True, icon
='ARROW_LEFTRIGHT')
431 row
.prop(self
, "vertex_group_factor")
433 col
.label(text
='Clean curves:')
434 col
.prop(self
, "clean_distance")
436 col
.label(text
='Displacement Pattern:')
437 row
= col
.row(align
=True)
438 row
.prop(self
, "pattern0")
439 row
.prop(self
, "pattern1")
440 row
= col
.row(align
=True)
441 row
.prop(self
, "pattern_depth")
442 row
.prop(self
, "pattern_offset")
444 def execute(self
, context
):
445 ob
= context
.active_object
447 crv
= bpy
.data
.curves
.new(ob
.name
+ '_Curve', type='CURVE')
448 crv
.dimensions
= '3D'
449 new_ob
= bpy
.data
.objects
.new(ob
.name
+ '_Curve', crv
)
450 bpy
.context
.collection
.objects
.link(new_ob
)
451 context
.view_layer
.objects
.active
= new_ob
453 new_ob
.select_set(True)
455 new_ob
.matrix_world
= ob
.matrix_world
457 new_ob
.tissue
.tissue_type
= 'TO_CURVE'
458 new_ob
.tissue
.bool_lock
= True
460 props
= new_ob
.tissue_to_curve
462 props
.use_modifiers
= self
.use_modifiers
463 props
.subdivision_mode
= self
.subdivision_mode
464 props
.clean_distance
= self
.clean_distance
465 props
.spline_type
= self
.spline_type
466 props
.mode
= self
.mode
467 props
.use_endpoint_u
= self
.use_endpoint_u
468 props
.nurbs_order
= self
.nurbs_order
469 props
.vertex_group
= self
.vertex_group
470 props
.vertex_group_factor
= self
.vertex_group_factor
471 props
.invert_vertex_group
= self
.invert_vertex_group
472 props
.bool_smooth
= self
.bool_smooth
473 props
.system
= self
.system
474 props
.periodic_selection
= self
.periodic_selection
475 props
.bounds_selection
= self
.bounds_selection
476 props
.only_sharp
= self
.only_sharp
477 props
.pattern0
= self
.pattern0
478 props
.pattern1
= self
.pattern1
479 props
.pattern_depth
= self
.pattern_depth
480 props
.pattern_offset
= self
.pattern_offset
482 new_ob
.tissue
.bool_lock
= False
484 bpy
.ops
.object.tissue_update_convert_to_curve()
488 class tissue_update_convert_to_curve(Operator
):
489 bl_idname
= "object.tissue_update_convert_to_curve"
490 bl_label
= "Tissue Update Curve"
491 bl_description
= "Update Curve object"
492 bl_options
= {'REGISTER', 'UNDO'}
495 def poll(cls
, context
):
498 bool_curve
= ob
.tissue_to_curve
.object != None
499 return ob
.type == 'CURVE' and ob
.mode
== 'OBJECT' and bool_curve
503 def execute(self
, context
):
505 tissue_time(None,'Tissue: Convert to Curve of "{}"...'.format(ob
.name
), levels
=0)
506 start_time
= time
.time()
508 props
= ob
.tissue_to_curve
510 if props
.mode
== 'PARTICLES':
511 eval_ob
= ob0
.evaluated_get(context
.evaluated_depsgraph_get())
512 system_id
= min(props
.system
, len(eval_ob
.particle_systems
))
513 psystem
= eval_ob
.particle_systems
[system_id
]
514 ob
.data
.splines
.clear()
515 particles
= psystem
.particles
516 for id,p
in enumerate(particles
):
517 s
= ob
.data
.splines
.new('POLY')
518 if psystem
.settings
.type == 'HAIR':
519 n_pts
= len(p
.hair_keys
)
521 p
.hair_keys
.foreach_get('co',pts
)
522 co
= np
.array(pts
).reshape((-1,3))
524 n_pts
= 2**psystem
.settings
.display_step
+ 1
526 for i
in range(n_pts
):
527 vec
= psystem
.co_hair(eval_ob
, particle_no
=id,step
=i
)
528 vec
= ob0
.matrix_world
.inverted() @ vec
531 w
= np
.ones(n_pts
).reshape((n_pts
,1))
532 co
= np
.concatenate((co
,w
),axis
=1).reshape((n_pts
*4))
533 s
.points
.add(n_pts
-1)
534 s
.points
.foreach_set('co',co
)
538 ob0
= convert_object_to_mesh(ob0
, apply_modifiers
=props
.use_modifiers
)
540 n_verts
= len(me
.vertices
)
541 verts
= [0]*n_verts
*3
542 me
.vertices
.foreach_get('co',verts
)
543 verts
= np
.array(verts
).reshape((-1,3))
545 normals
= [0]*n_verts
*3
546 me
.vertices
.foreach_get('normal',normals
)
547 normals
= np
.array(normals
).reshape((-1,3))
548 #tilt = np.degrees(np.arcsin(normals[:,2]))
549 #tilt = np.arccos(normals[:,2])/2
551 verts
= np
.array(verts
).reshape((-1,3))
552 if props
.mode
in ('LOOPS','EDGES'):
555 bm
.verts
.ensure_lookup_table()
556 bm
.edges
.ensure_lookup_table()
557 bm
.faces
.ensure_lookup_table()
558 todo_edges
= list(bm
.edges
)
559 if props
.use_modifiers
and props
.subdivision_mode
!= 'ALL':
560 me0
, subs
= get_mesh_before_subs(_ob0
)
561 n_edges0
= len(me0
.edges
)
562 bpy
.data
.meshes
.remove(me0
)
563 if props
.subdivision_mode
== 'CAGE':
564 todo_edges
= todo_edges
[:n_edges0
*(2**subs
)]
565 elif props
.subdivision_mode
== 'INNER':
566 todo_edges
= todo_edges
[n_edges0
*(2**subs
):]
572 edge
= me
.edges
[e
.index
]
573 if edge
.use_edge_sharp
:
574 _todo_edges
.append(e
)
575 sharp_verts
.append(edge
.vertices
[0])
576 sharp_verts
.append(edge
.vertices
[1])
577 todo_edges
= _todo_edges
579 if props
.bounds_selection
== 'BOUNDS': todo_edges
= [e
for e
in todo_edges
if len(e
.link_faces
)<2]
580 elif props
.bounds_selection
== 'INNER': todo_edges
= [e
for e
in todo_edges
if len(e
.link_faces
)>1]
582 if props
.mode
== 'EDGES':
583 ordered_points
= [[e
.verts
[0].index
, e
.verts
[1].index
] for e
in todo_edges
]
584 elif props
.mode
== 'LOOPS':
585 vert_loops
, edge_loops
= loops_from_bmesh(todo_edges
)
588 for loop
in vert_loops
:
591 if v
.index
in sharp_verts
:
592 loop_points
.append(v
.index
)
594 if len(loop_points
)>1:
595 ordered_points
.append(loop_points
)
597 if len(loop_points
)>1:
598 ordered_points
.append(loop_points
)
599 #ordered_points = [[v.index for v in loop if v.index in sharp_verts] for loop in vert_loops]
601 ordered_points
= [[v
.index
for v
in loop
] for loop
in vert_loops
]
602 if props
.periodic_selection
== 'CLOSED':
603 ordered_points
= [points
for points
in ordered_points
if points
[0] == points
[-1]]
604 elif props
.periodic_selection
== 'OPEN':
605 ordered_points
= [points
for points
in ordered_points
if points
[0] != points
[-1]]
608 ordered_points
= find_curves(edges
, n_verts
)
610 bpy
.data
.objects
.remove(ob0
)
614 weight
= get_weight_numpy(ob0
.vertex_groups
[props
.vertex_group
], n_verts
)
615 if props
.invert_vertex_group
: weight
= 1-weight
616 fact
= props
.vertex_group_factor
618 weight
= weight
*(1-fact
) + fact
625 for points in ordered_points:
626 if points[0] == points[-1]: # Closed curve
627 pts0 = [points[-1]] + points[:-1] # i-1
629 pts2 = points[1:] + [points[0]] # 1+1
631 pts0 = [points[0]] + points[:-1] # i-1
633 pts2 = points[1:] + [points[-1]] # i+1
635 for i0, i1, i2 in zip(pts0, pts1, pts2):
636 pt0 = Vector(verts[i0])
637 pt1 = Vector(verts[i1])
638 pt2 = Vector(verts[i2])
639 tan1 = (pt1-pt0).normalized()
640 tan2 = (pt2-pt1).normalized()
641 vec_tan = -(tan1 + tan2).normalized()
642 vec2 = vec_tan.cross(Vector((0,0,1)))
643 vec_z = vec_tan.cross(vec2)
645 if vec_z.length == 0:
647 ang = vec_z.angle(nor)
648 if nor[2] < 0: ang = 2*pi-ang
649 #if vec_tan[0] > vec_tan[1] and nor[0]>0: ang = -ang
650 #if vec_tan[0] > vec_tan[2] and nor[0]>0: ang = -ang
651 #if vec_tan[0] < vec_tan[1] and nor[1]>0: ang = -ang
652 #if nor[0]*nor[1]*nor[2] < 0: ang = -ang
653 if nor[2] == 0: ang = -5*pi/4
654 #ang = max(ang, np.arccos(nor[2]))
655 curve_tilt.append(ang)
656 #curve_tilt.append(np.arccos(nor[2]))
657 tilt.append(curve_tilt)
659 depth
= props
.pattern_depth
660 offset
= props
.pattern_offset
661 pattern
= [props
.pattern0
,props
.pattern1
]
662 update_curve_from_pydata(ob
.data
, verts
, normals
, weight
, ordered_points
, merge_distance
=props
.clean_distance
, pattern
=pattern
, depth
=depth
, offset
=offset
)
665 bpy
.data
.objects
.remove(ob0
)
666 for s
in ob
.data
.splines
:
667 s
.type = props
.spline_type
668 if s
.type == 'NURBS':
669 s
.use_endpoint_u
= props
.use_endpoint_u
670 s
.order_u
= props
.nurbs_order
671 ob
.data
.splines
.update()
672 if not props
.bool_smooth
: bpy
.ops
.object.shade_flat()
674 tissue_time(start_time
,'Convert to Curve',levels
=0)
679 class TISSUE_PT_convert_to_curve(Panel
):
680 bl_space_type
= 'PROPERTIES'
681 bl_region_type
= 'WINDOW'
683 bl_label
= "Tissue Convert to Curve"
684 bl_options
= {'DEFAULT_CLOSED'}
687 def poll(cls
, context
):
689 #bool_curve = context.object.tissue_to_curve.object != None
691 return ob
.type == 'CURVE' and ob
.tissue
.tissue_type
== 'TO_CURVE'
695 def draw(self
, context
):
697 props
= ob
.tissue_to_curve
700 #layout.use_property_split = True
701 #layout.use_property_decorate = False
702 col
= layout
.column(align
=True)
703 row
= col
.row(align
=True)
704 #col.operator("object.tissue_update_convert_to_curve", icon='FILE_REFRESH', text='Refresh')
705 row
.operator("object.tissue_update_tessellate_deps", icon
='FILE_REFRESH', text
='Refresh') ####
706 lock_icon
= 'LOCKED' if ob
.tissue
.bool_lock
else 'UNLOCKED'
707 #lock_icon = 'PINNED' if props.bool_lock else 'UNPINNED'
708 deps_icon
= 'LINKED' if ob
.tissue
.bool_dependencies
else 'UNLINKED'
709 row
.prop(ob
.tissue
, "bool_dependencies", text
="", icon
=deps_icon
)
710 row
.prop(ob
.tissue
, "bool_lock", text
="", icon
=lock_icon
)
711 col2
= row
.column(align
=True)
712 col2
.prop(ob
.tissue
, "bool_run", text
="",icon
='TIME')
713 col2
.enabled
= not ob
.tissue
.bool_lock
714 col2
= row
.column(align
=True)
715 col2
.operator("mesh.tissue_remove", text
="", icon
='X')
718 row
= col
.row(align
=True)
719 row
.prop_search(props
, "object", context
.scene
, "objects")
720 row
.prop(props
, "use_modifiers", icon
='MODIFIER', text
='')
722 col
.label(text
='Conversion Mode:')
723 row
= col
.row(align
=True)
725 props
, "mode", icon
='NONE', expand
=True,
726 slider
=False, toggle
=False, icon_only
=False, event
=False,
727 full_event
=False, emboss
=True, index
=-1)
728 if props
.mode
== 'PARTICLES':
730 col
.prop(props
, "system")
733 if props
.mode
in ('LOOPS', 'EDGES'):
734 row
= col
.row(align
=True)
735 row
.prop(props
, "use_modifiers")
736 col2
= row
.column(align
=True)
737 if props
.use_modifiers
:
738 col2
.prop(props
, "subdivision_mode", text
='', icon
='NONE', expand
=False,
739 slider
=True, toggle
=False, icon_only
=False, event
=False,
740 full_event
=False, emboss
=True, index
=-1)
742 for m
in props
.object.modifiers
:
743 if m
.type in ('SUBSURF','MULTIRES'): col2
.enabled
= True
745 row
= col
.row(align
=True)
746 row
.label(text
='Filter Edges:')
747 col2
= row
.column(align
=True)
748 col2
.prop(props
, "bounds_selection", text
='', icon
='NONE', expand
=False,
749 slider
=True, toggle
=False, icon_only
=False, event
=False,
750 full_event
=False, emboss
=True, index
=-1)
751 col2
.prop(props
, 'only_sharp')
753 if props
.mode
== 'LOOPS':
754 row
= col
.row(align
=True)
755 row
.label(text
='Filter Loops:')
756 row
.prop(props
, "periodic_selection", text
='', icon
='NONE', expand
=False,
757 slider
=True, toggle
=False, icon_only
=False, event
=False,
758 full_event
=False, emboss
=True, index
=-1)
761 col
.label(text
='Spline Type:')
762 row
= col
.row(align
=True)
764 props
, "spline_type", text
="Spline Type", icon
='NONE', expand
=True,
765 slider
=False, toggle
=False, icon_only
=False, event
=False,
766 full_event
=False, emboss
=True, index
=-1)
767 if props
.spline_type
== 'NURBS':
769 col
.label(text
='Nurbs Splines:')
770 row
= col
.row(align
=True)
771 row
.prop(props
, "use_endpoint_u")
772 row
.prop(props
, "nurbs_order")
774 col
.prop(props
, "bool_smooth")
775 if props
.object.type == 'MESH':
777 col
.label(text
='Variable Radius:')
778 row
= col
.row(align
=True)
779 row
.prop_search(props
, 'vertex_group', props
.object, "vertex_groups", text
='')
780 row
.prop(props
, "invert_vertex_group", text
="", toggle
=True, icon
='ARROW_LEFTRIGHT')
781 row
.prop(props
, "vertex_group_factor")
783 col
.label(text
='Clean Curves:')
784 col
.prop(props
, "clean_distance")
786 col
.label(text
='Displacement Pattern:')
787 row
= col
.row(align
=True)
788 row
.prop(props
, "pattern0")
789 row
.prop(props
, "pattern1")
790 row
= col
.row(align
=True)
791 row
.prop(props
, "pattern_depth")
792 row
.prop(props
, "pattern_offset")