Fix invalid string comparisons
[blender-addons.git] / add_advanced_objects_panels / object_laplace_lightning.py
blobfa50afd3a329073e090149c3f287516ec87b1a76
1 # ##### BEGIN GPL LICENSE BLOCK #####
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.
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.
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.
17 # ##### END GPL LICENSE BLOCK #####
19 # NOTE: moved the winmgr properties to __init__ and scene
20 # search for context.scene.advanced_objects1
22 bl_info = {
23 "name": "Laplacian Lightning",
24 "author": "teldredge",
25 "blender": (2, 78, 0),
26 "location": "3D View > Toolshelf > Create > Laplacian Lightning",
27 "description": "Lightning mesh generator using laplacian growth algorithm",
28 "warning": "",
29 "category": "Object"}
31 # BLENDER LAPLACIAN LIGHTNING
32 # teldredge
33 # www.funkboxing.com
34 # https://developer.blender.org/T27189
36 # using algorithm from
37 # FAST SIMULATION OF LAPLACIAN GROWTH (FSLG)
38 # http://gamma.cs.unc.edu/FRAC/
40 # and a few ideas ideas from
41 # FAST ANIMATION OF LIGHTNING USING AN ADAPTIVE MESH (FALUAM)
42 # http://gamma.cs.unc.edu/FAST_LIGHTNING/
45 """
46 ----- RELEASE LOG/NOTES/PONTIFICATIONS -----
47 v0.1.0 - 04.11.11
48 basic generate functions and UI
49 object creation report (Custom Properties: FSLG_REPORT)
50 v0.2.0 - 04.15.11
51 started spelling laplacian right.
52 add curve function (not in UI) ...twisting problem
53 classify stroke by MAIN path, h-ORDER paths, TIP paths
54 jitter cells for mesh creation
55 add materials if present
56 v0.2.1 - 04.16.11
57 mesh classification speedup
58 v0.2.2 - 04.21.11
59 fxns to write/read array to file
60 restrict growth to insulator cells (object bounding box)
61 origin/ground defineable by object
62 gridunit more like 'resolution'
63 v0.2.3 - 04.24.11
64 cloud attractor object (termintates loop if hit)
65 secondary path orders (hOrder) disabled in UI (set to 1)
66 v0.2.4 - 04.26.11
67 fixed object selection in UI
68 will not run if required object not selected
69 moved to view 3d > toolbox
70 v0.2.5 - 05.08.11
71 testing for 2.57b
72 single mesh output (for build modifier)
73 speedups (dist fxn)
74 v0.2.6 - 06.20.11
75 scale/pos on 'write to cubes' works now
76 if origin obj is mesh, uses all verts as initial charges
77 semi-helpful tooltips
78 speedups, faster dedupe fxn, faster classification
79 use any shape mesh obj as insulator mesh
80 must have rot=0, scale=1, origin set to geometry
81 often fails to block bolt with curved/complex shapes
82 separate single and multi mesh creation
83 v0.2.7 - 01.05.13
84 fixed the issue that prevented enabling the add-on
85 fixed makeMeshCube fxn
86 disabled visualization for voxels
88 v0.x -
89 -prevent create_setup_objects from generating duplicates
90 -fix vis fxn to only buildCPGraph once for VM or VS
91 -improve list fxns (rid of ((x,y,z),w) and use (x,y,z,w)), use 'sets'
92 -create python cmodule for a few of most costly fxns
93 i have pretty much no idea how to do this yet
94 -cloud and insulator can be groups of MESH objs
95 -text output, possibly to save on interrupt, allow continue from text
96 -?hook modifiers from tips->sides->main, weight w/ vert groups
97 -user defined 'attractor' path
98 -fix add curve function
99 -animated arcs via. ionization path
100 -environment map boundary conditions - requires Eqn. 15 from FSLG.
101 -assign wattage at each segment for HDRI
102 -?default settings for -lightning, -teslacoil, -spark/arc
103 -fix hOrder functionality
104 -multiple 'MAIN' brances for non-lightning discharges
105 -n-symmetry option, create mirror images, snowflakes, etc...
108 import bpy
109 import time
110 import random
111 from bpy.types import (
112 Operator,
113 Panel,
115 # from math import sqrt
116 from mathutils import Vector
117 import struct
118 import bisect
119 import os.path
121 # -- Globals --
122 notZero = 0.0000000001
123 # set to True to enable debug prints
124 DEBUG = False
127 # Utility Functions
129 # func - function name, text - message, var - variable to print
130 # it can have one variable to observe
131 def debug_prints(func="", text="Message", var=None):
132 global DEBUG
133 if DEBUG:
134 print("\n[{}]\nmessage: {}".format(func, text))
135 if var:
136 print("variable: ", var)
139 # pass variables just like for the regular prints
140 def debug_print_vars(*args, **kwargs):
141 global DEBUG
142 if DEBUG:
143 print(*args, **kwargs)
146 def within(x, y, d):
147 # CHECK IF x - d <= y <= x + d
148 if x - d <= y and x + d >= y:
149 return True
150 else:
151 return False
154 def dist(ax, ay, az, bx, by, bz):
155 dv = Vector((ax, ay, az)) - Vector((bx, by, bz))
156 d = dv.length
157 return d
160 def splitList(aList, idx):
161 ll = []
162 for x in aList:
163 ll.append(x[idx])
164 return ll
167 def splitListCo(aList):
168 ll = []
169 for p in aList:
170 ll.append((p[0], p[1], p[2]))
171 return ll
174 def getLowHigh(aList):
175 tLow = aList[0]
176 tHigh = aList[0]
177 for a in aList:
178 if a < tLow:
179 tLow = a
180 if a > tHigh:
181 tHigh = a
182 return tLow, tHigh
185 def weightedRandomChoice(aList):
186 tL = []
187 tweight = 0
188 for a in range(len(aList)):
189 idex = a
190 weight = aList[a]
191 if weight > 0.0:
192 tweight += weight
193 tL.append((tweight, idex))
194 i = bisect.bisect(tL, (random.uniform(0, tweight), None))
195 r = tL[i][1]
196 return r
199 def getStencil3D_26(x, y, z):
200 nL = []
201 for xT in range(x - 1, x + 2):
202 for yT in range(y - 1, y + 2):
203 for zT in range(z - 1, z + 2):
204 nL.append((xT, yT, zT))
205 nL.remove((x, y, z))
206 return nL
209 def jitterCells(aList, jit):
210 j = jit / 2
211 bList = []
212 for a in aList:
213 ax = a[0] + random.uniform(-j, j)
214 ay = a[1] + random.uniform(-j, j)
215 az = a[2] + random.uniform(-j, j)
216 bList.append((ax, ay, az))
217 return bList
220 def deDupe(seq, idfun=None):
221 # Thanks to this guy - http://www.peterbe.com/plog/uniqifiers-benchmark
222 if idfun is None:
223 def idfun(x):
224 return x
225 seen = {}
226 result = []
227 for item in seq:
228 marker = idfun(item)
229 if marker in seen:
230 continue
231 seen[marker] = 1
232 result.append(item)
233 return result
236 # Visulization functions
238 def writeArrayToVoxel(arr, filename):
239 gridS = 64
240 half = int(gridS / 2)
241 bitOn = 255
242 aGrid = [[[0 for z in range(gridS)] for y in range(gridS)] for x in range(gridS)]
243 for a in arr:
244 try:
245 aGrid[a[0] + half][a[1] + half][a[2] + half] = bitOn
246 except:
247 debug_prints(func="writeArrayToVoxel", text="Particle beyond voxel domain")
249 file = open(filename, "wb")
250 for z in range(gridS):
251 for y in range(gridS):
252 for x in range(gridS):
253 file.write(struct.pack('B', aGrid[x][y][z]))
254 file.flush()
255 file.close()
258 def writeArrayToFile(arr, filename):
259 file = open(filename, "w")
260 for a in arr:
261 tstr = str(a[0]) + ',' + str(a[1]) + ',' + str(a[2]) + '\n'
262 file.write(tstr)
263 file.close
266 def readArrayFromFile(filename):
267 file = open(filename, "r")
268 arr = []
269 for f in file:
270 pt = f[0:-1].split(',')
271 arr.append((int(pt[0]), int(pt[1]), int(pt[2])))
272 return arr
275 def makeMeshCube_OLD(msize):
276 msize = msize / 2
277 mmesh = bpy.data.meshes.new('q')
278 mmesh.vertices.add(8)
279 mmesh.vertices[0].co = [-msize, -msize, -msize]
280 mmesh.vertices[1].co = [-msize, msize, -msize]
281 mmesh.vertices[2].co = [msize, msize, -msize]
282 mmesh.vertices[3].co = [msize, -msize, -msize]
283 mmesh.vertices[4].co = [-msize, -msize, msize]
284 mmesh.vertices[5].co = [-msize, msize, msize]
285 mmesh.vertices[6].co = [msize, msize, msize]
286 mmesh.vertices[7].co = [msize, -msize, msize]
287 mmesh.faces.add(6)
288 mmesh.faces[0].vertices_raw = [0, 1, 2, 3]
289 mmesh.faces[1].vertices_raw = [0, 4, 5, 1]
290 mmesh.faces[2].vertices_raw = [2, 1, 5, 6]
291 mmesh.faces[3].vertices_raw = [3, 2, 6, 7]
292 mmesh.faces[4].vertices_raw = [0, 3, 7, 4]
293 mmesh.faces[5].vertices_raw = [5, 4, 7, 6]
294 mmesh.update(calc_edges=True)
296 return(mmesh)
299 def makeMeshCube(msize):
300 m2 = msize / 2
301 # verts = [(0,0,0),(0,5,0),(5,5,0),(5,0,0),(0,0,5),(0,5,5),(5,5,5),(5,0,5)]
302 verts = [(-m2, -m2, -m2), (-m2, m2, -m2), (m2, m2, -m2), (m2, -m2, -m2),
303 (-m2, -m2, m2), (-m2, m2, m2), (m2, m2, m2), (m2, -m2, m2)]
304 faces = [
305 (0, 1, 2, 3), (4, 5, 6, 7), (0, 4, 5, 1),
306 (1, 5, 6, 2), (2, 6, 7, 3), (3, 7, 4, 0)
308 # Define mesh and object
309 mmesh = bpy.data.meshes.new("Cube")
311 # Create mesh
312 mmesh.from_pydata(verts, [], faces)
313 mmesh.update(calc_edges=True)
314 return(mmesh)
317 def writeArrayToCubes(arr, gridBU, orig, cBOOL=False, jBOOL=True):
318 for a in arr:
319 x = a[0]
320 y = a[1]
321 z = a[2]
322 me = makeMeshCube(gridBU)
323 ob = bpy.data.objects.new('xCUBE', me)
324 ob.location.x = (x * gridBU) + orig[0]
325 ob.location.y = (y * gridBU) + orig[1]
326 ob.location.z = (z * gridBU) + orig[2]
328 if cBOOL: # mostly unused
329 # pos + blue, neg - red, zero: black
330 col = (1.0, 1.0, 1.0, 1.0)
331 if a[3] == 0:
332 col = (0.0, 0.0, 0.0, 1.0)
333 if a[3] < 0:
334 col = (-a[3], 0.0, 0.0, 1.0)
335 if a[3] > 0:
336 col = (0.0, 0.0, a[3], 1.0)
337 ob.color = col
338 bpy.context.scene.objects.link(ob)
339 bpy.context.scene.update()
341 if jBOOL:
342 # Selects all cubes w/ ?bpy.ops.object.join() b/c
343 # Can't join all cubes to a single mesh right... argh...
344 for q in bpy.context.scene.objects:
345 q.select = False
346 if q.name[0:5] == 'xCUBE':
347 q.select = True
348 bpy.context.scene.objects.active = q
351 def addVert(ob, pt, conni=-1):
352 mmesh = ob.data
353 mmesh.vertices.add(1)
354 vcounti = len(mmesh.vertices) - 1
355 mmesh.vertices[vcounti].co = [pt[0], pt[1], pt[2]]
356 if conni > -1:
357 mmesh.edges.add(1)
358 ecounti = len(mmesh.edges) - 1
359 mmesh.edges[ecounti].vertices = [conni, vcounti]
360 mmesh.update()
363 def addEdge(ob, va, vb):
364 mmesh = ob.data
365 mmesh.edges.add(1)
366 ecounti = len(mmesh.edges) - 1
367 mmesh.edges[ecounti].vertices = [va, vb]
368 mmesh.update()
371 def newMesh(mname):
372 mmesh = bpy.data.meshes.new(mname)
373 omesh = bpy.data.objects.new(mname, mmesh)
374 bpy.context.scene.objects.link(omesh)
375 return omesh
378 def writeArrayToMesh(mname, arr, gridBU, rpt=None):
379 mob = newMesh(mname)
380 mob.scale = (gridBU, gridBU, gridBU)
381 if rpt:
382 addReportProp(mob, rpt)
383 addVert(mob, arr[0], -1)
384 for ai in range(1, len(arr)):
385 a = arr[ai]
386 addVert(mob, a, ai - 1)
387 return mob
390 # out of order - some problem with it adding (0,0,0)
391 def writeArrayToCurves(cname, arr, gridBU, bd=.05, rpt=None):
392 cur = bpy.data.curves.new('fslg_curve', 'CURVE')
393 cur.use_fill_front = False
394 cur.use_fill_back = False
395 cur.bevel_depth = bd
396 cur.bevel_resolution = 2
397 cob = bpy.data.objects.new(cname, cur)
398 cob.scale = (gridBU, gridBU, gridBU)
400 if rpt:
401 addReportProp(cob, rpt)
402 bpy.context.scene.objects.link(cob)
403 cur.splines.new('BEZIER')
404 cspline = cur.splines[0]
405 div = 1 # spacing for handles (2 - 1/2 way, 1 - next bezier)
407 for a in range(len(arr)):
408 cspline.bezier_points.add(1)
409 bp = cspline.bezier_points[len(cspline.bezier_points) - 1]
410 if a - 1 < 0:
411 hL = arr[a]
412 else:
413 hx = arr[a][0] - ((arr[a][0] - arr[a - 1][0]) / div)
414 hy = arr[a][1] - ((arr[a][1] - arr[a - 1][1]) / div)
415 hz = arr[a][2] - ((arr[a][2] - arr[a - 1][2]) / div)
416 hL = (hx, hy, hz)
418 if a + 1 > len(arr) - 1:
419 hR = arr[a]
420 else:
421 hx = arr[a][0] + ((arr[a + 1][0] - arr[a][0]) / div)
422 hy = arr[a][1] + ((arr[a + 1][1] - arr[a][1]) / div)
423 hz = arr[a][2] + ((arr[a + 1][2] - arr[a][2]) / div)
424 hR = (hx, hy, hz)
425 bp.co = arr[a]
426 bp.handle_left = hL
427 bp.handle_right = hR
430 def addArrayToMesh(mob, arr):
431 addVert(mob, arr[0], -1)
432 mmesh = mob.data
433 vcounti = len(mmesh.vertices) - 1
434 for ai in range(1, len(arr)):
435 a = arr[ai]
436 addVert(mob, a, len(mmesh.vertices) - 1)
439 def addMaterial(ob, matname):
440 mat = bpy.data.materials[matname]
441 ob.active_material = mat
444 def writeStokeToMesh(arr, jarr, MAINi, HORDERi, TIPSi, orig, gs, rpt=None):
445 # main branch
446 debug_prints(func="writeStokeToMesh", text='Writing main branch')
447 llmain = []
449 for x in MAINi:
450 llmain.append(jarr[x])
451 mob = writeArrayToMesh('la0MAIN', llmain, gs)
452 mob.location = orig
454 # horder branches
455 for hOi in range(len(HORDERi)):
456 debug_prints(func="writeStokeToMesh", text="Writing order", var=hOi)
457 hO = HORDERi[hOi]
458 hob = newMesh('la1H' + str(hOi))
460 for y in hO:
461 llHO = []
462 for x in y:
463 llHO.append(jarr[x])
464 addArrayToMesh(hob, llHO)
465 hob.scale = (gs, gs, gs)
466 hob.location = orig
468 # tips
469 debug_prints(func="writeStokeToMesh", text="Writing tip paths")
470 tob = newMesh('la2TIPS')
471 for y in TIPSi:
472 llt = []
473 for x in y:
474 llt.append(jarr[x])
475 addArrayToMesh(tob, llt)
476 tob.scale = (gs, gs, gs)
477 tob.location = orig
479 # add materials to objects (if they exist)
480 try:
481 addMaterial(mob, 'edgeMAT-h0')
482 addMaterial(hob, 'edgeMAT-h1')
483 addMaterial(tob, 'edgeMAT-h2')
484 debug_prints(func="writeStokeToMesh", text="Added materials")
486 except:
487 debug_prints(func="writeStokeToMesh", text="Materials not found")
489 # add generation report to all meshes
490 if rpt:
491 addReportProp(mob, rpt)
492 addReportProp(hob, rpt)
493 addReportProp(tob, rpt)
496 def writeStokeToSingleMesh(arr, jarr, orig, gs, mct, rpt=None):
497 sgarr = buildCPGraph(arr, mct)
498 llALL = []
500 Aob = newMesh('laALL')
501 for pt in jarr:
502 addVert(Aob, pt)
503 for cpi in range(len(sgarr)):
504 ci = sgarr[cpi][0]
505 pi = sgarr[cpi][1]
506 addEdge(Aob, pi, ci)
507 Aob.location = orig
508 Aob.scale = ((gs, gs, gs))
510 if rpt:
511 addReportProp(Aob, rpt)
514 def visualizeArray(cg, oob, gs, vm, vs, vc, vv, rst):
515 winmgr = bpy.context.scene.advanced_objects1
516 # IN: (cellgrid, origin, gridscale,
517 # mulimesh, single mesh, cubes, voxels, report string)
518 origin = oob.location
520 # deal with vert multi-origins
521 oct = 2
522 if oob.type == 'MESH':
523 oct = len(oob.data.vertices)
525 # jitter cells
526 if vm or vs:
527 cjarr = jitterCells(cg, 1)
529 if vm: # write array to multi mesh
531 aMi, aHi, aTi = classifyStroke(cg, oct, winmgr.HORDER)
532 debug_prints(func="visualizeArray", text="Writing to multi-mesh")
533 writeStokeToMesh(cg, cjarr, aMi, aHi, aTi, origin, gs, rst)
534 debug_prints(func="visualizeArray", text="Multi-mesh written")
536 if vs: # write to single mesh
537 debug_prints(func="visualizeArray", text="Writing to single mesh")
538 writeStokeToSingleMesh(cg, cjarr, origin, gs, oct, rst)
539 debug_prints(func="visualizeArray", text="Single mesh written")
541 if vc: # write array to cube objects
542 debug_prints(func="visualizeArray", text="Writing to cubes")
543 writeArrayToCubes(cg, gs, origin)
544 debug_prints(func="visualizeArray", text="Cubes written")
546 if vv: # write array to voxel data file
547 debug_prints(func="visualizeArray", text="Writing to voxels")
548 fname = "FSLGvoxels.raw"
549 path = os.path.dirname(bpy.data.filepath)
550 writeArrayToVoxel(cg, path + "\\" + fname)
552 debug_prints(func="visualizeArray",
553 text="Voxel data written to:", var=path + "\\" + fname)
555 # read/write array to file (might not be necessary)
556 # tfile = 'c:\\testarr.txt'
557 # writeArrayToFile(cg, tfile)
558 # cg = readArrayFromFile(tfile)
560 # read/write array to curves (out of order)
561 # writeArrayToCurves('laMAIN', llmain, .10, .25)
564 # Algorithm functions
565 # from faluam paper
566 # plus some stuff i made up
568 def buildCPGraph(arr, sti=2):
569 # in -xyz array as built by generator
570 # out -[(childindex, parentindex)]
571 # sti - start index, 2 for empty, len(me.vertices) for mesh
572 sgarr = []
573 sgarr.append((1, 0))
575 for ai in range(sti, len(arr)):
576 cs = arr[ai]
577 cpts = arr[0:ai]
578 cslap = getStencil3D_26(cs[0], cs[1], cs[2])
580 for nc in cslap:
581 ct = cpts.count(nc)
582 if ct > 0:
583 cti = cpts.index(nc)
584 sgarr.append((ai, cti))
586 return sgarr
589 def buildCPGraph_WORKINPROGRESS(arr, sti=2):
590 # in -xyz array as built by generator
591 # out -[(childindex, parentindex)]
592 # sti - start index, 2 for empty, len(me.vertices) for mesh
593 sgarr = []
594 sgarr.append((1, 0))
595 ctix = 0
596 for ai in range(sti, len(arr)):
597 cs = arr[ai]
598 # cpts = arr[0:ai]
599 cpts = arr[ctix:ai]
600 cslap = getStencil3D_26(cs[0], cs[1], cs[2])
601 for nc in cslap:
602 ct = cpts.count(nc)
603 if ct > 0:
604 # cti = cpts.index(nc)
605 cti = ctix + cpts.index(nc)
606 ctix = cpts.index(nc)
608 sgarr.append((ai, cti))
610 return sgarr
613 def findChargePath(oc, fc, ngraph, restrict=[], partial=True):
614 # oc -origin charge index, fc -final charge index
615 # ngraph -node graph, restrict- index of sites cannot traverse
616 # partial -return partial path if restriction encountered
617 cList = splitList(ngraph, 0)
618 pList = splitList(ngraph, 1)
619 aRi = []
620 cNODE = fc
621 for x in range(len(ngraph)):
622 pNODE = pList[cList.index(cNODE)]
623 aRi.append(cNODE)
624 cNODE = pNODE
625 npNODECOUNT = cList.count(pNODE)
626 if cNODE == oc: # stop if origin found
627 aRi.append(cNODE) # return path
628 return aRi
629 if npNODECOUNT == 0: # stop if no parents
630 return [] # return []
631 if pNODE in restrict: # stop if parent is in restriction
632 if partial: # return partial or []
633 aRi.append(cNODE)
634 return aRi
635 else:
636 return []
639 def findTips(arr):
640 lt = []
641 for ai in arr[0: len(arr) - 1]:
642 a = ai[0]
643 cCOUNT = 0
644 for bi in arr:
645 b = bi[1]
646 if a == b:
647 cCOUNT += 1
648 if cCOUNT == 0:
649 lt.append(a)
651 return lt
654 def findChannelRoots(path, ngraph, restrict=[]):
655 roots = []
656 for ai in range(len(ngraph)):
657 chi = ngraph[ai][0]
658 par = ngraph[ai][1]
659 if par in path and chi not in path and chi not in restrict:
660 roots.append(par)
661 droots = deDupe(roots)
663 return droots
666 def findChannels(roots, tips, ngraph, restrict):
667 cPATHS = []
668 for ri in range(len(roots)):
669 r = roots[ri]
670 sL = 1
671 sPATHi = []
672 for ti in range(len(tips)):
673 t = tips[ti]
674 if t < r:
675 continue
676 tPATHi = findChargePath(r, t, ngraph, restrict, False)
677 tL = len(tPATHi)
678 if tL > sL:
679 if countChildrenOnPath(tPATHi, ngraph) > 1:
680 sL = tL
681 sPATHi = tPATHi
682 tTEMP = t
683 tiTEMP = ti
684 if len(sPATHi) > 0:
685 debug_print_vars(
686 "\n[findChannels]\n",
687 "found path/idex from", ri, 'of',
688 len(roots), "possible | tips:", tTEMP, tiTEMP
690 cPATHS.append(sPATHi)
691 tips.remove(tTEMP)
693 return cPATHS
696 def findChannels_WORKINPROGRESS(roots, ttips, ngraph, restrict):
697 cPATHS = []
698 tips = list(ttips)
699 for ri in range(len(roots)):
700 r = roots[ri]
701 sL = 1
702 sPATHi = []
703 tipREMOVE = [] # checked tip indexes, to be removed for next loop
704 for ti in range(len(tips)):
705 t = tips[ti]
706 if ti < ri:
707 continue
709 tPATHi = findChargePath(r, t, ngraph, restrict, False)
710 tL = len(tPATHi)
711 if tL > sL:
712 if countChildrenOnPath(tPATHi, ngraph) > 1:
713 sL = tL
714 sPATHi = tPATHi
715 tTEMP = t
716 tiTEMP = ti
717 if tL > 0:
718 tipREMOVE.append(t)
719 if len(sPATHi) > 0:
720 debug_print_vars(
721 "\n[findChannels_WORKINPROGRESS]\n",
722 "found path from root idex", ri, 'of',
723 len(roots), "possible roots | of tips= ", len(tips)
725 cPATHS.append(sPATHi)
727 for q in tipREMOVE:
728 tips.remove(q)
730 return cPATHS
733 def countChildrenOnPath(aPath, ngraph, quick=True):
734 # return how many branches
735 # count when node is a parent >1 times
736 # quick -stop and return after first
737 cCOUNT = 0
738 pList = splitList(ngraph, 1)
740 for ai in range(len(aPath) - 1):
741 ap = aPath[ai]
742 pc = pList.count(ap)
744 if quick and pc > 1:
745 return pc
747 return cCOUNT
750 # classify channels into 'main', 'hORDER/secondary' and 'side'
751 def classifyStroke(sarr, mct, hORDER=1):
752 debug_prints(func="classifyStroke", text="Classifying stroke")
753 # build child/parent graph (indexes of sarr)
754 sgarr = buildCPGraph(sarr, mct)
756 # find main channel
757 debug_prints(func="classifyStroke", text="Finding MAIN")
758 oCharge = sgarr[0][1]
759 fCharge = sgarr[len(sgarr) - 1][0]
760 aMAINi = findChargePath(oCharge, fCharge, sgarr)
762 # find tips
763 debug_prints(func="classifyStroke", text="Finding TIPS")
764 aTIPSi = findTips(sgarr)
766 # find horder channel roots
767 # hcount = orders between main and side/tips
768 # !!!still buggy!!!
769 hRESTRICT = list(aMAINi) # add to this after each time
770 allHPATHSi = [] # all ho paths: [[h0], [h1]...]
771 curPATHSi = [aMAINi] # list of paths find roots on
773 for h in range(hORDER):
774 allHPATHSi.append([])
775 for pi in range(len(curPATHSi)): # loop through all paths in this order
776 p = curPATHSi[pi]
777 # get roots for this path
778 aHROOTSi = findChannelRoots(p, sgarr, hRESTRICT)
779 debug_print_vars(
780 "\n[classifyStroke]\n",
781 "found", len(aHROOTSi), "roots in ORDER", h, ":paths:", len(curPATHSi)
783 # get channels for these roots
784 if len(aHROOTSi) == 0:
785 debug_prints(func="classifyStroke", text="No roots for found for channel")
786 aHPATHSi = []
787 continue
788 else:
789 aHPATHSiD = findChannels(aHROOTSi, aTIPSi, sgarr, hRESTRICT)
790 aHPATHSi = aHPATHSiD
791 allHPATHSi[h] += aHPATHSi
792 # set these channels as restrictions for next iterations
793 for hri in aHPATHSi:
794 hRESTRICT += hri
795 curPATHSi = aHPATHSi
797 # side branches, final order of hierarchy
798 # from tips that are not in an existing path
799 # back to any other point that is already on a path
800 aDRAWNi = []
801 aDRAWNi += aMAINi
802 for oH in allHPATHSi:
803 for o in oH:
804 aDRAWNi += o
805 aTPATHSi = []
806 for a in aTIPSi:
807 if a not in aDRAWNi:
808 aPATHi = findChargePath(oCharge, a, sgarr, aDRAWNi)
809 aDRAWNi += aPATHi
810 aTPATHSi.append(aPATHi)
812 return aMAINi, allHPATHSi, aTPATHSi
815 def voxelByVertex(ob, gs):
816 # 'voxelizes' verts in a mesh to list [(x,y,z),(x,y,z)]
817 # w/ respect gscale and ob origin (b/c should be origin obj)
818 # orig = ob.location
819 ll = []
820 for v in ob.data.vertices:
821 x = int(v.co.x / gs)
822 y = int(v.co.y / gs)
823 z = int(v.co.z / gs)
824 ll.append((x, y, z))
826 return ll
829 def voxelByRays(ob, orig, gs):
830 # mesh into a 3dgrid w/ respect gscale and bolt origin
831 # - does not take object rotation/scale into account
832 # - this is a horrible, inefficient function
833 # maybe the raycast/grid thing are a bad idea. but i
834 # have to 'voxelize the object w/ resct to gscale/origin
835 bbox = ob.bound_box
836 bbxL = bbox[0][0]
837 bbxR = bbox[4][0]
838 bbyL = bbox[0][1]
839 bbyR = bbox[2][1]
840 bbzL = bbox[0][2]
841 bbzR = bbox[1][2]
842 xct = int((bbxR - bbxL) / gs)
843 yct = int((bbyR - bbyL) / gs)
844 zct = int((bbzR - bbzL) / gs)
845 xs = int(xct / 2)
846 ys = int(yct / 2)
847 zs = int(zct / 2)
849 debug_print_vars(
850 "\n[voxelByRays]\n",
851 "Casting", xct, '/', yct, '/', zct, 'cells, total:',
852 xct * yct * zct, 'in obj-', ob.name
854 ll = []
855 rc = 100 # distance to cast from
856 # raycast top/bottom
857 debug_prints(func="voxelByRays", text="Raycasting top/bottom")
859 for x in range(xct):
860 for y in range(yct):
861 xco = bbxL + (x * gs)
862 yco = bbyL + (y * gs)
863 v1 = ((xco, yco, rc))
864 v2 = ((xco, yco, -rc))
865 vz1 = ob.ray_cast(v1, v2)
866 vz2 = ob.ray_cast(v2, v1)
868 debug_print_vars(
869 "\n[voxelByRays]\n", "vz1 is: ", vz1, "\nvz2 is: ", vz2
871 # Note: the API raycast return has changed now it is
872 # (result, location, normal, index) - result is a boolean
873 if vz1[0] is True:
874 ll.append((x - xs, y - ys, int(vz1[1][2] * (1 / gs))))
875 if vz2[0] is True:
876 ll.append((x - xs, y - ys, int(vz2[1][2] * (1 / gs))))
878 # raycast front/back
879 debug_prints(func="voxelByRays", text="Raycasting front/back")
881 for x in range(xct):
882 for z in range(zct):
883 xco = bbxL + (x * gs)
884 zco = bbzL + (z * gs)
885 v1 = ((xco, rc, zco))
886 v2 = ((xco, -rc, zco))
887 vy1 = ob.ray_cast(v1, v2)
888 vy2 = ob.ray_cast(v2, v1)
889 if vy1[0] is True:
890 ll.append((x - xs, int(vy1[1][1] * (1 / gs)), z - zs))
891 if vy2[0] is True:
892 ll.append((x - xs, int(vy2[1][1] * (1 / gs)), z - zs))
894 # raycast left/right
895 debug_prints(func="voxelByRays", text="Raycasting left/right")
897 for y in range(yct):
898 for z in range(zct):
899 yco = bbyL + (y * gs)
900 zco = bbzL + (z * gs)
901 v1 = ((rc, yco, zco))
902 v2 = ((-rc, yco, zco))
903 vx1 = ob.ray_cast(v1, v2)
904 vx2 = ob.ray_cast(v2, v1)
905 if vx1[0] is True:
906 ll.append((int(vx1[1][0] * (1 / gs)), y - ys, z - zs))
907 if vx2[0] is True:
908 ll.append((int(vx2[1][0] * (1 / gs)), y - ys, z - zs))
910 # add in neighbors so bolt wont go through
911 nlist = []
912 for l in ll:
913 nl = getStencil3D_26(l[0], l[1], l[2])
914 nlist += nl
916 # dedupe
917 debug_prints(func="voxelByRays", text="Added neighbors, deduping...")
918 rlist = deDupe(ll + nlist)
919 qlist = []
921 # relocate grid w/ respect gscale and bolt origin
922 # !!!need to add in obj rot/scale here somehow...
923 od = Vector(
924 ((ob.location[0] - orig[0]) / gs,
925 (ob.location[1] - orig[1]) / gs,
926 (ob.location[2] - orig[2]) / gs)
928 for r in rlist:
929 qlist.append((r[0] + int(od[0]), r[1] + int(od[1]), r[2] + int(od[2])))
931 return qlist
934 def fakeGroundChargePlane(z, charge):
935 eCL = []
936 xy = abs(z) / 2
937 eCL += [(0, 0, z, charge)]
938 eCL += [(xy, 0, z, charge)]
939 eCL += [(0, xy, z, charge)]
940 eCL += [(-xy, 0, z, charge)]
941 eCL += [(0, -xy, z, charge)]
943 return eCL
946 def addCharges(ll, charge):
947 # in: ll - [(x,y,z), (x,y,z)], charge - w
948 # out clist - [(x,y,z,w), (x,y,z,w)]
949 clist = []
950 for l in ll:
951 clist.append((l[0], l[1], l[2], charge))
952 return clist
955 # algorithm functions #
956 # from fslg #
958 def getGrowthProbability_KEEPFORREFERENCE(uN, aList):
959 # in: un -user term, clist -candidate sites, olist -candidate site charges
960 # out: list of [(xyz), pot, prob]
961 cList = splitList(aList, 0)
962 oList = splitList(aList, 1)
963 Omin, Omax = getLowHigh(oList)
964 if Omin == Omax:
965 Omax += notZero
966 Omin -= notZero
967 PdL = []
968 E = 0
969 E = notZero # divisor for (fslg - eqn. 12)
971 for o in oList:
972 Uj = (o - Omin) / (Omax - Omin) # (fslg - eqn. 13)
973 E += pow(Uj, uN)
975 for oi in range(len(oList)):
976 o = oList[oi]
977 Ui = (o - Omin) / (Omax - Omin)
978 Pd = (pow(Ui, uN)) / E # (fslg - eqn. 12)
979 PdINT = Pd * 100
980 PdL.append(Pd)
982 return PdL
985 # work in progress, trying to speed these up
986 def fslg_e13(x, min, max, u):
987 return pow((x - min) / (max - min), u)
990 def addit(x, y):
991 return x + y
994 def fslg_e12(x, min, max, u, e):
995 return (fslg_e13(x, min, max, u) / e) * 100
998 def getGrowthProbability(uN, aList):
999 # In: uN - user_term, cList - candidate sites, oList - candidate site charges
1000 # Out: list of prob
1001 cList = splitList(aList, 0)
1002 oList = splitList(aList, 1)
1003 Omin, Omax = getLowHigh(oList)
1005 if Omin == Omax:
1006 Omax += notZero
1007 Omin -= notZero
1009 PdL = []
1010 E = notZero
1011 minL = [Omin for q in range(len(oList))]
1012 maxL = [Omax for q in range(len(oList))]
1013 uNL = [uN for q in range(len(oList))]
1014 E = sum(map(fslg_e13, oList, minL, maxL, uNL))
1015 EL = [E for q in range(len(oList))]
1016 mp = map(fslg_e12, oList, minL, maxL, uNL, EL)
1018 for m in mp:
1019 PdL.append(m)
1021 return PdL
1024 def updatePointCharges(p, cList, eList=[]):
1025 # In: pNew - new growth cell
1026 # cList - old candidate sites, eList -SAME
1027 # Out: list of new charge at candidate sites
1028 r1 = 1 / 2 # (FSLG - Eqn. 10)
1029 nOiL = []
1031 for oi in range(len(cList)):
1032 o = cList[oi][1]
1033 c = cList[oi][0]
1034 iOe = 0
1035 rit = dist(c[0], c[1], c[2], p[0], p[1], p[2])
1036 iOe += (1 - (r1 / rit))
1037 Oit = o + iOe
1038 nOiL.append((c, Oit))
1040 return nOiL
1043 def initialPointCharges(pList, cList, eList=[]):
1044 # In: p -CHARGED CELL (XYZ), cList -candidate sites (XYZ, POT, PROB)
1045 # Out: cList -with potential calculated
1046 r1 = 1 / 2 # (FSLG - Eqn. 10)
1047 npList = []
1049 for p in pList:
1050 npList.append(((p[0], p[1], p[2]), 1.0))
1052 for e in eList:
1053 npList.append(((e[0], e[1], e[2]), e[3]))
1055 OiL = []
1056 for i in cList:
1057 Oi = 0
1058 for j in npList:
1059 if i != j[0]:
1060 rij = dist(i[0], i[1], i[2], j[0][0], j[0][1], j[0][2])
1061 Oi += (1 - (r1 / rij)) * j[1] # charge influence
1062 OiL.append(((i[0], i[1], i[2]), Oi))
1064 return OiL
1067 def getCandidateSites(aList, iList=[]):
1068 # In: aList -(X,Y,Z) of charged cell sites, iList - insulator sites
1069 # Out: candidate list of growth sites [(X,Y,Z)]
1070 cList = []
1071 for c in aList:
1072 tempList = getStencil3D_26(c[0], c[1], c[2])
1073 for t in tempList:
1074 if t not in aList and t not in iList:
1075 cList.append(t)
1076 ncList = deDupe(cList)
1078 return ncList
1081 # Setup functions
1083 def setupObjects():
1084 winmgr = bpy.context.scene.advanced_objects1
1085 oOB = bpy.data.objects.new('ELorigin', None)
1086 oOB.location = ((0, 0, 10))
1087 bpy.context.scene.objects.link(oOB)
1089 gOB = bpy.data.objects.new('ELground', None)
1090 gOB.empty_draw_type = 'ARROWS'
1091 bpy.context.scene.objects.link(gOB)
1093 cME = makeMeshCube(1)
1094 cOB = bpy.data.objects.new('ELcloud', cME)
1095 cOB.location = ((-2, 8, 12))
1096 cOB.hide_render = True
1097 bpy.context.scene.objects.link(cOB)
1099 iME = makeMeshCube(1)
1100 for v in iME.vertices:
1101 xyl = 6.5
1102 zl = .5
1103 v.co[0] = v.co[0] * xyl
1104 v.co[1] = v.co[1] * xyl
1105 v.co[2] = v.co[2] * zl
1106 iOB = bpy.data.objects.new('ELinsulator', iME)
1107 iOB.location = ((0, 0, 5))
1108 iOB.hide_render = True
1109 bpy.context.scene.objects.link(iOB)
1111 try:
1112 winmgr.OOB = 'ELorigin'
1113 winmgr.GOB = 'ELground'
1114 winmgr.COB = 'ELcloud'
1115 winmgr.IOB = 'ELinsulator'
1116 except:
1117 pass
1120 def checkSettings():
1121 check = True
1122 winmgr = bpy.context.scene.advanced_objects1
1123 message = ""
1124 if winmgr.OOB == "":
1125 message = "Error: no origin object selected"
1126 check = False
1128 if winmgr.GROUNDBOOL and winmgr.GOB == "":
1129 message = "Error: no ground object selected"
1130 check = False
1132 if winmgr.CLOUDBOOL and winmgr.COB == "":
1133 message = "Error: no cloud object selected"
1134 check = False
1136 if winmgr.IBOOL and winmgr.IOB == "":
1137 message = "Error: no insulator object selected"
1138 check = False
1140 if check is False:
1141 debug_prints(func="checkSettings", text=message)
1143 # return state and the message for the operator report
1144 return check, message
1147 # Main
1149 def FSLG():
1150 winmgr = bpy.context.scene.advanced_objects1
1151 # fast simulation of laplacian growth
1152 debug_prints(func="FSLG",
1153 text="Go go gadget: fast simulation of laplacian growth")
1154 tc1 = time.clock()
1155 TSTEPS = winmgr.TSTEPS
1157 obORIGIN = bpy.context.scene.objects[winmgr.OOB]
1158 obGROUND = bpy.context.scene.objects[winmgr.GOB]
1159 winmgr.ORIGIN = obORIGIN.location
1160 winmgr.GROUNDZ = int((obGROUND.location[2] - winmgr.ORIGIN[2]) / winmgr.GSCALE)
1162 # 1) insert initial charge(s) point (uses verts if mesh)
1163 cgrid = [(0, 0, 0)]
1165 if obORIGIN.type == 'MESH':
1166 debug_prints(
1167 func="FSLG",
1168 text="Origin object is mesh, 'voxelizing' initial charges from verts"
1170 cgrid = voxelByVertex(obORIGIN, winmgr.GSCALE)
1172 if winmgr.VMMESH:
1173 debug_prints(
1174 func="FSLG",
1175 text="Cannot classify stroke from vert origins yet, no multi-mesh output"
1177 winmgr.VMMESH = False
1178 winmgr.VSMESH = True
1180 # ground charge cell / insulator lists (echargelist/iclist)
1181 eChargeList = []
1182 icList = []
1183 if winmgr.GROUNDBOOL:
1184 eChargeList = fakeGroundChargePlane(winmgr.GROUNDZ, winmgr.GROUNDC)
1186 if winmgr.CLOUDBOOL:
1187 debug_prints(
1188 func="FSLG",
1189 text="'Voxelizing' cloud object (could take some time)"
1191 obCLOUD = bpy.context.scene.objects[winmgr.COB]
1192 eChargeListQ = voxelByRays(obCLOUD, winmgr.ORIGIN, winmgr.GSCALE)
1193 eChargeList = addCharges(eChargeListQ, winmgr.CLOUDC)
1194 debug_prints(
1195 func="FSLG",
1196 text="cloud object cell count", var=len(eChargeList)
1199 if winmgr.IBOOL:
1200 debug_prints(
1201 func="FSLG",
1202 text="'Voxelizing' insulator object (could take some time)"
1204 obINSULATOR = bpy.context.scene.objects[winmgr.IOB]
1205 icList = voxelByRays(obINSULATOR, winmgr.ORIGIN, winmgr.GSCALE)
1207 debug_prints(
1208 func="FSLG",
1209 text="Insulator object cell count", var=len(icList)
1212 # 2) locate candidate sites around charge
1213 cSites = getCandidateSites(cgrid, icList)
1215 # 3) calc potential at each site (eqn. 10)
1216 cSites = initialPointCharges(cgrid, cSites, eChargeList)
1218 ts = 1
1219 while ts <= TSTEPS:
1220 # 1) select new growth site (eqn. 12)
1221 # get probabilities at candidate sites
1222 gProbs = getGrowthProbability(winmgr.BIGVAR, cSites)
1223 # choose new growth site based on probabilities
1224 gSitei = weightedRandomChoice(gProbs)
1225 gsite = cSites[gSitei][0]
1227 # 2) add new point charge at growth site
1228 # add new growth cell to grid
1229 cgrid.append(gsite)
1230 # remove new growth cell from candidate sites
1231 cSites.remove(cSites[gSitei])
1233 # 3) update potential at candidate sites (eqn. 11)
1234 cSites = updatePointCharges(gsite, cSites, eChargeList)
1236 # 4) add new candidates surrounding growth site
1237 # get candidate 'stencil'
1238 ncSitesT = getCandidateSites([gsite], icList)
1239 # remove candidates already in candidate list or charge grid
1240 ncSites = []
1241 cSplit = splitList(cSites, 0)
1242 for cn in ncSitesT:
1243 if cn not in cSplit and cn not in cgrid:
1244 ncSites.append((cn, 0))
1246 # 5) calc potential at new candidate sites (eqn. 10)
1247 ncSplit = splitList(ncSites, 0)
1248 ncSites = initialPointCharges(cgrid, ncSplit, eChargeList)
1250 # add new candidate sites to candidate list
1251 for ncs in ncSites:
1252 cSites.append(ncs)
1254 # iteration complete
1255 istr1 = ':::T-STEP: ' + str(ts) + '/' + str(TSTEPS)
1256 istr12 = ' | GROUNDZ: ' + str(winmgr.GROUNDZ) + ' | '
1257 istr2 = 'CANDS: ' + str(len(cSites)) + ' | '
1258 istr3 = 'GSITE: ' + str(gsite)
1259 debug_prints(
1260 func="FSLG",
1261 text="Iteration complete",
1262 var=istr1 + istr12 + istr2 + istr3
1264 ts += 1
1266 # early termination for ground/cloud strike
1267 if winmgr.GROUNDBOOL:
1268 if gsite[2] == winmgr.GROUNDZ:
1269 ts = TSTEPS + 1
1270 debug_prints(
1271 func="FSLG",
1272 text="Early termination due to groundstrike"
1274 continue
1276 if winmgr.CLOUDBOOL:
1277 if gsite in splitListCo(eChargeList):
1278 ts = TSTEPS + 1
1279 debug_prints(
1280 func="FSLG",
1281 text="Early termination due to cloudstrike"
1283 continue
1285 tc2 = time.clock()
1286 tcRUN = tc2 - tc1
1287 debug_prints(
1288 func="FSLG",
1289 text="Laplacian growth loop completed",
1290 var=str(len(cgrid)) + " / " + str(tcRUN)[0:5] + " Seconds"
1292 debug_prints(func="FSLG", text="Visualizing data")
1294 reportSTRING = getReportString(tcRUN)
1296 # Visualize array
1297 visualizeArray(
1298 cgrid, obORIGIN, winmgr.GSCALE,
1299 winmgr.VMMESH, winmgr.VSMESH,
1300 winmgr.VCUBE, winmgr.VVOX, reportSTRING
1303 debug_prints(func="FSLG", text="COMPLETE")
1306 # GUI #
1308 class runFSLGLoopOperator(Operator):
1309 bl_idname = "object.runfslg_operator"
1310 bl_label = "run FSLG Loop Operator"
1311 bl_description = "By The Mighty Hammer Of Thor!!!"
1313 def execute(self, context):
1314 # tuple - state, report text
1315 is_conditions, message = checkSettings()
1317 if is_conditions:
1318 FSLG()
1319 else:
1320 self.report({'WARNING'}, message + " Operation Cancelled")
1322 return {'CANCELLED'}
1324 return {'FINISHED'}
1327 class setupObjectsOperator(Operator):
1328 bl_idname = "object.setup_objects_operator"
1329 bl_label = "Setup Objects Operator"
1330 bl_description = "Create origin/ground/cloud/insulator objects"
1332 def execute(self, context):
1333 setupObjects()
1335 return {'FINISHED'}
1338 class OBJECT_PT_fslg(Panel):
1339 bl_label = "Laplacian Lightning"
1340 bl_space_type = "VIEW_3D"
1341 bl_region_type = "TOOLS"
1342 bl_context = "objectmode"
1343 bl_category = "Create"
1344 bl_options = {'DEFAULT_CLOSED'}
1346 def draw(self, context):
1347 layout = self.layout
1348 winmgr = context.scene.advanced_objects1
1350 col = layout.column(align=True)
1351 col.prop(winmgr, "TSTEPS")
1352 col.prop(winmgr, "GSCALE")
1353 col.prop(winmgr, "BIGVAR")
1355 col = layout.column()
1356 col.operator("object.setup_objects_operator", text="Create Setup objects")
1357 col.label("Origin object")
1358 col.prop_search(winmgr, "OOB", context.scene, "objects")
1360 box = layout.box()
1361 col = box.column()
1362 col.prop(winmgr, "GROUNDBOOL")
1363 if winmgr.GROUNDBOOL:
1364 col.prop_search(winmgr, "GOB", context.scene, "objects")
1365 col.prop(winmgr, "GROUNDC")
1367 box = layout.box()
1368 col = box.column()
1369 col.prop(winmgr, "CLOUDBOOL")
1370 if winmgr.CLOUDBOOL:
1371 col.prop_search(winmgr, "COB", context.scene, "objects")
1372 col.prop(winmgr, "CLOUDC")
1374 box = layout.box()
1375 col = box.column()
1376 col.prop(winmgr, "IBOOL")
1377 if winmgr.IBOOL:
1378 col.prop_search(winmgr, "IOB", context.scene, "objects")
1380 col = layout.column()
1381 col.operator("object.runfslg_operator",
1382 text="Generate Lightning", icon="RNDCURVE")
1384 row = layout.row(align=True)
1385 row.prop(winmgr, "VMMESH", toggle=True)
1386 row.prop(winmgr, "VSMESH", toggle=True)
1387 row.prop(winmgr, "VCUBE", toggle=True)
1390 def getReportString(rtime):
1391 winmgr = bpy.context.scene.advanced_objects1
1392 rSTRING1 = 't:' + str(winmgr.TSTEPS) + ',sc:' + str(winmgr.GSCALE)[0:4] + ',uv:' + str(winmgr.BIGVAR)[0:4] + ','
1393 rSTRING2 = 'ori:' + str(winmgr. ORIGIN[0]) + '/' + str(winmgr. ORIGIN[1]) + '/' + str(winmgr. ORIGIN[2]) + ','
1394 rSTRING3 = 'gz:' + str(winmgr.GROUNDZ) + ',gc:' + str(winmgr.GROUNDC) + ',rtime:' + str(int(rtime))
1395 return rSTRING1 + rSTRING2 + rSTRING3
1398 def addReportProp(ob, str):
1399 bpy.types.Object.FSLG_REPORT = bpy.props.StringProperty(
1400 name='fslg_report', default='')
1401 ob.FSLG_REPORT = str
1404 def register():
1405 bpy.utils.register_class(runFSLGLoopOperator)
1406 bpy.utils.register_class(setupObjectsOperator)
1407 bpy.utils.register_class(OBJECT_PT_fslg)
1410 def unregister():
1411 bpy.utils.unregister_class(runFSLGLoopOperator)
1412 bpy.utils.unregister_class(setupObjectsOperator)
1413 bpy.utils.unregister_class(OBJECT_PT_fslg)
1416 if __name__ == "__main__":
1417 register()
1418 pass
1421 # Benchmarks Function
1423 def BENCH():
1424 debug_prints(func="BENCH", text="BEGIN BENCHMARK")
1425 bt0 = time.clock()
1426 # make a big list
1427 tsize = 25
1428 tlist = []
1429 for x in range(tsize):
1430 for y in range(tsize):
1431 for z in range(tsize):
1432 tlist.append((x, y, z))
1433 tlist.append((x, y, z))
1435 # function to test
1436 bt1 = time.clock()
1437 bt2 = time.clock()
1438 btRUNb = bt2 - bt1
1439 btRUNa = bt1 - bt0
1441 debug_prints(func="BENCH", text="SETUP TIME", var=btRUNa)
1442 debug_prints(func="BENCH", text="BENCHMARK TIME", var=btRUNb)
1443 debug_print_vars(
1444 "\n[BENCH]\n",
1445 "GRIDSIZE: ", tsize, ' - ', tsize * tsize * tsize