-- SG library-- Copyright (c) 2009, Neil Brown.-- All rights reserved.-- -- Redistribution and use in source and binary forms, with or without-- modification, are permitted provided that the following conditions are-- met:---- * Redistributions of source code must retain the above copyright-- notice, this list of conditions and the following disclaimer.-- * Redistributions in binary form must reproduce the above copyright-- notice, this list of conditions and the following disclaimer in the-- documentation and/or other materials provided with the distribution.-- * The author's name may not be used to endorse or promote products derived-- from this software without specific prior written permission.---- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS-- IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR-- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR-- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.-- | This module has the type-class (and associated functions) for dealing with-- geometric systems of 2 or 3 dimensions.moduleData.SG.GeometrywhereimportControl.ArrowimportData.SG.VectorimportData.SG.Vector.Basic-- | A geometry system, parameterised over points, relative (free) vectors, and-- lines. There are separate instances for two dimensions and for three dimensions.-- Each pair of type-class parameters is uniquely determined by the other parameter-- (i.e. by the dimensionality, and which vector type you are using).-- -- Minimal implementation: everything but scaleRel.class(VectorNumrel,Coordrel,Coordpt,IsomorphicVectorsrelpt,IsomorphicVectorsptrel)=>Geometryrelptln|rel->ptln,pt->relln,ln->relptwhere-- | Scales a relative (free) vector by the given amount.scaleRel::Numa=>a->rela->relascaleRela=fmapNum1(*a)-- | Adds a relative (free) vector to a given point.plusDir::Numa=>pta->rela->pta-- | Determines the relative (free) vector /to/ the first parameter /from/ the-- second parameter. So:---- > Point2 (1,8) `fromPt` Point2 (3,4) == Point2 (-2,3)fromPt::Numa=>pta->pta->rela-- | Given a line, converts it back into its point and relative vector. It should-- always be the case that @uncurry makeLine . getLineVecs@ is the identity function.getLineVecs::Numa=>lna->(pta,rela)-- | Given a point and relative vector, creates a line. It should always be-- the case that @uncurry makeLine . getLineVecs@ is the identity function.makeLine::Numa=>pta->rela->lnainstanceGeometryPairPairLinePairwhereplusDir=(+)fromPt=(-)getLineVecs(LinePairlp)=lpmakeLine=curryLinePairinstanceGeometryTripleTripleLineTriplewhereplusDir=(+)fromPt=(-)getLineVecs(LineTriplelp)=lpmakeLine=curryLineTriple-- | Adds the negation of the relative (free) vector to the point.minusDir::(Numa,Geometryrelptln)=>pta->rela->ptaminusDirpr=p`plusDir`fmapNum1negater-- | The flipped version of 'fromPt'.toPt::(Geometryrelptln,Numa)=>pta->pta->relatoPt=flipfromPt-- | Gets the line /from/ the first point, /to/ the second point.lineTo::(Numa,Geometryrelptln)=>pta->pta->lnalineToab=makeLinea(b`fromPt`a)-- | The flipped version of 'lineTo'.lineFrom::(Numa,Geometryrelptln)=>pta->pta->lnalineFrom=fliplineTo-- | Gets the point at the start of the line.getLineStart::(Numa,Geometryrelptln)=>lna->ptagetLineStart=fst.getLineVecs-- | Gets the direction vector of the line.getLineDir::(Numa,Geometryrelptln)=>lna->relagetLineDir=snd.getLineVecs-- | Gets the point at the end of the line.getLineEnd::(Geometryrelptln,Numa)=>lna->ptagetLineEnd=uncurryplusDir.getLineVecs-- | Alters the line to the given length, but with the same start point and direction.makeLength::(Floatinga,Orda,Geometryrelptln)=>a->lna->lnamakeLengthx=uncurrymakeLine.second(scaleRelx.unitVector).getLineVecs-- | Given a multiple of the /direction vector/ (this is /not/ distance unless-- the direction vector is a unit vector), calculates that point.alongLine::(Numa,Geometryrelptln)=>a->lna->ptaalongLinea=uncurryplusDir.second(scaleRela).getLineVecs-- | Checks if the given point is on the given line (to within a small epsilon-tolerance).-- If it is, gives back the distance along the line (as a multiple of its direction-- vector) to the point in a Just wrapper. If the point is not on the line, Nothing-- is returned.distAlongLine::(Geometryrelptln,Orda,Floatinga)=>pta->lna->MaybeadistAlongLineptln=ifsameDirectionlnDirfromStartthenJust$magfromStartelseNothingwherefromStart=pt`fromPt`getLineStartlnlnDir=getLineDirln-- | Checks if the given point is on the given line (to within a small epsilon-tolerance).isOnLine::(Geometryrelptln,Orda,Floatinga)=>pta->lna->BoolisOnLineptln=sameDirectionlnDirfromStartwherefromStart=pt`fromPt`getLineStartlnlnDir=getLineDirln-- | Finds the nearest point on the line to the given point, and gives back its-- distance along the line (as a multiple of the direction vector). Since the-- nearest distance will be at a right-angle to the point, this is the same as-- projecting the point onto the line.nearestDistOnLine::(Geometryrelptln,Orda,Floatinga)=>pta->lna->a-- The nearest point on the line will be the one forming a right-angle triangle-- between the line and the point. We can use the dot product to project the point-- onto the line. We want |a| cos theta / |b| for the distance, which is the same-- as a . b / |b|^2.nearestDistOnLineptln|lnDirMagSq==0=0-- all-zero direction vector|otherwise=(fromStart`dotProduct`lnDir)/lnDirMagSqwherefromStart=pt`fromPt`getLineStartlnlnDir=getLineDirlnlnDirMagSq=magSqlnDir-- | Finds the nearest point on the line to the given point, and gives back the-- point.nearestPointOnLine::(Geometryrelptln,Orda,Floatinga)=>pta->lna->ptanearestPointOnLineptln=nearestDistOnLineptln`alongLine`ln-- | Gives the distance along the line (2D or 3D) at a given X value. Returns Nothing-- if the line is parallel to the YZ plane (in 2D, if the X component of the line-- is zero). The value returned is a multiple of the direction vector of the line,-- which will only be the same as distance if the direction vector is a unit vector.valueAtX::(Geometryrelptln,Coord2rel,Coord2pt,Fractionala)=>lna->a->MaybeavalueAtXltgt|xd==0=Nothing|otherwise=lett=(tgt-x)/xdinJusttwherex=getX$getLineStartlxd=getX$getLineDirl-- | Gives the distance along the line (2D or 3D) at a given Y value. Returns Nothing-- if the line is parallel to the XZ plane (in 2D, if the Y component of the line-- is zero). The value returned is a multiple of the direction vector of the line,-- which will only be the same as distance if the direction vector is a unit vector.valueAtY::(Geometryrelptln,Coord2rel,Coord2pt,Fractionala)=>lna->a->MaybeavalueAtYltgt|yd==0=Nothing|otherwise=lett=(tgt-y)/ydinJusttwherey=getY$getLineStartlyd=getY$getLineDirl-- | Gives the distance along the 3D line at a given Z value. Returns Nothing-- if the line is parallel to the XY plane. The value returned is a multiple-- of the direction vector of the line, which will only be the same as-- distance if the direction vector is a unit vector.valueAtZ::(Geometryrelptln,Coord3rel,Coord3pt,Fractionala)=>lna->a->MaybeavalueAtZltgt|zd==0=Nothing|otherwise=lett=(tgt-z)/zdinJusttwherez=getZ$getLineStartlzd=getZ$getLineDirl-- | pointAtX (and the Y and Z equivalents) are wrappers around 'valueAtX' (and-- similar) that give back the point rather than distance along the line.pointAtX,pointAtY::(Geometryrelptln,Coord2rel,Coord2pt,Fractionala)=>lna->a->Maybe(pta)pointAtXl=fmap(flipalongLinel).valueAtXlpointAtYl=fmap(flipalongLinel).valueAtYlpointAtZ::(Geometryrelptln,Coord3rel,Coord3pt,Fractionala)=>lna->a->Maybe(pta)pointAtZl=fmap(flipalongLinel).valueAtZl