Posted: 26th Aug 2014 12:55
Given two unit/direction vectors, A and B, this code gives the shortest angular distance from A to B, and whether that is travelling clockwise or counter-clockwise (the orientation, or right- and left-handedness as I've referred to it in the code). Details.



code (requires M1U, Advanced2D plugins)
+ Code Snippet
set display mode desktop width(), desktop height(), 32, 1
sync on : sync rate 30 : sync
scrW = screen width()
scrH = screen height()

type vec2d x as float y as float endtype
vecA as vec2d
vecPerpA as vec2d
vecB as vec2d

// initial values (returns bad results if vectors are 0, that is, not unit length)
vecA.x = 1.0 : vecA.y = 0 : vecPerpA.x = 0 : vecPerpA.y = 1.0 : vecB.x = -0.707106781 : vecB.y = 0.707106781

// define circular area where we'll draw
r# = 160
cx# = scrW/2
cy# = scrH/2	
	
do
	// ---- input poll ----
	mx = mousex()
	my = mousey()
	mc = mouseclick()
	time = hitimer()
	
	// change A vector (red)
	if mc = 1
		vecA.x = mx - cx#
		vecA.y = my - cy#
		l# = sqrt(vecA.x*vecA.x + vecA.y*vecA.y)
		vecA.x = vecA.x / l#
		vecA.y = vecA.y / l#
		
		vecPerpA.x = -vecA.y
		vecPerpA.y = vecA.x
	endif
	
	// change B vector (blue)
	if mc = 2
		vecB.x = mx - cx#
		vecB.y = my - cy#
		l# = sqrt(vecB.x*vecB.x + vecB.y*vecB.y)
		vecB.x = vecB.x / l#
		vecB.y = vecB.y / l#
	endif
	
	// ---- maths bit ----
	// A.B = 1, A and B point in same direction; A.B = 0, A and B are perpendicular; A.B = -1, A and B point in exact opposite directions.
	// PerpA is a vector 90deg clockwise from A. Combination of A.B and PerpA.B can identify in which quadrant - of the imaginary circle with A at 0deg - that B is in.
	// AToB is the angle from A to B in the 0-180 interval (ie. without handedness).
	ADotB# = (vecA.x*vecB.x) + (vecA.y*vecB.y)
	PerpADotB# = (vecPerpA.x*vecB.x) + (vecPerpA.y*vecB.y)
	AToB# = acos(ADotB#)
	
	// as mentioned above, A.B and PerpA.B give B's A-relative quadrant, but we don't need the quadrant to find handedness - just the 'halfdrant'
	// (that is, we want to know which of the two semicircular areas (on either side of A) B is in.)
	if PerpADotB# >= 0
		AToB_handedness = 1
	else
		AToB_handedness = -1
	endif
	
	// ---- drawing ----
	// main circle
	a2circle cx#, cy#, r#, 0xffffffff
	a2line cx#-r#, cy#, cx#+r#, cy#, 0x80ffffff
	a2line cx#, cy#-r#, cx#, cy#+r#, 0x40ffffff
	
	// A (red), PerpA (orange, faded), and B (blue).
	a2line cx#, cy#, cx#+vecA.x*r#, cy#+vecA.y*r#, 0xffff0000
	a2line cx#, cy#, cx#+vecPerpA.x*r#, cy#+vecPerpA.y*r#, 0x40d06000
	a2line cx#, cy#, cx#+vecB.x*r#, cy#+vecB.y*r#, 0xff1040f0
	
	// radial gradient from A to B through the relevant semicircular area, plus a "ball bearing" to reinforce A-to-B directionality.
	t# = time%%2000
	if vecA.y > 0 then minA = acos(vecA.x) else minA = 360.0 - acos(vecA.x)
	maxA = int(AToB#)
	if maxA <> 0
		for a = 0 to maxA
			if AToB_handedness = -1 then a# = minA - a else a# = a + minA
			if t# <= (2000.0/maxA*a) then a2fillcircle cx#+cos(a#)*r#*0.96, cy#+sin(a#)*r#*0.96, r#*0.02, 0x40ffffff : t# = 2001.0
			a2filltriangle cx#, cy#, cx#+cos(a#)*r#, cy#+sin(a#)*r#, cx#+cos(a#+1)*r#, cy#+sin(a#+1)*r#, 0x40000000 || int(255.0 / maxA * a) || int(255.0 / maxA * (maxA-a)) << 16
		next a
	endif
	
	// A to B angular distance label and vector labels.
	if AToB_handedness = -1 then halfA# = minA - maxA*0.5 else halfA# = maxA*0.5 + minA
	center text cx#+cos(halfA#)*(r#+25), cy# + sin(halfA#)*(r#+25) - 7, str$(AToB#*AToB_handedness,1)
	center text cx#+vecA.x*(r#+25), cy#+vecA.y*(r#+25) - 7, "A"
	center text cx#+vecB.x*(r#+25), cy#+vecB.y*(r#+25) - 7, "B"
	
	// an arrow on A pointing in the direction of B.
	drawArrow(cx#+cos(minA)*r#, cy#+sin(minA)*r#, cx#+cos(minA)*r# + cos(minA+90)*AToB_handedness*100, cy#+sin(minA)*r# + sin(minA+90)*AToB_handedness*100, 6, 0xffff0000)
	
	// some debug output.
	print "A: ", str$(vecA.x,1), ", ", str$(vecA.y,1)
	print "B: ", str$(vecB.x,1), ", ", str$(vecB.y,1)
	print "A.B: ", str$(ADotB#,1), "  (", str$(AToB#,1), ")"
	print "PerpA.B: ", str$(PerpADotB#,1), "  (", str$(acos(PerpADotB#),1), ")"
	print "A to B Handedness: ", AToB_handedness, "  (", left$("left", 4*(AToB_handedness=-1)), left$("right", 5*(AToB_handedness=1)), ")"
	center text scrW/2, scrH - 40, "Left-click: Set A Vector  -  Right-click: Set B Vector"
	
	sync
	cls 0
	nice wait 20
loop

// by: neuro fuzzy
function drawArrow(fromx,fromy,tox,toy,size,color as dword)
	a2line fromx,fromy,tox,toy,color

	//type 2, arrow
	x=fromx-tox
	y=fromy-toy
	d#=sqrt(x*x+y*y)
	x2#=x/d#
	y2#=y/d# `<x1#,x2#> is just the direction from <tox,toy> to <fromx,fromy>
	a2line tox,toy,tox+size*0.707106781*(x2#-y2#),toy+size*0.707106781*(x2#+y2#), color
	a2line tox,toy,tox+size*0.707106781*(y2#+x2#),toy+size*0.707106781*(y2#-x2#), color
endfunction



code (no plugins):
+ Code Snippet
set display mode desktop width(), desktop height(), 32, 1
sync on : sync rate 30 : sync
scrW = screen width()
scrH = screen height()

type vec2d x as float y as float endtype
vecA as vec2d
vecPerpA as vec2d
vecB as vec2d

// initial values (returns bad results if vectors are 0, that is, not unit length)
vecA.x = 1.0 : vecA.y = 0 : vecPerpA.x = 0 : vecPerpA.y = 1.0 : vecB.x = -0.707106781 : vecB.y = 0.707106781

// define circular area where we'll draw
r# = 160
cx# = scrW/2
cy# = scrH/2	
	
do
	// ---- input poll ----
	mx = mousex()
	my = mousey()
	mc = mouseclick()
	time = timer()
	
	// change A vector (red)
	if mc = 1
		vecA.x = mx - cx#
		vecA.y = my - cy#
		l# = sqrt(vecA.x*vecA.x + vecA.y*vecA.y)
		vecA.x = vecA.x / l#
		vecA.y = vecA.y / l#
		
		vecPerpA.x = -vecA.y
		vecPerpA.y = vecA.x
	endif
	
	// change B vector (blue)
	if mc = 2
		vecB.x = mx - cx#
		vecB.y = my - cy#
		l# = sqrt(vecB.x*vecB.x + vecB.y*vecB.y)
		vecB.x = vecB.x / l#
		vecB.y = vecB.y / l#
	endif
	
	// ---- maths bit ----
	// A.B = 1, A and B point in same direction; A.B = 0, A and B are perpendicular; A.B = -1, A and B point in exact opposite directions.
	// PerpA is a vector 90deg clockwise from A. Combination of A.B and PerpA.B can identify in which quadrant - of the imaginary circle with A at 0deg - that B is in.
	// AToB is the angle from A to B in the 0-180 interval (ie. without handedness).
	ADotB# = (vecA.x*vecB.x) + (vecA.y*vecB.y)
	PerpADotB# = (vecPerpA.x*vecB.x) + (vecPerpA.y*vecB.y)
	AToB# = acos(ADotB#)
	
	// as mentioned above, A.B and PerpA.B give B's A-relative quadrant, but we don't need the quadrant to find handedness - just the 'halfdrant'
	// (that is, we want to know which of the two semicircular areas (on either side of A) B is in.)
	if PerpADotB# >= 0
		AToB_handedness = 1
	else
		AToB_handedness = -1
	endif
	
	// ---- drawing ----
	// radial gradient from A to B through the relevant semicircular area, plus a "ball bearing" to reinforce A-to-B directionality.
	t# = time%%2000
	if vecA.y > 0 then minA = acos(vecA.x) else minA = 360.0 - acos(vecA.x)
	maxA = int(AToB#)
	if maxA <> 0
		ink 0xff404040, 0
		for a = 0 to maxA
			if AToB_handedness = -1 then a# = minA - a else a# = a + minA
			if t# <= (2000.0/maxA*a) then circle cx#+cos(a#)*r#*0.96, cy#+sin(a#)*r#*0.96, r#*0.02 : t# = 2001.0
			line cx#+cos(a#)*r#*0.5, cy#+sin(a#)*r#*0.5, cx#+cos(a#)*r#, cy#+sin(a#)*r#
		next a
	endif
	
	// main circle
	ink 0xffffffff, 0
	circle cx#, cy#, r#
	line cx#-r#, cy#, cx#+r#, cy#
	line cx#, cy#-r#, cx#, cy#+r#
	
	// A (red), PerpA (orange, faded), and B (blue).
	ink 0xffff0000, 0 : line cx#, cy#, cx#+vecA.x*r#, cy#+vecA.y*r#
	ink 0xff341800, 0 : line cx#, cy#, cx#+vecPerpA.x*r#, cy#+vecPerpA.y*r#
	ink 0xff1040f0, 0 : line cx#, cy#, cx#+vecB.x*r#, cy#+vecB.y*r#
	
	ink 0xffffffff, 0
	
	// A to B angular distance label and vector labels.
	if AToB_handedness = -1 then halfA# = minA - maxA*0.5 else halfA# = maxA*0.5 + minA
	center text cx#+cos(halfA#)*(r#+25), cy# + sin(halfA#)*(r#+25) - 7, str$(AToB#*AToB_handedness,1)
	center text cx#+vecA.x*(r#+25), cy#+vecA.y*(r#+25) - 7, "A"
	center text cx#+vecB.x*(r#+25), cy#+vecB.y*(r#+25) - 7, "B"
	
	// an arrow on A pointing in the direction of B.
	drawArrow(cx#+cos(minA)*r#, cy#+sin(minA)*r#, cx#+cos(minA)*r# + cos(minA+90)*AToB_handedness*100, cy#+sin(minA)*r# + sin(minA+90)*AToB_handedness*100, 6, 0xffff0000)
	
	// some debug output.
	print "A: ", str$(vecA.x,1), ", ", str$(vecA.y,1)
	print "B: ", str$(vecB.x,1), ", ", str$(vecB.y,1)
	print "A.B: ", str$(ADotB#,1), "  (", str$(AToB#,1), ")"
	print "PerpA.B: ", str$(PerpADotB#,1), "  (", str$(acos(PerpADotB#),1), ")"
	print "A to B Handedness: ", AToB_handedness, "  (", left$("left", 4*(AToB_handedness=-1)), left$("right", 5*(AToB_handedness=1)), ")"
	center text scrW/2, scrH - 40, "Left-click: Set A Vector  -  Right-click: Set B Vector"
	
	sync
	cls 0
	`nice wait 20
loop

// by: neuro fuzzy
function drawArrow(fromx,fromy,tox,toy,size,color as dword)
	line fromx,fromy,tox,toy

	//type 2, arrow
	x=fromx-tox
	y=fromy-toy
	d#=sqrt(x*x+y*y)
	x2#=x/d#
	y2#=y/d# `<x1#,x2#> is just the direction from <tox,toy> to <fromx,fromy>
	line tox,toy,tox+size*0.707106781*(x2#-y2#),toy+size*0.707106781*(x2#+y2#)
	line tox,toy,tox+size*0.707106781*(y2#+x2#),toy+size*0.707106781*(y2#-x2#)
endfunction



.
Posted: 28th Aug 2014 10:37
Brilliant snippet; very good for calculating the shortest rotation towards a target angle.