From 0a823bec85191e0aaefaf63c3fdd0c4fdd6582ea Mon Sep 17 00:00:00 2001 From: cormullion Date: Sun, 6 Sep 2020 09:16:57 +0100 Subject: [PATCH] misc fixes, polycross --- CHANGELOG.md | 7 ++-- Project.toml | 8 ++--- docs/src/basics.md | 10 +++--- docs/src/polygons.md | 68 -------------------------------------- docs/src/simplegraphics.md | 68 ++++++++++++++++++++++++++++++++++++++ src/basics.jl | 5 ++- src/curves.jl | 3 +- src/drawings.jl | 68 ++++++++++++++++++++++---------------- src/shapes.jl | 13 +++++--- 9 files changed, 136 insertions(+), 114 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 322a5e5a..23559a93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [v2.5.0] - forthcoming - September 2020 +## [v2.5.0] - September 6 2020 ### Added @@ -10,14 +10,15 @@ ### Changed - docs use JuliaMono +- in a few functions (eg `sector`, `box`) a `:clip` action didn't work, because it was applied within a +`gsave()`/`grestore()` block. `:clip` actions should now work, as they're applied after. ### Removed -- old deprecations finally gone +- some old deprecations finally gone ### Deprecated - ## [v2.4.0] - August 13 2020 ### Added diff --git a/Project.toml b/Project.toml index e9ad864f..dd9861b4 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Luxor" uuid = "ae8d54c2-7ccd-5906-9d76-62fc9837b5bc" authors = ["cormullion "] -version = "2.4.0" +version = "2.5.0" [deps] Cairo = "159f3aea-2a34-519c-b102-8c37f9878175" @@ -23,7 +23,7 @@ test = ["Test"] julia = "1" Cairo = "0.7, 0.8, 1.0" Colors = "0.9, 0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 1.0" -FileIO = "^1.0" -ImageMagick = "0.7, 1.0" +FileIO = "1.0, 1.1, 1.2, 1.3, 1.4" +ImageMagick = "0.7, 1.0, 1.1" Juno = "0.7, 0.8" -QuartzImageIO = "^0.6" +QuartzImageIO = "0.6, 0.7" diff --git a/docs/src/basics.md b/docs/src/basics.md index 50849cd2..f2777cc8 100644 --- a/docs/src/basics.md +++ b/docs/src/basics.md @@ -260,12 +260,12 @@ end While drawing, you can copy the data in the form of a matrix, using the `image_as_matrix()` function. -`image_as_matrix()` returns a array of ARGB{32} values that encode the Red, Green, and Blue, and Alpha values of each pixel. +`image_as_matrix()` returns a array of ARGB32 values that encode the Red, Green, Blue, and Alpha values of each pixel. The following example draws a red box, then copies the drawing into a matrix called `mat1`. Then it draws a blue circle, and copies the updated drawing into `mat2`. Then, the second drawing reads the values in from the two matrices and draws some square tiles depending on the corresponding values in the two matrices ... a very primitive Boolean operation. ```@example -using Luxor, Colors # hide +using Luxor, Colors Drawing(40, 40, :png) origin() @@ -302,6 +302,8 @@ nothing # hide ![image drawings](assets/figures/image-drawings.svg) +(You can use `collect()` to gather the re-interpreted values together.) + If you're working with Images.jl, you will probably want to transpose the array: ``` @@ -309,14 +311,14 @@ using Luxor, Images # in Luxor -Drawing(50, 50, :image) +Drawing(50, 50, :png) origin() background(randomhue()...) sethue("white") fontsize(40) fontface("Georgia") text("42", halign=:center, valign=:middle) -mat = image_as_matrix() +mat = image_as_matrix(); finish() # in Images diff --git a/docs/src/polygons.md b/docs/src/polygons.md index 4748b7b7..4286d925 100644 --- a/docs/src/polygons.md +++ b/docs/src/polygons.md @@ -119,74 +119,6 @@ ngon ngonside ``` -## Stars and crosses - -Use `star()` to make a star. You can draw it immediately, or use the points it can create. - -```@example -using Luxor # hide -Drawing(500, 300, "assets/figures/stars.png") # hide -background("white") # hide -origin() # hide -tiles = Tiler(400, 300, 4, 6, margin=5) -for (pos, n) in tiles - randomhue() - star(pos, tiles.tilewidth/3, rand(3:8), 0.5, 0, :fill) -end -finish() # hide -nothing # hide -``` - -![stars](assets/figures/stars.png) - -The `ratio` determines the length of the inner radius compared with the outer. - -```@example -using Luxor # hide -Drawing(500, 250, "assets/figures/star-ratios.png") # hide -origin() # hide -background("white") # hide -sethue("black") # hide -setline(2) # hide -tiles = Tiler(500, 250, 1, 6, margin=10) -for (pos, n) in tiles - star(pos, tiles.tilewidth/2, 5, rescale(n, 1, 6, 1, 0), 0, :stroke) -end -finish() # hide -nothing # hide -``` - -![stars](assets/figures/star-ratios.png) - -Use `polycross()` to draw a cross-shaped polygon. - -```@example -using Luxor # hide -Drawing(600, 600, "assets/figures/polycross.png") # hide -origin() # hide -background("white") # hide -sethue("black") # hide -setline(2) # hide -tiles = Tiler(600, 600, 4, 4, margin=10) -for (pos, n) in tiles - randomhue() - polycross(pos, min(tiles.tileheight/3, tiles.tilewidth/3), - n + 2, # number of points - rescale(n, 1, length(tiles), 0.9, 0.1), # ratio - 0, # orientation - :fill) -end -finish() # hide -nothing # hide -``` - -![polycross](assets/figures/polycross.png) - -```@docs -star -polycross -``` - ## Polygons Use `poly()` to draw lines connecting the points and/or just fill the area: diff --git a/docs/src/simplegraphics.md b/docs/src/simplegraphics.md index ca544500..d8256cce 100644 --- a/docs/src/simplegraphics.md +++ b/docs/src/simplegraphics.md @@ -904,3 +904,71 @@ finish() # hide nothing # hide ``` ![rounded rect](assets/figures/round-rect.png) + +## Stars and crosses + +Use `star()` to make a star. You can draw it immediately, or use the points it can create. + +```@example +using Luxor # hide +Drawing(500, 300, "assets/figures/stars.png") # hide +background("white") # hide +origin() # hide +tiles = Tiler(400, 300, 4, 6, margin=5) +for (pos, n) in tiles + randomhue() + star(pos, tiles.tilewidth/3, rand(3:8), 0.5, 0, :fill) +end +finish() # hide +nothing # hide +``` + +![stars](assets/figures/stars.png) + +The `ratio` determines the length of the inner radius compared with the outer. + +```@example +using Luxor # hide +Drawing(500, 250, "assets/figures/star-ratios.png") # hide +origin() # hide +background("white") # hide +sethue("black") # hide +setline(2) # hide +tiles = Tiler(500, 250, 1, 6, margin=10) +for (pos, n) in tiles + star(pos, tiles.tilewidth/2, 5, rescale(n, 1, 6, 1, 0), 0, :stroke) +end +finish() # hide +nothing # hide +``` + +![stars](assets/figures/star-ratios.png) + +Use `polycross()` to draw a cross-shaped polygon. + +```@example +using Luxor # hide +Drawing(600, 600, "assets/figures/polycross.png") # hide +origin() # hide +background("white") # hide +sethue("black") # hide +setline(2) # hide +tiles = Tiler(600, 600, 4, 4, margin=10) +for (pos, n) in tiles + randomhue() + polycross(pos, min(tiles.tileheight/3, tiles.tilewidth/3), + n + 2, # number of points + rescale(n, 1, length(tiles), 0.9, 0.1), # ratio + 0, # orientation + :fill) +end +finish() # hide +nothing # hide +``` + +![polycross](assets/figures/polycross.png) + +```@docs +star +polycross +``` diff --git a/src/basics.jl b/src/basics.jl index 3005346a..09dc3d31 100644 --- a/src/basics.jl +++ b/src/basics.jl @@ -230,6 +230,9 @@ end Establish a new clipping region by intersecting the current clipping region with the current path and then clearing the current path. + +An existing clipping region is enforced through and after a `gsave()`-`grestore()` block, but a clipping region set inside +a `gsave()`-`grestore()` block is lost after `grestore()`. [?] """ clip() = Cairo.clip(get_current_cr()) @@ -251,7 +254,7 @@ clipreset() = Cairo.reset_clip(get_current_cr()) """ setline(n) -Set the line width. +Set the line width, in points. """ setline(n) = Cairo.set_line_width(get_current_cr(), n) diff --git a/src/curves.jl b/src/curves.jl index ebc4d610..34f17362 100644 --- a/src/curves.jl +++ b/src/curves.jl @@ -236,6 +236,7 @@ end sector(centerpoint::Point, innerradius, outerradius, startangle, endangle, action:none) Draw an annular sector centered at `centerpoint`. + """ function sector(centerpoint::Point, innerradius::Real, outerradius::Real, startangle::Real, endangle::Real, action::Symbol=:none) @@ -249,8 +250,8 @@ function sector(centerpoint::Point, innerradius::Real, outerradius::Real, starta line(innerradius * cos(endangle), innerradius * sin(endangle)) carc(0, 0, innerradius, endangle, startangle, :none) closepath() - do_action(action) grestore() + do_action(action) end """ diff --git a/src/drawings.jl b/src/drawings.jl index df8a19df..a2a3e1ea 100644 --- a/src/drawings.jl +++ b/src/drawings.jl @@ -703,34 +703,6 @@ function image_as_matrix() return reinterpret(ARGB32, permutedims(data, (2, 1))) end -# function image_as_matrix() -# if length(CURRENTDRAWING) != 1 -# error("no current drawing") -# end -# w = Int(current_surface().width) -# h = Int(current_surface().height) -# z = zeros(UInt32, w, h) -# -# # create a new image surface to receive the data from the current drawing -# imagesurface = CairoImageSurface(z, Cairo.FORMAT_ARGB32) -# -# # the destination - we're drawing on this -# crdest = Cairo.CairoContext(imagesurface) -# -# # set the source to be the current Luxor drawing -# Cairo.set_source_surface(crdest, Luxor.current_surface(), 0, 0) -# -# Cairo.set_operator(crdest, Cairo.OPERATOR_SOURCE) -# Cairo.paint(crdest) -# -# data = imagesurface.data -# r = unpremultiplyalpha(data) -# -# Cairo.finish(imagesurface) -# Cairo.destroy(imagesurface) -# return permutedims(r, (2, 1)) -# end - """ Create a drawing and return a matrix of the image. @@ -789,6 +761,46 @@ end drawimagematrix(m) ``` + +Transparency + +The default value for the cells in an image matrix is +transparent black. (Luxor's default color is opaque black.) + +``` +julia> @imagematrix begin + end 2 2 +2×2 reinterpret(ARGB32, ::Array{UInt32,2}): + ARGB32(0.0,0.0,0.0,0.0) ARGB32(0.0,0.0,0.0,0.0) + ARGB32(0.0,0.0,0.0,0.0) ARGB32(0.0,0.0,0.0,0.0) +``` + +Setting the background to a partially or completely +transparent value may give unexpected results: + +``` +julia> @imagematrix begin + background(1, 0.5, 0.0, 0.5) # semi-transparent orange + end 2 2 +2×2 reinterpret(ARGB32, ::Array{UInt32,2}): + ARGB32(0.502,0.251,0.0,0.502) ARGB32(0.502,0.251,0.0,0.502) + ARGB32(0.502,0.251,0.0,0.502) ARGB32(0.502,0.251,0.0,0.502) +``` + +here the semi-transparent orange color has been partially +applied to the transparent background. + +``` +julia> @imagematrix begin + sethue(1., 0.5, 0.0) + paint() + end 2 2 +2×2 reinterpret(ARGB32, ::Array{UInt32,2}): + ARGB32(1.0,0.502,0.0,1.0) ARGB32(1.0,0.502,0.0,1.0) + ARGB32(1.0,0.502,0.0,1.0) ARGB32(1.0,0.502,0.0,1.0) +``` + +picks up the default alpha of 1.0. """ macro imagematrix(body, width=256, height=256) quote diff --git a/src/shapes.jl b/src/shapes.jl index 6bfc2ffc..b62378d9 100644 --- a/src/shapes.jl +++ b/src/shapes.jl @@ -145,8 +145,8 @@ function box(centerpoint::Point, width, height, cornerradius, action::Symbol=:st line(p4end) closepath() - do_action(action) grestore() + do_action(action) end """ @@ -316,29 +316,32 @@ end """ polycross(pt::Point, radius, npoints::Int, ratio=0.5, orientation=0.0, action=:none; + splay = 0.5, vertices = false, reversepath = false) Make a cross-shaped polygon with `npoints` arms to fit inside a circle of radius `radius` centered at `pt`. -`ratio` specifies the ratio of the two sides of each arm. +`ratio` specifies the ratio of the two sides of each arm. `splay` makes the arms ... splayed. Use `vertices=true` to return the vertices of the shape instead of drawing it. -(Adapted from Compose.xgon())) +(Adapted from Compose.jl.xgon())) """ function polycross(pt::Point, radius, npoints::Int, ratio=0.5, orientation=0.0, action=:none; + splay = .5, vertices = false, reversepath = false) # adapted from: Compose.jl, https://github.com/GiovineItalia/Compose.jl/src/form.jl # original author: mattriks + ratio = clamp(ratio, 0.0, 1.0) + θ₁ = range(π/2 + orientation + 0, stop = π/2 + orientation + 2π, length = npoints + 1)[1:end-1] width = 2radius * ratio * sin(π/npoints) - # radius = width/2(ratio * sin(π/npoints)) - dₒ = asin(0.5 * width/radius) + dₒ = asin(clamp(splay * width/radius, -1.0, 1.0)) dᵢ = asin(0.5 * width/(radius * ratio)) r₂ = repeat([radius * ratio, radius, radius], outer = npoints)