From 0b252324f2ac47df94a5b63004e240258253e1e7 Mon Sep 17 00:00:00 2001
From: 0000OOOO0000 <63518686+0000OOOO0000@users.noreply.github.com>
Date: Sat, 21 Nov 2020 01:32:45 +0200
Subject: [PATCH] Delete TESSELLATE_NUMPY.PY

---
 .../ADDONS/TISSUE-MASTER/TESSELLATE_NUMPY.PY  | 4280 -----------------
 1 file changed, 4280 deletions(-)
 delete mode 100644 ◯ᴥᗱᗴᗝИNᗱᗴᙁ⚭ⵙ⚭ᙁᗱᗴИNᗝᗱᗴᴥ◯/2.90/SCRIPTS/ADDONS/TISSUE-MASTER/TESSELLATE_NUMPY.PY

diff --git a/◯ᴥᗱᗴᗝИNᗱᗴᙁ⚭ⵙ⚭ᙁᗱᗴИNᗝᗱᗴᴥ◯/2.90/SCRIPTS/ADDONS/TISSUE-MASTER/TESSELLATE_NUMPY.PY b/◯ᴥᗱᗴᗝИNᗱᗴᙁ⚭ⵙ⚭ᙁᗱᗴИNᗝᗱᗴᴥ◯/2.90/SCRIPTS/ADDONS/TISSUE-MASTER/TESSELLATE_NUMPY.PY
deleted file mode 100644
index a66f7748..00000000
--- a/◯ᴥᗱᗴᗝИNᗱᗴᙁ⚭ⵙ⚭ᙁᗱᗴИNᗝᗱᗴᴥ◯/2.90/SCRIPTS/ADDONS/TISSUE-MASTER/TESSELLATE_NUMPY.PY
+++ /dev/null
@@ -1,4280 +0,0 @@
-# ##### 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 --------------------------- #
-# ------------------------------- version 0.84 ------------------------------- #
-#                                                                              #
-# Creates duplicates of selected mesh to active morphing the shape according   #
-# to target faces.                                                             #
-#                                                                              #
-#                    (c)  Alessandro Zomparelli                                #
-#                             (2017)                                           #
-#                                                                              #
-# http://www.co-de-it.com/                                                     #
-#                                                                              #
-# ############################################################################ #
-
-
-import bpy
-from bpy.types import (
-        Operator,
-        Panel,
-        PropertyGroup,
-        )
-from bpy.props import (
-        BoolProperty,
-        EnumProperty,
-        FloatProperty,
-        IntProperty,
-        StringProperty,
-        PointerProperty
-        )
-from mathutils import Vector
-import numpy as np
-from math import *
-import random, time, copy
-import bmesh
-from .utils import *
-
-def anim_tessellate_active(self, context):
-    ob = context.object
-    props = ob.tissue_tessellate
-    if not (props.bool_lock or props.bool_hold):
-        try:
-            props.generator.name
-            props.component.name
-            bpy.ops.object.tissue_update_tessellate()
-        except: pass
-
-def anim_tessellate_object(ob):
-    try:
-        #bpy.context.view_layer.objects.active = ob
-        bpy.ops.object.tissue_update_tessellate()
-    except:
-        return None
-
-#from bpy.app.handlers import persistent
-
-def anim_tessellate(scene):
-    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'):
-        update_objects = []
-        for ob in scene.objects:
-            if ob.tissue_tessellate.bool_run and not ob.tissue_tessellate.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:
-            override = {'object': ob}
-            '''
-            win      = bpy.data.window_managers[0].windows[0]#bpy.context.window
-            scr      = win.screen
-            areas3d  = [area for area in scr.areas if area.type == 'VIEW_3D']
-            region   = [region for region in areas3d[0].regions if region.type == 'WINDOW']
-            override = {
-                'window':win,
-                'screen':scr,
-                'area'  :areas3d[0],
-                'region':region[0],
-                'scene' :scene,
-                'object': ob
-            }
-            '''
-            print(override)
-            bpy.ops.object.tissue_update_tessellate(override)
-    # restore selected objects
-    if old_mode != None:
-        for o in scene.objects:
-            if not o.hide_viewport: o.select_set(o in selected_objects)
-        bpy.context.view_layer.objects.active = active_object
-        bpy.ops.object.mode_set(mode=old_mode)
-    return
-
-
-def set_tessellate_handler(self, context):
-    old_handlers = []
-    blender_handlers = bpy.app.handlers.frame_change_post
-    for h in blender_handlers:
-        if "anim_tessellate" in str(h):
-            old_handlers.append(h)
-    for h in old_handlers: blender_handlers.remove(h)
-    for o in context.scene.objects:
-        if o.tissue_tessellate.bool_run:
-            blender_handlers.append(anim_tessellate)
-            break
-    return
-
-class tissue_tessellate_prop(PropertyGroup):
-    bool_lock : BoolProperty(
-        name="Lock",
-        description="Prevent automatic update on settings changes or if other objects have it in the hierarchy.",
-        default=False
-        )
-    bool_hold : BoolProperty(
-        name="Hold",
-        description="Wait...",
-        default=False
-        )
-    bool_dependencies : BoolProperty(
-        name="Update Dependencies",
-        description="Automatically updates base and components as well, if results of other tessellations",
-        default=False
-        )
-    bool_run : BoolProperty(
-        name="Animatable Tessellation",
-        description="Automatically recompute the tessellation when the frame is changed. Currently is not working during  Render Animation",
-        default = False,
-        update = set_tessellate_handler
-        )
-    zscale : FloatProperty(
-        name="Scale", default=1, soft_min=0, soft_max=10,
-        description="Scale factor for the component thickness",
-        update = anim_tessellate_active
-        )
-    scale_mode : EnumProperty(
-        items=(
-                ('CONSTANT', "Constant", "Uniform thinkness"),
-                ('ADAPTIVE', "Relative", "Preserve component's proportions")
-                ),
-        default='ADAPTIVE',
-        name="Z-Scale according to faces size",
-        update = anim_tessellate_active
-        )
-    offset : FloatProperty(
-        name="Surface Offset",
-        default=1,
-        min=-1,
-        max=1,
-        soft_min=-1,
-        soft_max=1,
-        description="Surface offset",
-        update = anim_tessellate_active
-        )
-    mode : EnumProperty(
-        items=(
-            ('BOUNDS', "Bounds", "The component fits automatically the size of the target face"),
-            ('LOCAL', "Local", "Based on Local coordinates, from 0 to 1"),
-            ('GLOBAL', 'Global', "Based on Global coordinates, from 0 to 1")),
-        default='BOUNDS',
-        name="Component Mode",
-        update = anim_tessellate_active
-        )
-    rotation_mode : EnumProperty(
-        items=(('RANDOM', "Random", "Random faces rotation"),
-               ('UV', "Active UV", "Rotate according to UV coordinates"),
-               ('WEIGHT', "Active Weight", "Rotate according to Vertex Group gradient"),
-               ('DEFAULT', "Default", "Default rotation")),
-        default='DEFAULT',
-        name="Component Rotation",
-        update = anim_tessellate_active
-        )
-    rotation_direction : EnumProperty(
-        items=(('ORTHO', "Orthogonal", "Component main directions in XY"),
-               ('DIAG', "Diagonal", "Component main direction aligned with diagonal")),
-        default='ORTHO',
-        name="Direction",
-        update = anim_tessellate_active
-        )
-    rotation_shift : IntProperty(
-        name="Shift",
-        default=0,
-        soft_min=0,
-        soft_max=3,
-        description="Shift components rotation",
-        update = anim_tessellate_active
-        )
-    fill_mode : EnumProperty(
-        items=(
-            ('QUAD', 'Quad', 'Regular quad tessellation. Uses only 3 or 4 vertices'),
-            ('FAN', 'Fan', 'Radial tessellation for polygonal faces'),
-            ('PATCH', 'Patch', 'Curved tessellation according to the last ' +
-            'Subsurf\n(or Multires) modifiers. Works only with 4 sides ' +
-            'patches.\nAfter the last Subsurf (or Multires) only ' +
-            'deformation\nmodifiers can be used'),
-            ('FRAME', 'Frame', 'Essellation along the edges of each face')),
-        default='QUAD',
-        name="Fill Mode",
-        update = anim_tessellate_active
-        )
-    combine_mode : EnumProperty(
-        items=(
-            ('LAST', 'Last', 'Show only the last iteration'),
-            ('UNUSED', 'Unused', 'Combine each iteration with the unused faces of the previous iteration. Used for branching systems'),
-            ('ALL', 'All', 'Combine the result of all iterations')),
-        default='LAST',
-        name="Combine Mode",
-        update = anim_tessellate_active
-        )
-    gen_modifiers : BoolProperty(
-        name="Generator Modifiers",
-        default=False,
-        description="Apply Modifiers and Shape Keys to the base object",
-        update = anim_tessellate_active
-        )
-    com_modifiers : BoolProperty(
-        name="Component Modifiers",
-        default=False,
-        description="Apply Modifiers and Shape Keys to the component object",
-        update = anim_tessellate_active
-        )
-    merge : BoolProperty(
-        name="Merge",
-        default=False,
-        description="Merge vertices in adjacent duplicates",
-        update = anim_tessellate_active
-        )
-    merge_thres : FloatProperty(
-        name="Distance",
-        default=0.001,
-        soft_min=0,
-        soft_max=10,
-        description="Limit below which to merge vertices",
-        update = anim_tessellate_active
-        )
-    generator : PointerProperty(
-        type=bpy.types.Object,
-        name="",
-        description="Base object for the tessellation",
-        update = anim_tessellate_active
-        )
-    component : PointerProperty(
-        type=bpy.types.Object,
-        name="",
-        description="Component object for the tessellation",
-        #default="",
-        update = anim_tessellate_active
-        )
-    bool_random : BoolProperty(
-        name="Randomize",
-        default=False,
-        description="Randomize component rotation",
-        update = anim_tessellate_active
-        )
-    random_seed : IntProperty(
-        name="Seed",
-        default=0,
-        soft_min=0,
-        soft_max=10,
-        description="Random seed",
-        update = anim_tessellate_active
-        )
-    bool_vertex_group : BoolProperty(
-        name="Map Vertex Group",
-        default=False,
-        description="Transfer all Vertex Groups from Base object",
-        update = anim_tessellate_active
-        )
-    bool_selection : BoolProperty(
-        name="On selected Faces",
-        default=False,
-        description="Create Tessellation only on selected faces",
-        update = anim_tessellate_active
-        )
-    bool_shapekeys : BoolProperty(
-        name="Use Shape Keys",
-        default=False,
-        description="Transfer Component's Shape Keys. If the name of Vertex "
-                    "Groups and Shape Keys are the same, they will be "
-                    "automatically combined",
-        update = anim_tessellate_active
-        )
-    bool_smooth : BoolProperty(
-        name="Smooth Shading",
-        default=False,
-        description="Output faces with smooth shading rather than flat shaded",
-        update = anim_tessellate_active
-        )
-    bool_materials : BoolProperty(
-        name="Transfer Materials",
-        default=False,
-        description="Preserve component's materials",
-        update = anim_tessellate_active
-        )
-    bool_material_id : BoolProperty(
-        name="Tessellation on Material ID",
-        default=False,
-        description="Apply the component only on the selected Material",
-        update = anim_tessellate_active
-        )
-    material_id : IntProperty(
-        name="Material ID",
-        default=0,
-        min=0,
-        description="Material ID",
-        update = anim_tessellate_active
-        )
-    bool_dissolve_seams : BoolProperty(
-        name="Dissolve Seams",
-        default=False,
-        description="Dissolve all seam edges",
-        update = anim_tessellate_active
-        )
-    iterations : IntProperty(
-        name="Iterations",
-        default=1,
-        min=1,
-        soft_max=5,
-        description="Automatically repeat the Tessellation using the "
-                    + "generated geometry as new base object.\nUsefull for "
-                    + "for branching systems. Dangerous!",
-        update = anim_tessellate_active
-        )
-    bool_combine : BoolProperty(
-        name="Combine unused",
-        default=False,
-        description="Combine the generated geometry with unused faces",
-        update = anim_tessellate_active
-        )
-    bool_advanced : BoolProperty(
-        name="Advanced Settings",
-        default=False,
-        description="Show more settings"
-        )
-    normals_mode : EnumProperty(
-        items=(
-            ('VERTS', 'Normals', 'Consistent direction based on vertices normal'),
-            ('FACES', 'Individual Faces', 'Based on individual faces normal'),
-            ('CUSTOM', 'Custom', "According to Base object's shape keys")),
-        default='VERTS',
-        name="Direction",
-        update = anim_tessellate_active
-        )
-    bool_multi_components : BoolProperty(
-        name="Multi Components",
-        default=False,
-        description="Combine different components according to materials name",
-        update = anim_tessellate_active
-        )
-    error_message : StringProperty(
-        name="Error Message",
-        default=""
-        )
-    warning_message : StringProperty(
-        name="Warning Message",
-        default=""
-        )
-    bounds_x : EnumProperty(
-            items=(
-                ('EXTEND', 'Extend', 'Default X coordinates'),
-                ('CLIP', 'Clip', 'Trim out of bounds in X direction'),
-                ('CYCLIC', 'Cyclic', 'Cyclic components in X direction')),
-            default='EXTEND',
-            name="Bounds X",
-            update = anim_tessellate_active
-            )
-    bounds_y : EnumProperty(
-            items=(
-                ('EXTEND', 'Extend', 'Default Y coordinates'),
-                ('CLIP', 'Clip', 'Trim out of bounds in Y direction'),
-                ('CYCLIC', 'Cyclic', 'Cyclic components in Y direction')),
-            default='EXTEND',
-            name="Bounds Y",
-            update = anim_tessellate_active
-            )
-    close_mesh : EnumProperty(
-            items=(
-                ('NONE', 'None', 'Keep the mesh open'),
-                ('CAP', 'Cap Holes', 'Automatically cap open loops'),
-                ('BRIDGE', 'Bridge Loops', 'Automatically bridge loop pairs')),
-            default='NONE',
-            name="Close Mesh",
-            update = anim_tessellate_active
-            )
-    cap_faces : BoolProperty(
-            name="Cap Holes",
-            default=False,
-            description="Cap open edges loops",
-            update = anim_tessellate_active
-            )
-    frame_boundary : BoolProperty(
-            name="Frame Boundary",
-            default=False,
-            description="Support face boundaries",
-            update = anim_tessellate_active
-            )
-    fill_frame : BoolProperty(
-            name="Fill Frame",
-            default=False,
-            description="Fill inner faces with Fan tessellation",
-            update = anim_tessellate_active
-            )
-    frame_boundary_mat : IntProperty(
-            name="Material Offset",
-            default=0,
-            description="Material Offset for boundaries",
-            update = anim_tessellate_active
-            )
-    fill_frame_mat : IntProperty(
-            name="Material Offset",
-            default=0,
-            description="Material Offset for inner faces",
-            update = anim_tessellate_active
-            )
-    open_edges_crease : FloatProperty(
-            name="Open Edges Crease",
-            default=0,
-            min=0,
-            max=1,
-            description="Automatically set crease for open edges",
-            update = anim_tessellate_active
-            )
-    bridge_smoothness : FloatProperty(
-            name="Smoothness",
-            default=1,
-            min=0,
-            max=1,
-            description="Bridge Smoothness",
-            update = anim_tessellate_active
-            )
-    frame_thickness : FloatProperty(
-            name="Frame Thickness",
-            default=0.2,
-            min=0,
-            soft_max=2,
-            description="Frame Thickness",
-            update = anim_tessellate_active
-            )
-    frame_mode : EnumProperty(
-            items=(
-                ('CONSTANT', 'Constant', 'Even thickness'),
-                ('RELATIVE', 'Relative', 'Frame offset depends on face areas')),
-            default='CONSTANT',
-            name="Offset",
-            update = anim_tessellate_active
-            )
-    bridge_cuts : IntProperty(
-            name="Cuts",
-            default=0,
-            min=0,
-            max=20,
-            description="Bridge Cuts",
-            update = anim_tessellate_active
-            )
-    cap_material_index : IntProperty(
-            name="Material",
-            default=0,
-            min=0,
-            description="Material index for the cap/bridge faces",
-            update = anim_tessellate_active
-            )
-    patch_subs : IntProperty(
-            name="Patch Subdivisions",
-            default=1,
-            min=0,
-            description="Subdivisions levels for Patch tessellation after the first iteration",
-            update = anim_tessellate_active
-            )
-
-def store_parameters(operator, ob):
-    ob.tissue_tessellate.bool_hold = True
-    ob.tissue_tessellate.bool_lock = operator.bool_lock
-    ob.tissue_tessellate.bool_dependencies = operator.bool_dependencies
-    ob.tissue_tessellate.generator = bpy.data.objects[operator.generator]
-    ob.tissue_tessellate.component = bpy.data.objects[operator.component]
-    ob.tissue_tessellate.zscale = operator.zscale
-    ob.tissue_tessellate.offset = operator.offset
-    ob.tissue_tessellate.gen_modifiers = operator.gen_modifiers
-    ob.tissue_tessellate.com_modifiers = operator.com_modifiers
-    ob.tissue_tessellate.mode = operator.mode
-    ob.tissue_tessellate.rotation_mode = operator.rotation_mode
-    ob.tissue_tessellate.rotation_shift = operator.rotation_shift
-    ob.tissue_tessellate.rotation_direction = operator.rotation_direction
-    ob.tissue_tessellate.merge = operator.merge
-    ob.tissue_tessellate.merge_thres = operator.merge_thres
-    ob.tissue_tessellate.scale_mode = operator.scale_mode
-    ob.tissue_tessellate.bool_random = operator.bool_random
-    ob.tissue_tessellate.random_seed = operator.random_seed
-    ob.tissue_tessellate.fill_mode = operator.fill_mode
-    ob.tissue_tessellate.bool_vertex_group = operator.bool_vertex_group
-    ob.tissue_tessellate.bool_selection = operator.bool_selection
-    ob.tissue_tessellate.bool_shapekeys = operator.bool_shapekeys
-    ob.tissue_tessellate.bool_smooth = operator.bool_smooth
-    ob.tissue_tessellate.bool_materials = operator.bool_materials
-    ob.tissue_tessellate.bool_material_id = operator.bool_material_id
-    ob.tissue_tessellate.material_id = operator.material_id
-    ob.tissue_tessellate.bool_dissolve_seams = operator.bool_dissolve_seams
-    ob.tissue_tessellate.iterations = operator.iterations
-    ob.tissue_tessellate.bool_advanced = operator.bool_advanced
-    ob.tissue_tessellate.normals_mode = operator.normals_mode
-    ob.tissue_tessellate.bool_combine = operator.bool_combine
-    ob.tissue_tessellate.bool_multi_components = operator.bool_multi_components
-    ob.tissue_tessellate.combine_mode = operator.combine_mode
-    ob.tissue_tessellate.bounds_x = operator.bounds_x
-    ob.tissue_tessellate.bounds_y = operator.bounds_y
-    ob.tissue_tessellate.cap_faces = operator.cap_faces
-    ob.tissue_tessellate.close_mesh = operator.close_mesh
-    ob.tissue_tessellate.bridge_cuts = operator.bridge_cuts
-    ob.tissue_tessellate.bridge_smoothness = operator.bridge_smoothness
-    ob.tissue_tessellate.frame_thickness = operator.frame_thickness
-    ob.tissue_tessellate.frame_mode = operator.frame_mode
-    ob.tissue_tessellate.frame_boundary = operator.frame_boundary
-    ob.tissue_tessellate.fill_frame = operator.fill_frame
-    ob.tissue_tessellate.frame_boundary_mat = operator.frame_boundary_mat
-    ob.tissue_tessellate.fill_frame_mat = operator.fill_frame_mat
-    ob.tissue_tessellate.cap_material_index = operator.cap_material_index
-    ob.tissue_tessellate.patch_subs = operator.patch_subs
-    ob.tissue_tessellate.bool_hold = False
-    return ob
-
-def load_parameters(operator, ob):
-    operator.bool_lock = ob.tissue_tessellate.bool_lock
-    operator.bool_dependencies = ob.tissue_tessellate.bool_dependencies
-    operator.generator = ob.tissue_tessellate.generator.name
-    operator.component = ob.tissue_tessellate.component.name
-    operator.zscale = ob.tissue_tessellate.zscale
-    operator.offset = ob.tissue_tessellate.offset
-    operator.gen_modifiers = ob.tissue_tessellate.gen_modifiers
-    operator.com_modifiers = ob.tissue_tessellate.com_modifiers
-    operator.mode = ob.tissue_tessellate.mode
-    operator.rotation_mode = ob.tissue_tessellate.rotation_mode
-    operator.rotation_shift = ob.tissue_tessellate.rotation_shift
-    operator.rotation_direction = ob.tissue_tessellate.rotation_direction
-    operator.merge = ob.tissue_tessellate.merge
-    operator.merge_thres = ob.tissue_tessellate.merge_thres
-    operator.scale_mode = ob.tissue_tessellate.scale_mode
-    operator.bool_random = ob.tissue_tessellate.bool_random
-    operator.random_seed = ob.tissue_tessellate.random_seed
-    operator.fill_mode = ob.tissue_tessellate.fill_mode
-    operator.bool_vertex_group = ob.tissue_tessellate.bool_vertex_group
-    operator.bool_selection = ob.tissue_tessellate.bool_selection
-    operator.bool_shapekeys = ob.tissue_tessellate.bool_shapekeys
-    operator.bool_smooth = ob.tissue_tessellate.bool_smooth
-    operator.bool_materials = ob.tissue_tessellate.bool_materials
-    operator.bool_material_id = ob.tissue_tessellate.bool_material_id
-    operator.material_id = ob.tissue_tessellate.material_id
-    operator.bool_dissolve_seams = ob.tissue_tessellate.bool_dissolve_seams
-    operator.iterations = ob.tissue_tessellate.iterations
-    operator.bool_advanced = ob.tissue_tessellate.bool_advanced
-    operator.normals_mode = ob.tissue_tessellate.normals_mode
-    operator.bool_combine = ob.tissue_tessellate.bool_combine
-    operator.bool_multi_components = ob.tissue_tessellate.bool_multi_components
-    operator.combine_mode = ob.tissue_tessellate.combine_mode
-    operator.bounds_x = ob.tissue_tessellate.bounds_x
-    operator.bounds_y = ob.tissue_tessellate.bounds_y
-    operator.cap_faces = ob.tissue_tessellate.cap_faces
-    operator.close_mesh = ob.tissue_tessellate.close_mesh
-    operator.bridge_cuts = ob.tissue_tessellate.bridge_cuts
-    operator.bridge_smoothness = ob.tissue_tessellate.bridge_smoothness
-    operator.cap_material_index = ob.tissue_tessellate.cap_material_index
-    operator.patch_subs = ob.tissue_tessellate.patch_subs
-    operator.frame_boundary = ob.tissue_tessellate.frame_boundary
-    operator.fill_frame = ob.tissue_tessellate.fill_frame
-    operator.frame_boundary_mat = ob.tissue_tessellate.frame_boundary_mat
-    operator.fill_frame_mat = ob.tissue_tessellate.fill_frame_mat
-    operator.frame_thickness = ob.tissue_tessellate.frame_thickness
-    operator.frame_mode = ob.tissue_tessellate.frame_mode
-    return ob
-
-def tessellate_patch(_ob0, _ob1, offset, zscale, com_modifiers, mode,
-               scale_mode, rotation_mode, rotation_shift, rand_seed, bool_vertex_group,
-               bool_selection, bool_shapekeys, bool_material_id, material_id,
-               normals_mode, bounds_x, bounds_y):
-    random.seed(rand_seed)
-
-    if normals_mode == 'CUSTOM':
-        if _ob0.data.shape_keys != None:
-            ob0_sk = convert_object_to_mesh(_ob0)
-            me0_sk = ob0_sk.data
-            key_values0 = [sk.value for sk in _ob0.data.shape_keys.key_blocks]
-            for sk in _ob0.data.shape_keys.key_blocks: sk.value = 0
-        else: normals_mode = 'VERTS'
-
-    ob0 = convert_object_to_mesh(_ob0)
-    me0 = ob0.data
-
-    # base normals
-    normals0 = []
-    if normals_mode == 'CUSTOM':
-        for sk, val in zip(_ob0.data.shape_keys.key_blocks, key_values0): sk.value = val
-        for v0, v1 in zip(ob0.data.vertices, me0_sk.vertices):
-            normals0.append(v1.co - v0.co)
-        bpy.data.objects.remove(ob0_sk)
-    else:
-        ob0.data.update()
-        normals0 = [v.normal for v in ob0.data.vertices]
-
-#    ob0 = convert_object_to_mesh(_ob0)
-    ob0.name = _ob0.name + "_apply_mod"
-    me0 = _ob0.data
-
-    # Check if zero faces are selected
-    if _ob0.type == 'MESH':
-        bool_cancel = True
-        for p in me0.polygons:
-            check_sel = check_mat = False
-            if not bool_selection or p.select: check_sel = True
-            if not bool_material_id or p.material_index == material_id: check_mat = True
-            if check_sel and check_mat:
-                    bool_cancel = False
-                    break
-        if bool_cancel:
-            bpy.data.meshes.remove(ob0.data)
-            #bpy.data.objects.remove(ob0)
-            return 0
-
-    levels = 0
-    sculpt_levels = 0
-    render_levels = 0
-    bool_multires = False
-    multires_name = ""
-    not_allowed  = ['FLUID_SIMULATION', 'ARRAY', 'BEVEL', 'BOOLEAN', 'BUILD',
-                    'DECIMATE', 'EDGE_SPLIT', 'MASK', 'MIRROR', 'REMESH',
-                    'SCREW', 'SOLIDIFY', 'TRIANGULATE', 'WIREFRAME', 'SKIN',
-                    'EXPLODE', 'PARTICLE_INSTANCE', 'PARTICLE_SYSTEM', 'SMOKE']
-    modifiers0 = list(_ob0.modifiers)#[m for m in ob0.modifiers]
-    show_modifiers = [m.show_viewport for m in _ob0.modifiers]
-    show_modifiers.reverse()
-    modifiers0.reverse()
-    for m in modifiers0:
-        visible = m.show_viewport
-        if not visible: continue
-        #m.show_viewport = False
-        if m.type in ('SUBSURF', 'MULTIRES') and visible:
-            levels = m.levels
-            multires_name = m.name
-            if m.type == 'MULTIRES':
-                bool_multires = True
-                multires_name = m.name
-                sculpt_levels = m.sculpt_levels
-                render_levels = m.render_levels
-            else: bool_multires = False
-            break
-        elif m.type in not_allowed:
-            bpy.data.meshes.remove(ob0.data)
-            #bpy.data.meshes.remove(me0)
-            return "modifiers_error"
-
-    before = _ob0.copy()
-    before.name = _ob0.name + "_before_subs"
-    bpy.context.collection.objects.link(before)
-    #if ob0.type == 'MESH': before.data = me0
-    before_mod = list(before.modifiers)
-    before_mod.reverse()
-    for m in before_mod:
-        if m.type in ('SUBSURF', 'MULTIRES') and m.show_viewport:
-            before.modifiers.remove(m)
-            break
-        else: before.modifiers.remove(m)
-
-    before_subsurf = simple_to_mesh(before)
-
-    before_bm = bmesh.new()
-    before_bm.from_mesh(before_subsurf)
-    before_bm.faces.ensure_lookup_table()
-    before_bm.edges.ensure_lookup_table()
-    before_bm.verts.ensure_lookup_table()
-
-    error = ""
-    for f in before_bm.faces:
-        if len(f.loops) != 4:
-            error = "topology_error"
-            break
-    for e in before_bm.edges:
-        if len(e.link_faces) == 0:
-            error = "wires_error"
-            break
-    for v in before_bm.verts:
-        if len(v.link_faces) == 0:
-            error = "verts_error"
-            break
-    if error != "":
-        bpy.data.meshes.remove(ob0.data)
-        #bpy.data.meshes.remove(me0)
-        bpy.data.meshes.remove(before_subsurf)
-        bpy.data.objects.remove(before)
-        return error
-
-    me0 = ob0.data
-    verts0 = me0.vertices   # Collect generator vertices
-
-    if com_modifiers or _ob1.type != 'MESH': bool_shapekeys = False
-
-    # set Shape Keys to zero
-    if bool_shapekeys or not com_modifiers:
-        try:
-            original_key_values = []
-            for sk in _ob1.data.shape_keys.key_blocks:
-                original_key_values.append(sk.value)
-                sk.value = 0
-        except:
-            bool_shapekeys = False
-
-    if not com_modifiers and not bool_shapekeys:
-        mod_visibility = []
-        for m in _ob1.modifiers:
-            mod_visibility.append(m.show_viewport)
-            m.show_viewport = False
-        com_modifiers = True
-
-    ob1 = convert_object_to_mesh(_ob1, com_modifiers, False)
-    me1 = ob1.data
-
-    if mode != 'BOUNDS':
-        ob1.active_shape_key_index = 0
-        # Bound X
-        if bounds_x != 'EXTEND':
-            if mode == 'GLOBAL':
-                planes_co = ((0,0,0),(1,1,1))
-                plane_no = (1,0,0)
-            if mode == 'LOCAL':
-                planes_co = (ob1.matrix_world @ Vector((0,0,0)), ob1.matrix_world @ Vector((1,0,0)))
-                plane_no = planes_co[0]-planes_co[1]
-            bpy.ops.object.mode_set(mode='EDIT')
-            for co in planes_co:
-                bpy.ops.mesh.select_all(action='SELECT')
-                bpy.ops.mesh.bisect(plane_co=co, plane_no=plane_no)
-                bpy.ops.mesh.mark_seam()
-            bpy.ops.object.mode_set(mode='OBJECT')
-            _faces = ob1.data.polygons
-            if mode == 'GLOBAL':
-                for f in [f for f in _faces if (ob1.matrix_world @ f.center).x > 1]:
-                    f.select = True
-                for f in [f for f in _faces if (ob1.matrix_world @ f.center).x < 0]:
-                    f.select = True
-            else:
-                for f in [f for f in _faces if f.center.x > 1]:
-                    f.select = True
-                for f in [f for f in _faces if f.center.x < 0]:
-                    f.select = True
-            bpy.ops.object.mode_set(mode='EDIT')
-            bpy.ops.mesh.select_mode(type='FACE')
-            if bounds_x == 'CLIP':
-                bpy.ops.mesh.delete(type='FACE')
-                bpy.ops.object.mode_set(mode='OBJECT')
-            if bounds_x == 'CYCLIC':
-                bpy.ops.mesh.split()
-                bpy.ops.object.mode_set(mode='OBJECT')
-        # Bound Y
-        if bounds_y != 'EXTEND':
-            if mode == 'GLOBAL':
-                planes_co = ((0,0,0),(1,1,1))
-                plane_no = (0,1,0)
-            if mode == 'LOCAL':
-                planes_co = (ob1.matrix_world @ Vector((0,0,0)), ob1.matrix_world @ Vector((0,1,0)))
-                plane_no = planes_co[0]-planes_co[1]
-            bpy.ops.object.mode_set(mode='EDIT')
-            for co in planes_co:
-                bpy.ops.mesh.select_all(action='SELECT')
-                bpy.ops.mesh.bisect(plane_co=co, plane_no=plane_no)
-                bpy.ops.mesh.mark_seam()
-            bpy.ops.object.mode_set(mode='OBJECT')
-            _faces = ob1.data.polygons
-            if mode == 'GLOBAL':
-                for f in [f for f in _faces if (ob1.matrix_world @ f.center).y > 1]:
-                    f.select = True
-                for f in [f for f in _faces if (ob1.matrix_world @ f.center).y < 0]:
-                    f.select = True
-            else:
-                for f in [f for f in _faces if f.center.y > 1]:
-                    f.select = True
-                for f in [f for f in _faces if f.center.y < 0]:
-                    f.select = True
-
-            bpy.ops.object.mode_set(mode='EDIT')
-            bpy.ops.mesh.select_mode(type='FACE')
-            if bounds_y == 'CLIP':
-                bpy.ops.mesh.delete(type='FACE')
-                bpy.ops.object.mode_set(mode='OBJECT')
-            if bounds_y == 'CYCLIC':
-                bpy.ops.mesh.split()
-                bpy.ops.object.mode_set(mode='OBJECT')
-            bpy.ops.object.mode_set(mode='OBJECT')
-
-    # Component statistics
-    n_verts = len(me1.vertices)
-
-    # Create empty lists
-    new_verts = []
-    new_edges = []
-    new_faces = []
-    new_verts_np = np.array(())
-
-    # Component bounding box
-    min_c = Vector((0, 0, 0))
-    max_c = Vector((0, 0, 0))
-    first = True
-    for v in me1.vertices:
-        vert = v.co
-        if vert[0] < min_c[0] or first:
-            min_c[0] = vert[0]
-        if vert[1] < min_c[1] or first:
-            min_c[1] = vert[1]
-        if vert[2] < min_c[2] or first:
-            min_c[2] = vert[2]
-        if vert[0] > max_c[0] or first:
-            max_c[0] = vert[0]
-        if vert[1] > max_c[1] or first:
-            max_c[1] = vert[1]
-        if vert[2] > max_c[2] or first:
-            max_c[2] = vert[2]
-        first = False
-    bb = max_c - min_c
-
-    # adaptive XY
-    verts1 = []
-    for v in me1.vertices:
-        if mode == 'BOUNDS':
-            vert = v.co - min_c  # (ob1.matrix_world * v.co) - min_c
-            vert[0] = vert[0] / bb[0] if bb[0] != 0 else 0.5
-            vert[1] = vert[1] / bb[1] if bb[1] != 0 else 0.5
-            vert[2] = vert[2] / bb[2] if bb[2] != 0 else 0
-            vert[2] = (vert[2] - 0.5 + offset * 0.5) * zscale
-        elif mode == 'LOCAL':
-            vert = v.co.xyz
-            vert[2] *= zscale
-            #vert[2] = (vert[2] - min_c[2] + (-0.5 + offset * 0.5) * bb[2]) * zscale
-        elif mode == 'GLOBAL':
-            vert = ob1.matrix_world @ v.co
-            vert[2] *= zscale
-            try:
-                for sk in me1.shape_keys.key_blocks:
-                    sk.data[v.index].co = ob1.matrix_world @ sk.data[v.index].co
-            except: pass
-        #verts1.append(vert)
-        v.co = vert
-
-    # Bounds X, Y
-    if mode != 'BOUNDS':
-        if bounds_x == 'CYCLIC':
-            move_verts = []
-            for f in [f for f in me1.polygons if (f.center).x > 1]:
-                for v in f.vertices:
-                    if v not in move_verts: move_verts.append(v)
-            for v in move_verts:
-                me1.vertices[v].co.x -= 1
-                try:
-                    _ob1.active_shape_key_index = 0
-                    for sk in me1.shape_keys.key_blocks:
-                        sk.data[v].co.x -= 1
-                except: pass
-            move_verts = []
-            for f in [f for f in me1.polygons if (f.center).x < 0]:
-                for v in f.vertices:
-                    if v not in move_verts: move_verts.append(v)
-            for v in move_verts:
-                me1.vertices[v].co.x += 1
-                try:
-                    _ob1.active_shape_key_index = 0
-                    for sk in me1.shape_keys.key_blocks:
-                        sk.data[v].co.x += 1
-                except: pass
-        if bounds_y == 'CYCLIC':
-            move_verts = []
-            for f in [f for f in me1.polygons if (f.center).y > 1]:
-                for v in f.vertices:
-                    if v not in move_verts: move_verts.append(v)
-            for v in move_verts:
-                me1.vertices[v].co.y -= 1
-                try:
-                    _ob1.active_shape_key_index = 0
-                    for sk in me1.shape_keys.key_blocks:
-                        sk.data[v].co.y -= 1
-                except: pass
-            move_verts = []
-            for f in [f for f in me1.polygons if (f.center).y < 0]:
-                for v in f.vertices:
-                    if v not in move_verts: move_verts.append(v)
-            for v in move_verts:
-                me1.vertices[v].co.y += 1
-                try:
-                    _ob1.active_shape_key_index = 0
-                    for sk in me1.shape_keys.key_blocks:
-                        sk.data[v].co.y += 1
-                except: pass
-    verts1 = [v.co for v in me1.vertices]
-    n_verts1 = len(verts1)
-
-    patch_faces = 4**levels
-    sides = int(sqrt(patch_faces))
-    step = 1/sides
-    sides0 = sides-2
-    patch_faces0 = int((sides-2)**2)
-    n_patches = int(len(me0.polygons)/patch_faces)
-    if len(me0.polygons)%patch_faces != 0:
-        #ob0.data = old_me0
-        return "topology_error"
-
-    new_verts = []
-    new_edges = []
-    new_faces = []
-
-    for o in bpy.context.view_layer.objects: o.select_set(False)
-    new_patch = None
-
-    # All vertex group
-    if bool_vertex_group:
-        try:
-            weight = []
-            for vg in ob0.vertex_groups:
-                _weight = []
-                for v in me0.vertices:
-                    try:
-                        _weight.append(vg.weight(v.index))
-                    except:
-                        _weight.append(0)
-                weight.append(_weight)
-        except:
-            bool_vertex_group = False
-
-    # Adaptive Z
-    if scale_mode == 'ADAPTIVE':
-        com_area = bb[0]*bb[1]
-        if mode != 'BOUNDS' or com_area == 0: com_area = 1
-        #mult = 1/com_area
-        verts_area = []
-        bm = bmesh.new()
-        bm.from_mesh(me0)
-        bm.verts.ensure_lookup_table()
-        for v in bm.verts:
-            area = 0
-            faces = v.link_faces
-            for f in faces:
-                area += f.calc_area()
-            area = area/len(faces)*patch_faces/com_area
-            #area*=mult*
-            verts_area.append(sqrt(area)*bb[2])
-
-    random.seed(rand_seed)
-    bool_correct = False
-
-    _faces = [[[0] for ii in range(sides)] for jj in range(sides)]
-    _verts = [[[0] for ii in range(sides+1)] for jj in range(sides+1)]
-
-    # find relative UV component's vertices
-    verts1_uv_quads = [0]*len(verts1)
-    verts1_uv = [0]*len(verts1)
-    for i, vert in enumerate(verts1):
-        # grid coordinates
-        u = int(vert[0]//step)
-        v = int(vert[1]//step)
-        u1 = min(u+1, sides)
-        v1 = min(v+1, sides)
-        if mode != 'BOUNDS':
-            if u > sides-1:
-                u = sides-1
-                u1 = sides
-            if u < 0:
-                u = 0
-                u1 = 1
-            if v > sides-1:
-                v = sides-1
-                v1 = sides
-            if v < 0:
-                v = 0
-                v1 = 1
-        verts1_uv_quads[i] = (u,v,u1,v1)
-        # factor coordinates
-        fu = (vert[0]-u*step)/step
-        fv = (vert[1]-v*step)/step
-        fw = vert.z
-        # interpolate Z scaling factor
-        verts1_uv[i] = Vector((fu,fv,fw))
-
-    sk_uv_quads = []
-    sk_uv = []
-    if bool_shapekeys:
-        for sk in ob1.data.shape_keys.key_blocks:
-            source = sk.data
-            _sk_uv_quads = [0]*len(verts1)
-            _sk_uv = [0]*len(verts1)
-            for i, sk_v in enumerate(source):
-                if mode == 'BOUNDS':
-                    sk_vert = sk_v.co - min_c
-                    sk_vert[0] = (sk_vert[0] / bb[0] if bb[0] != 0 else 0.5)
-                    sk_vert[1] = (sk_vert[1] / bb[1] if bb[1] != 0 else 0.5)
-                    sk_vert[2] = (sk_vert[2] / bb[2] if bb[2] != 0 else sk_vert[2])
-                    sk_vert[2] = (sk_vert[2] - 0.5 + offset * 0.5) * zscale
-                elif mode == 'LOCAL':
-                    sk_vert = sk_v.co
-                    sk_vert[2] *= zscale
-                elif mode == 'GLOBAL':
-                    sk_vert = sk_v.co
-                    sk_vert[2] *= zscale
-
-                # grid coordinates
-                u = int(sk_vert[0]//step)
-                v = int(sk_vert[1]//step)
-                u1 = min(u+1, sides)
-                v1 = min(v+1, sides)
-                if mode != 'BOUNDS':
-                    if u > sides-1:
-                        u = sides-1
-                        u1 = sides
-                    if u < 0:
-                        u = 0
-                        u1 = 1
-                    if v > sides-1:
-                        v = sides-1
-                        v1 = sides
-                    if v < 0:
-                        v = 0
-                        v1 = 1
-                _sk_uv_quads[i] = (u,v,u1,v1)
-                # factor coordinates
-                fu = (sk_vert[0]-u*step)/step
-                fv = (sk_vert[1]-v*step)/step
-                fw = sk_vert.z
-                _sk_uv[i] = Vector((fu,fv,fw))
-            sk_uv_quads.append(_sk_uv_quads)
-            sk_uv.append(_sk_uv)
-
-    for i in range(n_patches):
-        poly = me0.polygons[i*patch_faces]
-        if bool_selection and not poly.select: continue
-        if bool_material_id and not poly.material_index == material_id: continue
-
-        bool_correct = True
-        new_patch = bpy.data.objects.new("patch", me1.copy())
-        bpy.context.collection.objects.link(new_patch)
-
-        new_patch.select_set(True)
-        bpy.context.view_layer.objects.active = new_patch
-
-        for area in bpy.context.screen.areas:
-            for space in area.spaces:
-                try: new_patch.local_view_set(space, True)
-                except: pass
-
-        # Vertex Group
-        if bool_vertex_group:
-            for vg in ob0.vertex_groups:
-                new_patch.vertex_groups.new(name=vg.name)
-
-        # find patch faces
-        faces = _faces.copy()
-        verts = _verts.copy()
-        shift1 = sides
-        shift2 = sides*2-1
-        shift3 = sides*3-2
-        for j in range(patch_faces):
-            if j < patch_faces0:
-                if levels == 0:
-                    u = j%sides0
-                    v = j//sides0
-                else:
-                    u = j%sides0+1
-                    v = j//sides0+1
-            elif j < patch_faces0 + shift1:
-                u = j-patch_faces0
-                v = 0
-            elif j < patch_faces0 + shift2:
-                u = sides-1
-                v = j-(patch_faces0 + sides)+1
-            elif j < patch_faces0 + shift3:
-                jj = j-(patch_faces0 + shift2)
-                u = sides-jj-2
-                v = sides-1
-            else:
-                jj = j-(patch_faces0 + shift3)
-                u = 0
-                v = sides-jj-2
-            face = me0.polygons[j+i*patch_faces]
-            faces[u][v] = face
-            verts[u][v] = verts0[face.vertices[0]]
-            if u == sides-1:
-                verts[sides][v] = verts0[face.vertices[1]]
-            if v == sides-1:
-                verts[u][sides] = verts0[face.vertices[3]]
-            if u == v == sides-1:
-                verts[sides][sides] = verts0[face.vertices[2]]
-
-        # Random rotation
-        if rotation_mode == 'RANDOM' or rotation_shift != 0:
-            if rotation_mode == 'RANDOM': rot = random.randint(0, 3)
-            else: rot = rotation_shift%4
-            if rot == 1:
-                verts = [[verts[w][k] for w in range(sides+1)] for k in range(sides,-1,-1)]
-            elif rot == 2:
-                verts = [[verts[k][w] for w in range(sides,-1,-1)] for k in range(sides,-1,-1)]
-            elif rot == 3:
-                verts = [[verts[w][k] for w in range(sides,-1,-1)] for k in range(sides+1)]
-
-        # UV rotation
-        if rotation_mode == 'UV' and ob0.type == 'MESH':
-            if len(ob0.data.uv_layers) > 0:
-                uv0 = me0.uv_layers.active.data[faces[0][0].index*4].uv
-                uv1 = me0.uv_layers.active.data[faces[0][-1].index*4 + 3].uv
-                uv2 = me0.uv_layers.active.data[faces[-1][-1].index*4 + 2].uv
-                uv3 = me0.uv_layers.active.data[faces[-1][0].index*4 + 1].uv
-                v01 = (uv0 + uv1)
-                v32 = (uv3 + uv2)
-                v0132 = v32 - v01
-                v0132.normalize()
-                v12 = (uv1 + uv2)
-                v03 = (uv0 + uv3)
-                v1203 = v03 - v12
-                v1203.normalize()
-
-                vertUV = []
-                dot1203 = v1203.x
-                dot0132 = v0132.x
-                if(abs(dot1203) < abs(dot0132)):
-                    if (dot0132 > 0):
-                        pass
-                    else:
-                        verts = [[verts[k][w] for w in range(sides,-1,-1)] for k in range(sides,-1,-1)]
-                else:
-                    if(dot1203 < 0):
-                        verts = [[verts[w][k] for w in range(sides,-1,-1)] for k in range(sides+1)]
-                    else:
-                        verts = [[verts[w][k] for w in range(sides+1)] for k in range(sides,-1,-1)]
-
-        if True:
-            verts_xyz = np.array([[v.co for v in _verts] for _verts in verts])
-            #verts_norm = np.array([[v.normal for v in _verts] for _verts in verts])
-            verts_norm = np.array([[normals0[v.index] for v in _verts] for _verts in verts])
-            if normals_mode == 'FACES':
-                verts_norm = np.mean(verts_norm, axis=(0,1))
-                verts_norm = np.expand_dims(verts_norm, axis=0)
-                verts_norm = np.repeat(verts_norm,len(verts),axis=0)
-                verts_norm = np.expand_dims(verts_norm, axis=0)
-                verts_norm = np.repeat(verts_norm,len(verts),axis=0)
-            np_verts1_uv = np.array(verts1_uv)
-            verts1_uv_quads = np.array(verts1_uv_quads)
-            u = verts1_uv_quads[:,0]
-            v = verts1_uv_quads[:,1]
-            u1 = verts1_uv_quads[:,2]
-            v1 = verts1_uv_quads[:,3]
-            v00 = verts_xyz[u,v]
-            v10 = verts_xyz[u1,v]
-            v01 = verts_xyz[u,v1]
-            v11 = verts_xyz[u1,v1]
-            n00 = verts_norm[u,v]
-            n10 = verts_norm[u1,v]
-            n01 = verts_norm[u,v1]
-            n11 = verts_norm[u1,v1]
-            vx = np_verts1_uv[:,0].reshape((n_verts1,1))
-            vy = np_verts1_uv[:,1].reshape((n_verts1,1))
-            vz = np_verts1_uv[:,2].reshape((n_verts1,1))
-            co2 = np_lerp2(v00,v10,v01,v11,vx,vy)
-            n2 = np_lerp2(n00,n10,n01,n11,vx,vy)
-            if scale_mode == 'ADAPTIVE':
-                areas = np.array([[verts_area[v.index] for v in verts_v] for verts_v in verts])
-                a00 = areas[u,v].reshape((n_verts1,1))
-                a10 = areas[u1,v].reshape((n_verts1,1))
-                a01 = areas[u,v1].reshape((n_verts1,1))
-                a11 = areas[u1,v1].reshape((n_verts1,1))
-                # remapped z scale
-                a2 = np_lerp2(a00,a10,a01,a11,vx,vy)
-                co3 = co2 + n2 * vz * a2
-            else:
-                co3 = co2 + n2 * vz
-            coordinates = co3.flatten().tolist()
-            new_patch.data.vertices.foreach_set('co',coordinates)
-
-            # vertex groups
-            if bool_vertex_group:
-                for _weight, vg in zip(weight, new_patch.vertex_groups):
-                    np_weight = np.array([[_weight[v.index] for v in verts_v] for verts_v in verts])
-                    w00 = np_weight[u,v].reshape((n_verts1,1))
-                    w10 = np_weight[u1,v].reshape((n_verts1,1))
-                    w01 = np_weight[u,v1].reshape((n_verts1,1))
-                    w11 = np_weight[u1,v1].reshape((n_verts1,1))
-                    # remapped z scale
-                    w2 = np_lerp2(w00,w10,w01,w11,vx,vy)
-                    for vert_id in range(n_verts1):
-                        vg.add([vert_id], w2[vert_id], "ADD")
-
-            if bool_shapekeys:
-                for i_sk, sk in enumerate(ob1.data.shape_keys.key_blocks):
-                    np_verts1_uv = np.array(sk_uv[i_sk])
-                    np_sk_uv_quads = np.array(sk_uv_quads[i_sk])
-                    u = np_sk_uv_quads[:,0]
-                    v = np_sk_uv_quads[:,1]
-                    u1 = np_sk_uv_quads[:,2]
-                    v1 = np_sk_uv_quads[:,3]
-                    v00 = verts_xyz[u,v]
-                    v10 = verts_xyz[u1,v]
-                    v01 = verts_xyz[u,v1]
-                    v11 = verts_xyz[u1,v1]
-                    vx = np_verts1_uv[:,0].reshape((n_verts1,1))
-                    vy = np_verts1_uv[:,1].reshape((n_verts1,1))
-                    vz = np_verts1_uv[:,2].reshape((n_verts1,1))
-                    co2 = np_lerp2(v00,v10,v01,v11,vx,vy)
-                    n2 = np_lerp2(n00,n10,n01,n11,vx,vy)
-                    if scale_mode == 'ADAPTIVE':
-                        areas = np.array([[verts_area[v.index] for v in verts_v] for verts_v in verts])
-                        a00 = areas[u,v].reshape((n_verts1,1))
-                        a10 = areas[u1,v].reshape((n_verts1,1))
-                        a01 = areas[u,v1].reshape((n_verts1,1))
-                        a11 = areas[u1,v1].reshape((n_verts1,1))
-                        # remapped z scale
-                        a2 = np_lerp2(a00,a10,a01,a11,vx,vy)
-                        co3 = co2 + n2 * vz * a2
-                    else:
-                        co3 = co2 + n2 * vz
-                    coordinates = co3.flatten().tolist()
-                    new_patch.data.shape_keys.key_blocks[sk.name].data.foreach_set('co', coordinates)
-                    #new_patch.data.shape_keys.key_blocks[sk.name].data[i_vert].co = sk_co
-        else:
-            for _fvec, uv_quad, patch_vert in zip(verts1_uv, verts1_uv_quads, new_patch.data.vertices):
-                u = uv_quad[0]
-                v = uv_quad[1]
-                u1 = uv_quad[2]
-                v1 = uv_quad[3]
-                v00 = verts[u][v]
-                v10 = verts[u1][v]
-                v01 = verts[u][v1]
-                v11 = verts[u1][v1]
-                # interpolate Z scaling factor
-                fvec = _fvec.copy()
-                if scale_mode == 'ADAPTIVE':
-                    a00 = verts_area[v00.index]
-                    a10 = verts_area[v10.index]
-                    a01 = verts_area[v01.index]
-                    a11 = verts_area[v11.index]
-                    fvec[2]*=lerp2(a00,a10,a01,a11,fvec)
-                # interpolate vertex on patch
-                patch_vert.co = lerp3(v00, v10, v01, v11, fvec)
-
-                # Vertex Group
-                if bool_vertex_group:
-                    for _weight, vg in zip(weight, new_patch.vertex_groups):
-                        w00 = _weight[v00.index]
-                        w10 = _weight[v10.index]
-                        w01 = _weight[v01.index]
-                        w11 = _weight[v11.index]
-                        wuv = lerp2(w00,w10,w01,w11, fvec)
-                        vg.add([patch_vert.index], wuv, "ADD")
-
-            if bool_shapekeys:
-                for i_sk, sk in enumerate(ob1.data.shape_keys.key_blocks):
-                    for i_vert, _fvec, _sk_uv_quad in zip(range(len(new_patch.data.vertices)), sk_uv[i_sk], sk_uv_quads[i_sk]):
-                        u = _sk_uv_quad[0]
-                        v = _sk_uv_quad[1]
-                        u1 = _sk_uv_quad[2]
-                        v1 = _sk_uv_quad[3]
-                        v00 = verts[u][v]
-                        v10 = verts[u1][v]
-                        v01 = verts[u][v1]
-                        v11 = verts[u1][v1]
-
-                        fvec = _fvec.copy()
-                        if scale_mode == 'ADAPTIVE':
-                            a00 = verts_area[v00.index]
-                            a10 = verts_area[v10.index]
-                            a01 = verts_area[v01.index]
-                            a11 = verts_area[v11.index]
-                            fvec[2]*=lerp2(a00, a10, a01, a11, fvec)
-                        sk_co = lerp3(v00, v10, v01, v11, fvec)
-
-                        new_patch.data.shape_keys.key_blocks[sk.name].data[i_vert].co = sk_co
-
-    #if ob0.type == 'MESH': ob0.data = old_me0
-    if not bool_correct: return 0
-
-    bpy.ops.object.join()
-
-
-    if bool_shapekeys:
-        # set original values and combine Shape Keys and Vertex Groups
-        for sk, val in zip(_ob1.data.shape_keys.key_blocks, original_key_values):
-            sk.value = val
-            new_patch.data.shape_keys.key_blocks[sk.name].value = val
-        if bool_vertex_group:
-            for sk in new_patch.data.shape_keys.key_blocks:
-                for vg in new_patch.vertex_groups:
-                    if sk.name == vg.name:
-                        sk.vertex_group = vg.name
-    else:
-        try:
-            for sk, val in zip(_ob1.data.shape_keys.key_blocks, original_key_values):
-                sk.value = val
-        except: pass
-
-    new_name = ob0.name + "_" + ob1.name
-    new_patch.name = "tessellate_temp"
-
-    if bool_multires:
-        for m in ob0.modifiers:
-            if m.type == 'MULTIRES' and m.name == multires_name:
-                m.levels = levels
-                m.sculpt_levels = sculpt_levels
-                m.render_levels = render_levels
-    # restore original modifiers visibility for component object
-    try:
-        for m, vis in zip(_ob1.modifiers, mod_visibility):
-            m.show_viewport = vis
-    except: pass
-
-    bpy.data.objects.remove(before)
-    bpy.data.objects.remove(ob0)
-    bpy.data.objects.remove(ob1)
-    return new_patch
-
-def tessellate_original(_ob0, _ob1, offset, zscale, gen_modifiers, com_modifiers, mode,
-               scale_mode, rotation_mode, rotation_shift, rotation_direction, rand_seed, fill_mode,
-               bool_vertex_group, bool_selection, bool_shapekeys,
-               bool_material_id, material_id, normals_mode, bounds_x, bounds_y):
-
-    if com_modifiers or _ob1.type != 'MESH': bool_shapekeys = False
-    random.seed(rand_seed)
-
-    if bool_shapekeys:
-        try:
-            original_key_values = []
-            for sk in _ob1.data.shape_keys.key_blocks:
-                original_key_values.append(sk.value)
-                sk.value = 0
-        except:
-            bool_shapekeys = False
-
-    if normals_mode == 'CUSTOM':
-        if _ob0.data.shape_keys != None:
-            ob0_sk = convert_object_to_mesh(_ob0, True, True)
-            me0_sk = ob0_sk.data
-            key_values0 = [sk.value for sk in _ob0.data.shape_keys.key_blocks]
-            for sk in _ob0.data.shape_keys.key_blocks: sk.value = 0
-        else: normals_mode == 'VERTS'
-
-    ob0 = convert_object_to_mesh(_ob0, gen_modifiers, True)
-    me0 = ob0.data
-    ob1 = convert_object_to_mesh(_ob1, com_modifiers, True)
-    me1 = ob1.data
-
-    # base normals
-    normals0 = []
-    if normals_mode == 'CUSTOM' and _ob0.data.shape_keys != None:
-        for sk, val in zip(_ob0.data.shape_keys.key_blocks, key_values0): sk.value = val
-        for v0, v1 in zip(me0.vertices, me0_sk.vertices):
-            normals0.append(v1.co - v0.co)
-        bpy.data.objects.remove(ob0_sk)
-    else:
-        me0.update()
-        normals0 = [v.normal for v in me0.vertices]
-
-    base_polygons = []
-    base_face_normals = []
-
-    n_faces0 = len(me0.polygons)
-
-    # Check if zero faces are selected
-    if (bool_selection and ob0.type == 'MESH') or bool_material_id:
-        for p in me0.polygons:
-            if (bool_selection and ob0.type == 'MESH'):
-                is_sel = p.select
-            else: is_sel = True
-            if bool_material_id:
-                is_mat = p.material_index == material_id
-            else: is_mat = True
-            if is_sel and is_mat:
-                base_polygons.append(p)
-                base_face_normals.append(p.normal)
-    else:
-        base_polygons = me0.polygons
-        base_face_normals = [p.normal for p in me0.polygons]
-
-        # numpy test: slower
-        #base_face_normals = np.zeros(n_faces0*3)
-        #me0.polygons.foreach_get("normal", base_face_normals)
-        #base_face_normals = base_face_normals.reshape((n_faces0,3))
-
-    if len(base_polygons) == 0:
-        bpy.data.objects.remove(ob0)
-        bpy.data.objects.remove(ob1)
-        bpy.data.meshes.remove(me1)
-        bpy.data.meshes.remove(me0)
-        return 0
-
-    if mode != 'BOUNDS':
-
-        bpy.ops.object.select_all(action='DESELECT')
-        for o in bpy.context.view_layer.objects: o.select_set(False)
-        bpy.context.view_layer.objects.active = ob1
-        ob1.select_set(True)
-        ob1.active_shape_key_index = 0
-        # Bound X
-        if bounds_x != 'EXTEND':
-            if mode == 'GLOBAL':
-                planes_co = ((0,0,0),(1,1,1))
-                plane_no = (1,0,0)
-            if mode == 'LOCAL':
-                planes_co = (ob1.matrix_world @ Vector((0,0,0)), ob1.matrix_world @ Vector((1,0,0)))
-                plane_no = planes_co[0]-planes_co[1]
-            bpy.ops.object.mode_set(mode='EDIT')
-            for co in planes_co:
-                bpy.ops.mesh.select_all(action='SELECT')
-                bpy.ops.mesh.bisect(plane_co=co, plane_no=plane_no)
-                bpy.ops.mesh.mark_seam()
-            bpy.ops.object.mode_set(mode='OBJECT')
-            _faces = ob1.data.polygons
-            if mode == 'GLOBAL':
-                for f in [f for f in _faces if (ob1.matrix_world @ f.center).x > 1]:
-                    f.select = True
-                for f in [f for f in _faces if (ob1.matrix_world @ f.center).x < 0]:
-                    f.select = True
-            else:
-                for f in [f for f in _faces if f.center.x > 1]:
-                    f.select = True
-                for f in [f for f in _faces if f.center.x < 0]:
-                    f.select = True
-            bpy.ops.object.mode_set(mode='EDIT')
-            bpy.ops.mesh.select_mode(type='FACE')
-            if bounds_x == 'CLIP':
-                bpy.ops.mesh.delete(type='FACE')
-                bpy.ops.object.mode_set(mode='OBJECT')
-            if bounds_x == 'CYCLIC':
-                bpy.ops.mesh.split()
-                bpy.ops.object.mode_set(mode='OBJECT')
-        # Bound Y
-        if bounds_y != 'EXTEND':
-            if mode == 'GLOBAL':
-                planes_co = ((0,0,0),(1,1,1))
-                plane_no = (0,1,0)
-            if mode == 'LOCAL':
-                planes_co = (ob1.matrix_world @ Vector((0,0,0)), ob1.matrix_world @ Vector((0,1,0)))
-                plane_no = planes_co[0]-planes_co[1]
-            bpy.ops.object.mode_set(mode='EDIT')
-            for co in planes_co:
-                bpy.ops.mesh.select_all(action='SELECT')
-                bpy.ops.mesh.bisect(plane_co=co, plane_no=plane_no)
-                bpy.ops.mesh.mark_seam()
-            bpy.ops.object.mode_set(mode='OBJECT')
-            _faces = ob1.data.polygons
-            if mode == 'GLOBAL':
-                for f in [f for f in _faces if (ob1.matrix_world @ f.center).y > 1]:
-                    f.select = True
-                for f in [f for f in _faces if (ob1.matrix_world @ f.center).y < 0]:
-                    f.select = True
-            else:
-                for f in [f for f in _faces if f.center.y > 1]:
-                    f.select = True
-                for f in [f for f in _faces if f.center.y < 0]:
-                    f.select = True
-
-            bpy.ops.object.mode_set(mode='EDIT')
-            bpy.ops.mesh.select_mode(type='FACE')
-            if bounds_y == 'CLIP':
-                bpy.ops.mesh.delete(type='FACE')
-                bpy.ops.object.mode_set(mode='OBJECT')
-            if bounds_y == 'CYCLIC':
-                bpy.ops.mesh.split()
-                bpy.ops.object.mode_set(mode='OBJECT')
-            bpy.ops.object.mode_set(mode='OBJECT')
-        #ob1 = new_ob1
-
-        me1 = ob1.data
-
-    verts0 = me0.vertices   # Collect generator vertices
-
-    # Component statistics
-    n_verts1 = len(me1.vertices)
-    n_edges1 = len(me1.edges)
-    n_faces1 = len(me1.polygons)
-
-    # Create empty lists
-    new_verts = []
-    new_edges = []
-    new_faces = []
-    new_verts_np = np.array(())
-
-    # Component Coordinates
-    co1 = [0]*n_verts1*3
-
-    if mode == 'GLOBAL':
-        for v in me1.vertices:
-            v.co = ob1.matrix_world @ v.co
-            try:
-                for sk in me1.shape_keys.key_blocks:
-                    sk.data[v.index].co = ob1.matrix_world @ sk.data[v.index].co
-            except: pass
-    if mode != 'BOUNDS':
-        if bounds_x == 'CYCLIC':
-            move_verts = []
-            for f in [f for f in me1.polygons if (f.center).x > 1]:
-                for v in f.vertices:
-                    if v not in move_verts: move_verts.append(v)
-            for v in move_verts:
-                me1.vertices[v].co.x -= 1
-                try:
-                    _ob1.active_shape_key_index = 0
-                    for sk in me1.shape_keys.key_blocks:
-                        sk.data[v].co.x -= 1
-                except: pass
-            move_verts = []
-            for f in [f for f in me1.polygons if (f.center).x < 0]:
-                for v in f.vertices:
-                    if v not in move_verts: move_verts.append(v)
-            for v in move_verts:
-                me1.vertices[v].co.x += 1
-                try:
-                    _ob1.active_shape_key_index = 0
-                    for sk in me1.shape_keys.key_blocks:
-                        sk.data[v].co.x += 1
-                except: pass
-        if bounds_y == 'CYCLIC':
-            move_verts = []
-            for f in [f for f in me1.polygons if (f.center).y > 1]:
-                for v in f.vertices:
-                    if v not in move_verts: move_verts.append(v)
-            for v in move_verts:
-                me1.vertices[v].co.y -= 1
-                try:
-                    #new_ob1.active_shape_key_index = 0
-                    for sk in me1.shape_keys.key_blocks:
-                        sk.data[v].co.y -= 1
-                except: pass
-            move_verts = []
-            for f in [f for f in me1.polygons if (f.center).y < 0]:
-                for v in f.vertices:
-                    if v not in move_verts: move_verts.append(v)
-            for v in move_verts:
-                me1.vertices[v].co.y += 1
-                try:
-                    #new_ob1.active_shape_key_index = 0
-                    for sk in me1.shape_keys.key_blocks:
-                        sk.data[v].co.y += 1
-                except: pass
-    if len(me1.vertices) == 0:
-        bpy.data.objects.remove(ob0)
-        bpy.data.objects.remove(ob1)
-        return 0
-
-    me1.vertices.foreach_get("co", co1)
-    co1 = np.array(co1)
-    vx = co1[0::3].reshape((n_verts1,1))
-    vy = co1[1::3].reshape((n_verts1,1))
-    vz = co1[2::3].reshape((n_verts1,1))
-    min_c = Vector((vx.min(), vy.min(), vz.min()))          # Min BB Corner
-    max_c = Vector((vx.max(), vy.max(), vz.max()))          # Max BB Corner
-    bb = max_c - min_c                                      # Bounding Box
-
-    # Component Coordinates
-    if mode == 'BOUNDS':
-        vx = (vx - min_c[0]) / bb[0] if bb[0] != 0 else 0.5
-        vy = (vy - min_c[1]) / bb[1] if bb[1] != 0 else 0.5
-        vz = (vz - min_c[2]) / bb[2] if bb[2] != 0 else 0
-        vz = (vz - 0.5 + offset * 0.5) * zscale
-        #vz = ((vz - min_c[2]) + (-0.5 + offset * 0.5) * bb[2]) * zscale
-    else:
-        vz *= zscale
-
-    # Component polygons
-    fs1 = [[i for i in p.vertices] for p in me1.polygons]
-    new_faces = fs1[:]
-
-    # Component edges
-    es1 = np.array([[i for i in e.vertices] for e in me1.edges])
-    #es1 = [[i for i in e.vertices] for e in me1.edges if e.is_loose]
-    new_edges = es1[:]
-
-    # SHAPE KEYS
-    if bool_shapekeys:
-        basis = True #com_modifiers
-        vx_key = []
-        vy_key = []
-        vz_key = []
-        sk_np = []
-        for sk in ob1.data.shape_keys.key_blocks:
-            do_shapekeys = True
-            # set all keys to 0
-            for _sk in ob1.data.shape_keys.key_blocks: _sk.value = 0
-            sk.value = 1
-
-            if basis:
-                basis = False
-                continue
-
-            # Apply component modifiers
-            if com_modifiers:
-                sk_ob = convert_object_to_mesh(_ob1)
-                sk_data = sk_ob.data
-                source = sk_data.vertices
-            else:
-                source = sk.data
-
-            shapekeys = []
-            for v in source:
-                if mode == 'BOUNDS':
-                    vert = v.co - min_c
-                    vert[0] = (vert[0] / bb[0] if bb[0] != 0 else 0.5)
-                    vert[1] = (vert[1] / bb[1] if bb[1] != 0 else 0.5)
-                    vert[2] = (vert[2] / bb[2] if bb[2] != 0 else vert[2])
-                    vert[2] = (vert[2] - 0.5 + offset * 0.5) * zscale
-                elif mode == 'LOCAL':
-                    vert = v.co.xyz
-                    vert[2] *= zscale
-                    #vert[2] = (vert[2] - min_c[2] + (-0.5 + offset * 0.5) * bb[2]) * \
-                    #          zscale
-                elif mode == 'GLOBAL':
-                    vert = v.co.xyz
-                    #vert = ob1.matrix_world @ v.co
-                    vert[2] *= zscale
-                shapekeys.append(vert)
-
-            # Component vertices
-            key1 = np.array([v for v in shapekeys]).reshape(len(shapekeys), 3, 1)
-            vx_key.append(key1[:, 0])
-            vy_key.append(key1[:, 1])
-            vz_key.append(key1[:, 2])
-            #sk_np.append([])
-
-    # All vertex group
-    if bool_vertex_group or rotation_mode == 'WEIGHT':
-        try:
-            weight = []
-            for vg in ob0.vertex_groups:
-                _weight = []
-                for i,v in enumerate(me0.vertices):
-                    try:
-                        _weight.append(vg.weight(i))
-                    except:
-                        _weight.append(0)
-                weight.append(_weight)
-        except:
-            bool_vertex_group = False
-
-    # Adaptive Z
-    if scale_mode == 'ADAPTIVE':
-        com_area = bb[0]*bb[1]
-        if mode != 'BOUNDS' or com_area == 0: com_area = 1
-        verts_area = []
-        bm = bmesh.new()
-        bm.from_mesh(me0)
-        bm.verts.ensure_lookup_table()
-        for v in bm.verts:
-            area = 0
-            faces = v.link_faces
-            for f in faces:
-                area += f.calc_area()
-            try:
-                area/=len(faces) # average area
-                area/=com_area
-                verts_area.append(sqrt(area)*bb[2])
-                #verts_area.append(area)
-            except:
-                verts_area.append(1)
-
-    count = 0   # necessary for UV calculation
-
-    # TESSELLATION
-    j = 0
-    jj = -1
-    bool_correct = False
-
-    # optimization test
-    n_faces = len(base_polygons)
-    _vs0 = [0]*n_faces
-    _nvs0 = [0]*n_faces
-    _sz = [0]*n_faces
-    n_vg = len(ob0.vertex_groups)
-    _w0 = [[0]*n_faces for i in range(n_vg)]
-    np_faces = [np.array(p) for p in fs1]
-    new_faces = [0]*n_faces*n_faces1
-    face1_count = 0
-
-    for j, p in enumerate(base_polygons):
-
-        bool_correct = True
-        if rotation_mode in ['UV', 'WEIGHT'] and ob0.type != 'MESH':
-            rotation_mode = 'DEFAULT'
-
-        ordered = p.vertices
-
-        # Random rotation
-        if rotation_mode == 'RANDOM':
-            shifted_vertices = []
-            n_poly_verts = len(p.vertices)
-            rand = random.randint(0, n_poly_verts)
-            for i in range(n_poly_verts):
-                shifted_vertices.append(p.vertices[(i + rand) % n_poly_verts])
-            if scale_mode == 'ADAPTIVE':
-                verts_area0 = np.array([verts_area[i] for i in shifted_vertices])
-            ordered = shifted_vertices
-
-        # UV rotation
-        elif rotation_mode == 'UV':
-            if len(ob0.data.uv_layers) > 0:
-                i = p.index
-                if bool_material_id:
-                    count = sum([len(p.vertices) for p in me0.polygons[:i]])
-                    #if i == 0: count = 0
-                v01 = (me0.uv_layers.active.data[count].uv +
-                       me0.uv_layers.active.data[count + 1].uv)
-                if len(p.vertices) > 3:
-                    v32 = (me0.uv_layers.active.data[count + 3].uv +
-                           me0.uv_layers.active.data[count + 2].uv)
-                else:
-                    v32 = (me0.uv_layers.active.data[count].uv +
-                           me0.uv_layers.active.data[count + 2].uv)
-                v0132 = v32 - v01
-                v0132.normalize()
-
-                v12 = (me0.uv_layers.active.data[count + 1].uv +
-                       me0.uv_layers.active.data[count + 2].uv)
-                if len(p.vertices) > 3:
-                    v03 = (me0.uv_layers.active.data[count].uv +
-                           me0.uv_layers.active.data[count + 3].uv)
-                else:
-                    v03 = (me0.uv_layers.active.data[count].uv +
-                           me0.uv_layers.active.data[count].uv)
-                v1203 = v03 - v12
-                v1203.normalize()
-
-                vertUV = []
-                dot1203 = v1203.x
-                dot0132 = v0132.x
-                if(abs(dot1203) < abs(dot0132)):
-                    if (dot0132 > 0):
-                        vertUV = p.vertices[1:] + p.vertices[:1]
-                    else:
-                        vertUV = p.vertices[3:] + p.vertices[:3]
-                else:
-                    if(dot1203 < 0):
-                        vertUV = p.vertices[:]
-                    else:
-                        vertUV = p.vertices[2:] + p.vertices[:2]
-                ordered = vertUV
-                count += len(p.vertices)
-
-        # Weight Rotation
-        elif rotation_mode == 'WEIGHT':
-            if len(weight) > 0:
-                active_weight = weight[ob0.vertex_groups.active_index]
-                i = p.index
-                face_weights = [active_weight[v] for v in p.vertices]
-                face_weights*=2
-                if rotation_direction == 'DIAG':
-                    differential = [face_weights[ii]-face_weights[ii+2] for ii in range(4)]
-                else:
-                    differential = [face_weights[ii]+face_weights[ii+1]-face_weights[ii+2]- face_weights[ii+3] for ii in range(4)]
-                starting = differential.index(max(differential))
-
-                ordered = p.vertices[starting:] + p.vertices[:starting]
-
-        if rotation_mode != 'RANDOM':
-            ordered = np.roll(np.array(ordered),rotation_shift)
-        ordered = np.array((ordered[0], ordered[1], ordered[2], ordered[-1]))
-
-        # assign vertices and values
-        vs0 = np.array([verts0[i].co for i in ordered])
-        #nvs0 = np.array([verts0[i].normal for i in ordered])
-        nvs0 = np.array([normals0[i] for i in ordered])
-        if scale_mode == 'ADAPTIVE':
-            np_verts_area = np.array([verts_area[i] for i in ordered])
-            _sz[j] = np_verts_area
-        # Vertex weight
-        if bool_vertex_group:
-            ws0 = []
-            for w in weight:
-                _ws0 = []
-                for i in ordered:
-                    try:
-                        _ws0.append(w[i])
-                    except:
-                        _ws0.append(0)
-                ws0.append(np.array(_ws0))
-
-        # optimization test
-        _vs0[j] = (vs0[0], vs0[1], vs0[2], vs0[-1])
-        if normals_mode != 'FACES':
-            _nvs0[j] = (nvs0[0], nvs0[1], nvs0[2], nvs0[-1])
-
-        if bool_vertex_group:
-            for i_vg, ws0_face in enumerate(ws0):
-                _w0[i_vg][j] = (ws0_face[0], ws0_face[1], ws0_face[2], ws0_face[-1])
-
-        for p in fs1:
-            new_faces[face1_count] = [i + n_verts1 * j for i in p]
-            face1_count += 1
-
-    # build edges list
-    n_edges1 = new_edges.shape[0]
-    new_edges = new_edges.reshape((1, n_edges1, 2))
-    new_edges = new_edges.repeat(n_faces,axis=0)
-    new_edges = new_edges.reshape((n_edges1*n_faces, 2))
-    increment = np.arange(n_faces)*n_verts1
-    increment = increment.repeat(n_edges1, axis=0)
-    increment = increment.reshape((n_faces*n_edges1,1))
-    new_edges = new_edges + increment
-
-    # optimization test
-    _vs0 = np.array(_vs0)
-    _sz = np.array(_sz)
-
-    _vs0_0 = _vs0[:,0].reshape((n_faces,1,3))
-    _vs0_1 = _vs0[:,1].reshape((n_faces,1,3))
-    _vs0_2 = _vs0[:,2].reshape((n_faces,1,3))
-    _vs0_3 = _vs0[:,3].reshape((n_faces,1,3))
-
-    # remapped vertex coordinates
-    v2 = np_lerp2(_vs0_0, _vs0_1, _vs0_3, _vs0_2, vx, vy)
-
-    # remapped vertex normal
-    if normals_mode != 'FACES':
-        _nvs0 = np.array(_nvs0)
-        _nvs0_0 = _nvs0[:,0].reshape((n_faces,1,3))
-        _nvs0_1 = _nvs0[:,1].reshape((n_faces,1,3))
-        _nvs0_2 = _nvs0[:,2].reshape((n_faces,1,3))
-        _nvs0_3 = _nvs0[:,3].reshape((n_faces,1,3))
-        nv2 = np_lerp2(_nvs0_0, _nvs0_1, _nvs0_3, _nvs0_2, vx, vy)
-    else:
-        nv2 = np.array(base_face_normals).reshape((n_faces,1,3))
-
-    # interpolate vertex groups
-    if bool_vertex_group:
-        w = np.array(_w0)
-        w_0 = w[:,:,0].reshape((n_vg, n_faces,1,1))
-        w_1 = w[:,:,1].reshape((n_vg, n_faces,1,1))
-        w_2 = w[:,:,2].reshape((n_vg, n_faces,1,1))
-        w_3 = w[:,:,3].reshape((n_vg, n_faces,1,1))
-        # remapped weight
-        w = np_lerp2(w_0, w_1, w_3, w_2, vx, vy)
-        w = w.reshape((n_vg, n_faces*n_verts1))
-
-    if scale_mode == 'ADAPTIVE':
-        _sz_0 = _sz[:,0].reshape((n_faces,1,1))
-        _sz_1 = _sz[:,1].reshape((n_faces,1,1))
-        _sz_2 = _sz[:,2].reshape((n_faces,1,1))
-        _sz_3 = _sz[:,3].reshape((n_faces,1,1))
-        # remapped z scale
-        sz2 = np_lerp2(_sz_0, _sz_1, _sz_3, _sz_2, vx, vy)
-        v3 = v2 + nv2 * vz * sz2
-    else:
-        v3 = v2 + nv2 * vz
-
-    new_verts_np = v3.reshape((n_faces*n_verts1,3))
-
-    if bool_shapekeys:
-        n_sk = len(vx_key)
-        sk_np = [0]*n_sk
-        for i in range(n_sk):
-            vx = np.array(vx_key[i])
-            vy = np.array(vy_key[i])
-            vz = np.array(vz_key[i])
-
-            # remapped vertex coordinates
-            v2 = np_lerp2(_vs0_0, _vs0_1, _vs0_3, _vs0_2, vx, vy)
-
-            # remapped vertex normal
-            if normals_mode != 'FACES':
-                nv2 = np_lerp2(_nvs0_0, _nvs0_1, _nvs0_3, _nvs0_2, vx, vy)
-            else:
-                nv2 = np.array(base_face_normals).reshape((n_faces,1,3))
-
-            if scale_mode == 'ADAPTIVE':
-                # remapped z scale
-                sz2 = np_lerp2(_sz_0, _sz_1, _sz_3, _sz_2, vx, vy)
-                v3 = v2 + nv2 * vz * sz2
-            else:
-                v3 = v2 + nv2 * vz
-
-            sk_np[i] = v3.reshape((n_faces*n_verts1,3))
-
-    #if ob0.type == 'MESH': ob0.data = old_me0
-
-    if not bool_correct:
-        #bpy.data.objects.remove(ob1)
-        return 0
-
-    new_verts = new_verts_np.tolist()
-    new_name = ob0.name + "_" + ob1.name
-    new_me = bpy.data.meshes.new(new_name)
-    new_me.from_pydata(new_verts, new_edges.tolist(), new_faces)
-    new_me.update(calc_edges=True)
-    new_ob = bpy.data.objects.new("tessellate_temp", new_me)
-
-    # vertex group
-    if bool_vertex_group and False:
-        for vg in ob0.vertex_groups:
-            new_ob.vertex_groups.new(name=vg.name)
-            for i in range(len(vg_np[vg.index])):
-                new_ob.vertex_groups[vg.name].add([i], vg_np[vg.index][i],"ADD")
-    # vertex group
-    if bool_vertex_group:
-        for vg in ob0.vertex_groups:
-            new_ob.vertex_groups.new(name=vg.name)
-            for i, vertex_weight in enumerate(w[vg.index]):
-                new_ob.vertex_groups[vg.name].add([i], vertex_weight,"ADD")
-
-    if bool_shapekeys:
-        basis = com_modifiers
-        sk_count = 0
-        for sk, val in zip(_ob1.data.shape_keys.key_blocks, original_key_values):
-            sk.value = val
-            new_ob.shape_key_add(name=sk.name, from_mix=False)
-            new_ob.data.shape_keys.key_blocks[sk.name].value = val
-            # set shape keys vertices
-            sk_data = new_ob.data.shape_keys.key_blocks[sk.name].data
-            if sk_count == 0:
-                sk_count += 1
-                continue
-            for id in range(len(sk_data)):
-                sk_data[id].co = sk_np[sk_count-1][id]
-            sk_count += 1
-        if bool_vertex_group:
-            for sk in new_ob.data.shape_keys.key_blocks:
-                for vg in new_ob.vertex_groups:
-                    if sk.name == vg.name:
-                        sk.vertex_group = vg.name
-
-    # EDGES SEAMS
-    edge_data = [0]*n_edges1
-    me1.edges.foreach_get("use_seam",edge_data)
-    if any(edge_data):
-        edge_data = edge_data*n_faces
-        new_ob.data.edges.foreach_set("use_seam",edge_data)
-
-    # EDGES SHARP
-    edge_data = [0]*n_edges1
-    me1.edges.foreach_get("use_edge_sharp",edge_data)
-    if any(edge_data):
-        edge_data = edge_data*n_faces
-        new_ob.data.edges.foreach_set("use_edge_sharp",edge_data)
-
-    bpy.ops.object.select_all(action='DESELECT')
-    bpy.context.collection.objects.link(new_ob)
-    new_ob.select_set(True)
-    bpy.context.view_layer.objects.active = new_ob
-
-    # EDGES BEVEL
-    edge_data = [0]*n_edges1
-    me1.edges.foreach_get("bevel_weight",edge_data)
-    if any(edge_data):
-        bpy.ops.object.mode_set(mode='EDIT')
-        bpy.ops.mesh.select_all(action='SELECT')
-        bpy.ops.transform.edge_bevelweight(value=1)
-        bpy.ops.object.mode_set(mode='OBJECT')
-        edge_data = edge_data*n_faces
-        new_ob.data.edges.foreach_set("bevel_weight",edge_data)
-
-    # EDGE CREASES
-    edge_data = [0]*n_edges1
-    me1.edges.foreach_get("crease",edge_data)
-    if any(edge_data):
-        bpy.ops.object.mode_set(mode='EDIT')
-        bpy.ops.mesh.select_all(action='SELECT')
-        bpy.ops.transform.edge_crease(value=1)
-        bpy.ops.object.mode_set(mode='OBJECT')
-        edge_data = edge_data*n_faces
-        new_ob.data.edges.foreach_set('crease', edge_data)
-
-    # MATERIALS
-    for slot in ob1.material_slots: new_ob.data.materials.append(slot.material)
-
-    polygon_materials = [0]*n_faces1
-    me1.polygons.foreach_get("material_index", polygon_materials)
-    polygon_materials *= n_faces
-    new_ob.data.polygons.foreach_set("material_index", polygon_materials)
-    new_ob.data.update() ###
-
-    try:
-        bpy.data.objects.remove(new_ob1)
-    except: pass
-
-    bpy.data.objects.remove(ob0)
-    bpy.data.meshes.remove(me0)
-    bpy.data.objects.remove(ob1)
-    bpy.data.meshes.remove(me1)
-
-    return new_ob
-
-
-class tissue_tessellate(Operator):
-    bl_idname = "object.tissue_tessellate"
-    bl_label = "Tessellate"
-    bl_description = ("Create a copy of selected object on the active object's "
-                      "faces, adapting the shape to the different faces")
-    bl_options = {'REGISTER', 'UNDO'}
-
-
-    bool_hold : BoolProperty(
-            name="Hold",
-            description="Wait...",
-            default=False
-    )
-    bool_lock : BoolProperty(
-            name="Lock",
-            description="Prevent automatic update on settings changes or if other objects have it in the hierarchy.",
-            default=False
-    )
-    bool_dependencies : BoolProperty(
-            name="Update Dependencies",
-            description="Automatically updates base and components as well, if results of other tessellations",
-            default=False
-    )
-    object_name : StringProperty(
-            name="",
-            description="Name of the generated object"
-            )
-    zscale : FloatProperty(
-            name="Scale",
-            default=1,
-            soft_min=0,
-            soft_max=10,
-            description="Scale factor for the component thickness"
-            )
-    scale_mode : EnumProperty(
-            items=(
-                ('CONSTANT', "Constant", "Uniform thickness"),
-                ('ADAPTIVE', "Relative", "Preserve component's proportions")
-                ),
-            default='ADAPTIVE',
-            name="Z-Scale according to faces size"
-            )
-    offset : FloatProperty(
-            name="Surface Offset",
-            default=1,
-            min=-1, max=1,
-            soft_min=-1,
-            soft_max=1,
-            description="Surface offset"
-            )
-    mode : EnumProperty(
-            items=(
-                ('BOUNDS', "Bounds", "The component fits automatically the size of the target face"),
-                ('LOCAL', "Local", "Based on Local coordinates, from 0 to 1"),
-                ('GLOBAL', 'Global', "Based on Global coordinates, from 0 to 1")),
-            default='BOUNDS',
-            name="Component Mode"
-            )
-    rotation_mode : EnumProperty(
-            items=(('RANDOM', "Random", "Random faces rotation"),
-                   ('UV', "Active UV", "Face rotation is based on UV coordinates"),
-                   ('WEIGHT', "Active Weight", "Rotate according to Vertex Group gradient"),
-                   ('DEFAULT', "Default", "Default rotation")),
-            default='DEFAULT',
-            name="Component Rotation"
-            )
-    rotation_direction : EnumProperty(
-            items=(('ORTHO', "Orthogonal", "Component main directions in XY"),
-                   ('DIAG', "Diagonal", "Component main direction aligned with diagonal")),
-            default='ORTHO',
-            name="Direction"
-            )
-    rotation_shift : IntProperty(
-            name="Shift",
-            default=0,
-            soft_min=0,
-            soft_max=3,
-            description="Shift components rotation"
-            )
-    fill_mode : EnumProperty(
-            items=(
-                ('QUAD', 'Quad', 'Regular quad tessellation. Uses only 3 or 4 vertices'),
-                ('FAN', 'Fan', 'Radial tessellation for polygonal faces'),
-                ('PATCH', 'Patch', 'Curved tessellation according to the last ' +
-                'Subsurf\n(or Multires) modifiers. Works only with 4 sides ' +
-                'patches.\nAfter the last Subsurf (or Multires) only ' +
-                'deformation\nmodifiers can be used'),
-                ('FRAME', 'Frame', 'Essellation along the edges of each face')),
-            default='QUAD',
-            name="Fill Mode"
-            )
-    combine_mode : EnumProperty(
-            items=(
-                ('LAST', 'Last', 'Show only the last iteration'),
-                ('UNUSED', 'Unused', 'Combine each iteration with the unused faces of the previous iteration. Used for branching systems'),
-                ('ALL', 'All', 'Combine the result of all iterations')),
-            default='LAST',
-            name="Combine Mode",
-            )
-    gen_modifiers : BoolProperty(
-            name="Generator Modifiers",
-            default=False,
-            description="Apply Modifiers and Shape Keys to the base object"
-            )
-    com_modifiers : BoolProperty(
-            name="Component Modifiers",
-            default=False,
-            description="Apply Modifiers and Shape Keys to the component object"
-            )
-    merge : BoolProperty(
-            name="Merge",
-            default=False,
-            description="Merge vertices in adjacent duplicates"
-            )
-    merge_thres : FloatProperty(
-            name="Distance",
-            default=0.001,
-            soft_min=0,
-            soft_max=10,
-            description="Limit below which to merge vertices"
-            )
-    bool_random : BoolProperty(
-            name="Randomize",
-            default=False,
-            description="Randomize component rotation"
-            )
-    random_seed : IntProperty(
-            name="Seed",
-            default=0,
-            soft_min=0,
-            soft_max=10,
-            description="Random seed"
-            )
-    bool_vertex_group : BoolProperty(
-            name="Map Vertex Groups",
-            default=False,
-            description="Transfer all Vertex Groups from Base object"
-            )
-    bool_selection : BoolProperty(
-            name="On selected Faces",
-            default=False,
-            description="Create Tessellation only on selected faces"
-            )
-    bool_shapekeys : BoolProperty(
-            name="Use Shape Keys",
-            default=False,
-            description="Transfer Component's Shape Keys. If the name of Vertex "
-                        "Groups and Shape Keys are the same, they will be "
-                        "automatically combined"
-            )
-    bool_smooth : BoolProperty(
-            name="Smooth Shading",
-            default=False,
-            description="Output faces with smooth shading rather than flat shaded"
-            )
-    bool_materials : BoolProperty(
-            name="Transfer Materials",
-            default=True,
-            description="Preserve component's materials"
-            )
-    generator : StringProperty(
-            name="",
-            description="Base object for the tessellation",
-            default = ""
-            )
-    component : StringProperty(
-            name="",
-            description="Component object for the tessellation",
-            default = ""
-            )
-    bool_material_id : BoolProperty(
-            name="Tessellation on Material ID",
-            default=False,
-            description="Apply the component only on the selected Material"
-            )
-    bool_dissolve_seams : BoolProperty(
-            name="Dissolve Seams",
-            default=False,
-            description="Dissolve all seam edges"
-            )
-    material_id : IntProperty(
-            name="Material ID",
-            default=0,
-            min=0,
-            description="Material ID"
-            )
-    iterations : IntProperty(
-            name="Iterations",
-            default=1,
-            min=1,
-            soft_max=5,
-            description="Automatically repeat the Tessellation using the "
-                        + "generated geometry as new base object.\nUsefull for "
-                        + "for branching systems. Dangerous!"
-            )
-    bool_combine : BoolProperty(
-            name="Combine unused",
-            default=False,
-            description="Combine the generated geometry with unused faces"
-            )
-    bool_advanced : BoolProperty(
-            name="Advanced Settings",
-            default=False,
-            description="Show more settings"
-            )
-    normals_mode : EnumProperty(
-            items=(
-                ('VERTS', 'Normals', 'Consistent direction based on vertices normal'),
-                ('FACES', 'Individual Faces', 'Based on individual faces normal'),
-                ('CUSTOM', 'Custom', "According to Base object's shape keys")),
-            default='VERTS',
-            name="Direction"
-            )
-    bool_multi_components : BoolProperty(
-            name="Multi Components",
-            default=False,
-            description="Combine different components according to materials name"
-            )
-    bounds_x : EnumProperty(
-            items=(
-                ('EXTEND', 'Extend', 'Default X coordinates'),
-                ('CLIP', 'Clip', 'Trim out of bounds in X direction'),
-                ('CYCLIC', 'Cyclic', 'Cyclic components in X direction')),
-            default='EXTEND',
-            name="Bounds X",
-            )
-    bounds_y : EnumProperty(
-            items=(
-                ('EXTEND', 'Extend', 'Default Y coordinates'),
-                ('CLIP', 'Clip', 'Trim out of bounds in Y direction'),
-                ('CYCLIC', 'Cyclic', 'Cyclic components in Y direction')),
-            default='EXTEND',
-            name="Bounds Y",
-            )
-    close_mesh : EnumProperty(
-            items=(
-                ('NONE', 'None', 'Keep the mesh open'),
-                ('CAP', 'Cap Holes', 'Automatically cap open loops'),
-                ('BRIDGE', 'Bridge Loops', 'Automatically bridge loop pairs')),
-            default='NONE',
-            name="Close Mesh"
-            )
-    cap_faces : BoolProperty(
-            name="Cap Holes",
-            default=False,
-            description="Cap open edges loops"
-            )
-    frame_boundary : BoolProperty(
-            name="Frame Boundary",
-            default=False,
-            description="Support face boundaries"
-            )
-    fill_frame : BoolProperty(
-            name="Fill Frame",
-            default=False,
-            description="Fill inner faces with Fan tessellation"
-            )
-    frame_boundary_mat : IntProperty(
-            name="Material Offset",
-            default=0,
-            description="Material Offset for boundaries"
-            )
-    fill_frame_mat : IntProperty(
-            name="Material Offset",
-            default=0,
-            description="Material Offset for inner faces"
-            )
-    open_edges_crease : FloatProperty(
-            name="Open Edges Crease",
-            default=0,
-            min=0,
-            max=1,
-            description="Automatically set crease for open edges"
-            )
-    bridge_smoothness : FloatProperty(
-            name="Smoothness",
-            default=1,
-            min=0,
-            max=1,
-            description="Bridge Smoothness"
-            )
-    frame_thickness : FloatProperty(
-            name="Frame Thickness",
-            default=0.2,
-            min=0,
-            soft_max=2,
-            description="Frame Thickness"
-            )
-    frame_mode : EnumProperty(
-            items=(
-                ('CONSTANT', 'Constant', 'Even thickness'),
-                ('RELATIVE', 'Relative', 'Frame offset depends on face areas')),
-            default='CONSTANT',
-            name="Offset"
-            )
-    bridge_cuts : IntProperty(
-            name="Cuts",
-            default=0,
-            min=0,
-            max=20,
-            description="Bridge Cuts"
-            )
-    cap_material_index : IntProperty(
-            name="Material",
-            default=0,
-            min=0,
-            description="Material index for the cap/bridge faces"
-            )
-    patch_subs : IntProperty(
-            name="Patch Subdivisions",
-            default=1,
-            min=0,
-            description="Subdivisions levels for Patch tessellation after the first iteration"
-            )
-    working_on = ""
-
-    def draw(self, context):
-        allowed_obj = ('MESH', 'CURVE', 'SURFACE', 'FONT', 'META')
-        '''
-        try:
-            bool_working = self.working_on == self.object_name and \
-            self.working_on != ""
-        except:
-            bool_working = False
-        '''
-
-        bool_working = False
-        bool_allowed = False
-        ob0 = None
-        ob1 = None
-
-        sel = bpy.context.selected_objects
-        if len(sel) == 1:
-            try:
-                ob0 = sel[0].tissue_tessellate.generator
-                ob1 = sel[0].tissue_tessellate.component
-                self.generator = ob0.name
-                self.component = ob1.name
-                if self.working_on == '':
-                    load_parameters(self,sel[0])
-                    self.working_on = sel[0].name
-                bool_working = True
-                bool_allowed = True
-            except:
-                pass
-
-        if len(sel) == 2:
-            bool_allowed = True
-            for o in sel:
-                if o.type not in allowed_obj:
-                    bool_allowed = False
-
-        if len(sel) != 2 and not bool_working:
-            layout = self.layout
-            layout.label(icon='INFO')
-            layout.label(text="Please, select two different objects")
-            layout.label(text="Select first the Component object, then select")
-            layout.label(text="the Base object.")
-        elif not bool_allowed and not bool_working:
-            layout = self.layout
-            layout.label(icon='INFO')
-            layout.label(text="Only Mesh, Curve, Surface or Text objects are allowed")
-        else:
-            if ob0 == ob1 == None:
-                ob0 = bpy.context.active_object
-                self.generator = ob0.name
-                for o in sel:
-                    if o != ob0:
-                        ob1 = o
-                        self.component = o.name
-                        self.no_component = False
-                        break
-
-            # new object name
-            if self.object_name == "":
-                if self.generator == "":
-                    self.object_name = "Tessellation"
-                else:
-                    #self.object_name = self.generator + "_Tessellation"
-                    self.object_name = "Tessellation"
-
-            layout = self.layout
-            # Base and Component
-            col = layout.column(align=True)
-            row = col.row(align=True)
-            row.label(text="BASE : " + self.generator)
-            row.label(text="COMPONENT : " + self.component)
-
-            # Base Modifiers
-            row = col.row(align=True)
-            col2 = row.column(align=True)
-            col2.prop(self, "gen_modifiers", text="Use Modifiers", icon='MODIFIER')
-            base = bpy.data.objects[self.generator]
-            try:
-                if not (base.modifiers or base.data.shape_keys):
-                    col2.enabled = False
-                    self.gen_modifiers = False
-            except:
-                col2.enabled = False
-                self.gen_modifiers = False
-
-            # Component Modifiers
-            row.separator()
-            col3 = row.column(align=True)
-            col3.prop(self, "com_modifiers", text="Use Modifiers", icon='MODIFIER')
-            component = bpy.data.objects[self.component]
-            try:
-                if not (component.modifiers or component.data.shape_keys):
-                    col3.enabled = False
-                    self.com_modifiers = False
-            except:
-                col3.enabled = False
-                self.com_modifiers = False
-            col.separator()
-            # Fill and Rotation
-            row = col.row(align=True)
-            row.label(text="Fill Mode:")
-            row = col.row(align=True)
-            row.prop(
-                self, "fill_mode", icon='NONE', expand=True,
-                slider=True, toggle=False, icon_only=False, event=False,
-                full_event=False, emboss=True, index=-1)
-            row = col.row(align=True)
-            row.prop(self, "bool_smooth")
-
-            # frame settings
-            if self.fill_mode == 'FRAME':
-                col.separator()
-                col.label(text="Frame Settings:")
-                row = col.row(align=True)
-                row.prop(self, "frame_mode", expand=True)
-                col.prop(self, "frame_thickness", text='Thickness', icon='NONE')
-                col.separator()
-                row = col.row(align=True)
-                row.prop(self, "fill_frame", icon='NONE')
-                show_frame_mat = self.bool_multi_components or self.bool_material_id
-                if self.fill_frame and show_frame_mat:
-                    row.prop(self, "fill_frame_mat", icon='NONE')
-                row = col.row(align=True)
-                row.prop(self, "frame_boundary", text='Boundary', icon='NONE')
-                if self.frame_boundary and show_frame_mat:
-                    row.prop(self, "frame_boundary_mat", icon='NONE')
-
-            if self.rotation_mode == 'UV':
-                uv_error = False
-
-                if ob0.type != 'MESH':
-                    row = col.row(align=True)
-                    row.label(
-                        text="UV rotation supported only for Mesh objects",
-                        icon='ERROR')
-                    uv_error = True
-                else:
-                    if len(ob0.data.uv_layers) == 0:
-                        row = col.row(align=True)
-                        check_name = self.generator
-                        row.label(text="'" + check_name +
-                                  "' doesn't have UV Maps", icon='ERROR')
-                        uv_error = True
-                if uv_error:
-                    row = col.row(align=True)
-                    row.label(text="Default rotation will be used instead",
-                              icon='INFO')
-
-            # Component Z
-            col.separator()
-            col.label(text="Thickness:")
-            row = col.row(align=True)
-            row.prop(
-                self, "scale_mode", text="Scale Mode", icon='NONE', expand=True,
-                slider=False, toggle=False, icon_only=False, event=False,
-                full_event=False, emboss=True, index=-1)
-            col.prop(
-                self, "zscale", text="Scale", icon='NONE', expand=False,
-                slider=True, toggle=False, icon_only=False, event=False,
-                full_event=False, emboss=True, index=-1)
-            if self.mode == 'BOUNDS':
-                col.prop(
-                    self, "offset", text="Offset", icon='NONE', expand=False,
-                    slider=True, toggle=False, icon_only=False, event=False,
-                    full_event=False, emboss=True, index=-1)
-
-            # Component XY
-            col.separator()
-            row = col.row(align=True)
-            row.label(text="Component Coordinates:")
-            row = col.row(align=True)
-            row.prop(
-                self, "mode", text="Component XY", icon='NONE', expand=True,
-                slider=False, toggle=False, icon_only=False, event=False,
-                full_event=False, emboss=True, index=-1)
-
-            if self.mode != 'BOUNDS':
-                col.separator()
-                row = col.row(align=True)
-                row.label(text="X:")
-                row.prop(
-                    self, "bounds_x", text="Bounds X", icon='NONE', expand=True,
-                    slider=False, toggle=False, icon_only=False, event=False,
-                    full_event=False, emboss=True, index=-1)
-
-                row = col.row(align=True)
-                row.label(text="Y:")
-                row.prop(
-                    self, "bounds_y", text="Bounds X", icon='NONE', expand=True,
-                    slider=False, toggle=False, icon_only=False, event=False,
-                    full_event=False, emboss=True, index=-1)
-
-
-            # merge settings
-            col = layout.column(align=True)
-            row = col.row(align=True)
-            row.prop(self, "merge")
-            if self.merge:
-                row.prop(self, "merge_thres")
-                col.separator()
-                row = col.row(align=True)
-                col2 = row.column(align=True)
-                col2.label(text='Close Mesh:')
-                col2 = row.column(align=True)
-                col2.prop(self, "close_mesh",text='')
-                if self.close_mesh != 'NONE':
-                    row = col.row(align=True)
-                    row.prop(self, "open_edges_crease", text="Crease")
-                    row.prop(self, "cap_material_index")
-                    if self.close_mesh == 'BRIDGE':
-                        row = col.row(align=True)
-                        row.prop(self, "bridge_cuts")
-                        row.prop(self, "bridge_smoothness")
-                row = col.row(align=True)
-                row.prop(self, "bool_dissolve_seams")
-
-            # Advanced Settings
-            col = layout.column(align=True)
-            col.separator()
-            col.separator()
-            row = col.row(align=True)
-            row.prop(self, "bool_advanced", icon='SETTINGS')
-            if self.bool_advanced:
-                # rotation
-                layout.use_property_split = True
-                layout.use_property_decorate = False  # No animation.
-                col = layout.column(align=True)
-                col.prop(self, "rotation_mode", text='Rotation', icon='NONE', expand=False,
-                         slider=True, toggle=False, icon_only=False, event=False,
-                         full_event=False, emboss=True, index=-1)
-                if self.rotation_mode == 'WEIGHT':
-                    col.prop(self, "rotation_direction", expand=False,
-                              slider=True, toggle=False, icon_only=False, event=False,
-                              full_event=False, emboss=True, index=-1)
-                if self.rotation_mode == 'RANDOM':
-                    col.prop(self, "random_seed")
-                else:
-                    col.prop(self, "rotation_shift")
-
-                if self.rotation_mode == 'UV':
-                    uv_error = False
-                    if self.generator.type != 'MESH':
-                        row = col.row(align=True)
-                        row.label(
-                            text="UV rotation supported only for Mesh objects",
-                            icon='ERROR')
-                        uv_error = True
-                    else:
-                        if len(self.generator.data.uv_layers) == 0:
-                            row = col.row(align=True)
-                            row.label(text="'" + props.generator.name +
-                                      " doesn't have UV Maps", icon='ERROR')
-                            uv_error = True
-                    if uv_error:
-                        row = col.row(align=True)
-                        row.label(text="Default rotation will be used instead",
-                                  icon='INFO')
-                layout.use_property_split = False
-
-                # Direction
-                col = layout.column(align=True)
-                row = col.row(align=True)
-                row.label(text="Direction:")
-                row = col.row(align=True)
-                row.prop(
-                    self, "normals_mode", text="Direction", icon='NONE', expand=True,
-                    slider=False, toggle=False, icon_only=False, event=False,
-                    full_event=False, emboss=True, index=-1)
-                #row.enabled = self.fill_mode != 'PATCH'
-
-                allow_multi = False
-                allow_shapekeys = not self.com_modifiers
-                if self.com_modifiers: self.bool_shapekeys = False
-                for m in ob0.data.materials:
-                    try:
-                        o = bpy.data.objects[m.name]
-                        allow_multi = True
-                        try:
-                            if o.data.shape_keys is None: continue
-                            elif len(o.data.shape_keys.key_blocks) < 2: continue
-                            else: allow_shapekeys = not self.com_modifiers
-                        except: pass
-                    except: pass
-                # DATA #
-                col = layout.column(align=True)
-                col.label(text="Weight and Morphing:")
-                # vertex group + shape keys
-                row = col.row(align=True)
-                col2 = row.column(align=True)
-                col2.prop(self, "bool_vertex_group", icon='GROUP_VERTEX')
-                try:
-                    if len(ob0.vertex_groups) == 0:
-                        col2.enabled = False
-                except:
-                    col2.enabled = False
-                row.separator()
-                col2 = row.column(align=True)
-                row2 = col2.row(align=True)
-                row2.prop(self, "bool_shapekeys", text="Use Shape Keys",  icon='SHAPEKEY_DATA')
-                row2.enabled = allow_shapekeys
-
-                # LIMITED TESSELLATION
-                col = layout.column(align=True)
-                col.label(text="Limited Tessellation:")
-                row = col.row(align=True)
-                col2 = row.column(align=True)
-                col2.prop(self, "bool_multi_components", icon='MOD_TINT')
-                if not allow_multi:
-                    col2.enabled = False
-                    self.bool_multi_components = False
-                col.separator()
-                row = col.row(align=True)
-                col2 = row.column(align=True)
-                col2.prop(self, "bool_selection", text="On selected Faces", icon='RESTRICT_SELECT_OFF')
-                row.separator()
-                if ob0.type != 'MESH':
-                    col2.enabled = False
-                col2 = row.column(align=True)
-                col2.prop(self, "bool_material_id", icon='MATERIAL_DATA', text="Material ID")
-                if self.bool_material_id and not self.bool_multi_components:
-                    #col2 = row.column(align=True)
-                    col2.prop(self, "material_id")
-                col2.enabled = not self.bool_multi_components
-
-                col.separator()
-                row = col.row(align=True)
-                row.label(text='Reiterate Tessellation:', icon='FILE_REFRESH')
-                row.prop(self, 'iterations', text='Repeat', icon='SETTINGS')
-                if self.iterations > 1 and self.fill_mode == 'PATCH':
-                    col.separator()
-                    row = col.row(align=True)
-                    row.prop(self, 'patch_subs')
-                col.separator()
-                row = col.row(align=True)
-                row.label(text='Combine Iterations:')
-                row = col.row(align=True)
-                row.prop(
-                    self, "combine_mode", icon='NONE', expand=True,
-                    slider=False, toggle=False, icon_only=False, event=False,
-                    full_event=False, emboss=True, index=-1)
-
-    def execute(self, context):
-        allowed_obj = ('MESH', 'CURVE', 'META', 'SURFACE', 'FONT')
-        try:
-            ob0 = bpy.data.objects[self.generator]
-            ob1 = bpy.data.objects[self.component]
-        except:
-            return {'CANCELLED'}
-
-        self.object_name = "Tessellation"
-        # Check if existing object with same name
-        names = [o.name for o in bpy.data.objects]
-        if self.object_name in names:
-            count_name = 1
-            while True:
-                test_name = self.object_name + '.{:03d}'.format(count_name)
-                if not (test_name in names):
-                    self.object_name = test_name
-                    break
-                count_name += 1
-
-        if ob1.type not in allowed_obj:
-            message = "Component must be Mesh, Curve, Surface, Text or Meta object!"
-            self.report({'ERROR'}, message)
-            self.component = None
-
-        if ob0.type not in allowed_obj:
-            message = "Generator must be Mesh, Curve, Surface, Text or Meta object!"
-            self.report({'ERROR'}, message)
-            self.generator = ""
-
-        if True:#self.component not in ("",None) and self.generator not in ("",None):
-            if bpy.ops.object.select_all.poll():
-                bpy.ops.object.select_all(action='TOGGLE')
-            bpy.ops.object.mode_set(mode='OBJECT')
-
-            #data0 = ob0.to_mesh(False)
-            #data0 = ob0.data.copy()
-            bool_update = False
-            if bpy.context.object == ob0:
-                auto_layer_collection()
-                #new_ob = bpy.data.objects.new(self.object_name, data0)
-                new_ob = convert_object_to_mesh(ob0,False,False)
-                new_ob.data.name = self.object_name
-                #bpy.context.collection.objects.link(new_ob)
-                #bpy.context.view_layer.objects.active = new_ob
-                new_ob.name = self.object_name
-                #new_ob.select_set(True)
-            else:
-                new_ob = bpy.context.object
-                bool_update = True
-            new_ob = store_parameters(self, new_ob)
-            try: bpy.ops.object.tissue_update_tessellate()
-            except RuntimeError as e:
-                bpy.data.objects.remove(new_ob)
-                self.report({'ERROR'}, str(e))
-                return {'CANCELLED'}
-            if not bool_update:
-                self.object_name = new_ob.name
-                #self.working_on = self.object_name
-                new_ob.location = ob0.location
-                new_ob.matrix_world = ob0.matrix_world
-
-            return {'FINISHED'}
-
-    def invoke(self, context, event):
-        return context.window_manager.invoke_props_dialog(self)
-
-
-def update_dependencies(ob, objects):
-    ob0 = ob.tissue_tessellate.generator
-    ob1 = ob.tissue_tessellate.component
-    deps = [ob0, ob1]
-    for o in deps:
-        if o.tissue_tessellate.bool_lock: continue
-        o0 = o.tissue_tessellate.generator
-        o1 = o.tissue_tessellate.component
-        deps_deps = [o0, o1]
-        try:
-            o0.name
-            o1.name
-            if o0 not in objects and o1 not in objects:
-                objects.append(o)
-                objects = update_dependencies(o, objects)
-        except:
-            continue
-    return objects
-
-
-class tissue_refresh_tessellate(Operator):
-    bl_idname = "object.tissue_refresh_tessellate"
-    bl_label = "Refresh"
-    bl_description = ("Fast update the tessellated mesh according to base and "
-                      "component changes")
-    bl_options = {'REGISTER', 'UNDO'}
-
-    go = False
-
-    @classmethod
-    def poll(cls, context):
-        try:
-            return context.object.tissue_tessellate.generator != None and \
-                context.object.tissue_tessellate.component != None
-        except:
-            return False
-
-    @staticmethod
-    def check_gen_comp(checking):
-        # note pass the stored name key in here to check it out
-        return checking in bpy.data.objects.keys()
-
-    def execute(self, context):
-        ob = bpy.context.object
-        ob0 = ob.tissue_tessellate.generator
-        ob1 = ob.tissue_tessellate.component
-        try:
-            ob0.name
-            ob1.name
-        except:
-            self.report({'ERROR'},
-                        "Active object must be Tessellate before Update")
-            return {'CANCELLED'}
-
-        if ob.tissue_tessellate.bool_dependencies:
-            update_objects = list(reversed(update_dependencies(ob, [ob])))
-        else:
-            update_objects = [ob]
-        for o in update_objects:
-            override = {'object': o}
-            bpy.ops.object.tissue_update_tessellate(override)
-
-        return {'FINISHED'}
-
-
-class tissue_update_tessellate(Operator):
-    bl_idname = "object.tissue_update_tessellate"
-    bl_label = "Refresh"
-    bl_description = ("Fast update the tessellated mesh according to base and "
-                      "component changes")
-    bl_options = {'REGISTER', 'UNDO'}
-
-    go = False
-
-    @classmethod
-    def poll(cls, context):
-        #try:
-        try: #context.object == None: return False
-            return context.object.tissue_tessellate.generator != None and \
-                context.object.tissue_tessellate.component != None
-        except:
-            return False
-
-    @staticmethod
-    def check_gen_comp(checking):
-        # note pass the stored name key in here to check it out
-        return checking in bpy.data.objects.keys()
-
-    def execute(self, context):
-        start_time = time.time()
-
-        ob = context.object
-        if not self.go:
-            generator = ob.tissue_tessellate.generator
-            component = ob.tissue_tessellate.component
-            zscale = ob.tissue_tessellate.zscale
-            scale_mode = ob.tissue_tessellate.scale_mode
-            rotation_mode = ob.tissue_tessellate.rotation_mode
-            rotation_shift = ob.tissue_tessellate.rotation_shift
-            rotation_direction = ob.tissue_tessellate.rotation_direction
-            offset = ob.tissue_tessellate.offset
-            merge = ob.tissue_tessellate.merge
-            merge_thres = ob.tissue_tessellate.merge_thres
-            gen_modifiers = ob.tissue_tessellate.gen_modifiers
-            com_modifiers = ob.tissue_tessellate.com_modifiers
-            bool_random = ob.tissue_tessellate.bool_random
-            random_seed = ob.tissue_tessellate.random_seed
-            fill_mode = ob.tissue_tessellate.fill_mode
-            bool_vertex_group = ob.tissue_tessellate.bool_vertex_group
-            bool_selection = ob.tissue_tessellate.bool_selection
-            bool_shapekeys = ob.tissue_tessellate.bool_shapekeys
-            mode = ob.tissue_tessellate.mode
-            bool_smooth = ob.tissue_tessellate.bool_smooth
-            bool_materials = ob.tissue_tessellate.bool_materials
-            bool_dissolve_seams = ob.tissue_tessellate.bool_dissolve_seams
-            bool_material_id = ob.tissue_tessellate.bool_material_id
-            material_id = ob.tissue_tessellate.material_id
-            iterations = ob.tissue_tessellate.iterations
-            bool_combine = ob.tissue_tessellate.bool_combine
-            normals_mode = ob.tissue_tessellate.normals_mode
-            bool_advanced = ob.tissue_tessellate.bool_advanced
-            bool_multi_components = ob.tissue_tessellate.bool_multi_components
-            combine_mode = ob.tissue_tessellate.combine_mode
-            bounds_x = ob.tissue_tessellate.bounds_x
-            bounds_y = ob.tissue_tessellate.bounds_y
-            cap_faces = ob.tissue_tessellate.cap_faces
-            close_mesh = ob.tissue_tessellate.close_mesh
-            open_edges_crease = ob.tissue_tessellate.open_edges_crease
-            bridge_smoothness = ob.tissue_tessellate.bridge_smoothness
-            frame_thickness = ob.tissue_tessellate.frame_thickness
-            frame_mode = ob.tissue_tessellate.frame_mode
-            frame_boundary = ob.tissue_tessellate.frame_boundary
-            fill_frame = ob.tissue_tessellate.fill_frame
-            frame_boundary_mat = ob.tissue_tessellate.frame_boundary_mat
-            fill_frame_mat = ob.tissue_tessellate.fill_frame_mat
-            bridge_cuts = ob.tissue_tessellate.bridge_cuts
-            cap_material_index = ob.tissue_tessellate.cap_material_index
-            patch_subs = ob.tissue_tessellate.patch_subs
-        try:
-            generator.name
-            component.name
-        except:
-            self.report({'ERROR'},
-                        "Active object must be Tessellate before Update")
-            return {'CANCELLED'}
-
-        # Solve Local View issues
-        local_spaces = []
-        local_ob0 = []
-        local_ob1 = []
-        for area in context.screen.areas:
-            for space in area.spaces:
-                try:
-                    if ob.local_view_get(space):
-                        local_spaces.append(space)
-                        local_ob0 = ob0.local_view_get(space)
-                        ob0.local_view_set(space, True)
-                        local_ob1 = ob1.local_view_get(space)
-                        ob1.local_view_set(space, True)
-                except:
-                    pass
-
-        starting_mode = context.object.mode
-
-        #if starting_mode == 'PAINT_WEIGHT': starting_mode = 'WEIGHT_PAINT'
-        bpy.ops.object.mode_set(mode='OBJECT')
-
-        ob0 = generator
-        ob1 = component
-        ##### auto_layer_collection()
-
-        ob0_hide = ob0.hide_get()
-        ob0_hidev = ob0.hide_viewport
-        ob0_hider = ob0.hide_render
-        ob1_hide = ob1.hide_get()
-        ob1_hidev = ob1.hide_viewport
-        ob1_hider = ob1.hide_render
-        ob0.hide_set(False)
-        ob0.hide_viewport = False
-        ob0.hide_render = False
-        ob1.hide_set(False)
-        ob1.hide_viewport = False
-        ob1.hide_render = False
-
-        if ob0.type == 'META':
-            base_ob = convert_object_to_mesh(ob0, False, True)
-        else:
-            base_ob = ob0.copy()
-            base_ob.data = ob0.data#
-            context.collection.objects.link(base_ob)
-        base_ob.name = '_tissue_tmp_base'
-
-        # In Blender 2.80 cache of copied objects is lost, must be re-baked
-        bool_update_cloth = False
-        for m in base_ob.modifiers:
-            if m.type == 'CLOTH':
-                m.point_cache.frame_end = context.scene.frame_current
-                bool_update_cloth = True
-        if bool_update_cloth:
-            bpy.ops.ptcache.free_bake_all()
-            bpy.ops.ptcache.bake_all()
-        base_ob.modifiers.update()
-
-
-        #new_ob.location = ob.location
-        #new_ob.matrix_world = ob.matrix_world
-        #bpy.ops.object.select_all(action='DESELECT')
-        if bool_selection:
-            faces = base_ob.data.polygons
-            selections = [False]*len(faces)
-            faces.foreach_get('select',selections)
-            selections = np.array(selections)
-            if not selections.any():
-                message = "There are no faces selected."
-                context.view_layer.objects.active = ob
-                ob.select_set(True)
-                bpy.ops.object.mode_set(mode=starting_mode)
-                bpy.data.objects.remove(base_ob)
-                self.report({'ERROR'}, message)
-                return {'CANCELLED'}
-
-
-
-        iter_objects = [base_ob]
-        ob_location = ob.location
-        ob_matrix_world = ob.matrix_world
-        #base_ob = new_ob#.copy()
-
-        for iter in range(iterations):
-
-            if iter > 0 and len(iter_objects) == 0: break
-            if iter > 0 and normals_mode == 'CUSTOM': normals_mode = 'VERTS'
-            same_iteration = []
-            matched_materials = []
-            # iterate base object materials (needed for multi-components)
-            if bool_multi_components: mat_iter = len(base_ob.material_slots)
-            else: mat_iter = 1
-            for m_id in range(mat_iter):
-                if bool_multi_components:
-                    # check if material and components match
-                    try:
-                        mat = base_ob.material_slots[m_id].material
-                        ob1 = bpy.data.objects[mat.name]
-                        if ob1.type not in ('MESH', 'CURVE','SURFACE','FONT', 'META'):
-                            continue
-                        material_id = m_id
-                        matched_materials.append(m_id)
-                        bool_material_id = True
-                    except:
-                        continue
-                if com_modifiers or ob1.type != 'MESH':
-                    data1 = simple_to_mesh(ob1)
-                else:
-                    data1 = ob1.data.copy()
-                n_edges1 = len(data1.edges)
-                bpy.data.meshes.remove(data1)
-
-                if iter != 0: gen_modifiers = True
-
-                if fill_mode == 'PATCH':
-                    # patch subdivisions for additional iterations
-                    if iter > 0:
-                        base_ob.modifiers.new('Tissue_Subsurf', type='SUBSURF')
-                        base_ob.modifiers['Tissue_Subsurf'].levels = patch_subs
-                        temp_mod = base_ob.modifiers['Tissue_Subsurf']
-                    # patch tessellation
-                    new_ob = tessellate_patch(
-                            base_ob, ob1, offset, zscale, com_modifiers, mode, scale_mode,
-                            rotation_mode, rotation_shift, random_seed, bool_vertex_group,
-                            bool_selection, bool_shapekeys, bool_material_id, material_id,
-                            normals_mode, bounds_x, bounds_y
-                            )
-                    if iter > 0:
-                        base_ob.modifiers.remove(temp_mod)
-                else:
-                    ### FRAME and FAN ###
-                    if fill_mode in ('FRAME','FAN'):
-
-                        if fill_mode == 'FRAME': convert_function = convert_to_frame
-                        else: convert_function = convert_to_fan
-
-                        if normals_mode == 'CUSTOM' and base_ob.data.shape_keys != None:
-                            ## base key
-                            sk_values = [sk.value for sk in base_ob.data.shape_keys.key_blocks]
-                            for sk in ob0.data.shape_keys.key_blocks: sk.value = 0
-                            _base_ob = convert_function(base_ob, ob.tissue_tessellate, gen_modifiers)
-                            for i, sk in enumerate(ob0.data.shape_keys.key_blocks):
-                                sk.value = sk_values[i]
-                            ## key 1
-                            # hide modifiers
-                            if not gen_modifiers and len(base_ob.modifiers) > 0:
-                                mod_visibility = [m.show_viewport for m in base_ob.modifiers]
-                                for m in base_ob.modifiers: m.show_viewport = False
-                                base_ob.modifiers.update()
-                            base_ob_sk = convert_function(ob0, ob.tissue_tessellate, True)
-                            ## combine shapekeys
-                            _base_ob.shape_key_add(name='Basis', from_mix=False)
-                            _base_ob.shape_key_add(name='Key1', from_mix=False)
-                            sk_block = _base_ob.data.shape_keys.key_blocks[1]
-                            sk_block.value = 1
-                            for vert, sk in zip(base_ob_sk.data.vertices, sk_block.data):
-                                sk.co = vert.co
-                            bpy.data.objects.remove(base_ob_sk)
-                            # set original modifiers
-                            if not gen_modifiers and len(base_ob.modifiers) > 0:
-                                for i,m in enumerate(base_ob.modifiers):
-                                    m.show_viewport = mod_visibility[i]
-                                base_ob.modifiers.update()
-                        else:
-                            _base_ob = convert_function(base_ob, ob.tissue_tessellate, gen_modifiers)
-                        bpy.data.objects.remove(base_ob)
-                        base_ob = _base_ob
-                    # quad tessellation
-                    new_ob = tessellate_original(
-                            base_ob, ob1, offset, zscale, gen_modifiers,
-                            com_modifiers, mode, scale_mode, rotation_mode,
-                            rotation_shift, rotation_direction,
-                            random_seed, fill_mode, bool_vertex_group,
-                            bool_selection, bool_shapekeys, bool_material_id,
-                            material_id, normals_mode, bounds_x, bounds_y
-                            )
-
-                # if empty or error, continue
-                if type(new_ob) is not bpy.types.Object:
-                    continue
-
-                # prepare base object
-                if iter == 0 and gen_modifiers:
-                    temp_base_ob = convert_object_to_mesh(base_ob, True, True)
-                    bpy.data.objects.remove(base_ob)
-                    base_ob = temp_base_ob
-                    iter_objects = [base_ob]
-
-                # rename, make active and change transformations
-                new_ob.name = '_tissue_tmp_{}_{}'.format(iter,m_id)
-                new_ob.select_set(True)
-                context.view_layer.objects.active = new_ob
-                new_ob.location = ob_location
-                new_ob.matrix_world = ob_matrix_world
-
-                n_components = int(len(new_ob.data.edges) / n_edges1)
-                # SELECTION
-                if bool_selection:
-                    try:
-                        # create selection list
-                        polygon_selection = [p.select for p in ob1.data.polygons] * int(
-                                len(new_ob.data.polygons) / len(ob1.data.polygons))
-                        new_ob.data.polygons.foreach_set("select", polygon_selection)
-                    except:
-                        pass
-                if bool_multi_components: same_iteration.append(new_ob)
-
-            base_ob.location = ob_location
-            base_ob.matrix_world = ob_matrix_world
-
-            # join together multiple components iterations
-            if bool_multi_components:
-                if len(same_iteration) > 0:
-                    context.view_layer.update()
-                    for o in context.view_layer.objects:
-                        o.select_set(o in same_iteration)
-                    bpy.ops.object.join()
-                    new_ob = context.view_layer.objects.active
-                    new_ob.select_set(True)
-                    #new_ob.data.update()
-
-            if type(new_ob) in (int,str):
-                if iter == 0:
-                    try:
-                        bpy.data.objects.remove(iter_objects[0])
-                        iter_objects = []
-                    except: continue
-                continue
-
-            # Clean last iteration, needed for combine object
-            if (bool_selection or bool_material_id) and combine_mode == 'UNUSED':
-                # remove faces from last mesh
-                bm = bmesh.new()
-                last_mesh = iter_objects[-1].data.copy()
-                bm.from_mesh(last_mesh)
-                bm.faces.ensure_lookup_table()
-                if bool_multi_components:
-                    remove_materials = matched_materials
-                elif bool_material_id:
-                    remove_materials = [material_id]
-                else: remove_materials = []
-                if bool_selection:
-                    if bool_multi_components or bool_material_id:
-                        remove_faces = [f for f in bm.faces if f.material_index in remove_materials and f.select]
-                    else:
-                        remove_faces = [f for f in bm.faces if f.select]
-                else:
-                    remove_faces = [f for f in bm.faces if f.material_index in remove_materials]
-                bmesh.ops.delete(bm, geom=remove_faces, context='FACES')
-                bm.to_mesh(last_mesh)
-                last_mesh.update()
-                last_mesh.name = '_tissue_tmp_previous_unused'
-
-                # delete previous iteration if empty or update it
-                if len(last_mesh.vertices) > 0:
-                    iter_objects[-1].data = last_mesh.copy()
-                    iter_objects[-1].data.update()
-                else:
-                    bpy.data.objects.remove(iter_objects[-1])
-                    iter_objects = iter_objects[:-1]
-                # set new base object for next iteration
-                base_ob = convert_object_to_mesh(new_ob,True,True)
-                if iter < iterations-1: new_ob.data = base_ob.data
-                # store new iteration and set transformations
-                iter_objects.append(new_ob)
-                #try:
-                #    bpy.data.objects.remove(bpy.data.objects['_tissue_tmp_base'])
-                #except:
-                #    pass
-                base_ob.name = '_tissue_tmp_base'
-            elif combine_mode == 'ALL':
-                base_ob = new_ob.copy()
-                iter_objects.append(new_ob)
-            else:
-                if base_ob != new_ob:
-                    bpy.data.objects.remove(base_ob)
-                base_ob = new_ob
-                iter_objects = [new_ob]
-
-            # Combine
-            if combine_mode != 'LAST' and len(iter_objects)>0:
-                if base_ob not in iter_objects and type(base_ob) == bpy.types.Object:
-                    bpy.data.objects.remove(base_ob)
-                for o in context.view_layer.objects:
-                    o.select_set(o in iter_objects)
-                bpy.ops.object.join()
-                new_ob.data.update()
-                iter_objects = [new_ob]
-
-            if merge:
-                bpy.ops.object.mode_set(mode='EDIT')
-                bpy.ops.mesh.select_mode(
-                    use_extend=False, use_expand=False, type='VERT')
-                bpy.ops.mesh.select_non_manifold(
-                    extend=False, use_wire=True, use_boundary=True,
-                    use_multi_face=False, use_non_contiguous=False, use_verts=False)
-
-                bpy.ops.mesh.remove_doubles(
-                    threshold=merge_thres, use_unselected=False)
-
-                if bool_dissolve_seams:
-                    bpy.ops.mesh.select_mode(type='EDGE')
-                    bpy.ops.mesh.select_all(action='DESELECT')
-                    bpy.ops.object.mode_set(mode='OBJECT')
-                    for e in new_ob.data.edges:
-                        e.select = e.use_seam
-                    bpy.ops.object.mode_set(mode='EDIT')
-                    bpy.ops.mesh.dissolve_edges()
-                bpy.ops.object.mode_set(mode='OBJECT')
-
-                if close_mesh != 'NONE':
-                    bpy.ops.object.mode_set(mode='EDIT')
-                    bpy.ops.mesh.select_mode(
-                        use_extend=False, use_expand=False, type='EDGE')
-                    bpy.ops.mesh.select_non_manifold(
-                        extend=False, use_wire=False, use_boundary=True,
-                        use_multi_face=False, use_non_contiguous=False, use_verts=False)
-                    if open_edges_crease != 0:
-                        bpy.ops.transform.edge_crease(value=open_edges_crease)
-                    if close_mesh == 'CAP':
-                        bpy.ops.mesh.edge_face_add()
-                    if close_mesh == 'BRIDGE':
-                        try:
-                            bpy.ops.mesh.bridge_edge_loops(
-                                type='PAIRS',
-                                number_cuts=bridge_cuts,
-                                interpolation='SURFACE',
-                                smoothness=bridge_smoothness)
-                        except: pass
-                    bpy.ops.object.mode_set(mode='OBJECT')
-                    for f in new_ob.data.polygons:
-                        if f.select: f.material_index = cap_material_index
-            base_ob = context.view_layer.objects.active
-
-        # Combine iterations
-        if combine_mode != 'LAST' and len(iter_objects)>0:
-            #if base_ob not in iter_objects and type(base_ob) == bpy.types.Object:
-            #    bpy.data.objects.remove(base_ob)
-            for o in context.view_layer.objects:
-                o.select_set(o in iter_objects)
-            bpy.ops.object.join()
-            new_ob = context.view_layer.objects.active
-        elif combine_mode == 'LAST' and type(new_ob) != bpy.types.Object:
-            # if last iteration gives error, then use the last correct iteration
-            try:
-                if type(iter_objects[-1]) == bpy.types.Object:
-                    new_ob = iter_objects[-1]
-            except: pass
-
-        if new_ob == 0:
-            #bpy.data.objects.remove(base_ob.data)
-            try: bpy.data.objects.remove(base_ob)
-            except: pass
-            message = "The generated object is an empty geometry!"
-            context.view_layer.objects.active = ob
-            ob.select_set(True)
-            bpy.ops.object.mode_set(mode=starting_mode)
-            self.report({'ERROR'}, message)
-            return {'CANCELLED'}
-        errors = {}
-        errors["modifiers_error"] = "Modifiers that change the topology of the mesh \n" \
-                                    "after the last Subsurf (or Multires) are not allowed."
-        errors["topology_error"] = "Make sure that the topology of the mesh before \n" \
-                                    "the last Subsurf (or Multires) is quads only."
-        errors["wires_error"] = "Please remove all wire edges in the base object."
-        errors["verts_error"] = "Please remove all floating vertices in the base object"
-        if new_ob in errors:
-            for o in iter_objects:
-                try: bpy.data.objects.remove(o)
-                except: pass
-            try: bpy.data.meshes.remove(data1)
-            except: pass
-            context.view_layer.objects.active = ob
-            ob.select_set(True)
-            message = errors[new_ob]
-            ob.tissue_tessellate.error_message = message
-            bpy.ops.object.mode_set(mode=starting_mode)
-            self.report({'ERROR'}, message)
-            return {'CANCELLED'}
-
-        #new_ob.location = ob_location
-        #new_ob.matrix_world = ob_matrix_world
-
-        # update data and preserve name
-        if ob.type != 'MESH':
-            loc, matr = ob.location, ob.matrix_world
-            ob = convert_object_to_mesh(ob,False,True)
-            ob.location, ob.matrix_world = loc, matr
-        data_name = ob.data.name
-        old_data = ob.data
-        #ob.data = bpy.data.meshes.new_from_object(new_ob)#
-        ob.data = new_ob.data.copy()
-        ob.data.name = data_name
-        bpy.data.meshes.remove(old_data)
-
-        # copy vertex group
-        if bool_vertex_group:
-            for vg in new_ob.vertex_groups:
-                if not vg.name in ob.vertex_groups.keys():
-                    ob.vertex_groups.new(name=vg.name)
-                new_vg = ob.vertex_groups[vg.name]
-                for i in range(len(ob.data.vertices)):
-                    try:
-                        weight = vg.weight(i)
-                    except:
-                        weight = 0
-                    new_vg.add([i], weight, 'REPLACE')
-
-        selected_objects = [o for o in context.selected_objects]
-        for o in selected_objects: o.select_set(False)
-
-        ob.select_set(True)
-        context.view_layer.objects.active = ob
-
-        if merge:
-            try:
-                bpy.ops.object.mode_set(mode='EDIT')
-                #bpy.ops.mesh.select_mode(
-                #    use_extend=False, use_expand=False, type='VERT')
-                bpy.ops.mesh.select_mode(type='VERT')
-                bpy.ops.mesh.select_non_manifold(
-                    extend=False, use_wire=True, use_boundary=True,
-                    use_multi_face=False, use_non_contiguous=False, use_verts=False)
-
-                bpy.ops.mesh.remove_doubles(
-                    threshold=merge_thres, use_unselected=False)
-
-                bpy.ops.object.mode_set(mode='OBJECT')
-                if bool_dissolve_seams:
-                    bpy.ops.object.mode_set(mode='EDIT')
-                    bpy.ops.mesh.select_mode(type='EDGE')
-                    bpy.ops.mesh.select_all(action='DESELECT')
-                    bpy.ops.object.mode_set(mode='OBJECT')
-                    for e in ob.data.edges:
-                        e.select = e.use_seam
-                    bpy.ops.object.mode_set(mode='EDIT')
-                    bpy.ops.mesh.dissolve_edges()
-            except: pass
-            if close_mesh != 'NONE':
-                bpy.ops.object.mode_set(mode='EDIT')
-                bpy.ops.mesh.select_mode(
-                    use_extend=False, use_expand=False, type='EDGE')
-                bpy.ops.mesh.select_non_manifold(
-                    extend=False, use_wire=False, use_boundary=True,
-                    use_multi_face=False, use_non_contiguous=False, use_verts=False)
-                if open_edges_crease != 0:
-                    bpy.ops.transform.edge_crease(value=open_edges_crease)
-                if close_mesh == 'CAP':
-                    bpy.ops.mesh.edge_face_add()
-                if close_mesh == 'BRIDGE':
-                    try:
-                        bpy.ops.mesh.bridge_edge_loops(
-                            type='PAIRS',
-                            number_cuts=bridge_cuts,
-                            interpolation='SURFACE',
-                            smoothness=bridge_smoothness)
-                    except:
-                        pass
-                bpy.ops.object.mode_set(mode='OBJECT')
-                for f in ob.data.polygons:
-                    if f.select: f.material_index = cap_material_index
-        #else:
-
-        try:
-            bpy.ops.object.mode_set(mode='EDIT')
-            bpy.ops.object.mode_set(mode='OBJECT')
-        except: pass
-
-        if bool_smooth: bpy.ops.object.shade_smooth()
-
-        for mesh in bpy.data.meshes:
-            if not mesh.users: bpy.data.meshes.remove(mesh)
-
-        for o in selected_objects:
-            try: o.select_set(True)
-            except: pass
-
-        bpy.ops.object.mode_set(mode=starting_mode)
-
-        ob.tissue_tessellate.error_message = ""
-
-        # Restore Base visibility
-        ob0.hide_set(ob0_hide)
-        ob0.hide_viewport = ob0_hidev
-        ob0.hide_render = ob0_hider
-        # Restore Component visibility
-        ob1.hide_set(ob1_hide)
-        ob1.hide_viewport = ob1_hidev
-        ob1.hide_render = ob1_hider
-        # Restore Local visibility
-        for space, local0, local1 in zip(local_spaces, local_ob0, local_ob1):
-            ob0.local_view_set(space, local0)
-            ob1.local_view_set(space, local1)
-
-        bpy.data.objects.remove(new_ob)
-
-        # clean objects
-        for o in bpy.data.objects:
-            #if o.name not in context.view_layer.objects and "_tissue_tmp" in o.name:
-            if "_tissue_tmp" in o.name:
-                bpy.data.objects.remove(o)
-
-        end_time = time.time()
-        print('Tissue: object "{}" tessellated in {:.4f} sec'.format(ob.name, end_time-start_time))
-        return {'FINISHED'}
-
-    def check(self, context):
-        return True
-
-class TISSUE_PT_tessellate(Panel):
-    bl_label = "Tissue Tools"
-    bl_category = "Tissue"
-    bl_space_type = "VIEW_3D"
-    bl_region_type = "UI"
-    #bl_options = {'DEFAULT_OPEN'}
-
-    @classmethod
-    def poll(cls, context):
-        return context.mode in {'OBJECT', 'EDIT_MESH'}
-
-    def draw(self, context):
-        layout = self.layout
-
-        col = layout.column(align=True)
-        col.label(text="Tessellate:")
-        col.operator("object.tissue_tessellate")
-        col.operator("object.dual_mesh_tessellated")
-        col.separator()
-        col.operator("object.tissue_refresh_tessellate", icon='FILE_REFRESH')
-
-        col.separator()
-        col.label(text="Rotate Faces:")
-        row = col.row(align=True)
-        row.operator("mesh.tissue_rotate_face_left", text='Left', icon='LOOP_BACK')
-        row.operator("mesh.tissue_rotate_face_right", text='Right', icon='LOOP_FORWARDS')
-
-        col.separator()
-        col.label(text="Other:")
-        col.operator("object.dual_mesh")
-        col.operator("object.lattice_along_surface", icon="OUTLINER_OB_LATTICE")
-
-        act = context.object
-        if act and act.type == 'MESH':
-            col.operator("object.uv_to_mesh", icon="UV")
-
-            if act.mode == 'EDIT':
-                col.separator()
-                col.label(text="Weight:")
-                col.operator("object.tissue_weight_distance", icon="TRACKING")
-
-class TISSUE_PT_tessellate_object(Panel):
-    bl_space_type = 'PROPERTIES'
-    bl_region_type = 'WINDOW'
-    bl_context = "data"
-    bl_label = "Tessellate Settings"
-    bl_options = {'DEFAULT_CLOSED'}
-
-    @classmethod
-    def poll(cls, context):
-        try: return context.object.type == 'MESH'
-        except: return False
-
-    def draw(self, context):
-        ob = context.object
-        props = ob.tissue_tessellate
-        allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
-
-        try:
-            bool_tessellated = props.generator or props.component != None
-            ob0 = props.generator
-            ob1 = props.component
-        except: bool_tessellated = False
-        layout = self.layout
-        if not bool_tessellated:
-            layout.label(text="The selected object is not a Tessellated object",
-                        icon='INFO')
-        else:
-            if props.error_message != "":
-                layout.label(text=props.error_message,
-                            icon='ERROR')
-            col = layout.column(align=True)
-            row = col.row(align=True)
-
-            set_tessellate_handler(self,context)
-            set_animatable_fix_handler(self,context)
-            row.operator("object.tissue_refresh_tessellate", icon='FILE_REFRESH')
-            lock_icon = 'LOCKED' if props.bool_lock else 'UNLOCKED'
-            #lock_icon = 'PINNED' if props.bool_lock else 'UNPINNED'
-            deps_icon = 'LINKED' if props.bool_dependencies else 'UNLINKED'
-            row.prop(props, "bool_dependencies", text="", icon=deps_icon)
-            row.prop(props, "bool_lock", text="", icon=lock_icon)
-            col2 = row.column(align=True)
-            col2.prop(props, "bool_run", text="",icon='TIME')
-            col2.enabled = not props.bool_lock
-            '''
-            col = layout.column(align=True)
-            row = col.row(align=True)
-            row.label(text="Base :")
-            row.label(text="Component :")
-            row = col.row(align=True)
-
-            col2 = row.column(align=True)
-            col2.prop_search(props, "generator", context.scene, "objects")
-            row.separator()
-            col2 = row.column(align=True)
-            col2.prop_search(props, "component", context.scene, "objects")
-            row = col.row(align=True)
-            col2 = row.column(align=True)
-            col2.prop(props, "gen_modifiers", text="Use Modifiers", icon='MODIFIER')
-            row.separator()
-            try:
-                if not (ob0.modifiers or ob0.data.shape_keys) or props.fill_mode == 'PATCH':
-                    col2.enabled = False
-            except:
-                col2.enabled = False
-            col2 = row.column(align=True)
-            col2.prop(props, "com_modifiers", text="Use Modifiers", icon='MODIFIER')
-            try:
-                if not (props.component.modifiers or props.component.data.shape_keys):
-                    col2.enabled = False
-            except:
-                    col2.enabled = False
-            '''
-            layout.use_property_split = True
-            layout.use_property_decorate = False  # No animation.
-            col = layout.column(align=True)
-            row = col.row(align=True)
-            row.label(text='Base:')
-            row.prop_search(props, "generator", context.scene, "objects")
-            col2 = row.column(align=True)
-            col2.prop(props, "gen_modifiers", text='',icon='MODIFIER')
-            try:
-                if not (props.generator.modifiers or props.generator.data.shape_keys):
-                    col2.enabled = False
-            except:
-                    col2.enabled = False
-            col.separator()
-            row = col.row(align=True)
-            row.label(text='Component:')
-            row.prop_search(props, "component", context.scene, "objects")
-            col2 = row.column(align=True)
-            col2.prop(props, "com_modifiers", text='',icon='MODIFIER')
-            try:
-                if not (props.component.modifiers or props.component.data.shape_keys):
-                    col2.enabled = False
-            except:
-                    col2.enabled = False
-            layout.use_property_split = False
-
-            # Fill
-            col = layout.column(align=True)
-            col.label(text="Fill Mode:")
-
-            # fill
-            row = col.row(align=True)
-            row.prop(props, "fill_mode", icon='NONE', expand=True,
-                     slider=True, toggle=False, icon_only=False, event=False,
-                     full_event=False, emboss=True, index=-1)
-
-            #layout.use_property_split = True
-            col = layout.column(align=True)
-            col.prop(props, "bool_smooth")
-
-
-class TISSUE_PT_tessellate_frame(Panel):
-    bl_space_type = 'PROPERTIES'
-    bl_region_type = 'WINDOW'
-    bl_context = "data"
-    bl_parent_id = "TISSUE_PT_tessellate_object"
-    bl_label = "Frame Settings"
-    #bl_options = {'DEFAULT_CLOSED'}
-
-    @classmethod
-    def poll(cls, context):
-        try:
-            bool_frame = context.object.tissue_tessellate.fill_mode == 'FRAME'
-            bool_tessellated = context.object.tissue_tessellate.generator != None
-            return context.object.type == 'MESH' and bool_frame and bool_tessellated
-        except:
-            return False
-
-    def draw(self, context):
-        ob = context.object
-        props = ob.tissue_tessellate
-        allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
-
-        try:
-            bool_tessellated = props.generator or props.component != None
-            ob0 = props.generator
-            ob1 = props.component
-        except: bool_tessellated = False
-        layout = self.layout
-        if bool_tessellated:
-            col = layout.column(align=True)
-            row = col.row(align=True)
-            row.prop(props, "frame_mode", expand=True)
-            row = col.row(align=True)
-            row.prop(props, "frame_thickness", icon='NONE', expand=True)
-            row = col.row(align=True)
-            row.prop(props, "fill_frame", icon='NONE')
-            show_frame_mat = props.bool_multi_components or props.bool_material_id
-            if props.fill_frame and show_frame_mat:
-                row.prop(props, "fill_frame_mat", icon='NONE')
-            row = col.row(align=True)
-            row.prop(props, "frame_boundary", text='Boundary', icon='NONE')
-            if props.frame_boundary and show_frame_mat:
-                row.prop(props, "frame_boundary_mat", icon='NONE')
-
-
-class TISSUE_PT_tessellate_coordinates(Panel):
-    bl_space_type = 'PROPERTIES'
-    bl_region_type = 'WINDOW'
-    bl_context = "data"
-    bl_parent_id = "TISSUE_PT_tessellate_object"
-    bl_label = "Component Coordinates"
-    bl_options = {'DEFAULT_CLOSED'}
-
-    @classmethod
-    def poll(cls, context):
-        try:
-            bool_tessellated = context.object.tissue_tessellate.generator != None
-            return context.object.type == 'MESH' and bool_tessellated
-        except:
-            return False
-
-    def draw(self, context):
-        ob = context.object
-        props = ob.tissue_tessellate
-        allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
-
-        try:
-            bool_tessellated = props.generator or props.component != None
-            ob0 = props.generator
-            ob1 = props.component
-        except: bool_tessellated = False
-        layout = self.layout
-        if bool_tessellated:
-            col = layout.column(align=True)
-            # component XY
-            row = col.row(align=True)
-            row.prop(props, "mode", expand=True)
-
-            if props.mode != 'BOUNDS':
-                col.separator()
-                row = col.row(align=True)
-                row.label(text="X:")
-                row.prop(
-                    props, "bounds_x", text="Bounds X", icon='NONE', expand=True,
-                    slider=False, toggle=False, icon_only=False, event=False,
-                    full_event=False, emboss=True, index=-1)
-
-                row = col.row(align=True)
-                row.label(text="Y:")
-                row.prop(
-                    props, "bounds_y", text="Bounds X", icon='NONE', expand=True,
-                    slider=False, toggle=False, icon_only=False, event=False,
-                    full_event=False, emboss=True, index=-1)
-
-
-class TISSUE_PT_tessellate_rotation(Panel):
-    bl_space_type = 'PROPERTIES'
-    bl_region_type = 'WINDOW'
-    bl_context = "data"
-    bl_parent_id = "TISSUE_PT_tessellate_object"
-    bl_label = "Rotation"
-    bl_options = {'DEFAULT_CLOSED'}
-
-
-    @classmethod
-    def poll(cls, context):
-        try:
-            bool_tessellated = context.object.tissue_tessellate.generator != None
-            return context.object.type == 'MESH' and bool_tessellated
-        except:
-            return False
-
-    def draw(self, context):
-        ob = context.object
-        props = ob.tissue_tessellate
-        allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
-
-        try:
-            bool_tessellated = props.generator or props.component != None
-            ob0 = props.generator
-            ob1 = props.component
-        except: bool_tessellated = False
-        layout = self.layout
-        if bool_tessellated:
-            # rotation
-            layout.use_property_split = True
-            layout.use_property_decorate = False  # No animation.
-            col = layout.column(align=True)
-            col.prop(props, "rotation_mode", text='Rotation', icon='NONE', expand=False,
-                     slider=True, toggle=False, icon_only=False, event=False,
-                     full_event=False, emboss=True, index=-1)
-            if props.rotation_mode == 'WEIGHT':
-                col.prop(props, "rotation_direction", expand=False,
-                          slider=True, toggle=False, icon_only=False, event=False,
-                          full_event=False, emboss=True, index=-1)
-            if props.rotation_mode == 'RANDOM':
-                col.prop(props, "random_seed")
-            else:
-                col.prop(props, "rotation_shift")
-
-            if props.rotation_mode == 'UV':
-                uv_error = False
-                if props.generator.type != 'MESH':
-                    row = col.row(align=True)
-                    row.label(
-                        text="UV rotation supported only for Mesh objects",
-                        icon='ERROR')
-                    uv_error = True
-                else:
-                    if len(props.generator.data.uv_layers) == 0:
-                        row = col.row(align=True)
-                        row.label(text="'" + props.generator.name +
-                                  " doesn't have UV Maps", icon='ERROR')
-                        uv_error = True
-                if uv_error:
-                    row = col.row(align=True)
-                    row.label(text="Default rotation will be used instead",
-                              icon='INFO')
-
-class TISSUE_PT_tessellate_thickness(Panel):
-    bl_space_type = 'PROPERTIES'
-    bl_region_type = 'WINDOW'
-    bl_context = "data"
-    bl_parent_id = "TISSUE_PT_tessellate_object"
-    bl_label = "Thickness"
-    #bl_options = {'DEFAULT_CLOSED'}
-
-    @classmethod
-    def poll(cls, context):
-        try:
-            bool_tessellated = context.object.tissue_tessellate.generator != None
-            return context.object.type == 'MESH' and bool_tessellated
-        except:
-            return False
-
-    def draw(self, context):
-        ob = context.object
-        props = ob.tissue_tessellate
-        allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
-
-        try:
-            bool_tessellated = props.generator or props.component != None
-            ob0 = props.generator
-            ob1 = props.component
-        except: bool_tessellated = False
-        layout = self.layout
-        #layout.use_property_split = True
-        if bool_tessellated:
-            col = layout.column(align=True)
-            # component Z
-            row = col.row(align=True)
-            row.prop(props, "scale_mode", expand=True)
-            col.prop(props, "zscale", text="Scale", icon='NONE', expand=False,
-                     slider=True, toggle=False, icon_only=False, event=False,
-                     full_event=False, emboss=True, index=-1)
-            if props.mode == 'BOUNDS':
-                col.prop(props, "offset", text="Offset", icon='NONE', expand=False,
-                         slider=True, toggle=False, icon_only=False, event=False,
-                         full_event=False, emboss=True, index=-1)
-
-            # Direction
-            col = layout.column(align=True)
-            row = col.row(align=True)
-            row.label(text="Direction:")
-            row = col.row(align=True)
-            row.prop(
-            props, "normals_mode", text="Direction", icon='NONE', expand=True,
-                slider=False, toggle=False, icon_only=False, event=False,
-                full_event=False, emboss=True, index=-1)
-
-
-class TISSUE_PT_tessellate_options(Panel):
-    bl_space_type = 'PROPERTIES'
-    bl_region_type = 'WINDOW'
-    bl_context = "data"
-    bl_parent_id = "TISSUE_PT_tessellate_object"
-    bl_label = " "
-    bl_options = {'DEFAULT_CLOSED'}
-
-    @classmethod
-    def poll(cls, context):
-        try:
-            bool_tessellated = context.object.tissue_tessellate.generator != None
-            return context.object.type == 'MESH' and bool_tessellated
-        except:
-            return False
-
-    def draw_header(self, context):
-        ob = context.object
-        props = ob.tissue_tessellate
-        self.layout.prop(props, "merge")
-
-    def draw(self, context):
-        ob = context.object
-        props = ob.tissue_tessellate
-        allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
-
-        try:
-            bool_tessellated = props.generator or props.component != None
-            ob0 = props.generator
-            ob1 = props.component
-        except: bool_tessellated = False
-        layout = self.layout
-        layout.use_property_split = True
-        layout.use_property_decorate = False  # No animation.
-        if bool_tessellated:
-            col = layout.column(align=True)
-            if props.merge:
-                col.prop(props, "merge_thres")
-                col.prop(props, "bool_dissolve_seams")
-                col.prop(props, "close_mesh")
-                if props.close_mesh != 'NONE':
-                    #row = col.row(align=True)
-                    col.separator()
-                    col.prop(props, "open_edges_crease", text="Crease")
-                    col.prop(props, "cap_material_index", text='Material Index')
-                    if props.close_mesh == 'BRIDGE':
-                        col.separator()
-                        col.prop(props, "bridge_cuts")
-                        col.prop(props, "bridge_smoothness")
-
-
-class TISSUE_PT_tessellate_morphing(Panel):
-    bl_space_type = 'PROPERTIES'
-    bl_region_type = 'WINDOW'
-    bl_context = "data"
-    bl_parent_id = "TISSUE_PT_tessellate_object"
-    bl_label = "Weight and Morphing"
-    bl_options = {'DEFAULT_CLOSED'}
-
-    @classmethod
-    def poll(cls, context):
-        try:
-            bool_tessellated = context.object.tissue_tessellate.generator != None
-            return context.object.type == 'MESH' and bool_tessellated
-        except:
-            return False
-
-    def draw(self, context):
-        ob = context.object
-        props = ob.tissue_tessellate
-        allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
-
-        try:
-            bool_tessellated = props.generator or props.component != None
-            ob0 = props.generator
-            ob1 = props.component
-        except: bool_tessellated = False
-        layout = self.layout
-        if bool_tessellated:
-            allow_shapekeys = not props.com_modifiers
-            for m in ob0.data.materials:
-                try:
-                    o = bpy.data.objects[m.name]
-                    allow_multi = True
-                    try:
-                        if o.data.shape_keys is None: continue
-                        elif len(o.data.shape_keys.key_blocks) < 2: continue
-                        else: allow_shapekeys = not props.com_modifiers
-                    except: pass
-                except: pass
-            col = layout.column(align=True)
-            #col.label(text="Morphing:")
-            row = col.row(align=True)
-            col2 = row.column(align=True)
-            col2.prop(props, "bool_vertex_group", icon='GROUP_VERTEX')
-            #col2.prop_search(props, "vertex_group", props.generator, "vertex_groups")
-            try:
-                if len(props.generator.vertex_groups) == 0:
-                    col2.enabled = False
-            except:
-                col2.enabled = False
-            row.separator()
-            col2 = row.column(align=True)
-            row2 = col2.row(align=True)
-            row2.prop(props, "bool_shapekeys", text="Use Shape Keys",  icon='SHAPEKEY_DATA')
-            row2.enabled = allow_shapekeys
-            if not allow_shapekeys:
-                col2 = layout.column(align=True)
-                row2 = col2.row(align=True)
-                row2.label(text="Use Shape Keys is not compatible with Use Modifiers", icon='INFO')
-
-
-class TISSUE_PT_tessellate_selective(Panel):
-    bl_space_type = 'PROPERTIES'
-    bl_region_type = 'WINDOW'
-    bl_context = "data"
-    bl_parent_id = "TISSUE_PT_tessellate_object"
-    bl_label = "Selective"
-    bl_options = {'DEFAULT_CLOSED'}
-
-    @classmethod
-    def poll(cls, context):
-        try:
-            bool_tessellated = context.object.tissue_tessellate.generator != None
-            return context.object.type == 'MESH' and bool_tessellated
-        except:
-            return False
-
-    def draw(self, context):
-        ob = context.object
-        props = ob.tissue_tessellate
-        allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
-
-        try:
-            bool_tessellated = props.generator or props.component != None
-            ob0 = props.generator
-            ob1 = props.component
-        except: bool_tessellated = False
-        layout = self.layout
-        if bool_tessellated:
-            allow_multi = False
-            allow_shapekeys = not props.com_modifiers
-            for m in ob0.data.materials:
-                try:
-                    o = bpy.data.objects[m.name]
-                    allow_multi = True
-                    try:
-                        if o.data.shape_keys is None: continue
-                        elif len(o.data.shape_keys.key_blocks) < 2: continue
-                        else: allow_shapekeys = not props.com_modifiers
-                    except: pass
-                except: pass
-            # LIMITED TESSELLATION
-            col = layout.column(align=True)
-            #col.label(text="Limited Tessellation:")
-            row = col.row(align=True)
-            col2 = row.column(align=True)
-            col2.prop(props, "bool_selection", text="On selected Faces", icon='RESTRICT_SELECT_OFF')
-            row.separator()
-            if props.generator.type != 'MESH':
-                col2.enabled = False
-            col2 = row.column(align=True)
-            col2.prop(props, "bool_material_id", icon='MATERIAL_DATA', text="Material ID")
-            if props.bool_material_id and not props.bool_multi_components:
-                #col2 = row.column(align=True)
-                col2.prop(props, "material_id")
-            if props.bool_multi_components:
-                col2.enabled = False
-
-            col.separator()
-            row = col.row(align=True)
-            col2 = row.column(align=True)
-            col2.prop(props, "bool_multi_components", icon='MOD_TINT')
-            if not allow_multi:
-                col2.enabled = False
-
-
-class TISSUE_PT_tessellate_iterations(Panel):
-    bl_space_type = 'PROPERTIES'
-    bl_region_type = 'WINDOW'
-    bl_context = "data"
-    bl_parent_id = "TISSUE_PT_tessellate_object"
-    bl_label = "Iterations"
-    bl_options = {'DEFAULT_CLOSED'}
-
-    @classmethod
-    def poll(cls, context):
-        try:
-            bool_tessellated = context.object.tissue_tessellate.generator != None
-            return context.object.type == 'MESH' and bool_tessellated
-        except:
-            return False
-
-    def draw(self, context):
-        ob = context.object
-        props = ob.tissue_tessellate
-        allowed_obj = ('MESH','CURVE','SURFACE','FONT', 'META')
-
-        try:
-            bool_tessellated = props.generator or props.component != None
-            ob0 = props.generator
-            ob1 = props.component
-        except: bool_tessellated = False
-        layout = self.layout
-        layout.use_property_split = True
-        layout.use_property_decorate = False  # No animation.
-        if bool_tessellated:
-            col = layout.column(align=True)
-            row = col.row(align=True)
-            #row.label(text='', icon='FILE_REFRESH')
-            col.prop(props, 'iterations', text='Repeat')#, icon='FILE_REFRESH')
-            if props.iterations > 1 and props.fill_mode == 'PATCH':
-                col.separator()
-                #row = col.row(align=True)
-                col.prop(props, 'patch_subs')
-            layout.use_property_split = False
-            col = layout.column(align=True)
-            #row = col.row(align=True)
-            col.label(text='Combine Iterations:')
-            row = col.row(align=True)
-            row.prop(
-                props, "combine_mode", text="Combine:",icon='NONE', expand=True,
-                slider=False, toggle=False, icon_only=False, event=False,
-                full_event=False, emboss=True, index=-1)
-
-class tissue_rotate_face_right(Operator):
-    bl_idname = "mesh.tissue_rotate_face_right"
-    bl_label = "Rotate Faces Right"
-    bl_description = "Rotate clockwise selected faces and update tessellated meshes"
-    bl_options = {'REGISTER', 'UNDO'}
-
-    @classmethod
-    def poll(cls, context):
-        try:
-            #bool_tessellated = context.object.tissue_tessellate.generator != None
-            ob = context.object
-            return ob.type == 'MESH' and ob.mode == 'EDIT'# and bool_tessellated
-        except:
-            return False
-
-    def execute(self, context):
-        ob = context.active_object
-        me = ob.data
-
-        bm = bmesh.from_edit_mesh(me)
-        mesh_select_mode = [sm for sm in context.tool_settings.mesh_select_mode]
-
-        for face in bm.faces:
-            if (face.select):
-                vs = face.verts[:]
-                vs2 = vs[-1:]+vs[:-1]
-                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()
-
-        # trigger UI update
-        bmesh.update_edit_mesh(me)
-        ob.select_set(False)
-
-        # update tessellated meshes
-        bpy.ops.object.mode_set(mode='OBJECT')
-        for o in [obj for obj in bpy.data.objects if
-                  obj.tissue_tessellate.generator == ob and obj.visible_get()]:
-            context.view_layer.objects.active = o
-            bpy.ops.object.tissue_update_tessellate()
-            o.select_set(False)
-        ob.select_set(True)
-        context.view_layer.objects.active = ob
-        bpy.ops.object.mode_set(mode='EDIT')
-        context.tool_settings.mesh_select_mode = mesh_select_mode
-
-        return {'FINISHED'}
-
-class tissue_rotate_face_left(Operator):
-    bl_idname = "mesh.tissue_rotate_face_left"
-    bl_label = "Rotate Faces Left"
-    bl_description = "Rotate counterclockwise selected faces and update tessellated meshes"
-    bl_options = {'REGISTER', 'UNDO'}
-
-    @classmethod
-    def poll(cls, context):
-        try:
-            #bool_tessellated = context.object.tissue_tessellate.generator != None
-            ob = context.object
-            return ob.type == 'MESH' and ob.mode == 'EDIT'# and bool_tessellated
-        except:
-            return False
-
-    def execute(self, context):
-        ob = context.active_object
-        me = ob.data
-
-        bm = bmesh.from_edit_mesh(me)
-        mesh_select_mode = [sm for sm in context.tool_settings.mesh_select_mode]
-
-        for face in bm.faces:
-            if (face.select):
-                vs = face.verts[:]
-                vs2 = vs[1:]+vs[:1]
-                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()
-
-        # trigger UI update
-        bmesh.update_edit_mesh(me)
-        ob.select_set(False)
-
-        # update tessellated meshes
-        bpy.ops.object.mode_set(mode='OBJECT')
-        for o in [obj for obj in bpy.data.objects if
-                  obj.tissue_tessellate.generator == ob and obj.visible_get()]:
-            context.view_layer.objects.active = o
-            bpy.ops.object.tissue_update_tessellate()
-            o.select_set(False)
-        ob.select_set(True)
-        context.view_layer.objects.active = ob
-        bpy.ops.object.mode_set(mode='EDIT')
-        context.tool_settings.mesh_select_mode = mesh_select_mode
-
-        return {'FINISHED'}
-
-
-def convert_to_frame(ob, props, use_modifiers):
-    new_ob = convert_object_to_mesh(ob, use_modifiers, True)
-
-    # create bmesh
-    bm = bmesh.new()
-    bm.from_mesh(new_ob.data)
-    bm.verts.ensure_lookup_table()
-    bm.edges.ensure_lookup_table()
-    bm.faces.ensure_lookup_table()
-    if props.bool_selection:
-        original_faces = [f for f in bm.faces if f.select]
-    else:
-        original_faces = list(bm.faces)
-    # detect edge loops
-
-    loops = []
-    boundaries_mat = []
-    neigh_face_center = []
-    face_normals = []
-    # append boundary loops
-    if props.frame_boundary:
-        #selected_edges = [e for e in bm.edges if e.select]
-        selected_edges = [e for e in bm.edges if e.is_boundary]
-        if len(selected_edges) > 0:
-            loop = []
-            count = 0
-            e0 = selected_edges[0]
-            face = e0.link_faces[0]
-            boundary_mat = [face.material_index]
-            face_center = [face.calc_center_median()]
-            loop_normals = [face.normal]
-            selected_edges = selected_edges[1:]
-            if props.bool_vertex_group:
-                n_verts = len(new_ob.data.vertices)
-                base_vg = [get_weight(vg,n_verts) for vg in new_ob.vertex_groups]
-                '''
-                base_vg = []
-                for vg in new_ob.vertex_groups:
-                    vertex_group = []
-                    for v in bm.verts:
-                        try:
-                            vertex_group.append(vg.weight(v.index))
-                        except:
-                            vertex_group.append(0)
-                    base_vg.append(vertex_group)
-                '''
-            while True:
-                new_vert = None
-                face = None
-                for e1 in selected_edges:
-                    if e1.verts[0] in e0.verts: new_vert = e1.verts[1]
-                    elif e1.verts[1] in e0.verts: new_vert = e1.verts[0]
-                    if new_vert != None:
-                        if len(loop)==0:
-                            loop = [v for v in e1.verts if v != new_vert]
-                        loop.append(new_vert)
-                        e0 = e1
-                        face = e0.link_faces[0]
-                        boundary_mat.append(face.material_index)
-                        face_center.append(face.calc_center_median())
-                        loop_normals.append(face.normal)
-                        selected_edges.remove(e0)
-                        break
-                if new_vert == None:
-                    try:
-                        loops.append(loop)
-                        loop = []
-                        e0 = selected_edges[0]
-                        selected_edges = selected_edges[1:]
-                        boundaries_mat.append(boundary_mat)
-                        neigh_face_center.append(face_center)
-                        face_normals.append(loop_normals)
-                        face = e0.link_faces[0]
-                        boundary_mat = [face.material_index]
-                        face_center = [face.calc_center_median()]
-                        loop_normals = [face.normal]
-                    except: break
-            boundaries_mat.append(boundary_mat)
-            neigh_face_center.append(face_center)
-            face_normals.append(loop_normals)
-    # compute boundary frames
-    new_faces = []
-    vert_ids = []
-
-    # append regular faces
-    for f in original_faces:#bm.faces:
-        loop = list(f.verts)
-        loops.append(loop)
-        boundaries_mat.append([f.material_index for v in loop])
-        face_normals.append([f.normal for v in loop])
-
-    # calc areas for relative frame mode
-    if props.frame_mode == 'RELATIVE':
-        verts_area = []
-        for v in bm.verts:
-            linked_faces = v.link_faces
-            if len(linked_faces) > 0:
-                area = sum([sqrt(f.calc_area())/len(f.verts) for f in v.link_faces])*2
-                area /= len(linked_faces)
-            else: area = 0
-            verts_area.append(area)
-
-    for loop_index, loop in enumerate(loops):
-        is_boundary = loop_index < len(neigh_face_center)
-        materials = boundaries_mat[loop_index]
-        new_loop = []
-        loop_ext = [loop[-1]] + loop + [loop[0]]
-
-        # calc tangents
-        tangents = []
-        for i in range(len(loop)):
-            # vertices
-            vert0 = loop_ext[i]
-            vert = loop_ext[i+1]
-            vert1 = loop_ext[i+2]
-            # edge vectors
-            vec0 = (vert0.co - vert.co).normalized()
-            vec1 = (vert.co - vert1.co).normalized()
-            # tangent
-            _vec1 = -vec1
-            _vec0 = -vec0
-            ang = (pi - vec0.angle(vec1))/2
-            normal = face_normals[loop_index][i]
-            tan0 = normal.cross(vec0)
-            tan1 = normal.cross(vec1)
-            tangent = (tan0 + tan1).normalized()/sin(ang)*props.frame_thickness
-            tangents.append(tangent)
-
-        # calc correct direction for boundaries
-        mult = -1
-        if is_boundary:
-            dir_val = 0
-            for i in range(len(loop)):
-                surf_point = neigh_face_center[loop_index][i]
-                tangent = tangents[i]
-                vert = loop_ext[i+1]
-                dir_val += tangent.dot(vert.co - surf_point)
-            if dir_val > 0: mult = 1
-
-        # add vertices
-        for i in range(len(loop)):
-            vert = loop_ext[i+1]
-            if props.frame_mode == 'RELATIVE': area = verts_area[vert.index]
-            else: area = 1
-            new_co = vert.co + tangents[i] * mult * area
-            # add vertex
-            new_vert = bm.verts.new(new_co)
-            new_loop.append(new_vert)
-            vert_ids.append(vert.index)
-        new_loop.append(new_loop[0])
-
-        # add faces
-        materials += [materials[0]]
-        for i in range(len(loop)):
-             v0 = loop_ext[i+1]
-             v1 = loop_ext[i+2]
-             v2 = new_loop[i+1]
-             v3 = new_loop[i]
-             face_verts = [v1,v0,v3,v2]
-             if mult == -1: face_verts = [v0,v1,v2,v3]
-             new_face = bm.faces.new(face_verts)
-             new_face.material_index = materials[i+1] + props.frame_boundary_mat
-             new_face.select = True
-             new_faces.append(new_face)
-        # fill frame
-        if props.fill_frame and not is_boundary:
-            n_verts = len(new_loop)-1
-            loop_center = Vector((0,0,0))
-            for v in new_loop[1:]: loop_center += v.co
-            loop_center /= n_verts
-            center = bm.verts.new(loop_center)
-            for i in range(n_verts):
-                v0 = new_loop[i+1]
-                v1 = new_loop[i]
-                face_verts = [v1,v0,center]
-                new_face = bm.faces.new(face_verts)
-                new_face.material_index = materials[i] + props.frame_boundary_mat
-                new_face.select = True
-                new_faces.append(new_face)
-    bpy.ops.object.mode_set(mode='OBJECT')
-    #for f in bm.faces: f.select_set(f not in new_faces)
-    for f in original_faces: bm.faces.remove(f)
-    bm.to_mesh(new_ob.data)
-    # propagate vertex groups
-    if props.bool_vertex_group:
-        base_vg = []
-        for vg in new_ob.vertex_groups:
-            vertex_group = []
-            for v in bm.verts:
-                try:
-                    vertex_group.append(vg.weight(v.index))
-                except:
-                    vertex_group.append(0)
-            base_vg.append(vertex_group)
-        new_vert_ids = range(len(bm.verts)-len(vert_ids),len(bm.verts))
-        for vg_id, vg in enumerate(new_ob.vertex_groups):
-            for ii, jj in zip(vert_ids, new_vert_ids):
-                vg.add([jj], base_vg[vg_id][ii], 'REPLACE')
-    new_ob.data.update()
-    return new_ob
-
-def convert_to_fan(ob, props, use_modifiers):
-    new_ob = convert_object_to_mesh(ob, use_modifiers, True)
-    # make base object selected and active
-    for o in bpy.context.view_layer.objects: o.select_set(False)
-    new_ob.select_set(True)
-    bpy.context.view_layer.objects.active = new_ob
-    sk_index0 = new_ob.active_shape_key_index
-    new_ob.active_shape_key_index = 0
-
-    bpy.ops.object.mode_set(mode='EDIT')
-    bpy.ops.mesh.select_mode(type='FACE')
-    if not props.bool_selection:
-        bpy.ops.mesh.select_all(action='SELECT')
-    bpy.ops.mesh.poke()
-    bpy.ops.object.mode_set(mode='OBJECT')
-    return new_ob