Feb 182011
 

Blender 2.56 Python Script for an animation system 2D gravity/accretion simulation. I was intended to be sort of an artistic tool but the physics are correct. I still have some UI work to do but it’s on the back burner for right now. Anyway it works if you feel like playing with it.

create meshes from motion paths

optional explosion if impact velocity over threshold

optional explosion if impact velocity over threshold

Features:
-2d gravity/accretion of ‘glob’ objects
-adjustable (G)ravitational constant (not in UI)
1 blender unit = a(AU)
1 mass unit = m(Solar Mass)
24frames = t(sidereal year)
G = sqrt((2*pi/t)*(sqrt(a**2/m)))

Instructions:
-open Blender2.56b
-copy or load the script into the text editor and run script
-ui will be in physics panel
-adjust parameters
-create globs and/or sun/holes and/or add/adjust vector tails
-animate globs

##################################################
#
#	Simple Orbital Mechanics - v.0.0.01a - 01.30.11
#	GPL, Public Licence, Open-Source, Free, etc...
#	2D Gravity/Accretion Simuatior
#
#		Thanks to Thanassis Tsiodras for his
#"Naive simulator of gravity, in less than 200 Python lines"
#		http://users.softlab.ntua.gr/~ttsiod/gravity.html
#		it got me on the right track
#
#	>Physics Property Menu >Simple Orbital Mechanics
#	-adjust parameters
#	>createGobs, can create multiple times
#		and move if desired
#	>createSuns, or name other objects with prefix 'sun'
#		and move if desired
#
##############################################################
#
#		TODO/WISHLIST:
#		- other objects
#				arbitrary objects
#			- impliment negative mass? repulsive matter?
#					other implimentations of gravity?
#					pushes at a distance,
#			- variable gravity?
#	  !	- make 2dplane arbitrary somehow
#					maybe give each object a xyz reconfig
#					so you can reassing so you can have
#					various systems interacting on diff
#					plane 1st would just
#					be to reassign axis on keyframe
#					-make sure Z loc is not locked in any way
#					so i can play with stacking them
#					vertically, setup a sim, then
#					start camera top and dolly side to reveal dist...
#		- materials (PlanetTex patch?)
#		- sim analysis
#			run thru varions configs
#			keep those with best 'stability'
#				to make visually interesting sims
#				ie. most mass stays inside bounds
#				or most non merged mass in bounds
#				and most movement in bounds?
#		- other movement formulas?
#				maybe impliment fractal flames somehow though
#				that's probably another project
#		- drive other params such as colors with loc/vel/acc
#		- option for dynamic rotation?
#		-	color picker
#				vectors or other settings as color picker wheel?
#				lock to a black and use as vector?
#		-smaller regular partical emitter at ever merge
#		-also, maybe make all objects, attractors?#
#
#		KNOWN ISSUES
#		- accumulation of data - .unlink,
#				need to figure something else out
#				!!! have flush but crashes blender...
#		- cannot rotate vectortails directly in
#				3d view, only in 'n' panel. scale works though...
#		- CONSTRAINTS effecting start frame position
#				running sim should not change start frame
#				THE frame 1 JUMP IS ACTUALLY BECAUSE OF CONSTRANT/SOMETHING
#  			 or updating inital velocity
#		- clear animation data -FIX-
#		- sun' still getting location keyframed
#		- metballs don't really appear...
####################################################

###???ACRETION MERGERGLOB OFFSET ISN'T RIGHT. STILL
#		ALLOWS OFFSET WAY BEYOND RADIUS

###!!!3daxis interchange - just put a cust value for XYZ -> ZYX mappting
#			then adjust just before set location

###???clear animation should also clear paths?

###!!!add gloobject count to panel

###???not making very good use of the index idea...
#		probably get to that when I refactor for arbitary xyz
#		global scn, GI
#		GIsuns, GIglobs = [], []
#		GI = [GIsuns, GIglobs]
#		GI(index) will help with all kinds of stuff..
# 		also work in more build in fxns like vector ang - xy converions for tails and whatnot

###!!!also make time of partlife relative to framerun

###!!!ADD PROPULSION???? somehow add thrust/nav controls?
#		to try out different nav techniques

###???metaballs, explode into other metaballs and add into GI
#		maybe newly spawned objects are only effected by parent?
#		new custom value = generation... (1st, 2nd, 3rd, etc)
#		OR split metaELEMTS!
#		eventuallty make so metaball impacts create
#		new objects and those are then calced in gravity...

###!!!also - next progream...
#		dedicated script to gravitize a selection of any
#		objects based on bounding box...
#		... try to make this one reletivistic
#		incding lorenz... try to help visualize
# 		reletivity on pc
#		time dilation???
#		reletivistic mass? all velocities based on % of maximum (essentially c)
#		then incorporate E=mc2	(somehow so harder to accelerate the faster
# 		it's going...
#		faster to use GI index on animate
#		especuially if use keep 'merged' in index...

###!!!LATER... metaball element trails...

###???option to explode at clip? <-------------- ###!!!on create mesh path verify there is anim data			 ###!!!lockxy rotation on tails ###???figure out subdivision... #		context thing in panel? ###!!! add event to starting velocity #		make vecto suns toggle withevent... so you can change bac to random ###!!! maybe instead of merging with constraints, just  #		seup so each loop if its merged it sets keyframe to its parents #		this could make axis-remapping easier in cases of merging #		 #################################################### ### NEXT NEXT NEXT NEXT NEXT NEXT NEXT NEXT NEXT  #################################################### ###!!!----------->figure out dist to sun relative to earth size...
###!!!----------->radial glob field create option for kieper/oort  35-55au
###!!!Voyager...speed 3.5 au/yr - exit trajectory from sol	

#then ui#
#then refactor for release 00000.2 whaever
#no core refactor, just loose ends, cleanup...
#then post!!!
#then sleep?

#need some kind of collision damping

##############################################################
#-maybe some issues with significant digits at some point
#		i think python floats are 28 digits?

#-something about mass from radius...

"""sun = 1M = 1.98892e30 kg
mercury
3.3010e23/1.98892e30
venus
4.1380e24/1.98892e30
mars
6.4273e23/1.98892e30
jupiter
1.89852e27/1.98892e30
saturn
5.6846e26/1.98892e30
uranus
8.6819e25/1.98892e30
neptune
1.02431e26/1.98892e30"""

#figure out what a frame = in this setup

### got a lot of stuff working
#	but... accretion offset is still wonky...
#	and... tmass fcurve is CURVED
#	need to set curves to CONSTANT for merged, tmass, etc...

	###!!! its adding constrainy to emptys?

######################### DEFAULTS/CONFIG/SETTINGS #########################
import bpy
import sys
import random
import time
from math import pi, sqrt, sin, cos, asin, acos

"""
#derived from one mercury orbit-per-sec
1.2252211349000193/framerate = AU/frame

1 simsecond = 1 mercuryyear = 0.240 earthyear
365.25*24*60*60 = 31557600 sec/yr

0.240*31557600 = 7573824.07573824.0
1 simsecond = 7573824.07573824 realseconds

--->1 simframe = 0.240*31557600/framerate realseconds

365*24*60*60 = 31536000 sec/yr (earth)
c = 63188.25au/yr
c = 63188.25/31536000 au/sec
c = 0.0020036862633181127 au/sec
G = 6.67300e-11 m**3/kg**-1/s**2
F=G(m1*m2/r**2)
		"""

	### I THINK I HAVE SCALE/SIZE RIGHT
	###NOW NEED SOMETHING FOR KG AND SECONDS

#figure out G for this rigwith m/s/kg from mybases
scn = bpy.context.scene
#-dimensions
FRAMERUN = 300			#ui length of simulation
#AU = 1.0					#ui 1BlenderUnit = 1AU

WIDTH = 150				#ui Blender units (should I add a scale option?)
HEIGHT = 150
CLIP = 1000			#ui stop/ignore objects too far out
STARTFRAME = 1			#ui start frame of simulation

#-globs/sunholes
PARTICLES = 5			#ui number globs to add

#FRAMERATE = scn.RenderSettings.fps
FRAMERATE = 24
TIMESCALE = 1.0		#based on framerate, au, and cno idea... 1y?
#--->1 simframe = 0.240*31557600/framerate realseconds

#EdMASS = 1.0			#ui??? Earth = 1unit: to be used for mass scaling and such
#SOLMASS = 5.9736e24

A = 1.0		#astronomical unit = 149.60e6 km = 149.60e9 m
M = 1.0		#solar mass = 1.98892e30 kg
T = 365.256363	#sidereal year
#D = 1.0		#mean solar day = 86400.0025 SI seconds @ epoch J1900
#gaussian constant = 0.01720209895 A**2/3 D*-1 M*-1/2
G = sqrt((2*pi/T)*(sqrt(A**2/M)))

#GM = GRAVITY * S		#(GM product)
#GRAVITY = G				#ui effect of gravity 

#SOLDIAM = 1.0   ###!!!should be sol diam in AU?
#SOLDIAM = 8.517493549379003e-05	#Earth's diameter constant
							#		1AU = 149598000 km
							#		EarthDiameter = 12742.0 km
							#		EarthDiameter = 12742/149598000 AU

							#
DSIZE = 250				#		Solar unit draw size of glob mesh
							#		no effect on physics

AXMAP = ['X','Y','Z']#default axismapping

SIZEVAR = 1.0		  	#ui variation in glob size in feild generation

#SsUNRADIUS = 109.0		#ui default sun radius (same as Sol)
SUNRANDLOC = True		#ui default setting for random sun loc
#SUNsDSIZE = 1.0			#ui draw size of suns

#-physics
DAMP = 1.0				#ui temp use for vel and acc (1.0 = no damp)
MAXVEL = 20.0			#ui max velocity
MAXINTERAD = CLIP		#ui max dist between 2 particles too calc force
DAMPVEL = 1.0			#na damp or add velocity (1.0 = 100%)
DAMPACC = 1.0			#na damp or add acceleration (less makes more spinny animations)
DAMPVELx = DAMPVEL	#na in case someone wants asymetrical damping later
DAMPVELy = DAMPVEL
DAMPACCx = DAMPACC
DAMPACCy = DAMPACC
RANDVEL = True     	#ui generate random starting velocity

RANDVELRANGE = .125	#ui random range for staring velocity
VECMULT = 10000		#ui multiplier for vector tails should be just display

LOOPATCLIP = False	#ui option to loop at clip dist

ACCRETE = True			#ui
EXPLODE = True			#ui
EXTHRESH = .25			#ui total impact velocity required to explode
METABALLS = False

#-internal
GPREFIX = 'glob'	 	#fn prefix for globjects
MOBNAME = 'mglob'	 	#fn name of globject meshdata
SPREFIX = 'sun'		#fn name of sun objects

###!!! replace the prefix system with a 'glob' variable

print('----------GEAUX!!!----------')

################### playground ######################

#-dist, size, mass,  based on Earth = 1
def createSolSystem():
	###!!!OortCloud		2000-50,000 au
	###!!!ProximaCentauri	268000	au
	###!!!add Europa, Phobos, Demos, lots o' moons to add
	###!!!saturns rings?
	NOZERO = 0.00000000001
	print('make a mass-distance-speed / scale accurate copy of sol')
	solG2V = makeMeshCube('sun-SOLG2V', scn.DSIZE)
	solG2V.name = 'sun-SOLG2V'
	solG2V.location = [0.0, 0.0, 0.0]
	solG2V.scale = [scn.SOLDIAM*109, scn.SOLDIAM*109, scn.SOLDIAM*109]
	solG2V.tmass = scn.M
	solG2V.modifiers.new('sunbevel', 'BEVEL')
	solG2V.show_name = True
	scn.objects.link(solG2V)

	mercury = createMetaBall('glob-MERCURY', scn.DSIZE)
	mercury.name = 'glob-MERCURY'
	mercury.location = [-0.39, NOZERO, 0.0]
	mercury.scale = [scn.SOLDIAM*0.3829, scn.SOLDIAM*0.3829, scn.SOLDIAM*0.3829]
	mercury.tmass = scn.M*0.055
	mercury.vel = [0.0, -0.24, 0.0]
	mercury.show_name = True
	mercury.merged = False
	scn.objects.link(mercury)

	venus = createMetaBall('glob-VENUS', scn.DSIZE)
	venus.name = 'glob-VENUS'
	venus.location = [NOZERO, 0.72, 0.0]
	venus.scale = [scn.SOLDIAM*0.949, scn.SOLDIAM*0.949, scn.SOLDIAM*0.949]
	venus.tmass = scn.M*0.82
	venus.vel = [-0.62, 0.0, 0.0]
	venus.show_name = True
	venus.merged = False
	scn.objects.link(venus)

	earth = createMetaBall('glob-EARTH', scn.DSIZE)
	earth.name = 'glob-EARTH'
	earth.location = [1.0, NOZERO, 0.0]
	earth.scale = [scn.SOLDIAM, scn.SOLDIAM, scn.SOLDIAM]
	earth.tmass = scn.M
	earth.vel = [0.0, 1.0, 0.0]###??? EARTH BASED VELOCITY? SURE WHY NOT
	earth.show_name = True
	earth.merged =False
	scn.objects.link(earth)		###??? wrong though, it's orbital period, not velocity...

###
	luna = createMetaBall('glob-LUNA', scn.DSIZE)
	luna.name = 'glob-LUNA'
	luna.location = [1.026, NOZERO, 0.0]
	luna.scale = [scn.SOLDIAM*0.273, scn.SOLDIAM*0.273, scn.SOLDIAM*0.273]
	luna.tmass = scn.M*0.012
	luna.vel = [0.0, 1.0, 0.0]
	luna.show_name = True
	luna.merged = False
	scn.objects.link(luna)

	mars = createMetaBall('glob-MARS', scn.DSIZE)
	mars.name = 'glob-MARS'
	mars.location = [NOZERO, -1.52, 0.0]
	mars.scale = [scn.SOLDIAM*0.532, scn.SOLDIAM*0.532, scn.SOLDIAM*0.532]
	mars.tmass = scn.M*0.11
	mars.vel = [1.88, 0.0, 0.0]
	mars.show_name = True
	mars.merged = False
	scn.objects.link(mars)		

	jupiter = createMetaBall('glob-JUPITER', scn.DSIZE)
	jupiter.name = 'glob-JUPITER'
	jupiter.location = [-5.204267, NOZERO, 0.0]
	jupiter.scale = [scn.SOLDIAM*11.209, scn.SOLDIAM*11.209, scn.SOLDIAM*11.209]
	jupiter.tmass = scn.M*317.8
	jupiter.vel = [0.0, -11.86, 0.0]
	jupiter.show_name = True
	jupiter.merged = False
	scn.objects.link(jupiter)

	saturn = createMetaBall('glob-SATURN', scn.DSIZE)
	saturn.name = 'glob-SATURN'
	saturn.location = [NOZERO, 9.582, 0.0]
	saturn.scale = [scn.SOLDIAM*9.449, scn.SOLDIAM*9.449, scn.SOLDIAM*9.449]
	saturn.tmass = scn.M*95.2
	saturn.vel = [-29.46, 0.0, 0.0]
	saturn.show_name = True
	saturn.merged = False
	scn.objects.link(saturn)		

	uranus = createMetaBall('glob-URANUS', scn.DSIZE)
	uranus.name = 'glob-URANUS'
	uranus.location = [19.22, NOZERO, 0.0]
	uranus.scale = [scn.SOLDIAM*4.007, scn.SOLDIAM*4.007, scn.SOLDIAM*4.007]
	uranus.tmass = scn.M*14.6
	uranus.vel = [0.0, 84.01, 0.0]
	uranus.show_name = True
	uranus.merged = False
	scn.objects.link(uranus)		

	neptune = createMetaBall('glob-NEPTUNE', scn.DSIZE)
	neptune.name = 'glob-NEPTUNE'
	neptune.location = [NOZERO, -30.06, 0.0]
	neptune.scale = [scn.SOLDIAM*3.883, scn.SOLDIAM*3.883, scn.SOLDIAM*3.883]
	neptune.tmass = scn.M*17.2
	neptune.vel = [164.8, 0.0, 0.0]
	neptune.show_name = True
	neptune.merged = False
	scn.objects.link(neptune)		

	pluto = createMetaBall('glob-PLUTO', scn.DSIZE)
	pluto.name = 'glob-PLUTO'
	pluto.location = [-29.74, NOZERO, 0.0]
	pluto.scale = [scn.SOLDIAM*0.19, scn.SOLDIAM*0.19, scn.SOLDIAM*0.19]
	pluto.tmass = scn.M*0.002
	pluto.vel = [0.0, -248.09, 0.0]
	pluto.merged =False
	pluto.show_name = True
	scn.objects.link(pluto)

	sedna = createMetaBall('glob-SEDNA', scn.DSIZE)
	sedna.name = 'glob-SEDNA'
	sedna.location = [NOZERO, 960.0, 0.0]
	sedna.scale = [scn.SOLDIAM*0.2197, scn.SOLDIAM*0.2197, scn.SOLDIAM*0.2197]
	sedna.tmass = scn.M*0.0004185081023168609
	sedna.vel = [-118090.0, 0.0, 0.0]
	sedna.show_name = True
	sedna.merged = False
	scn.objects.link(sedna)

	voyager = makeMeshCube('glob-VOYAGER', scn.DSIZE)
	voyager.name = 'glob-VOYAGER1'
	voyager.location = [-115.0, NOZERO, 0.0]
	voyager.scale = [scn.SOLDIAM*1.5696123057604772e-06, 
		scn.SOLDIAM*1.5696123057604772e-06, scn.SOLDIAM*1.5696123057604772e-06]
	voyager.tmass = scn.M*1.2086513994910943e-22
	voyager.vel = [0.0, -10.0, 0.0]
	scn.objects.link(voyager)
	voyager.show_name = True
	voyager.merged = False	

def makeGlobField():
	for i in range(scn.PARTICLES):
		gx = random.uniform(-scn.WIDTH/2, scn.WIDTH/2)
		gy = random.uniform(-scn.HEIGHT/2, scn.HEIGHT/2)
		gz = 0.0												  #2D sim for now

		#gs = random.uniform(scn.SOLDIAM-(scn.SOLDIAM*(scn.SIZEVAR/2)), 
		#	scn.SOLDIAM+(scn.SOLDIAM*(scn.SIZEVAR/2)))
		#sun diameter = 1391000 kilometers
		#au = 149598000 kilometers
		#sun diameter = 1391000/149598000 AU
		#sun diameter = 0.009298252650436503 AU
		gs = 0.009298252650436503  #-make variable? this makes default sun size our sun

		if METABALLS:
			newob = createMetaBall('metaglobule', scn.DSIZE)
			globname = GPREFIX + 'meta-' + str(i+1000)[1:4]

		else:
			newob = makeMeshCube(MOBNAME, scn.DSIZE)
			globname = GPREFIX + '-' + str(i+1000)[1:4]

		newob.name = globname
		newob.location = [gx, gy, gz]
		newob.scale = [gs, gs, gs]		  

			#add custom properties
		newob.merged = False
		newob.bhole = False
		#newob.tmass = scn.M *random.uniform(1, SIZEVAR)
		newob.tmass = scn.M # should be a random mass var
		newob.vel = [0.0, 0.0, 0.0]
		#newob.density = scn.STDENSITY
		if scn.RANDVEL:
			gvx = random.uniform(-RANDVELRANGE/2, RANDVELRANGE/2)
			gvy = random.uniform(-RANDVELRANGE/2, RANDVELRANGE/2)
			gvz = 0.0
			newob.vel = [gvx, gvy, gvz]

		scn.objects.link(newob)

def createMetaBall(mname, msize):
	msize = msize/2 #-radius from diameter
	#-have to go into edit mode to refesh metaobj for some reason
	mball = bpy.data.metaballs.new(mname)
	for m in range(1):
		mel = mball.elements.new()
		#mel.type = 'BALL'
		mel.co = [0.0, 0.0, 0.0]
		mel.size_x = msize
		mel.size_y = msize
		mel.size_z = msize
		#mel.stiffness = 1.0
		#mel.use_negative = False		#could be fun
	oball = bpy.data.objects.new('metaglobule', mball)
	oball.data = mball
	oball.glob = True
	return(oball)

def makeMeshCube(mname, msize):
	msize = msize/2 #-radius from diameter
	mmesh = bpy.data.meshes.new(mname)
	mmesh.vertices.add(8)
	mmesh.vertices[0].co = [-msize, -msize, -msize]
	mmesh.vertices[1].co = [-msize,  msize, -msize]
	mmesh.vertices[2].co = [ msize,  msize, -msize]
	mmesh.vertices[3].co = [ msize, -msize, -msize]
	mmesh.vertices[4].co = [-msize, -msize,  msize]
	mmesh.vertices[5].co = [-msize,  msize,  msize]
	mmesh.vertices[6].co = [ msize,  msize,  msize]
	mmesh.vertices[7].co = [ msize, -msize,  msize]

	mmesh.faces.add(6)
	mmesh.faces[0].vertices_raw = [0,1,2,3]
	mmesh.faces[1].vertices_raw = [0,4,5,1]
	mmesh.faces[2].vertices_raw = [2,1,5,6]
	mmesh.faces[3].vertices_raw = [3,2,6,7]
	mmesh.faces[4].vertices_raw = [0,3,7,4]
	mmesh.faces[5].vertices_raw = [5,4,7,6]
	mmesh.update(calc_edges=True)

	omesh = bpy.data.objects.new(mname, mmesh)
	omesh.data = mmesh
	omesh.glob = True
	return(omesh)

def makeSunHole():
	holemass = 10
	ll = []
	for m in scn.objects:
		if m.name[0:len(SPREFIX)] == SPREFIX:
			ll.append(m.name)
	gx = random.uniform(-scn.WIDTH/2, scn.WIDTH/2)
	gy = random.uniform(-scn.HEIGHT/2, scn.HEIGHT/2)
	gz = 0
	gs = 0.009298252650436503 #-sol diam in AU
	newsun = makeMeshCube('sunmesh', scn.DSIZE)
	newsun.name = SPREFIX + '-' +str(len(ll)+1000)[1:4]
	if scn.SUNRANDLOC:
		newsun.location = [gx, gy, gz]
	newsun.scale = [gs, gs, gs]
	newsun.modifiers.new('sunbevel', 'BEVEL')
	newsun.merged = False
	newsun.bhole = True
	newsun.tmass = scn.M * holemass
	scn.objects.link(newsun)

def clearAnimation():
	for o in scn.objects:
		for f in range(scn.FRAMERUN):
			scn.frame_current = f+1
			if o.name[0:len(GPREFIX)] == GPREFIX 
			or o.name[0:len(SPREFIX)] == SPREFIX:
				o.keyframe_delete('location')
				o.keyframe_delete('vel')
				o.keyframe_delete('tmass')
				o.keyframe_delete('merged')
		for c in o.constraints:
			c.keyframe_delete('influence')
			o.constraints.remove(c)

		# QQQ remove constraints influence frames too?

def createMeshPath():
	###!!! set this up to use ob.glob but not ob.bhole
	#-this is UGLY, need to find how to get fcurve data easier
	for ob in scn.objects:
		if ob.name[0:len(GPREFIX)] == GPREFIX: # 
		#and len(GPREFIX)+4 == len(ob.name):	#-hope avoid making path for path
			locX, locY, locZ, frame = [], [], [], []
			for f in range(scn.STARTFRAME, scn.FRAMERUN+1):
				oanim = ob.animation_data
				ocurves = oanim.action.fcurves
				addframe = 0
				stopvert = 5
				#-some hacky bullshit to make merged blobs paths right
				for ocurve in ocurves:
					#-only add to array if object hasn's been merged. !!! BROKE!
					if ocurve.data_path == 'merged' and ocurve.evaluate(f) == False:
						addframe = 1
						stopvert = 0
				for ocurve in ocurves:
					if addframe == 1:
						if ocurve.data_path == 'location' 
						and ocurve.array_index == 0:
							locX.append(ocurve.evaluate(f))
						if ocurve.data_path == 'location' 
						and ocurve.array_index == 1:
							locY.append(ocurve.evaluate(f))

			#-dont create motion paths under a % of FRAMERUM
			PATHTHRESH = 20 #%<-----make a ui variable (or setup file)
			if len(locX)/scn.FRAMERUN < PATHTHRESH/scn.FRAMERUN: 				print(len(locX)/scn.FRAMERUN, PATHTHRESH/scn.FRAMERUN) 				continue											 			mpath = bpy.data.meshes.new('meshpath') 			mpath.use_auto_smooth = True 			for p in range(len(locX)-1): 				if p == 0 or p == len(locX)-1: 					mpath.vertices.add(1) 					mpath.vertices[len(mpath.vertices)-1].co = [locX[p], locY[p], 0.0] 				if p > 0 and p < len(locX)-1-stopvert:
					x2, y2 = locX[p-1], locY[p-1]
					x1, y1 = locX[p+1], locY[p+1]
					xM, yM = locX[p], locY[p]
					dix, diy = x2 - x1, y2 - y1
					hyp = sqrt(dix**2 + diy**2)
					if hyp == 0: hyp = 0.000000000000000000001
					ang = acos(dix/hyp)
					perpL = ang + pi/4
					perpR = ang - pi/4
					###???maybe rotate the angle dynamically?

					#-multiplying 1/hyp here makes width
					#		inv proportional to velocity
					#		because dist bt verts grow with vel, so does hyp
					###???WORKS SORT OF, it's SOMETHING WITH THAT /0 hyp thing?
					#velT = (1/ hyp)*2 #<====-----make a ui variable?
					velT = .5
					nx1 = cos(perpL)*velT
					ny1 = sin(perpL)*velT
					nx2 = cos(perpR)*velT
					ny2 = sin(perpR)*velT
					nxL = xM + nx1
					nyL = yM + ny1
					nxR = xM + nx2
					nyR = yM + ny2
					ZTIMESCALE = 50	#percent of framrun
					ztime = p*(ZTIMESCALE/100)	#<====-----make a ui variable
										#				and/or proportonal to wid/height

					mpath.vertices.add(1)
					mpath.vertices[len(mpath.vertices)-1].co = [ 
							nxL, nyL, ztime]
					#-centerline, same as loc...
					#mpath.vertices.add(1)
					#mpath.vertices[len(mpath.vertices)-1].co = [ 
					#		xM, yM, ztime]
					mpath.vertices.add(1)
					mpath.vertices[len(mpath.vertices)-1].co = [ 
							nxR, nyR, ztime]

			mpath.update()
			#-DO NOT face last 4 verts or b will crash (last -1 one is for index
			for fc in range(1, len(mpath.vertices)-5, 4):
				mpath.faces.add(1)
				mpath.faces[len(mpath.faces)-1].vertices_raw = [ 
					fc, fc+2, fc+3, fc+1]
				mpath.faces.add(1)
				mpath.faces[len(mpath.faces)-1].vertices_raw = [ 
					fc+3, fc+2, fc+4, fc+5]
				mpath.update(calc_edges=True)									

			mpath.update(calc_edges=True)
			opath = bpy.data.objects.new('path'+ob.name, mpath)
			scn.objects.link(opath)

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

def addExplosivo(obQ, vx, vy):
	if len(obQ.data.faces) < 512:
		#-have to subdivide cube first
		scn.objects.active = obQ
		cuts = len(obQ.data.faces) // 512
		###???don't lnow how to do this with ops like this
		#bpy.ops.mesh.subdivide(number_cuts = cuts, fractal = 0.25)

	###??? maybe use fluids? or randomize settings?
	obQ.modifiers.new(name = 'explosemit', type = 'PARTICLE_SYSTEM')
	expart = obQ.particle_systems[0]
	#expart.settings.count = 512 # 8x8x8 cube grid
	expart.settings.count = 6 # for now
	expart.settings.rotation_factor_random = 2.5
	expart.settings.frame_start = scn.frame_current
	expart.settings.frame_end = scn.frame_current + 1
	expart.settings.lifetime = 120
	expart.settings.lifetime_random = 1
	expart.settings.use_dynamic_rotation = True
	expart.settings.render_type = 'NONE'
	expart.settings.draw_method = 'NONE'
	expart.settings.rotation_factor_random = 1.0
	expart.settings.factor_random = 10.0

	expart.settings.object_align_factor	= [vx*vx*vx, vy*vy*vy, 0]
	#expart.settings.object_factor = 2
	expart.settings.effector_weights.gravity = 0.0
	expart.settings.brownian_factor = .25
	#expart.settings.particle_size = 1
	#expart.settings.use_multiply_size_mass = True
	#expart.settings.use_size_deflect = True
	#expart.settings.size_random = 2.5
	###??? child particles?
	exmod = obQ.modifiers.new(name = 'explosivo', type = 'EXPLODE')
	exmod.show_dead = False
	exmod.use_edge_split = True
	#!!!bpy.ops.ptcache.bake(bake=True) now
	#		or bpy.ops.ptcache.bake_all() at the end of loop?

#-set all globs vectors tangent to the nearest sun
#		hopefully will create more interesting sims
def setVecTangentToNearestHole():
	#-trying set tan to nearest ob if no sun...
	holeindex = []
	for hole in scn.objects:
		if hole.bhole:
			holeindex.append(hole)
			tempmult=35
	if len(holeindex)<1:
		for gobs in scn.objects:
			if gobs.glob:
				holeindex.append(gobs)
				tempmult=10

	print(holeindex)
	for gob in scn.objects:
		if gob.glob and gob.bhole == False:
			x1, y1 = gob.location[0], gob.location[1]
			nholeinit = True
			for hole in holeindex:
				if hole.name != gob.name:
				#if sol != gob:
					x2, y2 = hole.location[0], hole.location[1]
					dix, diy = x2 - x1, y2 - y1
					disq = dix*dix + diy*diy
					dri = sqrt(disq)
					# find nearest Sun
					if nholeinit:
						nholeinit = False
						nhole = hole
						nholedri = dri
						nholex = dix
						nholey = diy
						continue
					if	dri < nholedri: 						nhole = hole 						nholedri = dri 						nholex = dix 						nholey = diy 			vang, vmag = getVectorAngle(nholex, nholey, 0.0, False) 			#-random if vector is clockwz/anticlock to sun and if  			#		magnitude is proportional/invesely to dist to sun 			coinflip = random.uniform(0,1) 			coinflip = 1	#cheat for testing 			if coinflip >=.333:
				vangNEW = vang + (pi/2)
				vmagNEW = (1/nholedri)*tempmult
				#-should include mass of second object in calc...
			if coinflip < .333: 				vangNEW = vang - (pi/2) 				vmagNEW = vmag * nholedri / VECMULT 			for c in gob.children: 				if c.name[0:7] == 'vectail': 					c.rotation_euler = [0.0, 0.0, vangNEW] 					c.scale = [vmagNEW, vmagNEW, 0] 			hyp = vmagNEW  / scn.VECMULT 			cvx = cos(vangNEW)*hyp 			cvy = sin(vangNEW)*hyp 			gob.vel = [cvx, cvy, 0.0] def updateVecFromTail(globV): 	kids = globV.children 	for c in kids: 		# get vx and vy back from rot, scale 		vecang = c.rotation_euler[2] 		vecmag = c.scale[0] 		hyp = vecmag / scn.VECMULT 		cvx = cos(vecang)*hyp 		cvy = sin(vecang)*hyp 		globV.vel = [cvx, cvy, 0.0]					 				 def getVectorAngle(vx, vy, vz, hyp): 	if hyp == False: hyp = (sqrt((vx*vx)+(vy*vy))) 	# avoid div-0, hacky I know... 	if hyp == 0: hyp = .0000000000001 	if vx >= 0 and vy >= 0:					# -I
		vecang = asin(vy/hyp)
	if vx <= 0 and vy >= 0:					# -II
		vecang = pi-asin(vy/hyp)
	if vx <= 0 and vy <= 0:					# -III	 		vecang = -pi-asin(vy/hyp)		 	if vx >= 0 and vy <= 0:					# -IV 		vecang = asin(vy/hyp)	 	vecmag = (hyp*scn.VECMULT)		#this makes return value only good for tail DISPLAY 	return(vecang, vecmag)						 					 	 def flush(): 	killall = bpy.data.objects 	for deadmesh in killall: 		if deadmesh.glob  		or deadmesh.name[0:7] == "vectail": # 		#or deadmesh.name[0:len(SPREFIX)] == SPREFIX: 			if deadmesh.users == 0: 				bpy.data.objects.remove(deadmesh) 			else: print(deadmesh.name + ' is being stubborn...') 			scn.objects.link(deadmesh) 	for deadob in killall: 		if deadob.glob  		or deadob.name[0:7] == "vectail": # 	#	or deadob.name[0:len(SPREFIX)] == SPREFIX: 			if deadob.users == 0:			 				bpy.data.objects.remove(deadob) 			else: print(deadob.name + ' is being stubborn...') 			scn.objects.link(deadob)			 ######################### PHYSICS FXNS ######################### ######################### GLOB/SUN FXNS ######################### 		 def updateAllVecFromTail(): 	for obs in scn.objects: 		if obs.glob: 			updateVecFromTail(obs) def updateTailFromVec(globV): 	kids = globV.children 	if globV.merged == False and len(kids) > 0:
		vecang, vecmag = getVectorAngle( 
			globV.vel[0], globV.vel[1], globV.vel[2], False)
		for c in kids:
			c.rotation_euler = [0.0, 0.0, vecang]
			c.scale = [vecmag, vecmag, 0.0]
			c.keyframe_insert('rotation_euler', 2)
			c.keyframe_insert('scale', 0)
			c.keyframe_insert('scale', 1)

def makeVectorTail(globV):
	mmesh = bpy.data.meshes.new('vectail')
	mmesh.vertices.add(2)
	mmesh.vertices[0].co = [0.0, 0.0, 0.0]
	mmesh.vertices[1].co = [6.0, 0.0, 0.0]
	mmesh.edges.add(1)
	mmesh.edges[0].vertices = [0,1]
	mmesh.update()
	omesh = bpy.data.objects.new('vectail', mmesh)
	scn.objects.link(omesh)
	omesh.parent = globV
	omesh.data = mmesh
	omesh.hide_render = True

	vecang, vecmag = getVectorAngle( 
		globV.vel[0], globV.vel[1], globV.vel[2], False)

	omesh.rotation_euler = [0.0, 0.0, vecang]
	omesh.scale = [vecmag, vecmag, 0]

def addVectorTails():
	for obs in scn.objects:
		if obs.glob:
			if len(obs.children) > 0:
				for kid in obs.children:
					try: scn.objects.unlink(kid)
					except: print('i dont get unlinking...')
			makeVectorTail(obs)

def removeObject(xob):
	xmesh = xob.data
	scn.objects.unlink(xob)
	try: bpy.data.objects.remove(xmesh)
	except: print('---cannot remove ' + xmesh.name + '---')
	try: bpy.data.objects.remove(ob)
	except: print('---cannot remove ' + xob.name + '---')

def delSuns():
	for xall in scn.objects:
		if xall.bhole:
			removeObject(xall)
			#bpy.context.scene.objects.unlink(xall)
	selectCheck()		

def delGlobs():
	for xall in scn.objects:
		if xall.glob 
		or xall.name[0:7] == 'vectail' 
		or xall.name[0:8] == 'pathglob':
			removeObject(xall)
	selectCheck()					

def selectCheck():
#-make sure there's an object active so panel doesn't disappear
	if scn.objects.active: return
	else:
		if len(scn.objects) > 1:
			scn.objects.active = scn.objects[0]
		else:
			newempty = bpy.ops.object.add()
			newempty.hide = False
			newempty.hide_select = False
			newempty.hide_render = False
			scn.objects.link(newempty)
			scn.objects.active=newempty

"""
def makeGlobIndex():
	GI = []
	obs = scn.objects
	for ob in obs:
		if ob.glob:
		#or ob.name[0:len(SPREFIX)] == SPREFIX:
			GI.append(ob.name)
	return GI
"""

######################### RUNLOOPS #########################

#def runit(GI):
def runit():
	for globA in scn.objects:
		if globA.glob:
			#print(globA.name, ' is a glob')
	#for i in range(0, len(GI)):
		#globA = obs[GI[i]]
		#globA = glob
			if globA.bhole: continue #-don't calc force on blackholes (immovable)
			if globA.merged: continue #-don't calc force on merged

			Ax, Ay = globA.location[0], globA.location[1]

				### ??? ### add option for round cliploop?
				# check for CLIP (border)
			if sqrt(Ax*Ax+Ay*Ay) > scn.CLIP:
				if scn.LOOPATCLIP:
					#-if loopatclip set, loop glob around
					if abs(Ax) > scn.CLIP:
						globA.location[0] = -Ax
					if abs(Ay) > scn.CLIP:
						globA.location[1] = -Ay
				else:
					#-otherwise stop and mark as merged so it'll be ignored
					globA.merged = True
					continue	#-skip to next globA loop if globA outside CLIP

			# temp loc, vel and acc vars for loop
			tloc = globA.location
			tvel = globA.vel
			tacc = [0.0, 0.0, 0.0]

			#for j in range(0, len(GI)):
			for globB in scn.objects:
				if globB.glob:
					if globB == globA or globB.merged: continue
					#globB = obs[GI[j]]
					#globB = glob2
					Bx, By = globB.location[0], globB.location[1]
					dx, dy = Bx - Ax, By - Ay
					dsq = dx*dx + dy*dy
					#if dsq==0:dsq=0.0001 #-I am such a hack...
					dr = sqrt(dsq)
					#ignore own force
					if dr > scn.MAXINTERAD: continue #ignore if beyond interaction radius

					# add acceleration/force from this glob
					#print(globA.name, ' has a problem with ', globB.name)
					force = G * globA.tmass * globB.tmass / dsq
					tacc[0] += force*dx/dr
					tacc[1] += force*dy/dr
					tloc[0] += tvel[0] * scn.DAMP #DAMPVELx
					tloc[1] += tvel[1] * scn.DAMP #DAMPVELy
					tvel[0] += tacc[0] * scn.DAMP #DAMPACCx
					tvel[1] += tacc[1] * scn.DAMP #DAMPACCy
					###!!!maybe add a mock relativity here by
					#		damping acceleration as approaches v approaches c

					#-check if over max velocity, if so set to max
					if tvel[0] > scn.MAXVEL: tvel[0] = scn.MAXVEL
					if tvel[0] < -scn.MAXVEL: tvel[0] = -scn.MAXVEL	 					if tvel[1] > scn.MAXVEL: tvel[1] = scn.MAXVEL
					if tvel[1] < -scn.MAXVEL: tvel[1] = -scn.MAXVEL
					tacc = [0.0, 0.0, 0.0]

					#-merge if close enough (relative to draw size - a cheat - I know)
					if dr <= (globA.scale[0]+globB.scale[0])*scn.DSIZE: 						if scn.ACCRETE: 							#-they're close, merge smaller to larger 							if globA.tmass >= globB.tmass:

								globMerge(globB, globA)
							else:

								globMerge(globA, globB)
					###!!!also add collision damping here based on density
						if scn.EXPLODE:
							vx1, vy1 = globA.vel[0], globA.vel[1]
							vx2, vy2 = globB.vel[0], globB.vel[1]
							vdx, vdy = vx2 = vx1, vy2 - vy1
							vdsq =vdx*vdx + vdy*vdy
							vdr = sqrt(vdsq)
							if abs(vdr) > scn.EXTHRESH:
								if globA.tmass >= globB.tmass:
									if globB.type != 'META':
										addExplosivo(globB, vdx, vdy)
								else:
									if globA.type != 'META':
										addExplosivo(globA, vdx, vdy)

			#update loc, vel, and insert kframes
				globA.location[0] = tloc[0] #/ scn.SPACEsSCALE
				globA.location[1] = tloc[1] #/ scn.SPACESsCALE
				globA.vel = tvel
			#if globA.name[0:len(SPREFIX)] != SPREFIX:
			# ignore vectails of merged globs
			if globA.merged == False:
				globA.keyframe_insert('tmass')
				globA.keyframe_insert('merged')
			if globA.bhole == False:
				globA.keyframe_insert('location', 0)
				globA.keyframe_insert('location', 1)
				globA.keyframe_insert('vel', 0)
				globA.keyframe_insert('vel', 1)
			#-check if the glob has vectails, update if they exist
				for c in globA.children:
					if c.name[0:7] == 'vectail':
						updateTailFromVec(globA)
						continue	# vectail updated, so leave loop

def loopit():
	t1 = time.ctime(time.time())
	#GI = makeGlobIndex()
	scn.frame_current = scn.STARTFRAME
	#-adjust for user changes to vectails
	updateAllVecFromTail()
	setCurvesToConstant()
	#-start on frame 2, 1st should NOT be recalculated
	for f in range(scn.STARTFRAME+1, scn.STARTFRAME+scn.FRAMERUN):
		scn.frame_current = f
		print(scn.frame_current)
		#runit(GI)
		runit()
	t2 = time.ctime(time.time())
		# calc processing time
	s1, m1 = int(t1[17:19]), int(t1[14:16])
	s2, m2 = int(t2[17:19]), int(t2[14:16])
	print(str(t1)[11:20], str(t2)[11:20])
	print('simulation time: ' + str(abs(m2-m1)) + ':' + str(abs(s2-s1)))

###!!!ABSORB SOME IMPACT IN COLLISION!!!
###	THESEARE NOT INDESTRUCTABLE...
###	ALSO MAYBE ADD POTENTIAL FOR 'GLANCING BLOW'S and 'BOUNCES'
###	because for now w acrete and axplode off all collisions will pass thru
def globMerge(globS, globL):
	print(globL.name, globL.tmass, '+', globL.tmass, globS.name)
	#if globS.bhole == 1: return
	globS.merged = True

	#-make merged globs vectail disappear
	for cs in globS.children:
		if cs.name[0:7] == 'vectail':
			cs.scale = [0.0, 0.0, 0.0]
			cs.keyframe_insert('scale', 0)
			cs.keyframe_insert('scale', 1)

	#-figure out vector between L and S to offset
	#		constraint of S on L as if stuck where hit
	#		also to direct particles/explode modifier
	SLx = globS.location[0] - globL.location[0]
	SLy = globS.location[1] - globL.location[1]
	if SLx >= globL.scale[0]*scn.DSIZE: SLx = globL.scale[0]*scn.DSIZE
	if SLy >= globL.scale[0]*scn.DSIZE: SLy = globL.scale[0]*scn.DSIZE
	if SLx < -globL.scale[0]*scn.DSIZE: SLx = -globL.scale[0]*scn.DSIZE
	if SLy < -globL.scale[0]*scn.DSIZE: SLy = -globL.scale[0]*scn.DSIZE
	#SLx = 1			###!!! sun merging is still weird
	#SLy = 1			

	#-contrain smaller glob to larger
	gcon = globS.constraints.new('COPY_LOCATION')
	gcon.use_offset = True
	gcon.target = globL

	#-add constraint influence keyframe of
	#		pre-merge state to previous frame
	ftemp = scn.frame_current
	scn.frame_current = ftemp - 1
	gcon.influence = 0
	gcon.keyframe_insert('influence')

	#-back to this frame to insert merged state keyframe
	scn.frame_current = ftemp
	gcon.influence = 1
	gcon.keyframe_insert('influence')	

	globS.location = [SLx, SLy, 0.0]						  

	#-add keyframes for post-merge
	#		location and constraint influence
	globS.keyframe_insert('location', 0)
	globS.keyframe_insert('location', 1)				  	

	# add velocity ofsmall glob to larger
	### ??? ### CHECK THIS, OBJECT VELS SEEM TO GOOF ON IMPACK
	newvx = (globL.vel[0] * globL.tmass + 
				globS.vel[0] * globS.tmass) / 
				(globL.tmass + globS.tmass)
	newvy = (globL.vel[1] * globL.tmass + 
				globS.vel[1] * globS.tmass) / 
				(globL.tmass + globS.tmass)

	#-update larger globs total mass and keyframe

	globL.tmass += globS.tmass
	globL.keyframe_insert('tmass')
	print('mass updated : ', globL.name, globL.tmass)
	globL.vel[0] = newvx
	globL.vel[1] = newvy

def setCurvesToConstant():
	for ob in scn.objects:
		if ob.glob:
			oanim = ob.animation_data
			if oanim:
				ocurves = oanim.action.fcurves
				for ocurve in ocurves:
					ocurve.extrapolation = 'CONSTANT'

###analysis fxns
def calcTotalMassRemaing():
	#also how many last past 500 frames or so...
	pass

######################### USER INTERFACE #########################

def initSceneProps(scn):
	bpy.types.Scene.FRAMERUN = bpy.props.IntProperty(
		name = "framerun")
	bpy.types.Scene.PARTICLES = bpy.props.IntProperty(
		name = "particles")		  

	bpy.types.Scene.M = bpy.props.FloatProperty(
		name = "earthmass")

	bpy.types.Scene.T = bpy.props.FloatProperty(
			name = "siderealyear")			

	bpy.types.Scene.WIDTH = bpy.props.IntProperty(
		name = "width")
	bpy.types.Scene.HEIGHT = bpy.props.IntProperty(
		name = "height")
	bpy.types.Scene.CLIP = bpy.props.IntProperty(
		name = "clip")

	bpy.types.Scene.DSIZE = bpy.props.FloatProperty(
		name = "drawsize")
	#bpy.types.Scene.SOLDIAM = bpy.props.FloatProperty(
	#	name = "earthdiameter")		  

	bpy.types.Scene.A = bpy.props.FloatProperty(
		name = "spacespace")

	bpy.types.Scene.SIZEVAR = bpy.props.FloatProperty(
		name = "sizevar")
	bpy.types.Scene.DAMP = bpy.props.FloatProperty(
		name = "damp")

	bpy.types.Scene.LOOPATCLIP = bpy.props.BoolProperty(
		name = "loopatclip")
	bpy.types.Scene.MAXVEL = bpy.props.FloatProperty(
		name = "maxiumum velocity")
	bpy.types.Scene.SOLDIAM = bpy.props.FloatProperty(
		name = "sun radius")
	bpy.types.Scene.SUNRANDLOC = bpy.props.BoolProperty(
		name = "random sun location")

	bpy.types.Scene.RANDVEL = bpy.props.BoolProperty(
        name = "random starting velocity")

	bpy.types.Scene.STARTFRAME = bpy.props.IntProperty(
		name = "startframe")		

	bpy.types.Scene.VECMULT = bpy.props.FloatProperty(
		name = "vectordisplaymult")

	bpy.types.Scene.MAXINTERAD = bpy.props.FloatProperty(
		name = "maxinteractionradius")				

	bpy.types.Scene.RANDVELRANGE = bpy.props.FloatProperty(
		name = "rendvelrange")	

	bpy.types.Scene.EXPLODE = bpy.props.BoolProperty(
        name = "explode on impact")

	bpy.types.Scene.ACCRETE = bpy.props.BoolProperty(
        name = "accrete or not")

	bpy.types.Scene.METABALLS = bpy.props.BoolProperty(
        name = "accrete or not")

	bpy.types.Scene.EXTHRESH = bpy.props.FloatProperty(
			name = "explodeimpacethreshold")	

	scn.LOOPATCLIP = LOOPATCLIP
	scn.FRAMERUN = FRAMERUN
	scn.PARTICLES = PARTICLES
	scn.M = M
	scn.T = T
	scn.WIDTH = WIDTH
	scn.HEIGHT = HEIGHT
	scn.CLIP = CLIP
	scn.DSIZE = DSIZE
	#scn.SOLDIAM = SOLDIAM
	scn.SIZEVAR = SIZEVAR
	scn.DAMP = DAMP
	scn.MAXVEL = MAXVEL
	#scn.SOLDIAM = SOLDIAM
	scn.SUNRANDLOC = SUNRANDLOC

	scn.RANDVEL = RANDVEL

	scn.STARTFRAME = STARTFRAME
	scn.RANDVELRANGE = RANDVELRANGE

	scn.MAXINTERAD = MAXINTERAD
	scn.EXTHRESH = EXTHRESH
	scn.A = A

	scn.VECMULT = VECMULT
	scn.ACCRETE = ACCRETE
	scn.EXPLODE = EXPLODE
	scn.METABALLS = METABALLS

initSceneProps(scn)
bpy.types.Object.glob = bpy.props.BoolProperty(
	name = 'glob',
	default = False)
bpy.types.Object.tmass = bpy.props.FloatProperty(
	name = 'total mass of object including others merged to it.',
	default = 0.0)
bpy.types.Object.vel = bpy.props.FloatVectorProperty(
	name = 'velocity',
	default = (0.0, 0.0, 0.0))
bpy.types.Object.merged = bpy.props.BoolProperty(
	name = 'merged',
	default = False)
bpy.types.Object.bhole = bpy.props.BoolProperty(
	name = 'blackhole',
	default = False)	

class OBJECT_PT_SOMPanel(bpy.types.Panel):
	bl_space_type = "PROPERTIES"
	bl_region_type = "WINDOW"
	bl_context = "physics"
	bl_label = "Simple Orbital Mechanics"

	def draw_header(self, context):
		layout = self.layout

	def draw(self, context):
		layout = self.layout
		scene = scn
		row1 = layout.row(align=True)
		row2 = layout.row(align=True)

		split1 = row1.split(percentage=0.5)
		colX = split1.column()
		colY = split1.column()
		split2 = row2.split(percentage=0.33)
		colL = split2.column()
		colM = split2.column()
		colR = split2.column()

		colX.label(text="2D Gravity/Accretion Simulator", icon='WORLD_DATA')
		ltext1 = 	'vx: ' + str(scn.objects.active.vel[0])[0:9] + 
					' | ' + 
					'vy: ' + str(scn.objects.active.vel[1])[0:9]
		colY.label(text=ltext1)

		colX.label(text=scn.objects.active.name)
		colY.label(text='scale: ' + str(scn.objects.active.scale[0]))
		ltext2 =  'tmass: ' + str(scn.objects.active.tmass)[0:7]
		ltext3 = 'merged: ' + str(scn.objects.active.merged)
		colX.label(text=ltext3)
		colY.label(text=ltext2)
		colX.label(text='bhole: ' + str(scn.objects.active.bhole))
		colY.label(text='')

		colX.operator("create_globfield")
		colY.operator("delete_globfield")
		colX.label(text = '')
		colY.operator("clear_animation")
		colX.operator("addVecTails")
		colY.operator("setVecTanToNearestSun")		

		colX.prop(scene, 'RANDVEL', icon='BLENDER', toggle=True)
		colY.prop(scene, 'LOOPATCLIP', icon='BLENDER', toggle=True)
		colX.prop(scene, 'RANDVELRANGE', icon='BLENDER')#, toggle=True)
		colY.prop(scene, 'VECMULT', icon='BLENDER')#, toggle=True)		

		colL.operator("animate_globfield")
		colM.prop(scene, 'FRAMERUN')

		colR.prop(scene, 'STARTFRAME')

		colL.prop(scene, 'WIDTH', icon='BLENDER')#, toggle=True)
		colM.prop(scene, 'HEIGHT', icon='BLENDER')#, toggle=True)
		colR.prop(scene, 'CLIP', icon='BLENDER')#, toggle=True)

		colL.separator()
		colM.separator()
		colR.separator()				

		colL.prop(scene, 'PARTICLES', icon='BLENDER')#, toggle=True)
		colM.prop(scene, 'MAXINTERAD', icon='BLENDER')#, toggle=True)
		colR.prop(scene, 'SIZEVAR', icon='BLENDER')#, toggle=True)

		colL.label(text='')
		colM.prop(scene, 'DSIZE', icon='BLENDER')#, toggle=True)
		#colR.prop(scene, 'SOLDIAM', icon='BLENDER')#, toggle=True)		

		colL.separator()
		colM.separator()
		colR.separator()

		colL.prop(scene, 'DAMP', icon='BLENDER')#, toggle=True, expand=False)
		#colM.prop(scene, 'GRAVITY', icon='BLENDER')#, toggle=True)
		colR.prop(scene, 'MAXVEL', icon='BLENDER')#, toggle=True)					 						

		sunBox = colL.box()
		sunBox.label(text = "Sun/Blackhole")
		sunBox.operator("create_sunhole")
		sunBox.operator("delete_sunholes")
		sunBox.prop(scene, 'SOLDIAM', icon='BLENDER')#, toggle=True)
		sunBox.prop(scene, 'SUNRANDLOC', icon='BLENDER', toggle=True)
		#sunBox.prop(scene, 'SUNsDSIZE', icon='BLENDER')#, toggle=True)		

		#colM.template_curve_mapping(scene, 'mycurve', type='VECTOR')
		#colR.template_reports_banner()
		# these would be nice, also remember to useevent = true on props
		colM.prop(scene, 'EXTHRESH', icon='BLENDER', toggle=True)
		colR.prop(scene, 'A', icon='BLENDER')#, toggle=True)

		colM.operator('createMeshPath')
		colM.operator('testit3')
		#colR.label(text='')			

		colM.label(text='')
		colR.label(text='')	

		colM.operator('testit')
		colR.operator('testit2')
		colM.prop(scene, 'EXPLODE', icon='BLENDER', toggle=True)
		colR.prop(scene, 'ACCRETE', icon='BLENDER', toggle=True)				

		#colM.prop(scene, 'TESTCOLLECT', icon='BLENDER', toggle=True)
		#colR.menu('amenu', 'irmanu')

		#colM.prop_enum(scene, 'TESTMENU', 't')
		#colM.prop_enum(scene, 'TESTMENU', 't')		

class SCENE_OT_testit3(bpy.types.Operator):
	bl_idname = "testit3"
	bl_label = "testit3"
	bl_options = {'REGISTER'}
	bl_description = "testit3"

	def invoke(self, context, event):
		print('---trying blah---')
		#tob = scn.objects.active
		#print(tob.name)
		#createMetaBall()
		setCurvesToConstant()
		return("FINISHED")

class SCENE_OT_testit(bpy.types.Operator):
	bl_idname = "testit"
	bl_label = "testit"
	bl_options = {'REGISTER'}
	bl_description = "testit"

	def invoke(self, context, event):
		print('---trying to create a metaglob---')
		createSolSystem()
		return("FINISHED")

class SCENE_OT_testit2(bpy.types.Operator):
	bl_idname = "testit2"
	bl_label = "testit2FLUSH"
	bl_options = {'REGISTER'}
	bl_description = "testit2"

	def invoke(self, context, event):
		print('---testit2---')
		flush()
		#tob = scn.objects.active
		#print(tob.name)
		#createMetaBall()
		return("FINISHED")	

class SCENE_OT_createMeshPath(bpy.types.Operator):
	bl_idname = "createMeshPath"
	bl_label = "createMeshPath"
	bl_options = {'REGISTER'}
	bl_description = "create mesh ribbon from glob paths"

	def invoke(self, context, event):
		print('---watch it grow---')
		createMeshPath()
		return("FINISHED") 

class SCENE_OT_setVecTanToNearestSun(bpy.types.Operator):
	bl_idname = "setVecTanToNearestSun"
	bl_label = "setVectorTangentToSuns"
	bl_options = {'REGISTER'}
	bl_description = "set vectors tangent to their nearest sun"

	def invoke(self, context, event):
		print('---turn away from the light---')
		setVecTangentToNearestHole()
		return("FINISHED")			

class SCENE_OT_addVecTails(bpy.types.Operator):
	bl_idname = "addVecTails"
	bl_label = "addVectorTails"
	bl_options = {'REGISTER'}
	bl_description = "add/update vector tails to globs"

	def invoke(self, context, event):
		print('---youve got tails!---')
		addVectorTails()
		return("FINISHED")			

class SCENE_OT_delete_sunholes(bpy.types.Operator):
	bl_idname = "delete_sunholes"
	bl_label = "deleteSunHoles"
	bl_options = {'REGISTER'}
	bl_description = "remove all suns"

	def invoke(self, context, event):
		print('---goodbye norma jean---')
		delSuns()
		return("FINISHED")		

class SCENE_OT_create_globfield(bpy.types.Operator):
	bl_idname = "clear_animation"
	bl_label = "clearAnimation"
	bl_options = {'REGISTER'}
	bl_description = "clear all animation data"

	def invoke(self, context, event):
		print('---be gone animation data!---')
		clearAnimation()
		return("FINISHED")	

class SCENE_OT_create_sunhole(bpy.types.Operator):
	bl_idname = "create_sunhole"
	bl_label = "createSunHole"
	bl_options = {'REGISTER'}
	bl_description = "create a Sun/Blackhole type stationary gravity well"

	def invoke(self, context, event):
		print('---mmm... matter---')
		makeSunHole()
		return("FINISHED")

class SCENE_OT_create_globfield(bpy.types.Operator):
	bl_idname = "create_globfield"
	bl_label = "createGlobs"
	bl_options = {'REGISTER'}
	bl_description = "create a field of globs based on parameters"

	def invoke(self, context, event):
		print('---let there be globs---')
		makeGlobField()
		return("FINISHED")

class SCENE_OT_delete_globfield(bpy.types.Operator):
	bl_idname = "delete_globfield"
	bl_label = "deleteGlobs"
	bl_description = "delete all globs"
	bl_options = {'REGISTER', 'UNDO'}

	def invoke(self, context, event):
		print('---destroy all globs---')
		delGlobs()
		return{'FINISHED'}

class SCENE_OT_animate_globfield(bpy.types.Operator):
	bl_idname = "animate_globfield"
	bl_label = "animateGlobs"
	bl_description = "gravitometicically animateify globjects"
	bl_options = {'REGISTER', 'UNDO'}

	def invoke(self, context, event):
		print('---i am busy calculating glob animations---')
		loopit()
		bpy.context.scene.frame_current = STARTFRAME
		return{'FINISHED'}	 

def register():
	bpy.types.Scene.some_strvar = bpy.props.StringProperty(
		name='some_strvar',
		description='a string variable i might need')

def unregister():
	del bpy.types.Scene.some_strvar

if __name__ == "__main__":
    register()

##########    DROPDOWN BOX / MENU
"""
class RENDER_PT_matdropdown(bpy.types.Panel):
    bl_space_type = "PROPERTIES"
    bl_region_type = "WINDOW"
    bl_context = "render"
    bl_label = "Material Dropdown lists"

    def draw(self, context):
        rd = context.scene
        layout = self.layout
        layout.prop(rd, "mat_list_old", text="Replace")
        layout.prop(rd, "mat_list_new", text="by")
        layout.separator()
        row = layout.row()
        row.operator("custom.update_materiallists")
        row.operator("custom.print_materials")

def replace():
    MATERIALS = []
    for i in range(len(bpy.data.materials)):
        MATERIALS.append((str(i), bpy.data.materials[i].name, str(i)))

    bpy.types.Scene.EnumProperty( attr="mat_list_old",
        name="Replace",
        description="Choose a material to be replaced",
        items = MATERIALS, default = '0')
    bpy.types.Scene.EnumProperty( attr="mat_list_new",
        name="Replace by",
        description="Choose a replacement material",
        items = MATERIALS, default = '1')    

class CUSTOM_OT_update_materiallists(bpy.types.Operator):
    bl_idname = "CUSTOM_OT_update_materiallists"
    bl_label = "Update"
    bl_description = "Update the dropdown boxes. Necessary if you added or deleted materials in the scene."

    def invoke(self, context, event):
        replace()
        return{'FINISHED'}

class CUSTOM_OT_print_materials(bpy.types.Operator):
    bl_idname = "CUSTOM_OT_print_materials"
    bl_label = "Print"
    bl_description = "Print the selections of the dropdown boxes."
    def invoke(self, context, event):
        old = bpy.data.materials[int(bpy.context.scene.mat_list_old)]
        new = bpy.data.materials[int(bpy.context.scene.mat_list_new)]
        print("Replace",old.name,"by",new.name)
        return{'FINISHED'}

if __name__ == '__main__':
    bpy.types.register(CUSTOM_OT_update_materiallists)
    bpy.types.register(CUSTOM_OT_print_materials)
    replace()
    bpy.types.register(RENDER_PT_matdropdown)
"""

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(required)

(required)

This site uses Akismet to reduce spam. Learn how your comment data is processed.