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

Size units (sx, sy) #409

Merged
merged 2 commits into from
Jan 4, 2021
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 8 additions & 12 deletions docs/src/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,21 +175,24 @@ nothing # hide
## Measures can be a combination of absolute and relative units

Complex visualizations often are defined using a combination of relative and
absolute units. Compose makes these easy. In fact there are three sorts of units
absolute units. Compose makes these easy. In fact there are four sorts of units
used in Compose:

* **Context units**: If no unit is explicitly attached to a number, it is
* **Context (position) units**: If no unit is explicitly attached to a position, it is
assumed to be in “context units”, which are relative to the parent Context's
box and coordinate system. (Constants: `cx`, `cy`)
* **Context (relative position) units**: The radius of a form can be expressed in size units
with respect to the context units (Constants: `sx`, `sy`). If no unit is explicitly attached to
a size (e.g. radius) unit, it is assumed to be wrt the context `x` units. More info below.
* **Width/Height units**: Sometimes you'll want place geometry in relative
coordinates, but bypassing the parent context's coordinate system.
Width/height work so that `(0w, 0h)` is always the top-left corner of the
contxt, and `(1w, 1h)` is always the bottom-right. (Constants: `w`, `h`)
context, and `(1w, 1h)` is always the bottom-right. (Constants: `w`, `h`)
* **Absolute units**: Absolute units are inches, centimeters, points, etc.
(Constants: `inch`, `cm`, `mm`, `pt`)

Any linear combination of these types of units is allowed. For example: `0.5w +
2cm - 5cx` is a valid measure that can be used anywhere.
Any linear combination of these types of units is allowed. For example, `1w - 10mm` is a well formed expression, giving the width of the parent canvas minus ten millimeters. An example of the difference between
context position units (`cx`, `cy`) and size units (`sx`, `sy`), is when the coordinate system (defined by `UnitBox`) does not start at `(0,0)`, then the size `0sx` (or `0sy`) will always equal `0mm`, but the position `(0cx, 0cy)` will be dependent on the coordinates (and may refer to a point outside the context's `UnitBox`).


## Forms and Properties can be vectorized
Expand Down Expand Up @@ -338,13 +341,6 @@ Properties include `arrow`, `fill`, `fillopacity`, etc. See the [Properties gall
For colors, Compose supports the colors available in [Colors.jl](https://github.com/JuliaGraphics/Colors.jl), which includes many [color spaces](http://juliagraphics.github.io/Colors.jl/stable/), hex strings, and [named colors](http://juliagraphics.github.io/Colors.jl/stable/namedcolors/).



## Coordinates

Besides coordinate transformations, Compose also handles mixtures of relative
and absolute coordinates. For example, `1w - 10mm` is a well formed expression,
giving the width of the parent canvas minus ten millimeters.

## Text

Symbols can be used in text strings by inserting [HTML
Expand Down
2 changes: 1 addition & 1 deletion src/Compose.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export compose, compose!, Context, UnitBox, AbsoluteBoundingBox,
stroke, fill, strokedash, strokelinecap, arrow, strokelinejoin,
linewidth, visible, fillopacity, strokeopacity, clip, points,
font, fontsize, svgid, svgclass, svgattribute, jsinclude, jscall, Measure,
inch, mm, cm, pt, px, cx, cy, w, h, hleft, hcenter, hright, vtop, vcenter,
inch, mm, cm, pt, px, cx, cy, sx, sy, w, h, hleft, hcenter, hright, vtop, vcenter,
vbottom, SVG, SVGJS, PGF, PNG, PS, PDF, draw, pad, pad_inner, pad_outer,
hstack, vstack, gridstack, LineCapButt, LineCapSquare, LineCapRound,
CAIROSURFACE, introspect, set_default_graphic_size, set_default_jsmode,
Expand Down
12 changes: 6 additions & 6 deletions src/form.jl
Original file line number Diff line number Diff line change
Expand Up @@ -700,8 +700,8 @@ Define a `n`-sided polygon with its center at (`x`,`y`), and radius of `r`. For
"""
function ngon(x, y, r, n::Int, tag=empty_tag)
θ = range(-π/2, stop=1.5π, length=n+1)
x1 = x_measure(x) .+ x_measure(r).*cos.(θ)
y1 = y_measure(y) .+ x_measure(r).*sin.(θ)
x1 = x_measure(x) .+ size_x_measure(r).*cos.(θ)
y1 = y_measure(y) .+ size_x_measure(r).*sin.(θ)
points = collect(Tuple{Measure, Measure}, zip(x1, y1))
return Form([PolygonPrimitive(points)], tag)
end
Expand Down Expand Up @@ -732,8 +732,8 @@ Define a `n`-pointed star with its center at (`x`,`y`), outer radius of `r`, and
function star(x, y, r, n::Int, ratio::Float64=0.3, tag=empty_tag)
θ = range(-π/2, stop=1.5π, length=2*n+1)[1:end-1]
r1 = repeat([r, r*ratio], outer=n)
x1 = x_measure(x) .+ x_measure(r1).*cos.(θ)
y1 = y_measure(y) .+ x_measure(r1).*sin.(θ)
x1 = x_measure(x) .+ size_x_measure(r1).*cos.(θ)
y1 = y_measure(y) .+ size_x_measure(r1).*sin.(θ)
points = collect(Tuple{Measure, Measure}, zip(x1, y1))
return Form([PolygonPrimitive(points)], tag)
end
Expand Down Expand Up @@ -768,8 +768,8 @@ function xgon(x, y, r, n::Int, ratio::Float64=0.1, tag=empty_tag)
dᵢ = abs(asin(0.5*w/(r*ratio)))
r₂ = repeat([r*ratio,r,r], outer=n)
θ₂ = vec([θ+x for x in [-dᵢ, -dₒ, dₒ], θ in θ₁])
x1 = x_measure(x) .+ x_measure(r₂).*cos.(θ₂)
y1 = y_measure(y) .+ x_measure(r₂).*sin.(θ₂)
x1 = x_measure(x) .+ size_x_measure(r₂).*cos.(θ₂)
y1 = y_measure(y) .+ size_x_measure(r₂).*sin.(θ₂)
points = collect(Tuple{Measure, Measure}, zip(x1, y1))
return Form([PolygonPrimitive(points)], tag)
end
Expand Down
24 changes: 24 additions & 0 deletions src/measure.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ using LinearAlgebra

const cx = Length{:cx}
const cy = Length{:cy}
const sx = Length{:sx}
const sy = Length{:sy}

*(a::T, b::Type{cx}) where T = x_measure(a)
*(a::T, b::Type{cy}) where T = y_measure(a)
*(a::T, b::Type{sx}) where T = size_x_measure(a)
*(a::T, b::Type{sy}) where T = size_y_measure(a)

# Pixels are not typically used in Compose in preference of absolute
# measurements or measurements relative to parent canvases. So for the
Expand Down Expand Up @@ -46,18 +50,31 @@ x_measure(a::T) where T = Length{:cx, T}(a)
y_measure(a::Measure) = a
y_measure(a::T) where T = Length{:cy, T}(a)

size_x_measure(a::Measure) = a
size_x_measure(a::T) where T = Length{:sx, T}(a)
size_y_measure(a::Measure) = a
size_y_measure(a::T) where T = Length{:sy, T}(a)

x_measure(a::Vector{T}) where T <: Measure = a
x_measure(a::Vector) = Measure[x_measure(x) for x in a]

y_measure(a::Vector{T}) where T <: Measure = a
y_measure(a::Vector) = Measure[y_measure(y) for y in a]

size_x_measure(a::Vector{T}) where T <: Measure = a
size_x_measure(a::Vector) = Measure[size_x_measure(x) for x in a]
size_y_measure(a::Vector{T}) where T <: Measure = a
size_y_measure(a::Vector) = Measure[size_y_measure(y) for y in a]

size_measure(a::Measure) = a
size_measure(a) = a * mm

x_measure(a::Missing) = x_measure(NaN)
y_measure(a::Missing) = y_measure(NaN)

size_x_measure(a::Missing) = size_x_measure(NaN)
size_y_measure(a::Missing) = size_y_measure(NaN)

# Higher-order measures
# ---------------------

Expand Down Expand Up @@ -292,6 +309,13 @@ resolve_position(box::AbsoluteBox, units::UnitBox, t::Transform, a::Length{:cy})
resolve(box::AbsoluteBox, units::UnitBox, t::Transform, a::Length{:cy}) =
abs(a.value / height(units)) * box.a[2]

resolve(box::AbsoluteBox, units::UnitBox, t::Transform, a::Length{:sx}) =
a.value / width(units) * box.a[1]

resolve(box::AbsoluteBox, units::UnitBox, t::Transform, a::Length{:sy}) =
a.value / height(units) * box.a[2]


function resolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::Vec2)
xy = (resolve_position(box, units, t, p[1]) + box.x0[1],
resolve_position(box, units, t, p[2]) + box.x0[2])
Expand Down
13 changes: 13 additions & 0 deletions test/misc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -188,4 +188,17 @@ end
@testset "Missing" begin
@test Compose.x_measure(missing)===NaN*cx
@test Compose.y_measure(missing)===NaN*cy
@test Compose.size_x_measure(missing)===NaN*sx
@test Compose.size_y_measure(missing)===NaN*sy
end

@testset "cx, sx and cy, sy units" begin
ub, bb = UnitBox(-0.3, 0., 1.2, 1.), Compose.BoundingBox((50mm, 50mm), (40mm, 40mm))
tr = Compose.IdentityTransform()
xpos = (Compose.resolve_position(bb, ub, tr, 0sx),
Compose.resolve_position(bb, ub, tr, 0cx))
ub, bb = UnitBox(0., -0.3, 1., 1.2), Compose.BoundingBox((10mm, 10mm), (40mm, 40mm))
ypos = (Compose.resolve_position(bb, ub, tr, 0sy),
Compose.resolve_position(bb, ub, tr, 0cy))
@test ypos == xpos == (0.0mm, 10.0mm)
end