ellipse_mk2.py 11,4 КБ
Newer Older
DolphinDream's avatar
DolphinDream включено в состав коммита
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# ##### 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 #####

import bpy
from bpy.props import BoolProperty, IntProperty, FloatProperty, EnumProperty

durman's avatar
durman включено в состав коммита
22
from sverchok.node_tree import SverchCustomTreeNode
Durman's avatar
Durman включено в состав коммита
23
from sverchok.data_structure import (match_long_repeat, updateNode, get_edge_loop)
DolphinDream's avatar
DolphinDream включено в состав коммита
24
from sverchok.utils.sv_transform_helper import AngleUnits, SvAngleHelper
DolphinDream's avatar
DolphinDream включено в состав коммита
25
26
27

from math import sin, cos, pi, sqrt

DolphinDream's avatar
DolphinDream включено в состав коммита
28
29
30
centering_items = [("F1", "F1", "Ellipse focal point 1", 1),
                   ("C", "C", "Ellipse center point", 2),
                   ("F2", "F2", "Ellipse focal point 2", 3)]
DolphinDream's avatar
DolphinDream включено в состав коммита
31

DolphinDream's avatar
DolphinDream включено в состав коммита
32
33
34
mode_items = [("AB", "a b", "Major Radius / Minor Radius", 1),
              ("AE", "a e", "Major Radius / Eccentricity", 2),
              ("AC", "a c", "Major Radius / Focal Length", 3)]
DolphinDream's avatar
DolphinDream включено в состав коммита
35

DolphinDream's avatar
DolphinDream включено в состав коммита
36

DolphinDream's avatar
DolphinDream включено в состав коммита
37
class SvEllipseNodeMK2(bpy.types.Node, SverchCustomTreeNode, SvAngleHelper):
DolphinDream's avatar
DolphinDream включено в состав коммита
38
39
40
41
    """
    Triggers: Ellipse
    Tooltip: Generate ellipses
    """
DolphinDream's avatar
DolphinDream включено в состав коммита
42
    bl_idname = 'SvEllipseNodeMK2'
DolphinDream's avatar
DolphinDream включено в состав коммита
43
44
45
    bl_label = 'Ellipse'
    sv_icon = 'SV_ELLIPSE'

DolphinDream's avatar
DolphinDream включено в состав коммита
46
47
    replacement_nodes = [('SvEllipseNodeMK3', None, None)]

DolphinDream's avatar
DolphinDream включено в состав коммита
48
49
    def update_mode(self, context):
        ''' Update the ellipse parameters of the new mode based on previous mode ones'''
DolphinDream's avatar
DolphinDream включено в состав коммита
50
51
52
53
54
55
56
57
58
59
60

        if self.mode == self.last_mode:
            return

        #               from            to
        switch_state = (self.last_mode, self.mode)

        a = self.major_radius
        e = self.eccentricity
        c = self.focal_length

DolphinDream's avatar
DolphinDream включено в состав коммита
61
62
        self.updating = True

DolphinDream's avatar
DolphinDream включено в состав коммита
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
        if switch_state == ("AE", "AB"):
            self.minor_radius = a * sqrt(1 - e * e)

        elif switch_state == ("AC", "AB"):
            c = min(a, c)
            self.minor_radius = sqrt(a * a - c * c)

        elif switch_state == ("AB", "AE"):
            b = min(a, self.minor_radius)
            self.eccentricity = sqrt(1 - (b * b) / (a * a))

        elif switch_state == ("AC", "AE"):
            self.eccentricity = c / a

        elif switch_state == ("AB", "AC"):
            b = min(a, self.minor_radius)
            self.focal_length = sqrt(a * a - b * b)

        elif switch_state == ("AE", "AC"):
            self.focal_length = a * e
DolphinDream's avatar
DolphinDream включено в состав коммита
83
84
85

        self.updating = False

DolphinDream's avatar
DolphinDream включено в состав коммита
86
87
        self.last_mode = self.mode
        self.update_sockets()
Durman's avatar
Durman включено в состав коммита
88
        updateNode(self, context)
DolphinDream's avatar
DolphinDream включено в состав коммита
89
90
91
92
93
94
95

    def update_ellipse(self, context):
        if self.updating:
            return

        updateNode(self, context)

DolphinDream's avatar
DolphinDream включено в состав коммита
96
97
98
99
100
    def update_angles(self, context, au):
        ''' Update all the angles to preserve their values in the new units '''
        self.phase = self.phase * au
        self.rotation = self.rotation * au

zeffii's avatar
zeffii включено в состав коммита
101
    centering: EnumProperty(
DolphinDream's avatar
DolphinDream включено в состав коммита
102
        name="Centering", items=centering_items,
DolphinDream's avatar
DolphinDream включено в состав коммита
103
104
105
        description="Center the ellipse around F1, C or F2",
        default="C", update=updateNode)

zeffii's avatar
zeffii включено в состав коммита
106
    mode: EnumProperty(
DolphinDream's avatar
DolphinDream включено в состав коммита
107
        name="Mode", items=mode_items,
DolphinDream's avatar
DolphinDream включено в состав коммита
108
109
110
        description="Ellipse definition mode",
        default="AB", update=update_mode)

DolphinDream's avatar
DolphinDream включено в состав коммита
111
112
    last_mode: EnumProperty(
        name="Mode", items=mode_items,
DolphinDream's avatar
DolphinDream включено в состав коммита
113
114
115
        description="Ellipse definition last mode",
        default="AB")

zeffii's avatar
zeffii включено в состав коммита
116
    major_radius: FloatProperty(
DolphinDream's avatar
DolphinDream включено в состав коммита
117
118
119
        name='Major Radius', description='Ellipse major radius',
        default=1.0, min=0.0, update=update_ellipse)

zeffii's avatar
zeffii включено в состав коммита
120
    minor_radius: FloatProperty(
DolphinDream's avatar
DolphinDream включено в состав коммита
121
122
123
        name='Minor Radius', description='Ellipse minor radius',
        default=0.8, min=0.0, update=update_ellipse)

zeffii's avatar
zeffii включено в состав коммита
124
    eccentricity: FloatProperty(
DolphinDream's avatar
DolphinDream включено в состав коммита
125
126
127
        name='Eccentricity', description='Ellipse eccentricity',
        default=0.6, min=0.0, max=1.0, update=update_ellipse)

zeffii's avatar
zeffii включено в состав коммита
128
    focal_length: FloatProperty(
DolphinDream's avatar
DolphinDream включено в состав коммита
129
130
131
        name='Focal Length', description='Ellipse focal length',
        default=0.6, min=0.0, update=update_ellipse)

zeffii's avatar
zeffii включено в состав коммита
132
    num_verts: IntProperty(
DolphinDream's avatar
DolphinDream включено в состав коммита
133
134
135
        name='Num Verts', description='Number of vertices in the ellipse',
        default=36, min=3, update=updateNode)

zeffii's avatar
zeffii включено в состав коммита
136
    phase: FloatProperty(
DolphinDream's avatar
DolphinDream включено в состав коммита
137
138
        name='Phase', description='Phase ellipse vertices around the center by this angle amount',
        default=0.0, update=SvAngleHelper.update_angle)
DolphinDream's avatar
DolphinDream включено в состав коммита
139

zeffii's avatar
zeffii включено в состав коммита
140
    rotation: FloatProperty(
DolphinDream's avatar
DolphinDream включено в состав коммита
141
142
        name='Rotation', description='Rotate ellipse vertices around the centering point by this angle amount',
        default=0.0, update=SvAngleHelper.update_angle)
DolphinDream's avatar
DolphinDream включено в состав коммита
143

zeffii's avatar
zeffii включено в состав коммита
144
    scale: FloatProperty(
DolphinDream's avatar
DolphinDream включено в состав коммита
145
146
147
        name='Scale', description='Scale ellipse radii by this amount',
        default=1.0, min=0.0, update=updateNode)

zeffii's avatar
zeffii включено в состав коммита
148
    updating: BoolProperty(default=False)  # used for disabling update callback
DolphinDream's avatar
DolphinDream включено в состав коммита
149

DolphinDream's avatar
DolphinDream включено в состав коммита
150
151
152
153
154
155
    def migrate_from(self, old_node):
        ''' Migration from old nodes '''
        if old_node.bl_idname == "SvEllipseNode":
            self.angle_units = AngleUnits.RADIANS
            self.last_angle_units = AngleUnits.RADIANS

DolphinDream's avatar
DolphinDream включено в состав коммита
156
    def sv_init(self, context):
DolphinDream's avatar
DolphinDream включено в состав коммита
157
        self.width = 160
Victor Doval's avatar
Victor Doval включено в состав коммита
158
159
160
161
162
163
164
165
166
167
168
169
        self.inputs.new('SvStringsSocket', "Major Radius").prop_name = "major_radius"
        self.inputs.new('SvStringsSocket', "Minor Radius").prop_name = "minor_radius"
        self.inputs.new('SvStringsSocket', "Num Verts").prop_name = "num_verts"
        self.inputs.new('SvStringsSocket', "Phase").prop_name = "phase"
        self.inputs.new('SvStringsSocket', "Rotation").prop_name = "rotation"
        self.inputs.new('SvStringsSocket', "Scale").prop_name = "scale"

        self.outputs.new('SvVerticesSocket', "Verts")
        self.outputs.new('SvStringsSocket', "Edges")
        self.outputs.new('SvStringsSocket', "Polys")
        self.outputs.new('SvVerticesSocket', "F1")
        self.outputs.new('SvVerticesSocket', "F2")
DolphinDream's avatar
DolphinDream включено в состав коммита
170
171

    def draw_buttons(self, context, layout):
DolphinDream's avatar
DolphinDream включено в состав коммита
172
173
174
175
176
177
178
179
        col = layout.column(align=True)
        row = col.row(align=True)
        row.prop(self, "mode", expand=True)
        row = col.row(align=True)
        row.prop(self, "centering", expand=True)

    def draw_buttons_ext(self, context, layout):
        self.draw_angle_units_buttons(context, layout)
DolphinDream's avatar
DolphinDream включено в состав коммита
180
181
182
183

    def update_sockets(self):
        if self.mode == "AB":
            socket2 = self.inputs[1]
Victor Doval's avatar
Victor Doval включено в состав коммита
184
            socket2.replace_socket("SvStringsSocket", "Minor Radius").prop_name = "minor_radius"
DolphinDream's avatar
DolphinDream включено в состав коммита
185
186
        elif self.mode == "AE":
            socket2 = self.inputs[1]
Victor Doval's avatar
Victor Doval включено в состав коммита
187
            socket2.replace_socket("SvStringsSocket", "Eccentricity").prop_name = "eccentricity"
DolphinDream's avatar
DolphinDream включено в состав коммита
188
189
        else:  # AC
            socket2 = self.inputs[1]
Victor Doval's avatar
Victor Doval включено в состав коммита
190
            socket2.replace_socket("SvStringsSocket", "Focal Length").prop_name = "focal_length"
DolphinDream's avatar
DolphinDream включено в состав коммита
191
192

    def make_ellipse(self, a, b, N, phase, rotation, scale):
DolphinDream's avatar
DolphinDream включено в состав коммита
193
194
195
196
197
198
199
200
201
202
        '''
        Make an Ellipse (verts, edges and polys)

        a         : major radius of the ellipse
        b         : minor radius of the ellipse
        N         : number of vertices in the curve
        phase     : shift the points along the curve by this angle amount
        rotation  : rotate the ellipse in plane by this angle amount
        scale     : scale the major & minor radii by this factor
        '''
DolphinDream's avatar
DolphinDream включено в состав коммита
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
        verts = []
        edges = []
        polys = []

        a = a * scale
        b = b * scale

        if a > b:
            dx = sqrt(a * a - b * b)
            dy = 0
        else:
            dx = 0
            dy = sqrt(b * b - a * a)

        if self.centering == "F1":
            cx = -dx
            cy = -dy
        elif self.centering == "F2":
            cx = +dx
            cy = +dy
        else:  # "C"
            cx = 0
            cy = 0

        sins = sin(rotation)  # cached for performance
        coss = cos(rotation)  # cached for performance

        f1x = -cx - dx
        f1y = -cy - dy
        f2x = -cx + dx
        f2y = -cy + dy
        f1xx = f1x * coss - f1y * sins
        f1yy = f1x * sins + f1y * coss
        f2xx = f2x * coss - f2y * sins
        f2yy = f2x * sins + f2y * coss

        f1 = [f1xx, f1yy, 0]
        f2 = [f2xx, f2yy, 0]

DolphinDream's avatar
DolphinDream включено в состав коммита
242
243
244
        delta = 2 * pi / N  # cached for performance

        add_vert = verts.append
DolphinDream's avatar
DolphinDream включено в состав коммита
245
        for n in range(N):
DolphinDream's avatar
DolphinDream включено в состав коммита
246
            theta = delta * n + phase
DolphinDream's avatar
DolphinDream включено в состав коммита
247
248
            x = -cx + a * cos(theta)
            y = -cy + b * sin(theta)
DolphinDream's avatar
DolphinDream включено в состав коммита
249
            # apply in-plane rotation
DolphinDream's avatar
DolphinDream включено в состав коммита
250
251
            xx = x * coss - y * sins
            yy = x * sins + y * coss
DolphinDream's avatar
DolphinDream включено в состав коммита
252
            add_vert((xx, yy, 0))
DolphinDream's avatar
DolphinDream включено в состав коммита
253

DolphinDream's avatar
DolphinDream включено в состав коммита
254
        edges = get_edge_loop(N)
DolphinDream's avatar
DolphinDream включено в состав коммита
255
256
257
258
259
260
261
262
263
264
265
266
        polys = [list(range(N))]

        return verts, edges, polys, f1, f2

    def process(self):
        outputs = self.outputs
        # return if no outputs are connected
        if not any(s.is_linked for s in outputs):
            return

        # input values lists (single or multi value)
        inputs = self.inputs
DolphinDream's avatar
DolphinDream включено в состав коммита
267
268
        input_v1 = inputs[0].sv_get()[0]  # major radius
        input_v2 = inputs[1].sv_get()[0]  # minor radius, eccentricity or focal length
DolphinDream's avatar
DolphinDream включено в состав коммита
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
        input_N = inputs["Num Verts"].sv_get()[0]
        input_p = inputs["Phase"].sv_get()[0]
        input_r = inputs["Rotation"].sv_get()[0]
        input_s = inputs["Scale"].sv_get()[0]

        # convert main input parameters to major/minor radii (and sanitize inputs)
        if self.mode == "AB":
            input_a, input_b = match_long_repeat([input_v1, input_v2])
            input_a = list(map(lambda a: max(0.0, a), input_a))
            input_b = list(map(lambda a, b: max(0.0, min(a, b)), input_a, input_b))
        elif self.mode == "AE":
            input_a, input_e = match_long_repeat([input_v1, input_v2])
            input_a = list(map(lambda a: max(0.0, a), input_a))
            input_e = list(map(lambda e: max(0.0, min(1.0, e)), input_e))
            input_b = list(map(lambda a, e: a * sqrt(1 - e * e), input_a, input_e))
        else:  # "AC"
            input_a, input_c = match_long_repeat([input_v1, input_v2])
            input_a = list(map(lambda a: max(0.0, a), input_a))
            input_c = list(map(lambda a, c: max(0.0, min(a, c)), input_a, input_c))
            input_b = list(map(lambda a, c: sqrt(a * a - c * c), input_a, input_c))

DolphinDream's avatar
DolphinDream включено в состав коммита
290
        # sanitize more inputs
DolphinDream's avatar
DolphinDream включено в состав коммита
291
        input_N = list(map(lambda n: max(3, int(n)), input_N))
DolphinDream's avatar
DolphinDream включено в состав коммита
292
        input_s = list(map(lambda s: max(0.0, s), input_s))
DolphinDream's avatar
DolphinDream включено в состав коммита
293
294
295

        parameters = match_long_repeat([input_a, input_b, input_N, input_p, input_r, input_s])

DolphinDream's avatar
DolphinDream включено в состав коммита
296
297
298
299
300
301
302
303
        # conversion factor from the current angle units to radians
        au = self.radians_conversion_factor()

        verts_list = []
        edges_list = []
        polys_list = []
        f1_list = []
        f2_list = []
DolphinDream's avatar
DolphinDream включено в состав коммита
304
        for a, b, N, p, r, s in zip(*parameters):
DolphinDream's avatar
DolphinDream включено в состав коммита
305
306
307
308
309
310
311
312
313
314
315
316
317
            verts, edges, polys, f1, f2 = self.make_ellipse(a, b, N, p * au, r * au, s)
            verts_list.append(verts)
            edges_list.append(edges)
            polys_list.append(polys)
            f1_list.append(f1)
            f2_list.append(f2)

        outputs["Verts"].sv_set(verts_list)
        outputs["Edges"].sv_set(edges_list)
        outputs["Polys"].sv_set(polys_list)

        outputs["F1"].sv_set([f1_list])
        outputs["F2"].sv_set([f2_list])
DolphinDream's avatar
DolphinDream включено в состав коммита
318
319
320


def register():
DolphinDream's avatar
DolphinDream включено в состав коммита
321
    bpy.utils.register_class(SvEllipseNodeMK2)
DolphinDream's avatar
DolphinDream включено в состав коммита
322
323
324


def unregister():
DolphinDream's avatar
DolphinDream включено в состав коммита
325
    bpy.utils.unregister_class(SvEllipseNodeMK2)