Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Triangle-Based Collision Detection
#1
Hi,

Here’s a program that explores a particular approach to collision detection between shapes made of triangles. It’s based on detecting triangle overlap, which makes it possible to determine which parts of a shape are involved in a collision; this could inspire game scenarios — for example, collisions that destroy only part of a shape or cause it to deform. 

There may still be some cases where the detection isn't perfect; do you see any?  Cool

Performance is stable, and it holds 60 FPS on my modest setup with 250 small shapes.

Note also that half the code is dedicated to automatically generating shapes by assembling triangles.

Code: (Select All)
$console

type point_type
    x as double
    y as double
end type

type segment_type
    a as point_type
    b as point_type
end type

type triangle_type
    position as point_type
    angle as double
    hauteur as double
    demibase as point_type
    a as point_type
    b as point_type
    c as point_type
    center as point_type
    realA as point_type
    realB as point_type
    realC as point_type
    realCenter as point_type
    realMin as point_type
    realMax as point_type
    collid as integer
end type

type shape_type
    firstTriangleIndex as integer
    lastTriangleIndex as integer
    pointsUsageIndicator as string
    position as point_type
    firstSegmentIndex as integer
    lastSegmentIndex as integer
    center as point_type
    radius as double
    direction as double
    velocity as double
    orientation as double
    rotation as double
    shapeColor as _unsigned long
    borderColor as _unsigned long
    collisionColor as _unsigned long
end type

dim as double ix,iy

const TAU = 8 * atn(1)
const VIEWPORT_WIDTH = 1000 '800 '1950
const VIEWPORT_HEIGHT = 800 '600 '1080
const WORLD_WIDTH = VIEWPORT_WIDTH + 100
const WORLD_HEIGHT = VIEWPORT_HEIGHT + 100
const WORLD_MINX = ( VIEWPORT_WIDTH - WORLD_WIDTH ) \ 2
const WORLD_MAXX = - WORLD_MINX + VIEWPORT_WIDTH
const WORLD_MINY = ( VIEWPORT_HEIGHT - WORLD_HEIGHT  ) \ 2
const WORLD_MAXY = - WORLD_MINY + VIEWPORT_HEIGHT
const SHAPES_COUNT = 250
const TRIANGLES_IN_SHAPE_MIN = 3
const TRIANGLES_IN_SHAPE_MAX = 15
const TRIANGLE_BASE_MIN = 10
const TRIANGLE_BASE_MAX = 20 'TRIANGLE_BASE_MIN
const TRIANGLE_HEIGHT_MIN = 7 'TRIANGLE_BASE_MIN * 0.86602540378444
const TRIANGLE_HEIGHT_MAX = 13 'TRIANGLE_HEIGHT_MIN
const ATTEMPT_FPS = 60
const FULL_SCREEN = 0

randomize timer

redim segments(-1) as segment_type
redim triangles(-1) as triangle_type
redim shapes(-1) as shape_type

_title "meshes and triangles collisions"

_dest _console

? using "generate #### shapes";SHAPES_COUNT

t0# = timer(.001!)
for i% = 1 to SHAPES_COUNT
    generateShape _
        shapes(), triangles(), _
        TRIANGLES_IN_SHAPE_MIN + (TRIANGLES_IN_SHAPE_MAX - TRIANGLES_IN_SHAPE_MIN)*rnd, _
        TRIANGLE_BASE_MIN, TRIANGLE_BASE_MAX,TRIANGLE_HEIGHT_MIN, TRIANGLE_HEIGHT_MAX
    shapes(i%-1).position.x = VIEWPORT_WIDTH*rnd
    shapes(i%-1).position.y = VIEWPORT_HEIGHT*rnd
    shapes(i%-1).direction = TAU*rnd
    shapes(i%-1).velocity = 1 - 2*rnd
    shapes(i%-1).orientation = 0
    shapes(i%-1).rotation = 0.01 - 0.02*rnd
    getBorderSegments shapes(i%-1),triangles(),segments()
    computeShapeCenter shapes(i%-1), segments()
next i%
t1# = timer(.001!)

? using "create ##### triangles";ubound(triangles)
? using "done in ##.###s";(t1# - t0#)

'h& = _dest : _dest _console : ? ubound(shapes),ubound(triangles) : _dest h&

screen _newimage(VIEWPORT_WIDTH,VIEWPORT_HEIGHT,32)
if FULL_SCREEN then _fullscreen _squarepixels, _smooth
ROWS = VIEWPORT_HEIGHT/_fontheight
COLUMNS = VIEWPORT_WIDTH/_fontwidth
TRIANGLES_COUNT = shapes(ubound(shapes)).lastTriangleIndex

fps% = ATTEMPT_FPS
t# = timer(.001)
do
    _limit ATTEMPT_FPS

    frames% = frames% + 1
    if timer(.001)>=t#+1 then
        fps% = frames%
        frames% = 0
        t# = timer(.001)
    end if

    do
    keycode = inp(96)
    loop until keycode = 31 or keycode = 16 or keycode = 1 or keycode = 147 or keycode = 129

    _dest 0
    cls , &H00000000
    color &HFFFFFFFF, &H00000000

    ' reset triangles collision indicator

    for i% = lbound(triangles) to ubound(triangles)
        triangles(i%).collid = 0
    next i%

    ' shape moves

    for i% = lbound(shapes) to ubound(shapes)
        movingShape shapes(i%), triangles()
    next i%

    ' test shapes collisions

    trianglesCollisions% = 0
    shapesCollisions% = 0

    for i% = lbound(shapes) to ubound(shapes)
        for j% = i%+1 to ubound(shapes)
            if abs(shapes(i%).position.x - shapes(j%).position.x) > (shapes(i%).radius + shapes(j%).radius) _orelse _
              abs(shapes(i%).position.y - shapes(j%).position.y) > (shapes(i%).radius + shapes(j%).radius) then
            else
                dc% = detectCollision(shapes(i%), shapes(j%), triangles())
                if dc% then
                    trianglesCollisions% = trianglesCollisions% + dc%
                    shapesCollisions% = shapesCollisions% + 1
                end if
            end if
        next j%
    next i%

    ' draw shapes

    for i% = lbound(shapes) to ubound(shapes)
        drawShape shapes(i%), triangles(), segments()
    next i%

    ' display text informations

    locate 1,2          : ? "Q)uit  -  S)teps -  R)esume"
    locate 1,COLUMNS-66 : ? using "FPS:##  -  Shapes clashes:###/###  -  Triangles clashes:####/####";fps%;shapesCollisions%;SHAPES_COUNT;trianglesCollisions%;TRIANGLES_COUNT
    _display

loop until keycode = 16 or keycode = 1
system

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

' detect collision beetween two shapes

function detectCollision%(shapeA as shape_type, shapeB as shape_type, triangles() as triangle_type)
    newCollisions% = 0
    for i% = shapeA.firstTriangleIndex to shapeA.lastTriangleIndex
        for j% = shapeB.firstTriangleIndex to shapeB.lastTriangleIndex
            if triangles(j%).realMax.x < triangles(i%).realMin.x _orelse _
              triangles(j%).realMin.x > triangles(i%).realMax.x _orelse _
              triangles(j%).realMax.y < triangles(i%).realMin.y _orelse _
              triangles(j%).realMin.y > triangles(i%).realMax.y then
            else
                if TrianglesCollide(triangles(j%),triangles(i%)) then
                    if not triangles(i%).collid then
                        triangles(i%).collid = -1
                        newCollisions% = newCollisions% + 1
                    end if
                    if not triangles(j%).collid then
                        triangles(j%).collid = -1
                        newCollisions% = newCollisions% + 1
                    end if
              end if
            end if
        next j%
    next i%
    detectCollision% = newCollisions%
end function

' scalar product

function dotProduct#(p as point_type, q as point_type)
  dotProduct# = p.x * q.x + p.y * q.y
end function

' test triangles collision

function trianglesCollide%(A as triangle_type, B as triangle_type)
  dim t as integer, i as integer, j as integer
  dim p1 as point_type, p2 as point_type
  dim edge as point_type, axis as point_type
  dim minA as double, maxA as double, minB as double, maxB as double
  dim vertsA(2) as point_type, vertsB(2) as point_type
  const INF = 1e+308
  const EPS = 1e-9

  ' vertices
  vertsA(0) = A.realA: vertsA(1) = A.realB: vertsA(2) = A.realC
  vertsB(0) = B.realA: vertsB(1) = B.realB: vertsB(2) = B.realC

  ' triangles comparaison
  for t = 0 to 1
    for i = 0 to 2
      if t = 0 then
        p1 = vertsA(i)
        p2 = vertsA((i + 1) mod 3)
      else
        p1 = vertsB(i)
        p2 = vertsB((i + 1) mod 3)
      end if

      edge.x = p2.x - p1.x
      edge.y = p2.y - p1.y
      axis.x = -edge.y
      axis.y = edge.x

      ' projection A triangle
      minA = INF: maxA = -INF
      for j = 0 to 2
        proj = DotProduct(vertsA(j), axis)
        if proj < minA then minA = proj
        if proj > maxA then maxA = proj
      NEXT j

      ' projection B triangle
      minB = INF: maxB = -INF
      for j = 0 to 2
        proj = DotProduct(vertsB(j), axis)
        if proj < minB then minB = proj
        if proj > maxB then maxB = proj
      next j

      ' no intersection beetween A and B
      if maxA < minB - EPS OR maxB < minA - EPS then
        trianglesCollide% = 0
        exit function
      end if
    next i
  next t

  ' A and B intersect
  trianglesCollide% = -1
end function

' move and rotate shape

sub movingShape(shape as shape_type,triangles() as triangle_type)
    shape.position.x = shape.position.x + cos(shape.direction) * shape.velocity
    if shape.position.x < WORLD_MINX then
        shape.position.x = shape.position.x - WORLD_MINX + WORLD_MAXX
    elseif shape.position.x > WORLD_MAXX then
        shape.position.x = shape.position.x + WORLD_MINX - WORLD_MAXX
    end if
    shape.position.y = shape.position.y - sin(shape.direction) * shape.velocity
    if shape.position.y < WORLD_MINY then
        shape.position.y = shape.position.y - WORLD_MINY + WORLD_MAXY
    elseif shape.position.y > WORLD_MAXY then
        shape.position.y = shape.position.y + WORLD_MINY - WORLD_MAXY
    end if
    shape.orientation = shape.orientation + shape.rotation
    for i% = shape.firstTriangleIndex to shape.lastTriangleIndex
        computeRealPositions triangles(i%),shape.position, shape.center, shape.orientation
    next i%
end sub

' compute the screen position for a triangle base on shape position, shape center and shape orientation

sub computeRealPositions(t as triangle_type,p as point_type, axe as point_type, angle as double)
    dim as point_type a, b, c, center
    a.x = t.a.x + t.position.x
    a.y = t.a.y + t.position.y
    b.x = t.b.x + t.position.x
    b.y = t.b.y + t.position.y
    c.x = t.c.x + t.position.x
    c.y = t.c.y + t.position.y
    center.x = t.center.x + t.position.x
    center.y = t.center.y + t.position.y
    rotation a, axe, angle
    rotation b, axe, angle
    rotation c, axe, angle
    rotation center, axe, angle
    t.realA.x = p.x + a.x
    t.realA.y = p.y + a.y
    t.realB.x = p.x + b.x
    t.realB.y = p.y + b.y
    t.realC.x = p.x + c.x
    t.realC.y = p.y + c.y
    t.realCenter.x = p.x + center.x
    t.realCenter.y = p.y + center.y
    t.realMin.x = _min(_min(t.realA.x,t.realB.x),t.realC.x)
    t.realMin.y = _min(_min(t.realA.y,t.realB.y),t.realC.y)
    t.realMax.x = _max(_max(t.realA.x,t.realB.x),t.realC.x)
    t.realMax.y = _max(_max(t.realA.y,t.realB.y),t.realC.y)
end sub

' compute shape center base on boundary edges (border segments)

sub computeShapeCenter(shape as shape_type, segments() as segment_type)
    dim as double x, y, radius
    for i% = shape.firstSegmentIndex to shape.lastSegmentIndex
        x = x + segments(i%).a.x + segments(i%).b.x
        y = y + segments(i%).a.y + segments(i%).b.y
    next i%
    d% = 2 * (shape.lastSegmentIndex - shape.firstSegmentIndex + 1)
    shape.center.x = x/d%
    shape.center.y = y/d%
    radius = 0
    for i% = shape.firstSegmentIndex to shape.lastSegmentIndex
        radius = _max(radius,_hypot(segments(i%).a.x - shape.center.x, segments(i%).a.y - shape.center.y))
        radius = _max(radius,_hypot(segments(i%).b.x - shape.center.x, segments(i%).b.y - shape.center.y))
    next i%
    shape.radius = radius
end sub

' get boundary edges (border segments) of a shape

sub getBorderSegments(shape as shape_type, triangles() as triangle_type, segments() as segment_type)
    dim segmentCount%
    dim shard%
    dim s(1 to 3) as segment_type

    ' explore every triangle of the shape
    for i% = shape.firstTriangleIndex to shape.lastTriangleIndex

        ' get the 3 segments of the triangle
        s(1).a = triangles(i%).a : s(1).b = triangles(i%).b
        s(2).a = triangles(i%).b : s(2).b = triangles(i%).c
        s(3).a = triangles(i%).c : s(3).b = triangles(i%).a
       
        ' test segments
        for j% = 1 to 3
           
            ' a common segment with another triangle is not a boundary edge of the shape
            for k% = shape.firstTriangleIndex to shape.lastTriangleIndex
                if k% <> i% then
                    shard% = isSegmentShared(s(j%), triangles(k%))
                    if shard% then
                        exit for
                    end if
                end if
            next k%
           
            ' if the segment is not shared, it is a boundary edge.
            if not shard% then
                redim _preserve segments(0 to ubound(segments)+1) as segment_type
                segments(ubound(segments)) = s(j%)
                segmentCount% = segmentCount% + 1
            end if
        next j%
    next i%
   
    shape.firstSegmentIndex = ubound(segments)-segmentCount%+1
    shape.lastSegmentIndex = ubound(segments)
end sub

' check if a segment belongs to the triangle

function isSegmentShared%(s as segment_type, t as triangle_type)
    if (isPointEqual(s.a, t.a) and isPointEqual(s.b, t.b)) or _
      (isPointEqual(s.a, t.b) and isPointEqual(s.b, t.c)) or _
      (isPointEqual(s.a, t.c) and isPointEqual(s.b, t.a)) or _
      (isPointEqual(s.b, t.a) and isPointEqual(s.a, t.b)) or _
      (isPointEqual(s.b, t.b) and isPointEqual(s.a, t.c)) or _
      (isPointEqual(s.b, t.c) and isPointEqual(s.a, t.a)) then
        result% = -1
    else
        result% = 0
    end if
    isSegmentShared% = result%
end function

' compare 2 points

function isPointEqual%(p1 as point_type, p2 as point_type)
    const EPSILON = 0.0001
    isPointEqual% = (abs(p1.x - p2.x) < EPSILON and abs(p1.y - p2.y) < EPSILON)
end function

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

' rotate a point around an axis

sub rotation(a as point_type,axe as point_type,angle as double)
    ax0 = a.x - axe.x
    ay0 = a.y - axe.y
    a.x = ax0 * cos(angle) - ay0 * sin(angle) + axe.x
    a.y = ax0 * sin(angle) + ay0 * cos(angle) + axe.y
end sub

' draw a shape

sub drawShape(shape as shape_type,triangles() as triangle_type,segments() as segment_type)
    dim s as segment_type
    for i% = shape.firstSegmentIndex to shape.lastSegmentIndex
        s = segments(i%)
        rotation s.a, shape.center, shape.orientation
        rotation s.b, shape.center, shape.orientation
        pset (s.a.x + shape.position.x, s.a.y + shape.position.y), shape.borderColor
        line -(s.b.x + shape.position.x, s.b.y + shape.position.y), shape.borderColor
    next i%
    for i% = shape.firstTriangleIndex to shape.lastTriangleIndex
        drawTriangle triangles(i%),shape.shapeColor,shape.collisionColor
    next i%
end sub

' draw a triangle

sub drawTriangle(t as triangle_type,shapeColor as _unsigned long,collisionColor as _unsigned long)
    c& = _iif(t.collid,collisionColor,shapeColor)
    pset ( t.realA.x, t.realA.y ), c&
    line -( t.realB.x, t.realB.y ), c&
    line -( t.realC.x, t.realC.y ),c&
    line -( t.realA.x, t.realA.y ), c&
end sub

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

' create a new shape

sub generateShape(shapes() as shape_type, triangles() as triangle_type, nbr%, baseMin%, baseMax%, hauteurMin%, hauteurMax%)
    ' Allocation unique pour le shape
    redim _preserve shapes(0 to ubound(shapes) + 1) as shape_type
    dim shape as shape_type
    dim t as triangle_type

    ' Générer le premier triangle
    generateTriangle t, baseMin%, baseMax%, hauteurMin%, hauteurMax%
    redim _preserve triangles(0 to ubound(triangles) + 1) as triangle_type
    triangles(ubound(triangles)) = t
    shape.firstTriangleIndex = ubound(triangles)
    shape.lastTriangleIndex = ubound(triangles)
    shape.pointsUsageIndicator = "000"
    shape.shapeColor = _rgba32(rnd*92+127,rnd*92+127,rnd*92+127,&H70)
    shape.borderColor = shape.shapeColor OR &HFF000000
    shape.collisionColor = &HFFFF0000

    ' Variables réutilisées pour éviter les allocations répétées
    dim pt1 as point_type, pt2 as point_type, pt3 as point_type, pt0 as point_type
    dim a as point_type, b as point_type, c as point_type
    dim bs%, p%, i%, cnt%

    ' Générer les autres triangles
    do while nbr% > 1
        bs% = chooseBorderSegment(shape, triangles())
        p% = bs% mod 3
        i% = bs% \ 3
        a = triangles(shape.firstTriangleIndex + i%).a
        b = triangles(shape.firstTriangleIndex + i%).b
        c = triangles(shape.firstTriangleIndex + i%).c
        if p% = 0 then
            pt1 = a : pt2 = b : pt3 = c
        elseif p% = 1 then
            pt1 = b : pt2 = c : pt3 = a
        else
            pt1 = c : pt2 = a : pt3 = b
        end if

        cnt% = 20
        do
            cnt% = cnt% - 1
            generateVertexOutsideTriangle pt1, pt2, pt3, hauteurMax% - rnd * (hauteurMax% - hauteurMin%), pt0
        loop while isVertexInnerShape(shape, triangles(), pt0) and cnt% > 0

        createTriangle t, pt1, pt2, pt0
        if isTriangleValid(shape, triangles(), t) then
            redim _preserve triangles(0 to ubound(triangles) + 1) as triangle_type
            triangles(ubound(triangles)) = t
            shape.lastTriangleIndex = ubound(triangles)
            shape.pointsUsageIndicator = shape.pointsUsageIndicator + "100"
        end if
        nbr% = nbr% - 1
    loop

    shapes(ubound(shapes)) = shape
end sub

' check whether a new specific triangle can be added to the shape without covering an other triangle in the shape

function isTriangleValid%(shape as shape_type, triangles() as triangle_type, triangle as triangle_type)
    dim s2(1 to 3) as segment_type
    dim s1(1 to 3) as segment_type
    ' Prepare the triangle segments to be tested
    s2(1).a = triangle.a : s2(1).b = triangle.b
    s2(2).a = triangle.b : s2(2).b = triangle.c
    s2(3).a = triangle.c : s2(3).b = triangle.a

    for i% = shape.firstTriangleIndex to shape.lastTriangleIndex
        ' Prepare the segments of the existing triangle
        s1(1).a = triangles(i%).a : s1(1).b = triangles(i%).b
        s1(2).a = triangles(i%).b : s1(2).b = triangles(i%).c
        s1(3).a = triangles(i%).c : s1(3).b = triangles(i%).a

        ' Checks segments intersection
        for k% = 1 to 3
            for l% = 1 to 3
                if checkSegmentsIntersect(s1(k%), s2(l%), ix, iy) = -1 then
                    isTriangleValid% = 0
                    exit function
                end if
            next l%
        next k%
    next i%
    isTriangleValid% = -1
end function

function checkSegmentsIntersect%(s1 as segment_type, s2 as segment_type, ix as double, iy as double)
    const epsilon = 0.001
   
    ' tests bouding boxes
    dim as double x1min, x1max, y1min, y1max, x2min, x2max, y2min, y2max
   
    if s1.a.x < s1.b.x then x1min = s1.a.x : x1max = s1.b.x else x1min = s1.b.x : x1max = s1.a.x
    if s1.a.y < s1.b.y then y1min = s1.a.y : y1max = s1.b.y else y1min = s1.b.y : y1max = s1.a.y
    if s2.a.x < s2.b.x then x2min = s2.a.x : x2max = s2.b.x else x2min = s2.b.x : x2max = s2.a.x
    if s2.a.y < s2.b.y then y2min = s2.a.y : y2max = s2.b.y else y2min = s2.b.y : y2max = s2.a.y
   
    ' no intersection
    if x1max < x2min or x2max < x1min or y1max < y2min or y2max < y1min then
        checkSegmentsIntersect% = 0
        exit function
    end if
   
    ' vertex testing
    if (abs(s1.a.x - s2.a.x) < epsilon and abs(s1.a.y - s2.a.y) < epsilon) or _
      (abs(s1.a.x - s2.b.x) < epsilon and abs(s1.a.y - s2.b.y) < epsilon) or _
      (abs(s1.b.x - s2.a.x) < epsilon and abs(s1.b.y - s2.a.y) < epsilon) or _
      (abs(s1.b.x - s2.b.x) < epsilon and abs(s1.b.y - s2.b.y) < epsilon) then
        checkSegmentsIntersect% = -3
        exit function
    end if
   
    ' compute vectors
    dim as double dx1, dy1, dx2, dy2
    dx1 = s1.b.x - s1.a.x
    dy1 = s1.b.y - s1.a.y
    dx2 = s2.b.x - s2.a.x
    dy2 = s2.b.y - s2.a.y
   
    ' test orientation of the segments
    dim as double det
    det = dx1 * dy2 - dy1 * dx2
   
    if abs(det) < epsilon then
        ' parallel or collinear segments
        if abs((s2.a.x - s1.a.x) * dy1 - (s2.a.y - s1.a.y) * dx1) < epsilon then
            ' collinear - overlap test
            if (x1min <= x2max and x2min <= x1max) and (y1min <= y2max and y2min <= y1max) then
                checkSegmentsIntersect% = -1
            else
                checkSegmentsIntersect% = 0
            end if
        else
            checkSegmentsIntersect% = 0
        end if
        exit function
    end if
   
    ' compute intersection parameters
    dim as double t1
    t1 = ((s2.a.x - s1.a.x) * dy2 - (s2.a.y - s1.a.y) * dx2) / det
    dim as double t2
    t2 = ((s2.a.x - s1.a.x) * dy1 - (s2.a.y - s1.a.y) * dx1) / det
   
    ' check if intersection is in the segments
    if t1 >= 0 and t1 <= 1 and t2 >= 0 and t2 <= 1 then
        ix = s1.a.x + t1 * dx1
        iy = s1.a.y + t1 * dy1
        checkSegmentsIntersect% = -1
    else
        checkSegmentsIntersect% = 0
    end if
end function

' check if a vertex is inside the shape

function isVertexInnerShape%(shape as shape_type, triangles() as triangle_type, vertex as point_type)
    for i% = shape.firstTriangleIndex to shape.lastTriangleIndex
        if isVertexInnerTriangle(triangles(i%), vertex) then
            isVertexInnerShape% = -1
            exit function
        end if
    next i%
    isVertexInnerShape% = 0
end function

' check if a vertex is inside the triangle

function isVertexInnerTriangle%(triangle as triangle_type, vertex as point_type)
    dim v0 as point_type
    dim v1 as point_type
    dim v2 as point_type
    dim dot00 as double, dot01 as double, dot02 as double, dot11 as double, dot12 as double
    dim invdenom as double
    dim u as double, v as double

    ' vectors
    v0.x = triangle.c.x - triangle.a.x: v0.y = triangle.c.y - triangle.a.y
    v1.x = triangle.b.x - triangle.a.x: v1.y = triangle.b.y - triangle.a.y
    v2.x = vertex.x - triangle.a.x: v2.y = vertex.y - triangle.a.y

    ' scalar products
    dot00 = v0.x * v0.x + v0.y * v0.y
    dot01 = v0.x * v1.x + v0.y * v1.y
    dot02 = v0.x * v2.x + v0.y * v2.y
    dot11 = v1.x * v1.x + v1.y * v1.y
    dot12 = v1.x * v2.x + v1.y * v2.y

    ' centroid
    invdenom = dot00 * dot11 - dot01 * dot01
    if abs(invdenom) < 1e-12 then
        isVertexInnerTriangle% = 0    ' collinear
        exit function
    end if

    invdenom = 1# / invdenom
    u = (dot11 * dot02 - dot01 * dot12) * invdenom
    v = (dot00 * dot12 - dot01 * dot02) * invdenom

    ' point inside only if u>=0, v>=0 and u+v<=1
    if u >= 0 - 1e-12 and v >= 0 - 1e-12 and u + v <= 1 + 1e-12 then
        isVertexInnerTriangle% = -1
    else
        isVertexInnerTriangle% = 0    ' à l'extérieur
    end if
end function

' select a free border segment

function chooseBorderSegment%(shape as shape_type,triangles() as triangle_type)
    l% = len(shape.pointsUsageIndicator)+1
    i% = rnd*l%
    do
        bs% = instr(i%,shape.pointsUsageIndicator,"0")
        i% = ( i% + 1 ) mod l%
    loop until bs%>0
    mid$(shape.pointsUsageIndicator,bs%,1)="1"
    chooseBorderSegment% =bs%-1
end function

' create a vertex outside the triangle

sub generateVertexOutsideTriangle (p1 as point_type, p2 as point_type, p3 as point_type, h as double, r as point_type)
    dim result as point_type
    dim v as point_type    ' ab vector
    dim l as double
    dim u as point_type    ' normal unit vetor to ab
    dim side as double

    v.x = p2.x - p1.x
    v.y = p2.y - p1.y
    l = _hypot(v.x,v.y)
    if l = 0 then
        r.x = 0: r.y = 0
        exit sub
    end if

    ' normal vector perpendicular to ab : (-vy, vx)
    u.x = -v.y / l
    u.y =  v.x / l

    ' determine which side of the line ab is c
    ' sign of (ac x ab) (2D vector product) : (cx-x1,cy-y1) x (vx,vy) = (cx-x1)*vy - (cy-y1)*vx
    side = (p3.x - p1.x) * v.y - (p3.y - p1.y) * v.x

    ' if side > 0 then it is on the side where the normal vector (-vy, vx) gives a positive sign
    'to place p in the half-plane opposite c, then reverse the normal if necessary.
    if side <= 0 then
        u.x = -u.x
        u.y = -u.y
    end if

    ' choose the midpoint of ab as the base for measuring the height
    r.x = (p1.x + p2.x) / 2 + u.x * h
    r.y = (p1.y + p2.y) / 2 + u.y * h

end sub

' generate a new trinagle

sub generateTriangle(t as triangle_type,baseMin%,baseMax%,hauteurMin%,hauteurMax%)
    bas = baseMax% - rnd*(baseMax% - baseMin%)
    angle = rnd*TAU
    t.a.x = 0
    t.a.y = 0
    t.b.x = cos(angle)*bas
    t.b.y = -sin(angle)*bas
    hauteur = hauteurMax% - rnd*(hauteurMax% - hauteurMin%)
    dim demibase as point_type
    demibase.x = t.b.x / 2
    demibase.y = t.b.y / 2
    t.c.x = demibase.x + cos(angle+TAU/4)*hauteur
    t.c.y = demibase.y - sin(angle+TAU/4)*hauteur
    t.center.x = (t.a.x + t.b.x + t.c.x ) / 3
    t.center.y = (t.a.y + t.b.y + t.c.y ) / 3
end sub

' create a triangle with 3 choosen vertex

sub createTriangle(t as triangle_type, p1 as point_type, p2 as point_type, p3 as point_type)
    t.a = p1
    t.b = p2
    t.c = p3
    t.position.x = 0
    t.position.y = 0
    t.angle = _atan2(t.b.y - t.a.y, t.b.x - t.a.x)
    t.hauteur =  abs((t.b.x-t.a.x)*(t.a.y-t.c.y) - (t.b.y - t.a.y)*(t.a.x-t.c.x)) / _hypot((t.b.x-t.a.x),(t.b.y-t.a.y))
    t.demibase.x = (t.b.x - t.a.x)/2
    t.demibase.y = (t.b.y - t.a.y)/2
    t.center.x = (t.a.x + t.b.x + t.c.x ) / 3
    t.center.y = (t.a.y + t.b.y + t.c.y ) / 3
end sub
Reply


Messages In This Thread
Triangle-Based Collision Detection - by Herve - 10-31-2025, 09:21 PM
RE: Triangle-Based Collision Detection - by Herve - 11-01-2025, 01:10 AM

Possibly Related Threads…
Thread Author Replies Views Last Post
  OpenGL - GPU based rendering (GLList) Unseen Machine 0 272 10-19-2025, 10:11 PM
Last Post: Unseen Machine
  Sierpinski Triangle in QB64PE (and others) SMcNeill 14 2,360 02-06-2025, 10:26 PM
Last Post: Pete
  A bigger bouncing ball demo - collision with vector reflection Dav 14 3,186 09-19-2024, 06:54 PM
Last Post: sbblank
  Simple Rummy-based game PhilOfPerth 3 1,125 11-24-2023, 11:23 PM
Last Post: PhilOfPerth
  A GradeBook demo based on NasaCow UDTs and ideas TempodiBasic 6 1,538 04-20-2023, 05:36 AM
Last Post: NasaCow

Forum Jump:


Users browsing this thread: 1 Guest(s)