Skip to content
Snippets Groups Projects
  • Maurice Raybaud's avatar
    e44e5845
    fix: Texture slots for world and materials · e44e5845
    Maurice Raybaud authored
    fix: Orthographic and perspective camera view angle thanks to Iari Marino
    add: some numpy functions to export mesh possibly faster in next version
    fix: parametric surfaces much accelerated and now actually usable (max gradient defaults were wrong from the time of their implementation in most pov literature. Thanks to William F. Pokorny for finding this out!
    add: very basic "blurry reflection" hack for when using plain official POV
    add: push of (as of yet badly formatted) feedback to interactive console
    add: POV centric workspace, default when addon is left activated from previous session.
    add: Sound signal support on finished render (set from addon preferences)
    add: support for pov 3.8 and decremented in a few areas, waiting for the release
    add: freestyle interface with convoluted workflow currently but preparing for next release.
    fix: commented out Charset feature because POV 3.8 auto detects encoding
    fix: a few dot notation look ups aliased and removed
    fix: restored some more removed properties from 2.79 ( a few remain to do)
    fix: texture mapped specular max value increased
    fix: faster defaults for radiosity
    fix: many default texture influences switched to 1 because boolean enabling is required anyway so 0 was a bad default
    fix: some icons were missing since 2.8
    fix: some formatting improvement was started
    e44e5845
    History
    fix: Texture slots for world and materials
    Maurice Raybaud authored
    fix: Orthographic and perspective camera view angle thanks to Iari Marino
    add: some numpy functions to export mesh possibly faster in next version
    fix: parametric surfaces much accelerated and now actually usable (max gradient defaults were wrong from the time of their implementation in most pov literature. Thanks to William F. Pokorny for finding this out!
    add: very basic "blurry reflection" hack for when using plain official POV
    add: push of (as of yet badly formatted) feedback to interactive console
    add: POV centric workspace, default when addon is left activated from previous session.
    add: Sound signal support on finished render (set from addon preferences)
    add: support for pov 3.8 and decremented in a few areas, waiting for the release
    add: freestyle interface with convoluted workflow currently but preparing for next release.
    fix: commented out Charset feature because POV 3.8 auto detects encoding
    fix: a few dot notation look ups aliased and removed
    fix: restored some more removed properties from 2.79 ( a few remain to do)
    fix: texture mapped specular max value increased
    fix: faster defaults for radiosity
    fix: many default texture influences switched to 1 because boolean enabling is required anyway so 0 was a bad default
    fix: some icons were missing since 2.8
    fix: some formatting improvement was started
primitives.py 79.00 KiB
# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

# <pep8 compliant>

""" Get POV-Ray specific objects In and Out of Blender """

import bpy
import os.path
from bpy_extras.io_utils import ImportHelper
from bpy_extras import object_utils
from bpy.utils import register_class
from math import atan, pi, degrees, sqrt, cos, sin
from bpy.types import Operator

from bpy.props import (
    StringProperty,
    BoolProperty,
    IntProperty,
    FloatProperty,
    FloatVectorProperty,
    EnumProperty,
    PointerProperty,
    CollectionProperty,
)

from mathutils import Vector, Matrix


# import collections


def pov_define_mesh(mesh, verts, edges, faces, name, hide_geometry=True):
    """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):
    """Add the representation of POV lathe using a screw modifier."""

    bl_idname = "pov.addlathe"
    bl_label = "Lathe"
    bl_options = {'REGISTER', 'UNDO'}
    bl_description = "adds lathe"

    def execute(self, context):
        # ayers=[False]*20
        # layers[0]=True
        bpy.ops.curve.primitive_bezier_curve_add(
            location=context.scene.cursor.location,
            rotation=(0, 0, 0),
            # layers=layers,
        )
        ob = context.view_layer.objects.active
        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'
        self.report({'INFO'}, "This native POV-Ray primitive")
        ob.pov.curveshape = "lathe"
        bpy.ops.object.modifier_add(type='SCREW')
        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 the pov_superellipsoid_define() function."""

    if op:
        mesh = None

        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

    else:
        assert ob
        mesh = ob.data

        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 = []
    r = 1

    stepSegment = 360 / v * pi / 180
    stepRing = pi / u
    angSegment = 0
    angRing = -pi / 2

    step = 0
    for ring in range(0, u - 1):
        angRing += stepRing
        for segment in range(0, v):
            step += 1
            angSegment += stepSegment
            x = r * (abs(cos(angRing)) ** n1) * (abs(cos(angSegment)) ** n2)
            if (cos(angRing) < 0 and cos(angSegment) > 0) or (
                cos(angRing) > 0 and cos(angSegment) < 0
            ):
                x = -x
            y = r * (abs(cos(angRing)) ** n1) * (abs(sin(angSegment)) ** n2)
            if (cos(angRing) < 0 and sin(angSegment) > 0) or (
                cos(angRing) > 0 and sin(angSegment) < 0
            ):
                y = -y
            z = r * (abs(sin(angRing)) ** n1)
            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.append((0, 0, 1))
        verts.append((0, 0, -1))

    faces = []

    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)
            faces.append(face)
    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)
                faces.append(face)
            if i == v - 1:
                face = (indexDown, i, 0)
                faces.append(face)
        for i in range(0, v):
            if i < v - 1:
                face = (indexUp, i + indexStartDown, i + indexStartDown + 1)
                faces.append(face)
            if i == v - 1:
                face = (indexUp, i + indexStartDown, indexStartDown)
                faces.append(face)
    if edit == 'NGONS':
        face = []
        for i in range(0, v):
            face.append(i)
        faces.append(face)
        face = []
        indexUp = len(verts) - 1
        for i in range(0, v):
            face.append(indexUp - i)
        faces.append(face)
    mesh = pov_define_mesh(mesh, verts, [], faces, "SuperEllipsoid")

    if not ob:
        ob = object_utils.object_data_add(context, mesh, operator=None)
        # engine = context.scene.render.engine what for?
        ob = context.object
        ob.name = ob.data.name = "PovSuperellipsoid"
        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() function."""

    bl_idname = "pov.addsuperellipsoid"
    bl_label = "Add SuperEllipsoid"
    bl_description = "Create a SuperEllipsoid"
    bl_options = {'REGISTER', 'UNDO'}
    COMPAT_ENGINES = {'POVRAY_RENDER'}

    # XXX Keep it in sync with __init__'s RenderPovSettingsConePrimitive
    #     If someone knows how to define operators' props from a func, I'd be delighted to learn it!
    se_param1: FloatProperty(
        name="Parameter 1", description="", min=0.00, max=10.0, default=0.04
    )

    se_param2: FloatProperty(
        name="Parameter 2", description="", min=0.00, max=10.0, default=0.04
    )

    se_u: IntProperty(
        name="U-segments",
        description="radial segmentation",
        default=20,
        min=4,
        max=265,
    )
    se_v: IntProperty(
        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(
        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):
        pov_superellipsoid_define(context, self, None)

        self.report(
            {'INFO'},
            "This native POV-Ray primitive won't have any vertex to show in edit mode",
        )

        return {'FINISHED'}


class POVRAY_OT_superellipsoid_update(Operator):
    """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)

        return {'FINISHED'}


def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False):
    faces = []
    if not vertIdx1 or not vertIdx2:
        return None
    if len(vertIdx1) < 2 and len(vertIdx2) < 2:
        return None
    fan = False
    if len(vertIdx1) != len(vertIdx2):
        if len(vertIdx1) == 1 and len(vertIdx2) > 1:
            fan = True
        else:
            return None
    total = len(vertIdx2)
    if closed:
        if flipped:
            face = [vertIdx1[0], vertIdx2[0], vertIdx2[total - 1]]
            if not fan:
                face.append(vertIdx1[total - 1])
            faces.append(face)

        else:
            face = [vertIdx2[0], vertIdx1[0]]
            if not fan:
                face.append(vertIdx1[total - 1])
            face.append(vertIdx2[total - 1])
            faces.append(face)
    for num in range(total - 1):
        if flipped:
            if fan:
                face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
            else:
                face = [
                    vertIdx2[num],
                    vertIdx1[num],
                    vertIdx1[num + 1],
                    vertIdx2[num + 1],
                ]
            faces.append(face)
        else:
            if fan:
                face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
            else:
                face = [
                    vertIdx1[num],
                    vertIdx2[num],
                    vertIdx2[num + 1],
                    vertIdx1[num + 1],
                ]
            faces.append(face)

    return faces


def power(a, b):
    if a < 0:
        return -((-a) ** b)
    return a ** b


def supertoroid(R, r, u, v, n1, n2):
    a = 2 * pi / u
    b = 2 * pi / v
    verts = []
    faces = []
    for i in range(u):
        s = power(sin(i * a), n1)
        c = power(cos(i * a), n1)
        for j in range(v):
            c2 = R + r * power(cos(j * b), n2)
            s2 = r * power(sin(j * b), n2)
            verts.append(
                (c * c2, s * c2, s2)
            )  # type as a (mathutils.Vector(c*c2,s*c2,s2))?
        if i > 0:
            f = createFaces(
                range((i - 1) * v, i * v),
                range(i * v, (i + 1) * v),
                closed=True,
            )
            faces.extend(f)
    f = createFaces(range((u - 1) * v, u * v), range(v), closed=True)
    faces.extend(f)
    return verts, faces


def pov_supertorus_define(context, op, ob):
    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:
        ob = object_utils.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):
    """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(
        name="small radius",
        description="The radius of the tube",
        default=0.3,
        min=0.01,
        max=100.0,
    )
    st_u: IntProperty(
        name="U-segments",
        description="radial segmentation",
        default=16,
        min=3,
        max=265,
    )
    st_v: IntProperty(
        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(
        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)

        self.report(
            {'INFO'},
            "This native POV-Ray primitive won't have any vertex to show in edit mode",
        )
        return {'FINISHED'}


class POVRAY_OT_supertorus_update(Operator):
    """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)

        return {'FINISHED'}


#########################################################################################################
class POVRAY_OT_loft_add(Operator):
    """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'}

    loft_n: IntProperty(
        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
    )
    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,
    )
    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):

        props = self.properties
        loftData = bpy.data.curves.new('Loft', type='CURVE')
        loftData.dimensions = '3D'
        loftData.resolution_u = 2
        # loftData.show_normal_face = False # deprecated in 2.8
        n = props.loft_n
        thick = props.loft_thick
        side = props.loft_rings_side
        bottom = props.loft_rings_bottom
        h = props.loft_height
        r = props.loft_r
        distB = r / bottom
        r0 = 0.00001
        z = -h / 2
        print("New")
        for i in range(bottom + 1):
            coords = []
            angle = 0
            for p in range(n):
                x = r0 * cos(angle)
                y = r0 * sin(angle)
                coords.append((x, y, z))
                angle += pi * 2 / n
            r0 += distB
            nurbs = loftData.splines.new('NURBS')
            nurbs.points.add(len(coords) - 1)
            for i, coord in enumerate(coords):
                x, y, z = coord
                nurbs.points[i].co = (x, y, z, 1)
            nurbs.use_cyclic_u = True
        for i in range(side):
            z += h / side
            coords = []
            angle = 0
            for p in range(n):
                x = r * cos(angle)
                y = r * sin(angle)
                coords.append((x, y, z))
                angle += pi * 2 / n
            nurbs = loftData.splines.new('NURBS')
            nurbs.points.add(len(coords) - 1)
            for i, coord in enumerate(coords):
                x, y, z = coord
                nurbs.points[i].co = (x, y, z, 1)
            nurbs.use_cyclic_u = True
        r -= thick
        for i in range(side):
            coords = []
            angle = 0
            for p in range(n):
                x = r * cos(angle)
                y = r * sin(angle)
                coords.append((x, y, z))
                angle += pi * 2 / n
            nurbs = loftData.splines.new('NURBS')
            nurbs.points.add(len(coords) - 1)
            for i, coord in enumerate(coords):
                x, y, z = coord
                nurbs.points[i].co = (x, y, z, 1)
            nurbs.use_cyclic_u = True
            z -= h / side
        z = (-h / 2) + thick
        distB = (r - 0.00001) / bottom
        for i in range(bottom + 1):
            coords = []
            angle = 0
            for p in range(n):
                x = r * cos(angle)
                y = r * sin(angle)
                coords.append((x, y, z))
                angle += pi * 2 / n
            r -= distB
            nurbs = loftData.splines.new('NURBS')
            nurbs.points.add(len(coords) - 1)
            for i, coord in enumerate(coords):
                x, y, z = coord
                nurbs.points[i].co = (x, y, z, 1)
            nurbs.use_cyclic_u = True
        ob = bpy.data.objects.new('Loft_shape', loftData)
        scn = bpy.context.scene
        scn.collection.objects.link(ob)
        context.view_layer.objects.active = ob
        ob.select_set(True)
        ob.pov.curveshape = "loft"
        return {'FINISHED'}


class POVRAY_OT_plane_add(Operator):
    """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=100000)
        ob = context.object
        ob.name = ob.data.name = 'PovInfinitePlane'
        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 = "PLANE"
        return {'FINISHED'}


class POVRAY_OT_box_add(Operator):
    """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):
        # 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")
        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")
        ob.pov.object_as = "BOX"
        return {'FINISHED'}


def pov_cylinder_define(context, op, ob, radius, loc, loc_cap):
    if op:
        R = op.R
        loc = bpy.context.scene.cursor.location
        loc_cap[0] = loc[0]
        loc_cap[1] = loc[1]
        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(
        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):
    """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'}

    # XXX Keep it in sync with __init__'s cylinder Primitive
    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):
        props = self.properties
        R = props.R
        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
        else:
            if not props.imported_cyl_loc:
                LOC_CAP = LOC = bpy.context.scene.cursor.location
                LOC_CAP[2] += 2.0
            else:
                LOC = props.imported_cyl_loc
                LOC_CAP = props.imported_cyl_loc_cap
            self.report(
                {'INFO'},
                "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):
    """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
        ob = context.object
        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)

        return {'FINISHED'}


################################SPHERE##########################################
def pov_sphere_define(context, op, ob, loc):
    """create the representation of POV sphere using a Blender icosphere.

    Its nice platonic solid curvature better represents pov rendertime
    tesselation than a UV sphere"""

    if op:
        R = op.R
        loc = bpy.context.scene.cursor.location
    else:
        assert ob
        R = ob.pov.sphere_radius

        # keep object rotation and location for the add object operator
        obrot = ob.rotation_euler
        # obloc = ob.location
        obscale = ob.scale

        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(
            subdivisions=4,
            radius=ob.pov.sphere_radius,
            location=loc,
            rotation=obrot,
        )
        # 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.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')

    if not ob:
        bpy.ops.mesh.primitive_ico_sphere_add(
            subdivisions=4, radius=R, location=loc
        )
        ob = context.object
        ob.name = ob.data.name = "PovSphere"
        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):
    """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'}

    # XXX Keep it in sync with __init__'s torus Primitive
    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):
        props = self.properties
        R = props.R
        ob = context.object

        if ob:
            if ob.pov.imported_loc:
                LOC = ob.pov.imported_loc
        else:
            if 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",
                )
        pov_sphere_define(context, self, None, LOC)

        return {'FINISHED'}

    # def execute(self,context):
    ## 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):
    """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
        return (
            ob
            and ob.data
            and ob.type == 'MESH'
            and engine in cls.COMPAT_ENGINES
        )

    def execute(self, context):

        pov_sphere_define(
            context, None, context.object, context.object.location
        )

        return {'FINISHED'}


####################################CONE#######################################
def pov_cone_define(context, op, ob):
    """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:
        assert ob
        mesh = ob.data
        base = ob.pov.cone_base_radius
        cap = ob.pov.cone_cap_radius
        seg = ob.pov.cone_segments
        height = ob.pov.cone_height

    zc = height / 2
    zb = -zc
    angle = 2 * pi / seg
    t = 0
    for i in range(seg):
        xb = base * cos(t)
        yb = base * sin(t)
        xc = cap * cos(t)
        yc = cap * sin(t)
        verts.append((xb, yb, zb))
        verts.append((xc, yc, zc))
        t += angle
    for i in range(seg):
        f = i * 2
        if i == seg - 1:
            faces.append([0, 1, f + 1, f])
        else:
            faces.append([f + 2, f + 3, f + 1, f])
    if base != 0:
        base_face = []
        for i in range(seg - 1, -1, -1):
            p = i * 2
            base_face.append(p)
        faces.append(base_face)
    if cap != 0:
        cap_face = []
        for i in range(seg):
            p = i * 2 + 1
            cap_face.append(p)
        faces.append(cap_face)

    mesh = pov_define_mesh(mesh, verts, [], faces, "PovCone", True)
    if not ob:
        ob = object_utils.object_data_add(context, mesh, operator=None)
        ob.pov.object_as = "CONE"
        ob.pov.cone_base_radius = base
        ob.pov.cone_cap_radius = cap
        ob.pov.cone_height = height
        ob.pov.cone_base_z = zb
        ob.pov.cone_cap_z = zc


class POVRAY_OT_cone_add(Operator):
    """Add the representation of POV cone using pov_cone_define() function."""

    bl_idname = "pov.cone_add"
    bl_label = "Cone"
    bl_description = "Add Cone"
    bl_options = {'REGISTER', 'UNDO'}
    COMPAT_ENGINES = {'POVRAY_RENDER'}

    # XXX Keep it in sync with __init__.py's RenderPovSettingsConePrimitive
    #     If someone knows how to define operators' props from a func, I'd be delighted to learn it!
    base: FloatProperty(
        name="Base radius",
        description="The first radius of the cone",
        default=1.0,
        min=0.01,
        max=100.0,
    )
    cap: FloatProperty(
        name="Cap radius",
        description="The second radius of the cone",
        default=0.3,
        min=0.0,
        max=100.0,
    )
    seg: IntProperty(
        name="Segments",
        description="Radial segmentation of the proxy mesh",
        default=16,
        min=3,
        max=265,
    )
    height: FloatProperty(
        name="Height",
        description="Height of the cone",
        default=2.0,
        min=0.01,
        max=100.0,
    )

    @classmethod
    def poll(cls, context):
        engine = context.scene.render.engine
        return engine in cls.COMPAT_ENGINES

    def execute(self, context):
        pov_cone_define(context, self, None)

        self.report(
            {'INFO'},
            "This native POV-Ray primitive won't have any vertex to show in edit mode",
        )
        return {'FINISHED'}


class POVRAY_OT_cone_update(Operator):
    """Update the POV cone.

    Delete its previous proxy geometry and rerun pov_cone_define() function
    with the new parameters"""

    bl_idname = "pov.cone_update"
    bl_label = "Update"
    bl_description = "Update Cone"
    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_cone_define(context, None, context.object)

        return {'FINISHED'}


########################################ISOSURFACES##################################


class POVRAY_OT_isosurface_box_add(Operator):
    """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'}

    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")
        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")
        ob.pov.object_as = "ISOSURFACE"
        ob.pov.contained_by = 'box'
        ob.name = 'PovIsosurfaceBox'
        return {'FINISHED'}


class POVRAY_OT_isosurface_sphere_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.addisosurfacesphere"
    bl_label = "Isosurface Sphere"
    bl_description = "Add Isosurface contained by Sphere"
    bl_options = {'REGISTER', 'UNDO'}

    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")
        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 = "ISOSURFACE"
        ob.pov.contained_by = 'sphere'
        ob.name = 'PovIsosurfaceSphere'
        return {'FINISHED'}


class POVRAY_OT_sphere_sweep_add(Operator):
    """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'}

    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.fill_mode = 'FULL'
        # ob.data.splines[0].order_u = 4

        return {'FINISHED'}


class POVRAY_OT_blob_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.addblobsphere"
    bl_label = "Blob Sphere"
    bl_description = "Add Blob Sphere"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        # layers = 20*[False]
        # layers[0] = True
        bpy.ops.object.metaball_add(type='BALL')
        ob = context.object
        ob.name = "PovBlob"
        return {'FINISHED'}


class POVRAY_OT_rainbow_add(Operator):
    """Add the representation of POV rainbow using a Blender spot light.

    Rainbows indeed propagate along a visibility cone.
    Flag its primitive type with a specific ob.pov.object_as attribute
    and leave access to edit mode to keep user editable handles.
    Add a constraint to orient it towards camera because POV Rainbows
    are view dependant and having it always initially visible is less
    confusing """

    bl_idname = "pov.addrainbow"
    bl_label = "Rainbow"
    bl_description = "Add Rainbow"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        cam = context.scene.camera
        bpy.ops.object.light_add(type='SPOT', radius=1)
        ob = context.object
        ob.data.show_cone = False
        ob.data.spot_blend = 0.5
        # ob.data.shadow_buffer_clip_end = 0 # deprecated in 2.8
        ob.data.shadow_buffer_clip_start = 4 * cam.location.length
        ob.data.distance = cam.location.length
        ob.data.energy = 0
        ob.name = ob.data.name = "PovRainbow"
        ob.pov.object_as = "RAINBOW"

        # obj = context.object
        bpy.ops.object.constraint_add(type='DAMPED_TRACK')

        ob.constraints["Damped Track"].target = cam
        ob.constraints["Damped Track"].track_axis = 'TRACK_NEGATIVE_Z'
        ob.location = -cam.location

        # refocus on the actual rainbow
        bpy.context.view_layer.objects.active = ob
        ob.select_set(True)

        return {'FINISHED'}


class POVRAY_OT_height_field_add(bpy.types.Operator, ImportHelper):
    """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"
    bl_options = {'REGISTER', 'UNDO'}

    # XXX Keep it in sync with __init__'s hf Primitive
    # filename_ext = ".png"

    # filter_glob = StringProperty(
    # 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'},
    # )
    quality: IntProperty(
        name="Quality", description="", default=100, min=1, max=100
    )
    hf_filename: StringProperty(maxlen=1024)

    hf_gamma: FloatProperty(
        name="Gamma", description="Gamma", min=0.0001, max=20.0, default=1.0
    )

    hf_premultiplied: BoolProperty(
        name="Premultiplied", description="Premultiplied", default=True
    )

    hf_smooth: BoolProperty(name="Smooth", description="Smooth", default=False)

    hf_water: FloatProperty(
        name="Water Level",
        description="Wather Level",
        min=0.00,
        max=1.00,
        default=0.0,
    )

    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')
        hf_tex.image = img
        mat = bpy.data.materials.new('Tex_%s_hf' % im_name)
        hf_slot = mat.pov_texture_slots.add()
        hf_slot.texture = hf_tex.name
        # layers = 20*[False]
        # layers[0] = True
        quality = props.quality
        res = 100 / quality
        w, h = hf_tex.image.size[:]
        w = int(w / res)
        h = int(h / res)
        bpy.ops.mesh.primitive_grid_add(
            x_subdivisions=w, y_subdivisions=h, size=0.5
        )
        ob = context.object
        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 a displace modifier as noise deprecated in 2.8
        bpy.ops.object.mode_set(mode="OBJECT")

        # needs a loop to select by index?
        # bpy.ops.object.material_slot_remove()
        # material just left there for now

        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'
        ob.pov.hf_filename = impath
        return {'FINISHED'}


############################TORUS############################################
def pov_torus_define(context, op, ob):
    """Add the representation of POV torus using just a Blender torus.

    But flag its primitive type with a specific pov.object_as attribute and lock edit mode
    to keep proxy consistency by hiding edit geometry."""

    if op:
        mas = op.mas
        mis = op.mis
        mar = op.mar
        mir = op.mir
    else:
        assert ob
        mas = ob.pov.torus_major_segments
        mis = ob.pov.torus_minor_segments
        mar = ob.pov.torus_major_radius
        mir = ob.pov.torus_minor_radius

        # keep object rotation and location for the add object operator
        obrot = ob.rotation_euler
        obloc = ob.location

        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_torus_add(
            rotation=obrot,
            location=obloc,
            major_segments=mas,
            minor_segments=mis,
            major_radius=mar,
            minor_radius=mir,
        )

        bpy.ops.mesh.hide(unselected=False)
        bpy.ops.object.mode_set(mode="OBJECT")

    if not ob:
        bpy.ops.mesh.primitive_torus_add(
            major_segments=mas,
            minor_segments=mis,
            major_radius=mar,
            minor_radius=mir,
        )
        ob = context.object
        ob.name = ob.data.name = "PovTorus"
        ob.pov.object_as = "TORUS"
        ob.pov.torus_major_segments = mas
        ob.pov.torus_minor_segments = mis
        ob.pov.torus_major_radius = mar
        ob.pov.torus_minor_radius = mir
        bpy.ops.object.mode_set(mode="EDIT")
        bpy.ops.mesh.hide(unselected=False)
        bpy.ops.object.mode_set(mode="OBJECT")


class POVRAY_OT_torus_add(Operator):
    """Add the representation of POV torus using using pov_torus_define() function."""

    bl_idname = "pov.addtorus"
    bl_label = "Torus"
    bl_description = "Add Torus"
    bl_options = {'REGISTER', 'UNDO'}

    # XXX Keep it in sync with __init__'s torus Primitive
    mas: IntProperty(
        name="Major Segments", description="", default=48, min=3, max=720
    )
    mis: IntProperty(
        name="Minor Segments", description="", default=12, min=3, max=720
    )
    mar: FloatProperty(name="Major Radius", description="", default=1.0)
    mir: FloatProperty(name="Minor Radius", description="", default=0.25)

    def execute(self, context):
        props = self.properties
        mar = props.mar
        mir = props.mir
        mas = props.mas
        mis = props.mis
        pov_torus_define(context, self, None)
        self.report(
            {'INFO'},
            "This native POV-Ray primitive "
            "won't have any vertex to show in edit mode",
        )
        return {'FINISHED'}


class POVRAY_OT_torus_update(Operator):
    """Update the POV torus.

    Delete its previous proxy geometry and rerun pov_torus_define() function
    with the new parameters"""

    bl_idname = "pov.torus_update"
    bl_label = "Update"
    bl_description = "Update Torus"
    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):

        pov_torus_define(context, None, context.object)

        return {'FINISHED'}


###################################################################################


class POVRAY_OT_prism_add(Operator):
    """Add the representation of POV prism using using an extruded curve."""

    bl_idname = "pov.addprism"
    bl_label = "Prism"
    bl_description = "Create Prism"
    bl_options = {'REGISTER', 'UNDO'}

    prism_n: IntProperty(
        name="Sides", description="Number of sides", default=5, min=3, max=720
    )
    prism_r: FloatProperty(name="Radius", description="Radius", default=1.0)

    def execute(self, context):

        props = self.properties
        loftData = bpy.data.curves.new('Prism', type='CURVE')
        loftData.dimensions = '2D'
        loftData.resolution_u = 2
        # loftData.show_normal_face = False
        loftData.extrude = 2
        n = props.prism_n
        r = props.prism_r
        coords = []
        z = 0
        angle = 0
        for p in range(n):
            x = r * cos(angle)
            y = r * sin(angle)
            coords.append((x, y, z))
            angle += pi * 2 / n
        poly = loftData.splines.new('POLY')
        poly.points.add(len(coords) - 1)
        for i, coord in enumerate(coords):
            x, y, z = coord
            poly.points[i].co = (x, y, z, 1)
        poly.use_cyclic_u = True

        ob = bpy.data.objects.new('Prism_shape', loftData)
        scn = bpy.context.scene
        scn.collection.objects.link(ob)
        context.view_layer.objects.active = ob
        ob.select_set(True)
        ob.pov.curveshape = "prism"
        ob.name = ob.data.name = "Prism"
        return {'FINISHED'}


##############################PARAMETRIC######################################
def pov_parametric_define(context, op, ob):
    """Add the representation of POV parametric surfaces by math surface from add mesh extra objects addon."""

    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

    else:
        assert ob
        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
        obloc = ob.location
        obrot = ob.rotation_euler  # In radians
        # Parametric addon has no loc rot, some extra work is needed
        # in case cursor has moved
        curloc = bpy.context.scene.cursor.location

        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_xyz_function_surface(
            x_eq=x_eq,
            y_eq=y_eq,
            z_eq=z_eq,
            range_u_min=u_min,
            range_u_max=u_max,
            range_v_min=v_min,
            range_v_max=v_max,
        )
        bpy.ops.mesh.select_all(action='SELECT')
        # extra work:
        bpy.ops.transform.translate(value=(obloc - curloc), proportional_size=1)
        bpy.ops.transform.rotate(axis=obrot, proportional_size=1)

        bpy.ops.mesh.hide(unselected=False)
        bpy.ops.object.mode_set(mode="OBJECT")

    if not ob:
        bpy.ops.mesh.primitive_xyz_function_surface(
            x_eq=x_eq,
            y_eq=y_eq,
            z_eq=z_eq,
            range_u_min=u_min,
            range_u_max=u_max,
            range_v_min=v_min,
            range_v_max=v_max,
        )
        ob = context.object
        ob.name = ob.data.name = "PovParametric"
        ob.pov.object_as = "PARAMETRIC"

        ob.pov.u_min = u_min
        ob.pov.u_max = u_max
        ob.pov.v_min = v_min
        ob.pov.v_max = v_max
        ob.pov.x_eq = x_eq
        ob.pov.y_eq = y_eq
        ob.pov.z_eq = z_eq

        bpy.ops.object.mode_set(mode="EDIT")
        bpy.ops.mesh.hide(unselected=False)
        bpy.ops.object.mode_set(mode="OBJECT")


class POVRAY_OT_parametric_add(Operator):
    """Add the representation of POV parametric surfaces using pov_parametric_define() function."""

    bl_idname = "pov.addparametric"
    bl_label = "Parametric"
    bl_description = "Add Paramertic"
    bl_options = {'REGISTER', 'UNDO'}

    # XXX Keep it in sync with __init__'s Parametric primitive
    u_min: FloatProperty(name="U Min", description="", default=0.0)
    v_min: FloatProperty(name="V Min", description="", default=0.0)
    u_max: FloatProperty(name="U Max", description="", default=6.28)
    v_max: FloatProperty(name="V Max", description="", default=12.57)
    x_eq: StringProperty(maxlen=1024, default="cos(v)*(1+cos(u))*sin(v/8)")
    y_eq: StringProperty(maxlen=1024, default="sin(u)*sin(v/8)+cos(v/8)*1.5")
    z_eq: StringProperty(maxlen=1024, default="sin(v)*(1+cos(u))*sin(v/8)")

    def execute(self, context):
        props = self.properties
        u_min = props.u_min
        v_min = props.v_min
        u_max = props.u_max
        v_max = props.v_max
        x_eq = props.x_eq
        y_eq = props.y_eq
        z_eq = props.z_eq

        pov_parametric_define(context, self, None)
        self.report(
            {'INFO'},
            "This native POV-Ray primitive "
            "won't have any vertex to show in edit mode",
        )
        return {'FINISHED'}


class POVRAY_OT_parametric_update(Operator):
    """Update the representation of POV parametric surfaces.

    Delete its previous proxy geometry and rerun pov_parametric_define() function
    with the new parameters"""

    bl_idname = "pov.parametric_update"
    bl_label = "Update"
    bl_description = "Update parametric object"
    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):

        pov_parametric_define(context, None, context.object)

        return {'FINISHED'}


#######################################################################


class POVRAY_OT_shape_polygon_to_circle_add(Operator):
    """Add the proxy mesh for POV Polygon to circle lofting macro"""

    bl_idname = "pov.addpolygontocircle"
    bl_label = "Polygon To Circle Blending"
    bl_description = "Add Polygon To Circle Blending Surface"
    bl_options = {'REGISTER', 'UNDO'}
    COMPAT_ENGINES = {'POVRAY_RENDER'}

    # XXX Keep it in sync with __init__'s polytocircle properties
    polytocircle_resolution: IntProperty(
        name="Resolution", description="", default=3, min=0, max=256
    )
    polytocircle_ngon: IntProperty(
        name="NGon", description="", min=3, max=64, default=5
    )
    polytocircle_ngonR: FloatProperty(
        name="NGon Radius", description="", default=0.3
    )
    polytocircle_circleR: FloatProperty(
        name="Circle Radius", description="", default=1.0
    )

    def execute(self, context):
        props = self.properties
        ngon = props.polytocircle_ngon
        ngonR = props.polytocircle_ngonR
        circleR = props.polytocircle_circleR
        resolution = props.polytocircle_resolution
        # layers = 20*[False]
        # layers[0] = True
        bpy.ops.mesh.primitive_circle_add(
            vertices=ngon, radius=ngonR, fill_type='NGON', enter_editmode=True
        )
        bpy.ops.transform.translate(value=(0, 0, 1))
        bpy.ops.mesh.subdivide(number_cuts=resolution)
        numCircleVerts = ngon + (ngon * resolution)
        bpy.ops.mesh.select_all(action='DESELECT')
        bpy.ops.mesh.primitive_circle_add(
            vertices=numCircleVerts,
            radius=circleR,
            fill_type='NGON',
            enter_editmode=True,
        )
        bpy.ops.transform.translate(value=(0, 0, -1))
        bpy.ops.mesh.select_all(action='SELECT')
        bpy.ops.mesh.bridge_edge_loops()
        if ngon < 5:
            bpy.ops.mesh.select_all(action='DESELECT')
            bpy.ops.mesh.primitive_circle_add(
                vertices=ngon,
                radius=ngonR,
                fill_type='TRIFAN',
                enter_editmode=True,
            )
            bpy.ops.transform.translate(value=(0, 0, 1))
            bpy.ops.mesh.select_all(action='SELECT')
            bpy.ops.mesh.remove_doubles()
        bpy.ops.object.mode_set(mode='OBJECT')
        ob = context.object
        ob.name = "Polygon_To_Circle"
        ob.pov.object_as = 'POLYCIRCLE'
        ob.pov.ngon = ngon
        ob.pov.ngonR = ngonR
        ob.pov.circleR = circleR
        bpy.ops.object.mode_set(mode="EDIT")
        bpy.ops.mesh.hide(unselected=False)
        bpy.ops.object.mode_set(mode="OBJECT")
        return {'FINISHED'}


#############################IMPORT


class ImportPOV(bpy.types.Operator, ImportHelper):
    """Load Povray files"""

    bl_idname = "import_scene.pov"
    bl_label = "POV-Ray files (.pov/.inc)"
    bl_options = {'PRESET', 'UNDO'}
    COMPAT_ENGINES = {'POVRAY_RENDER'}

    # -----------
    # File props.
    files: CollectionProperty(
        type=bpy.types.OperatorFileListElement, options={'HIDDEN', 'SKIP_SAVE'}
    )
    directory: StringProperty(
        maxlen=1024, subtype='FILE_PATH', options={'HIDDEN', 'SKIP_SAVE'}
    )

    filename_ext = {".pov", ".inc"}
    filter_glob: StringProperty(default="*.pov;*.inc", options={'HIDDEN'})

    import_at_cur: BoolProperty(
        name="Import at Cursor Location",
        description="Ignore Object Matrix",
        default=False,
    )

    def execute(self, context):
        from mathutils import Matrix

        verts = []
        faces = []
        materials = []
        blendMats = []  ##############
        povMats = []  ##############
        colors = []
        matNames = []
        lenverts = None
        lenfaces = None
        suffix = -1
        name = 'Mesh2_%s' % suffix
        name_search = False
        verts_search = False
        faces_search = False
        plane_search = False
        box_search = False
        cylinder_search = False
        sphere_search = False
        cone_search = False
        tex_search = False  ##################
        cache = []
        matrixes = {}
        writematrix = False
        index = None
        value = None
        # filepov = bpy.path.abspath(self.filepath) #was used for single files

        def mat_search(cache):
            r = g = b = 0.5
            f = t = 0
            color = None

            for item, value in enumerate(cache):

                if value == 'texture':
                    pass

                if value == 'pigment':

                    if cache[item + 2] in {'rgb', 'srgb'}:
                        pass

                    elif cache[item + 2] in {'rgbf', 'srgbf'}:
                        pass

                    elif cache[item + 2] in {'rgbt', 'srgbt'}:
                        try:
                            r, g, b, t = (
                                float(cache[item + 3]),
                                float(cache[item + 4]),
                                float(cache[item + 5]),
                                float(cache[item + 6]),
                            )
                        except:
                            r = g = b = t = float(cache[item + 2])
                        color = (r, g, b, t)

                    elif cache[item + 2] in {'rgbft', 'srgbft'}:
                        pass

                    else:
                        pass

            if colors == [] or (colors != [] and color not in colors):
                colors.append(color)
                name = ob.name + "_mat"
                matNames.append(name)
                mat = bpy.data.materials.new(name)
                mat.diffuse_color = (r, g, b)
                mat.alpha = 1 - t
                if mat.alpha != 1:
                    mat.use_transparency = True
                ob.data.materials.append(mat)

            else:
                for i, value in enumerate(colors):
                    if color == value:
                        ob.data.materials.append(
                            bpy.data.materials[matNames[i]]
                        )

        for file in self.files:
            print("Importing file: " + file.name)
            filepov = self.directory + file.name
            for line in open(filepov):
                string = line.replace("{", " ")
                string = string.replace("}", " ")
                string = string.replace("<", " ")
                string = string.replace(">", " ")
                string = string.replace(",", " ")
                lw = string.split()
                lenwords = len(lw)
                if lw:
                    if lw[0] == "object":
                        writematrix = True
                    if writematrix:
                        if lw[0] not in {"object", "matrix"}:
                            index = lw[0]
                        if lw[0] in {"matrix"}:
                            value = [
                                float(lw[1]),
                                float(lw[2]),
                                float(lw[3]),
                                float(lw[4]),
                                float(lw[5]),
                                float(lw[6]),
                                float(lw[7]),
                                float(lw[8]),
                                float(lw[9]),
                                float(lw[10]),
                                float(lw[11]),
                                float(lw[12]),
                            ]
                            matrixes[index] = value
                            writematrix = False
            for line in open(filepov):
                S = line.replace("{", " { ")
                S = S.replace("}", " } ")
                S = S.replace(",", " ")
                S = S.replace("<", "")
                S = S.replace(">", " ")
                S = S.replace("=", " = ")
                S = S.replace(";", " ; ")
                S = S.split()
                lenS = len(S)
                for i, word in enumerate(S):
                    ##################Primitives Import##################
                    if word == 'cone':
                        cone_search = True
                        name_search = False
                    if cone_search:
                        cache.append(word)
                        if cache[-1] == '}':
                            try:
                                x0 = float(cache[2])
                                y0 = float(cache[3])
                                z0 = float(cache[4])
                                r0 = float(cache[5])
                                x1 = float(cache[6])
                                y1 = float(cache[7])
                                z1 = float(cache[8])
                                r1 = float(cache[9])
                                # Y is height in most pov files, not z
                                bpy.ops.pov.cone_add(
                                    base=r0, cap=r1, height=(y1 - y0)
                                )
                                ob = context.object
                                ob.location = (x0, y0, z0)
                                # ob.scale = (r,r,r)
                                mat_search(cache)
                            except (ValueError):
                                pass
                            cache = []
                            cone_search = False
                    if word == 'plane':
                        plane_search = True
                        name_search = False
                    if plane_search:
                        cache.append(word)
                        if cache[-1] == '}':
                            try:
                                bpy.ops.pov.addplane()
                                ob = context.object
                                mat_search(cache)
                            except (ValueError):
                                pass
                            cache = []
                            plane_search = False
                    if word == 'box':
                        box_search = True
                        name_search = False
                    if box_search:
                        cache.append(word)
                        if cache[-1] == '}':
                            try:
                                x0 = float(cache[2])
                                y0 = float(cache[3])
                                z0 = float(cache[4])
                                x1 = float(cache[5])
                                y1 = float(cache[6])
                                z1 = float(cache[7])
                                # imported_corner_1=(x0, y0, z0)
                                # imported_corner_2 =(x1, y1, z1)
                                center = (
                                    (x0 + x1) / 2,
                                    (y0 + y1) / 2,
                                    (z0 + z1) / 2,
                                )
                                bpy.ops.pov.addbox()
                                ob = context.object
                                ob.location = center
                                mat_search(cache)

                            except (ValueError):
                                pass
                            cache = []
                            box_search = False
                    if word == 'cylinder':
                        cylinder_search = True
                        name_search = False
                    if cylinder_search:
                        cache.append(word)
                        if cache[-1] == '}':
                            try:
                                x0 = float(cache[2])
                                y0 = float(cache[3])
                                z0 = float(cache[4])
                                x1 = float(cache[5])
                                y1 = float(cache[6])
                                z1 = float(cache[7])
                                imported_cyl_loc = (x0, y0, z0)
                                imported_cyl_loc_cap = (x1, y1, z1)

                                r = float(cache[8])

                                vec = Vector(imported_cyl_loc_cap) - Vector(
                                    imported_cyl_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.
                                # center = ((x0 + x1)/2,(y0 + y1)/2,(z0 + z1)/2)
                                scaleZ = (
                                    sqrt(
                                        (x1 - x0) ** 2
                                        + (y1 - y0) ** 2
                                        + (z1 - z0) ** 2
                                    )
                                    / 2
                                )
                                bpy.ops.pov.addcylinder(
                                    R=r,
                                    imported_cyl_loc=imported_cyl_loc,
                                    imported_cyl_loc_cap=imported_cyl_loc_cap,
                                )
                                ob = context.object
                                ob.location = (x0, y0, z0)
                                ob.rotation_euler = rot.to_euler()
                                ob.scale = (1, 1, scaleZ)

                                # scale data rather than obj?
                                # bpy.ops.object.mode_set(mode='EDIT')
                                # bpy.ops.mesh.reveal()
                                # bpy.ops.mesh.select_all(action='SELECT')
                                # bpy.ops.transform.resize(value=(1,1,scaleZ), orient_type='LOCAL')
                                # bpy.ops.mesh.hide(unselected=False)
                                # bpy.ops.object.mode_set(mode='OBJECT')

                                mat_search(cache)

                            except (ValueError):
                                pass
                            cache = []
                            cylinder_search = False
                    if word == 'sphere':
                        sphere_search = True
                        name_search = False
                    if sphere_search:
                        cache.append(word)
                        if cache[-1] == '}':
                            x = y = z = r = 0
                            try:
                                x = float(cache[2])
                                y = float(cache[3])
                                z = float(cache[4])
                                r = float(cache[5])

                            except (ValueError):
                                pass
                            except:
                                x = y = z = float(cache[2])
                                r = float(cache[3])
                            bpy.ops.pov.addsphere(R=r, imported_loc=(x, y, z))
                            ob = context.object
                            ob.location = (x, y, z)
                            ob.scale = (r, r, r)
                            mat_search(cache)
                            cache = []
                            sphere_search = False
                    ##################End Primitives Import##################
                    if word == '#declare':
                        name_search = True
                    if name_search:
                        cache.append(word)
                        if word == 'mesh2':
                            name_search = False
                            if cache[-2] == '=':
                                name = cache[-3]
                            else:
                                suffix += 1
                            cache = []
                        if word in {'texture', ';'}:
                            name_search = False
                            cache = []
                    if word == 'vertex_vectors':
                        verts_search = True
                    if verts_search:
                        cache.append(word)
                        if word == '}':
                            verts_search = False
                            lenverts = cache[2]
                            cache.pop()
                            cache.pop(0)
                            cache.pop(0)
                            cache.pop(0)
                            for i in range(int(lenverts)):
                                x = i * 3
                                y = (i * 3) + 1
                                z = (i * 3) + 2
                                verts.append(
                                    (
                                        float(cache[x]),
                                        float(cache[y]),
                                        float(cache[z]),
                                    )
                                )
                            cache = []
                    # if word == 'face_indices':
                    # faces_search = True
                    if word == 'texture_list':  ########
                        tex_search = True  #######
                    if tex_search:  #########
                        if (
                            word
                            not in {
                                'texture_list',
                                'texture',
                                '{',
                                '}',
                                'face_indices',
                            }
                            and word.isdigit() == False
                        ):  ##############
                            povMats.append(word)  #################
                    if word == 'face_indices':
                        tex_search = False  ################
                        faces_search = True
                    if faces_search:
                        cache.append(word)
                        if word == '}':
                            faces_search = False
                            lenfaces = cache[2]
                            cache.pop()
                            cache.pop(0)
                            cache.pop(0)
                            cache.pop(0)
                            lf = int(lenfaces)
                            var = int(len(cache) / lf)
                            for i in range(lf):
                                if var == 3:
                                    v0 = i * 3
                                    v1 = i * 3 + 1
                                    v2 = i * 3 + 2
                                    faces.append(
                                        (
                                            int(cache[v0]),
                                            int(cache[v1]),
                                            int(cache[v2]),
                                        )
                                    )
                                if var == 4:
                                    v0 = i * 4
                                    v1 = i * 4 + 1
                                    v2 = i * 4 + 2
                                    m = i * 4 + 3
                                    materials.append((int(cache[m])))
                                    faces.append(
                                        (
                                            int(cache[v0]),
                                            int(cache[v1]),
                                            int(cache[v2]),
                                        )
                                    )
                                if var == 6:
                                    v0 = i * 6
                                    v1 = i * 6 + 1
                                    v2 = i * 6 + 2
                                    m0 = i * 6 + 3
                                    m1 = i * 6 + 4
                                    m2 = i * 6 + 5
                                    materials.append(
                                        (
                                            int(cache[m0]),
                                            int(cache[m1]),
                                            int(cache[m2]),
                                        )
                                    )
                                    faces.append(
                                        (
                                            int(cache[v0]),
                                            int(cache[v1]),
                                            int(cache[v2]),
                                        )
                                    )
                            # mesh = pov_define_mesh(None, verts, [], faces, name, hide_geometry=False)
                            # ob = object_utils.object_data_add(context, mesh, operator=None)

                            me = bpy.data.meshes.new(name)  ########
                            ob = bpy.data.objects.new(name, me)  ##########
                            bpy.context.collection.objects.link(ob)  #########
                            me.from_pydata(verts, [], faces)  ############

                            for mat in bpy.data.materials:  ##############
                                blendMats.append(mat.name)  #############
                            for mName in povMats:  #####################
                                if mName not in blendMats:  ###########
                                    povMat = bpy.data.materials.new(
                                        mName
                                    )  #################
                                    mat_search(cache)
                                ob.data.materials.append(
                                    bpy.data.materials[mName]
                                )  ###################
                            if materials:  ##################
                                for i, val in enumerate(
                                    materials
                                ):  ####################
                                    try:  ###################
                                        ob.data.polygons[
                                            i
                                        ].material_index = (
                                            val
                                        )  ####################
                                    except TypeError:  ###################
                                        ob.data.polygons[
                                            i
                                        ].material_index = int(
                                            val[0]
                                        )  ##################

                            blendMats = []  #########################
                            povMats = []  #########################
                            materials = []  #########################
                            cache = []
                            name_search = True
                            if name in matrixes and self.import_at_cur == False:
                                global_matrix = Matrix.Rotation(
                                    pi / 2.0, 4, 'X'
                                )
                                ob = bpy.context.object
                                matrix = ob.matrix_world
                                v = matrixes[name]
                                matrix[0][0] = v[0]
                                matrix[1][0] = v[1]
                                matrix[2][0] = v[2]
                                matrix[0][1] = v[3]
                                matrix[1][1] = v[4]
                                matrix[2][1] = v[5]
                                matrix[0][2] = v[6]
                                matrix[1][2] = v[7]
                                matrix[2][2] = v[8]
                                matrix[0][3] = v[9]
                                matrix[1][3] = v[10]
                                matrix[2][3] = v[11]
                                matrix = global_matrix * ob.matrix_world
                                ob.matrix_world = matrix
                            verts = []
                            faces = []

                    # if word == 'pigment':
                    # try:
                    # #all indices have been incremented once to fit a bad test file
                    # r,g,b,t = float(S[2]),float(S[3]),float(S[4]),float(S[5])
                    # color = (r,g,b,t)

                    # except (IndexError):
                    # #all indices have been incremented once to fit alternate test file
                    # r,g,b,t = float(S[3]),float(S[4]),float(S[5]),float(S[6])
                    # color = (r,g,b,t)
                    # except UnboundLocalError:
                    # # In case no transmit is specified ? put it to 0
                    # r,g,b,t = float(S[2]),float(S[3]),float(S[4],0)
                    # color = (r,g,b,t)

                    # except (ValueError):
                    # color = (0.8,0.8,0.8,0)
                    # pass

                    # if colors == [] or (colors != [] and color not in colors):
                    # colors.append(color)
                    # name = ob.name+"_mat"
                    # matNames.append(name)
                    # mat = bpy.data.materials.new(name)
                    # mat.diffuse_color = (r,g,b)
                    # mat.alpha = 1-t
                    # if mat.alpha != 1:
                    # mat.use_transparency=True
                    # ob.data.materials.append(mat)
                    # print (colors)
                    # else:
                    # for i in range(len(colors)):
                    # if color == colors[i]:
                    # ob.data.materials.append(bpy.data.materials[matNames[i]])

        ##To keep Avogadro Camera angle:
        # for obj in bpy.context.view_layer.objects:
        # if obj.type == "CAMERA":
        # track = obj.constraints.new(type = "TRACK_TO")
        # track.target = ob
        # track.track_axis ="TRACK_NEGATIVE_Z"
        # track.up_axis = "UP_Y"
        # obj.location = (0,0,0)
        return {'FINISHED'}


classes = (
    POVRAY_OT_lathe_add,
    POVRAY_OT_superellipsoid_add,
    POVRAY_OT_superellipsoid_update,
    POVRAY_OT_supertorus_add,
    POVRAY_OT_supertorus_update,
    POVRAY_OT_loft_add,
    POVRAY_OT_plane_add,
    POVRAY_OT_box_add,
    POVRAY_OT_cylinder_add,
    POVRAY_OT_cylinder_update,
    POVRAY_OT_sphere_add,
    POVRAY_OT_sphere_update,
    POVRAY_OT_cone_add,
    POVRAY_OT_cone_update,
    POVRAY_OT_isosurface_box_add,
    POVRAY_OT_isosurface_sphere_add,
    POVRAY_OT_sphere_sweep_add,
    POVRAY_OT_blob_add,
    POVRAY_OT_rainbow_add,
    POVRAY_OT_height_field_add,
    POVRAY_OT_torus_add,
    POVRAY_OT_torus_update,
    POVRAY_OT_prism_add,
    POVRAY_OT_parametric_add,
    POVRAY_OT_parametric_update,
    POVRAY_OT_shape_polygon_to_circle_add,
    ImportPOV,
)


def register():
    # from bpy.utils import register_class

    for cls in classes:
        register_class(cls)


def unregister():
    from bpy.utils import unregister_class

    for cls in classes:
        unregister_class(cls)