Current section

Files

Jump to
paint src paint.gleam
Raw

src/paint.gleam

//// This module contains the main `Picture` type as well as the
//// function you can use to construct, modify and combine pictures.
import gleam/result
import gleam_community/colour
import paint/internal/types as internal_implementation
/// A 2D picture. This is the type which this entire library revolves around.
///
///> [!NOTE]
///> Unless you intend to author a new backend you should **consider this type opaque and never use any of its constructors**.
///> Instead, make use of the many utility functions defined in this module (`circle`, `combine`, `fill`, etc.)
pub type Picture =
internal_implementation.Picture
/// A segment from a 2D path.
///
///> [!NOTE]
///> Unless you intend to author a new backend you should **consider this type opaque and never use any of its constructors**.
///> Instead, make use the functions with the `path_` prefix.
pub type PathSegment =
internal_implementation.PathSegment
/// A reference to an image (i.e. a texture), not to be confused with the `Picture` type.
/// To create an image, see the image functions in the `canvas` back-end.
pub type Image =
internal_implementation.Image
pub type RotationDirection {
Clockwise
Counterclockwise
}
/// An angle in clock-wise direction.
/// See: `angle_rad` and `angle_deg`.
pub type Angle =
internal_implementation.Angle
/// Create an angle expressed in radians
pub fn angle_rad(radians: Float) -> Angle {
internal_implementation.Radians(radians)
}
/// Create an angle expressed in degrees
pub fn angle_deg(degrees: Float) -> Angle {
internal_implementation.Radians(degrees *. pi() /. 180.0)
}
/// A rexport of the Colour type from [gleam_community/colour](https://hexdocs.pm/gleam_community_colour/).
/// Paint also includes the functions `colour_hex` and `colour_rgb` to
/// easily construct Colours, but feel free to import the `gleam_community/colour` module
/// and use the many utility that are provided from there.
pub type Colour =
colour.Colour
/// A utility around [colour.from_rgb_hex_string](https://hexdocs.pm/gleam_community_colour/gleam_community/colour.html#from_rgb_hex_string)
/// (from `gleam_community/colour`) that **panics** on an invalid hex code.
pub fn colour_hex(string: String) -> Colour {
result.lazy_unwrap(colour.from_rgb_hex_string(string), fn() {
panic as "Failed to parse hex code"
})
}
/// A utility around [colour.from_rgb255](https://hexdocs.pm/gleam_community_colour/gleam_community/colour.html#from_rgb255)
/// (from `gleam_community/colour`) that **panics** if the values are outside of the allowed range.
pub fn colour_rgb(red: Int, green: Int, blue: Int) -> Colour {
result.lazy_unwrap(colour.from_rgb255(red, green, blue), fn() {
panic as "The value was not inside of the valid range [0-255]"
})
}
pub type Vec2 =
#(Float, Float)
/// A blank picture
pub fn blank() -> Picture {
internal_implementation.Blank
}
/// A circle with some given radius
pub fn circle(radius: Float) -> Picture {
internal_implementation.Path([
internal_implementation.ArcCentre(
centre: #(0.0, 0.0),
radius:,
start_angle: internal_implementation.Radians(0.0),
end_angle: internal_implementation.Radians(2.0 *. pi()),
counterclockwise: True,
),
])
}
/// An arc with some radius going from some
/// starting angle to some other angle in clock-wise direction
pub fn arc(
radius: Float,
start: Angle,
end: Angle,
direction: RotationDirection,
) -> Picture {
internal_implementation.Path([
internal_implementation.ArcCentre(
centre: #(0.0, 0.0),
radius:,
start_angle: start,
end_angle: end,
counterclockwise: case direction {
Clockwise -> False
Counterclockwise -> True
},
),
])
}
/// A path
pub fn path(start: Vec2, segments: List(PathSegment)) -> Picture {
internal_implementation.Path([
internal_implementation.MoveTo(start),
..segments
])
}
/// Move to in path
pub fn path_move(to: Vec2) -> PathSegment {
internal_implementation.MoveTo(to)
}
/// Line to in path
pub fn path_line(to: Vec2) -> PathSegment {
internal_implementation.LineTo(to)
}
/// Arc in path, defined as centre and start/end angles
pub fn path_arc_centre(
centre centre: Vec2,
radius radius: Float,
start_angle start_angle: Angle,
end_angle end_angle: Angle,
direction direction: RotationDirection,
) -> PathSegment {
internal_implementation.ArcCentre(
centre:,
radius:,
start_angle:,
end_angle:,
counterclockwise: case direction {
Clockwise -> False
Counterclockwise -> True
},
)
}
/// Arc in path, defined as corner point and end point
pub fn path_arc_corner(
corner corner: Vec2,
end end: Vec2,
radius radius: Float,
) -> PathSegment {
internal_implementation.ArcCorner(corner:, end:, radius:)
}
/// Bezier curve in path
pub fn path_bezier(cp1 cp1: Vec2, cp2 cp2: Vec2, end end: Vec2) -> PathSegment {
internal_implementation.BezierTo(cp1:, cp2:, end:)
}
/// A rectangle with some given width and height
pub fn rectangle(width: Float, height: Float) -> Picture {
path(#(0.0, 0.0), [
path_line(#(width, 0.0)),
path_line(#(width, height)),
path_line(#(0.0, height)),
path_line(#(0.0, 0.0)),
])
}
/// A square
pub fn square(length: Float) -> Picture {
rectangle(length, length)
}
/// Draw an image such as a PNG, JPEG or an SVG. See the `canvas` back-end for more details on how to load images.
pub fn image(image: Image, width_px width_px, height_px height_px) -> Picture {
// TODO: add a function that allows us to draw only part of an image, flip, and if we want smooth scaling or not
internal_implementation.ImageRef(image, width_px:, height_px:)
}
/// Set image scaling to be smooth (this is the default behaviour)
pub fn image_scaling_smooth(picture: Picture) -> Picture {
internal_implementation.ImageScalingBehaviour(
picture,
internal_implementation.ScalingSmooth,
)
}
/// Disable smooth image scaling, suitable for pixel art.
pub fn image_scaling_pixelated(picture: Picture) -> Picture {
internal_implementation.ImageScalingBehaviour(
picture,
internal_implementation.ScalingPixelated,
)
}
/// Text with some given font size
pub fn text(text: String, px font_size: Int) -> Picture {
internal_implementation.Text(text, size_px: font_size)
}
/// Horizontal alignment for text. See `text_align`.
pub type TextAlign {
/// The text is aligned at the normal start of the line (left-aligned for left-to-right locales, right-aligned for right-to-left locales).
TextAlignStart
/// The text is aligned at the normal end of the line (right-aligned for left-to-right locales, left-aligned for right-to-left locales).
TextAlignEnd
/// The text is left-aligned.
TextAlignLeft
/// The text is right-aligned.
TextAlignRight
/// The text is centered.
TextAlignCenter
}
/// Vertical baseline for text. See `text_baseline`.
pub type TextBaseline {
/// The text baseline is the top of the em square.
TextBaselineTop
/// The text baseline is the hanging baseline. (Used by Tibetan and other Indic scripts.)
TextBaselineHanging
/// The text baseline is the middle of the em square.
TextBaselineMiddle
/// The text baseline is the normal alphabetic baseline. Default value.
TextBaselineAlphabetic
/// The text baseline is the ideographic baseline; this is the bottom of the body of the characters,
/// if the main body of characters protrudes beneath the alphabetic baseline. (Used by Chinese, Japanese, and Korean scripts.)
TextBaselineIdeographic
/// The text baseline is the bottom of the bounding box. This differs from the ideographic baseline in that the ideographic baseline doesn't consider descenders.
TextBaselineBottom
}
/// Writing direction for text. See `text_direction`.
pub type TextDirection {
/// Left to right
TextDirectionLtr
/// Right to left
TextDirectionRtl
/// Inherited
Inherit
}
/// Set the font family used to render `text` inside the picture.
pub fn font_family(picture: Picture, family: String) -> Picture {
internal_implementation.FontFamily(picture, family)
}
/// Set the horizontal text alignment used to render `text` inside the picture.
pub fn text_align(picture: Picture, alignment: TextAlign) -> Picture {
internal_implementation.TextAlign(picture, case alignment {
// Ideally this internal type should be public
TextAlignStart -> internal_implementation.TextAlignStart
TextAlignEnd -> internal_implementation.TextAlignEnd
TextAlignLeft -> internal_implementation.TextAlignLeft
TextAlignRight -> internal_implementation.TextAlignRight
TextAlignCenter -> internal_implementation.TextAlignCenter
})
}
/// Set the vertical text baseline used to render `text` inside the picture.
pub fn text_baseline(picture: Picture, baseline: TextBaseline) -> Picture {
internal_implementation.TextBaseline(picture, case baseline {
// Ideally this internal type should be public
TextBaselineTop -> internal_implementation.TextBaselineTop
TextBaselineHanging -> internal_implementation.TextBaselineHanging
TextBaselineMiddle -> internal_implementation.TextBaselineMiddle
TextBaselineAlphabetic -> internal_implementation.TextBaselineAlphabetic
TextBaselineIdeographic -> internal_implementation.TextBaselineIdeographic
TextBaselineBottom -> internal_implementation.TextBaselineBottom
})
}
/// Set the writing direction used to render `text` inside the picture.
pub fn text_direction(picture: Picture, direction: TextDirection) -> Picture {
internal_implementation.TextDirection(picture, case direction {
// Ideally this internal type should be public
TextDirectionLtr -> internal_implementation.TextDirectionLtr
TextDirectionRtl -> internal_implementation.TextDirectionRtl
Inherit -> internal_implementation.TextDirectionInherit
})
}
/// Translate a picture in horizontal and vertical direction
pub fn translate_xy(picture: Picture, x: Float, y: Float) -> Picture {
internal_implementation.Translate(picture, #(x, y))
}
/// Translate a picture in the horizontal direction
pub fn translate_x(picture: Picture, x: Float) -> Picture {
translate_xy(picture, x, 0.0)
}
/// Translate a picture in the vertical direction
pub fn translate_y(picture: Picture, y: Float) -> Picture {
translate_xy(picture, 0.0, y)
}
/// Scale the picture in the horizontal direction
pub fn scale_x(picture: Picture, factor: Float) -> Picture {
internal_implementation.Scale(picture, #(factor, 1.0))
}
/// Scale the picture in the vertical direction
pub fn scale_y(picture: Picture, factor: Float) -> Picture {
internal_implementation.Scale(picture, #(1.0, factor))
}
/// Scale the picture uniformly in horizontal and vertical direction
pub fn scale_uniform(picture: Picture, factor: Float) -> Picture {
internal_implementation.Scale(picture, #(factor, factor))
}
/// Rotate the picture in a clock-wise direction
pub fn rotate(picture: Picture, angle: Angle) -> Picture {
internal_implementation.Rotate(picture, angle)
}
/// Fill a picture with some given colour, see `Colour`.
pub fn fill(picture: Picture, colour: Colour) -> Picture {
internal_implementation.Fill(picture, colour)
}
/// Set a solid stroke with some given colour and width
pub fn stroke(picture: Picture, colour: Colour, width width: Float) -> Picture {
stroke_dashed(picture, colour, width:, dashes: [])
}
/// Set a dashed stroke with some given colour and width
pub fn stroke_dashed(
picture: Picture,
colour: Colour,
width width: Float,
dashes dashes: List(Float),
) -> Picture {
internal_implementation.Stroke(
picture,
internal_implementation.DashedStroke(colour, width:, dashes:),
)
}
/// Remove the stroke of the given picture
pub fn stroke_none(picture: Picture) -> Picture {
internal_implementation.Stroke(picture, internal_implementation.NoStroke)
}
/// Concatenate two pictures
pub fn concat(picture: Picture, another_picture: Picture) -> Picture {
combine([picture, another_picture])
}
/// Combine multiple pictures into one
pub fn combine(pictures: List(Picture)) -> Picture {
internal_implementation.Combine(pictures)
}
// Internal utility function to get Pi π
@external(erlang, "math", "pi")
@external(javascript, "./numbers_ffi.mjs", "pi")
fn pi() -> Float {
3.1415926
}