Skip to content
Snippets Groups Projects
model_primitives_topology.py 40.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • # SPDX-License-Identifier: GPL-2.0-or-later
    
    """ Get POV-Ray specific objects In and Out of Blender """
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    from math import pi, cos, sin
    
    import os.path
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    import bpy
    from bpy_extras.object_utils import object_data_add
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    from bpy.utils import register_class, unregister_class
    
    from bpy.types import Operator
    
        StringProperty,
        BoolProperty,
        IntProperty,
        FloatProperty,
        FloatVectorProperty,
        EnumProperty,
    
    from mathutils import Vector, Matrix
    
    
    from . import model_primitives
    
    class POV_OT_lathe_add(Operator):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Add the representation of POV lathe using a screw modifier."""
    
        bl_idname = "pov.addlathe"
        bl_label = "Lathe"
        bl_description = "adds lathe"
    
        COMPAT_ENGINES = {"POVRAY_RENDER"}
    
    
            # ayers=[False]*20
            # layers[0]=True
    
    Campbell Barton's avatar
    Campbell Barton committed
            bpy.ops.curve.primitive_bezier_curve_add(
    
                location=context.scene.cursor.location,
    
    Campbell Barton's avatar
    Campbell Barton committed
                rotation=(0, 0, 0),
    
            ob = context.view_layer.objects.active
    
    Campbell Barton's avatar
    Campbell Barton committed
            ob_data = ob.data
            ob.name = ob_data.name = "PovLathe"
    
            ob_data.dimensions = "2D"
            ob_data.transform(Matrix.Rotation(-pi / 2.0, 4, "Z"))
            ob.pov.object_as = "LATHE"
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            self.report(
    
                {"INFO"}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode"
    
            bpy.ops.object.modifier_add(type="SCREW")
    
    Campbell Barton's avatar
    Campbell Barton committed
            mod = ob.modifiers[-1]
    
    Campbell Barton's avatar
    Campbell Barton committed
            mod.show_render = False
    
            ob.update_tag() # as prop set via python not updated in depsgraph
    
        """Create the proxy mesh of a POV superellipsoid using pov_superellipsoid_define()."""
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        if op:
            mesh = None
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            u = op.se_u
            v = op.se_v
            n1 = op.se_n1
            n2 = op.se_n2
            edit = op.se_edit
    
            se_param1 = n2  # op.se_param1
            se_param2 = n1  # op.se_param2
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        else:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            mesh = ob.data
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            u = ob.pov.se_u
            v = ob.pov.se_v
            n1 = ob.pov.se_n1
            n2 = ob.pov.se_n2
            edit = ob.pov.se_edit
            se_param1 = ob.pov.se_param1
            se_param2 = ob.pov.se_param2
    
        verts = []
    
        stepSegment = 360 / v * pi / 180
        stepRing = pi / u
        angSegment = 0
        angRing = -pi / 2
    
        step = 0
        for ring in range(0, u - 1):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            angRing += stepRing
    
            for segment in range(0, v):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                step += 1
                angSegment += stepSegment
    
                x = r * (abs(cos(angRing)) ** n1) * (abs(cos(angSegment)) ** n2)
    
                if (cos(angRing) < 0 < cos(angSegment)) or (cos(angRing) > 0 > cos(angSegment)):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    x = -x
    
                y = r * (abs(cos(angRing)) ** n1) * (abs(sin(angSegment)) ** n2)
    
                if (cos(angRing) < 0 < sin(angSegment)) or (cos(angRing) > 0 > sin(angSegment)):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    y = -y
    
                z = r * (abs(sin(angRing)) ** n1)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                if sin(angRing) < 0:
                    z = -z
    
                x = round(x, 4)
                y = round(y, 4)
                z = round(z, 4)
                verts.append((x, y, z))
    
        if edit == "TRIANGLES":
            verts.extend([(0, 0, 1),(0, 0, -1)])
    
        for i in range(0, u - 2):
            m = i * v
            for p in range(0, v):
                if p < v - 1:
                    face = (m + p, 1 + m + p, v + 1 + m + p, v + m + p)
                if p == v - 1:
                    face = (m + p, m, v + m, v + m + p)
    
            indexUp = len(verts) - 2
            indexDown = len(verts) - 1
            indexStartDown = len(verts) - 2 - v
            for i in range(0, v):
                if i < v - 1:
                    face = (indexDown, i, i + 1)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    faces.append(face)
    
                if i == v - 1:
                    face = (indexDown, i, 0)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    faces.append(face)
    
            for i in range(0, v):
                if i < v - 1:
                    face = (indexUp, i + indexStartDown, i + indexStartDown + 1)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    faces.append(face)
    
                if i == v - 1:
                    face = (indexUp, i + indexStartDown, indexStartDown)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    faces.append(face)
    
        if edit == "NGONS":
            face = list(range(v))
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            faces.append(face)
    
            face = []
            indexUp = len(verts) - 1
            for i in range(0, v):
                face.append(indexUp - i)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            faces.append(face)
    
        mesh = model_primitives.pov_define_mesh(mesh, verts, [], faces, "SuperEllipsoid")
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        if not ob:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            ob = object_data_add(context, mesh, operator=None)
    
            # engine = context.scene.render.engine what for?
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            ob = context.object
    
            ob.name = ob.data.name = "PovSuperellipsoid"
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            ob.pov.se_param1 = n2
            ob.pov.se_param2 = n1
    
            ob.pov.se_u = u
            ob.pov.se_v = v
            ob.pov.se_n1 = n1
            ob.pov.se_n2 = n2
            ob.pov.se_edit = edit
    
            bpy.ops.object.mode_set(mode="EDIT")
            bpy.ops.mesh.hide(unselected=False)
            bpy.ops.object.mode_set(mode="OBJECT")
    
            ob.data.auto_smooth_angle = 1.3
            bpy.ops.object.shade_smooth()
            ob.pov.object_as = "SUPERELLIPSOID"
    
            ob.update_tag() # as prop set via python not updated in depsgraph
    
    class POV_OT_superellipsoid_add(Operator):
    
        """Add the representation of POV superellipsoid using the pov_superellipsoid_define()."""
    
        bl_idname = "pov.addsuperellipsoid"
        bl_label = "Add SuperEllipsoid"
        bl_description = "Create a SuperEllipsoid"
        bl_options = {'REGISTER', 'UNDO'}
    
        COMPAT_ENGINES = {"POVRAY_RENDER"}
    
        # Keep in sync within model_properties.py section Superellipsoid
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        # as this allows interactive update
    
        #     If someone knows how to define operators' props from a func, I'd be delighted to learn it!
    
        # XXX ARE the first two used for import ? could we hide or suppress them otherwise?
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        se_param1: FloatProperty(name="Parameter 1", description="", min=0.00, max=10.0, default=0.04)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        se_param2: FloatProperty(name="Parameter 2", description="", min=0.00, max=10.0, default=0.04)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            name="U-segments", description="radial segmentation", default=20, min=4, max=265
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            name="V-segments", description="lateral segmentation", default=20, min=4, max=265
    
        )
        se_n1: FloatProperty(
            name="Ring manipulator",
            description="Manipulates the shape of the Ring",
            default=1.0,
            min=0.01,
            max=100.0,
        )
        se_n2: FloatProperty(
            name="Cross manipulator",
            description="Manipulates the shape of the cross-section",
            default=1.0,
            min=0.01,
            max=100.0,
        )
        se_edit: EnumProperty(
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            items=[("NOTHING", "Nothing", ""), ("NGONS", "N-Gons", ""), ("TRIANGLES", "Triangles", "")],
    
            name="Fill up and down",
            description="",
    
    
        @classmethod
        def poll(cls, context):
            engine = context.scene.render.engine
    
            return engine in cls.COMPAT_ENGINES
    
        def execute(self, context):
    
                {"INFO"}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode"
    
    class POV_OT_superellipsoid_update(Operator):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Update the superellipsoid.
    
        Delete its previous proxy geometry and rerun pov_superellipsoid_define() function
        with the new parameters"""
    
    
        bl_idname = "pov.superellipsoid_update"
        bl_label = "Update"
        bl_description = "Update Superellipsoid"
        bl_options = {'REGISTER', 'UNDO'}
    
        COMPAT_ENGINES = {"POVRAY_RENDER"}
    
    
        @classmethod
        def poll(cls, context):
            engine = context.scene.render.engine
            ob = context.object
    
            return ob and ob.data and ob.type == "MESH" and engine in cls.COMPAT_ENGINES
    
    
        def execute(self, context):
            bpy.ops.object.mode_set(mode="EDIT")
            bpy.ops.mesh.reveal()
    
            bpy.ops.mesh.select_all(action="SELECT")
            bpy.ops.mesh.delete(type="VERT")
    
            bpy.ops.object.mode_set(mode="OBJECT")
    
            pov_superellipsoid_define(context, None, context.object)
    
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    def create_faces(vert_idx_1, vert_idx_2, closed=False, flipped=False):
        """Generate viewport proxy mesh data for some pov primitives"""
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        if not vert_idx_1 or not vert_idx_2:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        if len(vert_idx_1) < 2 and len(vert_idx_2) < 2:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        if len(vert_idx_1) != len(vert_idx_2):
            if len(vert_idx_1) == 1 and len(vert_idx_2) > 1:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        total = len(vert_idx_2)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                face = [vert_idx_1[0], vert_idx_2[0], vert_idx_2[total - 1]]
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    face.append(vert_idx_1[total - 1])
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                face = [vert_idx_2[0], vert_idx_1[0]]
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    face.append(vert_idx_1[total - 1])
                face.append(vert_idx_2[total - 1])
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    face = [vert_idx_2[num], vert_idx_1[0], vert_idx_2[num + 1]]
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    face = [vert_idx_2[num], vert_idx_1[num], vert_idx_1[num + 1], vert_idx_2[num + 1]]
    
            elif fan:
                face = [vert_idx_1[0], vert_idx_2[num], vert_idx_2[num + 1]]
    
                face = [vert_idx_1[num], vert_idx_2[num], vert_idx_2[num + 1], vert_idx_1[num + 1]]
            faces.append(face)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Workaround to negative a, where the math.pow() method would return a ValueError."""
    
        return -((-a) ** b) if a < 0 else a**b
    
    
    def supertoroid(R, r, u, v, n1, n2):
        a = 2 * pi / u
        b = 2 * pi / v
    
            s = power(sin(i * a), n1)
            c = power(cos(i * a), n1)
    
                c2 = R + r * power(cos(j * b), n2)
                s2 = r * power(sin(j * b), n2)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                verts.append((c * c2, s * c2, s2))  # type as a (mathutils.Vector(c*c2,s*c2,s2))?
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                f = create_faces(range((i - 1) * v, i * v), range(i * v, (i + 1) * v), closed=True)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        f = create_faces(range((u - 1) * v, u * v), range(v), closed=True)
    
    def pov_supertorus_define(context, op, ob):
    
        """Get POV supertorus properties from operator (object creation/import) or data update."""
    
        if op:
            mesh = None
            st_R = op.st_R
            st_r = op.st_r
            st_u = op.st_u
            st_v = op.st_v
            st_n1 = op.st_n1
            st_n2 = op.st_n2
            st_ie = op.st_ie
            st_edit = op.st_edit
    
        else:
            assert ob
            mesh = ob.data
            st_R = ob.pov.st_major_radius
            st_r = ob.pov.st_minor_radius
            st_u = ob.pov.st_u
            st_v = ob.pov.st_v
            st_n1 = ob.pov.st_ring
            st_n2 = ob.pov.st_cross
            st_ie = ob.pov.st_ie
            st_edit = ob.pov.st_edit
    
        if st_ie:
            rad1 = (st_R + st_r) / 2
            rad2 = (st_R - st_r) / 2
            if rad2 > rad1:
                [rad1, rad2] = [rad2, rad1]
        else:
            rad1 = st_R
            rad2 = st_r
            if rad2 > rad1:
                rad1 = rad2
        verts, faces = supertoroid(rad1, rad2, st_u, st_v, st_n1, st_n2)
    
        mesh = model_primitives.pov_define_mesh(mesh, verts, [], faces, "PovSuperTorus", True)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            ob = object_data_add(context, mesh, operator=None)
    
            ob.pov.object_as = "SUPERTORUS"
    
            ob.pov.st_major_radius = st_R
            ob.pov.st_minor_radius = st_r
            ob.pov.st_u = st_u
            ob.pov.st_v = st_v
            ob.pov.st_ring = st_n1
            ob.pov.st_cross = st_n2
            ob.pov.st_ie = st_ie
            ob.pov.st_edit = st_edit
    
            ob.update_tag() # as prop set via python not updated in depsgraph
    
    class POV_OT_supertorus_add(Operator):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Add the representation of POV supertorus using the pov_supertorus_define() function."""
    
    
        bl_idname = "pov.addsupertorus"
        bl_label = "Add Supertorus"
        bl_description = "Create a SuperTorus"
        bl_options = {'REGISTER', 'UNDO'}
    
        COMPAT_ENGINES = {"POVRAY_RENDER"}
    
        st_R: FloatProperty(
            name="big radius",
            description="The radius inside the tube",
            default=1.0,
            min=0.01,
            max=100.0,
        )
        st_r: FloatProperty(
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            name="small radius", description="The radius of the tube", default=0.3, min=0.01, max=100.0
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            name="U-segments", description="radial segmentation", default=16, min=3, max=265
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            name="V-segments", description="lateral segmentation", default=8, min=3, max=265
    
        )
        st_n1: FloatProperty(
            name="Ring manipulator",
            description="Manipulates the shape of the Ring",
            default=1.0,
            min=0.01,
            max=100.0,
        )
        st_n2: FloatProperty(
            name="Cross manipulator",
            description="Manipulates the shape of the cross-section",
            default=1.0,
            min=0.01,
            max=100.0,
        )
        st_ie: BoolProperty(
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            name="Use Int.+Ext. radii", description="Use internal and external radii", default=False
    
        st_edit: BoolProperty(name="", description="", default=False, options={"HIDDEN"})
    
    
        @classmethod
        def poll(cls, context):
            engine = context.scene.render.engine
    
            return engine in cls.COMPAT_ENGINES
    
    
        def execute(self, context):
            pov_supertorus_define(context, self, None)
    
    
                {"INFO"}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode"
    
    class POV_OT_supertorus_update(Operator):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Update the supertorus.
    
        Delete its previous proxy geometry and rerun pov_supetorus_define() function
        with the new parameters"""
    
        bl_idname = "pov.supertorus_update"
        bl_label = "Update"
        bl_description = "Update SuperTorus"
        bl_options = {'REGISTER', 'UNDO'}
    
        COMPAT_ENGINES = {"POVRAY_RENDER"}
    
    
        @classmethod
        def poll(cls, context):
            engine = context.scene.render.engine
            ob = context.object
    
            return ob and ob.data and ob.type == "MESH" and engine in cls.COMPAT_ENGINES
    
    
        def execute(self, context):
            bpy.ops.object.mode_set(mode="EDIT")
            bpy.ops.mesh.reveal()
    
            bpy.ops.mesh.select_all(action="SELECT")
            bpy.ops.mesh.delete(type="VERT")
    
            bpy.ops.object.mode_set(mode="OBJECT")
    
            pov_supertorus_define(context, None, context.object)
    
    
    # -----------------------------------------------------------------------------
    
    class POV_OT_loft_add(Operator):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Create the representation of POV loft using Blender curves."""
    
    
        bl_idname = "pov.addloft"
        bl_label = "Add Loft Data"
        bl_description = "Create a Curve data for Meshmaker"
        bl_options = {'REGISTER', 'UNDO'}
    
        COMPAT_ENGINES = {"POVRAY_RENDER"}
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            name="Segments", description="Vertical segments", default=16, min=3, max=720
    
        )
        loft_rings_bottom: IntProperty(
            name="Bottom", description="Bottom rings", default=5, min=2, max=100
        )
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        loft_rings_side: IntProperty(name="Side", description="Side rings", default=10, min=2, max=100)
    
        loft_thick: FloatProperty(
            name="Thickness",
            description="Manipulates the shape of the Ring",
            default=0.3,
            min=0.01,
            max=1.0,
        )
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        loft_r: FloatProperty(name="Radius", description="Radius", default=1, min=0.01, max=10)
    
        loft_height: FloatProperty(
            name="Height",
            description="Manipulates the shape of the Ring",
            default=2,
            min=0.01,
            max=10.0,
        )
    
        def execute(self, context):
    
            loft_data = bpy.data.curves.new("Loft", type="CURVE")
            loft_data.dimensions = "3D"
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            loft_data.resolution_u = 2
            # loft_data.show_normal_face = False # deprecated in 2.8
    
            thick = props.loft_thick
            side = props.loft_rings_side
            bottom = props.loft_rings_bottom
            h = props.loft_height
            r = props.loft_r
    
            for i in range(bottom + 1):
    
                    x = r0 * cos(angle)
                    y = r0 * sin(angle)
                    coords.append((x, y, z))
                    angle += pi * 2 / n
                r0 += distB
    
                nurbs = loft_data.splines.new("NURBS")
    
                nurbs.points.add(len(coords) - 1)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                for c, coord in enumerate(coords):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    nurbs.points[c].co = (x, y, z, 1)
    
                    x = r * cos(angle)
                    y = r * sin(angle)
                    coords.append((x, y, z))
                    angle += pi * 2 / n
    
                nurbs = loft_data.splines.new("NURBS")
    
                nurbs.points.add(len(coords) - 1)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                for c, coord in enumerate(coords):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    nurbs.points[c].co = (x, y, z, 1)
    
                    x = r * cos(angle)
                    y = r * sin(angle)
                    coords.append((x, y, z))
                    angle += pi * 2 / n
    
                nurbs = loft_data.splines.new("NURBS")
    
                nurbs.points.add(len(coords) - 1)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                for c, coord in enumerate(coords):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    nurbs.points[c].co = (x, y, z, 1)
    
                z -= h / side
            z = (-h / 2) + thick
            distB = (r - 0.00001) / bottom
            for i in range(bottom + 1):
    
                    x = r * cos(angle)
                    y = r * sin(angle)
                    coords.append((x, y, z))
                    angle += pi * 2 / n
                r -= distB
    
                nurbs = loft_data.splines.new("NURBS")
    
                nurbs.points.add(len(coords) - 1)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                for c, coord in enumerate(coords):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    nurbs.points[c].co = (x, y, z, 1)
    
            ob = bpy.data.objects.new("Loft_shape", loft_data)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            scn.collection.objects.link(ob)
    
            context.view_layer.objects.active = ob
    
            ob.select_set(True)
    
    # ----------------------------------- ISOSURFACES ----------------------------------- #
    
    def pov_isosurface_view_define(context, op, ob, loc):
        """create the representation of POV isosurface using a Blender empty."""
    
        if op:
            eq = op.isosurface_eq
    
            loc = bpy.context.scene.cursor.location
    
        else:
            assert ob
            eq = ob.pov.isosurface_eq
    
            # keep object rotation and location for the add object operator
            obrot = ob.rotation_euler
            # obloc = ob.location
            obscale = ob.scale
    
    
            # bpy.ops.object.empty_add(type='CUBE', location=loc, rotation=obrot)
    
            bpy.ops.mesh.primitive_emptyvert_add()
    
            # bpy.ops.transform.rotate(axis=obrot,orient_type='GLOBAL')
            bpy.ops.transform.resize(value=obscale)
            # bpy.ops.transform.rotate(axis=obrot, proportional_size=1)
            bpy.ops.object.mode_set(mode="OBJECT")
        if not ob:
    
            # bpy.ops.object.empty_add(type='CUBE', location=loc)
    
            bpy.ops.mesh.primitive_emptyvert_add()
            ob = context.object
            ob.name = ob.data.name = "PovIsosurface"
            ob.pov.object_as = "ISOSURFACE_VIEW"
            ob.pov.isosurface_eq = eq
    
            ob.update_tag() # as prop set via python not updated in depsgraph
    
    
    class POV_OT_isosurface_add(Operator):
    
        """Add the representation of POV isosurface sphere by a Blender mesh icosphere.
    
        Flag its primitive type with a specific pov.object_as attribute and lock edit mode
        to keep proxy consistency by hiding edit geometry."""
    
        bl_idname = "pov.addisosurface"
        bl_label = "Generic Isosurface"
        bl_description = "Add Isosurface"
        bl_options = {'REGISTER', 'UNDO'}
    
        COMPAT_ENGINES = {"POVRAY_RENDER"}
    
        # Keep in sync within model_properties.py section Sphere
    
        # as this allows interactive update
        isosurface_eq: StringProperty(
            name="f(x,y,z)=",
            description="Type the POV Isosurface function syntax for equation, "
            "pattern,etc. ruling an implicit surface to be rendered",
            default="sqrt(pow(x,2) + pow(y,2) + pow(z,2)) - 1.5",
        )
        imported_loc: FloatVectorProperty(
            name="Imported Pov location", precision=6, default=(0.0, 0.0, 0.0)
        )
    
        def execute(self, context):
            # layers = 20*[False]
            # layers[0] = True
            props = self.properties
    
                if ob.pov.imported_loc:
                    LOC = ob.pov.imported_loc
            elif not props.imported_loc:
                LOC = bpy.context.scene.cursor.location
            else:
                LOC = props.imported_loc
    
            try:
                pov_isosurface_view_define(context, self, None, LOC)
                self.report(
                    {"INFO"}, "This native POV-Ray primitive " "is only an abstract proxy in Blender"
                )
            except AttributeError:
                self.report({"INFO"}, "Please enable Add Mesh: Extra Objects addon")
            return {"FINISHED"}
    
    class POV_OT_isosurface_update(Operator):
    
        """Update the POV isosurface.
    
        Rerun pov_isosurface_view_define() function
        with the new parameters"""
    
        bl_idname = "pov.isosurface_update"
        bl_label = "Update"
        bl_description = "Update Isosurface"
        bl_options = {'REGISTER', 'UNDO'}
    
        COMPAT_ENGINES = {"POVRAY_RENDER"}
    
    
        @classmethod
        def poll(cls, context):
            engine = context.scene.render.engine
            ob = context.object
    
            return ob and ob.data and ob.type == "ISOSURFACE_VIEW" and engine in cls.COMPAT_ENGINES
    
    
        def execute(self, context):
    
            pov_isosurface_view_define(context, None, context.object, context.object.location)
    
    
    class POV_OT_isosurface_box_add(Operator):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Add the representation of POV isosurface box using also just a Blender mesh cube.
    
        Flag its primitive type with a specific pov.object_as attribute and lock edit mode
        to keep proxy consistency by hiding edit geometry."""
    
    
        bl_idname = "pov.addisosurfacebox"
        bl_label = "Isosurface Box"
        bl_description = "Add Isosurface contained by Box"
        bl_options = {'REGISTER', 'UNDO'}
    
        COMPAT_ENGINES = {"POVRAY_RENDER"}
    
        def execute(self, context):
            # layers = 20*[False]
            # layers[0] = True
    
            bpy.ops.mesh.primitive_cube_add()
    
            ob = context.object
            bpy.ops.object.mode_set(mode="EDIT")
    
                {"INFO"}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode"
    
            bpy.ops.mesh.hide(unselected=False)
            bpy.ops.object.mode_set(mode="OBJECT")
    
            ob.pov.contained_by = "box"
            ob.name = "PovIsosurfaceBox"
    
            ob.update_tag() # as prop set via python not updated in depsgraph
    
    class POV_OT_isosurface_sphere_add(Operator):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Add the representation of POV isosurface sphere by a Blender mesh icosphere.
    
        Flag its primitive type with a specific pov.object_as attribute and lock edit mode
        to keep proxy consistency by hiding edit geometry."""
    
    
        bl_idname = "pov.addisosurfacesphere"
        bl_label = "Isosurface Sphere"
        bl_description = "Add Isosurface contained by Sphere"
        bl_options = {'REGISTER', 'UNDO'}
    
        COMPAT_ENGINES = {"POVRAY_RENDER"}
    
        def execute(self, context):
            # layers = 20*[False]
            # layers[0] = True
    
            bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=4)
    
            ob = context.object
            bpy.ops.object.mode_set(mode="EDIT")
    
                {"INFO"}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode"
    
            bpy.ops.mesh.hide(unselected=False)
            bpy.ops.object.mode_set(mode="OBJECT")
            bpy.ops.object.shade_smooth()
    
            ob.pov.contained_by = "sphere"
            ob.name = "PovIsosurfaceSphere"
    
            ob.update_tag() # as prop set via python not updated in depsgraph
    
    class POV_OT_sphere_sweep_add(Operator):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Add the representation of POV sphere_sweep using a Blender NURBS curve.
    
        Flag its primitive type with a specific ob.pov.curveshape attribute and
        leave access to edit mode to keep user editable handles."""
    
    
        bl_idname = "pov.addspheresweep"
        bl_label = "Sphere Sweep"
        bl_description = "Create Sphere Sweep along curve"
        bl_options = {'REGISTER', 'UNDO'}
    
        COMPAT_ENGINES = {"POVRAY_RENDER"}
    
        def execute(self, context):
            # layers = 20*[False]
            # layers[0] = True
    
            bpy.ops.curve.primitive_nurbs_curve_add()
    
            ob = context.object
            ob.name = ob.data.name = "PovSphereSweep"
            ob.pov.curveshape = "sphere_sweep"
            ob.data.bevel_depth = 0.02
            ob.data.bevel_resolution = 4
    
            # ob.data.splines[0].order_u = 4
    
    class POV_OT_blobsphere_add(Operator):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Add the representation of POV blob using a Blender meta ball.
    
        No need to flag its primitive type as meta are exported to blobs
        and leave access to edit mode to keep user editable thresholds."""
    
    
        bl_idname = "pov.addblobsphere"
        bl_label = "Blob Sphere"
        bl_description = "Add Blob Sphere"
        bl_options = {'REGISTER', 'UNDO'}
    
        COMPAT_ENGINES = {"POVRAY_RENDER"}
    
        def execute(self, context):
            # layers = 20*[False]
            # layers[0] = True
    
            bpy.ops.object.metaball_add(type="BALL")
    
    class POV_OT_blobcapsule_add(Operator):
    
        """Add the representation of POV blob using a Blender meta ball.
    
        No need to flag its primitive type as meta are exported to blobs
        and leave access to edit mode to keep user editable thresholds."""
    
        bl_idname = "pov.addblobcapsule"
        bl_label = "Blob Capsule"
        bl_description = "Add Blob Capsule"
        bl_options = {'REGISTER', 'UNDO'}
    
        COMPAT_ENGINES = {"POVRAY_RENDER"}
    
    
        def execute(self, context):
            # layers = 20*[False]
            # layers[0] = True
    
            bpy.ops.object.metaball_add(type="CAPSULE")
    
    class POV_OT_blobplane_add(Operator):
    
        """Add the representation of POV blob using a Blender meta ball.
    
        No need to flag its primitive type as meta are exported to blobs
        and leave access to edit mode to keep user editable thresholds."""
    
        bl_idname = "pov.addblobplane"
        bl_label = "Blob Plane"
        bl_description = "Add Blob Plane"
        bl_options = {'REGISTER', 'UNDO'}
    
        COMPAT_ENGINES = {"POVRAY_RENDER"}
    
    
        def execute(self, context):
            # layers = 20*[False]
            # layers[0] = True
    
            bpy.ops.object.metaball_add(type="PLANE")
    
    class POV_OT_blobellipsoid_add(Operator):
    
        """Add the representation of POV blob using a Blender meta ball.
    
        No need to flag its primitive type as meta are exported to blobs
        and leave access to edit mode to keep user editable thresholds."""
    
        bl_idname = "pov.addblobellipsoid"
        bl_label = "Blob Ellipsoid"
        bl_description = "Add Blob Ellipsoid"
    
    
        def execute(self, context):
            # layers = 20*[False]
            # layers[0] = True
    
            bpy.ops.object.metaball_add(type="ELLIPSOID")
    
    class POV_OT_blobcube_add(Operator):
    
        """Add the representation of POV blob using a Blender meta ball.
    
        No need to flag its primitive type as meta are exported to blobs
        and leave access to edit mode to keep user editable thresholds."""
    
        bl_idname = "pov.addblobcube"
        bl_label = "Blob Cube"
        bl_description = "Add Blob Cube"
        bl_options = {'REGISTER', 'UNDO'}
    
        COMPAT_ENGINES = {"POVRAY_RENDER"}
    
    
        def execute(self, context):
            # layers = 20*[False]
            # layers[0] = True
    
            bpy.ops.object.metaball_add(type="CUBE")
    
    class POV_OT_height_field_add(bpy.types.Operator, ImportHelper):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Add the representation of POV height_field using a displaced grid.
    
        texture slot fix and displace modifier will be needed because noise
        displace operator was deprecated in 2.8"""
    
    
        bl_idname = "pov.addheightfield"
        bl_label = "Height Field"
    
        bl_description = "Add Height Field"
    
        COMPAT_ENGINES = {"POVRAY_RENDER"}
    
        # Keep in sync within model_properties.py section HeightFields
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        # as this allows interactive update
    
    
        # default="*.exr;*.gif;*.hdr;*.iff;*.jpeg;*.jpg;*.pgm;*.png;*.pot;*.ppm;*.sys;*.tga;*.tiff;*.EXR;*.GIF;*.HDR;*.IFF;*.JPEG;*.JPG;*.PGM;*.PNG;*.POT;*.PPM;*.SYS;*.TGA;*.TIFF",
        # options={'HIDDEN'},
        # )
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        quality: IntProperty(name="Quality", description="", default=100, min=1, max=100)
    
        hf_filename: StringProperty(maxlen=1024)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        hf_gamma: FloatProperty(name="Gamma", description="Gamma", min=0.0001, max=20.0, default=1.0)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        hf_premultiplied: BoolProperty(name="Premultiplied", description="Premultiplied", default=True)
    
        hf_smooth: BoolProperty(name="Smooth", description="Smooth", default=False)
    
        hf_water: FloatProperty(
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            name="Water Level", description="Wather Level", min=0.00, max=1.00, default=0.0
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        hf_hierarchy: BoolProperty(name="Hierarchy", description="Height field hierarchy", default=True)
    
    
        def execute(self, context):
    
            props = self.properties
            impath = bpy.path.abspath(self.filepath)
            img = bpy.data.images.load(impath)
            im_name = img.name
            im_name, file_extension = os.path.splitext(im_name)
    
            hf_tex = bpy.data.textures.new("%s_hf_image" % im_name, type="IMAGE")
    
            mat = bpy.data.materials.new("Tex_%s_hf" % im_name)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            hf_slot = mat.pov_texture_slots.add()
            hf_slot.texture = hf_tex.name
    
            # layers = 20*[False]
            # layers[0] = True
    
            res = 100 / quality
            w, h = hf_tex.image.size[:]
            w = int(w / res)
            h = int(h / res)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            bpy.ops.mesh.primitive_grid_add(x_subdivisions=w, y_subdivisions=h, size=0.5)
    
            ob.name = ob.data.name = "%s" % im_name
    
            ob.data.materials.append(mat)
            bpy.ops.object.mode_set(mode="EDIT")
    
            # bpy.ops.mesh.noise(factor=1) # TODO replace by displace modifier, noise deprecated in 2.8
    
            # needs a loop to select by index?
            # bpy.ops.object.material_slot_remove()
            # material just left there for now
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            mat.pov_texture_slots.clear()
    
            bpy.ops.object.mode_set(mode="EDIT")
            bpy.ops.mesh.hide(unselected=False)
            bpy.ops.object.mode_set(mode="OBJECT")
    
            ob.pov.object_as = "HEIGHT_FIELD"
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            # POV-Ray will soon use only forwards slashes on every OS and already can
    
            forward_impath = impath.replace(os.sep, "/")
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            ob.pov.hf_filename = forward_impath
    
            ob.update_tag() # as prop set via python not updated in depsgraph
    
    # ----------------------------------- PARAMETRIC ----------------------------------- #
    
    def pov_parametric_define(context, op, ob):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Add the representation of POV parametric surfaces by math surface from add mesh extra objects addon.
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        Picking properties either from creation operator, import, or data update.
        But flag its primitive type with a specific pov.object_as attribute and lock edit mode
        to keep proxy consistency by hiding edit geometry."""
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        if op:
            u_min = op.u_min
            u_max = op.u_max
            v_min = op.v_min
            v_max = op.v_max
            x_eq = op.x_eq
            y_eq = op.y_eq
            z_eq = op.z_eq
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        else:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            u_min = ob.pov.u_min
            u_max = ob.pov.u_max
            v_min = ob.pov.v_min
            v_max = ob.pov.v_max
            x_eq = ob.pov.x_eq
            y_eq = ob.pov.y_eq
            z_eq = ob.pov.z_eq
    
    
            # keep object rotation and location for the updated object
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            obloc = ob.location
    
            obrot = ob.rotation_euler  # In radians
            # Parametric addon has no loc rot, some extra work is needed