From bad73fd61462643122958e27a612a8caef9471a3 Mon Sep 17 00:00:00 2001 From: cormullion Date: Sat, 26 Jan 2019 12:23:12 +0000 Subject: [PATCH] added dashes and fix rule bug --- CHANGELOG.md | 7 +-- docs/Project.toml | 3 -- docs/make.jl | 2 +- docs/src/assets/examples/luxor-logo.jl | 31 ------------ docs/src/colors-styles.md | 55 ++++++++++++++++----- src/Luxor.jl | 11 ++--- src/basics.jl | 53 +++++++++++++-------- test/dashtests.jl | 66 ++++++++++++++++++++++++++ test/runtests.jl | 1 + 9 files changed, 153 insertions(+), 76 deletions(-) delete mode 100644 docs/src/assets/examples/luxor-logo.jl create mode 100644 test/dashtests.jl diff --git a/CHANGELOG.md b/CHANGELOG.md index f0b7ac18..77d60954 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,15 @@ # Changelog -## [v1.1.5] - future release +## [v1.1.5] - 2019-01-26 ### Added -- a few more box functions take vertices=true/false +- more box functions take vertices=true/false +- added setdash(dashes) ### Changed -- +- tried to fix odd bug in `rule(..., π/2)` ### Removed diff --git a/docs/Project.toml b/docs/Project.toml index 75c61d28..6832e676 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -2,6 +2,3 @@ Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" Luxor = "ae8d54c2-7ccd-5906-9d76-62fc9837b5bc" - -[compat] -Documenter = "~0.20" diff --git a/docs/make.jl b/docs/make.jl index b82904db..59812d6b 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -3,7 +3,7 @@ using Documenter, Luxor makedocs( modules = [Luxor], sitename = "Luxor", - html_prettyurls = get(ENV, "CI", nothing) == "true", + format = Documenter.HTML(prettyurls = get(ENV, "CI", nothing) == "true"), pages = Any[ "Introduction to Luxor" => "index.md", "A few examples" => "examples.md", diff --git a/docs/src/assets/examples/luxor-logo.jl b/docs/src/assets/examples/luxor-logo.jl deleted file mode 100644 index d48f2ef1..00000000 --- a/docs/src/assets/examples/luxor-logo.jl +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env julia - -using Luxor, Colors, ColorSchemes - -width = 225 # pts -height = 225 # pts -Drawing(width, height) #, "/tmp/logo.pdf") - -function wheel(colscheme) - circle(0, 0, 90, :clip) - for theta in pi/2 - pi/8:pi/8: (19 * pi)/8 - sethue(get(colscheme, rescale(theta, pi/2, (19 * pi)/8, 0, 1))) - gsave() - rotate(theta) - move(5,0) - curve(Point(40, 40), Point(50, -40), Point(80, 30)) - closepath() - fillpath() - grestore() - end - clipreset() -end - -origin() -background("white") -scale(1.3, 1.3) -using ColorSchemes.solar -colschememirror = vcat(solar, reverse(solar)) -wheel(colschememirror) -finish() -preview() diff --git a/docs/src/colors-styles.md b/docs/src/colors-styles.md index e2bb7482..78927631 100644 --- a/docs/src/colors-styles.md +++ b/docs/src/colors-styles.md @@ -114,6 +114,49 @@ nothing # hide ![dashes](assets/figures/dashes.png) +To define more complicated dash patterns in Luxor, supply a vector to `setdash()`. + +```julia +dashes = [50.0, # ink + 10.0, # skip + 10.0, # ink + 10.0 # skip + ] +setdash(dashes) +``` + +```@example +using Luxor # hide +Drawing(600, 180, "assets/figures/moredashes.svg") # hide +background("white") # hide +origin() # hide +function dashing() + fontsize(12) # hide + sethue("black") # hide + setline(8) + setlinecap("butt") + patterns = [10, 4, 50, 25, 14, 100] + table = Table(fill(20, length(patterns)), [40, 325]) + for p in 1:length(patterns) + setdash(patterns) + pt = table[p, 2] + text(string(patterns), table[p, 1], halign=:right, valign=:middle) + line(pt - (150, 0), pt + (200, 0), :stroke) + patterns = circshift(patterns, 1) + pop!(patterns) + end +end + +dashing() + +finish() # hide +nothing # hide +``` + +![more dashes](assets/figures/moredashes.svg) + +Notice that odd-numbered patterns flip the ink and skip numbers each time through. + ```@docs setline setlinecap @@ -128,18 +171,6 @@ paint do_action ``` -Soon you'll be able to define dash patterns in Luxor. For now: - -```julia -dashes = [50.0, # ink - 10.0, # skip - 10.0, # ink - 10.0 # skip - ] -offset = -50.0 -Cairo.set_dash(get_current_cr()(), dashes, offset) -``` - ## Blends A blend is a color gradient. Use `setblend()` to select a blend in the same way that you'd use `setcolor()` and `sethue()` to select a solid color. diff --git a/src/Luxor.jl b/src/Luxor.jl index ff4e4d13..750a6378 100644 --- a/src/Luxor.jl +++ b/src/Luxor.jl @@ -5,8 +5,7 @@ module Luxor using Juno, Cairo, Colors, FileIO, Dates -#= from Cairo use: CairoARGBSurface, CairoEPSSurface, CairoMatrix, CairoPDFSurface, -CairoPattern, CairoPatternMesh, CairoSurface, CairoSVGSurface, +#= from Cairo use: CairoARGBSurface, CairoEPSSurface, CairoMatrix, CairoPDFSurface, CairoPattern, CairoPatternMesh, CairoSurface, CairoSVGSurface, CairoContext, arc, arc_negative, circle, clip, clip_preserve, close_path, convert_cairo_path_data, copy_path, copy_path_flat, curve_to, destroy, fill, fill_preserve, finish, get_matrix, get_operator, height, image, line_to, @@ -16,10 +15,10 @@ move_to, new_path, new_sub_path,paint, paint_with_alpha, pattern_add_color_stop_rgba, pattern_create_linear, pattern_create_radial, read_from_png, rectangle, rel_line_to, rel_move_to, reset_clip, restore, rotate, save, scale, select_font_face, set_antialias, set_font_face, set_font_size, -set_line_cap, set_line_join, set_line_type, set_line_width, set_matrix, -set_operator, set_source, set_source_rgba, set_source_surface, show_text, -status, stroke, stroke_preserve, text, text_extents, text_path, translate, -width, write_to_png =# +set_line_cap, set_line_join, set_dash, set_line_type, set_line_width, +set_matrix, set_operator, set_source, set_source_rgba, set_source_surface, +show_text, status, stroke, stroke_preserve, text, text_extents, text_path, +translate, width, write_to_png =# include("drawings.jl") include("point.jl") diff --git a/src/basics.jl b/src/basics.jl index e657abb5..deff8c7f 100644 --- a/src/basics.jl +++ b/src/basics.jl @@ -137,8 +137,8 @@ newpath() = Cairo.new_path(get_current_cr()) """ newsubpath() -Add a new subpath to the current path. This is Cairo's `new_sub_path()` function. It can -be used for example to make holes in shapes. +Add a new subpath to the current path. This is Cairo's `new_sub_path()` +function. It can be used for example to make holes in shapes. """ newsubpath() = Cairo.new_sub_path(get_current_cr()) @@ -152,8 +152,8 @@ closepath() = Cairo.close_path(get_current_cr()) """ strokepath() -Stroke the current path with the current line width, line join, line cap, and dash settings. -The current path is then cleared. +Stroke the current path with the current line width, line join, line cap, and +dash settings. The current path is then cleared. """ strokepath() = Cairo.stroke(get_current_cr()) @@ -284,15 +284,32 @@ function setlinejoin(str="miter") end """ - setlinedash("dot") + setdash("dot") -Set the dash pattern to one of: "solid", "dotted", "dot", "dotdashed", "longdashed", -"shortdashed", "dash", "dashed", "dotdotdashed", "dotdotdotdashed" +Set the dash pattern to one of: "solid", "dotted", "dot", "dotdashed", +"longdashed", "shortdashed", "dash", "dashed", "dotdotdashed", +"dotdotdotdashed". + +Use `setdash(dashes::Vector)` to specify the pattern numerically. """ -function setdash(dashing) +function setdash(dashing::AbstractString) Cairo.set_line_type(get_current_cr(), dashing) end +""" + setdash(dashes::Vector, offset=0.0) + +Set the dash pattern to the values in `dashes`. The first number is the length of the ink, the second the gap, and so on. + +The `offset` specifies an offset into the pattern at which the stroke begins. So an offset of 10 means that the stroke starts at `dashes[1] + 10` into the pattern. + +Or use `setdash("dot")` etc. +""" +function setdash(dashes::Vector, offset=0.0) + # no negative dashes + Cairo.set_dash(get_current_cr(), abs.(Float64.(dashes)), offset) +end + """ move(pt) @@ -357,14 +374,10 @@ draws a line that spans a bounding box half the width and height of the drawing. function rule(pos, theta=0.0; boundingbox=BoundingBox()) bbox = box(boundingbox, vertices=true) - topside = bbox[1:2] - rightside = bbox[2:3] - bottomside = bbox[3:4] - leftside = vcat(bbox[4], bbox[1]) - - #if !isinside(pos, bbox, allowonedge=true) - # #@warn "position is not inside bounding box" - #end + topside = bbox[2:3] + rightside = bbox[3:4] + bottomside = vcat(bbox[4], bbox[1]) + leftside = bbox[1:2] # ruled line could be as long as the diagonal so add a bit extra r = boxdiagonal(boundingbox)/2 + 10 @@ -376,7 +389,7 @@ function rule(pos, theta=0.0; interpoints = Set{Point}() # check for intersection with top of bounding box - flag, ip = intersection(ruledline[1], ruledline[2], topside[1], topside[2]) + flag, ip = intersection(ruledline[1], ruledline[2], topside[1], topside[2], crossingonly=true) if flag if !(ip.x > topside[2].x || ip.x < topside[1].x) push!(interpoints, ip) @@ -384,7 +397,7 @@ function rule(pos, theta=0.0; end # check for right intersection - flag, ip = intersection(ruledline[1], ruledline[2], rightside[1], rightside[2]) + flag, ip = intersection(ruledline[1], ruledline[2], rightside[1], rightside[2], crossingonly=true) if flag if !(ip.y > rightside[2].y || ip.y < rightside[1].y) push!(interpoints, ip) @@ -392,7 +405,7 @@ function rule(pos, theta=0.0; end # check for bottom intersection - flag, ip = intersection(ruledline[1], ruledline[2], bottomside[1], bottomside[2]) + flag, ip = intersection(ruledline[1], ruledline[2], bottomside[1], bottomside[2], crossingonly=true) if flag if !(ip.x < bottomside[2].x || ip.x > bottomside[1].x) push!(interpoints, ip) @@ -400,7 +413,7 @@ function rule(pos, theta=0.0; end # check for left intersection - flag, ip = intersection(ruledline[1], ruledline[2], leftside[1], leftside[2]) + flag, ip = intersection(ruledline[1], ruledline[2], leftside[1], leftside[2], crossingonly=true) if flag if !(ip.y > leftside[1].y || ip.y < leftside[2].y) push!(interpoints, ip) diff --git a/test/dashtests.jl b/test/dashtests.jl new file mode 100644 index 00000000..30f1f49a --- /dev/null +++ b/test/dashtests.jl @@ -0,0 +1,66 @@ +#!/usr/bin/env julia + +using Luxor + +using Test + +using Random +Random.seed!(42) + +function annotatedash(pos, dashes, offset) + @layer begin + translate(pos) + @layer begin + translate(0, 15) + fontsize(3) + sethue("black") + x = 0 + for n in dashes + text(string(n), O + (-offset + x + n/2, 0), halign=:center) + x += n + end + fontsize(6) + text("offset $offset", O + (-10, -10), halign=:right) + end + @layer begin + translate(0, 2.5) + setdash(dashes, offset) + setline(0.5) + sethue("grey60") + line(O + (0, 6), O + (3 * sum(dashes), 6), :stroke) + sethue("red") + setline(5) + setdash(dashes, offset) + line(O, O + (3 * sum(dashes), 0), :stroke) + end + end +end + +function dash_tests(fname) + pagewidth, pageheight = 800, 600 + Drawing(pagewidth, pageheight, fname) + origin() + background("ivory") + setline(3) + dashes = [30.0, # ink + 10.0, # skip + 5.0, # ink + 15.0, # skip + 20.0, # ink + 4. # skip + ] + offset = -50.0 + sethue("black") + text("Pattern is $(dashes)", boxtopcenter(BoundingBox() * 0.9), halign=:center) + sethue("blue") + for y in -pageheight/2 + 50:30:pageheight/2 - 50 + randomhue() + annotatedash(O + (-200, y), dashes, offset) + offset += 10 + end + @test finish() == true +end + +fname = "dash-tests.svg" +dash_tests(fname) +println("...finished dash tests: output in $(fname)") diff --git a/test/runtests.jl b/test/runtests.jl index c324dcdf..f58be6c5 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -130,6 +130,7 @@ function run_all_tests() include("cropmarkstest.jl") include("boxmaptest.jl") include("noise-test.jl") + include("dashtests.jl") end end