Import_3ds: Improved distance cue node setup
[blender-addons.git] / mesh_tissue / texture_reaction_diffusion.py
blob0b4376728ec6cb59efa787fd5da3061815fc6e96
1 # SPDX-FileCopyrightText: 2017 Alessandro Zomparelli
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 #-------------------------- COLORS / GROUPS EXCHANGER -------------------------#
6 # #
7 # Vertex Color to Vertex Group allow you to convert colors channles to weight #
8 # maps. #
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 #
11 # slot. #
12 # For use the command "Vertex Clors to Vertex Groups" use the search bar #
13 # (space bar). #
14 # #
15 # (c) Alessandro Zomparelli #
16 # (2017) #
17 # #
18 # http://www.co-de-it.com/ #
19 # #
20 ################################################################################
22 import bpy, bmesh, os
23 import numpy as np
24 import math, timeit, time
25 from math import pi
26 from statistics import mean, stdev
27 from mathutils import Vector
28 from mathutils.kdtree import KDTree
29 from numpy import *
30 try: from .numba_functions import run_tex_rd, run_tex_rd_ani
31 except: pass
32 #from .numba_functions import integrate_field
33 #from .numba_functions import numba_reaction_diffusion
34 try: import numexpr as ne
35 except: pass
37 # Reaction-Diffusion cache
38 from pathlib import Path
39 import random as rnd
40 import string
42 from bpy.types import (
43 Operator,
44 Panel,
45 PropertyGroup,
48 from bpy.props import (
49 BoolProperty,
50 EnumProperty,
51 FloatProperty,
52 IntProperty,
53 StringProperty,
54 FloatVectorProperty,
55 IntVectorProperty
58 from .utils import *
61 def tex_reaction_diffusion_add_handler(self, context):
62 # remove existing handlers
63 tex_reaction_diffusion_remove_handler(self, context)
64 # add new handler
65 bpy.app.handlers.frame_change_post.append(tex_rd_scene)
68 def tex_reaction_diffusion_remove_handler(self, context):
69 # remove existing handlers
70 old_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')
81 res_x : IntProperty(
82 name="Resolution X", default=512, min=2, soft_max=1000,
83 description="Resolution of the simulation")
85 res_y : IntProperty(
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")
93 dt : FloatProperty(
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")
105 f : FloatProperty(
106 name="f", default=0.055, soft_min=0.01, soft_max=0.06, precision=4, step=0.05,
107 description="Feed Rate")
109 k : FloatProperty(
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):
246 try:
247 props = ob.tex_reaction_diffusion_settings
248 except:
249 return
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
261 f_min = props.min_f
262 f_max = props.max_f
263 k_min = props.min_k
264 k_max = props.max_k
265 ani = props.anisotropy
266 dt = props.dt
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]
276 img_diff_a = None
277 img_diff_b = None
278 img_vector_field = None
279 img_f = None
280 img_k = None
281 img_scale = None
282 img_brush = 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)
304 for im in rd_images:
305 im.scale(res_x ,res_y)
306 im.pixels.update()
307 nx = res_y
308 ny = res_x
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)
314 if img_vector_field:
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))
323 # original field
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:
346 vf1, vf2 = vf2, vf1
347 else:
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)
351 vf2 = vf1
354 if img_diff_a:
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)
356 else:
357 diff_a = np.ones((nx,ny))*props.diff_a
359 if img_diff_b:
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)
361 else:
362 diff_b = np.ones((nx,ny))*props.diff_b
364 if img_scale:
365 scale = np_remap_image_values(img_scale, channel=0, min=min_scale, max=max_scale, invert=props.invert_img_scale)
366 diff_a *= scale
367 diff_b *= scale
368 else:
369 diff_a *= props.diff_mult
370 diff_b *= props.diff_mult
372 if img_f:
373 f = np_remap_image_values(img_f, channel=0, min=f_min, max=f_max, invert=props.invert_img_f)
374 else:
375 f = np.ones((nx,ny))*props.f
377 if img_k:
378 k = np_remap_image_values(img_k, channel=0, min=k_min, max=k_max, invert=props.invert_img_k)
379 else:
380 k = np.ones((nx,ny))*props.k
382 if img_brush:
383 brush = np_remap_image_values(img_brush)*props.brush_mult
384 else:
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))
392 a = a_px[:,0]
393 a = a.reshape((nx,ny))
394 lap_a = np.zeros((nx,ny))
396 b_px = np.array(b_px).reshape((-1,4))
397 b = b_px[:,0]
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()
408 np.clip(a,0,1,out=a)
409 np.clip(b,0,1,out=b)
410 a = a.flatten()
411 b = b.flatten()
412 a_px[:,0] = a
413 a_px[:,1] = a
414 a_px[:,2] = a
415 b_px[:,0] = b
416 b_px[:,1] = b
417 b_px[:,2] = b
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()
422 img_a.update()
423 img_b.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'}
432 run : BoolProperty(
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")
439 dt : FloatProperty(
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")
451 f : FloatProperty(
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")
455 k : FloatProperty(
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
461 props.dt = self.dt
462 props.time_steps = self.time_steps
463 props.f = self.f
464 props.k = self.k
465 props.diff_a = self.diff_a
466 props.diff_b = self.diff_b
467 res_x = props.res_x
468 res_y = props.res_y
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()
477 img_a.update()
478 img_b.update()
480 return {'FINISHED'}
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")
495 @classmethod
496 def poll(cls, context):
497 return True
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)
506 ob = context.object
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)
511 else:
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)
516 else:
517 img_b = bpy.data.images.new(name="B", width=props.res_x, height=props.res_y)
518 props.run = True
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
525 #props.dt = self.dt
526 #props.time_steps = self.time_steps
527 #props.f = self.f
528 #props.k = self.k
529 #props.diff_a = self.diff_a
530 #props.diff_b = self.diff_b
532 return {'FINISHED'}
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'}
542 #@classmethod
543 #def poll(cls, context):
544 # return True
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
550 img_a = props.img_a
551 img_b = props.img_b
552 layout = self.layout
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",
557 icon="EXPERIMENTAL")
558 col = layout.column(align=True)
559 row = col.row(align=True)
560 row.prop(props, 'res_x')
561 row.prop(props, 'res_y')
562 col.separator()
563 col.prop_search(props, 'img_a', bpy.data, "images")
564 col.prop_search(props, 'img_b', bpy.data, "images")
565 else:
566 row.operator("object.reset_tex_reaction_diffusion",
567 icon="EXPERIMENTAL")
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')
574 col.separator()
575 col.prop_search(props, 'img_a', bpy.data, "images")
576 col.prop_search(props, 'img_b', bpy.data, "images")
577 col.separator()
578 row = col.row(align=True)
579 row.prop(props, "time_steps")
580 row.prop(props, "dt")
581 row.enabled = not props.bool_cache
582 col.separator()
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
593 #col.separator()
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
602 col.separator()
603 col.label(text='Cache:')
604 #col.prop(props, "bool_cache")
605 col.prop(props, "cache_dir", text='')
606 col.separator()
607 row = col.row(align=True)
608 row.prop(props, "cache_frame_start")
609 row.prop(props, "cache_frame_end")
610 col.separator()
611 if props.bool_cache:
612 col.operator("object.reaction_diffusion_free_data")
613 else:
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 == '':
619 row.enabled = False
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'}
632 @classmethod
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():
636 return True
637 else:
638 return False
640 def draw(self, context):
641 ob = context.object
642 props = ob.tex_reaction_diffusion_settings
643 layout = self.layout
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] != '':
669 if name == 'brush':
670 col2.prop(props, "brush_mult")
671 elif name == 'vector_field':
672 col2.prop(props, "anisotropy")
673 else:
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")
678 col.separator()