moonlight-triangulation-1.4.0.2: ffi/abi/Moonlight/Triangulation/Foreign/Obstruction.hs
module Moonlight.Triangulation.Foreign.Obstruction
( AbiFailure (..)
, RegionCountKind (..)
, RegionLayoutError (..)
, bufferTooSmallFailure
, buildFailure
, countOverflowFailure
, emptyObstruction
, minkowskiFailure
, nullPointerFailure
, overlayFailure
, pointInputFailure
, projectionFailure
, regionLayoutFailure
, regionPublicationFailure
, regionValidationFailure
, runtimeFailure
, statusOk
, structuringElementEmptyFailure
, valuationFailure
) where
import Control.Exception (SomeException, displayException)
import Data.Word (Word32, Word64)
import Foreign.C.Types (CUInt)
import Moonlight.Triangulation.Foreign.Contract
import qualified Moonlight.Triangulation as T
data AbiFailure = AbiFailure !CUInt !CObstruction
statusCode :: AbiStatus -> CUInt
statusCode = fromIntegral . abiStatusId
statusOk :: CUInt
statusOk = statusCode AbiStatusOk
emptyObstruction :: CObstruction
emptyObstruction =
CObstruction
{ obstructionCode = obstructionCodeId ObstructionNone
, obstructionCoordinateError = coordinateErrorCodeId CoordinateErrorNone
, obstructionInputIndex = maxBound
, obstructionFirstIndex = 0
, obstructionSecondIndex = 0
, obstructionFirstValue = 0
, obstructionSecondValue = 0
, obstructionPointX = 0
, obstructionPointY = 0
, obstructionMessage = ""
}
apiFailure :: AbiStatus -> ObstructionCode -> String -> AbiFailure
apiFailure status code message =
AbiFailure
(statusCode status)
emptyObstruction
{ obstructionCode = obstructionCodeId code
, obstructionMessage = message
}
geometryFailure :: Show obstruction => ObstructionCode -> obstruction -> AbiFailure
geometryFailure code obstruction =
apiFailure AbiStatusGeometryObstruction code (show obstruction)
nullPointerFailure :: String -> AbiFailure
nullPointerFailure label =
apiFailure AbiStatusNullPointer ObstructionNullPointer (label <> " must not be null")
countOverflowFailure :: Word64 -> AbiFailure
countOverflowFailure count =
AbiFailure
(statusCode AbiStatusCountOverflow)
emptyObstruction
{ obstructionCode = obstructionCodeId ObstructionCountOverflow
, obstructionFirstIndex = count
, obstructionMessage = "count exceeds the host Int range"
}
bufferTooSmallFailure :: Int -> Int -> AbiFailure
bufferTooSmallFailure required capacity =
AbiFailure
(statusCode AbiStatusBufferTooSmall)
emptyObstruction
{ obstructionCode = obstructionCodeId ObstructionBufferTooSmall
, obstructionFirstIndex = fromIntegral required
, obstructionSecondIndex = fromIntegral capacity
, obstructionMessage = "output buffer is smaller than the required element count"
}
runtimeFailure :: SomeException -> AbiFailure
runtimeFailure = apiFailure AbiStatusRuntimeFailure ObstructionRuntimeFailure . displayException
buildFailure :: T.BuildError -> AbiFailure
buildFailure obstruction =
AbiFailure (statusCode AbiStatusGeometryObstruction) (buildErrorObstruction obstruction)
data RegionCountKind
= LoopPointCounts
| ComponentLoopCounts
deriving stock (Eq, Show)
data RegionLayoutError
= RegionGroupEmpty !RegionCountKind !Int
| RegionCountTotalMismatch !RegionCountKind !Integer !Integer
deriving stock (Eq, Show)
regionLayoutFailure :: RegionLayoutError -> AbiFailure
regionLayoutFailure layoutError =
AbiFailure (statusCode AbiStatusGeometryObstruction) (layoutObstruction layoutError)
layoutObstruction :: RegionLayoutError -> CObstruction
layoutObstruction layoutError =
( case layoutError of
RegionCountTotalMismatch _ actual expected ->
base
{ obstructionFirstIndex = fromIntegral actual
, obstructionSecondIndex = fromIntegral expected
}
RegionGroupEmpty _ index -> base {obstructionInputIndex = fromIntegral index}
)
{obstructionMessage = show layoutError}
where
base = emptyObstruction {obstructionCode = obstructionCodeId ObstructionRegionLayoutInvalid}
regionValidationFailure :: T.RegionValidationError -> AbiFailure
regionValidationFailure = geometryFailure ObstructionRegionValidationFailed
overlayFailure :: T.OverlayError Bool Bool -> AbiFailure
overlayFailure = geometryFailure ObstructionOverlayFailed
regionPublicationFailure :: T.RegionPublicationError -> AbiFailure
regionPublicationFailure = geometryFailure ObstructionRegionPublicationFailed
valuationFailure :: T.ValuationError -> AbiFailure
valuationFailure = geometryFailure ObstructionValuationFailed
minkowskiFailure :: T.MinkowskiError -> AbiFailure
minkowskiFailure = geometryFailure ObstructionMinkowskiFailed
structuringElementEmptyFailure :: AbiFailure
structuringElementEmptyFailure =
apiFailure
AbiStatusGeometryObstruction
ObstructionRegionLayoutInvalid
"structuring element requires at least one point"
pointInputFailure :: Int -> T.Point -> T.PointValidationError -> AbiFailure
pointInputFailure index (T.Point x y) pointError =
AbiFailure
(statusCode AbiStatusGeometryObstruction)
emptyObstruction
{ obstructionCode = obstructionCodeId ObstructionInvalidCoordinate
, obstructionCoordinateError = coordinateErrorCode reason
, obstructionInputIndex = fromIntegral index
, obstructionFirstValue = invalidValue
, obstructionPointX = x
, obstructionPointY = y
, obstructionMessage = show pointError
}
where
(invalidValue, reason) =
case pointError of
T.InvalidPointX coordinateError -> (x, coordinateError)
T.InvalidPointY coordinateError -> (y, coordinateError)
projectionFailure :: Int -> T.PointValidationError -> AbiFailure
projectionFailure index pointError =
AbiFailure
(statusCode AbiStatusGeometryObstruction)
emptyObstruction
{ obstructionCode = obstructionCodeId ObstructionRegionProjectionFailed
, obstructionCoordinateError =
coordinateErrorCode
( case pointError of
T.InvalidPointX coordinateError -> coordinateError
T.InvalidPointY coordinateError -> coordinateError
)
, obstructionInputIndex = fromIntegral index
, obstructionMessage = show pointError
}
buildErrorObstruction :: T.BuildError -> CObstruction
buildErrorObstruction failure =
( case failure of
T.InvalidCoordinate inputIndex value reason ->
emptyObstruction
{ obstructionCode = obstructionCodeId ObstructionInvalidCoordinate
, obstructionCoordinateError = coordinateErrorCode reason
, obstructionInputIndex = maybe maxBound fromIntegral inputIndex
, obstructionFirstValue = value
}
T.PointLocationFailed (T.Point x y) -> pointObstruction ObstructionPointLocationFailed x y
T.LocationWalkExhausted (T.Point x y) steps ->
(pointObstruction ObstructionLocationWalkExhausted x y) {obstructionFirstIndex = fromIntegral steps}
T.RefinementInputTopologyInvalid _ -> codeOnly ObstructionRefinementInputTopologyInvalid
T.FreshInsertionMatchedExistingVertex firstVertex secondVertex -> indices ObstructionFreshInsertionMatchedExistingVertex (T.unVertexId firstVertex) (T.unVertexId secondVertex)
T.DegenerateLineEndpointMissingOutgoing vertex -> firstIndex ObstructionDegenerateLineEndpointMissingOutgoing (T.unVertexId vertex)
T.DegenerateLineEndpointTurnMissing index -> firstIndex ObstructionDegenerateLineEndpointTurnMissing index
T.DegenerateLineConnectedVertexMissing index -> firstIndex ObstructionDegenerateLineConnectedVertexMissing index
T.HullStartNotVisible edge -> firstIndex ObstructionHullStartNotVisible (T.unDirectedEdgeId edge)
T.OuterRangeDidNotTerminate firstEdge secondEdge steps ->
(indices ObstructionOuterRangeDidNotTerminate (T.unDirectedEdgeId firstEdge) (T.unDirectedEdgeId secondEdge))
{obstructionFirstValue = fromIntegral steps}
T.OuterRangeContainsInnerEdge edge face -> indices ObstructionOuterRangeContainsInnerEdge (T.unDirectedEdgeId edge) (T.unFaceId face)
T.ConstrainedEdgeFlipRefused edge -> firstIndex ObstructionConstrainedEdgeFlipRefused (T.unUndirectedEdgeId edge)
T.RemovalVertexOutOfRange vertex count -> indices ObstructionRemovalVertexOutOfRange (T.unVertexId vertex) count
T.RemovalEdgeOutOfRange edge count -> indices ObstructionRemovalEdgeOutOfRange (T.unUndirectedEdgeId edge) count
T.RemovalFaceOutOfRange face count -> indices ObstructionRemovalFaceOutOfRange (T.unFaceId face) count
T.RemovalFaceCycleDidNotTerminate face edge steps ->
(indices ObstructionRemovalFaceCycleDidNotTerminate (T.unFaceId face) (T.unDirectedEdgeId edge))
{obstructionFirstValue = fromIntegral steps}
T.RemovalEmptyTriangulation vertex -> firstIndex ObstructionRemovalEmptyTriangulation (T.unVertexId vertex)
T.RemovalTwoPointDegreeMismatch vertex degree -> indices ObstructionRemovalTwoPointDegreeMismatch (T.unVertexId vertex) degree
T.RemovalCollinearDegreeMismatch vertex degree -> indices ObstructionRemovalCollinearDegreeMismatch (T.unVertexId vertex) degree
T.RemovalBorderTooShort count -> firstIndex ObstructionRemovalBorderTooShort count
T.RemovalBorderArityMismatch count -> firstIndex ObstructionRemovalBorderArityMismatch count
T.RemovalOutgoingCycleDidNotTerminate vertex edge steps ->
(indices ObstructionRemovalOutgoingCycleDidNotTerminate (T.unVertexId vertex) (T.unDirectedEdgeId edge))
{obstructionFirstValue = fromIntegral steps}
T.CircleSweepHullEmpty -> codeOnly ObstructionCircleSweepHullEmpty
T.OuterCycleDidNotTerminate firstEdge secondEdge steps ->
(indices ObstructionOuterCycleDidNotTerminate (T.unDirectedEdgeId firstEdge) (T.unDirectedEdgeId secondEdge))
{obstructionFirstValue = fromIntegral steps}
T.HierarchyLevelPopulationMismatch level expected observed ->
(indices ObstructionHierarchyLevelPopulationMismatch expected observed) {obstructionFirstValue = fromIntegral level}
T.HierarchyInsertionHandleMismatch expected observed -> indices ObstructionHierarchyInsertionHandleMismatch (T.unVertexId expected) (T.unVertexId observed)
T.PointIndexCapacityExhausted count -> firstIndex ObstructionPointIndexCapacityExhausted count
T.RefinementMinimumAngleNotFinite value -> nonFinite ObstructionRefinementMinimumAngleNotFinite value
T.RefinementMinimumAngleOutOfRange value -> firstValue ObstructionRefinementMinimumAngleOutOfRange value
T.RefinementMinimumAngleDerivedRatioNotFinite value -> nonFinite ObstructionRefinementMinimumAngleDerivedRatioNotFinite value
T.RefinementMaximumAdditionalVerticesNegative value -> firstValue ObstructionRefinementMaximumAdditionalVerticesNegative (fromIntegral value)
T.RefinementMinimumAreaNotFinite value -> nonFinite ObstructionRefinementMinimumAreaNotFinite value
T.RefinementMinimumAreaNegative value -> firstValue ObstructionRefinementMinimumAreaNegative value
T.RefinementMaximumAreaNotFinite value -> nonFinite ObstructionRefinementMaximumAreaNotFinite value
T.RefinementMaximumAreaNotPositive value -> firstValue ObstructionRefinementMaximumAreaNotPositive value
T.RefinementMaximumRadiusEdgeRatioNotFinite value -> nonFinite ObstructionRefinementMaximumRadiusEdgeRatioNotFinite value
T.RefinementMaximumRadiusEdgeRatioNotPositive value -> firstValue ObstructionRefinementMaximumRadiusEdgeRatioNotPositive value
T.RefinementMaximumEdgeLengthNotFinite value -> nonFinite ObstructionRefinementMaximumEdgeLengthNotFinite value
T.RefinementMaximumEdgeLengthNotPositive value -> firstValue ObstructionRefinementMaximumEdgeLengthNotPositive value
T.RefinementMinimumAreaExceedsMaximum minimumArea maximumArea -> values ObstructionRefinementMinimumAreaExceedsMaximum minimumArea maximumArea
T.RefinementSeedFaceNotActive face count -> indices ObstructionRefinementSeedFaceNotActive (T.unFaceId face) count
T.RefinementDomainTopologyChanged -> codeOnly ObstructionRefinementDomainTopologyChanged
T.RefinementDomainRequiresConvexHullPreservation -> codeOnly ObstructionRefinementDomainRequiresConvexHullPreservation
T.RefinementDomainRequiresConstraintPreservation -> codeOnly ObstructionRefinementDomainRequiresConstraintPreservation
T.RefinementDomainForbidsOuterFaceExclusion -> codeOnly ObstructionRefinementDomainForbidsOuterFaceExclusion
T.RefinementDomainWouldCrossInterface edge face -> indices ObstructionRefinementDomainWouldCrossInterface (T.unUndirectedEdgeId edge) (T.unFaceId face)
T.RefinementDomainWouldRewriteProtectedFace face -> firstIndex ObstructionRefinementDomainWouldRewriteProtectedFace (T.unFaceId face)
T.RefinementDomainProtectedFaceChanged face -> firstIndex ObstructionRefinementDomainProtectedFaceChanged (T.unFaceId face)
T.RefinementDomainInterfaceOppositeFaceNotPermitted edge face ->
indices ObstructionRefinementDomainInterfaceOppositeFaceNotPermitted (T.unUndirectedEdgeId edge) (T.unFaceId face)
T.RefinementOversizedEdge face edge actual bound ->
(indices ObstructionRefinementOversizedEdge (T.unFaceId face) (T.unUndirectedEdgeId edge))
{ obstructionFirstValue = actual
, obstructionSecondValue = bound
}
T.CapacityExceeded count -> firstIndex ObstructionCapacityExceeded count
T.HalfEdgeCapacityExceeded requested capacity -> indices ObstructionHalfEdgeCapacityExceeded requested capacity
T.FaceCapacityExceeded requested capacity -> indices ObstructionFaceCapacityExceeded requested capacity
T.PayloadStorageFailure _ -> codeOnly ObstructionPayloadStorageFailure
T.CoordinatePayloadCountMismatch coordinates payloads -> indices ObstructionCoordinatePayloadCountMismatch coordinates payloads
T.CircleSweepRequiresDenseStorage -> codeOnly ObstructionCircleSweepRequiresDenseStorage
T.SeamFrontierUnavailable -> codeOnly ObstructionSeamFrontierUnavailable
T.RefinementDomainRequiresFiniteVertexBudget -> codeOnly ObstructionRefinementDomainRequiresFiniteVertexBudget
T.SeamSourceEdgeRequiresFlip edge -> firstIndex ObstructionSeamSourceEdgeRequiresFlip (T.unUndirectedEdgeId edge)
T.RefinementSeamBridgeBudgetExceeded required available -> indices ObstructionRefinementSeamBridgeBudgetExceeded required available
T.RefinementSeamBridgeMidpointCollapsed edge -> firstIndex ObstructionRefinementSeamBridgeMidpointCollapsed (T.unUndirectedEdgeId edge)
T.BoundarySplitRequiresBoundaryEdge edge -> firstIndex ObstructionBoundarySplitRequiresBoundaryEdge (T.unUndirectedEdgeId edge)
)
{obstructionMessage = show failure}
where
codeOnly :: ObstructionCode -> CObstruction
codeOnly code = emptyObstruction {obstructionCode = obstructionCodeId code}
firstIndex :: Integral index => ObstructionCode -> index -> CObstruction
firstIndex code index = (codeOnly code) {obstructionFirstIndex = fromIntegral index}
indices :: (Integral first, Integral second) => ObstructionCode -> first -> second -> CObstruction
indices code firstIndexValue secondIndexValue =
(codeOnly code)
{ obstructionFirstIndex = fromIntegral firstIndexValue
, obstructionSecondIndex = fromIntegral secondIndexValue
}
firstValue :: ObstructionCode -> Double -> CObstruction
firstValue code value = (codeOnly code) {obstructionFirstValue = value}
values :: ObstructionCode -> Double -> Double -> CObstruction
values code firstCoordinateValue secondCoordinateValue =
(codeOnly code)
{ obstructionFirstValue = firstCoordinateValue
, obstructionSecondValue = secondCoordinateValue
}
pointObstruction :: ObstructionCode -> Double -> Double -> CObstruction
pointObstruction code x y =
(codeOnly code)
{ obstructionPointX = x
, obstructionPointY = y
}
nonFinite :: ObstructionCode -> T.NonFiniteValue -> CObstruction
nonFinite code value = firstIndex code (nonFiniteCode value)
coordinateErrorCode :: T.CoordinateError -> Word32
coordinateErrorCode reason =
coordinateErrorCodeId
( case reason of
T.CoordinateNaN -> CoordinateErrorNaN
T.CoordinateInfinite -> CoordinateErrorInfinite
T.CoordinateTooSmall -> CoordinateErrorTooSmall
T.CoordinateTooLarge -> CoordinateErrorTooLarge
)
nonFiniteCode :: T.NonFiniteValue -> Word32
nonFiniteCode value =
case value of
T.ValueNaN -> 1
T.ValuePositiveInfinity -> 2
T.ValueNegativeInfinity -> 3