Skip to content

Commit

Permalink
update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
cormullion committed Jul 6, 2021
1 parent dda804b commit abb1c45
Show file tree
Hide file tree
Showing 7 changed files with 338 additions and 76 deletions.
3 changes: 2 additions & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@ makedocs(
"Work with polygons" => "howto/polygons.md",
"Add text" => "howto/text.md",
"Clip graphics" => "howto/clipping.md",
"Placing images" => "howto/images.md",
"Placing images" => "howto/images.md",
"Turtle graphics" => "howto/turtle.md",
"Make animations" => "howto/animation.md",
"Live graphics and snapshots" => "howto/livegraphics.md",
],
"Explanations" => [
"Basic concepts" => "explanation/basics.md",
"Image matrix" => "explanation/imagematrix.md",
"Perfect pixels and antialising" => "explanation/perfectpixels.md",
"Transforms and matrices" => "explanation/transforms.md",
"Contributing" => "explanation/contributing.md",
],
Expand Down
67 changes: 67 additions & 0 deletions docs/src/example/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,70 @@ using Luxor # hide
arrow(O - (220, 0), O + (220, 0))
end 800 200
```

Another way you could approach tasks like this is to use the [`arrow`](@ref) functions, which let you decorate lines. Here's how a curved number line could be made:

```@example
using Luxor # hide
@drawsvg begin
background("antiquewhite")
_counter() = (a = -1; () -> a += 1)
counter = _counter() # closure
fontsize(15)
arrow(O + (0, 100), 200, π, 2π,
arrowheadlength=0,
decoration=range(0, 1, length=61),
decorate = () -> begin
d = counter()
if d % 5 == 0
text(string(d), O + (0, -20), halign=:center)
setline(3)
end
line(O - (0, 5), O + (0, 5), :stroke)
end
)
end 800 300
```

## Draw a matrix

To draw a matrix, you can use a Table to generate the
positions.

It's sometimes useful to be able to highight particular
cells. Here, numbers that have already been used once
are drawn in orange.

```@example
using Luxor
function drawmatrix(A::Matrix;
cellsize = (10, 10))
table = Table(size(A)..., cellsize...)
used = Set()
for i in CartesianIndices(A)
r, c = Tuple(i)
if A[r, c] ∈ used
sethue("orange")
else
sethue("purple")
push!(used, A[r, c])
end
text(string(A[r, c]), table[r, c],
halign=:center,
valign=:middle)
sethue("white")
box(table, r, c, :stroke)
end
end
A = rand(1:99, 5, 8)
@drawsvg begin
background("black")
fontsize(30)
setline(0.5)
sethue("white")
drawmatrix(A, cellsize = 10 .* size(A))
end
```
156 changes: 156 additions & 0 deletions docs/src/explanation/perfectpixels.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# Perfect pixels and anti-aliasing

## Antialiasing

The process of converting smooth graphic shapes to a grid of pixels is automatically performed by Luxor/Cairo when you save the drawing as a PNG file. If you make an SVG or PDF drawing, this process is carried out by the application you use to view or display the file. It's usually better to defer the conversion as long as possible: eventually - unless you're using a pen plotter or laser cutter - your smooth outlines will have to be converted ("rasterized") to a grid of colored pixels.

The smoothing process includes "anti-aliasing". You can - to some extent - adjust the amount of anti-aliasing used when you make drawings in Luxor.

```@setup draw_matrix
using Luxor, Colors
function make_matrix(;
antialias=0)
d = Drawing(20, 20, :image)
setantialias(antialias)
origin()
setcolor("red")
circle(Point(0, 0), 5, :fill)
mat = image_as_matrix()
finish()
return mat
end
function drawmatrix(A;
cellsize = (10, 10))
table = Table(size(A)..., cellsize...)
for i in CartesianIndices(A)
r, c = Tuple(i)
sethue(A[r, c])
box(table, r, c, :fill)
sethue("black")
box(table, r, c, :stroke)
end
end
function draw(antialias)
mat = make_matrix(antialias=antialias)
@drawsvg begin
background("black")
drawmatrix(mat, cellsize = 1 .* size(mat))
c = length(unique(color.(mat)))
sethue("white")
fontsize(8)
text("number of colors used: $c", boxbottomcenter(BoundingBox() * 0.9), halign=:center)
end 300 300
end
```

The [`setantialias`](@ref) function lets you set the antialiasing amount to a constant between 0 and 6. The Cairo documentation describes the different values as follows:

| Value | Name | Description |
|:----- |:---- |:---- |
|0 |`CAIRO_ANTIALIAS_DEFAULT` |Use the default antialiasing for the subsystem and target device|
|1 |`CAIRO_ANTIALIAS_NONE` |Use a bilevel alpha mask|
|2 |`CAIRO_ANTIALIAS_GRAY` |Perform single-color antialiasing (using shades of gray for black text on a white background, for example)|
|3 |`CAIRO_ANTIALIAS_SUBPIXEL` |Perform antialiasing by taking advantage of the order of subpixel elements on devices such as LCD panels|
|4 |`CAIRO_ANTIALIAS_FAST` |Hint that the backend should perform some antialiasing but prefer speed over quality|
|5 |`CAIRO_ANTIALIAS_GOOD` |The backend should balance quality against performance|
|6 |`CAIRO_ANTIALIAS_BEST` |Hint that the backend should render at the highest quality, sacrificing speed if necessary|

To show the antialiasing in action, the following code draws a red circle:

```
Drawing(20, 20, :image)
setantialias(0)
origin()
setcolor("red")
circle(Point(0, 0), 5, :fill)
mat = image_as_matrix()
finish()
```

Enlarging the matrix shows the default value of 0:

```@example draw_matrix
draw(0) # hide
```

Luxor used 18 colors to render this red circle.

Here's the bilevel mask (value 1 or "none"):

```@example draw_matrix
draw(1) # hide
```

and Luxor used two colors to draw the circle.

The other values are the same as the default (0), apart from 4 ("speed over quality"):

```@example draw_matrix
draw(4) # hide
```

which uses 12 rather than 16 colors.

The anti-aliasing process can vary according to the OS and device you're using. The [Cairo documentation](https://www.cairographics.org/manual/cairo-cairo-t.html) stresses this more than once:

> The value is a hint, and a particular backend may or may not support a particular value. [...] The values make no guarantee on how the backend will perform its rasterisation (if it even rasterises!) [...] The interpretation of CAIRO_ANTIALIAS_DEFAULT is left entirely up to the backend [...]
## Text

The antialiasing described above does not apply to text.

Text rendering is much more platform-dependent than graphics; Windows, MacOS, and Linux all have their own methods for rendering and rasterizing fonts, and currently Cairo.jl doesn't currently provide an interface to any font rendering APIs.

```@setup draw_text
using Luxor, Colors
function make_matrix(;
antialias=0)
d = Drawing(20, 20, :image)
setantialias(antialias)
origin()
setcolor("red")
fontsize(20)
text("a", halign=:center, valign=:middle)
mat = image_as_matrix()
finish()
return mat
end
function drawmatrix(A;
cellsize = (10, 10))
table = Table(size(A)..., cellsize...)
for i in CartesianIndices(A)
r, c = Tuple(i)
sethue(A[r, c])
box(table, r, c, :fill)
sethue("black")
box(table, r, c, :stroke)
end
end
function draw(antialias)
mat = make_matrix(antialias=antialias)
@drawsvg begin
background("black")
drawmatrix(mat, cellsize = 1 .* size(mat))
c = length(unique(color.(mat)))
sethue("white")
fontsize(8)
text("number of colors used: $c", boxbottomcenter(BoundingBox() * 0.9), halign=:center)
end 300 300
end
```

On some platforms you'll see anti-aliased fonts rendered like this:

```@example draw_text
draw(1) # hide
```

On Windows systems, text might be displayed using Microsoft’s Cleartype subpixel rendering process, and variously colored pixels are drawn around the edges of text in order to provide a “smoother” appearance.

In addition, Windows uses font-hinting, a process in which the outlines of text glyphs are shifted so as to align better on the rectangular grid of pixels.

If you want text to be aligned precisely on Windows, it might be worth investigating Luxor’s [`textoutlines`](@ref) function, which converts text to vector-based outlines.
35 changes: 16 additions & 19 deletions docs/src/howto/simplegraphics.md
Original file line number Diff line number Diff line change
Expand Up @@ -587,10 +587,10 @@ nothing # hide
### Decoration

The [`arrow`](@ref) functions allow you to specify
decorations - graphics at a point somewhere along the shaft.
decorations - graphics at one or more points somewhere along the shaft.
For example, say you want to draw a number and a circle at
the midpoint of an arrow, you can define a function that
draws text `t` in a circle of radius `r` :
the midpoint of an arrow's shaft, you can define a function that
draws text `t` in a circle of radius `r` like this:

```
function marker(r, t)
Expand All @@ -605,7 +605,7 @@ end
```

and then pass this to the `decorate` keyword argument of
`arrrow`. By default, the graphics origin when the function
`arrow`. By default, the graphics origin when the function
is called is placed at the midpoint (0.5) of the arrow's
shaft.

Expand Down Expand Up @@ -645,7 +645,9 @@ nothing # hide

Use the `decoration` keyword to specify one or more locations other than the default 0.5.

The graphics environment provided by the `decorate` function is centered at each decoration point, and rotated to the slope of the shaft at that point.
The graphics environment provided by the `decorate` function
is centered at each decoration point in turn, and rotated to the
slope of the shaft at that point.

```@example
using Luxor
Expand All @@ -655,30 +657,25 @@ function fletcher()
line(O, polar(30, deg2rad(140)), :stroke)
end
function customarrowhead(shaftendpoint, endpoint, shaftangle)
@layer begin
sidept1 = shaftendpoint - polar(40, shaftangle + π/5)
sidept2 = shaftendpoint - polar(40, shaftangle - π/5)
sethue("red")
poly([sidept1, endpoint, sidept2], :fill)
sethue("black")
poly([sidept1, endpoint, sidept2], :stroke, close=true)
end
end
@drawsvg begin
background("antiquewhite")
arrow(O, 150, 0, π + π/3,
linewidth=5,
arrowheadlength=50,
decorate=fletcher,
arrowheadfunction = customarrowhead,
decoration=range(0., .1, length=3))
end
end 800 350
```

### Custom arrowheads

To make custom arrowheads, you can define a three-argument function that draws them to your own design. This function should accept three arguments: the point at the end of the arrow's shaft, the point where the tip of the arrowhead would be, and the angle of the shaft at the end. You can then use any code to draw the arrow. Pass this function to the `arrow` function's `arrowheadfunction` keyword.
To make custom arrowheads, you can define a three-argument
function that draws them to your own design. This function
takes: the point at the end of the
arrow's shaft; the point where the tip of the arrowhead
would be; and the angle of the shaft at the end. You can
then use any code to draw the arrow. Pass this function to
the [`arrow`](@ref) function's `arrowheadfunction` keyword.

```@example
using Luxor # hide
Expand Down
16 changes: 11 additions & 5 deletions docs/src/howto/tables-grids.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ These are types which act as iterators. Their job is to provide you with centerp

The drawing area (or any other area) can be divided into rectangular tiles (as rows and columns) using the `Tiler` and `Partition` iterators.

The `Tiler` iterator returns the center point and tile number of each tile in turn.
The [`Tiler`](@ref) iterator returns the center point and tile number of each tile in turn.

In this example, every third tile is divided up into subtiles and colored:

Expand Down Expand Up @@ -52,7 +52,7 @@ nothing # hide

![tiler](../assets/figures/tiler.png)

`Partition` is like `Tiler`, but you specify the width and height of the tiles, rather than
[`Partition`])(@ref) is like `Tiler`, but you specify the width and height of the tiles, rather than
how many rows and columns of tiles you want.

You can obtain the centerpoints of all the tiles in one go with:
Expand All @@ -69,11 +69,17 @@ tiles[1:2:end]

## Tables

The `Table` iterator can be used to define tables: rectangular grids with a specific number of rows and columns.
The [`Table`](@ref) iterator can be used to define tables: rectangular grids with a specific number of rows and columns.

Unlike a Tiler, the Table iterator lets you have columns can have different widths, and rows with different heights.
Unlike a Tiler, the Table iterator lets you have columns with different widths, and rows with different heights.

(Luxor generally tries to keep to the Julia convention of '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'...)
!!! note

Luxor generally tries to keep to the Julia convention of
‘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’...)

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

Expand Down
Loading

0 comments on commit abb1c45

Please sign in to comment.