Skip to content

Commit

Permalink
more action defaults
Browse files Browse the repository at this point in the history
  • Loading branch information
cormullion committed Oct 7, 2021
1 parent e9e56eb commit 8e1a7d4
Show file tree
Hide file tree
Showing 20 changed files with 350 additions and 97 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# Changelog

## [v2.16.0] - forthcoming 2021-10-??
## [v2.16.0] - forthcoming 2021-10-07

### Added

### Changed

- many functions can now accept `action=` keyword arguments
- many functions can now accept `action=` keyword arguments as well as positional ones
- offsetpoly(... function) algorithm altered (it still sucks, though :))
- `include_first` kwarg added to `polysample()`
- texttrack() rewritten so that the alignment works

### Removed

Expand Down
88 changes: 62 additions & 26 deletions docs/src/example/moreexamples.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,47 +83,83 @@ where some of the characters—eg "F", "+", "-", and "t"—issue turtle control

## Strange

It's usually better to draw fractals and similar images using pixels and image processing tools. But just for fun it's an interesting experiment to render a strange attractor image using vector drawing rather than placing pixels. This version uses about 600,000 circular dots (which is why it's better to target PNG rather than SVG or PDF for this example!).
It's usually better to draw fractals and similar images
using pixels and image processing tools. But just for fun
it's an interesting experiment to render a strange attractor
image using vector drawing rather than placing pixels.

```@example
using Luxor, Colors
function strange(dotsize, w=800.0)
xmin = -2.0; xmax = 2.0; ymin= -2.0; ymax = 2.0
Drawing(w, w, "../assets/figures/strange-vector.png")
origin()
background("white")
xinc = w/(xmax - xmin)
yinc = w/(ymax - ymin)
background("grey5")
xinc = w / (xmax - xmin)
yinc = w / (ymax - ymin)
# control parameters
a = 2.24; b = 0.43; c = -0.65; d = -2.43; e1 = 1.0
a = 2.24
b = 0.43
c = -0.65
d = -2.43
e1 = 1.0
x = y = z = 0.0
wover2 = w/2
for j in 1:w
for i in 1:w
xx = sin(a * y) - z * cos(b * x)
wover2 = w / 2 - 50 # margin
for j = 1:w
for i = 1:w
xx = sin(a * y) - z * cos(b * x)
yy = z * sin(c * x) - cos(d * y)
zz = e1 * sin(x)
x = xx; y = yy; z = zz
if xx < xmax && xx > xmin && yy < ymax && yy > ymin
xpos = rescale(xx, xmin, xmax, -wover2, wover2) # scale to range
ypos = rescale(yy, ymin, ymax, -wover2, wover2) # scale to range
rcolor = rescale(xx, -1, 1, 0.0, .7)
gcolor = rescale(yy, -1, 1, 0.2, .5)
bcolor = rescale(zz, -1, 1, 0.3, .8)
setcolor(convert(Colors.HSV, Colors.RGB(rcolor, gcolor, bcolor)))
circle(Point(xpos, ypos), dotsize, :fill)
x = xx
y = yy
z = zz
if xx < xmax && xx > xmin
if yy < ymax && yy > ymin
xpos = rescale(
xx,
xmin,
xmax,
-wover2,
wover2,
) # scale to range
ypos = rescale(
yy,
ymin,
ymax,
-wover2,
wover2,
) # scale to range
rcolor = rescale(xx, -1, 1, 0.0, 0.6)
gcolor = rescale(yy, -1, 1, 0.2, 0.5)
bcolor = rescale(zz, -1, 1, 0.6, 0.9)
setcolor(rcolor, gcolor, bcolor)
move(Point(xpos, ypos))
line(Point(xpos + dotsize, ypos))
line(Point(
xpos + dotsize,
ypos + dotsize,
))
line(Point(xpos, ypos + dotsize))
fillpath()
end
end
end
end
finish()
end
strange(.3, 800)
strange(.5, 800)
nothing # hide
```

![strange attractor in vectors](../assets/figures/strange-vector.png)

This example generates about 650,000 paths, which is why
it’s better to target PNG rather than SVG or PDF for this
example. Also for speed, the “dots” are actually simple
paths, which are slightly quicker to draw than circles or
polygons.

## More animations

[![strange attractor in vectors](../assets/figures/animation-screengrab.jpg)](https://youtu.be/1FA2FgJU6dM)
Expand Down Expand Up @@ -171,9 +207,9 @@ end
function draw_scarab_body()
@layer begin
squircle(Point(0, -25), 26, 75, :path)
squircle(Point(0, 0), 50, 70, :path)
squircle(Point(0, 40), 65, 90, :path)
squircle(Point(0, -25), 26, 75, action=:path)
squircle(Point(0, 0), 50, 70, action=:path)
squircle(Point(0, 40), 65, 90, action=:path)
end
end
Expand All @@ -187,20 +223,20 @@ function draw()
height= 240
sethue("black")
squircle(O, width, height-5, rt=0.4, :fill)
squircle(O, width, height-5, rt=0.4, action=:fill)
set_gold_blend()
squircle(O, width, height-5, rt=0.4, :path)
squircle(O, width, height-5, rt=0.4, action=:path)
translate(0, 50)
draw_scarab_legs(O)
strokepath()
draw_scarab_body()
fillpath()
# julia dots === Ra
# julia dots === Ra egyptian sun deity
@layer begin
translate(0, -190)
circle(O, 48, :fill)
circle(O, 48, action=:fill)
juliacircles(20)
end
Expand Down
42 changes: 29 additions & 13 deletions docs/src/explanation/basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,24 @@ DocTestSetup = quote

# The drawing model

The underlying drawing model is that you build paths, and these are filled and/or stroked, using the current *graphics state*, which specifies colors, line thicknesses, scale, orientation, opacity, and so on.

You can modify the current graphics state by transforming/rotating/scaling it, setting color and style parameters, and so on.

Many of the drawing functions have an *action* argument, supplied either as a symbol argument (eg `:fill`) or as a keyword argument (eg `action=:fill`). This action can be `:none`, `:fill`, `:stroke`, `:fillstroke`, `:fillpreserve`, `:strokepreserve`, `:clip`, or `:path`. The default is `:none`, which is usually equivalent to `:path`, ie. add to the current path but otherwise do nothing.

Subsequent graphics use the new state, but the graphics you've already drawn are unchanged.
The underlying drawing model is that you build paths, and
these are filled and/or stroked, using the current *graphics
state*, which specifies colors, line thicknesses, scale,
orientation, opacity, and so on.

You can modify the current graphics state by
transforming/rotating/scaling it, setting color and style
parameters, and so on. Subsequent graphics use the new
state, but the graphics you've already drawn are unchanged.

Many of the drawing functions have an *action* argument,
supplied either as a symbol argument (eg `:fill`) or as a
keyword argument (eg `action=:fill`). This action determines
what happens to the current path. It can be `:none`,
`:fill`, `:stroke`, `:fillstroke`, `:fillpreserve`,
`:strokepreserve`, `:clip`, or `:path`. The default is
`:none`, which is usually equivalent to `:path`, ie. add to
the current path but otherwise do nothing.

The main Julia data types you'll encounter in Luxor are:

Expand All @@ -33,9 +44,12 @@ The main Julia data types you'll encounter in Luxor are:

## Points and coordinates

You specify points on the drawing surface using `Point(x, y)`. (A few older functions accept separate x and y values).
You specify points on the drawing surface using `Point(x, y)`.

The default _origin_ (ie the `x = 0, y = 0` point) is at the top left corner: the x axis runs left to right across the page, and the y axis runs top to bottom down the page, so Y coordinates increase downwards.
The default _origin_ (ie the `x = 0, y = 0` point) is at the
top left corner: the x axis runs left to right across the
page, and the y axis runs top to bottom down the page, so Y
coordinates increase downwards.

By default, `Point(0, 100)` is below `Point(0, 0)`.

Expand Down Expand Up @@ -124,7 +138,9 @@ julia> Point.(plist)
Point(14.2, 15.4)
```

You can use the letter **O** as a shortcut to refer to the current Origin, `Point(0, 0)`.
You can use the letter **O** as a shortcut to refer to the
current Origin, `Point(0, 0)`. Most coding fonts clearly
show the difference between the letter `O` and the digit `0`.

```@example
using Luxor # hide
Expand Down Expand Up @@ -251,8 +267,8 @@ You can use an environment such as a Jupyter or Pluto notebook or the Juno or VS
Luxor can create new SVG images, either in a file or in
memory, and can also place existing SVG images on a drawing.
See [Placing images](@ref) for more. It's also possible to
obtain the source of an SVG drawing as a string. For example,
this code draws the Julia logo using SVG code:
obtain the source of the current SVG drawing as a string. For example,
this code draws the Julia logo using SVG code and stores the SVG in `s`:

```julia
Drawing(500, 500, :svg)
Expand All @@ -262,7 +278,7 @@ finish()
s = svgstring()
```

You can now examine the SVG code in `s` programmatically:
You can now examine the SVG elements in `s` programmatically:

```julia
eachmatch(r"rgb\(.*?\)", s) |> collect
Expand Down
2 changes: 1 addition & 1 deletion docs/src/explanation/luxorcairo.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ documentation](https://cairographics.org/documentation/).

If a Cairo function isn't yet supported in Cairo.jl or Luxor.jl, a temporary workround is to add a direct call to the Cairo library in your Luxor script.

For example, the Cairo function to return the current line width ([cairo_get_line_width](https://cairographics.org/manual/cairo-cairo-t.html#cairo-get-line-width)) isn't yet available in Julia, but you can easily add it with code like this (or better):
For example, the Cairo function to return the current line width ([`cairo_get_line_width`](https://cairographics.org/manual/cairo-cairo-t.html#cairo-get-line-width)) isn't yet available in Julia, but you can easily add it with code like this (or better):

```julia
using Luxor
Expand Down
4 changes: 2 additions & 2 deletions docs/src/howto/colors-styles.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ gamma = 2.2
for n in 1:length(cols)
col = cols[n][1]
r, g, b = sethue(col)
box(table[n], table.colwidths[1], table.rowheights[1], :fill)
box(table[n], table.colwidths[1], table.rowheights[1], action=:fill)
luminance = 0.2126 * r^gamma + 0.7152 * g^gamma + 0.0722 * b^gamma
(luminance > 0.5^gamma) ? sethue("black") : sethue("white")
text(string(cols[n][1]), table[n], halign=:center, valign=:middle)
Expand Down Expand Up @@ -78,7 +78,7 @@ for l in 1:3
textcentred(["butt", "square", "round"][l], 80l, 80)
setlinejoin(["round", "miter", "bevel"][l])
textcentred(["round", "miter", "bevel"][l], 80l, 120)
poly(ngon(Point(80l, 0), 20, 3, 0, vertices=true), :strokepreserve, close=false)
poly(ngon(Point(80l, 0), 20, 3, 0, vertices=true), action=:strokepreserve, close=false)
sethue("white")
setline(1)
strokepath()
Expand Down
8 changes: 4 additions & 4 deletions docs/src/howto/tables-grids.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@ fontsize(20) # hide
tiles = Tiler(800, 500, 4, 5, margin=5)
for (pos, n) in tiles
randomhue()
box(pos, tiles.tilewidth, tiles.tileheight, :fill)
box(pos, tiles.tilewidth, tiles.tileheight, action=:fill)
if n % 3 == 0
gsave()
translate(pos)
subtiles = Tiler(tiles.tilewidth, tiles.tileheight, 4, 4, margin=5)
for (pos1, n1) in subtiles
randomhue()
box(pos1, subtiles.tilewidth, subtiles.tileheight, :fill)
box(pos1, subtiles.tilewidth, subtiles.tileheight, action=:fill)
end
grestore()
end
Expand Down Expand Up @@ -79,7 +79,7 @@ Unlike a `Tiler`, the `Table` iterator lets you have columns with different widt
‘width’ -> ‘height’, ‘row’ -> ‘column’. This flavour of
consistency can sometimes be confusing if you’re
expecting other kinds of consistency, such as ‘x before
y’ or ‘column major’...)
y’ or ‘column major’.)

Tables don't store data, of course, but are designed to help you draw tabular data.

Expand Down Expand Up @@ -153,7 +153,7 @@ end
setopacity(0.5)
sethue("thistle")
circle.(t[3, :], 20, :fill) # row 3, every column
circle.(t[3, :], 20, action=:fill) # row 3, every column
finish() # hide
nothing # hide
Expand Down
11 changes: 8 additions & 3 deletions docs/src/howto/text.md
Original file line number Diff line number Diff line change
Expand Up @@ -462,13 +462,18 @@ nothing # hide

## Text tracking

Use [`texttrack`](@ref) to track or letter-space text, i.e. vary the spacing between every letter. ("Kerning" is when you do this for just a pair of letters.) The units are 1/1000 em, so the actual distance of "50 units of tracking" varies depending on the current font size.
Use [`texttrack`](@ref) to track or letter-space text, i.e.
vary the spacing between every letter. ("Kerning" is when
you do this for just a pair of letters.)

But really, don’t track text unless you have to.
The tracking units depend on the current font size. In a
12‑point font, 1 em equals 12 points. A point is about
0.35mm, so a 1000 units of tracking for 12 point text
produces about 4.2mm of space between each character.

```@example
using Luxor # hide
Drawing(600, 600, "../assets/figures/texttrack.svg") # hide
Drawing(600, 400, "../assets/figures/texttrack.svg") # hide
origin() # hide
background("white") # hide
sethue("black") # hide
Expand Down
8 changes: 2 additions & 6 deletions docs/src/tutorial/basictutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ Experienced Julia users and programmers fluent in other languages and graphics s

If you've already downloaded Julia, and have added the Luxor package successfully (using `] add Luxor`):

```@raw html
<span style="font-feature-settings: 'ss20' on;">
<pre>
```
$ julia
_
_ _ _(_)_ | Documentation: https://docs.julialang.org
(_) | (_) (_) |
Expand All @@ -23,10 +22,7 @@ If you've already downloaded Julia, and have added the Luxor package successfull
_/ |\__'_|_|_|\__'_| | Official https://julialang.org/ release
|__/ |
$ julia
(v1.6) pkg> add Luxor
</pre>
</span>
```

then you're ready to start.
Expand Down
16 changes: 11 additions & 5 deletions docs/src/tutorial/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ svgimage = @drawsvg begin
setcolor("red")
corners = ngon(Point(0, 0), 80, 3, vertices=true)
circle.(corners, 10, :fill)
circle.(corners, 10, action=:fill)
end 500 500
```
Expand All @@ -80,7 +80,7 @@ Drawing(500, 500, "my-drawing.svg")
origin()
setcolor("red")
corners = ngon(Point(0, 0), 80, 3, vertices=true)
circle.(corners, 10, :fill)
circle.(corners, 10, action=:fill)
finish()
preview()
```
Expand Down Expand Up @@ -289,7 +289,7 @@ To tidy up, it's a good idea to move the code into functions (to avoid running t

Also, a background for the icon would look good. [`squircle`](@ref) is useful for drawing shapes that occupy the space between pointy dull rectangles and space-inefficient circles.

The final script looks like this:
The complete final script looks like this:

```@setup example_7
using Luxor, Colors
Expand Down Expand Up @@ -353,7 +353,7 @@ svgimage # hide

So, did the JuliaFission organization like their logo? Who
knows? - they may still be debating how accurate the
representation of an atom it should be... But if not, we can
representation of an atom should be... But if not, we can
always recycle some of these ideas for future projects. 😃

## Extra credit
Expand All @@ -364,4 +364,10 @@ Try refactoring your code so that you can automatically run through various para

### 2. Remember the random values

Using random numbers is a great way to find new patterns and shapes; but unless you know what values were used, you can't easily reproduce them. It's frustrating to produce something really good but not know what values were used to make it. So modify the code so that the random numbers are remembered, and drawn on the screen (you can use the `text(str, position)` function),
Using random numbers is a great way to find new patterns and
shapes; but unless you know what values were used, you can't
easily reproduce them. It's frustrating to produce something
really good but not know what values were used to make it.
So modify the code so that the random numbers are
remembered, and drawn on the screen (you can use the
`text(str, position)` function),
Loading

0 comments on commit 8e1a7d4

Please sign in to comment.