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 statistics
import mean
, stdev
27 from mathutils
import Vector
28 from mathutils
.kdtree
import KDTree
30 try: from .numba_functions
import run_tex_rd
, run_tex_rd_ani
32 #from .numba_functions import integrate_field
33 #from .numba_functions import numba_reaction_diffusion
34 try: import numexpr
as ne
37 # Reaction-Diffusion cache
38 from pathlib
import Path
42 from bpy
.types
import (
48 from bpy
.props
import (
61 def tex_reaction_diffusion_add_handler(self
, context
):
62 # remove existing handlers
63 tex_reaction_diffusion_remove_handler(self
, context
)
65 bpy
.app
.handlers
.frame_change_post
.append(tex_rd_scene
)
68 def tex_reaction_diffusion_remove_handler(self
, context
):
69 # remove existing handlers
71 for h
in bpy
.app
.handlers
.frame_change_post
:
72 if "tex_rd" in str(h
):
73 old_handlers
.append(h
)
74 for h
in old_handlers
: bpy
.app
.handlers
.frame_change_post
.remove(h
)
77 class tex_reaction_diffusion_prop(PropertyGroup
):
78 run
: BoolProperty(default
=False, update
= tex_reaction_diffusion_add_handler
,
79 description
='Compute a new iteration on frame changes. Currently is not working during Render Animation')
82 name
="Resolution X", default
=512, min=2, soft_max
=1000,
83 description
="Resolution of the simulation")
86 name
="Resolution Y", default
=512, min=2, soft_max
=1000,
87 description
="Resolution of the simulation")
89 time_steps
: IntProperty(
90 name
="Steps", default
=10, min=0, soft_max
=50,
91 description
="Number of Steps")
94 name
="dt", default
=1, min=0, soft_max
=0.2,
95 description
="Time Step")
97 diff_a
: FloatProperty(
98 name
="Diff A", default
=0.14, min=0, soft_max
=2, precision
=3,
99 description
="Diffusion A")
101 diff_b
: FloatProperty(
102 name
="Diff B", default
=0.07, min=0, soft_max
=2, precision
=3,
103 description
="Diffusion B")
106 name
="f", default
=0.055, soft_min
=0.01, soft_max
=0.06, precision
=4, step
=0.05,
107 description
="Feed Rate")
110 name
="k", default
=0.062, soft_min
=0.035, soft_max
=0.065, precision
=4, step
=0.05,
111 description
="Kill Rate")
113 diff_mult
: FloatProperty(
114 name
="Scale", default
=1, min=0, soft_max
=1, max=10, precision
=2,
115 description
="Multiplier for the diffusion of both substances")
117 anisotropy
: FloatProperty(
118 name
="Anisotropy", default
=0.5, min=0, max=1, precision
=2,
119 description
="Influence of the Vector Field")
121 img_vector_field
: StringProperty(
122 name
="Vector Field", default
='',
123 description
="Image used for the Vector Field. RGB to XY")
125 img_a
: StringProperty(
126 name
="A", default
='',
127 description
="Image used for the chemical A")
129 img_b
: StringProperty(
130 name
="B", default
='',
131 description
="Image used for the chemical B")
133 img_diff_a
: StringProperty(
134 name
="Diff A", default
='',
135 description
="Image used for A diffusion")
137 img_diff_b
: StringProperty(
138 name
="Diff B", default
='',
139 description
="Image used for B diffusion")
141 img_scale
: StringProperty(
142 name
="Scale", default
='',
143 description
="Image used for Scale value")
145 img_f
: StringProperty(
146 name
="f", default
='',
147 description
="Image used for Feed value (f)")
149 img_k
: StringProperty(
150 name
="k", default
='',
151 description
="Image used for Kill value (k)")
153 img_brush
: StringProperty(
154 name
="Brush", default
='',
155 description
="Image used for adding/removing B")
157 invert_img_diff_a
: BoolProperty(default
=False,
158 description
='Invert the value of the Vertex Group Diff A')
160 invert_img_diff_b
: BoolProperty(default
=False,
161 description
='Invert the value of the Vertex Group Diff B')
163 invert_img_scale
: BoolProperty(default
=False,
164 description
='Invert the value of the Vertex Group Scale')
166 invert_img_f
: BoolProperty(default
=False,
167 description
='Invert the value of the Vertex Group f')
169 invert_img_k
: BoolProperty(default
=False,
170 description
='Invert the value of the Vertex Group k')
172 invert_img_vector_field
: BoolProperty(default
=False,
173 description
='Use the perpendicular direction')
175 min_diff_a
: FloatProperty(
176 name
="Min Diff A", default
=0.1, min=0, soft_max
=2, precision
=3,
177 description
="Min Diff A")
179 max_diff_a
: FloatProperty(
180 name
="Max Diff A", default
=0.1, min=0, soft_max
=2, precision
=3,
181 description
="Max Diff A")
183 min_diff_b
: FloatProperty(
184 name
="Min Diff B", default
=0.1, min=0, soft_max
=2, precision
=3,
185 description
="Min Diff B")
187 max_diff_b
: FloatProperty(
188 name
="Max Diff B", default
=0.1, min=0, soft_max
=2, precision
=3,
189 description
="Max Diff B")
191 min_scale
: FloatProperty(
192 name
="Scale", default
=0.35, min=0, soft_max
=1, max=10, precision
=2,
193 description
="Min Scale Value")
195 max_scale
: FloatProperty(
196 name
="Scale", default
=1, min=0, soft_max
=1, max=10, precision
=2,
197 description
="Max Scale value")
199 min_f
: FloatProperty(
200 name
="Min f", default
=0.02, min=0, soft_min
=0.01, soft_max
=0.06, max=0.2, precision
=4, step
=0.05,
201 description
="Min Feed Rate")
203 max_f
: FloatProperty(
204 name
="Max f", default
=0.055, min=0, soft_min
=0.01, soft_max
=0.06, max=0.2, precision
=4, step
=0.05,
205 description
="Max Feed Rate")
207 min_k
: FloatProperty(
208 name
="Min k", default
=0.035, min=0, soft_min
=0.035, soft_max
=0.065, max=0.2, precision
=4, step
=0.05,
209 description
="Min Kill Rate")
211 max_k
: FloatProperty(
212 name
="Max k", default
=0.062, min=0, soft_min
=0.035, soft_max
=0.065, max=0.2, precision
=4, step
=0.05,
213 description
="Max Kill Rate")
215 brush_mult
: FloatProperty(
216 name
="Mult", default
=0.5, min=-1, max=1, precision
=3, step
=0.05,
217 description
="Multiplier for brush value")
219 bool_cache
: BoolProperty(
220 name
="Use Cache", default
=False,
221 description
="Read modifiers affect the vertex groups")
223 cache_frame_start
: IntProperty(
224 name
="Start", default
=1,
225 description
="Frame on which the simulation starts")
227 cache_frame_end
: IntProperty(
228 name
="End", default
=250,
229 description
="Frame on which the simulation ends")
231 cache_dir
: StringProperty(
232 name
="Cache directory", default
="", subtype
='FILE_PATH',
233 description
= 'Directory that contains Reaction-Diffusion cache files'
236 normalize
: BoolProperty(
237 name
="Normalize values", default
=False,
238 description
="Normalize values from 0 to 1")
240 def tex_rd_scene(scene
, bake
=False):
241 for ob
in bpy
.context
.scene
.objects
:
242 if ob
.tex_reaction_diffusion_settings
.run
:
243 tex_reaction_diffusion_def(ob
)
245 def tex_reaction_diffusion_def(ob
, bake
=False):
247 props
= ob
.tex_reaction_diffusion_settings
250 scene
= bpy
.context
.scene
251 print("Texture Reaction Diffusion: " + str(scene
.frame_current
))
252 start_time
= timeit
.default_timer()
253 img_a
= bpy
.data
.images
[props
.img_a
]
254 img_b
= bpy
.data
.images
[props
.img_b
]
255 diff_a
= props
.diff_a
256 diff_b
= props
.diff_b
257 diff_a_min
= props
.min_diff_a
258 diff_a_max
= props
.max_diff_a
259 diff_b_min
= props
.min_diff_b
260 diff_b_max
= props
.max_diff_b
265 ani
= props
.anisotropy
267 time_steps
= props
.time_steps
268 res_x
= props
.res_x
#int(img_b.size[0])
269 res_y
= props
.res_y
#int(img_b.size[1])
271 min_scale
= props
.min_scale
272 max_scale
= props
.max_scale
274 images
= bpy
.data
.images
.keys()
275 rd_images
= [img_a
, img_b
]
278 img_vector_field
= None
283 if props
.img_vector_field
in images
:
284 img_vector_field
= bpy
.data
.images
[props
.img_vector_field
]
285 rd_images
.append(img_vector_field
)
286 if props
.img_diff_a
in images
:
287 img_diff_a
= bpy
.data
.images
[props
.img_diff_a
]
288 rd_images
.append(img_diff_a
)
289 if props
.img_diff_b
in images
:
290 img_diff_b
= bpy
.data
.images
[props
.img_diff_b
]
291 rd_images
.append(img_diff_b
)
292 if props
.img_f
in images
:
293 img_f
= bpy
.data
.images
[props
.img_f
]
294 rd_images
.append(img_f
)
295 if props
.img_k
in images
:
296 img_k
= bpy
.data
.images
[props
.img_k
]
297 rd_images
.append(img_k
)
298 if props
.img_scale
in images
:
299 img_scale
= bpy
.data
.images
[props
.img_scale
]
300 rd_images
.append(img_scale
)
301 if props
.img_brush
in images
:
302 img_brush
= bpy
.data
.images
[props
.img_brush
]
303 rd_images
.append(img_brush
)
305 im
.scale(res_x
,res_y
)
310 a_px
= np
.float32(np
.zeros(nx
*ny
*4))
311 img_a
.pixels
.foreach_get(a_px
)
312 b_px
= np
.float32(np
.zeros(nx
*ny
*4))
313 img_b
.pixels
.foreach_get(b_px
)
315 vf_px
= np
.float32(np
.zeros(nx
*ny
*4))
316 img_vector_field
.pixels
.foreach_get(vf_px
)
317 vf_px
= np
.array(vf_px
).reshape((-1,4))
318 vf_x
= vf_px
[:,1]*2-1
319 vf_x
= vf_x
.reshape((nx
,ny
))
320 vf_y
= vf_px
[:,0]*2-1
321 vf_y
= vf_y
.reshape((nx
,ny
))
324 vf_x_
= sqrt(2)/2*vf_x
325 vf_y_
= sqrt(2)/2*vf_y
326 vf_xy1_
= abs(vf_x_
+ vf_y_
)
327 vf_xy2_
= abs(vf_x_
- vf_y_
)
328 vf_xy1
= (vf_xy1_
*ani
+ (1-ani
))*sqrt(2)/2
329 vf_xy2
= (vf_xy2_
*ani
+ (1-ani
))*sqrt(2)/2
330 vf_x_
= abs(vf_x
)*ani
+ (1-ani
)
331 vf_y_
= abs(vf_y
)*ani
+ (1-ani
)
332 vf1
= np
.concatenate((vf_x_
[np
.newaxis
,:,:], vf_y_
[np
.newaxis
,:,:], vf_xy1
[np
.newaxis
,:,:], vf_xy2
[np
.newaxis
,:,:]), axis
=0)
334 # perpendicular field
335 vf_x
, vf_y
= -vf_y
, vf_x
336 vf_x_
= sqrt(2)/2*vf_x
337 vf_y_
= sqrt(2)/2*vf_y
338 vf_xy1_
= abs(vf_x_
+ vf_y_
)
339 vf_xy2_
= abs(vf_x_
- vf_y_
)
340 vf_xy1
= (vf_xy1_
*ani
+ (1-ani
))*sqrt(2)/2
341 vf_xy2
= (vf_xy2_
*ani
+ (1-ani
))*sqrt(2)/2
342 vf_x
= abs(vf_x
)*ani
+ (1-ani
)
343 vf_y
= abs(vf_y
)*ani
+ (1-ani
)
344 vf2
= np
.concatenate((vf_x
[np
.newaxis
,:,:], vf_y
[np
.newaxis
,:,:], vf_xy1
[np
.newaxis
,:,:], vf_xy2
[np
.newaxis
,:,:]), axis
=0)
345 if props
.invert_img_vector_field
:
348 vf
= np
.ones((1,nx
,ny
))
349 vf_diag
= np
.ones((1,nx
,ny
))*sqrt(2)/2
350 vf1
= np
.concatenate((vf
, vf
, vf_diag
, vf_diag
), axis
=0)
355 diff_a
= np_remap_image_values(img_diff_a
, channel
=0, min=diff_a_min
, max=diff_a_max
, invert
=props
.invert_img_diff_a
)
357 diff_a
= np
.ones((nx
,ny
))*props
.diff_a
360 diff_b
= np_remap_image_values(img_diff_b
, channel
=0, min=diff_b_min
, max=diff_b_max
, invert
=props
.invert_img_diff_b
)
362 diff_b
= np
.ones((nx
,ny
))*props
.diff_b
365 scale
= np_remap_image_values(img_scale
, channel
=0, min=min_scale
, max=max_scale
, invert
=props
.invert_img_scale
)
369 diff_a
*= props
.diff_mult
370 diff_b
*= props
.diff_mult
373 f
= np_remap_image_values(img_f
, channel
=0, min=f_min
, max=f_max
, invert
=props
.invert_img_f
)
375 f
= np
.ones((nx
,ny
))*props
.f
378 k
= np_remap_image_values(img_k
, channel
=0, min=k_min
, max=k_max
, invert
=props
.invert_img_k
)
380 k
= np
.ones((nx
,ny
))*props
.k
383 brush
= np_remap_image_values(img_brush
)*props
.brush_mult
385 brush
= np
.zeros((nx
,ny
))
387 print("Load images: " + str(timeit
.default_timer() - start_time
) + " sec")
389 start_time
= timeit
.default_timer()
391 a_px
= np
.array(a_px
).reshape((-1,4))
393 a
= a
.reshape((nx
,ny
))
394 lap_a
= np
.zeros((nx
,ny
))
396 b_px
= np
.array(b_px
).reshape((-1,4))
398 b
= b
.reshape((nx
,ny
))
399 lap_b
= np
.zeros((nx
,ny
))
401 print("Reshape data time: " + str(timeit
.default_timer() - start_time
) + " sec")
403 start_time
= timeit
.default_timer()
404 run_tex_rd_ani(a
, b
, lap_a
, lap_b
, diff_a
, diff_b
, f
, k
, dt
, time_steps
, vf1
, vf2
, brush
)
405 print("Simulation time: " + str(timeit
.default_timer() - start_time
) + " sec")
407 start_time
= timeit
.default_timer()
418 img_a
.pixels
.foreach_set(np
.float32(a_px
.flatten()))
419 img_b
.pixels
.foreach_set(np
.float32(b_px
.flatten()))
420 img_a
.pixels
.update()
421 img_b
.pixels
.update()
424 print("Stored Images: " + str(timeit
.default_timer() - start_time
) + " sec")
426 class reset_tex_reaction_diffusion(Operator
):
427 bl_idname
= "object.reset_tex_reaction_diffusion"
428 bl_label
= "Reset Texture Reaction Diffusion"
429 bl_description
= ("Run a Reaction-Diffusion based on images: A and B")
430 bl_options
= {'REGISTER', 'UNDO'}
433 name
="Run Reaction-Diffusion", default
=True, description
="Compute a new iteration on frame changes")
435 time_steps
: IntProperty(
436 name
="Steps", default
=10, min=0, soft_max
=50,
437 description
="Number of Steps")
440 name
="dt", default
=1, min=0, soft_max
=0.2,
441 description
="Time Step")
443 diff_a
: FloatProperty(
444 name
="Diff A", default
=0.14, min=0, soft_max
=2,
445 description
="Diffusion A")
447 diff_b
: FloatProperty(
448 name
="Diff B", default
=0.07, min=0, soft_max
=2,
449 description
="Diffusion B")
452 name
="f", default
=0.055, min=0, soft_min
=0.01, soft_max
=0.06, max=0.1, precision
=4,
453 description
="Feed Rate")
456 name
="k", default
=0.062, min=0, soft_min
=0.035, soft_max
=0.065, max=0.1, precision
=4,
457 description
="Kill Rate")
459 def execute(self
, context
):
460 props
= context
.object.tex_reaction_diffusion_settings
462 props
.time_steps
= self
.time_steps
465 props
.diff_a
= self
.diff_a
466 props
.diff_b
= self
.diff_b
469 img_a
= bpy
.data
.images
[props
.img_a
]
470 img_b
= bpy
.data
.images
[props
.img_b
]
471 img_a
.scale(width
=res_x
, height
=res_y
)
472 img_b
.scale(width
=res_x
, height
=res_y
)
473 img_a
.pixels
.foreach_set([1]*res_x
*res_y
*4)
474 img_b
.pixels
.foreach_set([0,0,0,1]*res_x
*res_y
)
475 img_a
.pixels
.update()
476 img_b
.pixels
.update()
482 class start_tex_reaction_diffusion(Operator
):
483 bl_idname
= "object.start_tex_reaction_diffusion"
484 bl_label
= "Start Texture Reaction Diffusion"
485 bl_description
= ("Run a Reaction-Diffusion based on images: A and B")
486 bl_options
= {'REGISTER', 'UNDO'}
488 #res_x : IntProperty(
489 # name="Resolution X", default=512, min=2, soft_max=1000,
490 # description="Resolution of the simulation")
491 #res_y : IntProperty(
492 # name="Resolution Y", default=512, min=2, soft_max=1000,
493 # description="Resolution of the simulation")
496 def poll(cls
, context
):
499 #def invoke(self, context, event):
500 # return context.window_manager.invoke_props_dialog(self)
502 def execute(self
, context
):
503 tex_reaction_diffusion_add_handler(self
, context
)
504 set_animatable_fix_handler(self
, context
)
507 props
= ob
.tex_reaction_diffusion_settings
508 if props
.img_a
in bpy
.data
.images
.keys():
509 img_a
= bpy
.data
.images
[props
.img_a
]
510 img_a
.scale(props
.res_x
, props
.res_y
)
512 img_a
= bpy
.data
.images
.new(name
="A", width
=props
.res_x
, height
=props
.res_y
)
513 if props
.img_b
in bpy
.data
.images
.keys():
514 img_b
= bpy
.data
.images
[props
.img_b
]
515 img_b
.scale(props
.res_x
, props
.res_y
)
517 img_b
= bpy
.data
.images
.new(name
="B", width
=props
.res_x
, height
=props
.res_y
)
519 #props.res_x = self.res_x
520 #props.res_y = self.res_y
521 props
.img_a
= img_a
.name
522 props
.img_b
= img_b
.name
524 #props.run = self.run
526 #props.time_steps = self.time_steps
529 #props.diff_a = self.diff_a
530 #props.diff_b = self.diff_b
535 class TISSUE_PT_tex_reaction_diffusion(Panel
):
536 bl_space_type
= 'PROPERTIES'
537 bl_region_type
= 'WINDOW'
538 bl_context
= "object"
539 bl_label
= "Tissue Texture Reaction-Diffusion"
540 bl_options
= {'DEFAULT_CLOSED'}
543 #def poll(cls, context):
546 def draw(self
, context
):
547 tex_reaction_diffusion_add_handler(self
, context
)
548 ob
= bpy
.context
.object
549 props
= ob
.tex_reaction_diffusion_settings
553 col
= layout
.column(align
=True)
554 row
= col
.row(align
=True)
555 if not (img_a
and img_b
in bpy
.data
.images
):
556 row
.operator("object.start_tex_reaction_diffusion",
558 col
= layout
.column(align
=True)
559 row
= col
.row(align
=True)
560 row
.prop(props
, 'res_x')
561 row
.prop(props
, 'res_y')
563 col
.prop_search(props
, 'img_a', bpy
.data
, "images")
564 col
.prop_search(props
, 'img_b', bpy
.data
, "images")
566 row
.operator("object.reset_tex_reaction_diffusion",
568 row
= col
.row(align
=True)
569 row
.prop(props
, "run", text
="Run Reaction-Diffusion")
570 col
= layout
.column(align
=True)
571 row
= col
.row(align
=True)
572 row
.prop(props
, 'res_x')
573 row
.prop(props
, 'res_y')
575 col
.prop_search(props
, 'img_a', bpy
.data
, "images")
576 col
.prop_search(props
, 'img_b', bpy
.data
, "images")
578 row
= col
.row(align
=True)
579 row
.prop(props
, "time_steps")
580 row
.prop(props
, "dt")
581 row
.enabled
= not props
.bool_cache
583 row
= col
.row(align
=True)
584 col1
= row
.column(align
=True)
585 col1
.prop(props
, "diff_a")
586 col1
.enabled
= props
.img_diff_a
== '' and not props
.bool_cache
587 col1
= row
.column(align
=True)
588 col1
.prop(props
, "diff_b")
589 col1
.enabled
= props
.img_diff_b
== '' and not props
.bool_cache
590 row
= col
.row(align
=True)
591 row
.prop(props
, "diff_mult")
592 row
.enabled
= props
.img_scale
== '' and not props
.bool_cache
594 row
= col
.row(align
=True)
595 col1
= row
.column(align
=True)
596 col1
.prop(props
, "f")
597 col1
.enabled
= props
.img_f
== '' and not props
.bool_cache
598 col1
= row
.column(align
=True)
599 col1
.prop(props
, "k")
600 col1
.enabled
= props
.img_k
== '' and not props
.bool_cache
603 col.label(text='Cache:')
604 #col.prop(props, "bool_cache")
605 col.prop(props, "cache_dir", text='')
607 row = col.row(align=True)
608 row.prop(props, "cache_frame_start")
609 row.prop(props, "cache_frame_end")
612 col.operator("object.reaction_diffusion_free_data")
614 row = col.row(align=True)
615 row.operator("object.bake_reaction_diffusion")
616 file = bpy.context.blend_data.filepath
617 temp = bpy.context.preferences.filepaths.temporary_directory
618 if file == temp == props.cache_dir == '':
620 col.label(text="Cannot use cache", icon='ERROR')
621 col.label(text='please save the Blender or set a Cache directory')
624 class TISSUE_PT_tex_reaction_diffusion_images(Panel
):
625 bl_space_type
= 'PROPERTIES'
626 bl_region_type
= 'WINDOW'
627 bl_context
= "object"
628 bl_parent_id
= "TISSUE_PT_tex_reaction_diffusion"
629 bl_label
= "Image Maps"
630 bl_options
= {'DEFAULT_CLOSED'}
633 def poll(cls
, context
):
634 props
= context
.object.tex_reaction_diffusion_settings
635 if props
.img_a
and props
.img_b
in bpy
.data
.images
.keys():
640 def draw(self
, context
):
642 props
= ob
.tex_reaction_diffusion_settings
644 #layout.use_property_split = True
645 col
= layout
.column(align
=True)
646 insert_image_parameter(col
, ob
, 'brush', text
='Brush:')
647 insert_image_parameter(col
, ob
, 'diff_a', text
='Diff A:')
648 insert_image_parameter(col
, ob
, 'diff_b', text
='Diff B:')
649 insert_image_parameter(col
, ob
, 'scale', text
='Scale:')
650 insert_image_parameter(col
, ob
, 'f', text
='f:')
651 insert_image_parameter(col
, ob
, 'k', text
='k:')
652 insert_image_parameter(col
, ob
, 'vector_field', text
='Vector Field:')
653 col
.enabled
= not props
.bool_cache
655 def insert_image_parameter(col
, ob
, name
, text
=''):
656 props
= ob
.tex_reaction_diffusion_settings
657 split
= col
.split(factor
=0.25, align
=True)
658 col2
= split
.column(align
=True)
659 col2
.label(text
=text
)
660 col2
= split
.column(align
=True)
661 row2
= col2
.row(align
=True)
662 row2
.prop_search(props
, 'img_' + name
, bpy
.data
, "images", text
='')
663 if name
not in ('brush'):
664 if name
== 'vector_field': icon
= 'DRIVER_ROTATIONAL_DIFFERENCE'#'ORIENTATION_VIEW'
665 else: icon
= 'ARROW_LEFTRIGHT'
666 row2
.prop(props
, "invert_img_" + name
, text
="", toggle
=True, icon
=icon
)
667 if 'img_' + name
in props
:
668 if props
['img_' + name
] != '':
670 col2
.prop(props
, "brush_mult")
671 elif name
== 'vector_field':
672 col2
.prop(props
, "anisotropy")
674 row2
= col2
.row(align
=True)
675 row2
.prop(props
, "min_" + name
, text
="Min")
676 row2
= col2
.row(align
=True)
677 row2
.prop(props
, "max_" + name
, text
="Max")