From 84d8c6ae35f7b72705aebf0a3dd1802236482170 Mon Sep 17 00:00:00 2001 From: Gio Lucas Torres Date: Sat, 10 Sep 2022 13:48:23 -0300 Subject: [PATCH 1/3] Added support to draw rounded rectangles with varying corners. --- lib/scenic/script.ex | 134 ++++++++++++++++++++++++++++++++++++ test/scenic/script_test.exs | 69 +++++++++++++++++++ 2 files changed, 203 insertions(+) diff --git a/lib/scenic/script.ex b/lib/scenic/script.ex index 41f67266..6de63139 100644 --- a/lib/scenic/script.ex +++ b/lib/scenic/script.ex @@ -184,6 +184,7 @@ defmodule Scenic.Script do @op_draw_ellipse 0x09 @op_draw_text 0x0A @op_draw_sprites 0x0B + @op_draw_rrectv 0x0C @op_draw_script 0x0F @op_begin_path 0x20 @@ -272,6 +273,10 @@ defmodule Scenic.Script do x3 :: number, y3 :: number, fill_stroke()}} | {:draw_rect, {width :: number, height :: number, fill_stroke()}} | {:draw_rrect, {width :: number, height :: number, radius :: number, fill_stroke()}} + | {:draw_rrectv, + {width :: number, height :: number, upperLeftRadius :: number, + upperRightRadius :: number, lowerRightRadius :: number, lowerLeftRadius :: number, + fill_stroke()}} | {:draw_sector, {radius :: number, radians :: number, fill_stroke()}} | {:draw_arc, {radius :: number, radians :: number, fill_stroke()}} | {:draw_circle, {radius :: number, fill_stroke()}} @@ -534,6 +539,108 @@ defmodule Scenic.Script do [{:draw_rrect, {width, height, radius, flag}} | ops] end + @doc """ + Draw a rounded rectangle defined by height, width, radius1 and radius2. Can be filled or stroked. + + Radius1 and radius2 values will be set as follow: + + - Upper left corner: radius1 + - Upper right corner: radius2 + - Lower right corner: radius1 + - Lower left corner: radius2 + + Creates a new path and draws it. + """ + @spec draw_rounded_rectangle( + ops :: t(), + width :: number, + height :: number, + r1 :: number, + r2 :: number, + fill_stroke_flags :: fill_stroke() + ) :: ops :: t() + def draw_rounded_rectangle(ops, width, height, r1, r2, flag) do + upperLeftRadius = smallest([r1, width / 2, height / 2]) + upperRightRadius = smallest([r2, width / 2, height / 2]) + lowerRightRadius = smallest([r1, width / 2, height / 2]) + lowerLeftRadius = smallest([r2, width / 2, height / 2]) + + [ + {:draw_rrectv, + {width, height, upperLeftRadius, upperRightRadius, lowerRightRadius, lowerLeftRadius, flag}} + | ops + ] + end + + @doc """ + Draw a rounded rectangle defined by height, width, radius1, radius2 and radius3. Can be filled or stroked. + + Radius1 and radius2 values will be set as follow: + + - Upper left corner: radius1 + - Upper right corner: radius2 + - Lower right corner: radius3 + - Lower left corner: radius2 + + Creates a new path and draws it. + """ + @spec draw_rounded_rectangle( + ops :: t(), + width :: number, + height :: number, + r1 :: number, + r2 :: number, + r3 :: number, + fill_stroke_flags :: fill_stroke() + ) :: ops :: t() + def draw_rounded_rectangle(ops, width, height, r1, r2, r3, flag) do + upperLeftRadius = smallest([r1, width / 2, height / 2]) + upperRightRadius = smallest([r2, width / 2, height / 2]) + lowerRightRadius = smallest([r3, width / 2, height / 2]) + lowerLeftRadius = smallest([r2, width / 2, height / 2]) + + [ + {:draw_rrectv, + {width, height, upperLeftRadius, upperRightRadius, lowerRightRadius, lowerLeftRadius, flag}} + | ops + ] + end + + @doc """ + Draw a rounded rectangle defined by height, width, radius1, radius2, radius3 and radius4. Can be filled or stroked. + + Radius1 and radius2 values will be set as follow: + + - Upper left corner: radius1 + - Upper right corner: radius2 + - Lower right corner: radius3 + - Lower left corner: radius4 + + Creates a new path and draws it. + """ + @spec draw_rounded_rectangle( + ops :: t(), + width :: number, + height :: number, + r1 :: number, + r2 :: number, + r3 :: number, + r4 :: number, + fill_stroke_flags :: fill_stroke() + ) :: ops :: t() + def draw_rounded_rectangle(ops, width, height, r1, r2, r3, r4, flag) do + upperLeftRadius = smallest([r1, width / 2, height / 2]) + upperRightRadius = smallest([r2, width / 2, height / 2]) + lowerRightRadius = smallest([r3, width / 2, height / 2]) + lowerLeftRadius = smallest([r4, width / 2, height / 2]) + + [ + {:draw_rrectv, + {width, height, upperLeftRadius, upperRightRadius, lowerRightRadius, lowerLeftRadius, flag}} + | ops + ] + end + @doc """ Draw a sector defined by radius and an angle. Can be filled or stroked. @@ -1481,6 +1588,19 @@ defmodule Scenic.Script do ] end + defp serialize_op({:draw_rrectv, {w, h, ulR, urR, lrR, llR, flag}}) do + << + @op_draw_rrectv::16-big, + to_flag(flag)::16-big, + w::float-32-big, + h::float-32-big, + ulR::float-32-big, + urR::float-32-big, + lrR::float-32-big, + llR::float-32-big + >> + end + defp serialize_op({:script, id}) do [ << @@ -2214,6 +2334,20 @@ defmodule Scenic.Script do {{:draw_sprites, {id, cmds}}, bin} end + defp deserialize_op(<< + @op_draw_rrectv::16-big, + flag::16-big, + w::float-32-big, + h::float-32-big, + ulR::float-32-big, + urR::float-32-big, + lrR::float-32-big, + llR::float-32-big, + bin::binary + >>) do + {{:draw_rrectv, {w, h, ulR, urR, lrR, llR, from_flag(flag)}}, bin} + end + defp deserialize_op(<< @op_draw_script::16-big, id_size::16, diff --git a/test/scenic/script_test.exs b/test/scenic/script_test.exs index 2de9ec4b..e5a17a75 100644 --- a/test/scenic/script_test.exs +++ b/test/scenic/script_test.exs @@ -118,6 +118,75 @@ defmodule Scenic.ScriptTest do [{:draw_rrect, {13.0, 12.0, 6.0, :stroke}}] end + test "draw_rounded_rectangle varying all corners works" do + expected = [{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 4.0, 5.0, :fill}}] + assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, 4, 5, :fill) == expected + assert expected == Script.serialize(expected) |> Script.deserialize() + + assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, 4, 5, :stroke) == + [{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 4.0, 5.0, :stroke}}] + + assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, 4, 5, :fill_stroke) == + [{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 4.0, 5.0, :fill_stroke}}] + end + + test "draw_rounded_rectangle varying 3 corners works" do + expected = [{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 4.0, 3.0, :fill}}] + assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, 4, :fill) == expected + assert expected == Script.serialize(expected) |> Script.deserialize() + + assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, 4, :stroke) == + [{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 4.0, 3.0, :stroke}}] + + assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, 4, :fill_stroke) == + [{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 4.0, 3.0, :fill_stroke}}] + end + + test "draw_rounded_rectangle varying 2 corners works" do + expected = [{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 2.0, 3.0, :fill}}] + assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, :fill) == expected + assert expected == Script.serialize(expected) |> Script.deserialize() + + assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, :stroke) == + [{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 2.0, 3.0, :stroke}}] + + assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, :fill_stroke) == + [{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 2.0, 3.0, :fill_stroke}}] + end + + test "draw_rounded_rectangle varying all corners shrinks radius if too big" do + assert Script.draw_rounded_rectangle([], 10, 12, 30, 40, 50, 4, :fill) == + [{:draw_rrectv, {10.0, 12.0, 5.0, 5.0, 5.0, 4.0, :fill}}] + + assert Script.draw_rounded_rectangle([], 13, 12, 30, 40, 5, 60, :stroke) == + [{:draw_rrectv, {13.0, 12.0, 6.0, 6.0, 5.0, 6.0, :stroke}}] + + assert Script.draw_rounded_rectangle([], 13, 12, 30, 4, 50, 60, :stroke) == + [{:draw_rrectv, {13.0, 12.0, 6.0, 4.0, 6.0, 6.0, :stroke}}] + + assert Script.draw_rounded_rectangle([], 13, 12, 3, 40, 50, 60, :stroke) == + [{:draw_rrectv, {13.0, 12.0, 3.0, 6.0, 6.0, 6.0, :stroke}}] + end + + test "draw_rounded_rectangle varying 3 corners shrinks radius if too big" do + assert Script.draw_rounded_rectangle([], 10, 12, 30, 40, 5, :fill) == + [{:draw_rrectv, {10.0, 12.0, 5.0, 5.0, 5.0, 5.0, :fill}}] + + assert Script.draw_rounded_rectangle([], 13, 12, 30, 4, 50, :stroke) == + [{:draw_rrectv, {13.0, 12.0, 6.0, 4.0, 6.0, 4.0, :stroke}}] + + assert Script.draw_rounded_rectangle([], 13, 12, 3, 40, 50, :stroke) == + [{:draw_rrectv, {13.0, 12.0, 3.0, 6.0, 6.0, 6.0, :stroke}}] + end + + test "draw_rounded_rectangle varying 2 corners shrinks radius if too big" do + assert Script.draw_rounded_rectangle([], 10, 12, 30, 4, :fill) == + [{:draw_rrectv, {10.0, 12.0, 5.0, 4.0, 5.0, 4.0, :fill}}] + + assert Script.draw_rounded_rectangle([], 13, 12, 3, 40, :stroke) == + [{:draw_rrectv, {13.0, 12.0, 3.0, 6.0, 3.0, 6.0, :stroke}}] + end + test "draw_sector works" do expected = [{:draw_sector, {10.0, 3.0, :fill}}] assert Script.draw_sector([], 10, 3, :fill) == expected From 853bababae401cc174aa9dac8ebedd1da7016a65 Mon Sep 17 00:00:00 2001 From: Gio Lucas Torres Date: Sun, 18 Sep 2022 18:32:46 -0300 Subject: [PATCH 2/3] Changed Script interface for drawing variable rounded rectangles --- lib/scenic/script.ex | 73 ++----------------------------------- test/scenic/script_test.exs | 61 +++++-------------------------- 2 files changed, 12 insertions(+), 122 deletions(-) diff --git a/lib/scenic/script.ex b/lib/scenic/script.ex index 6de63139..14a8c1c2 100644 --- a/lib/scenic/script.ex +++ b/lib/scenic/script.ex @@ -539,77 +539,10 @@ defmodule Scenic.Script do [{:draw_rrect, {width, height, radius, flag}} | ops] end - @doc """ - Draw a rounded rectangle defined by height, width, radius1 and radius2. Can be filled or stroked. - - Radius1 and radius2 values will be set as follow: - - - Upper left corner: radius1 - - Upper right corner: radius2 - - Lower right corner: radius1 - - Lower left corner: radius2 - - Creates a new path and draws it. - """ - @spec draw_rounded_rectangle( - ops :: t(), - width :: number, - height :: number, - r1 :: number, - r2 :: number, - fill_stroke_flags :: fill_stroke() - ) :: ops :: t() - def draw_rounded_rectangle(ops, width, height, r1, r2, flag) do - upperLeftRadius = smallest([r1, width / 2, height / 2]) - upperRightRadius = smallest([r2, width / 2, height / 2]) - lowerRightRadius = smallest([r1, width / 2, height / 2]) - lowerLeftRadius = smallest([r2, width / 2, height / 2]) - - [ - {:draw_rrectv, - {width, height, upperLeftRadius, upperRightRadius, lowerRightRadius, lowerLeftRadius, flag}} - | ops - ] - end - - @doc """ - Draw a rounded rectangle defined by height, width, radius1, radius2 and radius3. Can be filled or stroked. - - Radius1 and radius2 values will be set as follow: - - - Upper left corner: radius1 - - Upper right corner: radius2 - - Lower right corner: radius3 - - Lower left corner: radius2 - - Creates a new path and draws it. - """ - @spec draw_rounded_rectangle( - ops :: t(), - width :: number, - height :: number, - r1 :: number, - r2 :: number, - r3 :: number, - fill_stroke_flags :: fill_stroke() - ) :: ops :: t() - def draw_rounded_rectangle(ops, width, height, r1, r2, r3, flag) do - upperLeftRadius = smallest([r1, width / 2, height / 2]) - upperRightRadius = smallest([r2, width / 2, height / 2]) - lowerRightRadius = smallest([r3, width / 2, height / 2]) - lowerLeftRadius = smallest([r2, width / 2, height / 2]) - - [ - {:draw_rrectv, - {width, height, upperLeftRadius, upperRightRadius, lowerRightRadius, lowerLeftRadius, flag}} - | ops - ] - end - @doc """ Draw a rounded rectangle defined by height, width, radius1, radius2, radius3 and radius4. Can be filled or stroked. - Radius1 and radius2 values will be set as follow: + Radii values will be set as follow: - Upper left corner: radius1 - Upper right corner: radius2 @@ -618,7 +551,7 @@ defmodule Scenic.Script do Creates a new path and draws it. """ - @spec draw_rounded_rectangle( + @spec draw_variable_rounded_rectangle( ops :: t(), width :: number, height :: number, @@ -628,7 +561,7 @@ defmodule Scenic.Script do r4 :: number, fill_stroke_flags :: fill_stroke() ) :: ops :: t() - def draw_rounded_rectangle(ops, width, height, r1, r2, r3, r4, flag) do + def draw_variable_rounded_rectangle(ops, width, height, r1, r2, r3, r4, flag) do upperLeftRadius = smallest([r1, width / 2, height / 2]) upperRightRadius = smallest([r2, width / 2, height / 2]) lowerRightRadius = smallest([r3, width / 2, height / 2]) diff --git a/test/scenic/script_test.exs b/test/scenic/script_test.exs index e5a17a75..68973493 100644 --- a/test/scenic/script_test.exs +++ b/test/scenic/script_test.exs @@ -118,75 +118,32 @@ defmodule Scenic.ScriptTest do [{:draw_rrect, {13.0, 12.0, 6.0, :stroke}}] end - test "draw_rounded_rectangle varying all corners works" do + test "draw_variable_rounded_rectangle varying all corners works" do expected = [{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 4.0, 5.0, :fill}}] - assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, 4, 5, :fill) == expected + assert Script.draw_variable_rounded_rectangle([], 10, 11, 2, 3, 4, 5, :fill) == expected assert expected == Script.serialize(expected) |> Script.deserialize() - assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, 4, 5, :stroke) == + assert Script.draw_variable_rounded_rectangle([], 10, 11, 2, 3, 4, 5, :stroke) == [{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 4.0, 5.0, :stroke}}] - assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, 4, 5, :fill_stroke) == + assert Script.draw_variable_rounded_rectangle([], 10, 11, 2, 3, 4, 5, :fill_stroke) == [{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 4.0, 5.0, :fill_stroke}}] end - test "draw_rounded_rectangle varying 3 corners works" do - expected = [{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 4.0, 3.0, :fill}}] - assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, 4, :fill) == expected - assert expected == Script.serialize(expected) |> Script.deserialize() - - assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, 4, :stroke) == - [{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 4.0, 3.0, :stroke}}] - - assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, 4, :fill_stroke) == - [{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 4.0, 3.0, :fill_stroke}}] - end - - test "draw_rounded_rectangle varying 2 corners works" do - expected = [{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 2.0, 3.0, :fill}}] - assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, :fill) == expected - assert expected == Script.serialize(expected) |> Script.deserialize() - - assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, :stroke) == - [{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 2.0, 3.0, :stroke}}] - - assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, :fill_stroke) == - [{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 2.0, 3.0, :fill_stroke}}] - end - - test "draw_rounded_rectangle varying all corners shrinks radius if too big" do - assert Script.draw_rounded_rectangle([], 10, 12, 30, 40, 50, 4, :fill) == + test "draw_variable_rounded_rectangle varying all corners shrinks radius if too big" do + assert Script.draw_variable_rounded_rectangle([], 10, 12, 30, 40, 50, 4, :fill) == [{:draw_rrectv, {10.0, 12.0, 5.0, 5.0, 5.0, 4.0, :fill}}] - assert Script.draw_rounded_rectangle([], 13, 12, 30, 40, 5, 60, :stroke) == + assert Script.draw_variable_rounded_rectangle([], 13, 12, 30, 40, 5, 60, :stroke) == [{:draw_rrectv, {13.0, 12.0, 6.0, 6.0, 5.0, 6.0, :stroke}}] - assert Script.draw_rounded_rectangle([], 13, 12, 30, 4, 50, 60, :stroke) == + assert Script.draw_variable_rounded_rectangle([], 13, 12, 30, 4, 50, 60, :stroke) == [{:draw_rrectv, {13.0, 12.0, 6.0, 4.0, 6.0, 6.0, :stroke}}] - assert Script.draw_rounded_rectangle([], 13, 12, 3, 40, 50, 60, :stroke) == + assert Script.draw_variable_rounded_rectangle([], 13, 12, 3, 40, 50, 60, :stroke) == [{:draw_rrectv, {13.0, 12.0, 3.0, 6.0, 6.0, 6.0, :stroke}}] end - test "draw_rounded_rectangle varying 3 corners shrinks radius if too big" do - assert Script.draw_rounded_rectangle([], 10, 12, 30, 40, 5, :fill) == - [{:draw_rrectv, {10.0, 12.0, 5.0, 5.0, 5.0, 5.0, :fill}}] - - assert Script.draw_rounded_rectangle([], 13, 12, 30, 4, 50, :stroke) == - [{:draw_rrectv, {13.0, 12.0, 6.0, 4.0, 6.0, 4.0, :stroke}}] - - assert Script.draw_rounded_rectangle([], 13, 12, 3, 40, 50, :stroke) == - [{:draw_rrectv, {13.0, 12.0, 3.0, 6.0, 6.0, 6.0, :stroke}}] - end - - test "draw_rounded_rectangle varying 2 corners shrinks radius if too big" do - assert Script.draw_rounded_rectangle([], 10, 12, 30, 4, :fill) == - [{:draw_rrectv, {10.0, 12.0, 5.0, 4.0, 5.0, 4.0, :fill}}] - - assert Script.draw_rounded_rectangle([], 13, 12, 3, 40, :stroke) == - [{:draw_rrectv, {13.0, 12.0, 3.0, 6.0, 3.0, 6.0, :stroke}}] - end - test "draw_sector works" do expected = [{:draw_sector, {10.0, 3.0, :fill}}] assert Script.draw_sector([], 10, 3, :fill) == expected From f63d4e42f040ca53aee8677b8351e248cf1df6e1 Mon Sep 17 00:00:00 2001 From: Gio Lucas Torres Date: Fri, 4 Aug 2023 11:12:49 +0200 Subject: [PATCH 3/3] Using sneak case variables name on Scenic.Script.draw_variable_rounded_rectangle/8 --- lib/scenic/script.ex | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/scenic/script.ex b/lib/scenic/script.ex index 14a8c1c2..eeb1e7b8 100644 --- a/lib/scenic/script.ex +++ b/lib/scenic/script.ex @@ -562,14 +562,14 @@ defmodule Scenic.Script do fill_stroke_flags :: fill_stroke() ) :: ops :: t() def draw_variable_rounded_rectangle(ops, width, height, r1, r2, r3, r4, flag) do - upperLeftRadius = smallest([r1, width / 2, height / 2]) - upperRightRadius = smallest([r2, width / 2, height / 2]) - lowerRightRadius = smallest([r3, width / 2, height / 2]) - lowerLeftRadius = smallest([r4, width / 2, height / 2]) + upper_left_radius = smallest([r1, width / 2, height / 2]) + upper_right_radius = smallest([r2, width / 2, height / 2]) + lower_right_radius = smallest([r3, width / 2, height / 2]) + lower_left_radius = smallest([r4, width / 2, height / 2]) [ {:draw_rrectv, - {width, height, upperLeftRadius, upperRightRadius, lowerRightRadius, lowerLeftRadius, flag}} + {width, height, upper_left_radius, upper_right_radius, lower_right_radius, lower_left_radius, flag}} | ops ] end