1# ##### BEGIN GPL LICENSE BLOCK #####
2#
3#  This program is free software; you can redistribute it and/or
4#  modify it under the terms of the GNU General Public License
5#  as published by the Free Software Foundation; either version 2
6#  of the License, or (at your option) any later version.
7#
8#  This program is distributed in the hope that it will be useful,
9#  but WITHOUT ANY WARRANTY; without even the implied warranty of
10#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11#  GNU General Public License for more details.
12#
13#  You should have received a copy of the GNU General Public License
14#  along with this program; if not, write to the Free Software Foundation,
15#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16#
17# ##### END GPL LICENSE BLOCK #####
18
19# <pep8 compliant>
20
21""" Get POV-Ray specific objects In and Out of Blender """
22
23import bpy
24import os.path
25from bpy_extras.io_utils import ImportHelper
26from bpy_extras import object_utils
27from bpy.utils import register_class
28from math import atan, pi, degrees, sqrt, cos, sin
29from bpy.types import Operator
30
31from bpy.props import (
32    StringProperty,
33    BoolProperty,
34    IntProperty,
35    FloatProperty,
36    FloatVectorProperty,
37    EnumProperty,
38    PointerProperty,
39    CollectionProperty,
40)
41
42from mathutils import Vector, Matrix
43
44
45# import collections
46
47
48def pov_define_mesh(mesh, verts, edges, faces, name, hide_geometry=True):
49    """Generate proxy mesh."""
50    if mesh is None:
51        mesh = bpy.data.meshes.new(name)
52    mesh.from_pydata(verts, edges, faces)
53    mesh.update()
54    mesh.validate(
55        verbose=False
56    )  # Set it to True to see debug messages (helps ensure you generate valid geometry).
57    if hide_geometry:
58        mesh.vertices.foreach_set("hide", [True] * len(mesh.vertices))
59        mesh.edges.foreach_set("hide", [True] * len(mesh.edges))
60        mesh.polygons.foreach_set("hide", [True] * len(mesh.polygons))
61    return mesh
62
63
64class POVRAY_OT_lathe_add(Operator):
65    """Add the representation of POV lathe using a screw modifier."""
66
67    bl_idname = "pov.addlathe"
68    bl_label = "Lathe"
69    bl_options = {'REGISTER', 'UNDO'}
70    bl_description = "adds lathe"
71
72    def execute(self, context):
73        # ayers=[False]*20
74        # layers[0]=True
75        bpy.ops.curve.primitive_bezier_curve_add(
76            location=context.scene.cursor.location,
77            rotation=(0, 0, 0),
78            # layers=layers,
79        )
80        ob = context.view_layer.objects.active
81        ob_data = ob.data
82        ob.name = ob_data.name = "PovLathe"
83        ob_data.dimensions = '2D'
84        ob_data.transform(Matrix.Rotation(-pi / 2.0, 4, 'Z'))
85        ob.pov.object_as = 'LATHE'
86        self.report({'INFO'}, "This native POV-Ray primitive")
87        ob.pov.curveshape = "lathe"
88        bpy.ops.object.modifier_add(type='SCREW')
89        mod = ob.modifiers[-1]
90        mod.axis = 'Y'
91        mod.show_render = False
92        return {'FINISHED'}
93
94
95def pov_superellipsoid_define(context, op, ob):
96    """Create the proxy mesh of a POV superellipsoid using the pov_superellipsoid_define() function."""
97
98    if op:
99        mesh = None
100
101        u = op.se_u
102        v = op.se_v
103        n1 = op.se_n1
104        n2 = op.se_n2
105        edit = op.se_edit
106        se_param1 = n2  # op.se_param1
107        se_param2 = n1  # op.se_param2
108
109    else:
110        assert ob
111        mesh = ob.data
112
113        u = ob.pov.se_u
114        v = ob.pov.se_v
115        n1 = ob.pov.se_n1
116        n2 = ob.pov.se_n2
117        edit = ob.pov.se_edit
118        se_param1 = ob.pov.se_param1
119        se_param2 = ob.pov.se_param2
120
121    verts = []
122    r = 1
123
124    stepSegment = 360 / v * pi / 180
125    stepRing = pi / u
126    angSegment = 0
127    angRing = -pi / 2
128
129    step = 0
130    for ring in range(0, u - 1):
131        angRing += stepRing
132        for segment in range(0, v):
133            step += 1
134            angSegment += stepSegment
135            x = r * (abs(cos(angRing)) ** n1) * (abs(cos(angSegment)) ** n2)
136            if (cos(angRing) < 0 and cos(angSegment) > 0) or (
137                cos(angRing) > 0 and cos(angSegment) < 0
138            ):
139                x = -x
140            y = r * (abs(cos(angRing)) ** n1) * (abs(sin(angSegment)) ** n2)
141            if (cos(angRing) < 0 and sin(angSegment) > 0) or (
142                cos(angRing) > 0 and sin(angSegment) < 0
143            ):
144                y = -y
145            z = r * (abs(sin(angRing)) ** n1)
146            if sin(angRing) < 0:
147                z = -z
148            x = round(x, 4)
149            y = round(y, 4)
150            z = round(z, 4)
151            verts.append((x, y, z))
152    if edit == 'TRIANGLES':
153        verts.append((0, 0, 1))
154        verts.append((0, 0, -1))
155
156    faces = []
157
158    for i in range(0, u - 2):
159        m = i * v
160        for p in range(0, v):
161            if p < v - 1:
162                face = (m + p, 1 + m + p, v + 1 + m + p, v + m + p)
163            if p == v - 1:
164                face = (m + p, m, v + m, v + m + p)
165            faces.append(face)
166    if edit == 'TRIANGLES':
167        indexUp = len(verts) - 2
168        indexDown = len(verts) - 1
169        indexStartDown = len(verts) - 2 - v
170        for i in range(0, v):
171            if i < v - 1:
172                face = (indexDown, i, i + 1)
173                faces.append(face)
174            if i == v - 1:
175                face = (indexDown, i, 0)
176                faces.append(face)
177        for i in range(0, v):
178            if i < v - 1:
179                face = (indexUp, i + indexStartDown, i + indexStartDown + 1)
180                faces.append(face)
181            if i == v - 1:
182                face = (indexUp, i + indexStartDown, indexStartDown)
183                faces.append(face)
184    if edit == 'NGONS':
185        face = []
186        for i in range(0, v):
187            face.append(i)
188        faces.append(face)
189        face = []
190        indexUp = len(verts) - 1
191        for i in range(0, v):
192            face.append(indexUp - i)
193        faces.append(face)
194    mesh = pov_define_mesh(mesh, verts, [], faces, "SuperEllipsoid")
195
196    if not ob:
197        ob = object_utils.object_data_add(context, mesh, operator=None)
198        # engine = context.scene.render.engine what for?
199        ob = context.object
200        ob.name = ob.data.name = "PovSuperellipsoid"
201        ob.pov.object_as = 'SUPERELLIPSOID'
202        ob.pov.se_param1 = n2
203        ob.pov.se_param2 = n1
204
205        ob.pov.se_u = u
206        ob.pov.se_v = v
207        ob.pov.se_n1 = n1
208        ob.pov.se_n2 = n2
209        ob.pov.se_edit = edit
210
211        bpy.ops.object.mode_set(mode="EDIT")
212        bpy.ops.mesh.hide(unselected=False)
213        bpy.ops.object.mode_set(mode="OBJECT")
214
215
216class POVRAY_OT_superellipsoid_add(Operator):
217    """Add the representation of POV superellipsoid using the pov_superellipsoid_define() function."""
218
219    bl_idname = "pov.addsuperellipsoid"
220    bl_label = "Add SuperEllipsoid"
221    bl_description = "Create a SuperEllipsoid"
222    bl_options = {'REGISTER', 'UNDO'}
223    COMPAT_ENGINES = {'POVRAY_RENDER'}
224
225    # XXX Keep it in sync with __init__'s RenderPovSettingsConePrimitive
226    #     If someone knows how to define operators' props from a func, I'd be delighted to learn it!
227    se_param1: FloatProperty(
228        name="Parameter 1", description="", min=0.00, max=10.0, default=0.04
229    )
230
231    se_param2: FloatProperty(
232        name="Parameter 2", description="", min=0.00, max=10.0, default=0.04
233    )
234
235    se_u: IntProperty(
236        name="U-segments",
237        description="radial segmentation",
238        default=20,
239        min=4,
240        max=265,
241    )
242    se_v: IntProperty(
243        name="V-segments",
244        description="lateral segmentation",
245        default=20,
246        min=4,
247        max=265,
248    )
249    se_n1: FloatProperty(
250        name="Ring manipulator",
251        description="Manipulates the shape of the Ring",
252        default=1.0,
253        min=0.01,
254        max=100.0,
255    )
256    se_n2: FloatProperty(
257        name="Cross manipulator",
258        description="Manipulates the shape of the cross-section",
259        default=1.0,
260        min=0.01,
261        max=100.0,
262    )
263    se_edit: EnumProperty(
264        items=[
265            ("NOTHING", "Nothing", ""),
266            ("NGONS", "N-Gons", ""),
267            ("TRIANGLES", "Triangles", ""),
268        ],
269        name="Fill up and down",
270        description="",
271        default='TRIANGLES',
272    )
273
274    @classmethod
275    def poll(cls, context):
276        engine = context.scene.render.engine
277        return engine in cls.COMPAT_ENGINES
278
279    def execute(self, context):
280        pov_superellipsoid_define(context, self, None)
281
282        self.report(
283            {'INFO'},
284            "This native POV-Ray primitive won't have any vertex to show in edit mode",
285        )
286
287        return {'FINISHED'}
288
289
290class POVRAY_OT_superellipsoid_update(Operator):
291    """Update the superellipsoid.
292
293    Delete its previous proxy geometry and rerun pov_superellipsoid_define() function
294    with the new parameters"""
295
296    bl_idname = "pov.superellipsoid_update"
297    bl_label = "Update"
298    bl_description = "Update Superellipsoid"
299    bl_options = {'REGISTER', 'UNDO'}
300    COMPAT_ENGINES = {'POVRAY_RENDER'}
301
302    @classmethod
303    def poll(cls, context):
304        engine = context.scene.render.engine
305        ob = context.object
306        return (
307            ob
308            and ob.data
309            and ob.type == 'MESH'
310            and engine in cls.COMPAT_ENGINES
311        )
312
313    def execute(self, context):
314        bpy.ops.object.mode_set(mode="EDIT")
315        bpy.ops.mesh.reveal()
316        bpy.ops.mesh.select_all(action='SELECT')
317        bpy.ops.mesh.delete(type='VERT')
318        bpy.ops.object.mode_set(mode="OBJECT")
319
320        pov_superellipsoid_define(context, None, context.object)
321
322        return {'FINISHED'}
323
324
325def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False):
326    faces = []
327    if not vertIdx1 or not vertIdx2:
328        return None
329    if len(vertIdx1) < 2 and len(vertIdx2) < 2:
330        return None
331    fan = False
332    if len(vertIdx1) != len(vertIdx2):
333        if len(vertIdx1) == 1 and len(vertIdx2) > 1:
334            fan = True
335        else:
336            return None
337    total = len(vertIdx2)
338    if closed:
339        if flipped:
340            face = [vertIdx1[0], vertIdx2[0], vertIdx2[total - 1]]
341            if not fan:
342                face.append(vertIdx1[total - 1])
343            faces.append(face)
344
345        else:
346            face = [vertIdx2[0], vertIdx1[0]]
347            if not fan:
348                face.append(vertIdx1[total - 1])
349            face.append(vertIdx2[total - 1])
350            faces.append(face)
351    for num in range(total - 1):
352        if flipped:
353            if fan:
354                face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
355            else:
356                face = [
357                    vertIdx2[num],
358                    vertIdx1[num],
359                    vertIdx1[num + 1],
360                    vertIdx2[num + 1],
361                ]
362            faces.append(face)
363        else:
364            if fan:
365                face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
366            else:
367                face = [
368                    vertIdx1[num],
369                    vertIdx2[num],
370                    vertIdx2[num + 1],
371                    vertIdx1[num + 1],
372                ]
373            faces.append(face)
374
375    return faces
376
377
378def power(a, b):
379    if a < 0:
380        return -((-a) ** b)
381    return a ** b
382
383
384def supertoroid(R, r, u, v, n1, n2):
385    a = 2 * pi / u
386    b = 2 * pi / v
387    verts = []
388    faces = []
389    for i in range(u):
390        s = power(sin(i * a), n1)
391        c = power(cos(i * a), n1)
392        for j in range(v):
393            c2 = R + r * power(cos(j * b), n2)
394            s2 = r * power(sin(j * b), n2)
395            verts.append(
396                (c * c2, s * c2, s2)
397            )  # type as a (mathutils.Vector(c*c2,s*c2,s2))?
398        if i > 0:
399            f = createFaces(
400                range((i - 1) * v, i * v),
401                range(i * v, (i + 1) * v),
402                closed=True,
403            )
404            faces.extend(f)
405    f = createFaces(range((u - 1) * v, u * v), range(v), closed=True)
406    faces.extend(f)
407    return verts, faces
408
409
410def pov_supertorus_define(context, op, ob):
411    if op:
412        mesh = None
413        st_R = op.st_R
414        st_r = op.st_r
415        st_u = op.st_u
416        st_v = op.st_v
417        st_n1 = op.st_n1
418        st_n2 = op.st_n2
419        st_ie = op.st_ie
420        st_edit = op.st_edit
421
422    else:
423        assert ob
424        mesh = ob.data
425        st_R = ob.pov.st_major_radius
426        st_r = ob.pov.st_minor_radius
427        st_u = ob.pov.st_u
428        st_v = ob.pov.st_v
429        st_n1 = ob.pov.st_ring
430        st_n2 = ob.pov.st_cross
431        st_ie = ob.pov.st_ie
432        st_edit = ob.pov.st_edit
433
434    if st_ie:
435        rad1 = (st_R + st_r) / 2
436        rad2 = (st_R - st_r) / 2
437        if rad2 > rad1:
438            [rad1, rad2] = [rad2, rad1]
439    else:
440        rad1 = st_R
441        rad2 = st_r
442        if rad2 > rad1:
443            rad1 = rad2
444    verts, faces = supertoroid(rad1, rad2, st_u, st_v, st_n1, st_n2)
445    mesh = pov_define_mesh(mesh, verts, [], faces, "PovSuperTorus", True)
446    if not ob:
447        ob = object_utils.object_data_add(context, mesh, operator=None)
448        ob.pov.object_as = 'SUPERTORUS'
449        ob.pov.st_major_radius = st_R
450        ob.pov.st_minor_radius = st_r
451        ob.pov.st_u = st_u
452        ob.pov.st_v = st_v
453        ob.pov.st_ring = st_n1
454        ob.pov.st_cross = st_n2
455        ob.pov.st_ie = st_ie
456        ob.pov.st_edit = st_edit
457
458
459class POVRAY_OT_supertorus_add(Operator):
460    """Add the representation of POV supertorus using the pov_supertorus_define() function."""
461
462    bl_idname = "pov.addsupertorus"
463    bl_label = "Add Supertorus"
464    bl_description = "Create a SuperTorus"
465    bl_options = {'REGISTER', 'UNDO'}
466    COMPAT_ENGINES = {'POVRAY_RENDER'}
467
468    st_R: FloatProperty(
469        name="big radius",
470        description="The radius inside the tube",
471        default=1.0,
472        min=0.01,
473        max=100.0,
474    )
475    st_r: FloatProperty(
476        name="small radius",
477        description="The radius of the tube",
478        default=0.3,
479        min=0.01,
480        max=100.0,
481    )
482    st_u: IntProperty(
483        name="U-segments",
484        description="radial segmentation",
485        default=16,
486        min=3,
487        max=265,
488    )
489    st_v: IntProperty(
490        name="V-segments",
491        description="lateral segmentation",
492        default=8,
493        min=3,
494        max=265,
495    )
496    st_n1: FloatProperty(
497        name="Ring manipulator",
498        description="Manipulates the shape of the Ring",
499        default=1.0,
500        min=0.01,
501        max=100.0,
502    )
503    st_n2: FloatProperty(
504        name="Cross manipulator",
505        description="Manipulates the shape of the cross-section",
506        default=1.0,
507        min=0.01,
508        max=100.0,
509    )
510    st_ie: BoolProperty(
511        name="Use Int.+Ext. radii",
512        description="Use internal and external radii",
513        default=False,
514    )
515    st_edit: BoolProperty(
516        name="", description="", default=False, options={'HIDDEN'}
517    )
518
519    @classmethod
520    def poll(cls, context):
521        engine = context.scene.render.engine
522        return engine in cls.COMPAT_ENGINES
523
524    def execute(self, context):
525        pov_supertorus_define(context, self, None)
526
527        self.report(
528            {'INFO'},
529            "This native POV-Ray primitive won't have any vertex to show in edit mode",
530        )
531        return {'FINISHED'}
532
533
534class POVRAY_OT_supertorus_update(Operator):
535    """Update the supertorus.
536
537    Delete its previous proxy geometry and rerun pov_supetorus_define() function
538    with the new parameters"""
539
540    bl_idname = "pov.supertorus_update"
541    bl_label = "Update"
542    bl_description = "Update SuperTorus"
543    bl_options = {'REGISTER', 'UNDO'}
544    COMPAT_ENGINES = {'POVRAY_RENDER'}
545
546    @classmethod
547    def poll(cls, context):
548        engine = context.scene.render.engine
549        ob = context.object
550        return (
551            ob
552            and ob.data
553            and ob.type == 'MESH'
554            and engine in cls.COMPAT_ENGINES
555        )
556
557    def execute(self, context):
558        bpy.ops.object.mode_set(mode="EDIT")
559        bpy.ops.mesh.reveal()
560        bpy.ops.mesh.select_all(action='SELECT')
561        bpy.ops.mesh.delete(type='VERT')
562        bpy.ops.object.mode_set(mode="OBJECT")
563
564        pov_supertorus_define(context, None, context.object)
565
566        return {'FINISHED'}
567
568
569#########################################################################################################
570class POVRAY_OT_loft_add(Operator):
571    """Create the representation of POV loft using Blender curves."""
572
573    bl_idname = "pov.addloft"
574    bl_label = "Add Loft Data"
575    bl_description = "Create a Curve data for Meshmaker"
576    bl_options = {'REGISTER', 'UNDO'}
577    COMPAT_ENGINES = {'POVRAY_RENDER'}
578
579    loft_n: IntProperty(
580        name="Segments",
581        description="Vertical segments",
582        default=16,
583        min=3,
584        max=720,
585    )
586    loft_rings_bottom: IntProperty(
587        name="Bottom", description="Bottom rings", default=5, min=2, max=100
588    )
589    loft_rings_side: IntProperty(
590        name="Side", description="Side rings", default=10, min=2, max=100
591    )
592    loft_thick: FloatProperty(
593        name="Thickness",
594        description="Manipulates the shape of the Ring",
595        default=0.3,
596        min=0.01,
597        max=1.0,
598    )
599    loft_r: FloatProperty(
600        name="Radius", description="Radius", default=1, min=0.01, max=10
601    )
602    loft_height: FloatProperty(
603        name="Height",
604        description="Manipulates the shape of the Ring",
605        default=2,
606        min=0.01,
607        max=10.0,
608    )
609
610    def execute(self, context):
611
612        props = self.properties
613        loftData = bpy.data.curves.new('Loft', type='CURVE')
614        loftData.dimensions = '3D'
615        loftData.resolution_u = 2
616        # loftData.show_normal_face = False # deprecated in 2.8
617        n = props.loft_n
618        thick = props.loft_thick
619        side = props.loft_rings_side
620        bottom = props.loft_rings_bottom
621        h = props.loft_height
622        r = props.loft_r
623        distB = r / bottom
624        r0 = 0.00001
625        z = -h / 2
626        print("New")
627        for i in range(bottom + 1):
628            coords = []
629            angle = 0
630            for p in range(n):
631                x = r0 * cos(angle)
632                y = r0 * sin(angle)
633                coords.append((x, y, z))
634                angle += pi * 2 / n
635            r0 += distB
636            nurbs = loftData.splines.new('NURBS')
637            nurbs.points.add(len(coords) - 1)
638            for i, coord in enumerate(coords):
639                x, y, z = coord
640                nurbs.points[i].co = (x, y, z, 1)
641            nurbs.use_cyclic_u = True
642        for i in range(side):
643            z += h / side
644            coords = []
645            angle = 0
646            for p in range(n):
647                x = r * cos(angle)
648                y = r * sin(angle)
649                coords.append((x, y, z))
650                angle += pi * 2 / n
651            nurbs = loftData.splines.new('NURBS')
652            nurbs.points.add(len(coords) - 1)
653            for i, coord in enumerate(coords):
654                x, y, z = coord
655                nurbs.points[i].co = (x, y, z, 1)
656            nurbs.use_cyclic_u = True
657        r -= thick
658        for i in range(side):
659            coords = []
660            angle = 0
661            for p in range(n):
662                x = r * cos(angle)
663                y = r * sin(angle)
664                coords.append((x, y, z))
665                angle += pi * 2 / n
666            nurbs = loftData.splines.new('NURBS')
667            nurbs.points.add(len(coords) - 1)
668            for i, coord in enumerate(coords):
669                x, y, z = coord
670                nurbs.points[i].co = (x, y, z, 1)
671            nurbs.use_cyclic_u = True
672            z -= h / side
673        z = (-h / 2) + thick
674        distB = (r - 0.00001) / bottom
675        for i in range(bottom + 1):
676            coords = []
677            angle = 0
678            for p in range(n):
679                x = r * cos(angle)
680                y = r * sin(angle)
681                coords.append((x, y, z))
682                angle += pi * 2 / n
683            r -= distB
684            nurbs = loftData.splines.new('NURBS')
685            nurbs.points.add(len(coords) - 1)
686            for i, coord in enumerate(coords):
687                x, y, z = coord
688                nurbs.points[i].co = (x, y, z, 1)
689            nurbs.use_cyclic_u = True
690        ob = bpy.data.objects.new('Loft_shape', loftData)
691        scn = bpy.context.scene
692        scn.collection.objects.link(ob)
693        context.view_layer.objects.active = ob
694        ob.select_set(True)
695        ob.pov.curveshape = "loft"
696        return {'FINISHED'}
697
698
699class POVRAY_OT_plane_add(Operator):
700    """Add the representation of POV infinite plane using just a very big Blender Plane.
701
702    Flag its primitive type with a specific pov.object_as attribute and lock edit mode
703    to keep proxy consistency by hiding edit geometry."""
704
705    bl_idname = "pov.addplane"
706    bl_label = "Plane"
707    bl_description = "Add Plane"
708    bl_options = {'REGISTER', 'UNDO'}
709
710    def execute(self, context):
711        # layers = 20*[False]
712        # layers[0] = True
713        bpy.ops.mesh.primitive_plane_add(size=100000)
714        ob = context.object
715        ob.name = ob.data.name = 'PovInfinitePlane'
716        bpy.ops.object.mode_set(mode="EDIT")
717        self.report(
718            {'INFO'},
719            "This native POV-Ray primitive "
720            "won't have any vertex to show in edit mode",
721        )
722        bpy.ops.mesh.hide(unselected=False)
723        bpy.ops.object.mode_set(mode="OBJECT")
724        bpy.ops.object.shade_smooth()
725        ob.pov.object_as = "PLANE"
726        return {'FINISHED'}
727
728
729class POVRAY_OT_box_add(Operator):
730    """Add the representation of POV box using a simple Blender mesh cube.
731
732    Flag its primitive type with a specific pov.object_as attribute and lock edit mode
733    to keep proxy consistency by hiding edit geometry."""
734
735    bl_idname = "pov.addbox"
736    bl_label = "Box"
737    bl_description = "Add Box"
738    bl_options = {'REGISTER', 'UNDO'}
739
740    def execute(self, context):
741        # layers = 20*[False]
742        # layers[0] = True
743        bpy.ops.mesh.primitive_cube_add()
744        ob = context.object
745        ob.name = ob.data.name = 'PovBox'
746        bpy.ops.object.mode_set(mode="EDIT")
747        self.report(
748            {'INFO'},
749            "This native POV-Ray primitive "
750            "won't have any vertex to show in edit mode",
751        )
752        bpy.ops.mesh.hide(unselected=False)
753        bpy.ops.object.mode_set(mode="OBJECT")
754        ob.pov.object_as = "BOX"
755        return {'FINISHED'}
756
757
758def pov_cylinder_define(context, op, ob, radius, loc, loc_cap):
759    if op:
760        R = op.R
761        loc = bpy.context.scene.cursor.location
762        loc_cap[0] = loc[0]
763        loc_cap[1] = loc[1]
764        loc_cap[2] = loc[2] + 2
765    vec = Vector(loc_cap) - Vector(loc)
766    depth = vec.length
767    rot = Vector((0, 0, 1)).rotation_difference(vec)  # Rotation from Z axis.
768    trans = rot @ Vector(
769        (0, 0, depth / 2)
770    )  # Such that origin is at center of the base of the cylinder.
771    roteuler = rot.to_euler()
772    if not ob:
773        bpy.ops.object.add(type='MESH', location=loc)
774        ob = context.object
775        ob.name = ob.data.name = "PovCylinder"
776        ob.pov.cylinder_radius = radius
777        ob.pov.cylinder_location_cap = vec
778        ob.pov.object_as = "CYLINDER"
779    else:
780        ob.location = loc
781
782    bpy.ops.object.mode_set(mode="EDIT")
783    bpy.ops.mesh.reveal()
784    bpy.ops.mesh.select_all(action='SELECT')
785    bpy.ops.mesh.delete(type='VERT')
786    bpy.ops.mesh.primitive_cylinder_add(
787        radius=radius,
788        depth=depth,
789        location=loc,
790        rotation=roteuler,
791        end_fill_type='NGON',
792    )  #'NOTHING'
793    bpy.ops.transform.translate(value=trans)
794
795    bpy.ops.mesh.hide(unselected=False)
796    bpy.ops.object.mode_set(mode="OBJECT")
797    bpy.ops.object.shade_smooth()
798
799
800class POVRAY_OT_cylinder_add(Operator):
801    """Add the representation of POV cylinder using pov_cylinder_define() function.
802
803    Use imported_cyl_loc when this operator is run by POV importer."""
804
805    bl_idname = "pov.addcylinder"
806    bl_label = "Cylinder"
807    bl_description = "Add Cylinder"
808    bl_options = {'REGISTER', 'UNDO'}
809
810    # XXX Keep it in sync with __init__'s cylinder Primitive
811    R: FloatProperty(name="Cylinder radius", min=0.00, max=10.0, default=1.0)
812
813    imported_cyl_loc: FloatVectorProperty(
814        name="Imported Pov base location", precision=6, default=(0.0, 0.0, 0.0)
815    )
816
817    imported_cyl_loc_cap: FloatVectorProperty(
818        name="Imported Pov cap location", precision=6, default=(0.0, 0.0, 2.0)
819    )
820
821    def execute(self, context):
822        props = self.properties
823        R = props.R
824        ob = context.object
825        # layers = 20*[False]
826        # layers[0] = True
827        if ob:
828            if ob.pov.imported_cyl_loc:
829                LOC = ob.pov.imported_cyl_loc
830            if ob.pov.imported_cyl_loc_cap:
831                LOC_CAP = ob.pov.imported_cyl_loc_cap
832        else:
833            if not props.imported_cyl_loc:
834                LOC_CAP = LOC = bpy.context.scene.cursor.location
835                LOC_CAP[2] += 2.0
836            else:
837                LOC = props.imported_cyl_loc
838                LOC_CAP = props.imported_cyl_loc_cap
839            self.report(
840                {'INFO'},
841                "This native POV-Ray primitive "
842                "won't have any vertex to show in edit mode",
843            )
844
845        pov_cylinder_define(context, self, None, self.R, LOC, LOC_CAP)
846
847        return {'FINISHED'}
848
849
850class POVRAY_OT_cylinder_update(Operator):
851    """Update the POV cylinder.
852
853    Delete its previous proxy geometry and rerun pov_cylinder_define() function
854    with the new parameters"""
855
856    bl_idname = "pov.cylinder_update"
857    bl_label = "Update"
858    bl_description = "Update Cylinder"
859    bl_options = {'REGISTER', 'UNDO'}
860    COMPAT_ENGINES = {'POVRAY_RENDER'}
861
862    @classmethod
863    def poll(cls, context):
864        engine = context.scene.render.engine
865        ob = context.object
866        return (
867            ob
868            and ob.data
869            and ob.type == 'MESH'
870            and ob.pov.object_as == "CYLINDER"
871            and engine in cls.COMPAT_ENGINES
872        )
873
874    def execute(self, context):
875        ob = context.object
876        radius = ob.pov.cylinder_radius
877        loc = ob.location
878        loc_cap = loc + ob.pov.cylinder_location_cap
879
880        pov_cylinder_define(context, None, ob, radius, loc, loc_cap)
881
882        return {'FINISHED'}
883
884
885################################SPHERE##########################################
886def pov_sphere_define(context, op, ob, loc):
887    """create the representation of POV sphere using a Blender icosphere.
888
889    Its nice platonic solid curvature better represents pov rendertime
890    tesselation than a UV sphere"""
891
892    if op:
893        R = op.R
894        loc = bpy.context.scene.cursor.location
895    else:
896        assert ob
897        R = ob.pov.sphere_radius
898
899        # keep object rotation and location for the add object operator
900        obrot = ob.rotation_euler
901        # obloc = ob.location
902        obscale = ob.scale
903
904        bpy.ops.object.mode_set(mode="EDIT")
905        bpy.ops.mesh.reveal()
906        bpy.ops.mesh.select_all(action='SELECT')
907        bpy.ops.mesh.delete(type='VERT')
908        bpy.ops.mesh.primitive_ico_sphere_add(
909            subdivisions=4,
910            radius=ob.pov.sphere_radius,
911            location=loc,
912            rotation=obrot,
913        )
914        # bpy.ops.transform.rotate(axis=obrot,orient_type='GLOBAL')
915        bpy.ops.transform.resize(value=obscale)
916        # bpy.ops.transform.rotate(axis=obrot, proportional_size=1)
917
918        bpy.ops.mesh.hide(unselected=False)
919        bpy.ops.object.mode_set(mode="OBJECT")
920        bpy.ops.object.shade_smooth()
921        # bpy.ops.transform.rotate(axis=obrot,orient_type='GLOBAL')
922
923    if not ob:
924        bpy.ops.mesh.primitive_ico_sphere_add(
925            subdivisions=4, radius=R, location=loc
926        )
927        ob = context.object
928        ob.name = ob.data.name = "PovSphere"
929        ob.pov.object_as = "SPHERE"
930        ob.pov.sphere_radius = R
931        bpy.ops.object.mode_set(mode="EDIT")
932        bpy.ops.mesh.hide(unselected=False)
933        bpy.ops.object.mode_set(mode="OBJECT")
934
935
936class POVRAY_OT_sphere_add(Operator):
937    """Add the representation of POV sphere using pov_sphere_define() function.
938
939    Use imported_loc when this operator is run by POV importer."""
940
941    bl_idname = "pov.addsphere"
942    bl_label = "Sphere"
943    bl_description = "Add Sphere Shape"
944    bl_options = {'REGISTER', 'UNDO'}
945
946    # XXX Keep it in sync with __init__'s torus Primitive
947    R: FloatProperty(name="Sphere radius", min=0.00, max=10.0, default=0.5)
948
949    imported_loc: FloatVectorProperty(
950        name="Imported Pov location", precision=6, default=(0.0, 0.0, 0.0)
951    )
952
953    def execute(self, context):
954        props = self.properties
955        R = props.R
956        ob = context.object
957
958        if ob:
959            if ob.pov.imported_loc:
960                LOC = ob.pov.imported_loc
961        else:
962            if not props.imported_loc:
963                LOC = bpy.context.scene.cursor.location
964
965            else:
966                LOC = props.imported_loc
967                self.report(
968                    {'INFO'},
969                    "This native POV-Ray primitive "
970                    "won't have any vertex to show in edit mode",
971                )
972        pov_sphere_define(context, self, None, LOC)
973
974        return {'FINISHED'}
975
976    # def execute(self,context):
977    ## layers = 20*[False]
978    ## layers[0] = True
979
980    # bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=4, radius=ob.pov.sphere_radius)
981    # ob = context.object
982    # bpy.ops.object.mode_set(mode="EDIT")
983    # self.report({'INFO'}, "This native POV-Ray primitive "
984    # "won't have any vertex to show in edit mode")
985    # bpy.ops.mesh.hide(unselected=False)
986    # bpy.ops.object.mode_set(mode="OBJECT")
987    # bpy.ops.object.shade_smooth()
988    # ob.pov.object_as = "SPHERE"
989    # ob.name = ob.data.name = 'PovSphere'
990    # return {'FINISHED'}
991
992
993class POVRAY_OT_sphere_update(Operator):
994    """Update the POV sphere.
995
996    Delete its previous proxy geometry and rerun pov_sphere_define() function
997    with the new parameters"""
998
999    bl_idname = "pov.sphere_update"
1000    bl_label = "Update"
1001    bl_description = "Update Sphere"
1002    bl_options = {'REGISTER', 'UNDO'}
1003    COMPAT_ENGINES = {'POVRAY_RENDER'}
1004
1005    @classmethod
1006    def poll(cls, context):
1007        engine = context.scene.render.engine
1008        ob = context.object
1009        return (
1010            ob
1011            and ob.data
1012            and ob.type == 'MESH'
1013            and engine in cls.COMPAT_ENGINES
1014        )
1015
1016    def execute(self, context):
1017
1018        pov_sphere_define(
1019            context, None, context.object, context.object.location
1020        )
1021
1022        return {'FINISHED'}
1023
1024
1025####################################CONE#######################################
1026def pov_cone_define(context, op, ob):
1027    """Add the representation of POV cone using pov_define_mesh() function.
1028
1029    Blender cone does not offer the same features such as a second radius."""
1030    verts = []
1031    faces = []
1032    if op:
1033        mesh = None
1034        base = op.base
1035        cap = op.cap
1036        seg = op.seg
1037        height = op.height
1038    else:
1039        assert ob
1040        mesh = ob.data
1041        base = ob.pov.cone_base_radius
1042        cap = ob.pov.cone_cap_radius
1043        seg = ob.pov.cone_segments
1044        height = ob.pov.cone_height
1045
1046    zc = height / 2
1047    zb = -zc
1048    angle = 2 * pi / seg
1049    t = 0
1050    for i in range(seg):
1051        xb = base * cos(t)
1052        yb = base * sin(t)
1053        xc = cap * cos(t)
1054        yc = cap * sin(t)
1055        verts.append((xb, yb, zb))
1056        verts.append((xc, yc, zc))
1057        t += angle
1058    for i in range(seg):
1059        f = i * 2
1060        if i == seg - 1:
1061            faces.append([0, 1, f + 1, f])
1062        else:
1063            faces.append([f + 2, f + 3, f + 1, f])
1064    if base != 0:
1065        base_face = []
1066        for i in range(seg - 1, -1, -1):
1067            p = i * 2
1068            base_face.append(p)
1069        faces.append(base_face)
1070    if cap != 0:
1071        cap_face = []
1072        for i in range(seg):
1073            p = i * 2 + 1
1074            cap_face.append(p)
1075        faces.append(cap_face)
1076
1077    mesh = pov_define_mesh(mesh, verts, [], faces, "PovCone", True)
1078    if not ob:
1079        ob = object_utils.object_data_add(context, mesh, operator=None)
1080        ob.pov.object_as = "CONE"
1081        ob.pov.cone_base_radius = base
1082        ob.pov.cone_cap_radius = cap
1083        ob.pov.cone_height = height
1084        ob.pov.cone_base_z = zb
1085        ob.pov.cone_cap_z = zc
1086
1087
1088class POVRAY_OT_cone_add(Operator):
1089    """Add the representation of POV cone using pov_cone_define() function."""
1090
1091    bl_idname = "pov.cone_add"
1092    bl_label = "Cone"
1093    bl_description = "Add Cone"
1094    bl_options = {'REGISTER', 'UNDO'}
1095    COMPAT_ENGINES = {'POVRAY_RENDER'}
1096
1097    # XXX Keep it in sync with __init__.py's RenderPovSettingsConePrimitive
1098    #     If someone knows how to define operators' props from a func, I'd be delighted to learn it!
1099    base: FloatProperty(
1100        name="Base radius",
1101        description="The first radius of the cone",
1102        default=1.0,
1103        min=0.01,
1104        max=100.0,
1105    )
1106    cap: FloatProperty(
1107        name="Cap radius",
1108        description="The second radius of the cone",
1109        default=0.3,
1110        min=0.0,
1111        max=100.0,
1112    )
1113    seg: IntProperty(
1114        name="Segments",
1115        description="Radial segmentation of the proxy mesh",
1116        default=16,
1117        min=3,
1118        max=265,
1119    )
1120    height: FloatProperty(
1121        name="Height",
1122        description="Height of the cone",
1123        default=2.0,
1124        min=0.01,
1125        max=100.0,
1126    )
1127
1128    @classmethod
1129    def poll(cls, context):
1130        engine = context.scene.render.engine
1131        return engine in cls.COMPAT_ENGINES
1132
1133    def execute(self, context):
1134        pov_cone_define(context, self, None)
1135
1136        self.report(
1137            {'INFO'},
1138            "This native POV-Ray primitive won't have any vertex to show in edit mode",
1139        )
1140        return {'FINISHED'}
1141
1142
1143class POVRAY_OT_cone_update(Operator):
1144    """Update the POV cone.
1145
1146    Delete its previous proxy geometry and rerun pov_cone_define() function
1147    with the new parameters"""
1148
1149    bl_idname = "pov.cone_update"
1150    bl_label = "Update"
1151    bl_description = "Update Cone"
1152    bl_options = {'REGISTER', 'UNDO'}
1153    COMPAT_ENGINES = {'POVRAY_RENDER'}
1154
1155    @classmethod
1156    def poll(cls, context):
1157        engine = context.scene.render.engine
1158        ob = context.object
1159        return (
1160            ob
1161            and ob.data
1162            and ob.type == 'MESH'
1163            and engine in cls.COMPAT_ENGINES
1164        )
1165
1166    def execute(self, context):
1167        bpy.ops.object.mode_set(mode="EDIT")
1168        bpy.ops.mesh.reveal()
1169        bpy.ops.mesh.select_all(action='SELECT')
1170        bpy.ops.mesh.delete(type='VERT')
1171        bpy.ops.object.mode_set(mode="OBJECT")
1172
1173        pov_cone_define(context, None, context.object)
1174
1175        return {'FINISHED'}
1176
1177
1178########################################ISOSURFACES##################################
1179
1180
1181class POVRAY_OT_isosurface_box_add(Operator):
1182    """Add the representation of POV isosurface box using also just a Blender mesh cube.
1183
1184    Flag its primitive type with a specific pov.object_as attribute and lock edit mode
1185    to keep proxy consistency by hiding edit geometry."""
1186
1187    bl_idname = "pov.addisosurfacebox"
1188    bl_label = "Isosurface Box"
1189    bl_description = "Add Isosurface contained by Box"
1190    bl_options = {'REGISTER', 'UNDO'}
1191
1192    def execute(self, context):
1193        # layers = 20*[False]
1194        # layers[0] = True
1195        bpy.ops.mesh.primitive_cube_add()
1196        ob = context.object
1197        bpy.ops.object.mode_set(mode="EDIT")
1198        self.report(
1199            {'INFO'},
1200            "This native POV-Ray primitive "
1201            "won't have any vertex to show in edit mode",
1202        )
1203        bpy.ops.mesh.hide(unselected=False)
1204        bpy.ops.object.mode_set(mode="OBJECT")
1205        ob.pov.object_as = "ISOSURFACE"
1206        ob.pov.contained_by = 'box'
1207        ob.name = 'PovIsosurfaceBox'
1208        return {'FINISHED'}
1209
1210
1211class POVRAY_OT_isosurface_sphere_add(Operator):
1212    """Add the representation of POV isosurface sphere by a Blender mesh icosphere.
1213
1214    Flag its primitive type with a specific pov.object_as attribute and lock edit mode
1215    to keep proxy consistency by hiding edit geometry."""
1216
1217    bl_idname = "pov.addisosurfacesphere"
1218    bl_label = "Isosurface Sphere"
1219    bl_description = "Add Isosurface contained by Sphere"
1220    bl_options = {'REGISTER', 'UNDO'}
1221
1222    def execute(self, context):
1223        # layers = 20*[False]
1224        # layers[0] = True
1225        bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=4)
1226        ob = context.object
1227        bpy.ops.object.mode_set(mode="EDIT")
1228        self.report(
1229            {'INFO'},
1230            "This native POV-Ray primitive "
1231            "won't have any vertex to show in edit mode",
1232        )
1233        bpy.ops.mesh.hide(unselected=False)
1234        bpy.ops.object.mode_set(mode="OBJECT")
1235        bpy.ops.object.shade_smooth()
1236        ob.pov.object_as = "ISOSURFACE"
1237        ob.pov.contained_by = 'sphere'
1238        ob.name = 'PovIsosurfaceSphere'
1239        return {'FINISHED'}
1240
1241
1242class POVRAY_OT_sphere_sweep_add(Operator):
1243    """Add the representation of POV sphere_sweep using a Blender NURBS curve.
1244
1245    Flag its primitive type with a specific ob.pov.curveshape attribute and
1246    leave access to edit mode to keep user editable handles."""
1247
1248    bl_idname = "pov.addspheresweep"
1249    bl_label = "Sphere Sweep"
1250    bl_description = "Create Sphere Sweep along curve"
1251    bl_options = {'REGISTER', 'UNDO'}
1252
1253    def execute(self, context):
1254        # layers = 20*[False]
1255        # layers[0] = True
1256        bpy.ops.curve.primitive_nurbs_curve_add()
1257        ob = context.object
1258        ob.name = ob.data.name = "PovSphereSweep"
1259        ob.pov.curveshape = "sphere_sweep"
1260        ob.data.bevel_depth = 0.02
1261        ob.data.bevel_resolution = 4
1262        ob.data.fill_mode = 'FULL'
1263        # ob.data.splines[0].order_u = 4
1264
1265        return {'FINISHED'}
1266
1267
1268class POVRAY_OT_blob_add(Operator):
1269    """Add the representation of POV blob using a Blender meta ball.
1270
1271    No need to flag its primitive type as meta are exported to blobs
1272    and leave access to edit mode to keep user editable thresholds."""
1273
1274    bl_idname = "pov.addblobsphere"
1275    bl_label = "Blob Sphere"
1276    bl_description = "Add Blob Sphere"
1277    bl_options = {'REGISTER', 'UNDO'}
1278
1279    def execute(self, context):
1280        # layers = 20*[False]
1281        # layers[0] = True
1282        bpy.ops.object.metaball_add(type='BALL')
1283        ob = context.object
1284        ob.name = "PovBlob"
1285        return {'FINISHED'}
1286
1287
1288class POVRAY_OT_rainbow_add(Operator):
1289    """Add the representation of POV rainbow using a Blender spot light.
1290
1291    Rainbows indeed propagate along a visibility cone.
1292    Flag its primitive type with a specific ob.pov.object_as attribute
1293    and leave access to edit mode to keep user editable handles.
1294    Add a constraint to orient it towards camera because POV Rainbows
1295    are view dependant and having it always initially visible is less
1296    confusing """
1297
1298    bl_idname = "pov.addrainbow"
1299    bl_label = "Rainbow"
1300    bl_description = "Add Rainbow"
1301    bl_options = {'REGISTER', 'UNDO'}
1302
1303    def execute(self, context):
1304        cam = context.scene.camera
1305        bpy.ops.object.light_add(type='SPOT', radius=1)
1306        ob = context.object
1307        ob.data.show_cone = False
1308        ob.data.spot_blend = 0.5
1309        # ob.data.shadow_buffer_clip_end = 0 # deprecated in 2.8
1310        ob.data.shadow_buffer_clip_start = 4 * cam.location.length
1311        ob.data.distance = cam.location.length
1312        ob.data.energy = 0
1313        ob.name = ob.data.name = "PovRainbow"
1314        ob.pov.object_as = "RAINBOW"
1315
1316        # obj = context.object
1317        bpy.ops.object.constraint_add(type='DAMPED_TRACK')
1318
1319        ob.constraints["Damped Track"].target = cam
1320        ob.constraints["Damped Track"].track_axis = 'TRACK_NEGATIVE_Z'
1321        ob.location = -cam.location
1322
1323        # refocus on the actual rainbow
1324        bpy.context.view_layer.objects.active = ob
1325        ob.select_set(True)
1326
1327        return {'FINISHED'}
1328
1329
1330class POVRAY_OT_height_field_add(bpy.types.Operator, ImportHelper):
1331    """Add the representation of POV height_field using a displaced grid.
1332
1333    texture slot fix and displace modifier will be needed because noise
1334    displace operator was deprecated in 2.8"""
1335
1336    bl_idname = "pov.addheightfield"
1337    bl_label = "Height Field"
1338    bl_description = "Add Height Field"
1339    bl_options = {'REGISTER', 'UNDO'}
1340
1341    # XXX Keep it in sync with __init__'s hf Primitive
1342    # filename_ext = ".png"
1343
1344    # filter_glob = StringProperty(
1345    # 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",
1346    # options={'HIDDEN'},
1347    # )
1348    quality: IntProperty(
1349        name="Quality", description="", default=100, min=1, max=100
1350    )
1351    hf_filename: StringProperty(maxlen=1024)
1352
1353    hf_gamma: FloatProperty(
1354        name="Gamma", description="Gamma", min=0.0001, max=20.0, default=1.0
1355    )
1356
1357    hf_premultiplied: BoolProperty(
1358        name="Premultiplied", description="Premultiplied", default=True
1359    )
1360
1361    hf_smooth: BoolProperty(name="Smooth", description="Smooth", default=False)
1362
1363    hf_water: FloatProperty(
1364        name="Water Level",
1365        description="Wather Level",
1366        min=0.00,
1367        max=1.00,
1368        default=0.0,
1369    )
1370
1371    hf_hierarchy: BoolProperty(
1372        name="Hierarchy", description="Height field hierarchy", default=True
1373    )
1374
1375    def execute(self, context):
1376        props = self.properties
1377        impath = bpy.path.abspath(self.filepath)
1378        img = bpy.data.images.load(impath)
1379        im_name = img.name
1380        im_name, file_extension = os.path.splitext(im_name)
1381        hf_tex = bpy.data.textures.new('%s_hf_image' % im_name, type='IMAGE')
1382        hf_tex.image = img
1383        mat = bpy.data.materials.new('Tex_%s_hf' % im_name)
1384        hf_slot = mat.pov_texture_slots.add()
1385        hf_slot.texture = hf_tex.name
1386        # layers = 20*[False]
1387        # layers[0] = True
1388        quality = props.quality
1389        res = 100 / quality
1390        w, h = hf_tex.image.size[:]
1391        w = int(w / res)
1392        h = int(h / res)
1393        bpy.ops.mesh.primitive_grid_add(
1394            x_subdivisions=w, y_subdivisions=h, size=0.5
1395        )
1396        ob = context.object
1397        ob.name = ob.data.name = '%s' % im_name
1398        ob.data.materials.append(mat)
1399        bpy.ops.object.mode_set(mode="EDIT")
1400        # bpy.ops.mesh.noise(factor=1) # TODO replace by a displace modifier as noise deprecated in 2.8
1401        bpy.ops.object.mode_set(mode="OBJECT")
1402
1403        # needs a loop to select by index?
1404        # bpy.ops.object.material_slot_remove()
1405        # material just left there for now
1406
1407        mat.pov_texture_slots.clear()
1408        bpy.ops.object.mode_set(mode="EDIT")
1409        bpy.ops.mesh.hide(unselected=False)
1410        bpy.ops.object.mode_set(mode="OBJECT")
1411        ob.pov.object_as = 'HEIGHT_FIELD'
1412        ob.pov.hf_filename = impath
1413        return {'FINISHED'}
1414
1415
1416############################TORUS############################################
1417def pov_torus_define(context, op, ob):
1418    """Add the representation of POV torus using just a Blender torus.
1419
1420    But flag its primitive type with a specific pov.object_as attribute and lock edit mode
1421    to keep proxy consistency by hiding edit geometry."""
1422
1423    if op:
1424        mas = op.mas
1425        mis = op.mis
1426        mar = op.mar
1427        mir = op.mir
1428    else:
1429        assert ob
1430        mas = ob.pov.torus_major_segments
1431        mis = ob.pov.torus_minor_segments
1432        mar = ob.pov.torus_major_radius
1433        mir = ob.pov.torus_minor_radius
1434
1435        # keep object rotation and location for the add object operator
1436        obrot = ob.rotation_euler
1437        obloc = ob.location
1438
1439        bpy.ops.object.mode_set(mode="EDIT")
1440        bpy.ops.mesh.reveal()
1441        bpy.ops.mesh.select_all(action='SELECT')
1442        bpy.ops.mesh.delete(type='VERT')
1443        bpy.ops.mesh.primitive_torus_add(
1444            rotation=obrot,
1445            location=obloc,
1446            major_segments=mas,
1447            minor_segments=mis,
1448            major_radius=mar,
1449            minor_radius=mir,
1450        )
1451
1452        bpy.ops.mesh.hide(unselected=False)
1453        bpy.ops.object.mode_set(mode="OBJECT")
1454
1455    if not ob:
1456        bpy.ops.mesh.primitive_torus_add(
1457            major_segments=mas,
1458            minor_segments=mis,
1459            major_radius=mar,
1460            minor_radius=mir,
1461        )
1462        ob = context.object
1463        ob.name = ob.data.name = "PovTorus"
1464        ob.pov.object_as = "TORUS"
1465        ob.pov.torus_major_segments = mas
1466        ob.pov.torus_minor_segments = mis
1467        ob.pov.torus_major_radius = mar
1468        ob.pov.torus_minor_radius = mir
1469        bpy.ops.object.mode_set(mode="EDIT")
1470        bpy.ops.mesh.hide(unselected=False)
1471        bpy.ops.object.mode_set(mode="OBJECT")
1472
1473
1474class POVRAY_OT_torus_add(Operator):
1475    """Add the representation of POV torus using using pov_torus_define() function."""
1476
1477    bl_idname = "pov.addtorus"
1478    bl_label = "Torus"
1479    bl_description = "Add Torus"
1480    bl_options = {'REGISTER', 'UNDO'}
1481
1482    # XXX Keep it in sync with __init__'s torus Primitive
1483    mas: IntProperty(
1484        name="Major Segments", description="", default=48, min=3, max=720
1485    )
1486    mis: IntProperty(
1487        name="Minor Segments", description="", default=12, min=3, max=720
1488    )
1489    mar: FloatProperty(name="Major Radius", description="", default=1.0)
1490    mir: FloatProperty(name="Minor Radius", description="", default=0.25)
1491
1492    def execute(self, context):
1493        props = self.properties
1494        mar = props.mar
1495        mir = props.mir
1496        mas = props.mas
1497        mis = props.mis
1498        pov_torus_define(context, self, None)
1499        self.report(
1500            {'INFO'},
1501            "This native POV-Ray primitive "
1502            "won't have any vertex to show in edit mode",
1503        )
1504        return {'FINISHED'}
1505
1506
1507class POVRAY_OT_torus_update(Operator):
1508    """Update the POV torus.
1509
1510    Delete its previous proxy geometry and rerun pov_torus_define() function
1511    with the new parameters"""
1512
1513    bl_idname = "pov.torus_update"
1514    bl_label = "Update"
1515    bl_description = "Update Torus"
1516    bl_options = {'REGISTER', 'UNDO'}
1517    COMPAT_ENGINES = {'POVRAY_RENDER'}
1518
1519    @classmethod
1520    def poll(cls, context):
1521        engine = context.scene.render.engine
1522        ob = context.object
1523        return (
1524            ob
1525            and ob.data
1526            and ob.type == 'MESH'
1527            and engine in cls.COMPAT_ENGINES
1528        )
1529
1530    def execute(self, context):
1531
1532        pov_torus_define(context, None, context.object)
1533
1534        return {'FINISHED'}
1535
1536
1537###################################################################################
1538
1539
1540class POVRAY_OT_prism_add(Operator):
1541    """Add the representation of POV prism using using an extruded curve."""
1542
1543    bl_idname = "pov.addprism"
1544    bl_label = "Prism"
1545    bl_description = "Create Prism"
1546    bl_options = {'REGISTER', 'UNDO'}
1547
1548    prism_n: IntProperty(
1549        name="Sides", description="Number of sides", default=5, min=3, max=720
1550    )
1551    prism_r: FloatProperty(name="Radius", description="Radius", default=1.0)
1552
1553    def execute(self, context):
1554
1555        props = self.properties
1556        loftData = bpy.data.curves.new('Prism', type='CURVE')
1557        loftData.dimensions = '2D'
1558        loftData.resolution_u = 2
1559        # loftData.show_normal_face = False
1560        loftData.extrude = 2
1561        n = props.prism_n
1562        r = props.prism_r
1563        coords = []
1564        z = 0
1565        angle = 0
1566        for p in range(n):
1567            x = r * cos(angle)
1568            y = r * sin(angle)
1569            coords.append((x, y, z))
1570            angle += pi * 2 / n
1571        poly = loftData.splines.new('POLY')
1572        poly.points.add(len(coords) - 1)
1573        for i, coord in enumerate(coords):
1574            x, y, z = coord
1575            poly.points[i].co = (x, y, z, 1)
1576        poly.use_cyclic_u = True
1577
1578        ob = bpy.data.objects.new('Prism_shape', loftData)
1579        scn = bpy.context.scene
1580        scn.collection.objects.link(ob)
1581        context.view_layer.objects.active = ob
1582        ob.select_set(True)
1583        ob.pov.curveshape = "prism"
1584        ob.name = ob.data.name = "Prism"
1585        return {'FINISHED'}
1586
1587
1588##############################PARAMETRIC######################################
1589def pov_parametric_define(context, op, ob):
1590    """Add the representation of POV parametric surfaces by math surface from add mesh extra objects addon."""
1591
1592    if op:
1593        u_min = op.u_min
1594        u_max = op.u_max
1595        v_min = op.v_min
1596        v_max = op.v_max
1597        x_eq = op.x_eq
1598        y_eq = op.y_eq
1599        z_eq = op.z_eq
1600
1601    else:
1602        assert ob
1603        u_min = ob.pov.u_min
1604        u_max = ob.pov.u_max
1605        v_min = ob.pov.v_min
1606        v_max = ob.pov.v_max
1607        x_eq = ob.pov.x_eq
1608        y_eq = ob.pov.y_eq
1609        z_eq = ob.pov.z_eq
1610
1611        # keep object rotation and location for the updated object
1612        obloc = ob.location
1613        obrot = ob.rotation_euler  # In radians
1614        # Parametric addon has no loc rot, some extra work is needed
1615        # in case cursor has moved
1616        curloc = bpy.context.scene.cursor.location
1617
1618        bpy.ops.object.mode_set(mode="EDIT")
1619        bpy.ops.mesh.reveal()
1620        bpy.ops.mesh.select_all(action='SELECT')
1621        bpy.ops.mesh.delete(type='VERT')
1622        bpy.ops.mesh.primitive_xyz_function_surface(
1623            x_eq=x_eq,
1624            y_eq=y_eq,
1625            z_eq=z_eq,
1626            range_u_min=u_min,
1627            range_u_max=u_max,
1628            range_v_min=v_min,
1629            range_v_max=v_max,
1630        )
1631        bpy.ops.mesh.select_all(action='SELECT')
1632        # extra work:
1633        bpy.ops.transform.translate(value=(obloc - curloc), proportional_size=1)
1634        bpy.ops.transform.rotate(axis=obrot, proportional_size=1)
1635
1636        bpy.ops.mesh.hide(unselected=False)
1637        bpy.ops.object.mode_set(mode="OBJECT")
1638
1639    if not ob:
1640        bpy.ops.mesh.primitive_xyz_function_surface(
1641            x_eq=x_eq,
1642            y_eq=y_eq,
1643            z_eq=z_eq,
1644            range_u_min=u_min,
1645            range_u_max=u_max,
1646            range_v_min=v_min,
1647            range_v_max=v_max,
1648        )
1649        ob = context.object
1650        ob.name = ob.data.name = "PovParametric"
1651        ob.pov.object_as = "PARAMETRIC"
1652
1653        ob.pov.u_min = u_min
1654        ob.pov.u_max = u_max
1655        ob.pov.v_min = v_min
1656        ob.pov.v_max = v_max
1657        ob.pov.x_eq = x_eq
1658        ob.pov.y_eq = y_eq
1659        ob.pov.z_eq = z_eq
1660
1661        bpy.ops.object.mode_set(mode="EDIT")
1662        bpy.ops.mesh.hide(unselected=False)
1663        bpy.ops.object.mode_set(mode="OBJECT")
1664
1665
1666class POVRAY_OT_parametric_add(Operator):
1667    """Add the representation of POV parametric surfaces using pov_parametric_define() function."""
1668
1669    bl_idname = "pov.addparametric"
1670    bl_label = "Parametric"
1671    bl_description = "Add Paramertic"
1672    bl_options = {'REGISTER', 'UNDO'}
1673
1674    # XXX Keep it in sync with __init__'s Parametric primitive
1675    u_min: FloatProperty(name="U Min", description="", default=0.0)
1676    v_min: FloatProperty(name="V Min", description="", default=0.0)
1677    u_max: FloatProperty(name="U Max", description="", default=6.28)
1678    v_max: FloatProperty(name="V Max", description="", default=12.57)
1679    x_eq: StringProperty(maxlen=1024, default="cos(v)*(1+cos(u))*sin(v/8)")
1680    y_eq: StringProperty(maxlen=1024, default="sin(u)*sin(v/8)+cos(v/8)*1.5")
1681    z_eq: StringProperty(maxlen=1024, default="sin(v)*(1+cos(u))*sin(v/8)")
1682
1683    def execute(self, context):
1684        props = self.properties
1685        u_min = props.u_min
1686        v_min = props.v_min
1687        u_max = props.u_max
1688        v_max = props.v_max
1689        x_eq = props.x_eq
1690        y_eq = props.y_eq
1691        z_eq = props.z_eq
1692
1693        pov_parametric_define(context, self, None)
1694        self.report(
1695            {'INFO'},
1696            "This native POV-Ray primitive "
1697            "won't have any vertex to show in edit mode",
1698        )
1699        return {'FINISHED'}
1700
1701
1702class POVRAY_OT_parametric_update(Operator):
1703    """Update the representation of POV parametric surfaces.
1704
1705    Delete its previous proxy geometry and rerun pov_parametric_define() function
1706    with the new parameters"""
1707
1708    bl_idname = "pov.parametric_update"
1709    bl_label = "Update"
1710    bl_description = "Update parametric object"
1711    bl_options = {'REGISTER', 'UNDO'}
1712    COMPAT_ENGINES = {'POVRAY_RENDER'}
1713
1714    @classmethod
1715    def poll(cls, context):
1716        engine = context.scene.render.engine
1717        ob = context.object
1718        return (
1719            ob
1720            and ob.data
1721            and ob.type == 'MESH'
1722            and engine in cls.COMPAT_ENGINES
1723        )
1724
1725    def execute(self, context):
1726
1727        pov_parametric_define(context, None, context.object)
1728
1729        return {'FINISHED'}
1730
1731
1732#######################################################################
1733
1734
1735class POVRAY_OT_shape_polygon_to_circle_add(Operator):
1736    """Add the proxy mesh for POV Polygon to circle lofting macro"""
1737
1738    bl_idname = "pov.addpolygontocircle"
1739    bl_label = "Polygon To Circle Blending"
1740    bl_description = "Add Polygon To Circle Blending Surface"
1741    bl_options = {'REGISTER', 'UNDO'}
1742    COMPAT_ENGINES = {'POVRAY_RENDER'}
1743
1744    # XXX Keep it in sync with __init__'s polytocircle properties
1745    polytocircle_resolution: IntProperty(
1746        name="Resolution", description="", default=3, min=0, max=256
1747    )
1748    polytocircle_ngon: IntProperty(
1749        name="NGon", description="", min=3, max=64, default=5
1750    )
1751    polytocircle_ngonR: FloatProperty(
1752        name="NGon Radius", description="", default=0.3
1753    )
1754    polytocircle_circleR: FloatProperty(
1755        name="Circle Radius", description="", default=1.0
1756    )
1757
1758    def execute(self, context):
1759        props = self.properties
1760        ngon = props.polytocircle_ngon
1761        ngonR = props.polytocircle_ngonR
1762        circleR = props.polytocircle_circleR
1763        resolution = props.polytocircle_resolution
1764        # layers = 20*[False]
1765        # layers[0] = True
1766        bpy.ops.mesh.primitive_circle_add(
1767            vertices=ngon, radius=ngonR, fill_type='NGON', enter_editmode=True
1768        )
1769        bpy.ops.transform.translate(value=(0, 0, 1))
1770        bpy.ops.mesh.subdivide(number_cuts=resolution)
1771        numCircleVerts = ngon + (ngon * resolution)
1772        bpy.ops.mesh.select_all(action='DESELECT')
1773        bpy.ops.mesh.primitive_circle_add(
1774            vertices=numCircleVerts,
1775            radius=circleR,
1776            fill_type='NGON',
1777            enter_editmode=True,
1778        )
1779        bpy.ops.transform.translate(value=(0, 0, -1))
1780        bpy.ops.mesh.select_all(action='SELECT')
1781        bpy.ops.mesh.bridge_edge_loops()
1782        if ngon < 5:
1783            bpy.ops.mesh.select_all(action='DESELECT')
1784            bpy.ops.mesh.primitive_circle_add(
1785                vertices=ngon,
1786                radius=ngonR,
1787                fill_type='TRIFAN',
1788                enter_editmode=True,
1789            )
1790            bpy.ops.transform.translate(value=(0, 0, 1))
1791            bpy.ops.mesh.select_all(action='SELECT')
1792            bpy.ops.mesh.remove_doubles()
1793        bpy.ops.object.mode_set(mode='OBJECT')
1794        ob = context.object
1795        ob.name = "Polygon_To_Circle"
1796        ob.pov.object_as = 'POLYCIRCLE'
1797        ob.pov.ngon = ngon
1798        ob.pov.ngonR = ngonR
1799        ob.pov.circleR = circleR
1800        bpy.ops.object.mode_set(mode="EDIT")
1801        bpy.ops.mesh.hide(unselected=False)
1802        bpy.ops.object.mode_set(mode="OBJECT")
1803        return {'FINISHED'}
1804
1805
1806#############################IMPORT
1807
1808
1809class ImportPOV(bpy.types.Operator, ImportHelper):
1810    """Load Povray files"""
1811
1812    bl_idname = "import_scene.pov"
1813    bl_label = "POV-Ray files (.pov/.inc)"
1814    bl_options = {'PRESET', 'UNDO'}
1815    COMPAT_ENGINES = {'POVRAY_RENDER'}
1816
1817    # -----------
1818    # File props.
1819    files: CollectionProperty(
1820        type=bpy.types.OperatorFileListElement, options={'HIDDEN', 'SKIP_SAVE'}
1821    )
1822    directory: StringProperty(
1823        maxlen=1024, subtype='FILE_PATH', options={'HIDDEN', 'SKIP_SAVE'}
1824    )
1825
1826    filename_ext = {".pov", ".inc"}
1827    filter_glob: StringProperty(default="*.pov;*.inc", options={'HIDDEN'})
1828
1829    import_at_cur: BoolProperty(
1830        name="Import at Cursor Location",
1831        description="Ignore Object Matrix",
1832        default=False,
1833    )
1834
1835    def execute(self, context):
1836        from mathutils import Matrix
1837
1838        verts = []
1839        faces = []
1840        materials = []
1841        blendMats = []  ##############
1842        povMats = []  ##############
1843        colors = []
1844        matNames = []
1845        lenverts = None
1846        lenfaces = None
1847        suffix = -1
1848        name = 'Mesh2_%s' % suffix
1849        name_search = False
1850        verts_search = False
1851        faces_search = False
1852        plane_search = False
1853        box_search = False
1854        cylinder_search = False
1855        sphere_search = False
1856        cone_search = False
1857        tex_search = False  ##################
1858        cache = []
1859        matrixes = {}
1860        writematrix = False
1861        index = None
1862        value = None
1863        # filepov = bpy.path.abspath(self.filepath) #was used for single files
1864
1865        def mat_search(cache):
1866            r = g = b = 0.5
1867            f = t = 0
1868            color = None
1869
1870            for item, value in enumerate(cache):
1871
1872                if value == 'texture':
1873                    pass
1874
1875                if value == 'pigment':
1876
1877                    if cache[item + 2] in {'rgb', 'srgb'}:
1878                        pass
1879
1880                    elif cache[item + 2] in {'rgbf', 'srgbf'}:
1881                        pass
1882
1883                    elif cache[item + 2] in {'rgbt', 'srgbt'}:
1884                        try:
1885                            r, g, b, t = (
1886                                float(cache[item + 3]),
1887                                float(cache[item + 4]),
1888                                float(cache[item + 5]),
1889                                float(cache[item + 6]),
1890                            )
1891                        except:
1892                            r = g = b = t = float(cache[item + 2])
1893                        color = (r, g, b, t)
1894
1895                    elif cache[item + 2] in {'rgbft', 'srgbft'}:
1896                        pass
1897
1898                    else:
1899                        pass
1900
1901            if colors == [] or (colors != [] and color not in colors):
1902                colors.append(color)
1903                name = ob.name + "_mat"
1904                matNames.append(name)
1905                mat = bpy.data.materials.new(name)
1906                mat.diffuse_color = (r, g, b)
1907                mat.alpha = 1 - t
1908                if mat.alpha != 1:
1909                    mat.use_transparency = True
1910                ob.data.materials.append(mat)
1911
1912            else:
1913                for i, value in enumerate(colors):
1914                    if color == value:
1915                        ob.data.materials.append(
1916                            bpy.data.materials[matNames[i]]
1917                        )
1918
1919        for file in self.files:
1920            print("Importing file: " + file.name)
1921            filepov = self.directory + file.name
1922            for line in open(filepov):
1923                string = line.replace("{", " ")
1924                string = string.replace("}", " ")
1925                string = string.replace("<", " ")
1926                string = string.replace(">", " ")
1927                string = string.replace(",", " ")
1928                lw = string.split()
1929                lenwords = len(lw)
1930                if lw:
1931                    if lw[0] == "object":
1932                        writematrix = True
1933                    if writematrix:
1934                        if lw[0] not in {"object", "matrix"}:
1935                            index = lw[0]
1936                        if lw[0] in {"matrix"}:
1937                            value = [
1938                                float(lw[1]),
1939                                float(lw[2]),
1940                                float(lw[3]),
1941                                float(lw[4]),
1942                                float(lw[5]),
1943                                float(lw[6]),
1944                                float(lw[7]),
1945                                float(lw[8]),
1946                                float(lw[9]),
1947                                float(lw[10]),
1948                                float(lw[11]),
1949                                float(lw[12]),
1950                            ]
1951                            matrixes[index] = value
1952                            writematrix = False
1953            for line in open(filepov):
1954                S = line.replace("{", " { ")
1955                S = S.replace("}", " } ")
1956                S = S.replace(",", " ")
1957                S = S.replace("<", "")
1958                S = S.replace(">", " ")
1959                S = S.replace("=", " = ")
1960                S = S.replace(";", " ; ")
1961                S = S.split()
1962                lenS = len(S)
1963                for i, word in enumerate(S):
1964                    ##################Primitives Import##################
1965                    if word == 'cone':
1966                        cone_search = True
1967                        name_search = False
1968                    if cone_search:
1969                        cache.append(word)
1970                        if cache[-1] == '}':
1971                            try:
1972                                x0 = float(cache[2])
1973                                y0 = float(cache[3])
1974                                z0 = float(cache[4])
1975                                r0 = float(cache[5])
1976                                x1 = float(cache[6])
1977                                y1 = float(cache[7])
1978                                z1 = float(cache[8])
1979                                r1 = float(cache[9])
1980                                # Y is height in most pov files, not z
1981                                bpy.ops.pov.cone_add(
1982                                    base=r0, cap=r1, height=(y1 - y0)
1983                                )
1984                                ob = context.object
1985                                ob.location = (x0, y0, z0)
1986                                # ob.scale = (r,r,r)
1987                                mat_search(cache)
1988                            except (ValueError):
1989                                pass
1990                            cache = []
1991                            cone_search = False
1992                    if word == 'plane':
1993                        plane_search = True
1994                        name_search = False
1995                    if plane_search:
1996                        cache.append(word)
1997                        if cache[-1] == '}':
1998                            try:
1999                                bpy.ops.pov.addplane()
2000                                ob = context.object
2001                                mat_search(cache)
2002                            except (ValueError):
2003                                pass
2004                            cache = []
2005                            plane_search = False
2006                    if word == 'box':
2007                        box_search = True
2008                        name_search = False
2009                    if box_search:
2010                        cache.append(word)
2011                        if cache[-1] == '}':
2012                            try:
2013                                x0 = float(cache[2])
2014                                y0 = float(cache[3])
2015                                z0 = float(cache[4])
2016                                x1 = float(cache[5])
2017                                y1 = float(cache[6])
2018                                z1 = float(cache[7])
2019                                # imported_corner_1=(x0, y0, z0)
2020                                # imported_corner_2 =(x1, y1, z1)
2021                                center = (
2022                                    (x0 + x1) / 2,
2023                                    (y0 + y1) / 2,
2024                                    (z0 + z1) / 2,
2025                                )
2026                                bpy.ops.pov.addbox()
2027                                ob = context.object
2028                                ob.location = center
2029                                mat_search(cache)
2030
2031                            except (ValueError):
2032                                pass
2033                            cache = []
2034                            box_search = False
2035                    if word == 'cylinder':
2036                        cylinder_search = True
2037                        name_search = False
2038                    if cylinder_search:
2039                        cache.append(word)
2040                        if cache[-1] == '}':
2041                            try:
2042                                x0 = float(cache[2])
2043                                y0 = float(cache[3])
2044                                z0 = float(cache[4])
2045                                x1 = float(cache[5])
2046                                y1 = float(cache[6])
2047                                z1 = float(cache[7])
2048                                imported_cyl_loc = (x0, y0, z0)
2049                                imported_cyl_loc_cap = (x1, y1, z1)
2050
2051                                r = float(cache[8])
2052
2053                                vec = Vector(imported_cyl_loc_cap) - Vector(
2054                                    imported_cyl_loc
2055                                )
2056                                depth = vec.length
2057                                rot = Vector((0, 0, 1)).rotation_difference(
2058                                    vec
2059                                )  # Rotation from Z axis.
2060                                trans = rot @ Vector(
2061                                    (0, 0, depth / 2)
2062                                )  # Such that origin is at center of the base of the cylinder.
2063                                # center = ((x0 + x1)/2,(y0 + y1)/2,(z0 + z1)/2)
2064                                scaleZ = (
2065                                    sqrt(
2066                                        (x1 - x0) ** 2
2067                                        + (y1 - y0) ** 2
2068                                        + (z1 - z0) ** 2
2069                                    )
2070                                    / 2
2071                                )
2072                                bpy.ops.pov.addcylinder(
2073                                    R=r,
2074                                    imported_cyl_loc=imported_cyl_loc,
2075                                    imported_cyl_loc_cap=imported_cyl_loc_cap,
2076                                )
2077                                ob = context.object
2078                                ob.location = (x0, y0, z0)
2079                                ob.rotation_euler = rot.to_euler()
2080                                ob.scale = (1, 1, scaleZ)
2081
2082                                # scale data rather than obj?
2083                                # bpy.ops.object.mode_set(mode='EDIT')
2084                                # bpy.ops.mesh.reveal()
2085                                # bpy.ops.mesh.select_all(action='SELECT')
2086                                # bpy.ops.transform.resize(value=(1,1,scaleZ), orient_type='LOCAL')
2087                                # bpy.ops.mesh.hide(unselected=False)
2088                                # bpy.ops.object.mode_set(mode='OBJECT')
2089
2090                                mat_search(cache)
2091
2092                            except (ValueError):
2093                                pass
2094                            cache = []
2095                            cylinder_search = False
2096                    if word == 'sphere':
2097                        sphere_search = True
2098                        name_search = False
2099                    if sphere_search:
2100                        cache.append(word)
2101                        if cache[-1] == '}':
2102                            x = y = z = r = 0
2103                            try:
2104                                x = float(cache[2])
2105                                y = float(cache[3])
2106                                z = float(cache[4])
2107                                r = float(cache[5])
2108
2109                            except (ValueError):
2110                                pass
2111                            except:
2112                                x = y = z = float(cache[2])
2113                                r = float(cache[3])
2114                            bpy.ops.pov.addsphere(R=r, imported_loc=(x, y, z))
2115                            ob = context.object
2116                            ob.location = (x, y, z)
2117                            ob.scale = (r, r, r)
2118                            mat_search(cache)
2119                            cache = []
2120                            sphere_search = False
2121                    ##################End Primitives Import##################
2122                    if word == '#declare':
2123                        name_search = True
2124                    if name_search:
2125                        cache.append(word)
2126                        if word == 'mesh2':
2127                            name_search = False
2128                            if cache[-2] == '=':
2129                                name = cache[-3]
2130                            else:
2131                                suffix += 1
2132                            cache = []
2133                        if word in {'texture', ';'}:
2134                            name_search = False
2135                            cache = []
2136                    if word == 'vertex_vectors':
2137                        verts_search = True
2138                    if verts_search:
2139                        cache.append(word)
2140                        if word == '}':
2141                            verts_search = False
2142                            lenverts = cache[2]
2143                            cache.pop()
2144                            cache.pop(0)
2145                            cache.pop(0)
2146                            cache.pop(0)
2147                            for i in range(int(lenverts)):
2148                                x = i * 3
2149                                y = (i * 3) + 1
2150                                z = (i * 3) + 2
2151                                verts.append(
2152                                    (
2153                                        float(cache[x]),
2154                                        float(cache[y]),
2155                                        float(cache[z]),
2156                                    )
2157                                )
2158                            cache = []
2159                    # if word == 'face_indices':
2160                    # faces_search = True
2161                    if word == 'texture_list':  ########
2162                        tex_search = True  #######
2163                    if tex_search:  #########
2164                        if (
2165                            word
2166                            not in {
2167                                'texture_list',
2168                                'texture',
2169                                '{',
2170                                '}',
2171                                'face_indices',
2172                            }
2173                            and word.isdigit() == False
2174                        ):  ##############
2175                            povMats.append(word)  #################
2176                    if word == 'face_indices':
2177                        tex_search = False  ################
2178                        faces_search = True
2179                    if faces_search:
2180                        cache.append(word)
2181                        if word == '}':
2182                            faces_search = False
2183                            lenfaces = cache[2]
2184                            cache.pop()
2185                            cache.pop(0)
2186                            cache.pop(0)
2187                            cache.pop(0)
2188                            lf = int(lenfaces)
2189                            var = int(len(cache) / lf)
2190                            for i in range(lf):
2191                                if var == 3:
2192                                    v0 = i * 3
2193                                    v1 = i * 3 + 1
2194                                    v2 = i * 3 + 2
2195                                    faces.append(
2196                                        (
2197                                            int(cache[v0]),
2198                                            int(cache[v1]),
2199                                            int(cache[v2]),
2200                                        )
2201                                    )
2202                                if var == 4:
2203                                    v0 = i * 4
2204                                    v1 = i * 4 + 1
2205                                    v2 = i * 4 + 2
2206                                    m = i * 4 + 3
2207                                    materials.append((int(cache[m])))
2208                                    faces.append(
2209                                        (
2210                                            int(cache[v0]),
2211                                            int(cache[v1]),
2212                                            int(cache[v2]),
2213                                        )
2214                                    )
2215                                if var == 6:
2216                                    v0 = i * 6
2217                                    v1 = i * 6 + 1
2218                                    v2 = i * 6 + 2
2219                                    m0 = i * 6 + 3
2220                                    m1 = i * 6 + 4
2221                                    m2 = i * 6 + 5
2222                                    materials.append(
2223                                        (
2224                                            int(cache[m0]),
2225                                            int(cache[m1]),
2226                                            int(cache[m2]),
2227                                        )
2228                                    )
2229                                    faces.append(
2230                                        (
2231                                            int(cache[v0]),
2232                                            int(cache[v1]),
2233                                            int(cache[v2]),
2234                                        )
2235                                    )
2236                            # mesh = pov_define_mesh(None, verts, [], faces, name, hide_geometry=False)
2237                            # ob = object_utils.object_data_add(context, mesh, operator=None)
2238
2239                            me = bpy.data.meshes.new(name)  ########
2240                            ob = bpy.data.objects.new(name, me)  ##########
2241                            bpy.context.collection.objects.link(ob)  #########
2242                            me.from_pydata(verts, [], faces)  ############
2243
2244                            for mat in bpy.data.materials:  ##############
2245                                blendMats.append(mat.name)  #############
2246                            for mName in povMats:  #####################
2247                                if mName not in blendMats:  ###########
2248                                    povMat = bpy.data.materials.new(
2249                                        mName
2250                                    )  #################
2251                                    mat_search(cache)
2252                                ob.data.materials.append(
2253                                    bpy.data.materials[mName]
2254                                )  ###################
2255                            if materials:  ##################
2256                                for i, val in enumerate(
2257                                    materials
2258                                ):  ####################
2259                                    try:  ###################
2260                                        ob.data.polygons[
2261                                            i
2262                                        ].material_index = (
2263                                            val
2264                                        )  ####################
2265                                    except TypeError:  ###################
2266                                        ob.data.polygons[
2267                                            i
2268                                        ].material_index = int(
2269                                            val[0]
2270                                        )  ##################
2271
2272                            blendMats = []  #########################
2273                            povMats = []  #########################
2274                            materials = []  #########################
2275                            cache = []
2276                            name_search = True
2277                            if name in matrixes and self.import_at_cur == False:
2278                                global_matrix = Matrix.Rotation(
2279                                    pi / 2.0, 4, 'X'
2280                                )
2281                                ob = bpy.context.object
2282                                matrix = ob.matrix_world
2283                                v = matrixes[name]
2284                                matrix[0][0] = v[0]
2285                                matrix[1][0] = v[1]
2286                                matrix[2][0] = v[2]
2287                                matrix[0][1] = v[3]
2288                                matrix[1][1] = v[4]
2289                                matrix[2][1] = v[5]
2290                                matrix[0][2] = v[6]
2291                                matrix[1][2] = v[7]
2292                                matrix[2][2] = v[8]
2293                                matrix[0][3] = v[9]
2294                                matrix[1][3] = v[10]
2295                                matrix[2][3] = v[11]
2296                                matrix = global_matrix * ob.matrix_world
2297                                ob.matrix_world = matrix
2298                            verts = []
2299                            faces = []
2300
2301                    # if word == 'pigment':
2302                    # try:
2303                    # #all indices have been incremented once to fit a bad test file
2304                    # r,g,b,t = float(S[2]),float(S[3]),float(S[4]),float(S[5])
2305                    # color = (r,g,b,t)
2306
2307                    # except (IndexError):
2308                    # #all indices have been incremented once to fit alternate test file
2309                    # r,g,b,t = float(S[3]),float(S[4]),float(S[5]),float(S[6])
2310                    # color = (r,g,b,t)
2311                    # except UnboundLocalError:
2312                    # # In case no transmit is specified ? put it to 0
2313                    # r,g,b,t = float(S[2]),float(S[3]),float(S[4],0)
2314                    # color = (r,g,b,t)
2315
2316                    # except (ValueError):
2317                    # color = (0.8,0.8,0.8,0)
2318                    # pass
2319
2320                    # if colors == [] or (colors != [] and color not in colors):
2321                    # colors.append(color)
2322                    # name = ob.name+"_mat"
2323                    # matNames.append(name)
2324                    # mat = bpy.data.materials.new(name)
2325                    # mat.diffuse_color = (r,g,b)
2326                    # mat.alpha = 1-t
2327                    # if mat.alpha != 1:
2328                    # mat.use_transparency=True
2329                    # ob.data.materials.append(mat)
2330                    # print (colors)
2331                    # else:
2332                    # for i in range(len(colors)):
2333                    # if color == colors[i]:
2334                    # ob.data.materials.append(bpy.data.materials[matNames[i]])
2335
2336        ##To keep Avogadro Camera angle:
2337        # for obj in bpy.context.view_layer.objects:
2338        # if obj.type == "CAMERA":
2339        # track = obj.constraints.new(type = "TRACK_TO")
2340        # track.target = ob
2341        # track.track_axis ="TRACK_NEGATIVE_Z"
2342        # track.up_axis = "UP_Y"
2343        # obj.location = (0,0,0)
2344        return {'FINISHED'}
2345
2346
2347classes = (
2348    POVRAY_OT_lathe_add,
2349    POVRAY_OT_superellipsoid_add,
2350    POVRAY_OT_superellipsoid_update,
2351    POVRAY_OT_supertorus_add,
2352    POVRAY_OT_supertorus_update,
2353    POVRAY_OT_loft_add,
2354    POVRAY_OT_plane_add,
2355    POVRAY_OT_box_add,
2356    POVRAY_OT_cylinder_add,
2357    POVRAY_OT_cylinder_update,
2358    POVRAY_OT_sphere_add,
2359    POVRAY_OT_sphere_update,
2360    POVRAY_OT_cone_add,
2361    POVRAY_OT_cone_update,
2362    POVRAY_OT_isosurface_box_add,
2363    POVRAY_OT_isosurface_sphere_add,
2364    POVRAY_OT_sphere_sweep_add,
2365    POVRAY_OT_blob_add,
2366    POVRAY_OT_rainbow_add,
2367    POVRAY_OT_height_field_add,
2368    POVRAY_OT_torus_add,
2369    POVRAY_OT_torus_update,
2370    POVRAY_OT_prism_add,
2371    POVRAY_OT_parametric_add,
2372    POVRAY_OT_parametric_update,
2373    POVRAY_OT_shape_polygon_to_circle_add,
2374    ImportPOV,
2375)
2376
2377
2378def register():
2379    # from bpy.utils import register_class
2380
2381    for cls in classes:
2382        register_class(cls)
2383
2384
2385def unregister():
2386    from bpy.utils import unregister_class
2387
2388    for cls in classes:
2389        unregister_class(cls)
2390