Page 1 of 1

Collision with Triangles

Posted: Sat Jul 07, 2007 2:59 am
by Fou-Lu
Hi everybody!

I'm trying to make an algorithm that determines wether a point is inside a triangle. So far I've done this:

Code: Select all

initmouse()
initsprite()

OpenWindow(1,0,0,640,480,#PB_Window_ScreenCentered,"triangle collision") 
OpenWindowedScreen(WindowID(1),0,0,640,480,0,0,0) 

structure T_point
x.f
y.f
endstructure

structure T_triangle
relative.T_point
p.T_point[3]
endstructure

procedure make_triangle(*t.T_triangle,x1.f,y1.f,x2.f,y2.f,x3.f,y3.f)
*t\p[0]\x=x1
*t\p[0]\y=y1
*t\p[1]\x=x2
*t\p[1]\y=y2
*t\p[2]\x=x3
*t\p[2]\y=y3
endprocedure

procedure  triangle_collision(*t.T_triangle,mx.f,my.f)
mx-*t\relative\x
my-*t\relative\y
;verify if its on the first side area
rx.f=(*t\p[1]\x-*t\p[0]\x)
ry.f=(*t\p[0]\y-*t\p[1]\y)
if mx*ry+my*rx=>rx*ry
 rx.f=(*t\p[2]\x-*t\p[1]\x)
 ry.f=(*t\p[1]\y-*t\p[2]\y)
 if mx*ry+my*rx=>rx*ry
   rx.f=(*t\p[0]\x-*t\p[2]\x)
   ry.f=(*t\p[2]\y-*t\p[0]\y)
   if mx*ry+my*rx=>rx*ry
  procedurereturn 1
  else
  procedurereturn 0
  endif
 else
 procedurereturn 0
 endif
else
procedurereturn 0
endif



endprocedure

createsprite(0,128,128)
createsprite(2,128,128)

createsprite(1,5,5)

tri.T_triangle
make_triangle(tri,0,78,64,0,128,128)
tri\relative\x=120
tri\relative\y=120

startdrawing(spriteoutput(0))
frontcolor(0,0,255)
linexy(tri\p[0]\x,tri\p[0]\y,tri\p[1]\x,tri\p[1]\y)
linexy(tri\p[1]\x,tri\p[1]\y,tri\p[2]\x,tri\p[2]\y)
linexy(tri\p[2]\x,tri\p[2]\y,tri\p[0]\x,tri\p[0]\y)
stopdrawing()

startdrawing(spriteoutput(2))
frontcolor(0,0,255)
linexy(tri\p[0]\x,tri\p[0]\y,tri\p[1]\x,tri\p[1]\y)
linexy(tri\p[1]\x,tri\p[1]\y,tri\p[2]\x,tri\p[2]\y)
linexy(tri\p[2]\x,tri\p[2]\y,tri\p[0]\x,tri\p[0]\y)
fillarea(64,64,rgb(0,0,255),rgb(128,128,255))
stopdrawing()

startdrawing(spriteoutput(1))
frontcolor(255,0,0)
box(0,0,5,5)
stopdrawing()

repeat

clearscreen(0,0,0)

if triangle_collision(tri,mousex(),mousey())
displaysprite(2,120,120)
else
displaysprite(0,120,120)
endif

displaysprite(1,mousex(),mousey())

flipbuffers()

examinemouse()
until mousebutton(1)
But as you can see it doesn't work near the bottom of it (I'm almost sure the other two sides won't work in some other cases...). Well, if someone out there could help me I would really appreciate! :wink:

Posted: Sat Jul 07, 2007 4:54 am
by netmaestro
Hehe. Doubtless you'll consider this a "cheat" - ok, it is! I only changed the triangle_collision proc and the line that calls it, nothing else:

Code: Select all

InitMouse() 
InitSprite() 

OpenWindow(1,0,0,640,480,#PB_Window_ScreenCentered,"triangle collision") 
OpenWindowedScreen(WindowID(1),0,0,640,480,0,0,0) 

Structure T_point 
x.f 
y.f 
EndStructure 

Structure T_triangle 
relative.T_point 
p.T_point[3] 
EndStructure 

Procedure make_triangle(*t.T_triangle,x1.f,y1.f,x2.f,y2.f,x3.f,y3.f) 
*t\p[0]\x=x1 
*t\p[0]\y=y1 
*t\p[1]\x=x2 
*t\p[1]\y=y2 
*t\p[2]\x=x3 
*t\p[2]\y=y3 
EndProcedure 

Procedure  triangle_collision(*t.T_triangle,mx.f,my.f, trix, triy) 
  Dim p.Point(2)
  p(0)\x = *t\p[0]\x 
  p(0)\y = *t\p[0]\y
  p(1)\x = *t\p[1]\x
  p(1)\y = *t\p[1]\y
  p(2)\x = *t\p[2]\x
  p(2)\y = *t\p[2]\y
  rgn = CreatePolygonRgn_(p(),3,#ALTERNATE)
 
  If PtInRegion_(rgn, Int(mx)-trix, Int(my)-triy)
    DeleteObject_(rgn)
    ProcedureReturn 1
  Else
    DeleteObject_(rgn)
    ProcedureReturn 0
  EndIf
  
EndProcedure 

CreateSprite(0,128,128) 
CreateSprite(2,128,128) 

CreateSprite(1,5,5) 

tri.T_triangle 
make_triangle(tri,0,78,64,0,128,128) 
tri\relative\x=120 
tri\relative\y=120 

StartDrawing(SpriteOutput(0)) 
FrontColor(0,0,255) 
LineXY(tri\p[0]\x,tri\p[0]\y,tri\p[1]\x,tri\p[1]\y) 
LineXY(tri\p[1]\x,tri\p[1]\y,tri\p[2]\x,tri\p[2]\y) 
LineXY(tri\p[2]\x,tri\p[2]\y,tri\p[0]\x,tri\p[0]\y) 
StopDrawing() 

StartDrawing(SpriteOutput(2)) 
FrontColor(0,0,255) 
LineXY(tri\p[0]\x,tri\p[0]\y,tri\p[1]\x,tri\p[1]\y) 
LineXY(tri\p[1]\x,tri\p[1]\y,tri\p[2]\x,tri\p[2]\y) 
LineXY(tri\p[2]\x,tri\p[2]\y,tri\p[0]\x,tri\p[0]\y) 
FillArea(64,64,RGB(0,0,255),RGB(128,128,255)) 
StopDrawing() 

StartDrawing(SpriteOutput(1)) 
FrontColor(255,0,0) 
Box(0,0,5,5) 
StopDrawing() 

Repeat 

ClearScreen(0,0,0) 

If triangle_collision(tri,MouseX(),MouseY(), 120,120) 
DisplaySprite(2,120,120) 
Else 
DisplaySprite(0,120,120) 
EndIf 

DisplaySprite(1,MouseX(),MouseY()) 

FlipBuffers() 

ExamineMouse() 
Until MouseButton(1) 
Oh, the memories of 3.94!

Posted: Sat Jul 07, 2007 6:08 am
by Comtois
3 method.
You can change procedure at line 165.

Code: Select all

;Comtois 05/02/05
;Détection d'un point dans un triangle

;Update 07/07/07 - PB 4.10

;-Initialisation
Global ScreenHeight.l,ScreenWidth.l
Declare Erreur(Message$)
If ExamineDesktops()
  ScreenWidth = DesktopWidth(0)
  ScreenHeight = DesktopHeight(0)
Else
    Erreur("Euh ?")
EndIf
If InitSprite() = 0 Or InitMouse() = 0 Or InitKeyboard()=0
  Erreur("Impossible d'initialiser DirectX 7 Ou plus")
ElseIf OpenWindow(0,0,0,ScreenWidth,ScreenHeight,"Collision", #PB_Window_BorderLess) = 0
  Erreur("Impossible de créer la fenêtre")
EndIf
;{/ouvre un écran
If OpenWindowedScreen( WindowID(0), 0, 0, ScreenWidth , ScreenHeight, 0, 0, 0 ) = 0
   Erreur("Impossible d'ouvrir l'écran ")
EndIf

Structure Triangle
  X1.l
  Y1.l
  X2.l
  Y2.l
  X3.l
  Y3.l
EndStructure
Structure Pointf
  x.f
  y.f
EndStructure
Structure FloatLong
  StructureUnion
    f.f
    l.l
  EndStructureUnion
EndStructure

Procedure Erreur(Message$)
  MessageRequester( "Erreur" , Message$ , 0 )
  End
EndProcedure
Procedure Signe(a.l)
  If a>0
    ProcedureReturn 1
  ElseIf a=0
    ProcedureReturn 0
  Else
    ProcedureReturn -1
  EndIf
EndProcedure

 
Macro NORME(V)
   (Sqr(V\x * V\x + V\y * V\y))
EndMacro
Macro SOUSTRACTION_VECTEUR(V, V1, V2)
  V\x = V1\x - V2\x
    V\y = V1\y - V2\y
EndMacro
Macro PRODUIT_SCALAIRE(V1, V2)
    (V1\x * V2\x + V1\y * V2\y)
EndMacro
Procedure Normalise(*N.Pointf)
   Define.f NormeVecteur
   NormeVecteur = NORME(*N)
   If NormeVecteur <> 0.0
       *N\x / NormeVecteur
      *N\y / NormeVecteur
   EndIf   
EndProcedure
Procedure CollisionTriangle(*T.Triangle,*P.point)
  ;Test la collision du point avec le triangle
  ;pour en savoir plus  http://tanopah.jo.free.fr/seconde/region.html
  ;Plan 1
  xu1=*T\X2-*T\X1:yu1=*T\Y2-*T\Y1
  c1=*T\Y1*xu1-*T\X1*yu1
  P1=*T\X3*yu1-*T\Y3*xu1+c1
  AX1=*P\x*yu1-*P\y*xu1+c1
  If Signe(AX1)<>Signe(P1)
    ProcedureReturn #False
  EndIf 
  ;Plan 2
  xu2=*T\X3-*T\X2:yu2=*T\Y3-*T\Y2
  c2=*T\Y2*xu2-*T\X2*yu2
  P2=*T\X1*yu2-*T\Y1*xu2+c2
  AX2=*P\x*yu2-*P\y*xu2+c2
  If Signe(AX2)<>Signe(P2)
    ProcedureReturn #False
  EndIf   
  ;Plan 3
  xu3=*T\X1-*T\X3:yu3=*T\Y1-*T\Y3
  c3=*T\Y3*xu3-*T\X3*yu3
  P3=*T\X2*yu3-*T\Y2*xu3+c3
  AX3=*P\x*yu3-*P\y*xu3+c3
  If Signe(AX3)<>Signe(P3)
    ProcedureReturn #False
  EndIf

  ProcedureReturn #True
EndProcedure

Procedure CollisionTriangle1(*T.Triangle,*P.point)
   Define.Pointf PA, PB, PC, Somme
   PA\x=*T\x1-*P\x
   PA\y=*T\y1-*P\y
   Normalise(@PA)
   PB\x=*T\x2-*P\x
   PB\y=*T\y2-*P\y
   Normalise(@PB)
   PC\x=*T\x3-*P\x
   PC\y=*T\y3-*P\y
   Normalise(@PC)
   Somme\x=PA\x+PB\x+PC\x
   Somme\y=PA\y+PB\y+PC\y
   ;on calcule la taille du vecteur Somme
   If NORME(Somme) > 1
      ProcedureReturn #False
   EndIf
   ProcedureReturn #True
EndProcedure

Procedure CollisionTriangle2(*T.Triangle,*P.point)
   ;Thanks To Keidy from Mr-Gamemaker who posted this particular version
   ;of the function in a little competition
   Define.Pointf e10, e20, vp
   Define.f a, b, c, d, e
   Define.FloatLong x, y, z
   
   e10\x = *T\x2 -*T\x1
   e10\y = *T\y2 -*T\y1
   
   e20\x = *T\x3 -*T\x1
   e20\y = *T\y3 -*T\y1
   
   a = PRODUIT_SCALAIRE(e10, e10)
   b = PRODUIT_SCALAIRE(e10, e20)
   c = PRODUIT_SCALAIRE(e20, e20)
   vp\x = *P\x -*T\x1
   vp\y = *P\y -*T\y1
   d = PRODUIT_SCALAIRE(vp, e10)
   e = PRODUIT_SCALAIRE(vp, e20)
   x\f = (d * c) - (e * b)
   y\f = (e * a) - (d * b)
   z\f = x\f + y\f - (a * c) + (b * b)
   
   ProcedureReturn ((z\l & ~(x\l | y\l)) & $80000000)
EndProcedure

Procedure AffPoints(*T.Triangle,*P.point,mem)
  StartDrawing(ScreenOutput())
  ;/Affiche le triangle
  Circle(*T\X1,*T\Y1,4, #Red)
  Circle(*T\X2,*T\Y2,4, #Red)
  Circle(*T\X3,*T\Y3,4, #Red)
  LineXY(*T\X1,*T\Y1,*T\X2,*T\Y2, #Red)
  LineXY(*T\X2,*T\Y2,*T\X3,*T\Y3, #Red)
  LineXY(*T\X1,*T\Y1,*T\X3,*T\Y3, #Red)

  If CollisionTriangle(*T,*P)            ;<<<<<<<<<<<<<<<<<<<<< CHANGE HERE <<<<<<<<<<<<<<<
    FillArea(*P\x,*P\y,#Red,#Blue)
  EndIf
 
  ;/Affiche une croix pour mieux suivre le déplacement du point
  LineXY(*P\x,0,*P\x,ScreenHeight-1,#White)
  LineXY(0,*P\y,ScreenWidth-1,*P\y,#White)
  ;/Affiche le point
  If mem
    DrawingMode(4)
    Circle(*P\x,*P\y,6, #White)
  Else
    DrawingMode(0)
    Circle(*P\x,*P\y,4, #White)
  EndIf
  DrawText(0,0,texte$)
  StopDrawing()
EndProcedure
Procedure TestPoint(X1,Y1,X2,Y2,d)
  If X1>X2-d And X1<X2+d And Y1>Y2-d And Y1<Y2+d
    Resultat=#True
  EndIf
  ProcedureReturn Resultat
EndProcedure

Triangle.Triangle
Point.point
;Triangle modifiable à la souris
Triangle\X1=50
Triangle\Y1=50
Triangle\X2=200
Triangle\Y2=400
Triangle\X3=730
Triangle\Y3=150
;Point à tester
Point\x=340
Point\y=100
DiametreSelection=6

Repeat
 While WindowEvent():Wend
  ClearScreen(0)
  ExamineKeyboard()
  ExamineMouse()
  ;Le triangle est modifiable à la souris en cliquant sur un point
  If MouseButton(1)
    If MemPoint=1
      Triangle\X1=MouseX()
      Triangle\Y1=MouseY()
    ElseIf MemPoint=2
      Triangle\X2=MouseX()
      Triangle\Y2=MouseY()
    ElseIf MemPoint=3
      Triangle\X3=MouseX()
      Triangle\Y3=MouseY()
    EndIf
  Else
    MemPoint=0
  EndIf
    If TestPoint(MouseX(),MouseY(),Triangle\X1,Triangle\Y1,DiametreSelection)
        MemPoint=1
    ElseIf TestPoint(MouseX(),MouseY(),Triangle\X2,Triangle\Y2,DiametreSelection)
      MemPoint=2
    ElseIf TestPoint(MouseX(),MouseY(),Triangle\X3,Triangle\Y3,DiametreSelection)
      MemPoint=3
    EndIf
  ;Place le point à tester sous la souris
  Point\x=MouseX()
  Point\y=MouseY()
  ;Affiche le tout
  AffPoints(@Triangle,@Point,MemPoint)
  FlipBuffers()
  Delay(1)
Until KeyboardPushed(#PB_Key_Escape)

Posted: Sat Jul 07, 2007 12:30 pm
by Psychophanta
My suggested function:

Code: Select all

Procedure.b IsPointInsideTriangle2D(x0.f,y0.f,x1.f,y1.f,x2.f,y2.f,px.f,py.f)
  Protected V1x.f=x1-x0,V1y.f=y1-y0
  Protected V2x.f=x2-x0,V2y.f=y2-y0
  Protected V3x.f=x1-x2,V3y.f=y1-y2
  Protected px0.f=px-x0,py0.f=py-y0
  Protected px1.f=px-x1,py1.f=py-y1
  Protected px2.f=px-x2,py2.f=py-y2
  If V1y*V2x>V1x*V2y:V1x=-V1x:V1y=-V1y
  Else:V2x=-V2x:V2y=-V2y:V3x=-V3x:V3y=-V3y
  EndIf
  If py0*V2x>px0*V2y And py1*V1x>px1*V1y And py2*V3x>px2*V3y
    ProcedureReturn 1
  EndIf
  ProcedureReturn 0
EndProcedure
Working example:

Code: Select all

;realtime determines wether a point is inside any triangle (only 2D plane):
; Author: Psychophanta
; Date: 7 July 2007

;-INITS:
Define.f
SCREENWIDTH.l=GetSystemMetrics_(#SM_CXSCREEN):SCREENHEIGHT.l=GetSystemMetrics_(#SM_CYSCREEN)
bitplanes.b=32
If InitSprite()=0 Or InitMouse()=0 Or InitKeyboard()=0
  MessageRequester("Error","Can't initialize",0):End
EndIf
While OpenScreen(SCREENWIDTH,SCREENHEIGHT,bitplanes.b,"")=0
  If bitplanes.b>16:bitplanes.b-8
  ElseIf SCREENHEIGHT>600:SCREENWIDTH=800:SCREENHEIGHT=600
  ElseIf SCREENHEIGHT>480:SCREENWIDTH=640:SCREENHEIGHT=480
  ElseIf SCREENHEIGHT>400:SCREENWIDTH=640:SCREENHEIGHT=400
  ElseIf SCREENHEIGHT>240:SCREENWIDTH=320:SCREENHEIGHT=240
  ElseIf SCREENHEIGHT>200:SCREENWIDTH=320:SCREENHEIGHT=200
  Else:MessageRequester("Listen:","Can't open Screen!",0):End
  EndIf
Wend

Procedure.b IsPointInsideTriangle2D(x0.f,y0.f,x1.f,y1.f,x2.f,y2.f,px.f,py.f)
  Protected V1x.f=x1-x0,V1y.f=y1-y0
  Protected V2x.f=x2-x0,V2y.f=y2-y0
  Protected V3x.f=x1-x2,V3y.f=y1-y2
  Protected px0.f=px-x0,py0.f=py-y0
  Protected px1.f=px-x1,py1.f=py-y1
  Protected px2.f=px-x2,py2.f=py-y2
  If V1y*V2x>V1x*V2y:V1x=-V1x:V1y=-V1y
  Else:V2x=-V2x:V2y=-V2y:V3x=-V3x:V3y=-V3y
  EndIf
  If py0*V2x>px0*V2y And py1*V1x>px1*V1y And py2*V3x>px2*V3y
    ProcedureReturn 1
  EndIf
  ProcedureReturn 0
EndProcedure

CreateSprite(0,16,16);<-The mouse cursor
StartDrawing(SpriteOutput(0)):BackColor(0)
Line(0,0,15,10,$CABE2A)
Line(0,0,5,15,$CABE2A)
LineXY(5,15,15,10,$CABE2A)
FillArea(2,2,$CABE2A,$C0C1D0)
StopDrawing()
;-MAIN:
x0.f=213:y0.f=345
x1.f=782:y1.f=286
x2.f=135:y2.f=286
px.f=100:py.f=100
Repeat
  ClearScreen(0)
  ExamineKeyboard()
  ExamineMouse():mx.l=MouseX():my.l=MouseY()
  a.f=Sqr(Pow(x0-mx,2)+Pow(y0-my,2))
  b.f=Sqr(Pow(x1-mx,2)+Pow(y1-my,2))
  c.f=Sqr(Pow(x2-mx,2)+Pow(y2-my,2))
  d.f=Sqr(Pow(px-mx,2)+Pow(py-my,2))
  If MouseButton(#PB_MouseButton_Left)
    If a<b And a<c And a<d
      x0+MouseDeltaX():y0+MouseDeltaY():x.f=x0:y.f=y0
    ElseIf b<a And b<c And b<d
      x1+MouseDeltaX():y1+MouseDeltaY():x.f=x1:y.f=y1
    ElseIf c<a And c<b And c<d
      x2+MouseDeltaX():y2+MouseDeltaY():x.f=x2:y.f=y2
    Else:px+MouseDeltaX():py+MouseDeltaY()
    EndIf
  EndIf
  StartDrawing(ScreenOutput()):DrawingMode(1)
  Circle(x,y,4,$3399AA)
  Circle(px,py,4,$88bbAA)
  FrontColor($dedebc):DrawText(0,20,Str(IsPointInsideTriangle2D(x0.f,y0.f,x1.f,y1.f,x2.f,y2.f,px.f,py.f)))
  LineXY(x0,y0,x1,y1,$913FAF)
  LineXY(x1,y1,x2,y2,$913FAF)
  LineXY(x2,y2,x0,y0,$913FAF)
  FrontColor($6f6f6f)
  DrawText(x0,y0,"(x0,y0)")
  DrawText(x1,y1,"(x1,y1)")
  DrawText(x2,y2,"(x2,y2)")
  StopDrawing()
  DisplayTransparentSprite(0,mx,my)
  FlipBuffers(0):Delay(16)
Until KeyboardPushed(#PB_Key_Escape)

Posted: Sat Jul 07, 2007 3:48 pm
by SCRJ
Nice functions! :D


Little question:
How can I determine whether the triangle collides with a box or not?

Posted: Sat Jul 07, 2007 3:58 pm
by Psychophanta
SCRJ wrote: How can I determine whether the triangle collides with a box or not?
That doesn't require any math knowledge, just use common sense to get it 8)

Posted: Sat Jul 07, 2007 6:12 pm
by Fou-Lu
Psychophanta wrote:
SCRJ wrote: How can I determine whether the triangle collides with a box or not?
That doesn't require any math knowledge, just use common sense to get it 8)
LOL :lol:

Psychophanta thanks a lot, your example is wonderful! I can't believe that procedure is so small! Thanks everybody for the other examples but I'll stick with the simplest one hehehe :wink:

(By the way, I didn't know you could make commentaries appear in the procedures window :shock: That's VERY useful. )

Posted: Sat Jul 07, 2007 7:08 pm
by SCRJ
>>That doesn't require any math knowledge, just use common sense to get it

LOL, I had a little blackout :lol:

Posted: Sat Jul 07, 2007 7:09 pm
by Psychophanta

Code: Select all

Procedure.b IsPointInsideTriangle2D(x0.f,y0.f,x1.f,y1.f,x2.f,y2.f,px.f,py.f)
  ;lets build 3 2D vectors which are the 3 sides of the triangle:
  Protected V1x.f=x1-x0,V1y.f=y1-y0,V2x.f=x2-x0,V2y.f=y2-y0,V3x.f,V3y.f
  ;Now make them 3 to point clockwise while we are watching at the triangle:
  If V1y*V2x>V1x*V2y:V1x=-V1x:V1y=-V1y:V3x.f=x1-x2:V3y.f=y1-y2
  Else:V2x=-V2x:V2y=-V2y:V3x.f=x2-x1:V3y.f=y2-y1
  EndIf
  ;We define now 3 point vectors which origin is each triangle vertex, and which end is the point coordenate.
  ;Then lets see, if each respective point vector is AT THE RIGHT SIDE of each side vector, then the point is inside, else it doesn't:
  If (py-y0)*V2x>(px-x0)*V2y And (py-y1)*V1x>(px-x1)*V1y And (py-y2)*V3x>(px-x2)*V3y
    ProcedureReturn 1
  EndIf
  ProcedureReturn 0
EndProcedure
@Fou-Lu, here it is a more summed-up version of the same function.
I just work with vectorial geometry, and since PB editor doesn't allow to include images in the source code, i can not explain it very good. However i have commented the code.
@SCRJ, if you referred to rectangles with any inclination, then ask for it again and i will give a function for it. If you referred to a box like Box() PB function does, then the answer is the above. :wink:

Posted: Sat Jul 07, 2007 11:23 pm
by Comtois
SCRJ wrote: Little question:
How can I determine whether the triangle collides with a box or not?
You can use separating axis collision detection

It work with any convex polygon.

Posted: Mon Jul 09, 2007 1:04 am
by citystate
using either Procedure, consider a box as two triangles,
  • * check to see if any of the triangle points are inside either of the box-triangles
    * check to see if any of the box corners are inside the triangle
    * repeat both of the above steps with the triangle midpoints
I think this will cover most cases...

Posted: Sun Jun 29, 2008 12:56 pm
by Psychophanta
An answer from
http://www.purebasic.fr/english/viewtopic.php?t=32998
i.e. Triangle-circle clipping in a 2D world (modify circle radius using mouse wheel):

Code: Select all

;realtime determines whether a circle is clipping a triangle (2D only):
; Author: Psychophanta
; Date: June 2008

;-INITS:
Define.f
SCREENWIDTH.l=GetSystemMetrics_(#SM_CXSCREEN):SCREENHEIGHT.l=GetSystemMetrics_(#SM_CYSCREEN)
bitplanes.b=32
If InitSprite()=0 Or InitMouse()=0 Or InitKeyboard()=0
  MessageRequester("Error","Can't initialize",0):End
EndIf
While OpenScreen(SCREENWIDTH,SCREENHEIGHT,bitplanes.b,"")=0
  If bitplanes.b>16:bitplanes.b-8
  ElseIf SCREENHEIGHT>600:SCREENWIDTH=800:SCREENHEIGHT=600
  ElseIf SCREENHEIGHT>480:SCREENWIDTH=640:SCREENHEIGHT=480
  ElseIf SCREENHEIGHT>400:SCREENWIDTH=640:SCREENHEIGHT=400
  ElseIf SCREENHEIGHT>240:SCREENWIDTH=320:SCREENHEIGHT=240
  ElseIf SCREENHEIGHT>200:SCREENWIDTH=320:SCREENHEIGHT=200
  Else:MessageRequester("Listen:","Can't open Screen!",0):End
  EndIf
Wend
Structure Vector2D
  x.f:y.f
  StructureUnion
    modulo.f:radius.f:any.f
  EndStructureUnion
EndStructure
Structure Triangle2D
  x0.f:y0.f
  x1.f:y1.f
  x2.f:y2.f
EndStructure
Procedure.b IsCircleClippingTriangle2D(*t.Triangle2D,*c.Vector2D)
  ;We define 3 vectors which origin is each triangle vertex, and end is the circle center.
  Protected d1.Vector2D,d2.Vector2D,d3.Vector2D
  d1\x=*c\x-*t\x0:d1\y=*c\y-*t\y0
  d2\x=*c\x-*t\x1:d2\y=*c\y-*t\y1
  d3\x=*c\x-*t\x2:d3\y=*c\y-*t\y2
  ;Collided to vertex?:
  ;lets see if radius is enough long:
  If *c\radius>=Sqr(d1\x*d1\x+d1\y*d1\y):ProcedureReturn 4:EndIf
  ;lets see if radius is enough long:
  If *c\radius>=Sqr(d2\x*d2\x+d2\y*d2\y):ProcedureReturn 5:EndIf
  ;lets see if radius is enough long:
  If *c\radius>=Sqr(d3\x*d3\x+d3\y*d3\y):ProcedureReturn 6:EndIf
  ;build 3 vectors which are the 3 sides of the triangle:
  Protected v1.Vector2D,v2.Vector2D,v3.Vector2D
  v1\x=*t\x1-*t\x0:v1\y=*t\y1-*t\y0
  v2\x=*t\x2-*t\x1:v2\y=*t\y2-*t\y1
  v3\x=*t\x0-*t\x2:v3\y=*t\y0-*t\y2
  v1\any=v1\x*v1\x+v1\y*v1\y
  v2\any=v2\x*v2\x+v2\y*v2\y
  v3\any=v3\x*v3\x+v3\y*v3\y
  d1\any=d1\x*v1\x+d1\y*v1\y
  d2\any=d2\x*v2\x+d2\y*v2\y
  d3\any=d3\x*v3\x+d3\y*v3\y
  ;Collided to side?:
  If *c\radius>=Abs(d1\x*v1\y-d1\y*v1\x)/Sqr(v1\any) And d1\any>=0 And d1\any<=v1\any
    ProcedureReturn 1
  EndIf
  If *c\radius>=Abs(d2\x*v2\y-d2\y*v2\x)/Sqr(v2\any) And d2\any>=0 And d2\any<=v2\any
    ProcedureReturn 2
  EndIf
  If *c\radius>=Abs(d3\x*v3\y-d3\y*v3\x)/Sqr(v3\any) And d3\any>=0 And d3\any<=v3\any
    ProcedureReturn 3
  EndIf
  ProcedureReturn 0
EndProcedure
CreateSprite(0,16,16);<-The mouse cursor
StartDrawing(SpriteOutput(0)):BackColor(0)
Line(0,0,15,10,$CABE2A)
Line(0,0,5,15,$CABE2A)
LineXY(5,15,15,10,$CABE2A)
FillArea(2,2,$CABE2A,$C0C1D0)
StopDrawing()
;-MAIN:
triangle.Triangle2D\x0=Random(SCREENWIDTH):triangle.Triangle2D\y0=Random(SCREENHEIGHT)
triangle.Triangle2D\x1=Random(SCREENWIDTH):triangle.Triangle2D\y1=Random(SCREENHEIGHT)
triangle.Triangle2D\x2=Random(SCREENWIDTH):triangle.Triangle2D\y2=Random(SCREENHEIGHT)
x.f=triangle\x0:y.f=triangle\y0
circle.Vector2D\x=Random(SCREENWIDTH):circle.Vector2D\y=Random(SCREENHEIGHT)
circle\radius=20
Repeat
  result.b=IsCircleClippingTriangle2D(@triangle.Triangle2D,@circle.Vector2D)
  ClearScreen($F0*result.b)
  ExamineKeyboard()
  ExamineMouse():mx.l=MouseX():my.l=MouseY()
  circle\radius+MouseWheel()
  a.f=Sqr(Pow(triangle\x0-mx,2)+Pow(triangle\y0-my,2))
  b.f=Sqr(Pow(triangle\x1-mx,2)+Pow(triangle\y1-my,2))
  c.f=Sqr(Pow(triangle\x2-mx,2)+Pow(triangle\y2-my,2))
  d.f=Sqr(Pow(circle\x-mx,2)+Pow(circle\y-my,2))
  If MouseButton(#PB_MouseButton_Left)
    If a<b And a<c And a<d
      triangle\x0+MouseDeltaX():triangle\y0+MouseDeltaY():x.f=triangle\x0:y.f=triangle\y0
    ElseIf b<a And b<c And b<d
      triangle\x1+MouseDeltaX():triangle\y1+MouseDeltaY():x.f=triangle\x1:y.f=triangle\y1
    ElseIf c<a And c<b And c<d
      triangle\x2+MouseDeltaX():triangle\y2+MouseDeltaY():x.f=triangle\x2:y.f=triangle\y2
    Else:circle\x+MouseDeltaX():circle\y+MouseDeltaY()
    EndIf
  EndIf
  StartDrawing(ScreenOutput()):DrawingMode(#PB_2DDrawing_Transparent)
  Circle(x,y,4,$3399AA)
  Circle(circle\x,circle\y,circle\radius,$88bbAA)
  FrontColor($dedebc):DrawText(0,20,Str(result))
  LineXY(triangle\x0,triangle\y0,triangle\x1,triangle\y1,$913FAF)
  LineXY(triangle\x1,triangle\y1,triangle\x2,triangle\y2,$913FAF)
  LineXY(triangle\x2,triangle\y2,triangle\x0,triangle\y0,$913FAF)
  FrontColor($6f6f6f)
  DrawText(triangle\x0,triangle\y0,"(x0,y0)")
  DrawText(triangle\x1,triangle\y1,"(x1,y1)")
  DrawText(triangle\x2,triangle\y2,"(x2,y2)")
  StopDrawing()
  DisplayTransparentSprite(0,mx,my)
  FlipBuffers(0):Delay(16)
Until KeyboardPushed(#PB_Key_Escape)
EDIT:
Improved the above function IsPointInsideTriangle2D():

Code: Select all

;funtion to determine whether a point is inside or outside a triangle in a 2D world: 
; Author: Psychophanta 
; Date: June 2008
Procedure.b IsPointInsideTriangle2D(x0.f,y0.f,x1.f,y1.f,x2.f,y2.f,px.f,py.f)
  ;lets build 3 2D vectors which are the 3 sides of the triangle:
  Protected V1x.f=x1-x0,V1y.f=y1-y0,V2x.f=x2-x1,V2y.f=y2-y1,V3x.f=x0-x2:V3y.f=y0-y2
  ;We define now 3 point vectors which origin is each triangle vertex, and which end is the point coordenate.
  ;Then lets see, if every respective point vector is AT THE SAME HAND of each side vector, then the point is inside, else it doesn't:
  If V1y*(px-x0)>=V1x*(py-y0)
    If V2y*(px-x1)>=V2x*(py-y1) And V3y*(px-x2)>=V3x*(py-y2):ProcedureReturn 1:EndIf
  Else
    If V2y*(px-x1)<V2x*(py-y1) And V3y*(px-x2)<V3x*(py-y2):ProcedureReturn 1:EndIf
  EndIf
  ProcedureReturn 0
EndProcedure
:)