Skip to content

Commit 84d8c6a

Browse files
committed
Added support to draw rounded rectangles with varying corners.
1 parent 391e9d2 commit 84d8c6a

File tree

2 files changed

+203
-0
lines changed

2 files changed

+203
-0
lines changed

lib/scenic/script.ex

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ defmodule Scenic.Script do
184184
@op_draw_ellipse 0x09
185185
@op_draw_text 0x0A
186186
@op_draw_sprites 0x0B
187+
@op_draw_rrectv 0x0C
187188
@op_draw_script 0x0F
188189

189190
@op_begin_path 0x20
@@ -272,6 +273,10 @@ defmodule Scenic.Script do
272273
x3 :: number, y3 :: number, fill_stroke()}}
273274
| {:draw_rect, {width :: number, height :: number, fill_stroke()}}
274275
| {:draw_rrect, {width :: number, height :: number, radius :: number, fill_stroke()}}
276+
| {:draw_rrectv,
277+
{width :: number, height :: number, upperLeftRadius :: number,
278+
upperRightRadius :: number, lowerRightRadius :: number, lowerLeftRadius :: number,
279+
fill_stroke()}}
275280
| {:draw_sector, {radius :: number, radians :: number, fill_stroke()}}
276281
| {:draw_arc, {radius :: number, radians :: number, fill_stroke()}}
277282
| {:draw_circle, {radius :: number, fill_stroke()}}
@@ -534,6 +539,108 @@ defmodule Scenic.Script do
534539
[{:draw_rrect, {width, height, radius, flag}} | ops]
535540
end
536541

542+
@doc """
543+
Draw a rounded rectangle defined by height, width, radius1 and radius2. Can be filled or stroked.
544+
545+
Radius1 and radius2 values will be set as follow:
546+
547+
- Upper left corner: radius1
548+
- Upper right corner: radius2
549+
- Lower right corner: radius1
550+
- Lower left corner: radius2
551+
552+
Creates a new path and draws it.
553+
"""
554+
@spec draw_rounded_rectangle(
555+
ops :: t(),
556+
width :: number,
557+
height :: number,
558+
r1 :: number,
559+
r2 :: number,
560+
fill_stroke_flags :: fill_stroke()
561+
) :: ops :: t()
562+
def draw_rounded_rectangle(ops, width, height, r1, r2, flag) do
563+
upperLeftRadius = smallest([r1, width / 2, height / 2])
564+
upperRightRadius = smallest([r2, width / 2, height / 2])
565+
lowerRightRadius = smallest([r1, width / 2, height / 2])
566+
lowerLeftRadius = smallest([r2, width / 2, height / 2])
567+
568+
[
569+
{:draw_rrectv,
570+
{width, height, upperLeftRadius, upperRightRadius, lowerRightRadius, lowerLeftRadius, flag}}
571+
| ops
572+
]
573+
end
574+
575+
@doc """
576+
Draw a rounded rectangle defined by height, width, radius1, radius2 and radius3. Can be filled or stroked.
577+
578+
Radius1 and radius2 values will be set as follow:
579+
580+
- Upper left corner: radius1
581+
- Upper right corner: radius2
582+
- Lower right corner: radius3
583+
- Lower left corner: radius2
584+
585+
Creates a new path and draws it.
586+
"""
587+
@spec draw_rounded_rectangle(
588+
ops :: t(),
589+
width :: number,
590+
height :: number,
591+
r1 :: number,
592+
r2 :: number,
593+
r3 :: number,
594+
fill_stroke_flags :: fill_stroke()
595+
) :: ops :: t()
596+
def draw_rounded_rectangle(ops, width, height, r1, r2, r3, flag) do
597+
upperLeftRadius = smallest([r1, width / 2, height / 2])
598+
upperRightRadius = smallest([r2, width / 2, height / 2])
599+
lowerRightRadius = smallest([r3, width / 2, height / 2])
600+
lowerLeftRadius = smallest([r2, width / 2, height / 2])
601+
602+
[
603+
{:draw_rrectv,
604+
{width, height, upperLeftRadius, upperRightRadius, lowerRightRadius, lowerLeftRadius, flag}}
605+
| ops
606+
]
607+
end
608+
609+
@doc """
610+
Draw a rounded rectangle defined by height, width, radius1, radius2, radius3 and radius4. Can be filled or stroked.
611+
612+
Radius1 and radius2 values will be set as follow:
613+
614+
- Upper left corner: radius1
615+
- Upper right corner: radius2
616+
- Lower right corner: radius3
617+
- Lower left corner: radius4
618+
619+
Creates a new path and draws it.
620+
"""
621+
@spec draw_rounded_rectangle(
622+
ops :: t(),
623+
width :: number,
624+
height :: number,
625+
r1 :: number,
626+
r2 :: number,
627+
r3 :: number,
628+
r4 :: number,
629+
fill_stroke_flags :: fill_stroke()
630+
) :: ops :: t()
631+
def draw_rounded_rectangle(ops, width, height, r1, r2, r3, r4, flag) do
632+
upperLeftRadius = smallest([r1, width / 2, height / 2])
633+
upperRightRadius = smallest([r2, width / 2, height / 2])
634+
lowerRightRadius = smallest([r3, width / 2, height / 2])
635+
lowerLeftRadius = smallest([r4, width / 2, height / 2])
636+
637+
[
638+
{:draw_rrectv,
639+
{width, height, upperLeftRadius, upperRightRadius, lowerRightRadius, lowerLeftRadius, flag}}
640+
| ops
641+
]
642+
end
643+
537644
@doc """
538645
Draw a sector defined by radius and an angle. Can be filled or stroked.
539646
@@ -1481,6 +1588,19 @@ defmodule Scenic.Script do
14811588
]
14821589
end
14831590

1591+
defp serialize_op({:draw_rrectv, {w, h, ulR, urR, lrR, llR, flag}}) do
1592+
<<
1593+
@op_draw_rrectv::16-big,
1594+
to_flag(flag)::16-big,
1595+
w::float-32-big,
1596+
h::float-32-big,
1597+
ulR::float-32-big,
1598+
urR::float-32-big,
1599+
lrR::float-32-big,
1600+
llR::float-32-big
1601+
>>
1602+
end
1603+
14841604
defp serialize_op({:script, id}) do
14851605
[
14861606
<<
@@ -2214,6 +2334,20 @@ defmodule Scenic.Script do
22142334
{{:draw_sprites, {id, cmds}}, bin}
22152335
end
22162336

2337+
defp deserialize_op(<<
2338+
@op_draw_rrectv::16-big,
2339+
flag::16-big,
2340+
w::float-32-big,
2341+
h::float-32-big,
2342+
ulR::float-32-big,
2343+
urR::float-32-big,
2344+
lrR::float-32-big,
2345+
llR::float-32-big,
2346+
bin::binary
2347+
>>) do
2348+
{{:draw_rrectv, {w, h, ulR, urR, lrR, llR, from_flag(flag)}}, bin}
2349+
end
2350+
22172351
defp deserialize_op(<<
22182352
@op_draw_script::16-big,
22192353
id_size::16,

test/scenic/script_test.exs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,75 @@ defmodule Scenic.ScriptTest do
118118
[{:draw_rrect, {13.0, 12.0, 6.0, :stroke}}]
119119
end
120120

121+
test "draw_rounded_rectangle varying all corners works" do
122+
expected = [{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 4.0, 5.0, :fill}}]
123+
assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, 4, 5, :fill) == expected
124+
assert expected == Script.serialize(expected) |> Script.deserialize()
125+
126+
assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, 4, 5, :stroke) ==
127+
[{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 4.0, 5.0, :stroke}}]
128+
129+
assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, 4, 5, :fill_stroke) ==
130+
[{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 4.0, 5.0, :fill_stroke}}]
131+
end
132+
133+
test "draw_rounded_rectangle varying 3 corners works" do
134+
expected = [{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 4.0, 3.0, :fill}}]
135+
assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, 4, :fill) == expected
136+
assert expected == Script.serialize(expected) |> Script.deserialize()
137+
138+
assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, 4, :stroke) ==
139+
[{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 4.0, 3.0, :stroke}}]
140+
141+
assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, 4, :fill_stroke) ==
142+
[{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 4.0, 3.0, :fill_stroke}}]
143+
end
144+
145+
test "draw_rounded_rectangle varying 2 corners works" do
146+
expected = [{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 2.0, 3.0, :fill}}]
147+
assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, :fill) == expected
148+
assert expected == Script.serialize(expected) |> Script.deserialize()
149+
150+
assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, :stroke) ==
151+
[{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 2.0, 3.0, :stroke}}]
152+
153+
assert Script.draw_rounded_rectangle([], 10, 11, 2, 3, :fill_stroke) ==
154+
[{:draw_rrectv, {10.0, 11.0, 2.0, 3.0, 2.0, 3.0, :fill_stroke}}]
155+
end
156+
157+
test "draw_rounded_rectangle varying all corners shrinks radius if too big" do
158+
assert Script.draw_rounded_rectangle([], 10, 12, 30, 40, 50, 4, :fill) ==
159+
[{:draw_rrectv, {10.0, 12.0, 5.0, 5.0, 5.0, 4.0, :fill}}]
160+
161+
assert Script.draw_rounded_rectangle([], 13, 12, 30, 40, 5, 60, :stroke) ==
162+
[{:draw_rrectv, {13.0, 12.0, 6.0, 6.0, 5.0, 6.0, :stroke}}]
163+
164+
assert Script.draw_rounded_rectangle([], 13, 12, 30, 4, 50, 60, :stroke) ==
165+
[{:draw_rrectv, {13.0, 12.0, 6.0, 4.0, 6.0, 6.0, :stroke}}]
166+
167+
assert Script.draw_rounded_rectangle([], 13, 12, 3, 40, 50, 60, :stroke) ==
168+
[{:draw_rrectv, {13.0, 12.0, 3.0, 6.0, 6.0, 6.0, :stroke}}]
169+
end
170+
171+
test "draw_rounded_rectangle varying 3 corners shrinks radius if too big" do
172+
assert Script.draw_rounded_rectangle([], 10, 12, 30, 40, 5, :fill) ==
173+
[{:draw_rrectv, {10.0, 12.0, 5.0, 5.0, 5.0, 5.0, :fill}}]
174+
175+
assert Script.draw_rounded_rectangle([], 13, 12, 30, 4, 50, :stroke) ==
176+
[{:draw_rrectv, {13.0, 12.0, 6.0, 4.0, 6.0, 4.0, :stroke}}]
177+
178+
assert Script.draw_rounded_rectangle([], 13, 12, 3, 40, 50, :stroke) ==
179+
[{:draw_rrectv, {13.0, 12.0, 3.0, 6.0, 6.0, 6.0, :stroke}}]
180+
end
181+
182+
test "draw_rounded_rectangle varying 2 corners shrinks radius if too big" do
183+
assert Script.draw_rounded_rectangle([], 10, 12, 30, 4, :fill) ==
184+
[{:draw_rrectv, {10.0, 12.0, 5.0, 4.0, 5.0, 4.0, :fill}}]
185+
186+
assert Script.draw_rounded_rectangle([], 13, 12, 3, 40, :stroke) ==
187+
[{:draw_rrectv, {13.0, 12.0, 3.0, 6.0, 3.0, 6.0, :stroke}}]
188+
end
189+
121190
test "draw_sector works" do
122191
expected = [{:draw_sector, {10.0, 3.0, :fill}}]
123192
assert Script.draw_sector([], 10, 3, :fill) == expected

0 commit comments

Comments
 (0)