-- | -- Module: Data.Geo.Jord.Triangle -- Copyright: (c) 2020 Cedric Liegeois -- License: BSD3 -- Maintainer: Cedric Liegeois -- Stability: experimental -- Portability: portable -- -- Types and functions for working with triangles at the surface of a __spherical__ celestial body. -- -- In order to use this module you should start with the following imports: -- -- @ -- import qualified Data.Geo.Jord.Geodetic as Geodetic -- import qualified Data.Geo.Jord.Triangle as Triangle -- @ module Data.Geo.Jord.Triangle ( Triangle , vertex0 , vertex1 , vertex2 , make , unsafeMake , centroid , circumcentre , contains ) where import Control.Monad (join) import Data.Maybe (fromJust) import Data.Geo.Jord.Geodetic (HorizontalPosition) import qualified Data.Geo.Jord.Geodetic as Geodetic import qualified Data.Geo.Jord.GreatCircle as GreatCircle import qualified Data.Geo.Jord.Math3d as Math3d import Data.Geo.Jord.Model (Spherical) -- | A triangle whose vertices are horizontal geodetic positions. data Triangle a = Triangle (HorizontalPosition a) (HorizontalPosition a) (HorizontalPosition a) deriving (Eq, Show) -- | First vertex of given triangle. vertex0 :: (Spherical a) => Triangle a -> HorizontalPosition a vertex0 (Triangle v _ _) = v -- | Second vertex of given triangle. vertex1 :: (Spherical a) => Triangle a -> HorizontalPosition a vertex1 (Triangle _ v _) = v -- | Third vertex of given triangle. vertex2 :: (Spherical a) => Triangle a -> HorizontalPosition a vertex2 (Triangle _ _ v) = v -- | Triangle from given vertices. Returns 'Nothing' if some vertices are equal or some are antipodes of others. make :: (Spherical a) => HorizontalPosition a -> HorizontalPosition a -> HorizontalPosition a -> Maybe (Triangle a) make v0 v1 v2 | v0 == v1 || v1 == v2 || v2 == v0 = Nothing | a0 == v1 || a0 == v2 || a1 == v2 = Nothing | otherwise = Just (Triangle v0 v1 v2) where a0 = Geodetic.antipode v0 a1 = Geodetic.antipode v1 -- | Triangle from given vertices. This is unsafe, if any vertices are equal or some are antipodes of others, -- the resulting triangle is actually undefined. unsafeMake :: (Spherical a) => HorizontalPosition a -> HorizontalPosition a -> HorizontalPosition a -> Triangle a unsafeMake = Triangle -- | @contains t p@ returns 'True' if position @p@ is enclosed by the vertices of triangle -- @t@ - see 'GreatCircle.enclosedBy'. contains :: (Spherical a) => Triangle a -> HorizontalPosition a -> Bool contains (Triangle v0 v1 v2) p = GreatCircle.enclosedBy p [v0, v1, v2] -- | Computes the centroid of the given triangle: the position which is the intersection of the three medians of -- the triangle (each median connecting a vertex with the midpoint of the opposite side). -- -- The centroid is always within the triangle. centroid :: (Spherical a) => Triangle a -> HorizontalPosition a centroid (Triangle v0 v1 v2) = fromJust c -- this is safe unless triangle was created using unsafeMake. where m1 = GreatCircle.mean [v1, v2] >>= GreatCircle.minorArc v0 m2 = GreatCircle.mean [v2, v0] >>= GreatCircle.minorArc v1 c = join (GreatCircle.intersection <$> m1 <*> m2) -- | The circumcentre of the triangle: the position which is equidistant from all three vertices. -- -- The circumscribed circle or circumcircle of a triangle is a circle which passes through all -- the vertices of the triangle; The circumcentre is not necessarily inside the triangle. -- -- Thanks to STRIPACK: http://orion.math.iastate.edu/burkardt/f_src/stripack/stripack.f90 circumcentre :: (Spherical a) => Triangle a -> HorizontalPosition a circumcentre (Triangle v0 v1 v2) = Geodetic.nvectorPos' cu (Geodetic.model v0) where e0 = Math3d.subtract (Geodetic.nvector v1) (Geodetic.nvector v0) e1 = Math3d.subtract (Geodetic.nvector v2) (Geodetic.nvector v0) cu = Math3d.cross e0 e1