-module(sparklinekit@internal@raster).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/sparklinekit/internal/raster.gleam").
-export([new/3, to_grid/1, put/4, fill_circle/5, fill_vertical_gradient/7, draw_line/7, fill_rect/6, fill_rounded_rect/7]).
-export_type([canvas/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(false).
-type canvas() :: {canvas,
integer(),
integer(),
sparklinekit@internal@color:rgba(),
gleam@dict:dict(integer(), gleam@dict:dict(integer(), sparklinekit@internal@color:rgba()))}.
-file("src/sparklinekit/internal/raster.gleam", 29).
?DOC(false).
-spec new(integer(), integer(), sparklinekit@internal@color:rgba()) -> canvas().
new(Width, Height, Background) ->
{canvas, Width, Height, Background, maps:new()}.
-file("src/sparklinekit/internal/raster.gleam", 208).
?DOC(false).
-spec float_sqrt(float()) -> float().
float_sqrt(Value) ->
case Value =< +0.0 of
true ->
+0.0;
false ->
case gleam@float:square_root(Value) of
{ok, V} ->
V;
{error, _} ->
+0.0
end
end.
-file("src/sparklinekit/internal/raster.gleam", 253).
?DOC(false).
-spec interpolate(
sparklinekit@internal@color:rgba(),
sparklinekit@internal@color:rgba(),
float()
) -> sparklinekit@internal@color:rgba().
interpolate(A, B, T) ->
T_ = case T of
V when V < +0.0 ->
+0.0;
V@1 when V@1 > 1.0 ->
1.0;
V@2 ->
V@2
end,
Mix = fun(A_ch, B_ch) ->
Af = erlang:float(A_ch),
Bf = erlang:float(B_ch),
case erlang:round(Af + ((Bf - Af) * T_)) of
V@3 when V@3 < 0 ->
0;
V@4 when V@4 > 255 ->
255;
V@5 ->
V@5
end
end,
{rgba, Ar, Ag, Ab, Aa} = A,
{rgba, Br, Bg, Bb, Ba} = B,
{rgba, Mix(Ar, Br), Mix(Ag, Bg), Mix(Ab, Bb), Mix(Aa, Ba)}.
-file("src/sparklinekit/internal/raster.gleam", 300).
?DOC(false).
-spec do_range(integer(), integer(), list(integer())) -> list(integer()).
do_range(Current, Lo, Acc) ->
case Current < Lo of
true ->
Acc;
false ->
do_range(Current - 1, Lo, [Current | Acc])
end.
-file("src/sparklinekit/internal/raster.gleam", 293).
?DOC(false).
-spec range(integer(), integer()) -> list(integer()).
range(Lo, Hi) ->
case Lo > Hi of
true ->
[];
false ->
do_range(Hi, Lo, [])
end.
-file("src/sparklinekit/internal/raster.gleam", 276).
?DOC(false).
-spec to_grid(canvas()) -> list(list(sparklinekit@internal@color:rgba())).
to_grid(Canvas) ->
Xs = range(0, erlang:element(2, Canvas) - 1),
_pipe = range(0, erlang:element(3, Canvas) - 1),
gleam@list:map(
_pipe,
fun(Y) -> case gleam_stdlib:map_get(erlang:element(5, Canvas), Y) of
{error, _} ->
gleam@list:map(Xs, fun(_) -> erlang:element(4, Canvas) end);
{ok, Row} ->
gleam@list:map(
Xs,
fun(X) -> case gleam_stdlib:map_get(Row, X) of
{ok, C} ->
C;
{error, _} ->
erlang:element(4, Canvas)
end end
)
end end
).
-file("src/sparklinekit/internal/raster.gleam", 307).
?DOC(false).
-spec current_at(canvas(), integer(), integer()) -> sparklinekit@internal@color:rgba().
current_at(Canvas, X, Y) ->
case gleam_stdlib:map_get(erlang:element(5, Canvas), Y) of
{error, _} ->
erlang:element(4, Canvas);
{ok, Row} ->
case gleam_stdlib:map_get(Row, X) of
{ok, C} ->
C;
{error, _} ->
erlang:element(4, Canvas)
end
end.
-file("src/sparklinekit/internal/raster.gleam", 318).
?DOC(false).
-spec in_bounds(canvas(), integer(), integer()) -> boolean().
in_bounds(Canvas, X, Y) ->
(((X >= 0) andalso (X < erlang:element(2, Canvas))) andalso (Y >= 0))
andalso (Y < erlang:element(3, Canvas)).
-file("src/sparklinekit/internal/raster.gleam", 40).
?DOC(false).
-spec put(canvas(), integer(), integer(), sparklinekit@internal@color:rgba()) -> canvas().
put(Canvas, X, Y, Colour) ->
case in_bounds(Canvas, X, Y) of
false ->
Canvas;
true ->
Existing = current_at(Canvas, X, Y),
Blended = sparklinekit@internal@color:over(Colour, Existing),
Row = case gleam_stdlib:map_get(erlang:element(5, Canvas), Y) of
{ok, R} ->
R;
{error, _} ->
maps:new()
end,
Row@1 = gleam@dict:insert(Row, X, Blended),
{canvas,
erlang:element(2, Canvas),
erlang:element(3, Canvas),
erlang:element(4, Canvas),
gleam@dict:insert(erlang:element(5, Canvas), Y, Row@1)}
end.
-file("src/sparklinekit/internal/raster.gleam", 182).
?DOC(false).
-spec plot_disc_pixel(
canvas(),
integer(),
integer(),
float(),
float(),
float(),
sparklinekit@internal@color:rgba()
) -> canvas().
plot_disc_pixel(Canvas, X, Y, Cx, Cy, Radius, Colour) ->
Dx = (erlang:float(X) + 0.5) - Cx,
Dy = (erlang:float(Y) + 0.5) - Cy,
Dist = float_sqrt((Dx * Dx) + (Dy * Dy)),
Coverage = case Dist =< (Radius - 0.5) of
true ->
1.0;
false ->
case Dist >= (Radius + 0.5) of
true ->
+0.0;
false ->
(Radius + 0.5) - Dist
end
end,
case Coverage > +0.0 of
false ->
Canvas;
true ->
put(
Canvas,
X,
Y,
sparklinekit@internal@color:with_alpha(Colour, Coverage)
)
end.
-file("src/sparklinekit/internal/raster.gleam", 157).
?DOC(false).
-spec fill_circle(
canvas(),
float(),
float(),
float(),
sparklinekit@internal@color:rgba()
) -> canvas().
fill_circle(Canvas, Cx, Cy, Radius, Colour) ->
case Radius =< +0.0 of
true ->
Canvas;
false ->
X0 = erlang:round((Cx - Radius) - 1.0),
X1 = erlang:round((Cx + Radius) + 1.0),
Y0 = erlang:round((Cy - Radius) - 1.0),
Y1 = erlang:round((Cy + Radius) + 1.0),
Xs = range(X0, X1),
_pipe = range(Y0, Y1),
gleam@list:fold(
_pipe,
Canvas,
fun(C, Y) ->
gleam@list:fold(
Xs,
C,
fun(C2, X) ->
plot_disc_pixel(C2, X, Y, Cx, Cy, Radius, Colour)
end
)
end
)
end.
-file("src/sparklinekit/internal/raster.gleam", 222).
?DOC(false).
-spec fill_vertical_gradient(
canvas(),
float(),
float(),
float(),
float(),
sparklinekit@internal@color:rgba(),
sparklinekit@internal@color:rgba()
) -> canvas().
fill_vertical_gradient(Canvas, X0, Y0, X1, Y1, Top_colour, Bottom_colour) ->
Xa = erlang:round(gleam@float:min(X0, X1)),
Xb = erlang:round(gleam@float:max(X0, X1)),
Ya = erlang:round(gleam@float:min(Y0, Y1)),
Yb = erlang:round(gleam@float:max(Y0, Y1)),
case (Xa >= Xb) orelse (Ya >= Yb) of
true ->
Canvas;
false ->
Xs = range(Xa, Xb - 1),
Span_f = erlang:float(Yb - Ya),
_pipe = range(Ya, Yb - 1),
gleam@list:fold(
_pipe,
Canvas,
fun(C, Y) ->
T = case Span_f =< +0.0 of
true ->
+0.0;
false ->
case Span_f of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> erlang:float(Y - Ya) / Gleam@denominator
end
end,
Row_colour = interpolate(Top_colour, Bottom_colour, T),
gleam@list:fold(
Xs,
C,
fun(C2, X) -> put(C2, X, Y, Row_colour) end
)
end
)
end.
-file("src/sparklinekit/internal/raster.gleam", 400).
?DOC(false).
-spec isqrt_loop(integer(), integer()) -> integer().
isqrt_loop(Value, Guess) ->
Next = (Guess + (case Guess of
0 -> 0;
Gleam@denominator -> Value div Gleam@denominator
end)) div 2,
case Next >= Guess of
true ->
Guess;
false ->
isqrt_loop(Value, Next)
end.
-file("src/sparklinekit/internal/raster.gleam", 393).
?DOC(false).
-spec isqrt(integer()) -> integer().
isqrt(Value) ->
case Value =< 0 of
true ->
0;
false ->
isqrt_loop(Value, Value)
end.
-file("src/sparklinekit/internal/raster.gleam", 374).
?DOC(false).
-spec row_inset(integer(), integer(), integer()) -> integer().
row_inset(Dy, Height, Radius) ->
Top_zone = Dy < Radius,
Bottom_zone = Dy >= (Height - Radius),
case {Top_zone, Bottom_zone} of
{false, false} ->
0;
{_, _} ->
Local_y = case Top_zone of
true ->
(Radius - 1) - Dy;
false ->
Dy - (Height - Radius)
end,
R_sq = Radius * Radius,
Local_y_sq = Local_y * Local_y,
Max_x_sq = R_sq - Local_y_sq,
Dx = isqrt(Max_x_sq),
Radius - Dx
end.
-file("src/sparklinekit/internal/raster.gleam", 475).
?DOC(false).
-spec pixel_coverage(float(), float(), float()) -> float().
pixel_coverage(Pixel_centre, Line_centre, Half_thick) ->
Pixel_lo = Pixel_centre - 0.5,
Pixel_hi = Pixel_centre + 0.5,
Band_lo = Line_centre - Half_thick,
Band_hi = Line_centre + Half_thick,
Overlap_lo = gleam@float:max(Pixel_lo, Band_lo),
Overlap_hi = gleam@float:min(Pixel_hi, Band_hi),
Overlap = Overlap_hi - Overlap_lo,
case Overlap =< +0.0 of
true ->
+0.0;
false ->
case Overlap >= 1.0 of
true ->
1.0;
false ->
Overlap
end
end.
-file("src/sparklinekit/internal/raster.gleam", 446).
?DOC(false).
-spec plot_perpendicular(
canvas(),
integer(),
float(),
boolean(),
float(),
sparklinekit@internal@color:rgba()
) -> canvas().
plot_perpendicular(Canvas, Major, Minor_centre, Steep, Half_thick, Colour) ->
Minor_lo = math:floor(Minor_centre - Half_thick),
Minor_hi = math:ceil(Minor_centre + Half_thick),
Lo_i = erlang:round(Minor_lo),
Hi_i = erlang:round(Minor_hi),
_pipe = range(Lo_i, Hi_i),
gleam@list:fold(
_pipe,
Canvas,
fun(C, Minor) ->
Coverage = pixel_coverage(
erlang:float(Minor),
Minor_centre,
Half_thick
),
case Coverage > +0.0 of
false ->
C;
true ->
Tinted = sparklinekit@internal@color:with_alpha(
Colour,
Coverage
),
{Px, Py} = case Steep of
true ->
{Minor, Major};
false ->
{Major, Minor}
end,
put(C, Px, Py, Tinted)
end
end
).
-file("src/sparklinekit/internal/raster.gleam", 408).
?DOC(false).
-spec do_wu_line(
canvas(),
integer(),
integer(),
float(),
float(),
float(),
boolean(),
float(),
sparklinekit@internal@color:rgba()
) -> canvas().
do_wu_line(
Canvas,
Major,
End_major,
Raw_major,
Minor_centre,
Gradient,
Steep,
Half_thick,
Colour
) ->
case Major > End_major of
true ->
Canvas;
false ->
Updated = plot_perpendicular(
Canvas,
Major,
Minor_centre,
Steep,
Half_thick,
Colour
),
do_wu_line(
Updated,
Major + 1,
End_major,
Raw_major + 1.0,
Minor_centre + Gradient,
Gradient,
Steep,
Half_thick,
Colour
)
end.
-file("src/sparklinekit/internal/raster.gleam", 108).
?DOC(false).
-spec draw_line(
canvas(),
float(),
float(),
float(),
float(),
float(),
sparklinekit@internal@color:rgba()
) -> canvas().
draw_line(Canvas, X0, Y0, X1, Y1, Thickness, Colour) ->
Actual_thickness = case Thickness > +0.0 of
true ->
Thickness;
false ->
1.0
end,
Dx = X1 - X0,
Dy = Y1 - Y0,
Steep = gleam@float:absolute_value(Dy) > gleam@float:absolute_value(Dx),
{X0_, Y0_, X1_, Y1_} = case Steep of
true ->
{Y0, X0, Y1, X1};
false ->
{X0, Y0, X1, Y1}
end,
{X0_@1, Y0_@1, X1_@1, Y1_@1} = case X0_ > X1_ of
true ->
{X1_, Y1_, X0_, Y0_};
false ->
{X0_, Y0_, X1_, Y1_}
end,
New_dx = X1_@1 - X0_@1,
New_dy = Y1_@1 - Y0_@1,
Gradient = case New_dx =:= +0.0 of
true ->
1.0;
false ->
case New_dx of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> New_dy / Gleam@denominator
end
end,
Start_x = erlang:round(X0_@1),
End_x = erlang:round(X1_@1),
Half_thick = Actual_thickness / 2.0,
do_wu_line(
Canvas,
Start_x,
End_x,
erlang:float(Start_x),
Y0_@1 + (Gradient * (erlang:float(Start_x) - X0_@1)),
Gradient,
Steep,
Half_thick,
Colour
).
-file("src/sparklinekit/internal/raster.gleam", 497).
?DOC(false).
-spec min_int(integer(), integer()) -> integer().
min_int(A, B) ->
case A < B of
true ->
A;
false ->
B
end.
-file("src/sparklinekit/internal/raster.gleam", 504).
?DOC(false).
-spec max_int(integer(), integer()) -> integer().
max_int(A, B) ->
case A > B of
true ->
A;
false ->
B
end.
-file("src/sparklinekit/internal/raster.gleam", 322).
?DOC(false).
-spec fill_rect_int(
canvas(),
integer(),
integer(),
integer(),
integer(),
sparklinekit@internal@color:rgba()
) -> canvas().
fill_rect_int(Canvas, X0, Y0, X1, Y1, Colour) ->
X_lo = max_int(0, min_int(X0, X1)),
X_hi = min_int(erlang:element(2, Canvas), max_int(X0, X1)),
Y_lo = max_int(0, min_int(Y0, Y1)),
Y_hi = min_int(erlang:element(3, Canvas), max_int(Y0, Y1)),
case (X_hi =< X_lo) orelse (Y_hi =< Y_lo) of
true ->
Canvas;
false ->
Xs = range(X_lo, X_hi - 1),
_pipe = range(Y_lo, Y_hi - 1),
gleam@list:fold(
_pipe,
Canvas,
fun(C, Y) ->
gleam@list:fold(
Xs,
C,
fun(C2, X) -> put(C2, X, Y, Colour) end
)
end
)
end.
-file("src/sparklinekit/internal/raster.gleam", 60).
?DOC(false).
-spec fill_rect(
canvas(),
float(),
float(),
float(),
float(),
sparklinekit@internal@color:rgba()
) -> canvas().
fill_rect(Canvas, X, Y, Width, Height, Colour) ->
X0 = erlang:round(X),
Y0 = erlang:round(Y),
X1 = erlang:round(X + Width),
Y1 = erlang:round(Y + Height),
fill_rect_int(Canvas, X0, Y0, X1, Y1, Colour).
-file("src/sparklinekit/internal/raster.gleam", 346).
?DOC(false).
-spec rounded_rect_rows(
canvas(),
integer(),
integer(),
integer(),
integer(),
integer(),
sparklinekit@internal@color:rgba()
) -> canvas().
rounded_rect_rows(Canvas, X0, Y0, X1, Y1, R, Colour) ->
W = X1 - X0,
H = Y1 - Y0,
case (W =< 0) orelse (H =< 0) of
true ->
Canvas;
false ->
_pipe = range(0, H - 1),
gleam@list:fold(
_pipe,
Canvas,
fun(C, Dy) ->
Inset = row_inset(Dy, H, R),
Row_x0 = X0 + Inset,
Row_x1 = X1 - Inset,
case Row_x1 > Row_x0 of
true ->
fill_rect_int(
C,
Row_x0,
Y0 + Dy,
Row_x1,
(Y0 + Dy) + 1,
Colour
);
false ->
C
end
end
)
end.
-file("src/sparklinekit/internal/raster.gleam", 77).
?DOC(false).
-spec fill_rounded_rect(
canvas(),
float(),
float(),
float(),
float(),
float(),
sparklinekit@internal@color:rgba()
) -> canvas().
fill_rounded_rect(Canvas, X, Y, Width, Height, Radius, Colour) ->
X0 = erlang:round(X),
Y0 = erlang:round(Y),
X1 = erlang:round(X + Width),
Y1 = erlang:round(Y + Height),
W = X1 - X0,
H = Y1 - Y0,
R = begin
_pipe = Radius,
_pipe@1 = gleam@float:min(_pipe, erlang:float(W) / 2.0),
_pipe@2 = gleam@float:min(_pipe@1, erlang:float(H) / 2.0),
gleam@float:max(_pipe@2, +0.0)
end,
R_int = erlang:round(R),
case R_int =< 0 of
true ->
fill_rect_int(Canvas, X0, Y0, X1, Y1, Colour);
false ->
rounded_rect_rows(Canvas, X0, Y0, X1, Y1, R_int, Colour)
end.