{-# LANGUAGE PatternGuards #-}-- | Geometric functions concerning lines and segments.---- A @Line@ is taken to be infinite in length, while a @Seg@ is finite length line segment represented by its two endpoints. moduleGraphics.Gloss.Geometry.Line(segClearsBox-- * Closest points,closestPointOnLine,closestPointOnLineParam-- * Line-Line intersection,intersectLineLine-- * Seg-Line intersection,intersectSegLine,intersectSegHorzLine,intersectSegVertLine-- * Seg-Seg intersection,intersectSegSeg,intersectSegHorzSeg,intersectSegVertSeg)whereimportGraphics.Gloss.Data.PointimportGraphics.Gloss.Data.Vector-- | Check if line segment (P1-P2) clears a box (P3-P4) by being well outside it.segClearsBox::Point-- ^ P1 First point of segment. ->Point-- ^ P2 Second point of segment.->Point-- ^ P3 Lower left point of box.->Point-- ^ P4 Upper right point of box.->BoolsegClearsBox(x1,y1)(x2,y2)(xa,ya)(xb,yb)|x1<xa,x2<xa=True|x1>xb,x2>xb=True|y1<ya,y2<ya=True|y1>yb,y2>yb=True|otherwise=False-- | Given an infinite line which intersects `P1` and `P1`,-- return the point on that line that is closest to `P3`closestPointOnLine::Point-- ^ `P1`->Point-- ^ `P2`->Point-- ^ `P3`->Point-- ^ the point on the line P1-P2 that is closest to `P3`{-# INLINE closestPointOnLine #-}closestPointOnLinep1p2p3=p1+(u`mulSV`(p2-p1))whereu=closestPointOnLineParamp1p2p3-- | Given an infinite line which intersects P1 and P2,-- let P4 be the point on the line that is closest to P3.---- Return an indication of where on the line P4 is relative to P1 and P2.---- @-- if P4 == P1 then 0-- if P4 == P2 then 1-- if P4 is halfway between P1 and P2 then 0.5-- @---- @-- |-- P1-- | -- P4 +---- P3 -- |-- P2-- |-- @--{-# INLINE closestPointOnLineParam #-}closestPointOnLineParam::Point-- ^ `P1`->Point-- ^ `P2`->Point-- ^ `P3`->FloatclosestPointOnLineParamp1p2p3=(p3-p1)`dotV`(p2-p1)/(p2-p1)`dotV`(p2-p1)-- Line-Line intersection --------------------------------------------------------------------------- | Given four points specifying two lines, get the point where the two lines cross, if any.-- Note that the lines extend off to infinity, so the intersection point might not lie-- between either of the two pairs of points.---- @-- \\ /-- P1 P4-- \\ /-- +-- / \\-- P3 P2-- / \\-- @--intersectLineLine::Point-- ^ `P1`->Point-- ^ `P2`->Point-- ^ `P3`->Point-- ^ `P4`->MaybePointintersectLineLine(x1,y1)(x2,y2)(x3,y3)(x4,y4)=letdx12=x1-x2dx34=x3-x4dy12=y1-y2dy34=y3-y4den=dx12*dy34-dy12*dx34inifden==0thenNothingelseletdet12=x1*y2-y1*x2det34=x3*y4-y3*x4numx=det12*dx34-dx12*det34numy=det12*dy34-dy12*det34inJust(numx/den,numy/den)-- Segment-Line intersection ------------------------------------------------------------------------ | Get the point where a segment @P1-P2@ crosses an infinite line @P3-P4@, if any.--intersectSegLine::Point-- ^ `P1`->Point-- ^ `P2`->Point-- ^ `P3`->Point-- ^ `P4`->MaybePointintersectSegLinep1p2p3p4-- TODO: merge closest point check with intersection, reuse subterms.|Justp0<-intersectLineLinep1p2p3p4,t12<-closestPointOnLineParamp1p2p0,t12>=0&&t12<=1=Justp0|otherwise=Nothing-- | Get the point where a segment crosses a horizontal line, if any.---- @ -- + P1-- /-- -------+----------- / y0-- P2 +-- @--intersectSegHorzLine::Point-- ^ P1 First point of segment.->Point-- ^ P2 Second point of segment.->Float-- ^ y value of line.->MaybePointintersectSegHorzLine(x1,y1)(x2,y2)y0-- seg is on line|y1==y0,y2==y0=Nothing-- seg is above line|y1>y0,y2>y0=Nothing-- seg is below line|y1<y0,y2<y0=Nothing-- seg is a single point on the line.-- this should be caught by the first case, -- but we'll test for it anyway.|y2-y1==0=Just(x1,y1)|otherwise=Just((y0-y1)*(x2-x1)/(y2-y1)+x1,y0)-- | Get the point where a segment crosses a vertical line, if any.---- @-- |-- | + P1-- | /-- +-- / |-- P2 + |-- | x0-- @--intersectSegVertLine::Point-- ^ P1 First point of segment.->Point-- ^ P2 Second point of segment.->Float-- ^ x value of line.->MaybePointintersectSegVertLine(x1,y1)(x2,y2)x0-- seg is on line|x1==x0,x2==x0=Nothing-- seg is to right of line|x1>x0,x2>x0=Nothing-- seg is to left of line|x1<x0,x2<x0=Nothing-- seg is a single point on the line.-- this should be caught by the first case, -- but we'll test for it anyway.|x2-x1==0=Just(x1,y1)|otherwise=Just(x0,(x0-x1)*(y2-y1)/(x2-x1)+y1)-- Segment-Segment intersection --------------------------------------------------------------------- | Get the point where a segment @P1-P2@ crosses another segement @P3-P4@, if any.intersectSegSeg::Point-- ^ `P1`->Point-- ^ `P2`->Point-- ^ `P3`->Point-- ^ `P4`->MaybePointintersectSegSegp1p2p3p4-- TODO: merge closest point checks with intersection, reuse subterms.|Justp0<-intersectLineLinep1p2p3p4,t12<-closestPointOnLineParamp1p2p0,t23<-closestPointOnLineParamp3p4p0,t12>=0&&t12<=1,t23>=0&&t23<=1=Justp0|otherwise=Nothing-- | Check if an arbitrary segment intersects a horizontal segment.---- @-- + P2-- /-- (xa, y3) +---+----+ (xb, y3)-- /-- P1 +-- @ intersectSegHorzSeg::Point-- ^ P1 First point of segment.->Point-- ^ P2 Second point of segment.->Float-- ^ (y3) y value of horizontal segment.->Float-- ^ (xa) Leftmost x value of horizontal segment.->Float-- ^ (xb) Rightmost x value of horizontal segment.->MaybePoint-- ^ (x3, y3) Intersection point, if any.intersectSegHorzSegp1@(x1,y1)p2@(x2,y2)y0xaxb|segClearsBoxp1p2(xa,y0)(xb,y0)=Nothing|x0<xa=Nothing|x0>xb=Nothing|otherwise=Just(x0,y0)wherex0|(y2-y1)==0=x1|otherwise=(y0-y1)*(x2-x1)/(y2-y1)+x1-- | Check if an arbitrary segment intersects a vertical segment.---- @-- (x3, yb) +-- | + P1-- | /-- +-- / |-- P2 + |-- + (x3, ya)-- @ intersectSegVertSeg::Point-- ^ P1 First point of segment.->Point-- ^ P2 Second point of segment.->Float-- ^ (x3) x value of vertical segment->Float-- ^ (ya) Lowest y value of vertical segment.->Float-- ^ (yb) Highest y value of vertical segment.->MaybePoint-- ^ (x3, y3) Intersection point, if any.intersectSegVertSegp1@(x1,y1)p2@(x2,y2)x0yayb|segClearsBoxp1p2(x0,ya)(x0,yb)=Nothing|y0<ya=Nothing|y0>yb=Nothing|otherwise=Just(x0,y0)wherey0|(x2-x1)==0=y1|otherwise=(x0-x1)*(y2-y1)/(x2-x1)+y1