| line-line intersect with ray reflection 
 
 
'Reflection / 2D Raytracing
'M.Rauch 15.08.2013
'(Monkey Pro 73b)
Strict
Import mojo
Class Vector
 Field x:Float
 Field y:Float
 Field z:Float
 
 Function Create:Vector(x:Float,y:Float,z:Float)
 
 	Local v:Vector=New Vector
 	
 	v.x=x
 	v.y=y
 	v.z=z
 	
 	Return v
 
 End
 
 Method Length:Float()
	Return Sqrt(x*x + y*y + z*z)
 End
 
 Method Length1:Vector()
	Local v:Vector = Vector.Create(x,y,z)
		
		Local l:Float=v.Length()
		If l>0
		 v.x=v.x / l
		 v.y=v.y / l
		 v.z=v.z / l
		Endif
		
		Return v
 End
 
 Function Sub:Vector(a:Vector , b:Vector)
	'Delta
	
	Local v:Vector=New Vector
	v.x = a.x - b.x
	v.y = a.y - b.y
	v.z = a.z - b.z
	Return v 
 End
 Method Sub:Vector(b:Vector)
	'Delta
	
	Local v:Vector=New Vector
	v.x = x - b.x
	v.y = y - b.y
	v.z = z - b.z
	Return v 
 End
 Method Add:Vector(b:Vector)
	'Plus
	
	Local v:Vector=New Vector
	v.x = x + b.x
	v.y = y + b.y
	v.z = z + b.z
	Return v 
 End
 Method Mul:Vector(b:Vector)
	'*
	
	Local v:Vector=New Vector
	v.x = x * b.x
	v.y = y * b.y
	v.z = z * b.z
	Return v 
 End
 Function Middle:Vector(a:Vector , b:Vector)
	'Mitte
	
	Local m:Vector=New Vector
	m.x = (a.x + b.x) / 2.0
	m.y = (a.y + b.y) / 2.0
	m.z = (a.z + b.z) / 2.0
	Return m 
 End
 
 Method Draw:Void()
 
 	DrawOval(x-4,y-4,8,8)
 
 End
End
Class MyApp Extends App
	Field walllist:List<Line> = New List<Line>
	Method OnCreate:Int()
		Print "Device Screen Size "+DeviceWidth() + " x " + DeviceHeight()
	
		'wenn das bei HTML5 fehlt kein Bild nur weiß
				
		SetUpdateRate 30 'Render & Update identisch
		
		MakeWalls()
		
		Return 1
		
	End
	
	Method MakeWalls:void()
	  	Local wall:Line
	  	wall=New Line
	  	wall.a=Vector.Create(10,DeviceHeight()-10,0)
		wall.b=Vector.Create(10,10,0)
		walllist.AddLast(wall)
	  	wall=New Line
	  	wall.a=Vector.Create(10,10,0)
		wall.b=Vector.Create(DeviceWidth()-10,10,0)
		walllist.AddLast(wall)
	  	wall=New Line
		wall.a=Vector.Create(DeviceWidth()-10,10,0)
	  	wall.b=Vector.Create(DeviceWidth()-10,DeviceHeight()-10,0)
		walllist.AddLast(wall)
	  	wall=New Line
	  	wall.a=Vector.Create(DeviceWidth()-10,DeviceHeight()-10,0)
		wall.b=Vector.Create(10,DeviceHeight()-10,0)
		walllist.AddLast(wall)
		const s:Float=10.0
		For Local w:Float=0 To 360.0-s Step s
			Local p1:Vector=Vector.Create(100.0+Sin(w  )*40,100.0+Cos(w  )*40,0)
			Local p2:Vector=Vector.Create(100.0+Sin(w+s)*40,100.0+Cos(w+s)*40,0)
			wall=Line.Create(p1,p2)
			walllist.AddLast(wall)
		Next
		For Local w:Float=0 To 360.0-s Step s
			Local p1:Vector=Vector.Create(100.0+Sin(w  )*40,300.0+Cos(w  )*40,0)
			Local p2:Vector=Vector.Create(100.0+Sin(w+s)*40,300.0+Cos(w+s)*40,0)
			wall=Line.Create(p1,p2)
			walllist.AddLast(wall)
		Next
	End
	
	Method RenderWalls:void()
	 	'-------------------- paint walls
	 	SetColor 128,128,128
	 	For Local wall:Line=Eachin walllist
		 	wall.DrawNormal(5.0)
		Next
	 	SetColor 255,255,255
	 	For Local wall:Line=Eachin walllist
		 	wall.Draw()	  	  
		Next
	End
	
	Method OnUpdate:Int()
		Return 1
	End
	Method OnRender:Int()
	  	Cls 0,0,0
	  	PushMatrix
	 
	 	RenderWalls()
	 
	 	'-------------------- start ray
			
	 	Local ray:Line=New Line
	 	ray.a=Vector.Create(DeviceWidth()/2,DeviceHeight()/2,0)
	 	ray.b=Vector.Create(TouchX(),TouchY(),0).Sub(ray.a).Length1().Mul(Vector.Create(1000.0,1000.0,1000.0)).Add(ray.a)
	 	
	 	'-------------------- ray trace / Raytracing
	 	 
	 	Ray.Trace(ray,walllist,10) 'with trace depth limit	
	 		
		PopMatrix
	 
	 Return 1
	End
End
Class Ray
	Function Trace:Void(ray:Line,lines:List<Line>,depth:int)
	
		depth=depth-1
		If depth=0 Then Return
		 	
	 	Local l:Float=1000000.0
		Local newray:Line=null
	 	
	 	For Local wall:Line=Eachin lines
	 		
			Local hit:Vector = LineLine.Intersection(ray,wall)
			If hit
				'SetColor 255,0,0
				'hit.Draw()
	
				'surface Normal
				Local n:Vector=wall.Normal()
				
				'Direction
				Local dir:Vector=ray.Direction()
				
				'Reflect Direction
				Local o:Vector=New Vector
				Local op:Float =2.0 * (dir.x*n.x + dir.y*n.y + dir.z*n.z)
				o.x=dir.x - (op * n.x)
				o.y=dir.y - (op * n.y)
				o.z=dir.z - (op * n.z)
				
				If hit.Sub(ray.a).Length() <l Then 'find the nearest
					l=hit.Sub(ray.a).Length()
				
					ray.b=hit 'shorten the ray
				
					'Reflected Line
					'Start from End To New Point
					newray=New Line
					newray.a=Vector.Create(hit.x+n.x*0.5,hit.y+n.y*0.5,hit.z+n.z*0.5) 'else we hit direct the same wall, we start a little bit up ( sonst trift man die gleiche Mauer wieder)
					newray.b=Vector.Create(hit.x+o.x*1000.0,hit.y+o.y*1000.0,hit.z+o.z*1000.0) 'länge 1000 weiter
					
				Endif
											
			 Endif
		 
		 Next
		 
		 SetColor 255,255,0
	 	 ray.Draw()
		 'do the same again
		 If newray<>Null
						newray.a.Draw()
		 				Ray.Trace(newray,lines,depth)
		 Endif		 
	
	End
	
End
Class Line
	Field a:Vector
	Field b:Vector
	
	Function Create:Line(a:Vector,b:Vector)
	
		Local l:Line= New Line
		l.a=a
		l.b=b
	
		Return l 
	
	end
	
	Method Draw:Void()
		DrawLine(a.x , a.y , b.x , b.y )
	End	
	
	Method DrawNormal:Void(l:Float)
		Local m:Vector=Vector.Middle(a,b)
		Local n:Vector=Normal()
		
		DrawLine(m.x , m.y , m.x + n.x*l , m.y + n.y*l )
	End
	Method Normal:Vector()
	
		'Normale der Linie!
	
		'define dx=x2-x1 And dy=y2-y1		
		'the normals are (-dy, dx) And (dy, -dx)
		
		Local d:Vector=b.Sub(a).Length1()
		
		Return Vector.Create(-d.y,d.x,0)
	
	End
	
	Method Direction:Vector()
				
		Return b.Sub(a).Length1()
	End
	
	Method Length:Float()
	
		'Länge Linie
		Return b.Sub(a).Length()
	End
	
End
Class LineLine
	Function Intersection:Vector(line1:Line,line2:Line)     
		
        Local p:Vector=Null
		Local x1:Float=line1.a.x
		Local y1:Float=line1.a.y
		Local x2:Float=line1.b.x
		Local y2:Float=line1.b.y
		Local x3:Float=line2.a.x
		Local y3:Float=line2.a.y
		Local x4:Float=line2.b.x
		Local y4:Float=line2.b.y
		Local bx:Float = x2 - x1
		Local by:Float = y2 - y1
		Local dx:Float = x4 - x3
		Local dy:Float = y4 - y3
		
		Local b_dot_d_perp:Float = bx * dy - by * dx
		If b_dot_d_perp = 0.0 Then Return Null
  
		Local cx:Float = x3 - x1
	  	Local cy:Float = y3 - y1
	  	
	  	Local t:Float = (cx * dy - cy * dx) / b_dot_d_perp
	  	If (t < 0.0) Or (t > 1.0) Then	Return Null
	  	
	  	Local u:Float = (cx * by - cy * bx) / b_dot_d_perp
	  	If (u < 0.0) Or (u > 1.0) Then Return Null
	  	
  		p=Vector.Create(x1+t*bx, y1+t*by,0)
  
		Return p
		
	End
End
Function Main:Int()
	Local myApp:MyApp = New MyApp
	
	Return 1
	
End	
 
 |