csgPlane.splitPolygon()   F
last analyzed

Complexity

Conditions 18

Size

Total Lines 69

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 18
dl 0
loc 69
rs 2.588

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like csgPlane.splitPolygon() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
import math
2
from gem import vector
3
from gem import plane
4
5
class CSG(object):
6
    def __init__(self):
7
        self.polygons = []
8
9
    def clone(self):
10
        newCSG = CSG()
11
        for i in range(len(self.polygons)):
12
            newCSG.polygons.append(self.polygons[i].clone())
13
        return newCSG
14
15
    def fromPolygons(self, polygons):
16
        newCSG = CSG()
17
        newCSG.polygons = polygons
18
        return newCSG
19
20
    def toPolygons(self):
21
        return self.polygons
22
23
    def setColor(self, r, g, b):
24
        for i in range(len(self.polygons)):
25
            self.polygons[i].shared = [r, g, b]
26
27
    def union(self, otherCSG):
28
        csgA = csgNode(self.clone().polygons)
29
        csgB = csgNode(otherCSG.clone().polygons)
30
        csgA.clipTo(csgB)
31
        csgB.clipTo(csgA)
32
        csgB.invert()
33
        csgB.clipTo(csgA)
34
        csgB.invert()
35
        csgA.build(csgB.allPolygons())
36
37
        return self.fromPolygons(csgA.allPolygons())
38
39 View Code Duplication
    def subtract(self, otherCSG):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
40
        csgA = csgNode(self.clone().polygons)
41
        csgB = csgNode(otherCSG.clone().polygons)
42
43
        csgA.invert()
44
        csgA.clipTo(csgB)
45
        csgB.clipTo(csgA)
46
        csgB.invert()
47
        csgB.clipTo(csgA)
48
        csgB.invert()
49
        csgA.build(csgB.allPolygons())
50
        csgA.invert()
51
        return self.fromPolygons(csgA.allPolygons())
52
53 View Code Duplication
    def intersect(self, otherCSG):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
54
        csgA = csgNode(self.clone().polygons)
55
        csgB = csgNode(otherCSG.clone().polygons)
56
57
        csgA.invert()
58
        csgB.clipTo(csgA)
59
        csgB.invert()
60
        csgA.clipTo(csgB)
61
        csgB.clipTo(csgA)
62
        csgA.build(csgB.allPolygons())
63
        csgA.invert()
64
65
66
        return self.fromPolygons(csgA.allPolygons())
67
68
    def inverse(self):
69
        newCSG = self.clone()
70
        for i in range(len(self.polygons)):
71
            newCSG.polygons[i] = self.polygons[i].flip()
72
        return newCSG
73
74
    def cube(self, center, radius):
75
        c = center
76
        r = radius
77
78
        indices = [[0, 4, 6, 2],
79
                   [1, 3, 7, 5],
80
                   [0, 1, 5, 4],
81
                   [2, 6, 7, 3],
82
                   [0, 2, 3, 1],
83
                   [4, 5, 7, 6]]
84
85
        normals = [[-1, 0, 0],
86
                   [ 1, 0, 0],
87
                   [ 0,-1, 0],
88
                   [ 0, 1, 0],
89
                   [ 0, 0,-1],
90
                   [ 0, 0, 1]]
91
92
        finalPolygons = []
93
        invidiualPolygonVertices = []
94
95
        for i in range(6):
96
            invidiualPolygonVertices = []
97
            for j in range(4):
98
                pos = vector.Vector(3,
99
                                    data = [c[0] + r[0] * (2 *  int(bool(indices[i][j] & 1)) - 1),
100
                                            c[1] + r[1] * (2 *  int(bool(indices[i][j] & 2)) - 1),
101
                                            c[2] + r[2] * (2 *  int(bool(indices[i][j] & 4)) - 1)])
102
103
                invidiualPolygonVertices.append(csgVertex(pos, vector.Vector(3, normals[i])))
104
            finalPolygons.append(csgPolygon(invidiualPolygonVertices, [1, 1, 1]))
105
106
        return self.fromPolygons(finalPolygons)
107
108
    def sphere(self, center, radius, slices, stacks):
109
        c = vector.Vector(3, data=center)
110
        r = radius
111
        sl = slices
112
        st = stacks
113
114
        polygons = []
115
        vertices = []
116
117
        #vertex = lambda theta, phi: vertices.append(csgVertex(c + (vector.Vector(3, data=[math.cos(theta) * math.sin(phi), math.cos(phi), math.sin(theta) * math.sin(phi)]) * r), vector.Vector(3, data=[math.cos(theta) * math.sin(phi), math.cos(phi), math.sin(theta) * math.sin(phi)])))
118
        def vertex(theta, phi):
119
            t = theta * math.pi * 2
120
            p = phi * math.pi
121
            cosT = math.cos(t)
122
            cosP = math.cos(p)
123
            sinT = math.sin(t)
124
            sinP = math.sin(p)
125
126
            direction = vector.Vector(3, data=[cosT * sinP, cosP, sinT * sinP])
127
128
            vertices.append(csgVertex(c + (direction * r), direction))
129
130
        for i in range(sl):
131
            for j in range(st):
132
133
                i = float(i)
134
                j = float(j)
135
                
136
                vertices = []
137
138
                vertex(i / sl, j / st)
139
140
                if j > 0:
141
                    vertex((i + 1) / sl, j / st)
142
143
                if j < (stacks - 1):
144
                    vertex((i + 1) / sl, (j + 1) / st)
145
146
                vertex(i / sl, (j + 1) / st)
147
148
                polygons.append(csgPolygon(vertices, [1, 1, 1]))
149
150
        return self.fromPolygons(polygons)
151
152
    def cylinder(self, start, end, radius, slices):
153
        s = vector.Vector(3, data=start)
154
        e = vector.Vector(3, data=end)
155
        r = radius
156
        slices = slices
157
158
        ray = e - s
159
160
        axisZ = ray.normalize()
161
        isY = abs(axisZ.vector[1]) > 0.5
162
        axisX = vector.cross(vector.Vector(3, data=[isY, int(not isY), 0.0]), axisZ).normalize()
163
        axisY = vector.cross(axisX, axisZ).normalize()
164
165
        start = csgVertex(s, -axisZ)
166
        end = csgVertex(e, axisZ.normalize())
167
168
        polygons = []
169
170
        def point(stack, sliceC, normalBlend):
171
            angle = sliceC * math.pi * 2
172
            out = axisX * math.cos(angle) + axisY * math.sin(angle)
173
            pos = s + ray * stack + out * r
174
            normal = out * (1 - abs(normalBlend)) + axisZ * normalBlend
175
            return csgVertex(pos, normal)
176
177
        for i in range(slices):
178
            t0 = i / slices
179
            t1 = (i + 1) / slices
180
181
            polygons.append(csgPolygon([start, point(0, t0, -1), point(0, t1, -1)], [1, 1, 1]))
182
            polygons.append(csgPolygon([point(0, t1, 0), point(0, t0, 0), point(1, t0, 0), point(1, t1, 0)], [1, 1, 1]))
183
            polygons.append(csgPolygon([end, point(1, t1, 1), point(1, t0, 1)], [1, 1, 1]))
184
185
        return self.fromPolygons(polygons)
186
187
188
class csgVertex(object):
189
    def __init__(self, pos, normal):
190
        self.pos = pos
191
        self.normal = normal
192
        self.color = [1, 0, 0]
193
194
    def __repr__(self):
195
        return 'csgVertex: pos:{} , normal:{}, color:{}'.format(self.pos, self.normal, self.color)
196
197
    def clone(self):
198
        return csgVertex(self.pos.clone(), self.normal.clone())
199
200
    def flip(self):
201
        self.normal = -self.normal
202
203
    def interpolate(self, vert, t):
204
        pos = vector.lerp(self.pos, vert.pos, t)
205
        normal = vector.lerp(self.normal, vert.normal, t)
206
        return csgVertex(pos, normal)
207
208
class csgPlane(plane.Plane):
209
    def __init__(self, normal, d):
210
        super(csgPlane, self).__init__()
211
        self.normal = normal
212
        self.d = d
213
        self.epsilon = 1e-5
214
215
    def __repr__(self):
216
        return 'csgPlane: normal:{} , d:{}'.format(self.normal, self.d)
217
218
    def clone(self):
219
        return csgPlane(self.normal.clone(), self.d)
220
221
    def splitPolygon(self, polygon, cFront, cBack, f1, b1):
222
        coplanarFront = cFront
223
        coplanarBack = cBack
224
        front = f1
225
        back = b1
226
227
        COPLANAR = 0
228
        FRONT = 1
229
        BACK = 2
230
        SPANNING = 3
231
232
        polygonType = 0
233
        types = []
234
235
        for i in range(len(polygon.vertices)):
236
            t = self.normal.dot(polygon.vertices[i].pos) - self.d
237
            typeP = 0
238
            if t < -self.epsilon:
239
                typeP = BACK
240
            if t > self.epsilon:
241
                typeP = FRONT
242
            if t > -self.epsilon and t < self.epsilon:
243
                typeP = COPLANAR
244
245
            polygonType |= typeP
246
            types.append(typeP)
247
248
249
        if polygonType == COPLANAR:
250
            if self.normal.dot(polygon.plane.normal) > 0:
251
                coplanarFront.append(polygon)
252
            else:
253
                coplanarBack.append(polygon)
254
        if polygonType == FRONT:
255
            front.append(polygon)
256
        if polygonType == BACK:
257
            back.append(polygon)
258
        if polygonType == SPANNING:
259
            f = []
260
            b = []
261
262
            for i in range(len(polygon.vertices)):
263
                j = (i + 1) % len(polygon.vertices)
264
                ti = types[i]
265
                tj = types[j]
266
267
                vi = polygon.vertices[i]
268
                vj = polygon.vertices[j]
269
270
                if (ti != BACK):
271
                    f.append(vi)
272
                if (ti != FRONT):
273
                    if ti != BACK:
274
                        b.append(vi.clone())
275
                    else:
276
                        b.append(vi)
277
278
                if ((ti | tj) == SPANNING):
279
                    t = (self.d - self.normal.dot(vi.pos)) / self.normal.dot(vj.pos - vi.pos)
280
                    v = vi.interpolate(vj, t)
281
                    f.append(v)
282
                    b.append(v.clone())
283
284
            if (len(f) >= 3):
285
                front.append(csgPolygon(f, polygon.shared))
286
            if (len(b) >= 3):
287
                back.append(csgPolygon(b, polygon.shared))
288
289
        return coplanarFront, coplanarBack, front, back
290
291
class csgPolygon(object):
292
    def __init__(self, vertices, shared):
293
        self.vertices = vertices
294
        self.shared = shared
295
        self.plane = csgPlane(vector.Vector(3), 0)
296
        self.plane.fromPoints(vertices[0].pos, vertices[1].pos, vertices[2].pos)
297
298
    def __repr__(self):
299
        return 'csgPolygon: vertices:{} , shared:{}, plane:{}'.format(self.vertices, self.shared, self.plane)
300
301
    def clone(self):
302
        return csgPolygon(self.vertices, self.shared)
303
304
    def flip(self):
305
        vertices = self.vertices[::-1]
306
        for i in range(len(self.vertices)):
307
            if self.vertices[i] == None:
308
                vertices.append(self.vertices[i].flip())
309
310
        self.vertices = vertices
311
        self.plane.flip()
312
313
class csgNode(CSG):
314
    def __init__(self, polygons):
315
        super(csgNode, self).__init__()
316
        self.plane = None
317
        self.front = None
318
        self.back = None
319
        self.polygons = []
320
321
        if polygons:
322
            self.build(polygons)
323
324
    def clone(self):
325
        newNode = csgNode([])
326
        newNode.plane = self.plane and self.plane.clone()
327
        newNode.front = self.front and self.front.clone()
328
        newNode.back = self.back and self.back.clone()
329
        newNode.polygons = self.polygons
330
        return newNode
331
332
    def invert(self):
333
        for i in range(len(self.polygons)):
334
            self.polygons[i].flip()
335
336
        self.plane.flip()
337
338
        if (self.front):
339
            self.front.invert()
340
341
        if (self.back):
342
            self.back.invert()
343
344
        temp = self.front
345
        self.front = self.back
346
        self.back = temp
347
348
    def clipPolygons(self, polygons):
349
        if (self.plane == 0):
350
            return polygons
351
352
        front = []
353
        back = []
354
355
        for i in range(len(polygons)):
356
            front, back, front, back = self.plane.splitPolygon(polygons[i], front, back, front, back)
357
358
        if self.front:
359
            front = self.front.clipPolygons(front)
360
361
        if self.back:
362
            back = self.back.clipPolygons(back)
363
        else:
364
            back = []
365
366
        return front + back
367
368
    def clipTo(self, bsp):
369
        self.polygons = bsp.clipPolygons(self.polygons)
370
371
        if self.front:
372
            self.front.clipTo(bsp)
373
        if self.back:
374
            self.back.clipTo(bsp)
375
376
    def allPolygons(self):
377
        polygons = self.polygons
378
379
        if self.front:
380
            polygons = polygons + (self.front.allPolygons())
381
        if self.back:
382
            polygons = polygons + (self.back.allPolygons())
383
        return polygons
384
385
    def build(self, polygons):
386
        if (len(polygons) == 0):
387
            return
388
389
        if (self.plane is None):
390
            self.plane = polygons[0].plane.clone()
391
392
        front = []
393
        back = []
394
395
        for i in range(len(polygons)):
396
            self.polygons, self.polygons, front, back = self.plane.splitPolygon(polygons[i], self.polygons, self.polygons, front, back)
397
398
        if len(front):
399
            if (self.front is None):
400
                self.front = csgNode([])
401
            self.front.build(front)
402
403
        if len(back):
404
            if(self.back is None):
405
                self.back = csgNode([])
406
            self.back.build(back)
407