tissue updated to v0.3.70 #105180
@ -1,21 +1,15 @@
|
|||||||
# Tissue
|
# Tissue
|
||||||
![cover](http://www.co-de-it.com/wordpress/wp-content/uploads/2015/07/tissue_graphics.jpg)
|
![cover](http://www.co-de-it.com/wordpress/wp-content/uploads/2015/07/tissue_graphics.jpg)
|
||||||
Tissue - Blender's add-on for computational design by Co-de-iT
|
Tissue - Blender's add-on for computational design by Co-de-iT
|
||||||
http://www.co-de-it.com/wordpress/code/blender-tissue
|
https://www.co-de-it.com/code/blender-tissue
|
||||||
|
|
||||||
Tissue is already shipped with both Blender. However I recommend to update the default version downloading manually the most recent one, for more updated features and more stability.
|
Tissue is already shipped with both Blender. However I recommend to update the default version downloading manually the most recent one, for more updated features and more stability.
|
||||||
|
|
||||||
### Blender 2.93
|
### Blender 4.0.2
|
||||||
|
|
||||||
Tissue v0.3.52 for Blender 2.93 (latest stable release): https://github.com/alessandro-zomparelli/tissue/releases/tag/v0-3-52
|
Download the latest release: https://github.com/alessandro-zomparelli/tissue/releases
|
||||||
|
|
||||||
Development branch (usually the most updated version): https://github.com/alessandro-zomparelli/tissue/tree/b290-dev
|
Current development branch (usually the most updated version): https://github.com/alessandro-zomparelli/tissue/tree/b401-dev
|
||||||
|
|
||||||
### Blender 2.79 (unsupported)
|
|
||||||
|
|
||||||
Tissue v0.3.4 for Blender 2.79b (latest stable release): https://github.com/alessandro-zomparelli/tissue/releases/tag/v0-3-4
|
|
||||||
|
|
||||||
Development branch (most updated version): https://github.com/alessandro-zomparelli/tissue/tree/dev1
|
|
||||||
|
|
||||||
|
|
||||||
### Installation:
|
### Installation:
|
||||||
@ -27,21 +21,18 @@ Development branch (most updated version): https://github.com/alessandro-zompare
|
|||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
Tissue documentation for Blender 2.80: https://github.com/alessandro-zomparelli/tissue/wiki
|
Tissue documentation for Blender's latest version: https://docs.blender.org/manual/en/latest/addons/mesh/tissue.html
|
||||||
|
|
||||||
|
|
||||||
### Issues
|
### Issues
|
||||||
Please help me keeping Tissue stable and updated, report any issues or feedback here: https://github.com/alessandro-zomparelli/tissue/issues
|
Please help me keep Tissue stable and updated, report any issues or feedback here: https://github.com/alessandro-zomparelli/tissue/issues
|
||||||
|
|
||||||
### Contribute
|
### Contribute
|
||||||
Tissue is free and open-source. I really think that this is the power of Blender and I wanted to give my small contribution to it.
|
Tissue is free and open-source. I think that this is the power of Blender and I wanted to give my small contribution to it.
|
||||||
|
|
||||||
If you like my work and you want to help me, please consider to support me on **Patreon**, where I share some tips about Blender, Tissue and scripting: https://www.patreon.com/alessandrozomparelli
|
If you like my work and you want to help me, please consider supporting me on **Patreon**, where I share some tips about Blender, Tissue and scripting: https://www.patreon.com/alessandrozomparelli
|
||||||
|
|
||||||
[![Patreon](http://alessandrozomparelli.com/wp-content/uploads/2020/04/patreon-transparent-vector-small.png)](https://www.patreon.com/alessandrozomparelli)
|
[![Patreon](http://alessandrozomparelli.com/wp-content/uploads/2020/04/patreon-transparent-vector-small.png)](https://www.patreon.com/alessandrozomparelli)
|
||||||
|
|
||||||
A special thanks to all my patrons, in particular to my **Tissue Supporters**: *TomaLaboratory*, *Scott Shorter*, *Garrett Post*, *Kairomon*, *Art Evans*, *Justin Davis*, *John Wise*, *Avi Bryant*, *Ahmed Saber*, *SlimeSound Production*, *Steffen Meier*.
|
|
||||||
|
|
||||||
Many thanks,
|
Many thanks,
|
||||||
|
|
||||||
Alessandro
|
Alessandro
|
||||||
|
@ -1,6 +1,21 @@
|
|||||||
# SPDX-FileCopyrightText: 2017-2023 Blender Foundation
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
#
|
||||||
|
# ##### END GPL LICENSE BLOCK #####
|
||||||
|
|
||||||
# --------------------------------- TISSUE ----------------------------------- #
|
# --------------------------------- TISSUE ----------------------------------- #
|
||||||
# ------------------------------- version 0.3 -------------------------------- #
|
# ------------------------------- version 0.3 -------------------------------- #
|
||||||
@ -12,15 +27,15 @@
|
|||||||
# (2017) #
|
# (2017) #
|
||||||
# #
|
# #
|
||||||
# http://www.co-de-it.com/ #
|
# http://www.co-de-it.com/ #
|
||||||
# http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Mesh/Tissue #
|
# https://docs.blender.org/manual/en/latest/addons/mesh/tissue.html #
|
||||||
# #
|
# #
|
||||||
# ############################################################################ #
|
# ############################################################################ #
|
||||||
|
|
||||||
bl_info = {
|
bl_info = {
|
||||||
"name": "Tissue",
|
"name": "Tissue",
|
||||||
"author": "Alessandro Zomparelli (Co-de-iT)",
|
"author": "Alessandro Zomparelli",
|
||||||
"version": (0, 3, 54),
|
"version": (0, 3, 70),
|
||||||
"blender": (2, 93, 0),
|
"blender": (4, 0, 2),
|
||||||
"location": "",
|
"location": "",
|
||||||
"description": "Tools for Computational Design",
|
"description": "Tools for Computational Design",
|
||||||
"warning": "",
|
"warning": "",
|
||||||
@ -35,6 +50,7 @@ if "bpy" in locals():
|
|||||||
importlib.reload(tessellate_numpy)
|
importlib.reload(tessellate_numpy)
|
||||||
importlib.reload(tissue_properties)
|
importlib.reload(tissue_properties)
|
||||||
importlib.reload(weight_tools)
|
importlib.reload(weight_tools)
|
||||||
|
importlib.reload(weight_reaction_diffusion)
|
||||||
importlib.reload(dual_mesh)
|
importlib.reload(dual_mesh)
|
||||||
importlib.reload(lattice)
|
importlib.reload(lattice)
|
||||||
importlib.reload(uv_to_mesh)
|
importlib.reload(uv_to_mesh)
|
||||||
@ -43,11 +59,14 @@ if "bpy" in locals():
|
|||||||
importlib.reload(material_tools)
|
importlib.reload(material_tools)
|
||||||
importlib.reload(curves_tools)
|
importlib.reload(curves_tools)
|
||||||
importlib.reload(polyhedra)
|
importlib.reload(polyhedra)
|
||||||
|
importlib.reload(texture_reaction_diffusion)
|
||||||
|
importlib.reload(contour_curves)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
from . import tessellate_numpy
|
from . import tessellate_numpy
|
||||||
from . import tissue_properties
|
from . import tissue_properties
|
||||||
from . import weight_tools
|
from . import weight_tools
|
||||||
|
from . import weight_reaction_diffusion
|
||||||
from . import dual_mesh
|
from . import dual_mesh
|
||||||
from . import lattice
|
from . import lattice
|
||||||
from . import uv_to_mesh
|
from . import uv_to_mesh
|
||||||
@ -56,6 +75,8 @@ else:
|
|||||||
from . import material_tools
|
from . import material_tools
|
||||||
from . import curves_tools
|
from . import curves_tools
|
||||||
from . import polyhedra
|
from . import polyhedra
|
||||||
|
from . import texture_reaction_diffusion
|
||||||
|
from . import contour_curves
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
from bpy.props import PointerProperty, CollectionProperty, BoolProperty
|
from bpy.props import PointerProperty, CollectionProperty, BoolProperty
|
||||||
@ -86,6 +107,7 @@ classes = (
|
|||||||
tessellate_numpy.TISSUE_PT_tessellate_morphing,
|
tessellate_numpy.TISSUE_PT_tessellate_morphing,
|
||||||
tessellate_numpy.TISSUE_PT_tessellate_iterations,
|
tessellate_numpy.TISSUE_PT_tessellate_iterations,
|
||||||
tessellate_numpy.tissue_render_animation,
|
tessellate_numpy.tissue_render_animation,
|
||||||
|
tessellate_numpy.tissue_remove,
|
||||||
|
|
||||||
weight_tools.face_area_to_vertex_groups,
|
weight_tools.face_area_to_vertex_groups,
|
||||||
weight_tools.vertex_colors_to_vertex_groups,
|
weight_tools.vertex_colors_to_vertex_groups,
|
||||||
@ -93,31 +115,36 @@ classes = (
|
|||||||
weight_tools.vertex_group_to_uv,
|
weight_tools.vertex_group_to_uv,
|
||||||
weight_tools.TISSUE_PT_weight,
|
weight_tools.TISSUE_PT_weight,
|
||||||
weight_tools.TISSUE_PT_color,
|
weight_tools.TISSUE_PT_color,
|
||||||
weight_tools.weight_contour_curves,
|
|
||||||
weight_tools.tissue_weight_contour_curves_pattern,
|
|
||||||
weight_tools.weight_contour_mask,
|
weight_tools.weight_contour_mask,
|
||||||
weight_tools.weight_contour_displace,
|
weight_tools.weight_contour_displace,
|
||||||
weight_tools.harmonic_weight,
|
weight_tools.harmonic_weight,
|
||||||
weight_tools.edges_deformation,
|
weight_tools.edges_deformation,
|
||||||
weight_tools.edges_bending,
|
weight_tools.edges_bending,
|
||||||
weight_tools.weight_laplacian,
|
weight_tools.weight_laplacian,
|
||||||
weight_tools.reaction_diffusion,
|
weight_reaction_diffusion.start_reaction_diffusion,
|
||||||
weight_tools.start_reaction_diffusion,
|
weight_reaction_diffusion.TISSUE_PT_reaction_diffusion,
|
||||||
weight_tools.TISSUE_PT_reaction_diffusion,
|
weight_reaction_diffusion.TISSUE_PT_reaction_diffusion_performance,
|
||||||
weight_tools.TISSUE_PT_reaction_diffusion_weight,
|
weight_reaction_diffusion.TISSUE_PT_reaction_diffusion_vector_field,
|
||||||
weight_tools.reset_reaction_diffusion_weight,
|
weight_reaction_diffusion.TISSUE_PT_reaction_diffusion_weight,
|
||||||
|
weight_reaction_diffusion.TISSUE_PT_reaction_diffusion_cache,
|
||||||
|
weight_reaction_diffusion.reset_reaction_diffusion_weight,
|
||||||
weight_tools.formula_prop,
|
weight_tools.formula_prop,
|
||||||
weight_tools.reaction_diffusion_prop,
|
weight_reaction_diffusion.reaction_diffusion_prop,
|
||||||
weight_tools.weight_formula,
|
weight_tools.weight_formula,
|
||||||
weight_tools.update_weight_formula,
|
weight_tools.update_weight_formula,
|
||||||
weight_tools.curvature_to_vertex_groups,
|
weight_tools.curvature_to_vertex_groups,
|
||||||
weight_tools.weight_formula_wiki,
|
weight_tools.weight_formula_wiki,
|
||||||
weight_tools.tissue_weight_distance,
|
weight_tools.tissue_weight_distance,
|
||||||
weight_tools.random_weight,
|
weight_tools.random_weight,
|
||||||
weight_tools.bake_reaction_diffusion,
|
weight_reaction_diffusion.bake_reaction_diffusion,
|
||||||
weight_tools.reaction_diffusion_free_data,
|
weight_reaction_diffusion.reaction_diffusion_free_data,
|
||||||
weight_tools.tissue_weight_streamlines,
|
weight_tools.tissue_weight_streamlines,
|
||||||
|
|
||||||
|
contour_curves.tissue_weight_contour_curves_pattern,
|
||||||
|
contour_curves.tissue_update_contour_curves,
|
||||||
|
contour_curves.tissue_contour_curves_prop,
|
||||||
|
contour_curves.TISSUE_PT_contour_curves,
|
||||||
|
|
||||||
dual_mesh.dual_mesh,
|
dual_mesh.dual_mesh,
|
||||||
dual_mesh.dual_mesh_tessellated,
|
dual_mesh.dual_mesh_tessellated,
|
||||||
|
|
||||||
@ -128,12 +155,21 @@ classes = (
|
|||||||
|
|
||||||
curves_tools.tissue_to_curve_prop,
|
curves_tools.tissue_to_curve_prop,
|
||||||
curves_tools.tissue_convert_to_curve,
|
curves_tools.tissue_convert_to_curve,
|
||||||
curves_tools.tissue_convert_to_curve_update,
|
curves_tools.tissue_update_convert_to_curve,
|
||||||
curves_tools.TISSUE_PT_convert_to_curve,
|
curves_tools.TISSUE_PT_convert_to_curve,
|
||||||
|
|
||||||
uv_to_mesh.uv_to_mesh,
|
uv_to_mesh.uv_to_mesh,
|
||||||
|
|
||||||
polyhedra.polyhedra_wireframe
|
polyhedra.polyhedral_wireframe,
|
||||||
|
polyhedra.tissue_update_polyhedra,
|
||||||
|
polyhedra.tissue_polyhedra_prop,
|
||||||
|
polyhedra.TISSUE_PT_polyhedra_object,
|
||||||
|
|
||||||
|
texture_reaction_diffusion.tex_reaction_diffusion_prop,
|
||||||
|
texture_reaction_diffusion.start_tex_reaction_diffusion,
|
||||||
|
texture_reaction_diffusion.reset_tex_reaction_diffusion,
|
||||||
|
texture_reaction_diffusion.TISSUE_PT_tex_reaction_diffusion,
|
||||||
|
texture_reaction_diffusion.TISSUE_PT_tex_reaction_diffusion_images
|
||||||
)
|
)
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
@ -147,18 +183,27 @@ def register():
|
|||||||
bpy.types.Object.tissue_tessellate = PointerProperty(
|
bpy.types.Object.tissue_tessellate = PointerProperty(
|
||||||
type=tissue_properties.tissue_tessellate_prop
|
type=tissue_properties.tissue_tessellate_prop
|
||||||
)
|
)
|
||||||
|
bpy.types.Object.tissue_polyhedra = PointerProperty(
|
||||||
|
type=polyhedra.tissue_polyhedra_prop
|
||||||
|
)
|
||||||
bpy.types.Object.tissue_to_curve = PointerProperty(
|
bpy.types.Object.tissue_to_curve = PointerProperty(
|
||||||
type=curves_tools.tissue_to_curve_prop
|
type=curves_tools.tissue_to_curve_prop
|
||||||
)
|
)
|
||||||
|
bpy.types.Object.tissue_contour_curves = PointerProperty(
|
||||||
|
type=contour_curves.tissue_contour_curves_prop
|
||||||
|
)
|
||||||
bpy.types.Object.formula_settings = CollectionProperty(
|
bpy.types.Object.formula_settings = CollectionProperty(
|
||||||
type=weight_tools.formula_prop
|
type=weight_tools.formula_prop
|
||||||
)
|
)
|
||||||
bpy.types.Object.reaction_diffusion_settings = PointerProperty(
|
bpy.types.Object.reaction_diffusion_settings = PointerProperty(
|
||||||
type=weight_tools.reaction_diffusion_prop
|
type=weight_reaction_diffusion.reaction_diffusion_prop
|
||||||
)
|
)
|
||||||
|
bpy.types.Object.tex_reaction_diffusion_settings = PointerProperty(
|
||||||
|
type=texture_reaction_diffusion.tex_reaction_diffusion_prop
|
||||||
|
)
|
||||||
# weight_tools
|
# weight_tools
|
||||||
bpy.app.handlers.frame_change_post.append(weight_tools.reaction_diffusion_def)
|
bpy.app.handlers.frame_change_post.append(weight_reaction_diffusion.reaction_diffusion_def)
|
||||||
#bpy.app.handlers.frame_change_post.append(tessellate_numpy.anim_tessellate)
|
bpy.app.handlers.frame_change_post.append(texture_reaction_diffusion.tex_reaction_diffusion_def)
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
from bpy.utils import unregister_class
|
from bpy.utils import unregister_class
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
# SPDX-FileCopyrightText: 2022-2023 Blender Foundation
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
@ -15,7 +13,7 @@ class tissuePreferences(bpy.types.AddonPreferences):
|
|||||||
|
|
||||||
print_stats : IntProperty(
|
print_stats : IntProperty(
|
||||||
name="Print Stats",
|
name="Print Stats",
|
||||||
description="Print in the console all details about the computing time",
|
description="Print in the console all details about the computing time.",
|
||||||
default=1,
|
default=1,
|
||||||
min=0,
|
min=0,
|
||||||
max=4
|
max=4
|
||||||
@ -37,9 +35,13 @@ class tissuePreferences(bpy.types.AddonPreferences):
|
|||||||
numba_spec = importlib.util.find_spec('numba')
|
numba_spec = importlib.util.find_spec('numba')
|
||||||
found = numba_spec is not None
|
found = numba_spec is not None
|
||||||
if found:
|
if found:
|
||||||
layout.label(text='Numba module installed correctly!', icon='INFO')
|
try:
|
||||||
layout.prop(self, "use_numba_tess")
|
import numba
|
||||||
else:
|
layout.label(text='Numba module installed correctly!', icon='INFO')
|
||||||
|
layout.prop(self, "use_numba_tess")
|
||||||
|
except:
|
||||||
|
found = False
|
||||||
|
if not found:
|
||||||
layout.label(text='Numba module not installed!', icon='ERROR')
|
layout.label(text='Numba module not installed!', icon='ERROR')
|
||||||
layout.label(text='Installing Numba will make Tissue faster', icon='INFO')
|
layout.label(text='Installing Numba will make Tissue faster', icon='INFO')
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
@ -56,6 +58,8 @@ class tissue_install_numba(bpy.types.Operator):
|
|||||||
try:
|
try:
|
||||||
from .utils_pip import Pip
|
from .utils_pip import Pip
|
||||||
#Pip.upgrade_pip()
|
#Pip.upgrade_pip()
|
||||||
|
Pip.uninstall('llvmlite')
|
||||||
|
Pip.uninstall('numba')
|
||||||
Pip.install('llvmlite')
|
Pip.install('llvmlite')
|
||||||
Pip.install('numba')
|
Pip.install('numba')
|
||||||
from numba import jit, njit, guvectorize, float64, int32, prange
|
from numba import jit, njit, guvectorize, float64, int32, prange
|
||||||
|
1189
mesh_tissue/contour_curves.py
Normal file
1189
mesh_tissue/contour_curves.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,23 @@
|
|||||||
# SPDX-FileCopyrightText: 2022-2023 Blender Foundation
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
#
|
||||||
|
# ##### END GPL LICENSE BLOCK #####
|
||||||
|
|
||||||
# #
|
# #
|
||||||
# (c) Alessandro Zomparelli #
|
# (c) Alessandro Zomparelli #
|
||||||
# (2017) #
|
# (2017) #
|
||||||
@ -37,7 +53,8 @@ from .utils import (
|
|||||||
convert_object_to_mesh,
|
convert_object_to_mesh,
|
||||||
get_weight_numpy,
|
get_weight_numpy,
|
||||||
loops_from_bmesh,
|
loops_from_bmesh,
|
||||||
get_mesh_before_subs
|
get_mesh_before_subs,
|
||||||
|
tissue_time
|
||||||
)
|
)
|
||||||
import time
|
import time
|
||||||
|
|
||||||
@ -48,7 +65,7 @@ def anim_curve_active(self, context):
|
|||||||
try:
|
try:
|
||||||
props.object.name
|
props.object.name
|
||||||
if not ob.tissue.bool_lock:
|
if not ob.tissue.bool_lock:
|
||||||
bpy.ops.object.tissue_convert_to_curve_update()
|
bpy.ops.object.tissue_update_convert_to_curve()
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
|
|
||||||
@ -67,7 +84,7 @@ class tissue_to_curve_prop(PropertyGroup):
|
|||||||
)
|
)
|
||||||
bool_lock : BoolProperty(
|
bool_lock : BoolProperty(
|
||||||
name="Lock",
|
name="Lock",
|
||||||
description="Prevent automatic update on settings changes or if other objects have it in the hierarchy",
|
description="Prevent automatic update on settings changes or if other objects have it in the hierarchy.",
|
||||||
default=False,
|
default=False,
|
||||||
update = anim_curve_active
|
update = anim_curve_active
|
||||||
)
|
)
|
||||||
@ -79,7 +96,7 @@ class tissue_to_curve_prop(PropertyGroup):
|
|||||||
)
|
)
|
||||||
bool_run : BoolProperty(
|
bool_run : BoolProperty(
|
||||||
name="Animatable Curve",
|
name="Animatable Curve",
|
||||||
description="Automatically recompute the conversion when the frame is changed",
|
description="Automatically recompute the conversion when the frame is changed.",
|
||||||
default = False
|
default = False
|
||||||
)
|
)
|
||||||
use_modifiers : BoolProperty(
|
use_modifiers : BoolProperty(
|
||||||
@ -480,12 +497,12 @@ class tissue_convert_to_curve(Operator):
|
|||||||
|
|
||||||
new_ob.tissue.bool_lock = False
|
new_ob.tissue.bool_lock = False
|
||||||
|
|
||||||
bpy.ops.object.tissue_convert_to_curve_update()
|
bpy.ops.object.tissue_update_convert_to_curve()
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
class tissue_convert_to_curve_update(Operator):
|
class tissue_update_convert_to_curve(Operator):
|
||||||
bl_idname = "object.tissue_convert_to_curve_update"
|
bl_idname = "object.tissue_update_convert_to_curve"
|
||||||
bl_label = "Tissue Update Curve"
|
bl_label = "Tissue Update Curve"
|
||||||
bl_description = "Update Curve object"
|
bl_description = "Update Curve object"
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
@ -500,9 +517,10 @@ class tissue_convert_to_curve_update(Operator):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
ob = context.object
|
||||||
|
tissue_time(None,'Tissue: Convert to Curve of "{}"...'.format(ob.name), levels=0)
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
|
|
||||||
ob = context.object
|
|
||||||
props = ob.tissue_to_curve
|
props = ob.tissue_to_curve
|
||||||
ob0 = props.object
|
ob0 = props.object
|
||||||
if props.mode == 'PARTICLES':
|
if props.mode == 'PARTICLES':
|
||||||
@ -669,8 +687,7 @@ class tissue_convert_to_curve_update(Operator):
|
|||||||
ob.data.splines.update()
|
ob.data.splines.update()
|
||||||
if not props.bool_smooth: bpy.ops.object.shade_flat()
|
if not props.bool_smooth: bpy.ops.object.shade_flat()
|
||||||
|
|
||||||
end_time = time.time()
|
tissue_time(start_time,'Convert to Curve',levels=0)
|
||||||
print('Tissue: object "{}" converted to Curve in {:.4f} sec'.format(ob.name, end_time-start_time))
|
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
@ -700,7 +717,7 @@ class TISSUE_PT_convert_to_curve(Panel):
|
|||||||
#layout.use_property_decorate = False
|
#layout.use_property_decorate = False
|
||||||
col = layout.column(align=True)
|
col = layout.column(align=True)
|
||||||
row = col.row(align=True)
|
row = col.row(align=True)
|
||||||
#col.operator("object.tissue_convert_to_curve_update", icon='FILE_REFRESH', text='Refresh')
|
#col.operator("object.tissue_update_convert_to_curve", icon='FILE_REFRESH', text='Refresh')
|
||||||
row.operator("object.tissue_update_tessellate_deps", icon='FILE_REFRESH', text='Refresh') ####
|
row.operator("object.tissue_update_tessellate_deps", icon='FILE_REFRESH', text='Refresh') ####
|
||||||
lock_icon = 'LOCKED' if ob.tissue.bool_lock else 'UNLOCKED'
|
lock_icon = 'LOCKED' if ob.tissue.bool_lock else 'UNLOCKED'
|
||||||
#lock_icon = 'PINNED' if props.bool_lock else 'UNPINNED'
|
#lock_icon = 'PINNED' if props.bool_lock else 'UNPINNED'
|
||||||
@ -710,6 +727,8 @@ class TISSUE_PT_convert_to_curve(Panel):
|
|||||||
col2 = row.column(align=True)
|
col2 = row.column(align=True)
|
||||||
col2.prop(ob.tissue, "bool_run", text="",icon='TIME')
|
col2.prop(ob.tissue, "bool_run", text="",icon='TIME')
|
||||||
col2.enabled = not ob.tissue.bool_lock
|
col2.enabled = not ob.tissue.bool_lock
|
||||||
|
col2 = row.column(align=True)
|
||||||
|
col2.operator("mesh.tissue_remove", text="", icon='X')
|
||||||
|
|
||||||
col.separator()
|
col.separator()
|
||||||
row = col.row(align=True)
|
row = col.row(align=True)
|
||||||
|
@ -1,7 +1,23 @@
|
|||||||
# SPDX-FileCopyrightText: 2017-2023 Blender Foundation
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
#
|
||||||
|
# ##### END GPL LICENSE BLOCK #####
|
||||||
|
|
||||||
# --------------------------------- DUAL MESH -------------------------------- #
|
# --------------------------------- DUAL MESH -------------------------------- #
|
||||||
# -------------------------------- version 0.3 ------------------------------- #
|
# -------------------------------- version 0.3 ------------------------------- #
|
||||||
# #
|
# #
|
||||||
@ -230,10 +246,14 @@ class dual_mesh(Operator):
|
|||||||
)
|
)
|
||||||
bpy.ops.mesh.select_all(action='DESELECT')
|
bpy.ops.mesh.select_all(action='DESELECT')
|
||||||
bpy.ops.object.mode_set(mode='OBJECT')
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
subsurf_modifier = context.object.modifiers.new("dual_mesh_subsurf", 'SUBSURF')
|
bpy.ops.object.modifier_add(type='SUBSURF')
|
||||||
context.object.modifiers.move(len(context.object.modifiers)-1, 0)
|
ob.modifiers[-1].name = "dual_mesh_subsurf"
|
||||||
|
while True:
|
||||||
|
bpy.ops.object.modifier_move_up(modifier="dual_mesh_subsurf")
|
||||||
|
if ob.modifiers[0].name == "dual_mesh_subsurf":
|
||||||
|
break
|
||||||
|
|
||||||
bpy.ops.object.modifier_apply(modifier=subsurf_modifier.name)
|
bpy.ops.object.modifier_apply(modifier='dual_mesh_subsurf')
|
||||||
|
|
||||||
bpy.ops.object.mode_set(mode='EDIT')
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
bpy.ops.mesh.select_all(action='DESELECT')
|
bpy.ops.mesh.select_all(action='DESELECT')
|
||||||
@ -246,7 +266,7 @@ class dual_mesh(Operator):
|
|||||||
bpy.ops.mesh.select_more(use_face_step=False)
|
bpy.ops.mesh.select_more(use_face_step=False)
|
||||||
|
|
||||||
bpy.ops.mesh.select_similar(
|
bpy.ops.mesh.select_similar(
|
||||||
type='EDGE', compare='EQUAL', threshold=0.01)
|
type='VERT_EDGES', compare='EQUAL', threshold=0.01)
|
||||||
bpy.ops.mesh.select_all(action='INVERT')
|
bpy.ops.mesh.select_all(action='INVERT')
|
||||||
|
|
||||||
bpy.ops.mesh.dissolve_verts()
|
bpy.ops.mesh.dissolve_verts()
|
||||||
|
@ -1,7 +1,20 @@
|
|||||||
# SPDX-FileCopyrightText: 2017-2023 Blender Foundation
|
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
#
|
||||||
|
# ##### END GPL LICENSE BLOCK #####
|
||||||
# --------------------------- LATTICE ALONG SURFACE -------------------------- #
|
# --------------------------- LATTICE ALONG SURFACE -------------------------- #
|
||||||
# -------------------------------- version 0.3 ------------------------------- #
|
# -------------------------------- version 0.3 ------------------------------- #
|
||||||
# #
|
# #
|
||||||
@ -327,6 +340,7 @@ class lattice_along_surface(Operator):
|
|||||||
grid_mesh = temp_grid_obj.data
|
grid_mesh = temp_grid_obj.data
|
||||||
for v in grid_mesh.vertices:
|
for v in grid_mesh.vertices:
|
||||||
v.co = grid_obj.matrix_world @ v.co
|
v.co = grid_obj.matrix_world @ v.co
|
||||||
|
#grid_mesh.calc_normals()
|
||||||
|
|
||||||
if len(grid_mesh.polygons) > 64 * 64:
|
if len(grid_mesh.polygons) > 64 * 64:
|
||||||
bpy.data.objects.remove(temp_grid_obj)
|
bpy.data.objects.remove(temp_grid_obj)
|
||||||
@ -372,13 +386,16 @@ class lattice_along_surface(Operator):
|
|||||||
lattice.scale.z = 1
|
lattice.scale.z = 1
|
||||||
|
|
||||||
context.view_layer.objects.active = obj
|
context.view_layer.objects.active = obj
|
||||||
lattice_modifier = context.object.modifiers.new("", 'LATTICE')
|
bpy.ops.object.modifier_add(type='LATTICE')
|
||||||
lattice_modifier.object = lattice
|
obj.modifiers[-1].object = lattice
|
||||||
|
|
||||||
# set as parent
|
# set as parent
|
||||||
if self.set_parent:
|
if self.set_parent:
|
||||||
override = {'active_object': obj, 'selected_objects' : [lattice,obj]}
|
override = context.copy()
|
||||||
bpy.ops.object.parent_set(override, type='OBJECT', keep_transform=False)
|
override['active_object'] = obj
|
||||||
|
override['selected_objects'] = [lattice,obj]
|
||||||
|
with context.temp_override(**override):
|
||||||
|
bpy.ops.object.parent_set(type='OBJECT', keep_transform=False)
|
||||||
|
|
||||||
# reading grid structure
|
# reading grid structure
|
||||||
verts_grid, edges_grid, faces_grid = grid_from_mesh(
|
verts_grid, edges_grid, faces_grid = grid_from_mesh(
|
||||||
@ -434,7 +451,7 @@ class lattice_along_surface(Operator):
|
|||||||
bpy.ops.object.delete(use_global=False)
|
bpy.ops.object.delete(use_global=False)
|
||||||
context.view_layer.objects.active = obj
|
context.view_layer.objects.active = obj
|
||||||
obj.select_set(True)
|
obj.select_set(True)
|
||||||
bpy.ops.object.modifier_remove(modifier=lattice_modifier.name)
|
bpy.ops.object.modifier_remove(modifier=obj.modifiers[-1].name)
|
||||||
if nu > 64 or nv > 64:
|
if nu > 64 or nv > 64:
|
||||||
self.report({'ERROR'}, "Maximum resolution allowed for Lattice is 64")
|
self.report({'ERROR'}, "Maximum resolution allowed for Lattice is 64")
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
@ -1,6 +1,20 @@
|
|||||||
# SPDX-FileCopyrightText: 2022 Blender Foundation
|
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
#
|
||||||
|
# ##### END GPL LICENSE BLOCK #####
|
||||||
|
|
||||||
# #
|
# #
|
||||||
# (c) Alessandro Zomparelli #
|
# (c) Alessandro Zomparelli #
|
||||||
@ -210,8 +224,8 @@ class weight_to_materials(Operator):
|
|||||||
faces_weight.append(w)
|
faces_weight.append(w)
|
||||||
faces_weight = np.array(faces_weight)
|
faces_weight = np.array(faces_weight)
|
||||||
faces_weight = faces_weight * count
|
faces_weight = faces_weight * count
|
||||||
faces_weight.astype('int')
|
faces_weight = list(faces_weight.astype('int'))
|
||||||
ob.data.polygons.foreach_set('material_index',list(faces_weight))
|
ob.data.polygons.foreach_set('material_index', faces_weight)
|
||||||
ob.data.update()
|
ob.data.update()
|
||||||
bpy.ops.object.mode_set(mode='OBJECT')
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
@ -1,6 +1,20 @@
|
|||||||
# SPDX-FileCopyrightText: 2019-2022 Blender Foundation
|
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
#
|
||||||
|
# ##### END GPL LICENSE BLOCK #####
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import time
|
import time
|
||||||
@ -32,11 +46,87 @@ except:
|
|||||||
if bool_numba:
|
if bool_numba:
|
||||||
#from numba import jit, njit, guvectorize, float64, int32, prange
|
#from numba import jit, njit, guvectorize, float64, int32, prange
|
||||||
|
|
||||||
|
|
||||||
|
@njit(parallel=True)
|
||||||
|
#@cuda.jit('void(float32[:,:], float32[:,:])')
|
||||||
|
def tex_laplacian(lap, arr):
|
||||||
|
arr2 = arr*2
|
||||||
|
diag = sqrt(2)/2
|
||||||
|
nx = arr.shape[0]
|
||||||
|
ny = arr.shape[1]
|
||||||
|
for i in prange(nx):
|
||||||
|
for j in prange(ny):
|
||||||
|
i0 = (i-1)%nx
|
||||||
|
j0 = (j-1)%ny
|
||||||
|
i1 = (i+1)%nx
|
||||||
|
j1 = (j+1)%ny
|
||||||
|
#lap[i+1,j+1] = arr[i, j+1] + arr[i+2, j+1] + arr[i+1, j] + arr[i+1, j+2] - 4*arr[i+1,j+1]
|
||||||
|
|
||||||
|
lap[i,j] = ((arr[i0, j] + arr[i1, j] - arr2[i,j]) + \
|
||||||
|
(arr[i, j0] + arr[i, j1] - arr2[i,j]) + \
|
||||||
|
(arr[i0, j0] + arr[i1, j1] - arr2[i,j])*diag + \
|
||||||
|
(arr[i1, j0] + arr[i0, j1] - arr2[i,j])*diag)*0.75
|
||||||
|
|
||||||
|
@njit(parallel=True)
|
||||||
|
def tex_laplacian_ani(lap, arr, VF):
|
||||||
|
arr2 = arr*2
|
||||||
|
nx = arr.shape[0]
|
||||||
|
ny = arr.shape[1]
|
||||||
|
i0 = np.arange(nx)-1
|
||||||
|
i0[0] = 1
|
||||||
|
i1 = np.arange(nx)+1
|
||||||
|
i1[nx-1] = nx-2
|
||||||
|
j0 = np.arange(ny)-1
|
||||||
|
j0[0] = 1
|
||||||
|
j1 = np.arange(ny)+1
|
||||||
|
j1[ny-1] = ny-2
|
||||||
|
for i in prange(nx):
|
||||||
|
for j in prange(ny):
|
||||||
|
lap[i,j] = (arr[i0[i], j] + arr[i1[i], j] - arr2[i,j])*VF[0,i,j] + \
|
||||||
|
(arr[i, j0[j]] + arr[i, j1[j]] - arr2[i,j])*VF[1,i,j] + \
|
||||||
|
(arr[i0[i], j0[j]] + arr[i1[i], j1[j]] - arr2[i,j])*VF[2,i,j] + \
|
||||||
|
(arr[i1[i], j0[j]] + arr[i0[i], j1[j]] - arr2[i,j])*VF[3,i,j]
|
||||||
|
#lap[0,:] = lap[1,:]
|
||||||
|
#lap[:,0] = lap[:,1]
|
||||||
|
#lap[-1,:] = lap[-2,:]
|
||||||
|
#lap[:,-1] = lap[:,-2]
|
||||||
|
|
||||||
|
#@cuda.jit(parallel=True)
|
||||||
|
@njit(parallel=True)
|
||||||
|
def run_tex_rd(A, B, lap_A, lap_B, diff_A, diff_B, f, k, dt, steps, brush):
|
||||||
|
for t in range(steps):
|
||||||
|
tex_laplacian(lap_A, A)
|
||||||
|
tex_laplacian(lap_B, B)
|
||||||
|
nx = A.shape[0]
|
||||||
|
ny = A.shape[1]
|
||||||
|
for i in prange(nx):
|
||||||
|
for j in prange(ny):
|
||||||
|
B[i,j] += brush[i,j]
|
||||||
|
ab2 = A[i,j]*B[i,j]**2
|
||||||
|
A[i,j] += (lap_A[i,j]*diff_A - ab2 + f*(1-A[i,j]))*dt
|
||||||
|
B[i,j] += (lap_B[i,j]*diff_B + ab2 - (k+f)*B[i,j])*dt
|
||||||
|
|
||||||
|
@njit(parallel=True)
|
||||||
|
def run_tex_rd_ani(A, B, lap_A, lap_B, diff_A, diff_B, f, k, dt, steps, vf1, vf2, brush):
|
||||||
|
for t in range(steps):
|
||||||
|
tex_laplacian_ani(lap_A, A, vf2)
|
||||||
|
#laplacian(lap_A, A)
|
||||||
|
tex_laplacian_ani(lap_B, B, vf1)
|
||||||
|
nx = A.shape[0]
|
||||||
|
ny = A.shape[1]
|
||||||
|
for i in prange(nx):
|
||||||
|
for j in prange(ny):
|
||||||
|
B[i,j] += brush[i,j]
|
||||||
|
ab2 = A[i ,j]*B[i,j]**2
|
||||||
|
A[i,j] += (lap_A[i,j]*diff_A[i,j] - ab2 + f[i,j]*(1-A[i,j]))*dt
|
||||||
|
B[i,j] += (lap_B[i,j]*diff_B[i,j] + ab2 - (k[i,j]+f[i,j])*B[i,j])*dt
|
||||||
|
|
||||||
|
|
||||||
@njit(parallel=True)
|
@njit(parallel=True)
|
||||||
def numba_reaction_diffusion(n_verts, n_edges, edge_verts, a, b, brush, diff_a, diff_b, f, k, dt, time_steps):
|
def numba_reaction_diffusion(n_verts, n_edges, edge_verts, a, b, brush, diff_a, diff_b, f, k, dt, time_steps):
|
||||||
arr = np.arange(n_edges)*2
|
arr = np.arange(n_edges)
|
||||||
id0 = edge_verts[arr]
|
id0 = edge_verts[arr*2]
|
||||||
id1 = edge_verts[arr+1]
|
id1 = edge_verts[arr*2+1]
|
||||||
for i in range(time_steps):
|
for i in range(time_steps):
|
||||||
lap_a, lap_b = rd_init_laplacian(n_verts)
|
lap_a, lap_b = rd_init_laplacian(n_verts)
|
||||||
numba_rd_laplacian(id0, id1, a, b, lap_a, lap_b)
|
numba_rd_laplacian(id0, id1, a, b, lap_a, lap_b)
|
||||||
@ -62,17 +152,14 @@ if bool_numba:
|
|||||||
return values
|
return values
|
||||||
|
|
||||||
@njit(parallel=True)
|
@njit(parallel=True)
|
||||||
def numba_reaction_diffusion_anisotropic(n_verts, n_edges, edge_verts, a, b, brush, diff_a, diff_b, f, k, dt, time_steps, grad):
|
def numba_reaction_diffusion_anisotropic(n_verts, n_edges, edge_verts, a, b, brush, diff_a, diff_b, f, k, dt, time_steps, field_mult):
|
||||||
arr = np.arange(n_edges)*2
|
arr = np.arange(n_edges)
|
||||||
id0 = edge_verts[arr]
|
id0 = edge_verts[arr*2]
|
||||||
id1 = edge_verts[arr+1]
|
id1 = edge_verts[arr*2+1]
|
||||||
#grad = weight_grad[id0] - weight_grad[id1]
|
mult = field_mult[arr]
|
||||||
#grad = np.abs(grad)
|
|
||||||
#grad /= abs(np.max(grad))
|
|
||||||
#grad = grad*0.98 + 0.02
|
|
||||||
for i in range(time_steps):
|
for i in range(time_steps):
|
||||||
lap_a, lap_b = rd_init_laplacian(n_verts)
|
lap_a, lap_b = rd_init_laplacian(n_verts)
|
||||||
numba_rd_laplacian_anisotropic(id0, id1, a, b, lap_a, lap_b, grad)
|
numba_rd_laplacian_anisotropic(id0, id1, a, b, lap_a, lap_b, mult)
|
||||||
numba_rd_core(a, b, lap_a, lap_b, diff_a, diff_b, f, k, dt)
|
numba_rd_core(a, b, lap_a, lap_b, diff_a, diff_b, f, k, dt)
|
||||||
numba_set_ab(a,b,brush)
|
numba_set_ab(a,b,brush)
|
||||||
return a,b
|
return a,b
|
||||||
@ -112,28 +199,27 @@ if bool_numba:
|
|||||||
if a[i] < 0: a[i] = 0
|
if a[i] < 0: a[i] = 0
|
||||||
elif a[i] > 1: a[i] = 1
|
elif a[i] > 1: a[i] = 1
|
||||||
|
|
||||||
|
|
||||||
#@guvectorize(['(float64[:] ,float64[:] ,float64[:] , float64[:], float64[:], float64[:])'],'(m),(m),(n),(n),(n),(n)',target='parallel')
|
|
||||||
@njit(parallel=True)
|
@njit(parallel=True)
|
||||||
def numba_rd_laplacian(id0, id1, a, b, lap_a, lap_b):
|
def numba_rd_laplacian(id0, id1, a, b, lap_a, lap_b):
|
||||||
for i in prange(len(id0)):
|
for i in prange(len(id0)):
|
||||||
v0 = id0[i]
|
v0 = id0[i]
|
||||||
v1 = id1[i]
|
v1 = id1[i]
|
||||||
lap_a[v0] += a[v1] - a[v0]
|
lap_a[v0] += (a[v1] - a[v0])
|
||||||
lap_a[v1] += a[v0] - a[v1]
|
lap_a[v1] += (a[v0] - a[v1])
|
||||||
lap_b[v0] += b[v1] - b[v0]
|
lap_b[v0] += (b[v1] - b[v0])
|
||||||
lap_b[v1] += b[v0] - b[v1]
|
lap_b[v1] += (b[v0] - b[v1])
|
||||||
#return lap_a, lap_b
|
#return lap_a, lap_b
|
||||||
|
|
||||||
@njit(parallel=True)
|
@njit(parallel=True)
|
||||||
def numba_rd_laplacian_anisotropic(id0, id1, a, b, lap_a, lap_b, grad):
|
def numba_rd_laplacian_anisotropic(id0, id1, a, b, lap_a, lap_b, mult):
|
||||||
for i in prange(len(id0)):
|
for i in prange(len(id0)):
|
||||||
v0 = id0[i]
|
v0 = id0[i]
|
||||||
v1 = id1[i]
|
v1 = id1[i]
|
||||||
lap_a[v0] += (a[v1] - a[v0])
|
multiplier = mult[i]
|
||||||
lap_a[v1] += (a[v0] - a[v1])
|
lap_a[v0] += (a[v1] - a[v0])# * multiplier
|
||||||
lap_b[v0] -= (b[v1] - b[v0])*grad[i]
|
lap_a[v1] += (a[v0] - a[v1])# * multiplier
|
||||||
lap_b[v1] += (b[v0] - b[v1])*grad[i]
|
lap_b[v0] += (b[v1] - b[v0]) * multiplier
|
||||||
|
lap_b[v1] += (b[v0] - b[v1]) * multiplier
|
||||||
#return lap_a, lap_b
|
#return lap_a, lap_b
|
||||||
|
|
||||||
@njit(parallel=True)
|
@njit(parallel=True)
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
694
mesh_tissue/texture_reaction_diffusion.py
Normal file
694
mesh_tissue/texture_reaction_diffusion.py
Normal file
@ -0,0 +1,694 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
#
|
||||||
|
# ##### END GPL LICENSE BLOCK #####
|
||||||
|
|
||||||
|
#-------------------------- COLORS / GROUPS EXCHANGER -------------------------#
|
||||||
|
# #
|
||||||
|
# Vertex Color to Vertex Group allow you to convert colors channles to weight #
|
||||||
|
# maps. #
|
||||||
|
# The main purpose is to use vertex colors to store information when importing #
|
||||||
|
# files from other softwares. The script works with the active vertex color #
|
||||||
|
# slot. #
|
||||||
|
# For use the command "Vertex Clors to Vertex Groups" use the search bar #
|
||||||
|
# (space bar). #
|
||||||
|
# #
|
||||||
|
# (c) Alessandro Zomparelli #
|
||||||
|
# (2017) #
|
||||||
|
# #
|
||||||
|
# http://www.co-de-it.com/ #
|
||||||
|
# #
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
import bpy, bmesh, os
|
||||||
|
import numpy as np
|
||||||
|
import math, timeit, time
|
||||||
|
from math import pi
|
||||||
|
from statistics import mean, stdev
|
||||||
|
from mathutils import Vector
|
||||||
|
from mathutils.kdtree import KDTree
|
||||||
|
from numpy import *
|
||||||
|
try: from .numba_functions import run_tex_rd, run_tex_rd_ani
|
||||||
|
except: pass
|
||||||
|
#from .numba_functions import integrate_field
|
||||||
|
#from .numba_functions import numba_reaction_diffusion
|
||||||
|
try: import numexpr as ne
|
||||||
|
except: pass
|
||||||
|
|
||||||
|
# Reaction-Diffusion cache
|
||||||
|
from pathlib import Path
|
||||||
|
import random as rnd
|
||||||
|
import string
|
||||||
|
|
||||||
|
from bpy.types import (
|
||||||
|
Operator,
|
||||||
|
Panel,
|
||||||
|
PropertyGroup,
|
||||||
|
)
|
||||||
|
|
||||||
|
from bpy.props import (
|
||||||
|
BoolProperty,
|
||||||
|
EnumProperty,
|
||||||
|
FloatProperty,
|
||||||
|
IntProperty,
|
||||||
|
StringProperty,
|
||||||
|
FloatVectorProperty,
|
||||||
|
IntVectorProperty
|
||||||
|
)
|
||||||
|
|
||||||
|
from .utils import *
|
||||||
|
|
||||||
|
|
||||||
|
def tex_reaction_diffusion_add_handler(self, context):
|
||||||
|
# remove existing handlers
|
||||||
|
tex_reaction_diffusion_remove_handler(self, context)
|
||||||
|
# add new handler
|
||||||
|
bpy.app.handlers.frame_change_post.append(tex_rd_scene)
|
||||||
|
|
||||||
|
|
||||||
|
def tex_reaction_diffusion_remove_handler(self, context):
|
||||||
|
# remove existing handlers
|
||||||
|
old_handlers = []
|
||||||
|
for h in bpy.app.handlers.frame_change_post:
|
||||||
|
if "tex_rd" in str(h):
|
||||||
|
old_handlers.append(h)
|
||||||
|
for h in old_handlers: bpy.app.handlers.frame_change_post.remove(h)
|
||||||
|
|
||||||
|
|
||||||
|
class tex_reaction_diffusion_prop(PropertyGroup):
|
||||||
|
run : BoolProperty(default=False, update = tex_reaction_diffusion_add_handler,
|
||||||
|
description='Compute a new iteration on frame changes. Currently is not working during Render Animation')
|
||||||
|
|
||||||
|
res_x : IntProperty(
|
||||||
|
name="Resolution X", default=512, min=2, soft_max=1000,
|
||||||
|
description="Resolution of the simulation")
|
||||||
|
|
||||||
|
res_y : IntProperty(
|
||||||
|
name="Resolution Y", default=512, min=2, soft_max=1000,
|
||||||
|
description="Resolution of the simulation")
|
||||||
|
|
||||||
|
time_steps : IntProperty(
|
||||||
|
name="Steps", default=10, min=0, soft_max=50,
|
||||||
|
description="Number of Steps")
|
||||||
|
|
||||||
|
dt : FloatProperty(
|
||||||
|
name="dt", default=1, min=0, soft_max=0.2,
|
||||||
|
description="Time Step")
|
||||||
|
|
||||||
|
diff_a : FloatProperty(
|
||||||
|
name="Diff A", default=0.14, min=0, soft_max=2, precision=3,
|
||||||
|
description="Diffusion A")
|
||||||
|
|
||||||
|
diff_b : FloatProperty(
|
||||||
|
name="Diff B", default=0.07, min=0, soft_max=2, precision=3,
|
||||||
|
description="Diffusion B")
|
||||||
|
|
||||||
|
f : FloatProperty(
|
||||||
|
name="f", default=0.055, soft_min=0.01, soft_max=0.06, precision=4, step=0.05,
|
||||||
|
description="Feed Rate")
|
||||||
|
|
||||||
|
k : FloatProperty(
|
||||||
|
name="k", default=0.062, soft_min=0.035, soft_max=0.065, precision=4, step=0.05,
|
||||||
|
description="Kill Rate")
|
||||||
|
|
||||||
|
diff_mult : FloatProperty(
|
||||||
|
name="Scale", default=1, min=0, soft_max=1, max=10, precision=2,
|
||||||
|
description="Multiplier for the diffusion of both substances")
|
||||||
|
|
||||||
|
anisotropy : FloatProperty(
|
||||||
|
name="Anisotropy", default=0.5, min=0, max=1, precision=2,
|
||||||
|
description="Influence of the Vector Field")
|
||||||
|
|
||||||
|
img_vector_field : StringProperty(
|
||||||
|
name="Vector Field", default='',
|
||||||
|
description="Image used for the Vector Field. RGB to XY")
|
||||||
|
|
||||||
|
img_a : StringProperty(
|
||||||
|
name="A", default='',
|
||||||
|
description="Image used for the chemical A")
|
||||||
|
|
||||||
|
img_b : StringProperty(
|
||||||
|
name="B", default='',
|
||||||
|
description="Image used for the chemical B")
|
||||||
|
|
||||||
|
img_diff_a : StringProperty(
|
||||||
|
name="Diff A", default='',
|
||||||
|
description="Image used for A diffusion")
|
||||||
|
|
||||||
|
img_diff_b : StringProperty(
|
||||||
|
name="Diff B", default='',
|
||||||
|
description="Image used for B diffusion")
|
||||||
|
|
||||||
|
img_scale : StringProperty(
|
||||||
|
name="Scale", default='',
|
||||||
|
description="Image used for Scale value")
|
||||||
|
|
||||||
|
img_f : StringProperty(
|
||||||
|
name="f", default='',
|
||||||
|
description="Image used for Feed value (f)")
|
||||||
|
|
||||||
|
img_k : StringProperty(
|
||||||
|
name="k", default='',
|
||||||
|
description="Image used for Kill value (k)")
|
||||||
|
|
||||||
|
img_brush : StringProperty(
|
||||||
|
name="Brush", default='',
|
||||||
|
description="Image used for adding/removing B")
|
||||||
|
|
||||||
|
invert_img_diff_a : BoolProperty(default=False,
|
||||||
|
description='Invert the value of the Vertex Group Diff A')
|
||||||
|
|
||||||
|
invert_img_diff_b : BoolProperty(default=False,
|
||||||
|
description='Invert the value of the Vertex Group Diff B')
|
||||||
|
|
||||||
|
invert_img_scale : BoolProperty(default=False,
|
||||||
|
description='Invert the value of the Vertex Group Scale')
|
||||||
|
|
||||||
|
invert_img_f : BoolProperty(default=False,
|
||||||
|
description='Invert the value of the Vertex Group f')
|
||||||
|
|
||||||
|
invert_img_k : BoolProperty(default=False,
|
||||||
|
description='Invert the value of the Vertex Group k')
|
||||||
|
|
||||||
|
invert_img_vector_field : BoolProperty(default=False,
|
||||||
|
description='Use the perpendicular direction')
|
||||||
|
|
||||||
|
min_diff_a : FloatProperty(
|
||||||
|
name="Min Diff A", default=0.1, min=0, soft_max=2, precision=3,
|
||||||
|
description="Min Diff A")
|
||||||
|
|
||||||
|
max_diff_a : FloatProperty(
|
||||||
|
name="Max Diff A", default=0.1, min=0, soft_max=2, precision=3,
|
||||||
|
description="Max Diff A")
|
||||||
|
|
||||||
|
min_diff_b : FloatProperty(
|
||||||
|
name="Min Diff B", default=0.1, min=0, soft_max=2, precision=3,
|
||||||
|
description="Min Diff B")
|
||||||
|
|
||||||
|
max_diff_b : FloatProperty(
|
||||||
|
name="Max Diff B", default=0.1, min=0, soft_max=2, precision=3,
|
||||||
|
description="Max Diff B")
|
||||||
|
|
||||||
|
min_scale : FloatProperty(
|
||||||
|
name="Scale", default=0.35, min=0, soft_max=1, max=10, precision=2,
|
||||||
|
description="Min Scale Value")
|
||||||
|
|
||||||
|
max_scale : FloatProperty(
|
||||||
|
name="Scale", default=1, min=0, soft_max=1, max=10, precision=2,
|
||||||
|
description="Max Scale value")
|
||||||
|
|
||||||
|
min_f : FloatProperty(
|
||||||
|
name="Min f", default=0.02, min=0, soft_min=0.01, soft_max=0.06, max=0.2, precision=4, step=0.05,
|
||||||
|
description="Min Feed Rate")
|
||||||
|
|
||||||
|
max_f : FloatProperty(
|
||||||
|
name="Max f", default=0.055, min=0, soft_min=0.01, soft_max=0.06, max=0.2, precision=4, step=0.05,
|
||||||
|
description="Max Feed Rate")
|
||||||
|
|
||||||
|
min_k : FloatProperty(
|
||||||
|
name="Min k", default=0.035, min=0, soft_min=0.035, soft_max=0.065, max=0.2, precision=4, step=0.05,
|
||||||
|
description="Min Kill Rate")
|
||||||
|
|
||||||
|
max_k : FloatProperty(
|
||||||
|
name="Max k", default=0.062, min=0, soft_min=0.035, soft_max=0.065, max=0.2, precision=4, step=0.05,
|
||||||
|
description="Max Kill Rate")
|
||||||
|
|
||||||
|
brush_mult : FloatProperty(
|
||||||
|
name="Mult", default=0.5, min=-1, max=1, precision=3, step=0.05,
|
||||||
|
description="Multiplier for brush value")
|
||||||
|
|
||||||
|
bool_cache : BoolProperty(
|
||||||
|
name="Use Cache", default=False,
|
||||||
|
description="Read modifiers affect the vertex groups")
|
||||||
|
|
||||||
|
cache_frame_start : IntProperty(
|
||||||
|
name="Start", default=1,
|
||||||
|
description="Frame on which the simulation starts")
|
||||||
|
|
||||||
|
cache_frame_end : IntProperty(
|
||||||
|
name="End", default=250,
|
||||||
|
description="Frame on which the simulation ends")
|
||||||
|
|
||||||
|
cache_dir : StringProperty(
|
||||||
|
name="Cache directory", default="", subtype='FILE_PATH',
|
||||||
|
description = 'Directory that contains Reaction-Diffusion cache files'
|
||||||
|
)
|
||||||
|
|
||||||
|
normalize : BoolProperty(
|
||||||
|
name="Normalize values", default=False,
|
||||||
|
description="Normalize values from 0 to 1")
|
||||||
|
|
||||||
|
def tex_rd_scene(scene, bake=False):
|
||||||
|
for ob in bpy.context.scene.objects:
|
||||||
|
if ob.tex_reaction_diffusion_settings.run:
|
||||||
|
tex_reaction_diffusion_def(ob)
|
||||||
|
|
||||||
|
def tex_reaction_diffusion_def(ob, bake=False):
|
||||||
|
try:
|
||||||
|
props = ob.tex_reaction_diffusion_settings
|
||||||
|
except:
|
||||||
|
return
|
||||||
|
scene = bpy.context.scene
|
||||||
|
print("Texture Reaction Diffusion: " + str(scene.frame_current))
|
||||||
|
start_time = timeit.default_timer()
|
||||||
|
img_a = bpy.data.images[props.img_a]
|
||||||
|
img_b = bpy.data.images[props.img_b]
|
||||||
|
diff_a = props.diff_a
|
||||||
|
diff_b = props.diff_b
|
||||||
|
diff_a_min = props.min_diff_a
|
||||||
|
diff_a_max = props.max_diff_a
|
||||||
|
diff_b_min = props.min_diff_b
|
||||||
|
diff_b_max = props.max_diff_b
|
||||||
|
f_min = props.min_f
|
||||||
|
f_max = props.max_f
|
||||||
|
k_min = props.min_k
|
||||||
|
k_max = props.max_k
|
||||||
|
ani = props.anisotropy
|
||||||
|
dt = props.dt
|
||||||
|
time_steps = props.time_steps
|
||||||
|
res_x = props.res_x #int(img_b.size[0])
|
||||||
|
res_y = props.res_y #int(img_b.size[1])
|
||||||
|
|
||||||
|
min_scale = props.min_scale
|
||||||
|
max_scale = props.max_scale
|
||||||
|
|
||||||
|
images = bpy.data.images.keys()
|
||||||
|
rd_images = [img_a, img_b]
|
||||||
|
img_diff_a = None
|
||||||
|
img_diff_b = None
|
||||||
|
img_vector_field = None
|
||||||
|
img_f = None
|
||||||
|
img_k = None
|
||||||
|
img_scale = None
|
||||||
|
img_brush = None
|
||||||
|
if props.img_vector_field in images:
|
||||||
|
img_vector_field = bpy.data.images[props.img_vector_field]
|
||||||
|
rd_images.append(img_vector_field)
|
||||||
|
if props.img_diff_a in images:
|
||||||
|
img_diff_a = bpy.data.images[props.img_diff_a]
|
||||||
|
rd_images.append(img_diff_a)
|
||||||
|
if props.img_diff_b in images:
|
||||||
|
img_diff_b = bpy.data.images[props.img_diff_b]
|
||||||
|
rd_images.append(img_diff_b)
|
||||||
|
if props.img_f in images:
|
||||||
|
img_f = bpy.data.images[props.img_f]
|
||||||
|
rd_images.append(img_f)
|
||||||
|
if props.img_k in images:
|
||||||
|
img_k = bpy.data.images[props.img_k]
|
||||||
|
rd_images.append(img_k)
|
||||||
|
if props.img_scale in images:
|
||||||
|
img_scale = bpy.data.images[props.img_scale]
|
||||||
|
rd_images.append(img_scale)
|
||||||
|
if props.img_brush in images:
|
||||||
|
img_brush = bpy.data.images[props.img_brush]
|
||||||
|
rd_images.append(img_brush)
|
||||||
|
for im in rd_images:
|
||||||
|
im.scale(res_x ,res_y)
|
||||||
|
im.pixels.update()
|
||||||
|
nx = res_y
|
||||||
|
ny = res_x
|
||||||
|
|
||||||
|
a_px = np.float32(np.zeros(nx*ny*4))
|
||||||
|
img_a.pixels.foreach_get(a_px)
|
||||||
|
b_px = np.float32(np.zeros(nx*ny*4))
|
||||||
|
img_b.pixels.foreach_get(b_px)
|
||||||
|
if img_vector_field:
|
||||||
|
vf_px = np.float32(np.zeros(nx*ny*4))
|
||||||
|
img_vector_field.pixels.foreach_get(vf_px)
|
||||||
|
vf_px = np.array(vf_px).reshape((-1,4))
|
||||||
|
vf_x = vf_px[:,1]*2-1
|
||||||
|
vf_x = vf_x.reshape((nx,ny))
|
||||||
|
vf_y = vf_px[:,0]*2-1
|
||||||
|
vf_y = vf_y.reshape((nx,ny))
|
||||||
|
|
||||||
|
# original field
|
||||||
|
vf_x_ = sqrt(2)/2*vf_x
|
||||||
|
vf_y_ = sqrt(2)/2*vf_y
|
||||||
|
vf_xy1_ = abs(vf_x_ + vf_y_)
|
||||||
|
vf_xy2_ = abs(vf_x_ - vf_y_)
|
||||||
|
vf_xy1 = (vf_xy1_*ani + (1-ani))*sqrt(2)/2
|
||||||
|
vf_xy2 = (vf_xy2_*ani + (1-ani))*sqrt(2)/2
|
||||||
|
vf_x_ = abs(vf_x)*ani + (1-ani)
|
||||||
|
vf_y_ = abs(vf_y)*ani + (1-ani)
|
||||||
|
vf1 = np.concatenate((vf_x_[np.newaxis,:,:], vf_y_[np.newaxis,:,:], vf_xy1[np.newaxis,:,:], vf_xy2[np.newaxis,:,:]), axis=0)
|
||||||
|
|
||||||
|
# perpendicular field
|
||||||
|
vf_x, vf_y = -vf_y, vf_x
|
||||||
|
vf_x_ = sqrt(2)/2*vf_x
|
||||||
|
vf_y_ = sqrt(2)/2*vf_y
|
||||||
|
vf_xy1_ = abs(vf_x_ + vf_y_)
|
||||||
|
vf_xy2_ = abs(vf_x_ - vf_y_)
|
||||||
|
vf_xy1 = (vf_xy1_*ani + (1-ani))*sqrt(2)/2
|
||||||
|
vf_xy2 = (vf_xy2_*ani + (1-ani))*sqrt(2)/2
|
||||||
|
vf_x = abs(vf_x)*ani + (1-ani)
|
||||||
|
vf_y = abs(vf_y)*ani + (1-ani)
|
||||||
|
vf2 = np.concatenate((vf_x[np.newaxis,:,:], vf_y[np.newaxis,:,:], vf_xy1[np.newaxis,:,:], vf_xy2[np.newaxis,:,:]), axis=0)
|
||||||
|
if props.invert_img_vector_field:
|
||||||
|
vf1, vf2 = vf2, vf1
|
||||||
|
else:
|
||||||
|
vf = np.ones((1,nx,ny))
|
||||||
|
vf_diag = np.ones((1,nx,ny))*sqrt(2)/2
|
||||||
|
vf1 = np.concatenate((vf, vf, vf_diag, vf_diag), axis=0)
|
||||||
|
vf2 = vf1
|
||||||
|
|
||||||
|
|
||||||
|
if img_diff_a:
|
||||||
|
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)
|
||||||
|
else:
|
||||||
|
diff_a = np.ones((nx,ny))*props.diff_a
|
||||||
|
|
||||||
|
if img_diff_b:
|
||||||
|
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)
|
||||||
|
else:
|
||||||
|
diff_b = np.ones((nx,ny))*props.diff_b
|
||||||
|
|
||||||
|
if img_scale:
|
||||||
|
scale = np_remap_image_values(img_scale, channel=0, min=min_scale, max=max_scale, invert=props.invert_img_scale)
|
||||||
|
diff_a *= scale
|
||||||
|
diff_b *= scale
|
||||||
|
else:
|
||||||
|
diff_a *= props.diff_mult
|
||||||
|
diff_b *= props.diff_mult
|
||||||
|
|
||||||
|
if img_f:
|
||||||
|
f = np_remap_image_values(img_f, channel=0, min=f_min, max=f_max, invert=props.invert_img_f)
|
||||||
|
else:
|
||||||
|
f = np.ones((nx,ny))*props.f
|
||||||
|
|
||||||
|
if img_k:
|
||||||
|
k = np_remap_image_values(img_k, channel=0, min=k_min, max=k_max, invert=props.invert_img_k)
|
||||||
|
else:
|
||||||
|
k = np.ones((nx,ny))*props.k
|
||||||
|
|
||||||
|
if img_brush:
|
||||||
|
brush = np_remap_image_values(img_brush)*props.brush_mult
|
||||||
|
else:
|
||||||
|
brush = np.zeros((nx,ny))
|
||||||
|
|
||||||
|
print("Load images: " + str(timeit.default_timer() - start_time) + " sec")
|
||||||
|
|
||||||
|
start_time = timeit.default_timer()
|
||||||
|
|
||||||
|
a_px = np.array(a_px).reshape((-1,4))
|
||||||
|
a = a_px[:,0]
|
||||||
|
a = a.reshape((nx,ny))
|
||||||
|
lap_a = np.zeros((nx,ny))
|
||||||
|
|
||||||
|
b_px = np.array(b_px).reshape((-1,4))
|
||||||
|
b = b_px[:,0]
|
||||||
|
b = b.reshape((nx,ny))
|
||||||
|
lap_b = np.zeros((nx,ny))
|
||||||
|
|
||||||
|
print("Reshape data time: " + str(timeit.default_timer() - start_time) + " sec")
|
||||||
|
|
||||||
|
start_time = timeit.default_timer()
|
||||||
|
run_tex_rd_ani(a, b, lap_a, lap_b, diff_a, diff_b, f, k, dt, time_steps, vf1, vf2, brush)
|
||||||
|
print("Simulation time: " + str(timeit.default_timer() - start_time) + " sec")
|
||||||
|
|
||||||
|
start_time = timeit.default_timer()
|
||||||
|
np.clip(a,0,1,out=a)
|
||||||
|
np.clip(b,0,1,out=b)
|
||||||
|
a = a.flatten()
|
||||||
|
b = b.flatten()
|
||||||
|
a_px[:,0] = a
|
||||||
|
a_px[:,1] = a
|
||||||
|
a_px[:,2] = a
|
||||||
|
b_px[:,0] = b
|
||||||
|
b_px[:,1] = b
|
||||||
|
b_px[:,2] = b
|
||||||
|
img_a.pixels.foreach_set(np.float32(a_px.flatten()))
|
||||||
|
img_b.pixels.foreach_set(np.float32(b_px.flatten()))
|
||||||
|
img_a.pixels.update()
|
||||||
|
img_b.pixels.update()
|
||||||
|
img_a.update()
|
||||||
|
img_b.update()
|
||||||
|
print("Stored Images: " + str(timeit.default_timer() - start_time) + " sec")
|
||||||
|
|
||||||
|
class reset_tex_reaction_diffusion(Operator):
|
||||||
|
bl_idname = "object.reset_tex_reaction_diffusion"
|
||||||
|
bl_label = "Reset Texture Reaction Diffusion"
|
||||||
|
bl_description = ("Run a Reaction-Diffusion based on images: A and B")
|
||||||
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
run : BoolProperty(
|
||||||
|
name="Run Reaction-Diffusion", default=True, description="Compute a new iteration on frame changes")
|
||||||
|
|
||||||
|
time_steps : IntProperty(
|
||||||
|
name="Steps", default=10, min=0, soft_max=50,
|
||||||
|
description="Number of Steps")
|
||||||
|
|
||||||
|
dt : FloatProperty(
|
||||||
|
name="dt", default=1, min=0, soft_max=0.2,
|
||||||
|
description="Time Step")
|
||||||
|
|
||||||
|
diff_a : FloatProperty(
|
||||||
|
name="Diff A", default=0.14, min=0, soft_max=2,
|
||||||
|
description="Diffusion A")
|
||||||
|
|
||||||
|
diff_b : FloatProperty(
|
||||||
|
name="Diff B", default=0.07, min=0, soft_max=2,
|
||||||
|
description="Diffusion B")
|
||||||
|
|
||||||
|
f : FloatProperty(
|
||||||
|
name="f", default=0.055, min=0, soft_min=0.01, soft_max=0.06, max=0.1, precision=4,
|
||||||
|
description="Feed Rate")
|
||||||
|
|
||||||
|
k : FloatProperty(
|
||||||
|
name="k", default=0.062, min=0, soft_min=0.035, soft_max=0.065, max=0.1, precision=4,
|
||||||
|
description="Kill Rate")
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
props = context.object.tex_reaction_diffusion_settings
|
||||||
|
props.dt = self.dt
|
||||||
|
props.time_steps = self.time_steps
|
||||||
|
props.f = self.f
|
||||||
|
props.k = self.k
|
||||||
|
props.diff_a = self.diff_a
|
||||||
|
props.diff_b = self.diff_b
|
||||||
|
res_x = props.res_x
|
||||||
|
res_y = props.res_y
|
||||||
|
img_a = bpy.data.images[props.img_a]
|
||||||
|
img_b = bpy.data.images[props.img_b]
|
||||||
|
img_a.scale(width=res_x, height=res_y)
|
||||||
|
img_b.scale(width=res_x, height=res_y)
|
||||||
|
img_a.pixels.foreach_set([1]*res_x*res_y*4)
|
||||||
|
img_b.pixels.foreach_set([0,0,0,1]*res_x*res_y)
|
||||||
|
img_a.pixels.update()
|
||||||
|
img_b.pixels.update()
|
||||||
|
img_a.update()
|
||||||
|
img_b.update()
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
class start_tex_reaction_diffusion(Operator):
|
||||||
|
bl_idname = "object.start_tex_reaction_diffusion"
|
||||||
|
bl_label = "Start Texture Reaction Diffusion"
|
||||||
|
bl_description = ("Run a Reaction-Diffusion based on images: A and B")
|
||||||
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
#res_x : IntProperty(
|
||||||
|
# name="Resolution X", default=512, min=2, soft_max=1000,
|
||||||
|
# description="Resolution of the simulation")
|
||||||
|
#res_y : IntProperty(
|
||||||
|
# name="Resolution Y", default=512, min=2, soft_max=1000,
|
||||||
|
# description="Resolution of the simulation")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return True
|
||||||
|
|
||||||
|
#def invoke(self, context, event):
|
||||||
|
# return context.window_manager.invoke_props_dialog(self)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
tex_reaction_diffusion_add_handler(self, context)
|
||||||
|
set_animatable_fix_handler(self, context)
|
||||||
|
|
||||||
|
ob = context.object
|
||||||
|
props = ob.tex_reaction_diffusion_settings
|
||||||
|
if props.img_a in bpy.data.images.keys():
|
||||||
|
img_a = bpy.data.images[props.img_a]
|
||||||
|
img_a.scale(props.res_x, props.res_y)
|
||||||
|
else:
|
||||||
|
img_a = bpy.data.images.new(name="A", width=props.res_x, height=props.res_y)
|
||||||
|
if props.img_b in bpy.data.images.keys():
|
||||||
|
img_b = bpy.data.images[props.img_b]
|
||||||
|
img_b.scale(props.res_x, props.res_y)
|
||||||
|
else:
|
||||||
|
img_b = bpy.data.images.new(name="B", width=props.res_x, height=props.res_y)
|
||||||
|
props.run = True
|
||||||
|
#props.res_x = self.res_x
|
||||||
|
#props.res_y = self.res_y
|
||||||
|
props.img_a = img_a.name
|
||||||
|
props.img_b = img_b.name
|
||||||
|
|
||||||
|
#props.run = self.run
|
||||||
|
#props.dt = self.dt
|
||||||
|
#props.time_steps = self.time_steps
|
||||||
|
#props.f = self.f
|
||||||
|
#props.k = self.k
|
||||||
|
#props.diff_a = self.diff_a
|
||||||
|
#props.diff_b = self.diff_b
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
class TISSUE_PT_tex_reaction_diffusion(Panel):
|
||||||
|
bl_space_type = 'PROPERTIES'
|
||||||
|
bl_region_type = 'WINDOW'
|
||||||
|
bl_context = "object"
|
||||||
|
bl_label = "Tissue Texture Reaction-Diffusion"
|
||||||
|
bl_options = {'DEFAULT_CLOSED'}
|
||||||
|
|
||||||
|
#@classmethod
|
||||||
|
#def poll(cls, context):
|
||||||
|
# return True
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
tex_reaction_diffusion_add_handler(self, context)
|
||||||
|
ob = bpy.context.object
|
||||||
|
props = ob.tex_reaction_diffusion_settings
|
||||||
|
img_a = props.img_a
|
||||||
|
img_b = props.img_b
|
||||||
|
layout = self.layout
|
||||||
|
col = layout.column(align=True)
|
||||||
|
row = col.row(align=True)
|
||||||
|
if not (img_a and img_b in bpy.data.images):
|
||||||
|
row.operator("object.start_tex_reaction_diffusion",
|
||||||
|
icon="EXPERIMENTAL")
|
||||||
|
col = layout.column(align=True)
|
||||||
|
row = col.row(align=True)
|
||||||
|
row.prop(props, 'res_x')
|
||||||
|
row.prop(props, 'res_y')
|
||||||
|
col.separator()
|
||||||
|
col.prop_search(props, 'img_a', bpy.data, "images")
|
||||||
|
col.prop_search(props, 'img_b', bpy.data, "images")
|
||||||
|
else:
|
||||||
|
row.operator("object.reset_tex_reaction_diffusion",
|
||||||
|
icon="EXPERIMENTAL")
|
||||||
|
row = col.row(align=True)
|
||||||
|
row.prop(props, "run", text="Run Reaction-Diffusion")
|
||||||
|
col = layout.column(align=True)
|
||||||
|
row = col.row(align=True)
|
||||||
|
row.prop(props, 'res_x')
|
||||||
|
row.prop(props, 'res_y')
|
||||||
|
col.separator()
|
||||||
|
col.prop_search(props, 'img_a', bpy.data, "images")
|
||||||
|
col.prop_search(props, 'img_b', bpy.data, "images")
|
||||||
|
col.separator()
|
||||||
|
row = col.row(align=True)
|
||||||
|
row.prop(props, "time_steps")
|
||||||
|
row.prop(props, "dt")
|
||||||
|
row.enabled = not props.bool_cache
|
||||||
|
col.separator()
|
||||||
|
row = col.row(align=True)
|
||||||
|
col1 = row.column(align=True)
|
||||||
|
col1.prop(props, "diff_a")
|
||||||
|
col1.enabled = props.img_diff_a == '' and not props.bool_cache
|
||||||
|
col1 = row.column(align=True)
|
||||||
|
col1.prop(props, "diff_b")
|
||||||
|
col1.enabled = props.img_diff_b == '' and not props.bool_cache
|
||||||
|
row = col.row(align=True)
|
||||||
|
row.prop(props, "diff_mult")
|
||||||
|
row.enabled = props.img_scale == '' and not props.bool_cache
|
||||||
|
#col.separator()
|
||||||
|
row = col.row(align=True)
|
||||||
|
col1 = row.column(align=True)
|
||||||
|
col1.prop(props, "f")
|
||||||
|
col1.enabled = props.img_f == '' and not props.bool_cache
|
||||||
|
col1 = row.column(align=True)
|
||||||
|
col1.prop(props, "k")
|
||||||
|
col1.enabled = props.img_k == '' and not props.bool_cache
|
||||||
|
'''
|
||||||
|
col.separator()
|
||||||
|
col.label(text='Cache:')
|
||||||
|
#col.prop(props, "bool_cache")
|
||||||
|
col.prop(props, "cache_dir", text='')
|
||||||
|
col.separator()
|
||||||
|
row = col.row(align=True)
|
||||||
|
row.prop(props, "cache_frame_start")
|
||||||
|
row.prop(props, "cache_frame_end")
|
||||||
|
col.separator()
|
||||||
|
if props.bool_cache:
|
||||||
|
col.operator("object.reaction_diffusion_free_data")
|
||||||
|
else:
|
||||||
|
row = col.row(align=True)
|
||||||
|
row.operator("object.bake_reaction_diffusion")
|
||||||
|
file = bpy.context.blend_data.filepath
|
||||||
|
temp = bpy.context.preferences.filepaths.temporary_directory
|
||||||
|
if file == temp == props.cache_dir == '':
|
||||||
|
row.enabled = False
|
||||||
|
col.label(text="Cannot use cache", icon='ERROR')
|
||||||
|
col.label(text='please save the Blender or set a Cache directory')
|
||||||
|
'''
|
||||||
|
|
||||||
|
class TISSUE_PT_tex_reaction_diffusion_images(Panel):
|
||||||
|
bl_space_type = 'PROPERTIES'
|
||||||
|
bl_region_type = 'WINDOW'
|
||||||
|
bl_context = "object"
|
||||||
|
bl_parent_id = "TISSUE_PT_tex_reaction_diffusion"
|
||||||
|
bl_label = "Image Maps"
|
||||||
|
bl_options = {'DEFAULT_CLOSED'}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
props = context.object.tex_reaction_diffusion_settings
|
||||||
|
if props.img_a and props.img_b in bpy.data.images.keys():
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
ob = context.object
|
||||||
|
props = ob.tex_reaction_diffusion_settings
|
||||||
|
layout = self.layout
|
||||||
|
#layout.use_property_split = True
|
||||||
|
col = layout.column(align=True)
|
||||||
|
insert_image_parameter(col, ob, 'brush', text='Brush:')
|
||||||
|
insert_image_parameter(col, ob, 'diff_a', text='Diff A:')
|
||||||
|
insert_image_parameter(col, ob, 'diff_b', text='Diff B:')
|
||||||
|
insert_image_parameter(col, ob, 'scale', text='Scale:')
|
||||||
|
insert_image_parameter(col, ob, 'f', text='f:')
|
||||||
|
insert_image_parameter(col, ob, 'k', text='k:')
|
||||||
|
insert_image_parameter(col, ob, 'vector_field', text='Vector Field:')
|
||||||
|
col.enabled = not props.bool_cache
|
||||||
|
|
||||||
|
def insert_image_parameter(col, ob, name, text=''):
|
||||||
|
props = ob.tex_reaction_diffusion_settings
|
||||||
|
split = col.split(factor=0.25, align=True)
|
||||||
|
col2 = split.column(align=True)
|
||||||
|
col2.label(text=text)
|
||||||
|
col2 = split.column(align=True)
|
||||||
|
row2 = col2.row(align=True)
|
||||||
|
row2.prop_search(props, 'img_' + name, bpy.data, "images", text='')
|
||||||
|
if name not in ('brush'):
|
||||||
|
if name == 'vector_field': icon = 'DRIVER_ROTATIONAL_DIFFERENCE'#'ORIENTATION_VIEW'
|
||||||
|
else: icon = 'ARROW_LEFTRIGHT'
|
||||||
|
row2.prop(props, "invert_img_" + name, text="", toggle=True, icon=icon)
|
||||||
|
if 'img_' + name in props:
|
||||||
|
if props['img_' + name] != '':
|
||||||
|
if name == 'brush':
|
||||||
|
col2.prop(props, "brush_mult")
|
||||||
|
elif name == 'vector_field':
|
||||||
|
col2.prop(props, "anisotropy")
|
||||||
|
else:
|
||||||
|
row2 = col2.row(align=True)
|
||||||
|
row2.prop(props, "min_" + name, text="Min")
|
||||||
|
row2 = col2.row(align=True)
|
||||||
|
row2.prop(props, "max_" + name, text="Max")
|
||||||
|
col.separator()
|
@ -1,7 +1,23 @@
|
|||||||
# SPDX-FileCopyrightText: 2022-2023 Blender Foundation
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
#
|
||||||
|
# ##### END GPL LICENSE BLOCK #####
|
||||||
|
|
||||||
# ---------------------------- ADAPTIVE DUPLIFACES --------------------------- #
|
# ---------------------------- ADAPTIVE DUPLIFACES --------------------------- #
|
||||||
# ------------------------------- version 0.84 ------------------------------- #
|
# ------------------------------- version 0.84 ------------------------------- #
|
||||||
# #
|
# #
|
||||||
@ -29,7 +45,9 @@ from bpy.props import (
|
|||||||
StringProperty,
|
StringProperty,
|
||||||
PointerProperty
|
PointerProperty
|
||||||
)
|
)
|
||||||
|
from .utils import tissue_time
|
||||||
from . import config
|
from . import config
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
def update_dependencies(ob, objects):
|
def update_dependencies(ob, objects):
|
||||||
@ -50,6 +68,8 @@ def get_deps(ob):
|
|||||||
return [ob.tissue_tessellate.generator, ob.tissue_tessellate.component]
|
return [ob.tissue_tessellate.generator, ob.tissue_tessellate.component]
|
||||||
elif type == 'TO_CURVE':
|
elif type == 'TO_CURVE':
|
||||||
return [ob.tissue_to_curve.object]
|
return [ob.tissue_to_curve.object]
|
||||||
|
elif type == 'POLYHEDRA':
|
||||||
|
return [ob.tissue_polyhedra.object]
|
||||||
else: return []
|
else: return []
|
||||||
|
|
||||||
def anim_tessellate_active(self, context):
|
def anim_tessellate_active(self, context):
|
||||||
@ -75,10 +95,9 @@ def anim_tessellate_object(ob):
|
|||||||
#from bpy.app.handlers import persistent
|
#from bpy.app.handlers import persistent
|
||||||
|
|
||||||
|
|
||||||
def anim_tessellate(scene, depsgraph=None):
|
def anim_tissue(scene, depsgraph=None):
|
||||||
print('Tissue: animating tessellations...')
|
tissue_time(None,'Tissue: Animating Tissue objects at frame {}...'.format(scene.frame_current), levels=0)
|
||||||
|
start_time = time.time()
|
||||||
#config.evaluatedDepsgraph = depsgraph
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
active_object = bpy.context.object
|
active_object = bpy.context.object
|
||||||
@ -109,10 +128,15 @@ def anim_tessellate(scene, depsgraph=None):
|
|||||||
override['mode'] = 'OBJECT'
|
override['mode'] = 'OBJECT'
|
||||||
override['view_layer'] = scene.view_layers[0]
|
override['view_layer'] = scene.view_layers[0]
|
||||||
break
|
break
|
||||||
if ob.tissue.tissue_type == 'TESSELLATE':
|
with bpy.context.temp_override(**override):
|
||||||
bpy.ops.object.tissue_update_tessellate(override)
|
if ob.tissue.tissue_type == 'TESSELLATE':
|
||||||
elif ob.tissue.tissue_type == 'TO_CURVE':
|
bpy.ops.object.tissue_update_tessellate()
|
||||||
bpy.ops.object.tissue_convert_to_curve_update(override)
|
elif ob.tissue.tissue_type == 'TO_CURVE':
|
||||||
|
bpy.ops.object.tissue_update_convert_to_curve()
|
||||||
|
elif ob.tissue.tissue_type == 'POLYHEDRA':
|
||||||
|
bpy.ops.object.tissue_update_polyhedra()
|
||||||
|
elif ob.tissue.tissue_type == 'CONTOUR_CURVES':
|
||||||
|
bpy.ops.object.tissue_update_contour_curves()
|
||||||
|
|
||||||
if old_mode != None:
|
if old_mode != None:
|
||||||
objects = bpy.context.view_layer.objects
|
objects = bpy.context.view_layer.objects
|
||||||
@ -121,68 +145,40 @@ def anim_tessellate(scene, depsgraph=None):
|
|||||||
bpy.ops.object.mode_set(mode=old_mode)
|
bpy.ops.object.mode_set(mode=old_mode)
|
||||||
|
|
||||||
config.evaluatedDepsgraph = None
|
config.evaluatedDepsgraph = None
|
||||||
print('end')
|
tissue_time(start_time,'Animated Tissue objects at frame {}'.format(scene.frame_current), levels=0)
|
||||||
return
|
return
|
||||||
'''
|
|
||||||
def OLD_anim_tessellate(scene, depsgraph):
|
|
||||||
print('Tissue: animating tessellations...')
|
|
||||||
|
|
||||||
#global evaluatedDepsgraph
|
def remove_tissue_handler():
|
||||||
#print(evaluatedDepsgraph)
|
|
||||||
print(config.evaluatedDepsgraph)
|
|
||||||
config.evaluatedDepsgraph = depsgraph
|
|
||||||
print(config.evaluatedDepsgraph)
|
|
||||||
|
|
||||||
try:
|
|
||||||
active_object = bpy.context.object
|
|
||||||
old_mode = bpy.context.object.mode
|
|
||||||
selected_objects = bpy.context.selected_objects
|
|
||||||
except: active_object = old_mode = selected_objects = None
|
|
||||||
|
|
||||||
if old_mode in ('OBJECT', 'PAINT_WEIGHT') or True:
|
|
||||||
update_objects = []
|
|
||||||
for ob in scene.objects:
|
|
||||||
if ob.tissue.bool_run and not ob.tissue.bool_lock:
|
|
||||||
if ob not in update_objects: update_objects.append(ob)
|
|
||||||
update_objects = list(reversed(update_dependencies(ob, update_objects)))
|
|
||||||
for ob in update_objects:
|
|
||||||
for window in bpy.context.window_manager.windows:
|
|
||||||
screen = window.screen
|
|
||||||
for area in screen.areas:
|
|
||||||
if area.type == 'VIEW_3D':
|
|
||||||
override = bpy.context.copy()
|
|
||||||
override['window'] = window
|
|
||||||
override['screen'] = screen
|
|
||||||
override['area'] = area
|
|
||||||
override['selected_objects'] = [ob]
|
|
||||||
override['object'] = ob
|
|
||||||
override['active_object'] = ob
|
|
||||||
override['selected_editable_objects'] = [ob]
|
|
||||||
override['mode'] = 'OBJECT'
|
|
||||||
override['view_layer'] = scene.view_layers[0]
|
|
||||||
break
|
|
||||||
bpy.ops.object.tissue_update_tessellate(override)
|
|
||||||
|
|
||||||
config.evaluatedDepsgraph = None
|
|
||||||
print('end')
|
|
||||||
print(config.evaluatedDepsgraph)
|
|
||||||
return
|
|
||||||
'''
|
|
||||||
def remove_tessellate_handler():
|
|
||||||
tissue_handlers = []
|
tissue_handlers = []
|
||||||
blender_handlers = bpy.app.handlers.frame_change_post
|
blender_handlers = bpy.app.handlers.frame_change_post
|
||||||
for h in blender_handlers:
|
for h in blender_handlers:
|
||||||
if "anim_tessellate" in str(h):
|
if "anim_tissue" in str(h):
|
||||||
tissue_handlers.append(h)
|
tissue_handlers.append(h)
|
||||||
for h in tissue_handlers: blender_handlers.remove(h)
|
for h in tissue_handlers: blender_handlers.remove(h)
|
||||||
|
|
||||||
def set_tessellate_handler(self, context):
|
def set_tissue_handler(self, context):
|
||||||
|
remove_tissue_handler()
|
||||||
remove_tessellate_handler()
|
|
||||||
for o in context.scene.objects:
|
for o in context.scene.objects:
|
||||||
if o.tissue.bool_run:
|
if o.tissue.bool_run:
|
||||||
blender_handlers = bpy.app.handlers.frame_change_post
|
blender_handlers = bpy.app.handlers.frame_change_post
|
||||||
blender_handlers.append(anim_tessellate)
|
blender_handlers.append(anim_tissue)
|
||||||
|
break
|
||||||
|
return
|
||||||
|
|
||||||
|
def remove_polyhedra_handler():
|
||||||
|
tissue_handlers = []
|
||||||
|
blender_handlers = bpy.app.handlers.frame_change_post
|
||||||
|
for h in blender_handlers:
|
||||||
|
if "anim_polyhedra" in str(h):
|
||||||
|
tissue_handlers.append(h)
|
||||||
|
for h in tissue_handlers: blender_handlers.remove(h)
|
||||||
|
|
||||||
|
def set_polyhedra_handler(self, context):
|
||||||
|
remove_polyhedra_handler()
|
||||||
|
for o in context.scene.objects:
|
||||||
|
if o.tissue.bool_run:
|
||||||
|
blender_handlers = bpy.app.handlers.frame_change_post
|
||||||
|
blender_handlers.append(anim_polyhedra)
|
||||||
break
|
break
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -190,7 +186,7 @@ def set_tessellate_handler(self, context):
|
|||||||
class tissue_prop(PropertyGroup):
|
class tissue_prop(PropertyGroup):
|
||||||
bool_lock : BoolProperty(
|
bool_lock : BoolProperty(
|
||||||
name="Lock",
|
name="Lock",
|
||||||
description="Prevent automatic update on settings changes or if other objects have it in the hierarchy",
|
description="Prevent automatic update on settings changes or if other objects have it in the hierarchy.",
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
bool_dependencies : BoolProperty(
|
bool_dependencies : BoolProperty(
|
||||||
@ -202,17 +198,24 @@ class tissue_prop(PropertyGroup):
|
|||||||
name="Animatable",
|
name="Animatable",
|
||||||
description="Automatically recompute the geometry when the frame is changed. Tessellations may not work using the default Render Animation",
|
description="Automatically recompute the geometry when the frame is changed. Tessellations may not work using the default Render Animation",
|
||||||
default = False,
|
default = False,
|
||||||
update = set_tessellate_handler
|
update = set_tissue_handler
|
||||||
)
|
)
|
||||||
tissue_type : EnumProperty(
|
tissue_type : EnumProperty(
|
||||||
items=(
|
items=(
|
||||||
('NONE', "None", ""),
|
('NONE', "None", ""),
|
||||||
('TESSELLATE', "Tessellate", ""),
|
('TESSELLATE', "Tessellate", ""),
|
||||||
('TO_CURVE', "To Curve", "")
|
('TO_CURVE', "To Curve", ""),
|
||||||
|
('POLYHEDRA', "Polyhedra", ""),
|
||||||
|
('CONTOUR_CURVES', "Contour Curves", "")
|
||||||
),
|
),
|
||||||
default='NONE',
|
default='NONE',
|
||||||
name=""
|
name=""
|
||||||
)
|
)
|
||||||
|
bool_hold : BoolProperty(
|
||||||
|
name="Hold",
|
||||||
|
description="Wait...",
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
class tissue_tessellate_prop(PropertyGroup):
|
class tissue_tessellate_prop(PropertyGroup):
|
||||||
bool_hold : BoolProperty(
|
bool_hold : BoolProperty(
|
||||||
@ -561,13 +564,13 @@ class tissue_tessellate_prop(PropertyGroup):
|
|||||||
boundary_mat_offset : IntProperty(
|
boundary_mat_offset : IntProperty(
|
||||||
name="Material Offset",
|
name="Material Offset",
|
||||||
default=0,
|
default=0,
|
||||||
description="Material Offset for boundaries (with Multi Components or Material ID)",
|
description="Material Offset for boundaries (with components based on Materials)",
|
||||||
update = anim_tessellate_active
|
update = anim_tessellate_active
|
||||||
)
|
)
|
||||||
fill_frame_mat : IntProperty(
|
fill_frame_mat : IntProperty(
|
||||||
name="Material Offset",
|
name="Material Offset",
|
||||||
default=0,
|
default=0,
|
||||||
description="Material Offset for inner faces (with Multi Components or Material ID)",
|
description="Material Offset for inner faces (with components based on Materials)",
|
||||||
update = anim_tessellate_active
|
update = anim_tessellate_active
|
||||||
)
|
)
|
||||||
open_edges_crease : FloatProperty(
|
open_edges_crease : FloatProperty(
|
||||||
@ -598,14 +601,23 @@ class tissue_tessellate_prop(PropertyGroup):
|
|||||||
name="Frame Thickness",
|
name="Frame Thickness",
|
||||||
default=0.2,
|
default=0.2,
|
||||||
min=0,
|
min=0,
|
||||||
soft_max=2,
|
soft_max=1,
|
||||||
description="Frame Thickness",
|
description="Frame Thickness",
|
||||||
update = anim_tessellate_active
|
update = anim_tessellate_active
|
||||||
)
|
)
|
||||||
|
frame_boundary_thickness : FloatProperty(
|
||||||
|
name="Frame Boundary Thickness",
|
||||||
|
default=0,
|
||||||
|
min=0,
|
||||||
|
soft_max=1,
|
||||||
|
description="Frame Boundary Thickness (when zero it uses the Frame Thickness instead)",
|
||||||
|
update = anim_tessellate_active
|
||||||
|
)
|
||||||
frame_mode : EnumProperty(
|
frame_mode : EnumProperty(
|
||||||
items=(
|
items=(
|
||||||
('CONSTANT', 'Constant', 'Even thickness'),
|
('CONSTANT', 'Constant', 'Even thickness'),
|
||||||
('RELATIVE', 'Relative', 'Frame offset depends on face areas')),
|
('RELATIVE', 'Relative', 'Frame offset depends on face areas'),
|
||||||
|
('CENTER', 'Center', 'Toward the center of the face (uses Incenter for Triangles)')),
|
||||||
default='CONSTANT',
|
default='CONSTANT',
|
||||||
name="Offset",
|
name="Offset",
|
||||||
update = anim_tessellate_active
|
update = anim_tessellate_active
|
||||||
@ -641,7 +653,7 @@ class tissue_tessellate_prop(PropertyGroup):
|
|||||||
)
|
)
|
||||||
use_origin_offset : BoolProperty(
|
use_origin_offset : BoolProperty(
|
||||||
name="Align to Origins",
|
name="Align to Origins",
|
||||||
default=False,
|
default=True,
|
||||||
description="Define offset according to components origin and local Z coordinate",
|
description="Define offset according to components origin and local Z coordinate",
|
||||||
update = anim_tessellate_active
|
update = anim_tessellate_active
|
||||||
)
|
)
|
||||||
@ -665,6 +677,31 @@ class tissue_tessellate_prop(PropertyGroup):
|
|||||||
update = anim_tessellate_active
|
update = anim_tessellate_active
|
||||||
)
|
)
|
||||||
|
|
||||||
|
vertex_group_frame_thickness : StringProperty(
|
||||||
|
name="Frame Thickness weight", default='',
|
||||||
|
description="Vertex Group used for frame thickness",
|
||||||
|
update = anim_tessellate_active
|
||||||
|
)
|
||||||
|
invert_vertex_group_frame_thickness : BoolProperty(
|
||||||
|
name="Invert", default=False,
|
||||||
|
description="Invert the vertex group influence",
|
||||||
|
update = anim_tessellate_active
|
||||||
|
)
|
||||||
|
vertex_group_frame_thickness_factor : FloatProperty(
|
||||||
|
name="Factor",
|
||||||
|
default=0,
|
||||||
|
min=0,
|
||||||
|
max=1,
|
||||||
|
description="Frame thickness factor to use for zero vertex group influence",
|
||||||
|
update = anim_tessellate_active
|
||||||
|
)
|
||||||
|
face_weight_frame : BoolProperty(
|
||||||
|
name="Face Weight",
|
||||||
|
default=True,
|
||||||
|
description="Uniform weight for individual faces",
|
||||||
|
update = anim_tessellate_active
|
||||||
|
)
|
||||||
|
|
||||||
vertex_group_cap_owner : EnumProperty(
|
vertex_group_cap_owner : EnumProperty(
|
||||||
items=(
|
items=(
|
||||||
('BASE', 'Base', 'Use base vertex group'),
|
('BASE', 'Base', 'Use base vertex group'),
|
||||||
@ -802,6 +839,12 @@ class tissue_tessellate_prop(PropertyGroup):
|
|||||||
description="Automatically rotate the boundary faces",
|
description="Automatically rotate the boundary faces",
|
||||||
update = anim_tessellate_active
|
update = anim_tessellate_active
|
||||||
)
|
)
|
||||||
|
preserve_quads : BoolProperty(
|
||||||
|
name="Preserve Quads",
|
||||||
|
default=False,
|
||||||
|
description="Quad faces are tessellated using QUAD mode",
|
||||||
|
update = anim_tessellate_active
|
||||||
|
)
|
||||||
|
|
||||||
def store_parameters(operator, ob):
|
def store_parameters(operator, ob):
|
||||||
ob.tissue_tessellate.bool_hold = True
|
ob.tissue_tessellate.bool_hold = True
|
||||||
@ -852,6 +895,7 @@ def store_parameters(operator, ob):
|
|||||||
ob.tissue_tessellate.bridge_cuts = operator.bridge_cuts
|
ob.tissue_tessellate.bridge_cuts = operator.bridge_cuts
|
||||||
ob.tissue_tessellate.bridge_smoothness = operator.bridge_smoothness
|
ob.tissue_tessellate.bridge_smoothness = operator.bridge_smoothness
|
||||||
ob.tissue_tessellate.frame_thickness = operator.frame_thickness
|
ob.tissue_tessellate.frame_thickness = operator.frame_thickness
|
||||||
|
ob.tissue_tessellate.frame_boundary_thickness = operator.frame_boundary_thickness
|
||||||
ob.tissue_tessellate.frame_mode = operator.frame_mode
|
ob.tissue_tessellate.frame_mode = operator.frame_mode
|
||||||
ob.tissue_tessellate.frame_boundary = operator.frame_boundary
|
ob.tissue_tessellate.frame_boundary = operator.frame_boundary
|
||||||
ob.tissue_tessellate.fill_frame = operator.fill_frame
|
ob.tissue_tessellate.fill_frame = operator.fill_frame
|
||||||
@ -863,6 +907,10 @@ def store_parameters(operator, ob):
|
|||||||
ob.tissue_tessellate.vertex_group_thickness = operator.vertex_group_thickness
|
ob.tissue_tessellate.vertex_group_thickness = operator.vertex_group_thickness
|
||||||
ob.tissue_tessellate.invert_vertex_group_thickness = operator.invert_vertex_group_thickness
|
ob.tissue_tessellate.invert_vertex_group_thickness = operator.invert_vertex_group_thickness
|
||||||
ob.tissue_tessellate.vertex_group_thickness_factor = operator.vertex_group_thickness_factor
|
ob.tissue_tessellate.vertex_group_thickness_factor = operator.vertex_group_thickness_factor
|
||||||
|
ob.tissue_tessellate.vertex_group_frame_thickness = operator.vertex_group_frame_thickness
|
||||||
|
ob.tissue_tessellate.invert_vertex_group_frame_thickness = operator.invert_vertex_group_frame_thickness
|
||||||
|
ob.tissue_tessellate.vertex_group_frame_thickness_factor = operator.vertex_group_frame_thickness_factor
|
||||||
|
ob.tissue_tessellate.face_weight_frame = operator.face_weight_frame
|
||||||
ob.tissue_tessellate.vertex_group_distribution = operator.vertex_group_distribution
|
ob.tissue_tessellate.vertex_group_distribution = operator.vertex_group_distribution
|
||||||
ob.tissue_tessellate.invert_vertex_group_distribution = operator.invert_vertex_group_distribution
|
ob.tissue_tessellate.invert_vertex_group_distribution = operator.invert_vertex_group_distribution
|
||||||
ob.tissue_tessellate.vertex_group_distribution_factor = operator.vertex_group_distribution_factor
|
ob.tissue_tessellate.vertex_group_distribution_factor = operator.vertex_group_distribution_factor
|
||||||
@ -888,6 +936,7 @@ def store_parameters(operator, ob):
|
|||||||
ob.tissue_tessellate.invert_vertex_group_scale_normals = operator.invert_vertex_group_scale_normals
|
ob.tissue_tessellate.invert_vertex_group_scale_normals = operator.invert_vertex_group_scale_normals
|
||||||
ob.tissue_tessellate.boundary_variable_offset = operator.boundary_variable_offset
|
ob.tissue_tessellate.boundary_variable_offset = operator.boundary_variable_offset
|
||||||
ob.tissue_tessellate.auto_rotate_boundary = operator.auto_rotate_boundary
|
ob.tissue_tessellate.auto_rotate_boundary = operator.auto_rotate_boundary
|
||||||
|
ob.tissue_tessellate.preserve_quads = operator.preserve_quads
|
||||||
ob.tissue_tessellate.bool_hold = False
|
ob.tissue_tessellate.bool_hold = False
|
||||||
return ob
|
return ob
|
||||||
|
|
||||||
@ -938,11 +987,16 @@ def load_parameters(operator, ob):
|
|||||||
operator.boundary_mat_offset = ob.tissue_tessellate.boundary_mat_offset
|
operator.boundary_mat_offset = ob.tissue_tessellate.boundary_mat_offset
|
||||||
operator.fill_frame_mat = ob.tissue_tessellate.fill_frame_mat
|
operator.fill_frame_mat = ob.tissue_tessellate.fill_frame_mat
|
||||||
operator.frame_thickness = ob.tissue_tessellate.frame_thickness
|
operator.frame_thickness = ob.tissue_tessellate.frame_thickness
|
||||||
|
operator.frame_boundary_thickness = ob.tissue_tessellate.frame_boundary_thickness
|
||||||
operator.frame_mode = ob.tissue_tessellate.frame_mode
|
operator.frame_mode = ob.tissue_tessellate.frame_mode
|
||||||
operator.use_origin_offset = ob.tissue_tessellate.use_origin_offset
|
operator.use_origin_offset = ob.tissue_tessellate.use_origin_offset
|
||||||
operator.vertex_group_thickness = ob.tissue_tessellate.vertex_group_thickness
|
operator.vertex_group_thickness = ob.tissue_tessellate.vertex_group_thickness
|
||||||
operator.invert_vertex_group_thickness = ob.tissue_tessellate.invert_vertex_group_thickness
|
operator.invert_vertex_group_thickness = ob.tissue_tessellate.invert_vertex_group_thickness
|
||||||
operator.vertex_group_thickness_factor = ob.tissue_tessellate.vertex_group_thickness_factor
|
operator.vertex_group_thickness_factor = ob.tissue_tessellate.vertex_group_thickness_factor
|
||||||
|
operator.vertex_group_frame_thickness = ob.tissue_tessellate.vertex_group_frame_thickness
|
||||||
|
operator.invert_vertex_group_frame_thickness = ob.tissue_tessellate.invert_vertex_group_frame_thickness
|
||||||
|
operator.vertex_group_frame_thickness_factor = ob.tissue_tessellate.vertex_group_frame_thickness_factor
|
||||||
|
operator.face_weight_frame = ob.tissue_tessellate.face_weight_frame
|
||||||
operator.vertex_group_distribution = ob.tissue_tessellate.vertex_group_distribution
|
operator.vertex_group_distribution = ob.tissue_tessellate.vertex_group_distribution
|
||||||
operator.invert_vertex_group_distribution = ob.tissue_tessellate.invert_vertex_group_distribution
|
operator.invert_vertex_group_distribution = ob.tissue_tessellate.invert_vertex_group_distribution
|
||||||
operator.vertex_group_distribution_factor = ob.tissue_tessellate.vertex_group_distribution_factor
|
operator.vertex_group_distribution_factor = ob.tissue_tessellate.vertex_group_distribution_factor
|
||||||
@ -968,6 +1022,7 @@ def load_parameters(operator, ob):
|
|||||||
operator.invert_vertex_group_scale_normals = ob.tissue_tessellate.invert_vertex_group_scale_normals
|
operator.invert_vertex_group_scale_normals = ob.tissue_tessellate.invert_vertex_group_scale_normals
|
||||||
operator.boundary_variable_offset = ob.tissue_tessellate.boundary_variable_offset
|
operator.boundary_variable_offset = ob.tissue_tessellate.boundary_variable_offset
|
||||||
operator.auto_rotate_boundary = ob.tissue_tessellate.auto_rotate_boundary
|
operator.auto_rotate_boundary = ob.tissue_tessellate.auto_rotate_boundary
|
||||||
|
operator.preserve_quads = ob.tissue_tessellate.preserve_quads
|
||||||
return ob
|
return ob
|
||||||
|
|
||||||
def props_to_dict(ob):
|
def props_to_dict(ob):
|
||||||
@ -1003,6 +1058,7 @@ def props_to_dict(ob):
|
|||||||
tessellate_dict['even_thickness'] = props.even_thickness
|
tessellate_dict['even_thickness'] = props.even_thickness
|
||||||
tessellate_dict['even_thickness_iter'] = props.even_thickness_iter
|
tessellate_dict['even_thickness_iter'] = props.even_thickness_iter
|
||||||
tessellate_dict['frame_thickness'] = props.frame_thickness
|
tessellate_dict['frame_thickness'] = props.frame_thickness
|
||||||
|
tessellate_dict['frame_boundary_thickness'] = props.frame_boundary_thickness
|
||||||
tessellate_dict['frame_mode'] = props.frame_mode
|
tessellate_dict['frame_mode'] = props.frame_mode
|
||||||
tessellate_dict['frame_boundary'] = props.frame_boundary
|
tessellate_dict['frame_boundary'] = props.frame_boundary
|
||||||
tessellate_dict['fill_frame'] = props.fill_frame
|
tessellate_dict['fill_frame'] = props.fill_frame
|
||||||
@ -1011,6 +1067,10 @@ def props_to_dict(ob):
|
|||||||
tessellate_dict['vertex_group_thickness'] = props.vertex_group_thickness
|
tessellate_dict['vertex_group_thickness'] = props.vertex_group_thickness
|
||||||
tessellate_dict['invert_vertex_group_thickness'] = props.invert_vertex_group_thickness
|
tessellate_dict['invert_vertex_group_thickness'] = props.invert_vertex_group_thickness
|
||||||
tessellate_dict['vertex_group_thickness_factor'] = props.vertex_group_thickness_factor
|
tessellate_dict['vertex_group_thickness_factor'] = props.vertex_group_thickness_factor
|
||||||
|
tessellate_dict['vertex_group_frame_thickness'] = props.vertex_group_frame_thickness
|
||||||
|
tessellate_dict['invert_vertex_group_frame_thickness'] = props.invert_vertex_group_frame_thickness
|
||||||
|
tessellate_dict['vertex_group_frame_thickness_factor'] = props.vertex_group_frame_thickness_factor
|
||||||
|
tessellate_dict['face_weight_frame'] = props.face_weight_frame
|
||||||
tessellate_dict['vertex_group_distribution'] = props.vertex_group_distribution
|
tessellate_dict['vertex_group_distribution'] = props.vertex_group_distribution
|
||||||
tessellate_dict['invert_vertex_group_distribution'] = props.invert_vertex_group_distribution
|
tessellate_dict['invert_vertex_group_distribution'] = props.invert_vertex_group_distribution
|
||||||
tessellate_dict['vertex_group_distribution_factor'] = props.vertex_group_distribution_factor
|
tessellate_dict['vertex_group_distribution_factor'] = props.vertex_group_distribution_factor
|
||||||
@ -1036,6 +1096,10 @@ def props_to_dict(ob):
|
|||||||
tessellate_dict["invert_vertex_group_scale_normals"] = props.invert_vertex_group_scale_normals
|
tessellate_dict["invert_vertex_group_scale_normals"] = props.invert_vertex_group_scale_normals
|
||||||
tessellate_dict["boundary_variable_offset"] = props.boundary_variable_offset
|
tessellate_dict["boundary_variable_offset"] = props.boundary_variable_offset
|
||||||
tessellate_dict["auto_rotate_boundary"] = props.auto_rotate_boundary
|
tessellate_dict["auto_rotate_boundary"] = props.auto_rotate_boundary
|
||||||
|
tessellate_dict["merge"] = props.merge
|
||||||
|
tessellate_dict["merge_thres"] = props.merge_thres
|
||||||
|
tessellate_dict["merge_open_edges_only"] = props.merge_open_edges_only
|
||||||
|
tessellate_dict["preserve_quads"] = props.preserve_quads
|
||||||
return tessellate_dict
|
return tessellate_dict
|
||||||
|
|
||||||
def copy_tessellate_props(source_ob, target_ob):
|
def copy_tessellate_props(source_ob, target_ob):
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
# SPDX-FileCopyrightText: 2019-2023 Blender Foundation
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
import bpy, bmesh
|
import bpy, bmesh
|
||||||
@ -183,6 +181,25 @@ def vector_rotation(vec):
|
|||||||
if ang < 0: ang = 2*pi + ang
|
if ang < 0: ang = 2*pi + ang
|
||||||
return ang
|
return ang
|
||||||
|
|
||||||
|
def signed_angle_with_axis(va, vb, axis):
|
||||||
|
return atan2(va.cross(vb).dot(axis.normalized()), va.dot(vb))
|
||||||
|
|
||||||
|
def round_angle_with_axis(va, vb, axis):
|
||||||
|
angle = signed_angle_with_axis(va, vb, axis)
|
||||||
|
return 2*pi + angle if angle < 0 else angle
|
||||||
|
|
||||||
|
def incenter(vecs):
|
||||||
|
lengths = x = y = z = 0
|
||||||
|
mid = len(vecs)//2+1
|
||||||
|
for vi, vj, vk in zip(vecs, vecs[1:]+vecs[:1], vecs[mid:]+vecs[:mid]):
|
||||||
|
length = (vj-vi).length
|
||||||
|
lengths += length
|
||||||
|
x += length*vk.x
|
||||||
|
y += length*vk.y
|
||||||
|
z += length*vk.z
|
||||||
|
inc = Vector((x/lengths, y/lengths, z/lengths))
|
||||||
|
return inc
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# SCENE
|
# SCENE
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
@ -215,7 +232,15 @@ def turn_off_animatable(scene):
|
|||||||
# OBJECTS
|
# OBJECTS
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
def convert_object_to_mesh(ob, apply_modifiers=True, preserve_status=True):
|
def remove_temp_objects():
|
||||||
|
# clean objects
|
||||||
|
for o in bpy.data.objects:
|
||||||
|
if "_tissue_tmp" in o.name:
|
||||||
|
bpy.data.objects.remove(o)
|
||||||
|
return
|
||||||
|
|
||||||
|
def convert_object_to_mesh(ob, apply_modifiers=True, preserve_status=True, mirror_correction = True):
|
||||||
|
#mirror_correction = False
|
||||||
try: ob.name
|
try: ob.name
|
||||||
except: return None
|
except: return None
|
||||||
if ob.type != 'MESH':
|
if ob.type != 'MESH':
|
||||||
@ -226,7 +251,10 @@ def convert_object_to_mesh(ob, apply_modifiers=True, preserve_status=True):
|
|||||||
#dg = bpy.context.evaluated_depsgraph_get()
|
#dg = bpy.context.evaluated_depsgraph_get()
|
||||||
#ob_eval = ob.evaluated_get(dg)
|
#ob_eval = ob.evaluated_get(dg)
|
||||||
#me = bpy.data.meshes.new_from_object(ob_eval, preserve_all_data_layers=True, depsgraph=dg)
|
#me = bpy.data.meshes.new_from_object(ob_eval, preserve_all_data_layers=True, depsgraph=dg)
|
||||||
me = simple_to_mesh(ob)
|
if mirror_correction:
|
||||||
|
me = simple_to_mesh_mirror(ob)
|
||||||
|
else:
|
||||||
|
me = simple_to_mesh(ob)
|
||||||
new_ob = bpy.data.objects.new(ob.data.name, me)
|
new_ob = bpy.data.objects.new(ob.data.name, me)
|
||||||
new_ob.location, new_ob.matrix_world = ob.location, ob.matrix_world
|
new_ob.location, new_ob.matrix_world = ob.location, ob.matrix_world
|
||||||
if not apply_modifiers:
|
if not apply_modifiers:
|
||||||
@ -234,7 +262,10 @@ def convert_object_to_mesh(ob, apply_modifiers=True, preserve_status=True):
|
|||||||
else:
|
else:
|
||||||
if apply_modifiers:
|
if apply_modifiers:
|
||||||
new_ob = ob.copy()
|
new_ob = ob.copy()
|
||||||
new_me = simple_to_mesh(ob)
|
if mirror_correction:
|
||||||
|
new_me = simple_to_mesh_mirror(ob)
|
||||||
|
else:
|
||||||
|
new_me = simple_to_mesh(ob)
|
||||||
new_ob.modifiers.clear()
|
new_ob.modifiers.clear()
|
||||||
new_ob.data = new_me
|
new_ob.data = new_me
|
||||||
else:
|
else:
|
||||||
@ -250,6 +281,76 @@ def convert_object_to_mesh(ob, apply_modifiers=True, preserve_status=True):
|
|||||||
bpy.context.view_layer.objects.active = new_ob
|
bpy.context.view_layer.objects.active = new_ob
|
||||||
return new_ob
|
return new_ob
|
||||||
|
|
||||||
|
def simple_to_mesh_mirror(ob, depsgraph=None):
|
||||||
|
'''
|
||||||
|
Convert object to mesh applying Modifiers and Shape Keys.
|
||||||
|
Automatically correct Faces rotation for Tessellations.
|
||||||
|
'''
|
||||||
|
if 'MIRROR' in [m.type for m in ob.modifiers]:
|
||||||
|
|
||||||
|
_ob = ob.copy()
|
||||||
|
_ob.name = _ob.name + "_mirror"
|
||||||
|
bpy.context.collection.objects.link(_ob)
|
||||||
|
# Store modifiers
|
||||||
|
mods = list(_ob.modifiers)
|
||||||
|
# Store visibility setting
|
||||||
|
mods_vis = [m.show_viewport for m in _ob.modifiers]
|
||||||
|
# Turn modifiers off
|
||||||
|
for m in _ob.modifiers:
|
||||||
|
m.show_viewport = False
|
||||||
|
while True:
|
||||||
|
if len(mods) == 0: break
|
||||||
|
remove_mods = []
|
||||||
|
|
||||||
|
for m, vis in zip(mods, mods_vis):
|
||||||
|
m.show_viewport = vis
|
||||||
|
remove_mods.append(m)
|
||||||
|
if m.type == 'MIRROR' and vis:
|
||||||
|
n_axis = m.use_axis[0] + m.use_axis[1] + m.use_axis[2]
|
||||||
|
fraction = 2**n_axis
|
||||||
|
me = simple_to_mesh(_ob, depsgraph)
|
||||||
|
bm = bmesh.new()
|
||||||
|
bm.from_mesh(me)
|
||||||
|
bm.faces.ensure_lookup_table()
|
||||||
|
n_faces = len(bm.faces)
|
||||||
|
if n_axis > 0:
|
||||||
|
bm.faces.ensure_lookup_table()
|
||||||
|
rotate_faces = bm.faces
|
||||||
|
rot_index = []
|
||||||
|
if n_axis == 1: fraction_val = [0,1]
|
||||||
|
elif n_axis == 2: fraction_val = [0,1,1,0]
|
||||||
|
elif n_axis == 3: fraction_val = [0,1,1,0,1,0,0,1]
|
||||||
|
for i in fraction_val:
|
||||||
|
for j in range(n_faces//fraction):
|
||||||
|
rot_index.append(i)
|
||||||
|
for face, shift in zip(rotate_faces, rot_index):
|
||||||
|
if shift == 0: continue
|
||||||
|
vs = face.verts[:]
|
||||||
|
vs2 = vs[-shift:]+vs[:-shift]
|
||||||
|
material_index = face.material_index
|
||||||
|
bm.faces.remove(face)
|
||||||
|
f2 = bm.faces.new(vs2)
|
||||||
|
f2.select = True
|
||||||
|
f2.material_index = material_index
|
||||||
|
bm.normal_update()
|
||||||
|
bm.to_mesh(me)
|
||||||
|
bm.free()
|
||||||
|
for rm in remove_mods:
|
||||||
|
_ob.modifiers.remove(rm)
|
||||||
|
_ob.data = me
|
||||||
|
mods = mods[1:]
|
||||||
|
mods_vis = mods_vis[1:]
|
||||||
|
remove_mods = []
|
||||||
|
break
|
||||||
|
if m == mods[-1]:
|
||||||
|
mods = []
|
||||||
|
me = simple_to_mesh(_ob, depsgraph)
|
||||||
|
_ob.data = me
|
||||||
|
_ob.modifiers.clear()
|
||||||
|
else:
|
||||||
|
me = simple_to_mesh(ob, depsgraph)
|
||||||
|
return me
|
||||||
|
|
||||||
def simple_to_mesh(ob, depsgraph=None):
|
def simple_to_mesh(ob, depsgraph=None):
|
||||||
'''
|
'''
|
||||||
Convert object to mesh applying Modifiers and Shape Keys
|
Convert object to mesh applying Modifiers and Shape Keys
|
||||||
@ -263,6 +364,7 @@ def simple_to_mesh(ob, depsgraph=None):
|
|||||||
dg = depsgraph
|
dg = depsgraph
|
||||||
ob_eval = ob.evaluated_get(dg)
|
ob_eval = ob.evaluated_get(dg)
|
||||||
me = bpy.data.meshes.new_from_object(ob_eval, preserve_all_data_layers=True, depsgraph=dg)
|
me = bpy.data.meshes.new_from_object(ob_eval, preserve_all_data_layers=True, depsgraph=dg)
|
||||||
|
#me.calc_normals()
|
||||||
return me
|
return me
|
||||||
|
|
||||||
def _join_objects(context, objects, link_to_scene=True, make_active=True):
|
def _join_objects(context, objects, link_to_scene=True, make_active=True):
|
||||||
@ -320,11 +422,10 @@ def join_objects(context, objects):
|
|||||||
return new_ob
|
return new_ob
|
||||||
|
|
||||||
def join_objects(objects):
|
def join_objects(objects):
|
||||||
override = bpy.context.copy()
|
|
||||||
new_ob = objects[0]
|
new_ob = objects[0]
|
||||||
override['active_object'] = new_ob
|
override = {'active_object': new_ob, 'selected_editable_objects': objects}
|
||||||
override['selected_editable_objects'] = objects
|
with bpy.context.temp_override(**override):
|
||||||
bpy.ops.object.join(override)
|
bpy.ops.object.join()
|
||||||
return new_ob
|
return new_ob
|
||||||
|
|
||||||
def repeat_mesh(me, n):
|
def repeat_mesh(me, n):
|
||||||
@ -345,9 +446,8 @@ def array_mesh(ob, n):
|
|||||||
arr = ob.modifiers.new('Repeat','ARRAY')
|
arr = ob.modifiers.new('Repeat','ARRAY')
|
||||||
arr.relative_offset_displace[0] = 0
|
arr.relative_offset_displace[0] = 0
|
||||||
arr.count = n
|
arr.count = n
|
||||||
# with bpy.context.temp_override(active_object=ob):
|
#bpy.ops.object.modifier_apply({'active_object':ob},modifier='Repeat')
|
||||||
# bpy.ops.object.modifier_apply(modifier='Repeat')
|
#me = ob.data
|
||||||
# me = ob.data
|
|
||||||
ob.modifiers.update()
|
ob.modifiers.update()
|
||||||
|
|
||||||
dg = bpy.context.evaluated_depsgraph_get()
|
dg = bpy.context.evaluated_depsgraph_get()
|
||||||
@ -366,7 +466,8 @@ def array_mesh_object(ob, n):
|
|||||||
override = bpy.context.copy()
|
override = bpy.context.copy()
|
||||||
override['active_object'] = ob
|
override['active_object'] = ob
|
||||||
override = {'active_object': ob}
|
override = {'active_object': ob}
|
||||||
bpy.ops.object.modifier_apply(override, modifier=arr.name)
|
with bpy.context.temp_override(**override):
|
||||||
|
bpy.ops.object.modifier_apply(modifier=arr.name)
|
||||||
return ob
|
return ob
|
||||||
|
|
||||||
|
|
||||||
@ -389,7 +490,7 @@ def get_mesh_before_subs(ob):
|
|||||||
hide_mods = []
|
hide_mods = []
|
||||||
mods_visibility = []
|
mods_visibility = []
|
||||||
for m in hide_mods: m.show_viewport = False
|
for m in hide_mods: m.show_viewport = False
|
||||||
me = simple_to_mesh(ob)
|
me = simple_to_mesh_mirror(ob)
|
||||||
for m, vis in zip(hide_mods,mods_visibility): m.show_viewport = vis
|
for m, vis in zip(hide_mods,mods_visibility): m.show_viewport = vis
|
||||||
return me, subs
|
return me, subs
|
||||||
|
|
||||||
@ -542,9 +643,6 @@ def get_patches____(me_low, me_high, sides, subs, bool_selection, bool_material_
|
|||||||
# fill inners
|
# fill inners
|
||||||
patches[:,1:-1,1:-1] = inners[None,:,:] + ips[:,None,None]
|
patches[:,1:-1,1:-1] = inners[None,:,:] + ips[:,None,None]
|
||||||
|
|
||||||
#end_time = time.time()
|
|
||||||
#print('Tissue: Got Patches in {:.4f} sec'.format(end_time-start_time))
|
|
||||||
|
|
||||||
return patches, mask
|
return patches, mask
|
||||||
|
|
||||||
def tessellate_prepare_component(ob1, props):
|
def tessellate_prepare_component(ob1, props):
|
||||||
@ -673,10 +771,11 @@ def tessellate_prepare_component(ob1, props):
|
|||||||
cut_edges = [g for g in bisect['geom_cut'] if type(g)==bmesh.types.BMEdge]
|
cut_edges = [g for g in bisect['geom_cut'] if type(g)==bmesh.types.BMEdge]
|
||||||
cut_verts = [g for g in bisect['geom_cut'] if type(g)==bmesh.types.BMVert]
|
cut_verts = [g for g in bisect['geom_cut'] if type(g)==bmesh.types.BMVert]
|
||||||
|
|
||||||
if bound!='CLIP':
|
if True or bound!='CLIP':
|
||||||
for e in cut_edges:
|
for e in cut_edges:
|
||||||
seam = True
|
seam = True
|
||||||
# Prevent glitches
|
# Prevent glitches
|
||||||
|
'''
|
||||||
for e1 in original_edges:
|
for e1 in original_edges:
|
||||||
match_00 = (e.verts[0].co-e1.verts[0].co).length < thres
|
match_00 = (e.verts[0].co-e1.verts[0].co).length < thres
|
||||||
match_11 = (e.verts[1].co-e1.verts[1].co).length < thres
|
match_11 = (e.verts[1].co-e1.verts[1].co).length < thres
|
||||||
@ -685,6 +784,7 @@ def tessellate_prepare_component(ob1, props):
|
|||||||
if (match_00 and match_11) or (match_01 and match_10):
|
if (match_00 and match_11) or (match_01 and match_10):
|
||||||
seam = False
|
seam = False
|
||||||
break
|
break
|
||||||
|
'''
|
||||||
e.seam = seam
|
e.seam = seam
|
||||||
|
|
||||||
if bound == 'CYCLIC':
|
if bound == 'CYCLIC':
|
||||||
@ -912,6 +1012,26 @@ def get_edges_id_numpy(mesh):
|
|||||||
edges = np.concatenate((edges,indexes), axis=1)
|
edges = np.concatenate((edges,indexes), axis=1)
|
||||||
return edges
|
return edges
|
||||||
|
|
||||||
|
def get_edges_numpy_ex(mesh):
|
||||||
|
'''
|
||||||
|
Create a numpy array with the edges of a given mesh, or all the possible
|
||||||
|
between the vertices of a same face
|
||||||
|
'''
|
||||||
|
edges_verts = get_edges_numpy(mesh)
|
||||||
|
polygons_diag = []
|
||||||
|
for f in mesh.polygons:
|
||||||
|
sides = len(f.vertices)
|
||||||
|
if sides < 4: continue
|
||||||
|
for i in range(sides-2):
|
||||||
|
v0 = f.vertices[i]
|
||||||
|
for j in range(i+2, sides-1 if i == 0 else sides):
|
||||||
|
v1 = f.vertices[j]
|
||||||
|
polygons_diag.append((v0,v1))
|
||||||
|
if len(polygons_diag) == 0:
|
||||||
|
return edges_verts
|
||||||
|
polygons_diag = np.array(polygons_diag,dtype=np.int32)
|
||||||
|
return np.concatenate((edges_verts, polygons_diag), axis=0)
|
||||||
|
|
||||||
def get_polygons_select_numpy(mesh):
|
def get_polygons_select_numpy(mesh):
|
||||||
n_polys = len(mesh.polygons)
|
n_polys = len(mesh.polygons)
|
||||||
selections = [0]*n_polys*2
|
selections = [0]*n_polys*2
|
||||||
@ -919,13 +1039,16 @@ def get_polygons_select_numpy(mesh):
|
|||||||
selections = np.array(selections)
|
selections = np.array(selections)
|
||||||
return selections
|
return selections
|
||||||
|
|
||||||
def get_attribute_numpy(elements_list, attribute='select', mult=1):
|
def get_attribute_numpy(elements_list, attribute='select', mult=1, size=None):
|
||||||
'''
|
'''
|
||||||
Generate a numpy array getting attribute from a list of element using
|
Generate a numpy array getting attribute from a list of element using
|
||||||
the foreach_get() function.
|
the foreach_get() function.
|
||||||
'''
|
'''
|
||||||
n_elements = len(elements_list)
|
if size:
|
||||||
values = [0]*n_elements*mult
|
n_elements = size
|
||||||
|
else:
|
||||||
|
n_elements = len(elements_list)
|
||||||
|
values = np.zeros(int(n_elements*mult))
|
||||||
elements_list.foreach_get(attribute, values)
|
elements_list.foreach_get(attribute, values)
|
||||||
values = np.array(values)
|
values = np.array(values)
|
||||||
if mult > 1: values = values.reshape((n_elements,mult))
|
if mult > 1: values = values.reshape((n_elements,mult))
|
||||||
@ -1006,6 +1129,73 @@ def find_curves(edges, n_verts):
|
|||||||
curves.append(curve)
|
curves.append(curve)
|
||||||
return curves
|
return curves
|
||||||
|
|
||||||
|
def find_curves_attribute(edges, n_verts, attribute):
|
||||||
|
# dictionary with a list for every point
|
||||||
|
verts_dict = {key:[] for key in range(n_verts)}
|
||||||
|
# get neighbors for every point
|
||||||
|
for e in edges:
|
||||||
|
verts_dict[e[0]].append(e[1])
|
||||||
|
verts_dict[e[1]].append(e[0])
|
||||||
|
curves = []
|
||||||
|
ordered_attr = []
|
||||||
|
while True:
|
||||||
|
if len(verts_dict) == 0: break
|
||||||
|
# next starting point
|
||||||
|
v = list(verts_dict.keys())[0]
|
||||||
|
# neighbors
|
||||||
|
v01 = verts_dict[v]
|
||||||
|
if len(v01) == 0:
|
||||||
|
verts_dict.pop(v)
|
||||||
|
continue
|
||||||
|
curve = []
|
||||||
|
attr = []
|
||||||
|
if len(v01) > 1:
|
||||||
|
curve.append(v01[1]) # add neighbors
|
||||||
|
attr.append(attribute[v01[1]]) # add neighbors
|
||||||
|
curve.append(v) # add starting point
|
||||||
|
attr.append(attribute[v])
|
||||||
|
curve.append(v01[0]) # add neighbors
|
||||||
|
attr.append(attribute[v01[0]])
|
||||||
|
verts_dict.pop(v)
|
||||||
|
# start building curve
|
||||||
|
while True:
|
||||||
|
#last_point = curve[-1]
|
||||||
|
#if last_point not in verts_dict: break
|
||||||
|
|
||||||
|
# try to change direction if needed
|
||||||
|
if curve[-1] in verts_dict: pass
|
||||||
|
elif curve[0] in verts_dict:
|
||||||
|
curve.reverse()
|
||||||
|
attr.reverse()
|
||||||
|
else: break
|
||||||
|
|
||||||
|
# neighbors points
|
||||||
|
last_point = curve[-1]
|
||||||
|
v01 = verts_dict[last_point]
|
||||||
|
|
||||||
|
# curve end
|
||||||
|
if len(v01) == 1:
|
||||||
|
verts_dict.pop(last_point)
|
||||||
|
if curve[0] in verts_dict: continue
|
||||||
|
else: break
|
||||||
|
|
||||||
|
# chose next point
|
||||||
|
new_point = None
|
||||||
|
if v01[0] == curve[-2]: new_point = v01[1]
|
||||||
|
elif v01[1] == curve[-2]: new_point = v01[0]
|
||||||
|
#else: break
|
||||||
|
|
||||||
|
#if new_point != curve[1]:
|
||||||
|
curve.append(new_point)
|
||||||
|
ordered_attr.append(attr)
|
||||||
|
verts_dict.pop(last_point)
|
||||||
|
if curve[0] == curve[-1]:
|
||||||
|
verts_dict.pop(new_point)
|
||||||
|
break
|
||||||
|
if(len(curve)>0):
|
||||||
|
curves.append(curve)
|
||||||
|
return curves, ordered_attr
|
||||||
|
|
||||||
def curve_from_points(points, name='Curve'):
|
def curve_from_points(points, name='Curve'):
|
||||||
curve = bpy.data.curves.new(name,'CURVE')
|
curve = bpy.data.curves.new(name,'CURVE')
|
||||||
for c in points:
|
for c in points:
|
||||||
@ -1015,8 +1205,54 @@ def curve_from_points(points, name='Curve'):
|
|||||||
ob_curve = bpy.data.objects.new(name,curve)
|
ob_curve = bpy.data.objects.new(name,curve)
|
||||||
return ob_curve
|
return ob_curve
|
||||||
|
|
||||||
def curve_from_pydata(points, radii, indexes, name='Curve', skip_open=False, merge_distance=1, set_active=True, only_data=False):
|
def curve_from_pydata(points, radii, indexes, name='Curve', skip_open=False, merge_distance=1, set_active=True, only_data=False, curve=None, spline_type='POLY'):
|
||||||
curve = bpy.data.curves.new(name,'CURVE')
|
if not curve:
|
||||||
|
curve = bpy.data.curves.new(name,'CURVE')
|
||||||
|
curve.dimensions = '3D'
|
||||||
|
use_rad = True
|
||||||
|
for c in indexes:
|
||||||
|
bool_cyclic = c[0] == c[-1]
|
||||||
|
if bool_cyclic: c.pop(-1)
|
||||||
|
# cleanup
|
||||||
|
pts = np.array([points[i] for i in c])
|
||||||
|
try:
|
||||||
|
rad = np.array([radii[i] for i in c])
|
||||||
|
except:
|
||||||
|
use_rad = False
|
||||||
|
rad = 1
|
||||||
|
if merge_distance > 0:
|
||||||
|
pts1 = np.roll(pts,1,axis=0)
|
||||||
|
dist = np.linalg.norm(np.array(pts1-pts, dtype=np.float64), axis=1)
|
||||||
|
count = 0
|
||||||
|
n = len(dist)
|
||||||
|
mask = np.ones(n).astype('bool')
|
||||||
|
for i in range(n):
|
||||||
|
count += dist[i]
|
||||||
|
if count > merge_distance: count = 0
|
||||||
|
else: mask[i] = False
|
||||||
|
pts = pts[mask]
|
||||||
|
if use_rad: rad = rad[mask]
|
||||||
|
|
||||||
|
if skip_open and not bool_cyclic: continue
|
||||||
|
s = curve.splines.new(spline_type)
|
||||||
|
n_pts = len(pts)
|
||||||
|
s.points.add(n_pts-1)
|
||||||
|
w = np.ones(n_pts).reshape((n_pts,1))
|
||||||
|
co = np.concatenate((pts,w),axis=1).reshape((n_pts*4))
|
||||||
|
s.points.foreach_set('co',co)
|
||||||
|
if use_rad: s.points.foreach_set('radius',rad)
|
||||||
|
s.use_cyclic_u = bool_cyclic
|
||||||
|
if only_data:
|
||||||
|
return curve
|
||||||
|
else:
|
||||||
|
ob_curve = bpy.data.objects.new(name,curve)
|
||||||
|
bpy.context.collection.objects.link(ob_curve)
|
||||||
|
if set_active:
|
||||||
|
bpy.context.view_layer.objects.active = ob_curve
|
||||||
|
return ob_curve
|
||||||
|
|
||||||
|
def update_curve_from_pydata_simple(curve, points, radii, indexes, skip_open=False, merge_distance=1, set_active=True, only_data=False, spline_type='POLY'):
|
||||||
|
curve.splines.clear()
|
||||||
curve.dimensions = '3D'
|
curve.dimensions = '3D'
|
||||||
use_rad = True
|
use_rad = True
|
||||||
for c in indexes:
|
for c in indexes:
|
||||||
@ -1043,7 +1279,7 @@ def curve_from_pydata(points, radii, indexes, name='Curve', skip_open=False, mer
|
|||||||
if use_rad: rad = rad[mask]
|
if use_rad: rad = rad[mask]
|
||||||
|
|
||||||
if skip_open and not bool_cyclic: continue
|
if skip_open and not bool_cyclic: continue
|
||||||
s = curve.splines.new('POLY')
|
s = curve.splines.new(spline_type)
|
||||||
n_pts = len(pts)
|
n_pts = len(pts)
|
||||||
s.points.add(n_pts-1)
|
s.points.add(n_pts-1)
|
||||||
w = np.ones(n_pts).reshape((n_pts,1))
|
w = np.ones(n_pts).reshape((n_pts,1))
|
||||||
@ -1091,13 +1327,14 @@ def update_curve_from_pydata(curve, points, normals, radii, indexes, merge_dista
|
|||||||
#if skip_open and not bool_cyclic: continue
|
#if skip_open and not bool_cyclic: continue
|
||||||
n_pts = len(pts)
|
n_pts = len(pts)
|
||||||
series = np.arange(n_pts)
|
series = np.arange(n_pts)
|
||||||
patt1 = series + (series-series%pattern[1])/pattern[1]*pattern[0]+pattern[0]
|
if pattern[0]*pattern[1] != 0:
|
||||||
patt1 = patt1[patt1<n_pts].astype('int')
|
patt1 = series + (series-series%pattern[1])/pattern[1]*pattern[0]+pattern[0]
|
||||||
patt0 = series + (series-series%pattern[0])/pattern[0]*pattern[1]
|
patt1 = patt1[patt1<n_pts].astype('int')
|
||||||
patt0 = patt0[patt0<n_pts].astype('int')
|
patt0 = series + (series-series%pattern[0])/pattern[0]*pattern[1]
|
||||||
nor[patt0] *= 0.5*depth*(1 + offset)
|
patt0 = patt0[patt0<n_pts].astype('int')
|
||||||
nor[patt1] *= 0.5*depth*(-1 + offset)
|
nor[patt0] *= 0.5*depth*(1 + offset)
|
||||||
if pattern[0]*pattern[1] != 0: pts += nor
|
nor[patt1] *= 0.5*depth*(-1 + offset)
|
||||||
|
pts += nor
|
||||||
s = curve.splines.new('POLY')
|
s = curve.splines.new('POLY')
|
||||||
s.points.add(n_pts-1)
|
s.points.add(n_pts-1)
|
||||||
w = np.ones(n_pts).reshape((n_pts,1))
|
w = np.ones(n_pts).reshape((n_pts,1))
|
||||||
@ -1253,7 +1490,7 @@ def get_weight(vertex_group, n_verts):
|
|||||||
:type vertex_group: :class:'bpy.types.VertexGroup'
|
:type vertex_group: :class:'bpy.types.VertexGroup'
|
||||||
:arg n_verts: Number of Vertices (output list size).
|
:arg n_verts: Number of Vertices (output list size).
|
||||||
:type n_verts: int
|
:type n_verts: int
|
||||||
:return: Read weight values.
|
:return: Readed weight values.
|
||||||
:rtype: list
|
:rtype: list
|
||||||
"""
|
"""
|
||||||
weight = [0]*n_verts
|
weight = [0]*n_verts
|
||||||
@ -1269,22 +1506,24 @@ def get_weight_numpy(vertex_group, n_verts):
|
|||||||
:type vertex_group: :class:'bpy.types.VertexGroup'
|
:type vertex_group: :class:'bpy.types.VertexGroup'
|
||||||
:arg n_verts: Number of Vertices (output list size).
|
:arg n_verts: Number of Vertices (output list size).
|
||||||
:type n_verts: int
|
:type n_verts: int
|
||||||
:return: Read weight values as numpy array.
|
:return: Readed weight values as numpy array.
|
||||||
:rtype: :class:'numpy.ndarray'
|
:rtype: :class:'numpy.ndarray'
|
||||||
"""
|
"""
|
||||||
weight = [0]*n_verts
|
weight = np.zeros(n_verts)
|
||||||
for i in range(n_verts):
|
for i in range(n_verts):
|
||||||
try: weight[i] = vertex_group.weight(i)
|
try: weight[i] = vertex_group.weight(i)
|
||||||
except: pass
|
except: pass
|
||||||
return np.array(weight)
|
return weight
|
||||||
|
|
||||||
def bmesh_get_weight_numpy(group_index, layer, verts):
|
def bmesh_get_weight_numpy(group_index, layer, verts, normalized=False):
|
||||||
weight = np.zeros(len(verts))
|
weight = np.zeros(len(verts))
|
||||||
for i, v in enumerate(verts):
|
for i, v in enumerate(verts):
|
||||||
dvert = v[layer]
|
dvert = v[layer]
|
||||||
if group_index in dvert:
|
if group_index in dvert:
|
||||||
weight[i] = dvert[group_index]
|
weight[i] = dvert[group_index]
|
||||||
#dvert[group_index] = 0.5
|
#dvert[group_index] = 0.5
|
||||||
|
if normalized:
|
||||||
|
weight = (weight - np.min(weight))/np.ptp(weight)
|
||||||
return weight
|
return weight
|
||||||
|
|
||||||
def bmesh_set_weight_numpy(group_index, layer, verts, weight):
|
def bmesh_set_weight_numpy(group_index, layer, verts, weight):
|
||||||
@ -1410,6 +1649,22 @@ def mesh_diffusion_vector(me, vectors, iter, diff, uv_dir=0):
|
|||||||
vectors[:,2] = z
|
vectors[:,2] = z
|
||||||
return vectors
|
return vectors
|
||||||
|
|
||||||
|
def fill_neighbors_attribute(verts,weight,attribute):
|
||||||
|
neigh = {}
|
||||||
|
for v0 in verts:
|
||||||
|
for f in v0.link_faces:
|
||||||
|
for v1 in f.verts:
|
||||||
|
if attribute == 'GEODESIC':
|
||||||
|
dist = weight[v0.index] + (v0.co-v1.co).length
|
||||||
|
elif attribute == 'TOPOLOGY':
|
||||||
|
dist = weight[v0.index] + 1.0
|
||||||
|
w1 = weight[v1.index]
|
||||||
|
if w1 == None or w1 > dist:
|
||||||
|
weight[v1.index] = dist
|
||||||
|
neigh[v1] = 0
|
||||||
|
if len(neigh) == 0: return weight
|
||||||
|
else: return fill_neighbors_attribute(neigh.keys(), weight, attribute)
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# MODIFIERS
|
# MODIFIERS
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
@ -1434,7 +1689,7 @@ def mod_preserve_shape(mod):
|
|||||||
|
|
||||||
def recurLayerCollection(layerColl, collName):
|
def recurLayerCollection(layerColl, collName):
|
||||||
'''
|
'''
|
||||||
Recursively transverse layer_collection for a particular name.
|
Recursivly transverse layer_collection for a particular name.
|
||||||
'''
|
'''
|
||||||
found = None
|
found = None
|
||||||
if (layerColl.name == collName):
|
if (layerColl.name == collName):
|
||||||
@ -1456,3 +1711,15 @@ def auto_layer_collection():
|
|||||||
lc = recurLayerCollection(layer_collection, c.name)
|
lc = recurLayerCollection(layer_collection, c.name)
|
||||||
if not c.hide_viewport and not lc.hide_viewport:
|
if not c.hide_viewport and not lc.hide_viewport:
|
||||||
bpy.context.view_layer.active_layer_collection = lc
|
bpy.context.view_layer.active_layer_collection = lc
|
||||||
|
|
||||||
|
def np_remap_image_values(img, channel=0, min=0, max=1, invert=False):
|
||||||
|
nx = img.size[1]
|
||||||
|
ny = img.size[0]
|
||||||
|
px = np.float32(np.zeros(nx*ny*4))
|
||||||
|
img.pixels.foreach_get(px)
|
||||||
|
px = np.array(px).reshape((-1,4))
|
||||||
|
values = px[:,channel]
|
||||||
|
values = values.reshape((nx,ny))
|
||||||
|
if invert:
|
||||||
|
values = 1-values
|
||||||
|
return min + values*(max-min)
|
||||||
|
@ -1,7 +1,25 @@
|
|||||||
# SPDX-FileCopyrightText: 2022 Blender Foundation
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
#
|
||||||
|
# ##### END GPL LICENSE BLOCK #####
|
||||||
|
|
||||||
|
# <pep8 compliant>
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
# Author: Stephen Leger (s-leger)
|
# Author: Stephen Leger (s-leger)
|
||||||
#
|
#
|
||||||
@ -26,7 +44,7 @@ class Pip:
|
|||||||
import sys
|
import sys
|
||||||
site_package = site.getusersitepackages()
|
site_package = site.getusersitepackages()
|
||||||
if not os.path.exists(site_package):
|
if not os.path.exists(site_package):
|
||||||
site_package = bpy.utils.user_resource('SCRIPTS', "site_package", create=True)
|
site_package = bpy.utils.user_resource('SCRIPTS', path="site_package", create=True)
|
||||||
site.addsitedir(site_package)
|
site.addsitedir(site_package)
|
||||||
if site_package not in sys.path:
|
if site_package not in sys.path:
|
||||||
sys.path.append(site_package)
|
sys.path.append(site_package)
|
||||||
|
@ -1,7 +1,23 @@
|
|||||||
# SPDX-FileCopyrightText: 2017-2022 Blender Foundation
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
#
|
||||||
|
# ##### END GPL LICENSE BLOCK #####
|
||||||
|
|
||||||
# --------------------------------- UV to MESH ------------------------------- #
|
# --------------------------------- UV to MESH ------------------------------- #
|
||||||
# -------------------------------- version 0.1.1 ----------------------------- #
|
# -------------------------------- version 0.1.1 ----------------------------- #
|
||||||
# #
|
# #
|
||||||
|
1180
mesh_tissue/weight_reaction_diffusion.py
Normal file
1180
mesh_tissue/weight_reaction_diffusion.py
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user