-module(svg_path@transform@serialize).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/svg_path/transform/serialize.gleam").
-export([default_options/0, decimal_options/1, fixed_decimal_options/1, force_matrix/1, to_string_with_options/2, to_string/1]).
-export_type([linear_transform/0, options/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(
" SVG transform attribute serializer.\n"
"\n"
" This module serializes affine matrices as SVG transform attribute strings.\n"
" It prefers readable transform functions such as `translate`, `scale`,\n"
" `rotate`, and `skew` when a matrix clearly matches them, and falls back to\n"
" `matrix(a b c d e f)` otherwise.\n"
).
-type linear_transform() :: matrix2x2 |
identity2x2 |
{scale2x2, float(), float()} |
{skew_x2x2, float()} |
{skew_y2x2, float()} |
{rotate_scale2x2, float(), float(), float()}.
-type options() :: {options,
gleam@option:option(integer()),
boolean(),
boolean()}.
-file("src/svg_path/transform/serialize.gleam", 35).
?DOC(
" Default transform serialization options.\n"
"\n"
" Defaults to up to 5 decimal places, stripped trailing zeroes, and readable\n"
" transform functions when possible.\n"
).
-spec default_options() -> options().
default_options() ->
{options, {some, 5}, false, false}.
-file("src/svg_path/transform/serialize.gleam", 42).
?DOC(
" Create options that round numbers to the given number of decimal places.\n"
"\n"
" Trailing zeroes are stripped. Negative decimal places are clamped to zero.\n"
).
-spec decimal_options(integer()) -> options().
decimal_options(Decimal_places) ->
{options, {some, Decimal_places}, false, false}.
-file("src/svg_path/transform/serialize.gleam", 54).
?DOC(
" Create options that round numbers and keep exactly the given number of\n"
" decimal places.\n"
"\n"
" Negative decimal places are clamped to zero.\n"
).
-spec fixed_decimal_options(integer()) -> options().
fixed_decimal_options(Decimal_places) ->
{options, {some, Decimal_places}, true, false}.
-file("src/svg_path/transform/serialize.gleam", 66).
?DOC(
" Force serialization as `matrix(a b c d e f)`.\n"
"\n"
" This disables the nicer `translate`, `scale`, `rotate`, and `skew`\n"
" representations.\n"
).
-spec force_matrix(options()) -> options().
force_matrix(Options) ->
{options, erlang:element(2, Options), erlang:element(3, Options), true}.
-file("src/svg_path/transform/serialize.gleam", 246).
-spec transform_function(binary(), binary()) -> binary().
transform_function(Name, Arguments) ->
<<<<<<Name/binary, "("/utf8>>/binary, Arguments/binary>>/binary, ")"/utf8>>.
-file("src/svg_path/transform/serialize.gleam", 328).
-spec strip_trailing_zeros(binary()) -> binary().
strip_trailing_zeros(String) ->
case gleam_stdlib:string_ends_with(String, <<"0"/utf8>>) of
true ->
_pipe = String,
_pipe@1 = gleam@string:drop_end(_pipe, 1),
strip_trailing_zeros(_pipe@1);
false ->
String
end.
-file("src/svg_path/transform/serialize.gleam", 314).
-spec strip_trailing_decimal_zeros(binary()) -> binary().
strip_trailing_decimal_zeros(Number) ->
case gleam@string:split_once(Number, <<"."/utf8>>) of
{error, _} ->
Number;
{ok, {Whole, Fractional}} ->
Fractional@1 = strip_trailing_zeros(Fractional),
case Fractional@1 of
<<""/utf8>> ->
Whole;
_ ->
<<<<Whole/binary, "."/utf8>>/binary, Fractional@1/binary>>
end
end.
-file("src/svg_path/transform/serialize.gleam", 343).
-spec power_of_ten_int(integer()) -> integer().
power_of_ten_int(Exponent) ->
case Exponent =< 0 of
true ->
1;
false ->
10 * power_of_ten_int(Exponent - 1)
end.
-file("src/svg_path/transform/serialize.gleam", 339).
-spec power_of_ten(integer()) -> float().
power_of_ten(Exponent) ->
_pipe = power_of_ten_int(Exponent),
erlang:float(_pipe).
-file("src/svg_path/transform/serialize.gleam", 289).
-spec fixed_decimal(float(), integer()) -> binary().
fixed_decimal(Number, Decimal_places) ->
Decimal_places@1 = gleam@int:max(Decimal_places, 0),
Scale = power_of_ten(Decimal_places@1),
Scaled = begin
_pipe = Number * Scale,
erlang:round(_pipe)
end,
Sign = case Scaled < 0 of
true ->
<<"-"/utf8>>;
false ->
<<""/utf8>>
end,
Absolute_scaled = gleam@int:absolute_value(Scaled),
case Decimal_places@1 of
0 ->
<<Sign/binary, (erlang:integer_to_binary(Absolute_scaled))/binary>>;
_ ->
Whole = case power_of_ten_int(Decimal_places@1) of
0 -> 0;
Gleam@denominator -> Absolute_scaled div Gleam@denominator
end,
Fractional = case power_of_ten_int(Decimal_places@1) of
0 -> 0;
Gleam@denominator@1 -> Absolute_scaled rem Gleam@denominator@1
end,
Fractional@1 = begin
_pipe@1 = Fractional,
_pipe@2 = erlang:integer_to_binary(_pipe@1),
gleam@string:pad_start(_pipe@2, Decimal_places@1, <<"0"/utf8>>)
end,
<<<<<<Sign/binary, (erlang:integer_to_binary(Whole))/binary>>/binary,
"."/utf8>>/binary,
Fractional@1/binary>>
end.
-file("src/svg_path/transform/serialize.gleam", 280).
-spec decimal(float(), integer(), boolean()) -> binary().
decimal(Number, Decimal_places, Fixed_decimals) ->
Fixed = fixed_decimal(Number, Decimal_places),
case Fixed_decimals of
true ->
Fixed;
false ->
strip_trailing_decimal_zeros(Fixed)
end.
-file("src/svg_path/transform/serialize.gleam", 272).
-spec number(float(), options()) -> binary().
number(Number, Options) ->
case erlang:element(2, Options) of
none ->
gleam_stdlib:float_to_string(Number);
{some, Decimal_places} ->
decimal(Number, Decimal_places, erlang:element(3, Options))
end.
-file("src/svg_path/transform/serialize.gleam", 93).
-spec translate_transform(float(), float(), options()) -> binary().
translate_transform(X, Y, Options) ->
Arguments = case Y =:= +0.0 of
true ->
number(X, Options);
false ->
<<<<(number(X, Options))/binary, " "/utf8>>/binary,
(number(Y, Options))/binary>>
end,
transform_function(<<"translate"/utf8>>, Arguments).
-file("src/svg_path/transform/serialize.gleam", 115).
-spec translate_optional_transform(float(), float(), options()) -> binary().
translate_optional_transform(X, Y, Options) ->
case (X =:= +0.0) andalso (Y =:= +0.0) of
true ->
<<""/utf8>>;
false ->
translate_transform(X, Y, Options)
end.
-file("src/svg_path/transform/serialize.gleam", 102).
-spec scale_transform(float(), float(), options()) -> binary().
scale_transform(X, Y, Options) ->
Arguments = case X =:= Y of
true ->
number(X, Options);
false ->
<<<<(number(X, Options))/binary, " "/utf8>>/binary,
(number(Y, Options))/binary>>
end,
transform_function(<<"scale"/utf8>>, Arguments).
-file("src/svg_path/transform/serialize.gleam", 268).
-spec close(float(), float()) -> boolean().
close(Left, Right) ->
gleam@float:absolute_value(Left - Right) =< 0.000001.
-file("src/svg_path/transform/serialize.gleam", 206).
-spec scale_optional_transform(float(), float(), options()) -> binary().
scale_optional_transform(X, Y, Options) ->
case close(X, 1.0) andalso close(Y, 1.0) of
true ->
<<""/utf8>>;
false ->
scale_transform(X, Y, Options)
end.
-file("src/svg_path/transform/serialize.gleam", 111).
-spec rotate_transform(float(), options()) -> binary().
rotate_transform(Degrees, Options) ->
transform_function(<<"rotate"/utf8>>, number(Degrees, Options)).
-file("src/svg_path/transform/serialize.gleam", 250).
-spec degrees_from_tangent(float()) -> float().
degrees_from_tangent(Tangent) ->
case gleam_community@maths:pi() of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> gleam_community@maths:atan(Tangent) * 180.0 / Gleam@denominator
end.
-file("src/svg_path/transform/serialize.gleam", 217).
-spec skew_y_transform(float(), options()) -> binary().
skew_y_transform(Tangent, Options) ->
transform_function(
<<"skewY"/utf8>>,
number(degrees_from_tangent(Tangent), Options)
).
-file("src/svg_path/transform/serialize.gleam", 213).
-spec skew_x_transform(float(), options()) -> binary().
skew_x_transform(Tangent, Options) ->
transform_function(
<<"skewX"/utf8>>,
number(degrees_from_tangent(Tangent), Options)
).
-file("src/svg_path/transform/serialize.gleam", 140).
-spec linear_transform(linear_transform(), options()) -> binary().
linear_transform(Linear, Options) ->
case Linear of
matrix2x2 ->
<<""/utf8>>;
identity2x2 ->
<<""/utf8>>;
{scale2x2, X, Y} ->
scale_transform(X, Y, Options);
{skew_x2x2, Tangent} ->
skew_x_transform(Tangent, Options);
{skew_y2x2, Tangent@1} ->
skew_y_transform(Tangent@1, Options);
{rotate_scale2x2, Degrees, Scale_x, Scale_y} ->
<<(rotate_transform(Degrees, Options))/binary,
(scale_optional_transform(Scale_x, Scale_y, Options))/binary>>
end.
-file("src/svg_path/transform/serialize.gleam", 126).
-spec affine_transform(linear_transform(), float(), float(), options()) -> binary().
affine_transform(Linear, Translate_x, Translate_y, Options) ->
case linear_transform(Linear, Options) of
<<""/utf8>> ->
translate_transform(Translate_x, Translate_y, Options);
Transform ->
<<(translate_optional_transform(Translate_x, Translate_y, Options))/binary,
Transform/binary>>
end.
-file("src/svg_path/transform/serialize.gleam", 221).
-spec matrix_transform(
float(),
float(),
float(),
float(),
float(),
float(),
options()
) -> binary().
matrix_transform(A, B, C, D, E, F, Options) ->
transform_function(
<<"matrix"/utf8>>,
<<<<<<<<<<<<<<<<<<<<(number(A, Options))/binary, " "/utf8>>/binary,
(number(B, Options))/binary>>/binary,
" "/utf8>>/binary,
(number(C, Options))/binary>>/binary,
" "/utf8>>/binary,
(number(D, Options))/binary>>/binary,
" "/utf8>>/binary,
(number(E, Options))/binary>>/binary,
" "/utf8>>/binary,
(number(F, Options))/binary>>
).
-file("src/svg_path/transform/serialize.gleam", 254).
-spec radians_to_degrees(float()) -> float().
radians_to_degrees(Radians) ->
case gleam_community@maths:pi() of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> Radians * 180.0 / Gleam@denominator
end.
-file("src/svg_path/transform/serialize.gleam", 264).
-spec close_to_zero(float()) -> boolean().
close_to_zero(Value) ->
gleam@float:absolute_value(Value) =< 0.000001.
-file("src/svg_path/transform/serialize.gleam", 258).
-spec length(float(), float()) -> float().
length(X, Y) ->
Result@1 = case gleam@float:square_root((X * X) + (Y * Y)) of
{ok, Result} -> Result;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"svg_path/transform/serialize"/utf8>>,
function => <<"length"/utf8>>,
line => 259,
value => _assert_fail,
start => 6826,
'end' => 6885,
pattern_start => 6837,
pattern_end => 6847})
end,
Result@1.
-file("src/svg_path/transform/serialize.gleam", 180).
-spec analyze_rotation_scale(float(), float(), float(), float()) -> linear_transform().
analyze_rotation_scale(A, B, C, D) ->
Scale_x = length(A, B),
Scale_y = length(C, D),
Determinant = (A * D) - (B * C),
Dot_product = (A * C) + (B * D),
case (((Scale_x > 0.000001) andalso (Scale_y > 0.000001)) andalso (Determinant
> 0.000001))
andalso close_to_zero(Dot_product) of
false ->
matrix2x2;
true ->
Rotation_degrees = radians_to_degrees(
gleam_community@maths:atan2(B, A)
),
{rotate_scale2x2, Rotation_degrees, Scale_x, Scale_y}
end.
-file("src/svg_path/transform/serialize.gleam", 153).
-spec analyze_linear_transform(float(), float(), float(), float()) -> linear_transform().
analyze_linear_transform(A, B, C, D) ->
case (((A =:= 1.0) andalso (B =:= +0.0)) andalso (C =:= +0.0)) andalso (D
=:= 1.0) of
true ->
identity2x2;
false ->
case (B =:= +0.0) andalso (C =:= +0.0) of
true ->
{scale2x2, A, D};
false ->
case ((A =:= 1.0) andalso (B =:= +0.0)) andalso (D =:= 1.0) of
true ->
{skew_x2x2, C};
false ->
case ((A =:= 1.0) andalso (C =:= +0.0)) andalso (D
=:= 1.0) of
true ->
{skew_y2x2, B};
false ->
analyze_rotation_scale(A, B, C, D)
end
end
end
end.
-file("src/svg_path/transform/serialize.gleam", 76).
?DOC(" Serialize a transform matrix with custom options.\n").
-spec to_string_with_options(svg_path@transform:matrix(), options()) -> binary().
to_string_with_options(Transform, Options) ->
{A, B, C, D, E, F} = svg_path@transform:to_tuple(Transform),
case erlang:element(4, Options) of
true ->
matrix_transform(A, B, C, D, E, F, Options);
false ->
case analyze_linear_transform(A, B, C, D) of
matrix2x2 ->
matrix_transform(A, B, C, D, E, F, Options);
Linear ->
affine_transform(Linear, E, F, Options)
end
end.
-file("src/svg_path/transform/serialize.gleam", 71).
?DOC(" Serialize a transform matrix with default options.\n").
-spec to_string(svg_path@transform:matrix()) -> binary().
to_string(Transform) ->
to_string_with_options(Transform, default_options()).