Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cornerradius attribute to Box #3308

Merged
merged 3 commits into from Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 30 additions & 4 deletions CairoMakie/src/overrides.jl
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
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
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
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
@@ -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
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
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