Waterfall Macro

Discuss DMXIS Python macro programming, and post any new macros here!
Post Reply
RichG
Posts: 98
Joined: Mon Nov 24, 2014 8:19 am

Waterfall Macro

Post by RichG » Thu Apr 11, 2019 6:31 am

I saw a concert where all the moving heads only were on during downward transitions. With all the moving heads sweeping downward at random this gave a neat effect light continuously cascading down creating a Waterfall of light. I took it upon myself to write a macro to achieve this. Select the Tilt and Dimmer channels for a moving head and run the macro. The macro sets the Dimmer value ON only during downward tilt transitions. Select as many Tilt/Dimmer pairs as you want. Just make sure your heads are set correctly... the macro assumes decreasing values correspond to downward tilting.

A few things to note about the Waterfall macro:
1. The macro works for any wave shape, although Saw Down is probably the most practical.
2. The macro takes into account value saturation (where it sticks at 100% or 0%)
3. Some movers are faster than others. The Dimmer may go ON as the head is tilting as fast as it can. There are adjustments available to fine tune the response so the Dimmer stays OFF long enough for the head to move into position.
4. After running the macro, the Dimmers will be out of sync with the Tilt. Click the Tap-Tempo in Show Buddy Active to reset the oscillators (or in DMXIS, save the preset, choose a different preset, then go back to the saved preset).

Code: Select all

#==============================================================================================
# DMXIS Macro
# Waterfall.py (c) RCG, 4/10/2019
# Turns Dimmer ON for downward transition, OFF for stationary
# and up transitions
# 
# Select fixtures in Tilt/Dimmer pairs.
# Dimmer is modified accordingly.
# Assumptions:
# 1. BPM and Speed are set slow enough for the fixtures to follow
# 2. Any fixture/channel inversions are set as desired
#==============================================================================================

#----------------------------------------------------------------------------------------------
# Custom Fine Tuning
# Modify these based on type of fixture.
# r:      Range of movement. This determines the On/Off point for the fixture.
#         Some waveshapes sit at 0% or 100% before they transition. For example, Saw Down with
#         a shape of 100.  This parameter controls the percent movement value when the light
#         turns on. When set to 0.95, the fixture light will go ON at 95% tilt, and OFF at 5%.
# mnshp:  Minimum Shape Value. This adjust the minimum ON duration for the fixture light.
# mnst_n: Minimum start point. Some fixtures move faster than others. For example, a scanner
#         with a mirror moves more quickly than a moving head. These parameters control time
#         to remain off while the fixture moves to the top of the waveform. The higher the
#         number, the longer the off time as the fixture transitions. If a fixture is
#         already sitting at the top of travel, then no delay is needed.
#         Notes for specific types:
#         Sine -     The higher the shape value, the more the sine will look like a Saw Down
#                    waveform. The fixture may not be able to follow the abrupt waveform upward
#                    for transition high shape values. This value compensates for slow upward
#                    transitions which require no delay vs. fast transitions requiring some
#                    delay.
#
#         Square -   The square wave relies on the fixture's finite transition time from top to
#                    bottom. The downward transition happens at the shape value, then goes back
#                    up at 1.
#
#         Triangle - The triangle type may linearly transition up and down, or may swoop up
#                    or down depending on the shape value.  High or low shape values can
#                    in prolonged stays at high or low values. Adjusting the 'r' value can
#                    ensure the light does not stay lit while the fixture is stationary.
#
#         Saw Up -   The saw up type transitions downward at the end of the waveform. It's like
#                    the square wave because it relies on the fixture speed to dictate the
#                    amount of ON time.
#
#         Saw Down - The saw down type is probably the most logical choice for this
#                    application. It follows the waveform downward, then quickly reverts
#                    back to the top point of travel. Since the upward transition happens
#                    at start of the waveform, a minimum start time should be set to avoid
#                    turning the light ON while the fixture is moving upward. The 'r' should
#                    be set to avoid turning the fixture ON while it is stationary.
#----------------------------------------------------------------------------------------------
print("===================================")

r      = 0.98 #-- 0..1 Range of ON movement
mnshp  = 0.05 #-- 0..1 Minimum Shape Value. Controls minimum ON time.    
mnst_1 = 0.10 #-- 0..1 Minimum Start Sine Type.
mnst_2 = 0.0  #-- 0..1 Minimum Start Square Wave Type.
mnst_3 = 0.10 #-- 0..1 Minimum Start Triangle Type.
mnst_4 = 0.0  #-- 0..1 Minimum Start Saw Up Type.
mnst_5 = 0.10 #-- 0..1 Minimum Start Saw Down Type.
    
#--------------------------------------------------
# Get the user selected channels 
#--------------------------------------------------
sel = GetAllSelCh(False)
nsel = len(sel)

#--------------------------------------------------
# Check user selection 
#--------------------------------------------------
if nsel < 2:
	Message("Select at least 2 channels")
elif nsel%2 > 0:
	Message("Select an even number of channels")
else:
	mx = 1.0  #-- maximum waveform value
	mn = 0.0  #-- minimum waveform value
	
	for i in range(0, nsel/2):
		tch = sel[i*2]     #-- Tilt Channel
		dch = sel[i*2+1]   #-- Dimmer Control Channel
		nm = GetChName(tch).lower()
		if nm == "tilt" or nm == "rotate":
			nm = GetChName(dch).lower()
			if nm == "dim" or nm == "dimmer":
				mag  = GetOscAmount(tch)          #-- Oscillation +/- per unit magnitude
				bias = float(GetChVal(tch))/255.0 #-- Bias point per unit value 0 = bottom of range 1 = top of range
				otyp = GetOscType(tch)            #-- Get Osc Type
				oshp = GetOscShape(tch)           #-- Get Osc Shape
				ochs = GetOscChase(tch)           #-- Get Osc Chase
				
				#-- OFF Osc Type -------------------------------------------------------------
				if otyp == 0:
					print ("Osc Typ is OFF for selected fixture %d. No changes made." % (i+1))			
				else:
				
				#-- Sine Osc Type -------------------------------------------------------------
					if otyp == 1:
						if oshp < (1 - mnst_1):
							mnstr = 0.0
						else:
							mnstr = oshp - (1 - mnst_1)

						sy = __builtins__.min( bias + r*mag, mx)
						fy = __builtins__.max( bias - r*mag, mn)

						x = (sy-bias)/mag
						#-- 15th order approximation of asin(x)
						asinx = 6.12683146168e-010+9.87783240580e-001*x-6.43155298791e-008*x*x+7.20086973441e-001*x*x*x+1.10581741541e-006*x*x*x*x-7.05868447545e+000*x*x*x*x*x-7.13154427362e-006*x*x*x*x*x*x+4.00656807560e+001*x*x*x*x*x*x*x+2.18726319233e-005*x*x*x*x*x*x*x*x-1.14049647532e+002*x*x*x*x*x*x*x*x*x-3.43685066853e-005*x*x*x*x*x*x*x*x*x*x+1.72788558895e+002*x*x*x*x*x*x*x*x*x*x*x+2.67183864481e-005*x*x*x*x*x*x*x*x*x*x*x*x-1.32449399902e+002*x*x*x*x*x*x*x*x*x*x*x*x*x-8.14116000307e-006*x*x*x*x*x*x*x*x*x*x*x*x*x*x+4.05067623852e+001*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x
	
						start = oshp*(0.5 - asinx/3.14159)
	
						x = (fy-bias)/mag
						#-- 15th order approximation of asin(x)
						asinx = 6.12683146168e-010+9.87783240580e-001*x-6.43155298791e-008*x*x+7.20086973441e-001*x*x*x+1.10581741541e-006*x*x*x*x-7.05868447545e+000*x*x*x*x*x-7.13154427362e-006*x*x*x*x*x*x+4.00656807560e+001*x*x*x*x*x*x*x+2.18726319233e-005*x*x*x*x*x*x*x*x-1.14049647532e+002*x*x*x*x*x*x*x*x*x-3.43685066853e-005*x*x*x*x*x*x*x*x*x*x+1.72788558895e+002*x*x*x*x*x*x*x*x*x*x*x+2.67183864481e-005*x*x*x*x*x*x*x*x*x*x*x*x-1.32449399902e+002*x*x*x*x*x*x*x*x*x*x*x*x*x-8.14116000307e-006*x*x*x*x*x*x*x*x*x*x*x*x*x*x+4.05067623852e+001*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x
	
						fin = oshp*(0.5 - asinx/3.14159)
	
					#-- Square Wave Osc Type -------------------------------------------------------------
					elif otyp == 2:
						mnstr = mnst_2
						start = oshp
						fin   = oshp
						
					#-- Triangle Osc Type -------------------------------------------------------------
					elif otyp == 3:
						mnstr = mnst_3

						sy = __builtins__.min(bias + r*mag, mx)
						fy = __builtins__.max(bias - r*mag, mn)
						
						if oshp < 0.5:
							n   = 7 - 12*oshp
							start = 1 - (2**(-(n+1)/n))*( (sy-bias+mag)/mag )**(1/n)
							fin   = 1 - (2**(-(n+1)/n))*( (fy-bias+mag)/mag )**(1/n) 
						
						else:
							n   = 12*oshp - 5
							start = 0.5 + (2**(-(n+1)/n))*( (bias+mag-sy)/mag )**(1/n)
							fin   = 0.5 + (2**(-(n+1)/n))*( (bias+mag-fy)/mag )**(1/n)
	
					#-- Saw Up Osc Type -------------------------------------------------------------
					elif otyp == 4:
						mnstr = mnst_4
						start = 1
						fin   = 1
	
					#-- Saw Down Osc Type -------------------------------------------------------------
					elif otyp == 5:
						mnstr = mnst_5
						
						sy = __builtins__.min(bias + r*mag, mx)
						fy = __builtins__.max(bias - r*mag, mn)
						
						if oshp < 0.5:
							n   = 7 - 12*oshp
							start = 1 - ( (sy - bias + mag) / (2*mag) )**(1/n)
							fin   = 1 - ( (fy - bias + mag) / (2*mag) )**(1/n)
						
						else:
							n   = 12*oshp - 5
							start = ( (bias + mag - sy) / (2*mag) )**(1/n)
							fin   = ( (bias + mag - fy) / (2*mag) )**(1/n) 
						
					start = __builtins__.max(start, mnstr)
					shape = __builtins__.max(fin - start, mnshp)
					
					phase = ochs + 1 - start #-- Align start with descent point
					if phase > 1.0:
						phase -= 1.0
										
					print ("fx=%d mg= %5.3f bi=%5.3f str=%5.3f fin=%5.3f ph=%5.3f shp=%5.3f" % (i+1, mag, bias, start, fin, phase, shape)) 
					SetChVal(dch, 128)
					SetOscAmount(dch, 1)
					SetOscType(dch, 2)
					SetOscSpeed(dch, GetOscSpeed(tch))
					SetOscChase(dch, phase)
					SetOscShape(dch, shape)
			else:
				print ("Invalid Dimmer channel for selected fixture %d. No changes made." % (i+1))
		else:
			print ("Invalid Tilt channel for selected fixture %d. No changes made." % (i+1))
			

Post Reply