Skip to content

Commit

Permalink
Add cornerradius attribute to Box (#3308)
Browse files Browse the repository at this point in the history
* Add `cornerradius` attribute to `Box`

* add news entry
  • Loading branch information
jkrumbiegel authored Oct 23, 2023
1 parent c388144 commit 8d68198
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 31 deletions.
34 changes: 30 additions & 4 deletions CairoMakie/src/overrides.jl
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,12 @@ function draw_poly(scene::Scene, screen::Screen, poly, points_list::Vector{<:Vec
end

draw_poly(scene::Scene, screen::Screen, poly, rect::Rect2) = draw_poly(scene, screen, poly, [rect])
draw_poly(scene::Scene, screen::Screen, poly, bezierpath::BezierPath) = draw_poly(scene, screen, poly, [bezierpath])

function draw_poly(scene::Scene, screen::Screen, poly, rects::Vector{<:Rect2})
function draw_poly(scene::Scene, screen::Screen, poly, shapes::Vector{<:Union{Rect2,BezierPath}})
model = poly.model[]
space = to_value(get(poly, :space, :data))
projected_rects = project_rect.(Ref(scene), space, rects, Ref(model))
projected_shapes = project_shape.(Ref(scene), space, shapes, Ref(model))

color = to_cairo_color(poly.color[], poly)

Expand All @@ -90,8 +91,8 @@ function draw_poly(scene::Scene, screen::Screen, poly, rects::Vector{<:Rect2})
error("Wrong type for linestyle: $(poly.linestyle[]).")
end
strokecolor = to_cairo_color(poly.strokecolor[], poly)
broadcast_foreach(projected_rects, color, strokecolor, poly.strokewidth[]) do r, c, sc, sw
Cairo.rectangle(screen.context, origin(r)..., widths(r)...)
broadcast_foreach(projected_shapes, color, strokecolor, poly.strokewidth[]) do shape, c, sc, sw
create_shape_path!(screen.context, shape)
set_source(screen.context, c)
Cairo.fill_preserve(screen.context)
isnothing(linestyle_diffed) || Cairo.set_dash(screen.context, linestyle_diffed .* sw)
Expand All @@ -101,6 +102,31 @@ function draw_poly(scene::Scene, screen::Screen, poly, rects::Vector{<:Rect2})
end
end

function project_shape(scene, space, shape::BezierPath, model)
commands = Makie.PathCommand[]
for cmd in shape.commands
if cmd isa EllipticalArc
bezier = Makie.elliptical_arc_to_beziers(cmd)
for b in bezier.commands
push!(commands, project_command(b, scene, space, model))
end
else
push!(commands, project_command(cmd, scene, space, model))
end
end
BezierPath(commands)
end

function create_shape_path!(ctx, r::Rect2)
Cairo.rectangle(ctx, origin(r)..., widths(r)...)
end

function create_shape_path!(ctx, b::BezierPath)
for cmd in b.commands
path_command(ctx, cmd)
end
end

function polypath(ctx, polygon)
isempty(polygon) && return nothing
ext = decompose(Point2f, polygon.exterior)
Expand Down
2 changes: 1 addition & 1 deletion CairoMakie/src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ function project_scale(scene::Scene, space, s, model = Mat4f(I))
end
end

function project_rect(scenelike, space, rect::Rect, model)
function project_shape(scenelike, space, rect::Rect, model)
mini = project_position(scenelike, space, minimum(rect), model)
maxi = project_position(scenelike, space, maximum(rect), model)
return Rect(mini, maxi .- mini)
Expand Down
3 changes: 3 additions & 0 deletions CairoMakie/test/svg_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ end
fig
end)
@test svg_isnt_rasterized(poly(Circle(Point2f(0, 0), 10)))
@test svg_isnt_rasterized(poly(BezierPath([
MoveTo(0.0, 0.0), LineTo(1.0, 0.0), LineTo(1.0, 1.0), CurveTo(1.0, 1.0, 0.5, 1.0, 0.5, 0.5), ClosePath()
])))
end

@testset "reproducable svg ids" begin
Expand Down
4 changes: 2 additions & 2 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

## master
- Fix grouping of a zero-height bar in `barplot`. Now a zero-height bar shares the same properties of the previous bar, and if the bar is the first one, its height is treated as positive if and only if there exists a bar of positive height or all bars are zero-height. [#3058](https://github.com/MakieOrg/Makie.jl/pull/3058)

- Fixed a bug where Axis still consumes scroll events when interactions are disabled [#3272](https://github.com/MakieOrg/Makie.jl/pull/3272)
- Upgrades `StableHashTraits` from 1.0 to 1.1
- Added `cornerradius` attribute to `Box` for rounded corners [#3308](https://github.com/MakieOrg/Makie.jl/pull/3308).
- Upgraded `StableHashTraits` from 1.0 to 1.1 [#3309](https://github.com/MakieOrg/Makie.jl/pull/3309).

## v0.19.11

Expand Down
22 changes: 2 additions & 20 deletions docs/reference/blocks/box.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,7 @@


# Box

A simple rectangle poly that is block. This can be useful to make boxes for
facet plots or when a rectangular placeholder is needed.

\begin{examplefigure}{}
```julia
using CairoMakie
using ColorSchemes
CairoMakie.activate!() # hide

fig = Figure()

rects = fig[1:4, 1:6] = [
Box(fig, color = c)
for c in get.(Ref(ColorSchemes.rainbow), (0:23) ./ 23)]

fig
```
\end{examplefigure}
A simple rectangle with optionally rounded corners.
This can be useful to visually group parts of a layout, or as a placeholder.

## Attributes

Expand Down
86 changes: 84 additions & 2 deletions src/makielayout/blocks/box.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,35 @@ function initialize_block!(box::Box)
vis ? col : RGBAf(0, 0, 0, 0)
end

ibbox = lift(round_to_IRect2D, blockscene, box.layoutobservables.computedbbox)
path = lift(blockscene, box.layoutobservables.computedbbox, box.cornerradius) do bbox, r
if r == 0
BezierPath([
MoveTo(topright(bbox)),
LineTo(topleft(bbox)),
LineTo(bottomleft(bbox)),
LineTo(bottomright(bbox)),
ClosePath()
])
else
w, h = widths(bbox)
_max = min(w/2, h/2)
r1, r2, r3, r4 = r isa NTuple{4, Real} ? r : r isa Real ? (r, r, r, r) : throw(ArgumentError("Invalid cornerradius value $r. Must be a `Real` or a tuple with 4 `Real`s."))

poly!(blockscene, ibbox, color = box.color, visible = box.visible,
r1, r2, r3, r4 = min.(_max, (r1, r2, r3, r4))
BezierPath([
MoveTo(bbox.origin + Point(w, h/2)),
EllipticalArc(topright(bbox) - Point2f(r1, r1), r1, r1, 0.0, 0, pi/2),
EllipticalArc(topleft(bbox) + Point2f(r4, -r4), r4, r4, 0.0, pi/2, pi),
EllipticalArc(bottomleft(bbox) + Point2f(r3, r3), r3, r3, 0.0, pi, 3/2 * pi),
EllipticalArc(bottomright(bbox) + Point2f(-r2, r2), r2, r2, 0.0, 3/2 * pi, 2pi),
ClosePath(),
])
end
end



poly!(blockscene, path, color = box.color, visible = box.visible,
strokecolor = strokecolor_with_visibility, strokewidth = box.strokewidth,
inspectable = false)

Expand All @@ -16,3 +42,59 @@ function initialize_block!(box::Box)

return
end


function attribute_examples(::Type{Box})
Dict(
:color => [
Example(
name = "Colors",
code = """
fig = Figure()
Box(fig[1, 1], color = :red)
Box(fig[1, 2], color = (:red, 0.5))
Box(fig[2, 1], color = RGBf(0.2, 0.5, 0.7))
Box(fig[2, 2], color = RGBAf(0.2, 0.5, 0.7, 0.5))
fig
"""
)
],
:strokecolor => [
Example(
name = "Stroke colors",
code = """
fig = Figure()
Box(fig[1, 1], strokecolor = :red)
Box(fig[1, 2], strokecolor = (:red, 0.5))
Box(fig[2, 1], strokecolor = RGBf(0.2, 0.5, 0.7))
Box(fig[2, 2], strokecolor = RGBAf(0.2, 0.5, 0.7, 0.5))
fig
"""
)
],
:strokewidth => [
Example(
name = "Stroke widths",
code = """
fig = Figure()
Box(fig[1, 1], strokewidth = 1)
Box(fig[1, 2], strokewidth = 10)
Box(fig[1, 3], strokewidth = 0)
fig
"""
)
],
:cornerradius => [
Example(
name = "Corner radius",
code = """
fig = Figure()
Box(fig[1, 1], cornerradius = 0)
Box(fig[1, 2], cornerradius = 20)
Box(fig[1, 3], cornerradius = (0, 10, 20, 30))
fig
"""
)
],
)
end
4 changes: 2 additions & 2 deletions src/makielayout/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -856,14 +856,14 @@ end
valign = :center
"The horizontal alignment of the rectangle in its suggested boundingbox"
halign = :center
"The extra space added to the sides of the rectangle boundingbox."
padding = (0f0, 0f0, 0f0, 0f0)
"The line width of the rectangle's border."
strokewidth = 1f0
"Controls if the border of the rectangle is visible."
strokevisible = true
"The color of the border."
strokecolor = RGBf(0, 0, 0)
"The radius of the rounded corner. One number is for all four corners, four numbers for going clockwise from top-right."
cornerradius = 0.0
"The width setting of the rectangle."
width = nothing
"The height setting of the rectangle."
Expand Down

0 comments on commit 8d68198

Please sign in to comment.