packages feed

moonlight-planar-1.1.0.0: src-illustration/Moonlight/Planar/Illustration/Motif.hs

-- | A picture and its semantic attachment frames share one affine action.
-- Port vocabularies belong to authors: lookup is total, with no registration,
-- missing-name case, or global namespace. Initial frame meaning is supplied
-- by the author; this owner preserves coherence under subsequent placement.
module Moonlight.Planar.Illustration.Motif
  ( Motif
  , motif
  , motifPicture
  , motifPort
  , transformMotif
  , attachMotif
  ) where

import Moonlight.Planar.Affine
  ( AffineIso2, affineIsoMap, composeAffineIso2, inverseAffineIso2 )
import Moonlight.Planar.Illustration (Picture, place)

data Motif port part = Motif !(Picture part) (port -> AffineIso2)

-- | Each frame maps attachment-local coordinates into the picture's space.
-- This constructor does not prove that an authored port lies on its geometry.
motif :: Picture part -> (port -> AffineIso2) -> Motif port part
motif = Motif

motifPicture :: Motif port part -> Picture part
motifPicture (Motif picture _) = picture

motifPort :: Motif port part -> port -> AffineIso2
motifPort (Motif _ ports) = ports

-- | Transform geometry and all ports together. Shear, scale, and reflection
-- are allowed: this is an affine action, not a rigid-mounting promise.
transformMotif :: AffineIso2 -> Motif port part -> Motif port part
transformMotif transform (Motif picture ports) =
  Motif (place (affineIsoMap transform) picture) (composeAffineIso2 transform . ports)
{-# INLINE transformMotif #-}

-- | Align the chosen source frame with the target using @target . inverse
-- source@. This guarantees frame matching, not tangent continuity or clearance.
attachMotif :: port -> AffineIso2 -> Motif port part -> Motif port part
attachMotif sourcePort target value =
  transformMotif
    (composeAffineIso2 target (inverseAffineIso2 (motifPort value sourcePort)))
    value
-- Expose the source-port projection to callers so repeated attachments can
-- share its inverse instead of retaining an opaque per-instance calculation.
{-# INLINE attachMotif #-}