module Geometry.Segment ( Segment , translateSegment , splitSegmentsOnY , splitSegmentsOnX , chooseSplitX) where import Graphics.Gloss import Graphics.Gloss.Geometry.Line import Data.Maybe import Data.Function import qualified Array as A import Array (Array) -- | A line segement in the 2D plane. type Segment = (Int, (Float, Float), (Float, Float)) -- | Translate both endpoints of a segment. translateSegment :: Float -> Float -> Segment -> Segment translateSegment tx ty (n, (x1, y1), (x2, y2)) = (n, (x1 + tx, y1 + ty), (x2 + tx, y2 + ty)) -- | Split segments that cross the line y = y0, for some y0. splitSegmentsOnY :: Float -> Array Segment -> Array Segment splitSegmentsOnY y0 segs = let -- TODO: we only need to know IF the seg crosse the line here, -- not the actual intersection point. Do a faster test. (segsCross, segsOther) = A.unstablePartition (\(_, p1, p2) -> isJust $ intersectSegHorzLine p1 p2 y0) segs -- TODO: going via lists here is bad. splitCrossingSeg :: Segment -> Array Segment splitCrossingSeg (n, p1, p2) = let Just pCross = intersectSegHorzLine p1 p2 y0 in A.fromList [(n, p1, pCross), (n, pCross, p2)] -- TODO: vector append requires a copy. in segsOther A.++ (A.concat $ map splitCrossingSeg $ A.toList segsCross) -- | Split segments that cross the line x = x0, for some x0. splitSegmentsOnX :: Float -> Array Segment -> Array Segment splitSegmentsOnX x0 segs = let -- TODO: we only need to know IF the seg crosse the line here, -- not the actual intersection point. Do a faster test. (segsCross, segsOther) = A.unstablePartition (\(_, p1, p2) -> isJust $ intersectSegVertLine p1 p2 x0) segs -- TODO: going via lists here is bad. splitCrossingSeg :: Segment -> Array Segment splitCrossingSeg (n, p1, p2) = let Just pCross = intersectSegVertLine p1 p2 x0 in A.fromList [(n, p1, pCross), (n, pCross, p2)] -- TODO: vector append requires a copy. in segsOther A.++ (A.concat $ map splitCrossingSeg $ A.toList segsCross) -- | Decide where to split the plane. -- TODO: We're just taking the first point of the segment in the middle of the vector. -- It might be better to base the split on: -- - the closest segment -- - the widest sgement -- - the one closes to the middle of the field. -- - some combination of above. -- chooseSplitX :: Array Segment -> Float chooseSplitX segments = let Just (_, (x1, _), _) = segments A.!? (A.length segments `div` 2) in x1