-
Notifications
You must be signed in to change notification settings - Fork 0
Overhaul geometry transformations. #1
Description
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?)