Skip to content
Snippets Groups Projects
object_primitives.py 63.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • # SPDX-License-Identifier: GPL-2.0-or-later
    
    
    # <pep8 compliant>
    
    
    """ 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
    
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Translate some object level POV statements from Blender UI
        to POV syntax and write to exported file """
    
        # Maybe return that string to be added instead of directly written.
    
        '''XXX WIP
    
        # import .object_mesh_topology.write_object_csg_inside_vector
        write_object_csg_inside_vector(ob, file)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        '''
    
        if ob.pov.hollow:
            File.write("\thollow\n")
        if ob.pov.double_illuminate:
            File.write("\tdouble_illuminate\n")
        if ob.pov.sturm:
            File.write("\tsturm\n")
        if ob.pov.no_shadow:
            File.write("\tno_shadow\n")
        if ob.pov.no_image:
            File.write("\tno_image\n")
        if ob.pov.no_reflection:
            File.write("\tno_reflection\n")
        if ob.pov.no_radiosity:
            File.write("\tno_radiosity\n")
        if ob.pov.inverse:
            File.write("\tinverse\n")
        if ob.pov.hierarchy:
            File.write("\thierarchy\n")
    
        # XXX, Commented definitions
        '''
        if scene.pov.photon_enable:
            File.write("photons {\n")
            if ob.pov.target:
                File.write("target %.4g\n"%ob.pov.target_value)
            if ob.pov.refraction:
                File.write("refraction on\n")
            if ob.pov.reflection:
                File.write("reflection on\n")
            if ob.pov.pass_through:
                File.write("pass_through\n")
            File.write("}\n")
        if ob.pov.object_ior > 1:
            File.write("interior {\n")
            File.write("ior %.4g\n"%ob.pov.object_ior)
            if scene.pov.photon_enable and ob.pov.target and ob.pov.refraction and ob.pov.dispersion:
                File.write("ior %.4g\n"%ob.pov.dispersion_value)
                File.write("ior %s\n"%ob.pov.dispersion_samples)
            if scene.pov.photon_enable == False:
                File.write("caustics %.4g\n"%ob.pov.fake_caustics_power)
        '''
    
    
    
    def pov_define_mesh(mesh, verts, edges, faces, name, hide_geometry=True):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Generate proxy mesh."""
    
        if mesh is None:
            mesh = bpy.data.meshes.new(name)
        mesh.from_pydata(verts, edges, faces)
        mesh.update()
    
        mesh.validate(
            verbose=False
        )  # Set it to True to see debug messages (helps ensure you generate valid geometry).
    
        if hide_geometry:
            mesh.vertices.foreach_set("hide", [True] * len(mesh.vertices))
            mesh.edges.foreach_set("hide", [True] * len(mesh.edges))
            mesh.polygons.foreach_set("hide", [True] * len(mesh.polygons))
        return mesh
    
    
    
    class POVRAY_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"
    
        bl_options = {'REGISTER', 'UNDO'}
        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"
            )
    
            ob.pov.curveshape = "lathe"
            bpy.ops.object.modifier_add(type='SCREW')
    
    Campbell Barton's avatar
    Campbell Barton committed
            mod = ob.modifiers[-1]
            mod.axis = 'Y'
            mod.show_render = False
    
            return {'FINISHED'}
    
    
    def pov_superellipsoid_define(context, op, ob):
    
        """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))
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        if edit == 'TRIANGLES':
    
            verts.append((0, 0, 1))
            verts.append((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)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        if edit == 'TRIANGLES':
    
            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 = []
            for i in range(0, v):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                face.append(i)
            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 = 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.object_as = 'SUPERELLIPSOID'
            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")
    
    class POVRAY_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'}
    
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        # Keep in sync within object_properties.py section Superellipsoid
        # 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="",
            default='TRIANGLES',
        )
    
    
        @classmethod
        def poll(cls, context):
            engine = context.scene.render.engine
    
            return engine in cls.COMPAT_ENGINES
    
        def execute(self, context):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                {'INFO'}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode"
    
    class POVRAY_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
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            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)
    
            return {'FINISHED'}
    
    
    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])
    
                faces.append(face)
        for num in range(total - 1):
            if flipped:
                if fan:
    
    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]]
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    face = [vert_idx_1[0], vert_idx_2[num], vert_idx_2[num + 1]]
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    face = [vert_idx_1[num], vert_idx_2[num], vert_idx_2[num + 1], vert_idx_1[num + 1]]
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Workaround to negative a, where the math.pow() method would return a ValueError."""
    
            return -((-a) ** b)
        return 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 = pov_define_mesh(mesh, verts, [], faces, "PovSuperTorus", True)
        if not ob:
    
    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
    
    class POVRAY_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
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        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)
    
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                {'INFO'}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode"
    
            return {'FINISHED'}
    
    class POVRAY_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
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            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)
    
            return {'FINISHED'}
    
    # -----------------------------------------------------------------------------
    
    class POVRAY_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):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            loft_data = bpy.data.curves.new('Loft', type='CURVE')
            loft_data.dimensions = '3D'
            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
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                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
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                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
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                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
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                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)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            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)
    
    class POVRAY_OT_plane_add(Operator):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Add the representation of POV infinite plane using just a very big Blender Plane.
    
        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.addplane"
        bl_label = "Plane"
        bl_description = "Add Plane"
        bl_options = {'REGISTER', 'UNDO'}
    
        def execute(self, context):
            # layers = 20*[False]
            # layers[0] = True
    
            bpy.ops.mesh.primitive_plane_add(size=10000)
    
            ob = context.object
            ob.name = ob.data.name = 'PovInfinitePlane'
            bpy.ops.object.mode_set(mode="EDIT")
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                {'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.object_as = "PLANE"
            return {'FINISHED'}
    
    class POVRAY_OT_box_add(Operator):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Add the representation of POV box using a simple 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.addbox"
        bl_label = "Box"
        bl_description = "Add Box"
        bl_options = {'REGISTER', 'UNDO'}
    
        def execute(self, context):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            # layers = 20*[False]
            # layers[0] = True
            bpy.ops.mesh.primitive_cube_add()
    
            ob = context.object
            ob.name = ob.data.name = 'PovBox'
            bpy.ops.object.mode_set(mode="EDIT")
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                {'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.object_as = "BOX"
            return {'FINISHED'}
    
    
    
    def pov_cylinder_define(context, op, ob, radius, loc, loc_cap):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Pick POV cylinder properties either from creation operator, import, or data update """
    
            loc = bpy.context.scene.cursor.location
    
            loc_cap[2] = loc[2] + 2
    
        vec = Vector(loc_cap) - Vector(loc)
        depth = vec.length
        rot = Vector((0, 0, 1)).rotation_difference(vec)  # Rotation from Z axis.
    
        trans = rot @ Vector(
            (0, 0, depth / 2)
        )  # Such that origin is at center of the base of the cylinder.
    
        roteuler = rot.to_euler()
        if not ob:
            bpy.ops.object.add(type='MESH', location=loc)
            ob = context.object
            ob.name = ob.data.name = "PovCylinder"
            ob.pov.cylinder_radius = radius
            ob.pov.cylinder_location_cap = vec
            ob.pov.object_as = "CYLINDER"
        else:
            ob.location = loc
    
        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.mesh.primitive_cylinder_add(
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            radius=radius, depth=depth, location=loc, rotation=roteuler, end_fill_type='NGON'
    
        )  # 'NOTHING'
    
        bpy.ops.transform.translate(value=trans)
    
        bpy.ops.mesh.hide(unselected=False)
        bpy.ops.object.mode_set(mode="OBJECT")
        bpy.ops.object.shade_smooth()
    
    
    
    class POVRAY_OT_cylinder_add(Operator):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Add the representation of POV cylinder using pov_cylinder_define() function.
    
        Use imported_cyl_loc when this operator is run by POV importer."""
    
        bl_idname = "pov.addcylinder"
        bl_label = "Cylinder"
        bl_description = "Add Cylinder"
        bl_options = {'REGISTER', 'UNDO'}
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        # Keep in sync within object_properties.py section Cylinder
        # as this allows interactive update
    
        R: FloatProperty(name="Cylinder radius", min=0.00, max=10.0, default=1.0)
    
        imported_cyl_loc: FloatVectorProperty(
    
            name="Imported Pov base location", precision=6, default=(0.0, 0.0, 0.0)
        )
    
        imported_cyl_loc_cap: FloatVectorProperty(
    
            name="Imported Pov cap location", precision=6, default=(0.0, 0.0, 2.0)
        )
    
        def execute(self, context):
    
            ob = context.object
    
            # layers = 20*[False]
            # layers[0] = True
    
            if ob:
                if ob.pov.imported_cyl_loc:
                    LOC = ob.pov.imported_cyl_loc
                if ob.pov.imported_cyl_loc_cap:
                    LOC_CAP = ob.pov.imported_cyl_loc_cap
    
            elif not props.imported_cyl_loc:
                LOC_CAP = LOC = bpy.context.scene.cursor.location
                LOC_CAP[2] += 2.0
    
                LOC = props.imported_cyl_loc
                LOC_CAP = props.imported_cyl_loc_cap
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    "This native POV-Ray primitive " "won't have any vertex to show in edit mode",
    
    
            pov_cylinder_define(context, self, None, self.R, LOC, LOC_CAP)
    
            return {'FINISHED'}
    
    
    
    class POVRAY_OT_cylinder_update(Operator):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Update the POV cylinder.
    
        Delete its previous proxy geometry and rerun pov_cylinder_define() function
        with the new parameters"""
    
    
        bl_idname = "pov.cylinder_update"
        bl_label = "Update"
        bl_description = "Update Cylinder"
        bl_options = {'REGISTER', 'UNDO'}
        COMPAT_ENGINES = {'POVRAY_RENDER'}
    
        @classmethod
        def poll(cls, context):
            engine = context.scene.render.engine
    
            return (
                ob
                and ob.data
                and ob.type == 'MESH'
                and ob.pov.object_as == "CYLINDER"
                and engine in cls.COMPAT_ENGINES
            )
    
    
        def execute(self, context):
            ob = context.object
            radius = ob.pov.cylinder_radius
            loc = ob.location
            loc_cap = loc + ob.pov.cylinder_location_cap
    
            pov_cylinder_define(context, None, ob, radius, loc, loc_cap)
    
    # ----------------------------------- SPHERE---------------------------------- #
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """create the representation of POV sphere using a Blender icosphere.
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        Its nice platonic solid curvature better represents pov rendertime
        tesselation than a UV sphere"""
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        if op:
            R = op.R
            loc = bpy.context.scene.cursor.location
        else:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            R = ob.pov.sphere_radius
    
            # keep object rotation and location for the add object operator
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            obrot = ob.rotation_euler
    
            # obloc = ob.location
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            obscale = ob.scale
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            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.mesh.primitive_ico_sphere_add(
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                subdivisions=4, radius=ob.pov.sphere_radius, location=loc, rotation=obrot
    
            )
            # bpy.ops.transform.rotate(axis=obrot,orient_type='GLOBAL')
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            bpy.ops.transform.resize(value=obscale)
    
            # bpy.ops.transform.rotate(axis=obrot, proportional_size=1)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
            bpy.ops.mesh.hide(unselected=False)
            bpy.ops.object.mode_set(mode="OBJECT")
            bpy.ops.object.shade_smooth()
    
            # bpy.ops.transform.rotate(axis=obrot,orient_type='GLOBAL')
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
        if not ob:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=4, radius=R, location=loc)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            ob = context.object
    
            ob.name = ob.data.name = "PovSphere"
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            ob.pov.object_as = "SPHERE"
            ob.pov.sphere_radius = R
            bpy.ops.object.mode_set(mode="EDIT")
            bpy.ops.mesh.hide(unselected=False)
            bpy.ops.object.mode_set(mode="OBJECT")
    
    class POVRAY_OT_sphere_add(Operator):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Add the representation of POV sphere using pov_sphere_define() function.
    
        Use imported_loc when this operator is run by POV importer."""
    
    
        bl_idname = "pov.addsphere"
        bl_label = "Sphere"
        bl_description = "Add Sphere Shape"
        bl_options = {'REGISTER', 'UNDO'}
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        # Keep in sync within object_properties.py section Sphere
        # as this allows interactive update
    
        R: FloatProperty(name="Sphere radius", min=0.00, max=10.0, default=0.5)
    
        imported_loc: FloatVectorProperty(
    
            name="Imported Pov location", precision=6, default=(0.0, 0.0, 0.0)
        )
    
        def execute(self, context):
    
            elif not props.imported_loc:
                LOC = bpy.context.scene.cursor.location
    
            else:
                LOC = props.imported_loc
                self.report(
                    {'INFO'},
                    "This native POV-Ray primitive " "won't have any vertex to show in edit mode",
                )
    
        #  layers = 20*[False]
        #  layers[0] = True
    
    
        # bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=4, radius=ob.pov.sphere_radius)
        # ob = context.object
        # bpy.ops.object.mode_set(mode="EDIT")
        # self.report({'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.object_as = "SPHERE"
        # ob.name = ob.data.name = 'PovSphere'
        # return {'FINISHED'}
    
    
    
    class POVRAY_OT_sphere_update(Operator):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Update the POV sphere.
    
        Delete its previous proxy geometry and rerun pov_sphere_define() function
        with the new parameters"""
    
    
        bl_idname = "pov.sphere_update"
        bl_label = "Update"
        bl_description = "Update Sphere"
        bl_options = {'REGISTER', 'UNDO'}
        COMPAT_ENGINES = {'POVRAY_RENDER'}
    
        @classmethod
        def poll(cls, context):
            engine = context.scene.render.engine
            ob = context.object
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            return ob and ob.data and ob.type == 'MESH' and engine in cls.COMPAT_ENGINES
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            pov_sphere_define(context, None, context.object, context.object.location)
    
            return {'FINISHED'}
    
    
    # ----------------------------------- CONE ---------------------------------- #
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Add the representation of POV cone using pov_define_mesh() function.
    
        Blender cone does not offer the same features such as a second radius."""
    
        verts = []
        faces = []
        if op:
            mesh = None
            base = op.base
            cap = op.cap
            seg = op.seg
            height = op.height
        else:
    
            mesh = ob.data
            base = ob.pov.cone_base_radius
            cap = ob.pov.cone_cap_radius
            seg = ob.pov.cone_segments