Skip to content

Overhaul geometry transformations. #1

@Demonstrandum

Description

@Demonstrandum

Change translate/scale/rotate to structural tagging/datatypes instead of actually computing transformations on each vertex.
This can not only be used to optimise certain operations, but can have different behaviours implemented for more nuanced geometry. For example, accounting for primitives, like triangles, rectangles, and text.

Especially since some geometries are secretly CPU rasterised textures, such as with font/text shaping.

Make transformations separate data types which can be destructured by the back-end, to perform the educated, more optimal transformation.
In this idea, transformations are tags, and shapes are not just vertices.
All shapes thus originally sit at the origin, and are transformed therefrom.

Basic idea:

data TextShaping = { textFont :: Font, textSize :: Distance, textAlign :: TextAlign }  -- etc.

data Geometry = Vertices [Point]   -- first point should be at (0,0)?
              | Circle Distance
              | Arc Distance Radians
              | Rectangle Distance Distance
              | Text TextShaping String

data StrokeOffs = Inside | Outside | Middle
data Stroke = Stroke { strokeThickness :: Distance
                     , strokeText :: Texture
                     , strokeOffs :: StrokeOffs
                     , strokeCap :: StrokeCap
                     }

data Shape = Shape { geometry :: Geometry
                   , filling :: Color
                   , stroke :: Stroke

data Picture = Picture [Shape] deriving (Groupoid, Monoid)

data Transformation t where
  Translation :: Transformable t => Distance -> Distance -> t -> Transformation t
  Rotation    :: Transformable t =>              Radians -> t -> Transformation t
  Scaling     :: Transformable t =>               Scalar -> t -> Transformation t

-- These functions should be used, not the constructors,
-- Since special cases need to be handled, such as consolidating
-- repeated transformations of the same kind, and handling
-- raw vertex transformation from other kinds.
class Transformable t where
  translate :: Distance -> Distance -> t -> t
  rotate :: Radians -> t -> t
  scale :: Scalar -> t -> t

instance Transformable (Transformation t) where
  translate dx dy (Translation dx' dy' geo) = Translation (dx + dx') (dy + dy') geo
  translate dx dy geo = Translation dx dy geo
  rotate ϑ (Rotation ϑ' geo) = Rotation (ϑ + ϑ') geo
  rotate ϑ geo = Rotation ϑ geo
  scale x (Scaling x' geo) = Scaling (x * x') geo
  scale x geo = Scaling x geo

instance Transformable Shape where
  -- ...

instance Transformable Picture where
  -- ... maps on all shapes.

-- &c.

Transformations are according to the shapes origin, which is set to be what (would be) the shapes first vertex.

Finding a shapes geometric centre is perfectly left up for a specialising function, which can then have sibling functions which transforms a shape such that the shape actual/geometric centre is at the origin, such that all later/subsequent transformations are relative to the shapes centre.

Circles can be computed pixel perfect, but for OpenGL backend, picking an optimal vertex count based on size is a better idea, i.e. circle vertices are picked later, intelligently.

If the Picture we're drawing is itself immediately transformed, take that as a viewport transformation, which is more optimal for GL (maybe don't do this, maybe provide separate functionality?)

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions