# Polygons and paths

For drawing shapes, Luxor provides polygons and paths.

A polygon is an ordered collection of Points stored in an array.

A path is a sequence of one or more straight and curved (circular arc or Bézier curve) segments. Paths can consist of subpaths. Luxor maintains a 'current path', to which you can add lines and curves until you finish with a stroke or fill instruction.

Luxor also provides a BezierPath type, which is an array of four-point tuples, each of which is a Bézier cubic curve section.

## Regular polygons ("ngons")

A polygon is an array of points. The points can be joined with straight lines.

You can make regular polygons — from triangles, pentagons, hexagons, septagons, heptagons, octagons, nonagons, decagons, and on-and-on-agons — with `ngon()`

.

```
using Luxor, Colors
Drawing(1200, 1400)
origin()
cols = diverging_palette(60, 120, 20) # hue 60 to hue 120
background(cols[1])
setopacity(0.7)
setline(2)
# circumradius of 500
ngon(0, 0, 500, 8, 0, :clip)
for y in -500:50:500
for x in -500:50:500
setcolor(cols[rand(1:20)])
ngon(x, y, rand(20:25), rand(3:12), 0, :fill)
setcolor(cols[rand(1:20)])
ngon(x, y, rand(10:20), rand(3:12), 0, :stroke)
end
end
finish()
preview()
```

If you want to specify the side length rather than the circumradius, use `ngonside()`

.

```
for i in 20:-1:3
sethue(i/20, 0.5, 0.7)
ngonside(O, 75, i, 0, :fill)
sethue("black")
ngonside(O, 75, i, 0, :stroke)
end
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

`Luxor.ngon`

— Function.```
ngon(x, y, radius, sides=5, orientation=0, action=:nothing;
vertices=false, reversepath=false)
```

Find the vertices of a regular n-sided polygon centered at `x`

, `y`

with circumradius `radius`

.

`ngon()`

draws the shapes: if you just want the raw points, use keyword argument `vertices=true`

, which returns the array of points instead. Compare:

```
ngon(0, 0, 4, 4, 0, vertices=true) # returns the polygon's points:
4-element Array{Luxor.Point,1}:
Luxor.Point(2.4492935982947064e-16,4.0)
Luxor.Point(-4.0,4.898587196589413e-16)
Luxor.Point(-7.347880794884119e-16,-4.0)
Luxor.Point(4.0,-9.797174393178826e-16)
```

whereas

`ngon(0, 0, 4, 4, 0, :close) # draws a polygon`

```
ngon(centerpos, radius, sides=5, orientation=0, action=:nothing;
vertices=false,
reversepath=false)
```

Draw a regular polygon centered at point `centerpos`

:

`Luxor.ngonside`

— Function.```
ngonside(centerpoint::Point, sidelength::Real, sides::Int=5, orientation=0,
action=:nothing; kwargs...)
```

Draw a regular polygon centered at `centerpoint`

with `sides`

sides of length `sidelength`

.

## Stars

Use `star()`

to make a star. You can draw it immediately, or use the points it can create.

```
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
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

The `ratio`

determines the length of the inner radius compared with the outer.

```
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
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

`Luxor.star`

— Function.```
star(xcenter, ycenter, radius, npoints=5, ratio=0.5, orientation=0, action=:nothing;
vertices = false,
reversepath=false)
```

Make a star. `ratio`

specifies the height of the smaller radius of the star relative to the larger.

Use `vertices=true`

to return the vertices of a star instead of drawing it.

```
star(center, radius, npoints=5, ratio=0.5, orientation=0, action=:nothing;
vertices = false, reversepath=false)
```

Draw a star centered at a position:

## Polygons

Use `poly()`

to draw lines connecting the points and/or just fill the area:

```
tiles = Tiler(600, 250, 1, 2, margin=20)
tile1, tile2 = collect(tiles)
randompoints = [Point(rand(-100:100), rand(-100:100)) for i in 1:10]
gsave()
translate(tile1[1])
poly(randompoints, :stroke)
grestore()
gsave()
translate(tile2[1])
poly(randompoints, :fill)
grestore()
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

`Luxor.poly`

— Function.Draw a polygon.

```
poly(pointlist::AbstractArray{Point, 1}, action = :nothing;
close=false,
reversepath=false)
```

A polygon is an Array of Points. By default `poly()`

doesn't close or fill the polygon, to allow for clipping.

`poly(bbox::BoundingBox, :action; kwargs...)`

Make a polygon around the BoundingBox in `bbox`

.

A polygon can contain holes. The `reversepath`

keyword changes the direction of the polygon. The following piece of code uses `ngon()`

to make and draw two paths, the second forming a hole in the first, to make a hexagonal bolt shape:

```
setline(5)
sethue("gold")
line(Point(-200, 0), Point(200, 0), :stroke)
sethue("orchid4")
ngon(0, 0, 60, 6, 0, :path)
newsubpath()
ngon(0, 0, 40, 6, 0, :path, reversepath=true)
fillstroke()
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

The `prettypoly()`

function can place graphics at each vertex of a polygon. After the polygon action, the supplied `vertexfunction`

function is evaluated at each vertex. For example, to mark each vertex of a polygon with a randomly-colored circle:

```
apoly = star(O, 70, 7, 0.6, 0, vertices=true)
prettypoly(apoly, :fill, () ->
begin
randomhue()
circle(O, 10, :fill)
end,
close=true)
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

An optional keyword argument `vertexlabels`

lets you pass a function that can number each vertex. The function can use two arguments, the current vertex number, and the total number of points in the polygon:

```
apoly = star(O, 80, 5, 0.6, 0, vertices=true)
prettypoly(apoly,
:stroke,
vertexlabels = (n, l) -> (text(string(n, " of ", l), halign=:center)),
close=true)
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

`Luxor.prettypoly`

— Function.```
prettypoly(points::AbstractArray{Point, 1}, action=:nothing, vertexfunction = () -> circle(O, 2, :stroke);
close=false,
reversepath=false,
vertexlabels = (n, l) -> ()
)
```

Draw the polygon defined by `points`

, possibly closing and reversing it, using the current parameters, and then evaluate the `vertexfunction`

function at every vertex of the polygon.

The default vertexfunction draws a 2 pt radius circle.

To mark each vertex of a polygon with a randomly colored filled circle:

```
p = star(O, 70, 7, 0.6, 0, vertices=true)
prettypoly(p, :fill, () ->
begin
randomhue()
circle(O, 10, :fill)
end,
close=true)
```

The optional keyword argument `vertexlabels`

lets you supply a function with two arguments that can access the current vertex number and the total number of vertices at each vertex. For example, you can label the vertices of a triangle "1 of 3", "2 of 3", and "3 of 3" using:

```
prettypoly(triangle, :stroke,
vertexlabels = (n, l) -> (text(string(n, " of ", l))))
```

`prettypoly(bbox::BoundingBox, :action; kwargs...)`

Make a decorated polygon around the BoundingBox in `bbox`

.

Recursive decoration is possible:

```
decorate(pos, p, level) = begin
if level < 4
randomhue()
scale(0.25, 0.25)
prettypoly(p, :fill, () -> decorate(pos, p, level+1), close=true)
end
end
apoly = star(O, 100, 7, 0.6, 0, vertices=true)
prettypoly(apoly, :fill, () -> decorate(O, apoly, 1), close=true)
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

Polygons can be simplified using the Douglas-Peucker algorithm (non-recursive version), via `simplify()`

.

```
sincurve = [Point(6x, 80sin(x)) for x in -5pi:pi/20:5pi]
prettypoly(collect(sincurve), :stroke,
() -> begin
sethue("red")
circle(O, 3, :fill)
end)
text(string("number of points: ", length(collect(sincurve))), 0, 100)
translate(0, 200)
simplercurve = simplify(collect(sincurve), 0.5)
prettypoly(simplercurve, :stroke,
() -> begin
sethue("red")
circle(O, 3, :fill)
end)
text(string("number of points: ", length(simplercurve)), 0, 100)
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

`Luxor.simplify`

— Function.Simplify a polygon:

`simplify(pointlist::AbstractArray, detail=0.1)`

`detail`

is the smallest permitted distance between two points in pixels.

The `isinside()`

function returns true if a point is inside a polygon.

```
setline(0.5)
apolygon = star(O, 100, 5, 0.5, 0, vertices=true)
for n in 1:10000
apoint = randompoint(Point(-200, -150), Point(200, 150))
randomhue()
isinside(apoint, apolygon) ? circle(apoint, 3, :fill) : circle(apoint, .5, :stroke)
end
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

`Luxor.isinside`

— Function.`isinside(p, pol; allowonedge=false)`

Is a point `p`

inside a polygon `pol`

? Returns true if it does, or false.

This is an implementation of the Hormann-Agathos (2001) Point in Polygon algorithm.

The classification of points lying on the edges of the target polygon, or coincident with its vertices is not clearly defined, due to rounding errors or arithmetical inadequacy. By default these will generate errors, but you can suppress these by setting `allowonedge`

to `true`

.

`isinside(p::Point, bb:BoundingBox)`

Returns `true`

if `pt`

is inside bounding box `bb`

.

You can use `randompoint()`

and `randompointarray()`

to create a random Point or list of Points.

```
pt1 = Point(-100, -100)
pt2 = Point(100, 100)
sethue("gray80")
map(pt -> circle(pt, 6, :fill), (pt1, pt2))
box(pt1, pt2, :stroke)
sethue("red")
circle(randompoint(pt1, pt2), 7, :fill)
sethue("blue")
map(pt -> circle(pt, 2, :fill), randompointarray(pt1, pt2, 100))
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

`Luxor.randompoint`

— Function.`randompoint(lowpt, highpt)`

Return a random point somewhere inside the rectangle defined by the two points.

`randompoint(lowx, lowy, highx, highy)`

Return a random point somewhere inside a rectangle defined by the four values.

`Luxor.randompointarray`

— Function.`randompointarray(lowpt, highpt, n)`

Return an array of `n`

random points somewhere inside the rectangle defined by two points.

`randompointarray(lowx, lowy, highx, highy, n)`

Return an array of `n`

random points somewhere inside the rectangle defined by the four coordinates.

There are some experimental polygon functions. These don't work well for polygons that aren't simple or where the sides intersect each other, but they sometimes do a reasonable job. For example, here's `polysplit()`

:

```
s = squircle(O, 60, 60, vertices=true)
pt1 = Point(0, -120)
pt2 = Point(0, 120)
line(pt1, pt2, :stroke)
poly1, poly2 = polysplit(s, pt1, pt2)
randomhue()
poly(poly1, :fill)
randomhue()
poly(poly2, :fill)
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

`Luxor.polysplit`

— Function.`polysplit(p, p1, p2)`

Split a polygon into two where it intersects with a line. It returns two polygons:

`(poly1, poly2)`

This doesn't always work, of course. For example, a polygon the shape of the letter "E" might end up being divided into more than two parts.

`Luxor.polysortbydistance`

— Function.Sort a polygon by finding the nearest point to the starting point, then the nearest point to that, and so on.

`polysortbydistance(p, starting::Point)`

You can end up with convex (self-intersecting) polygons, unfortunately.

`Luxor.polysortbyangle`

— Function.Sort the points of a polygon into order. Points are sorted according to the angle they make with a specified point.

`polysortbyangle(pointlist::AbstractArray, refpoint=minimum(pointlist))`

The `refpoint`

can be chosen, but the minimum point is usually OK too:

`polysortbyangle(parray, polycentroid(parray))`

`Luxor.polycentroid`

— Function.Find the centroid of simple polygon.

`polycentroid(pointlist)`

Returns a point. This only works for simple (non-intersecting) polygons.

You could test the point using `isinside()`

.

### Smoothing polygons

Because polygons can have sharp corners, the experimental `polysmooth()`

function attempts to insert arcs at the corners and draw the result.

The original polygon is shown in red; the smoothed polygon is shown on top:

```
tiles = Tiler(600, 250, 1, 5, margin=10)
for (pos, n) in tiles
p = star(pos, tiles.tilewidth/2 - 2, 5, 0.3, 0, vertices=true)
setdash("dot")
sethue("red")
prettypoly(p, close=true, :stroke)
setdash("solid")
sethue("black")
polysmooth(p, n * 2, :fill)
end
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

The final polygon shows that you can get unexpected results if you attempt to smooth corners by more than the possible amount. The `debug=true`

option draws the circles if you want to find out what's going wrong, or if you want to explore the effect in more detail.

```
p = star(O, 60, 5, 0.35, 0, vertices=true)
setdash("dot")
sethue("red")
prettypoly(p, close=true, :stroke)
setdash("solid")
sethue("black")
polysmooth(p, 40, :fill, debug=true)
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

`Luxor.polysmooth`

— Function.`polysmooth(points, radius, action=:action; debug=false)`

Make a closed path from the `points`

and round the corners by making them arcs with the given radius. Execute the action when finished.

The arcs are sometimes different sizes: if the given radius is bigger than the length of the shortest side, the arc can't be drawn at its full radius and is therefore drawn as large as possible (as large as the shortest side allows).

The `debug`

option also draws the construction circles at each corner.

### Offsetting polygons

The experimental `offsetpoly()`

function constructs an outline polygon outside or inside an existing polygon. In the following example, the dotted red polygon is the original, the black polygons have positive offsets and surround the original, the cyan polygons have negative offsets and run inside the original. Use `poly()`

to draw the result returned by `offsetpoly()`

.

```
p = star(O, 45, 5, 0.5, 0, vertices=true)
sethue("red")
setdash("dot")
poly(p, :stroke, close=true)
setdash("solid")
sethue("black")
poly(offsetpoly(p, 20), :stroke, close=true)
poly(offsetpoly(p, 25), :stroke, close=true)
poly(offsetpoly(p, 30), :stroke, close=true)
poly(offsetpoly(p, 35), :stroke, close=true)
sethue("darkcyan")
poly(offsetpoly(p, -10), :stroke, close=true)
poly(offsetpoly(p, -15), :stroke, close=true)
poly(offsetpoly(p, -20), :stroke, close=true)
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

The function is intended for simple cases, and it can go wrong if pushed too far. Sometimes the offset distances can be larger than the polygon segments, and things will start to go wrong. In this example, the offset goes so far negative that the polygon overshoots the origin, becomes inverted and starts getting larger again.

`Luxor.offsetpoly`

— Function.`offsetpoly(path::AbstractArray{Point, 1}, d)`

Return a polygon that is offset from a polygon by `d`

units.

The incoming set of points `path`

is treated as a polygon, and another set of points is created, which form a polygon lying `d`

units away from the source poly.

Polygon offsetting is a topic on which people have written PhD theses and published academic papers, so this short brain-dead routine will give good results for simple polygons up to a point (!). There are a number of issues to be aware of:

very short lines tend to make the algorithm 'flip' and produce larger lines

small polygons that are counterclockwise and larger offsets may make the new polygon appear the wrong side of the original

very sharp vertices will produce even sharper offsets, as the calculated intersection point veers off to infinity

duplicated adjacent points might cause the routine to scratch its head and wonder how to draw a line parallel to them

### Fitting splines

The experimental `polyfit()`

function constructs a B-spline that follows the points approximately.

```
pts = [Point(x, rand(-100:100)) for x in -280:30:280]
setopacity(0.7)
sethue("red")
prettypoly(pts, :none, () -> circle(O, 5, :fill))
sethue("darkmagenta")
poly(polyfit(pts, 200), :stroke)
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

`Luxor.polyfit`

— Function.`polyfit(plist::AbstractArray, npoints=30)`

Build a polygon that constructs a B-spine approximation to it. The resulting list of points makes a smooth path that runs between the first and last points.

## Converting paths to polygons

You can convert the current path to an array of polygons, using `pathtopoly()`

.

In the next example, the path consists of a number of paths, some of which are subpaths, which form the holes.

```
textpath("get polygons from paths")
plist = pathtopoly()
for (n, pgon) in enumerate(plist)
randomhue()
prettypoly(pgon, :stroke, close=true)
gsave()
translate(0, 100)
poly(polysortbyangle(pgon, polycentroid(pgon)), :stroke, close=true)
grestore()
end
```

```
WARNING: Base.uninitialized is deprecated, use undef instead.
likely near /Users/travis/build/JuliaGraphics/Luxor.jl/docs/make.jl:3
WARNING: Base.uninitialized is deprecated, use undef instead.
likely near /Users/travis/build/JuliaGraphics/Luxor.jl/docs/make.jl:3
WARNING: Base.uninitialized is deprecated, use undef instead.
likely near /Users/travis/build/JuliaGraphics/Luxor.jl/docs/make.jl:3
WARNING: Base.uninitialized is deprecated, use undef instead.
likely near /Users/travis/build/JuliaGraphics/Luxor.jl/docs/make.jl:3
┌ Warning: `unsafe_wrap(T, pointer, dims, own)` is deprecated, use `unsafe_wrap(T, pointer, dims, own=own)` instead.
│ caller = convert_cairo_path_data(::Cairo.CairoPath) at Cairo.jl:780
└ @ Cairo Cairo.jl:780
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

The `pathtopoly()`

function calls `getpathflat()`

to convert the current path to an array of polygons, with each curved section flattened to line segments.

The `getpath()`

function gets the current path as an array of elements, lines, and unflattened curves.

`Luxor.pathtopoly`

— Function.`pathtopoly()`

Convert the current path to an array of polygons.

Returns an array of polygons.

`Luxor.getpath`

— Function.`getpath()`

Get the current path and return a CairoPath object, which is an array of `element_type`

and `points`

objects. With the results you can step through and examine each entry:

```
o = getpath()
for e in o
if e.element_type == Cairo.CAIRO_PATH_MOVE_TO
(x, y) = e.points
move(x, y)
elseif e.element_type == Cairo.CAIRO_PATH_LINE_TO
(x, y) = e.points
# straight lines
line(x, y)
strokepath()
circle(x, y, 1, :stroke)
elseif e.element_type == Cairo.CAIRO_PATH_CURVE_TO
(x1, y1, x2, y2, x3, y3) = e.points
# Bezier control lines
circle(x1, y1, 1, :stroke)
circle(x2, y2, 1, :stroke)
circle(x3, y3, 1, :stroke)
move(x, y)
curve(x1, y1, x2, y2, x3, y3)
strokepath()
(x, y) = (x3, y3) # update current point
elseif e.element_type == Cairo.CAIRO_PATH_CLOSE_PATH
closepath()
else
error("unknown CairoPathEntry " * repr(e.element_type))
error("unknown CairoPathEntry " * repr(e.points))
end
end
```

`Luxor.getpathflat`

— Function.`getpathflat()`

Get the current path, like `getpath()`

but flattened so that there are no Bèzier curves.

Returns a CairoPath which is an array of `element_type`

and `points`

objects.

## Polygons to Bézier paths and back again

Use the `makebezierpath()`

and `drawbezierpath()`

functions to make and draw Bézier paths, and `pathtobezierpaths()`

to convert the current path to an array of Bézier paths.

A BezierPath type contains a sequence of `BezierPathSegment`

s; each curve segment is defined by four points: two end points and their control points.

```
(Point(-129.904, 75.0), # start point
Point(-162.38, 18.75), # ^ control point
Point(-64.9519, -150.0), # v control point
Point(-2.75546e-14, -150.0)), # end point
(Point(-2.75546e-14, -150.0),
Point(64.9519, -150.0),
Point(162.38, 18.75),
Point(129.904, 75.0)),
(Point(129.904, 75.0),
Point(97.4279, 131.25),
Point(-97.4279, 131.25),
Point(-129.904, 75.0)
),
...
```

Bézier paths are different from ordinary paths in that they don't usually contain straight line segments. However, by setting the two control points to be the same as their matching start/end points, you create straight line sections.

`makebezierpath()`

takes the points in a polygon and converts each line segment into one Bézier curve. `drawbezierpath()`

draws the resulting sequence.

```
pts = ngon(O, 150, 3, pi/6, vertices=true)
bezpath = makebezierpath(pts)
poly(pts, :stroke)
for (p1, c1, c2, p2) in bezpath[1:end-1]
circle.([p1, p2], 4, :stroke)
circle.([c1, c2], 2, :fill)
line(p1, c1, :stroke)
line(p2, c2, :stroke)
end
sethue("black")
setline(3)
drawbezierpath(bezpath, :stroke, close=false)
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

```
tiles = Tiler(600, 300, 1, 4, margin=20)
for (pos, n) in tiles
@layer begin
translate(pos)
pts = polysortbyangle(
randompointarray(
Point(-tiles.tilewidth/2, -tiles.tilewidth/2),
Point(tiles.tilewidth/2, tiles.tilewidth/2),
4))
setopacity(0.7)
sethue("black")
prettypoly(pts, :stroke, close=true)
randomhue()
drawbezierpath(makebezierpath(pts), :fill)
end
end
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

You can convert a Bézier path to a polygon (an array of points), using the `bezierpathtopoly()`

function. This chops up the curves into a series of straight line segments. An optional `steps`

keyword lets you specify how many line segments are used to approximate each Bézier segment.

In this example, the original star is drawn in a dotted gray line, then converted to a Bézier path (drawn in orange), then the Bézier path is converted (with low resolution) to a polygon but offset by 20 units before being drawn (in blue).

```
pgon = star(O, 250, 5, 0.6, 0, vertices=true)
@layer begin
setgrey(0.5)
setdash("dot")
poly(pgon, :stroke, close=true)
setline(5)
end
setline(4)
sethue("orangered")
np = makebezierpath(pgon)
drawbezierpath(np, :stroke)
sethue("steelblue")
p = bezierpathtopoly(np, steps=3)
q1 = offsetpoly(p, 20)
prettypoly(q1, :stroke, close=true)
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

You can convert the current path to an array of BezierPaths using the `pathtobezierpaths()`

function.

In the next example, the letter "a" is placed at the current position (set by `move()`

) and then converted to an array of Bézier paths. Each Bézier path is drawn first of all in gray, then the control points of segment are drawn (in orange) showing how they affect the curvature.

```
st = "a"
thefontsize = 500
fontsize(thefontsize)
sethue("red")
tex = textextents(st)
move(-tex[3]/2, tex[4]/2)
textpath(st)
nbps = pathtobezierpaths()
setline(1.5)
for nbp in nbps
sethue("grey80")
drawbezierpath(nbp, :stroke)
for p in nbp
sethue("darkorange")
circle(p[2], 2.0, :fill)
circle(p[3], 2.0, :fill)
line(p[2], p[1], :stroke)
line(p[3], p[4], :stroke)
if p[1] != p[4]
sethue("black")
circle(p[1], 2.0, :fill)
circle(p[4], 2.0, :fill)
end
end
end
```

```
┌ Warning: `unsafe_wrap(T, pointer, dims, own)` is deprecated, use `unsafe_wrap(T, pointer, dims, own=own)` instead.
│ caller = convert_cairo_path_data(::Cairo.CairoPath) at Cairo.jl:780
└ @ Cairo Cairo.jl:780
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

### Brush strokes

```
brush(Point(-250, 0), Point(250, 0), 20,
strokes=15,
tidystart=true,
twist=-5,
lowhandle=-0.5,
highhandle=0.5)
```

```
┌ Warning: broadcast will default to iterating over its arguments in the future. Wrap arguments of
│ type `x::Point` with `Ref(x)` to ensure they broadcast as "scalar" elements.
│ caller = ip:0x0
└ @ Core :-1
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

`Luxor.bezier`

— Function.`bezier(t, A::Point, A1::Point, B1::Point, B::Point)`

Return the result of evaluating the Bezier cubic curve function, `t`

going from 0 to 1, starting at A, finishing at B, control points A1 (controlling A), and B1 (controlling B).

`Luxor.beziercurvature`

— Function.`beziercurvature(t, A::Point, A1::Point, B1::Point, B::Point)`

Return the curvature of the Bezier curve at `t`

([0-1]), given start and end points A and B, and control points A1 and B1. The value (kappa) will typically be a value between -0.001 and 0.001 for points with coordinates in the 100-500 range.

κ(t) is the curvature of the curve at point t, which for a parametric planar curve is:

\begin{equation} \kappa = \frac{\mid \dot{x}\ddot{y}-\dot{y}\ddot{x}\mid} {(\dot{x}^2 + \dot{y}^2)^{\frac{3}{2}}} \end{equation}

The radius of curvature, or the radius of an osculating circle at a point, is 1/κ(t). Values of 1/κ will typically be in the range -1000 to 1000 for points with coordinates in the 100-500 range.

TODO Fix overshoot...

...The value of kappa can sometimes collapse near 0, returning NaN (and Inf for radius of curvature).

`Luxor.bezierfrompoints`

— Function.```
bezierfrompoints(startpoint::Point, pointonline1::Point,
pointonline2::Point, endpoint::Point)
```

Given four points, return the Bezier curve that passes through all four points, starting at `startpoint`

and ending at `endpoint`

. The two middle points of the returned BezierPathSegment are the two control points that make the curve pass through the two middle points supplied.

`bezierfrompoints(ptslist::Array{Point, 1})`

Given four points, return the Bezier curve that passes through all four points.

`Luxor.bezierpathtopoly`

— Function.`bezierpathtopoly(bezierpath::BezierPath; steps=10)`

Convert a Bezier path (an array of Bezier segments, where each segment is a tuple of four points: anchor1, control1, control2, anchor2) to a polygon.

To make a Bezier path, use `makebezierpath()`

on a polygon.

The `steps`

optional keyword determines how many line sections are used for each path.

`Luxor.bezierstroke`

— Function.`bezierstroke(point1, point2, width=0.0)`

Return a BezierPath, a stroked version of a straight line between two points.

It wil have 2 or 6 Bezier path segments that define a brush or pen shape. If width is 0, the brush shape starts and ends at a point. Otherwise the brush shape starts and ends with the thick end.

To draw it, use eg `drawbezierpath(..., :fill)`

.

`Luxor.beziertopoly`

— Function.`beziertopoly(bpseg::BezierPathSegment; steps=10)`

Convert a Bezier segment to a polygon (an array of points).

`Luxor.drawbezierpath`

— Function.```
drawbezierpath(bezierpath::BezierPath, action=:none;
close=true)
```

Draw the Bézier path, and apply the action, such as `:none`

, `:stroke`

, `:fill`

, etc. By default the path is closed.

```
drawbezierpath(bps::BezierPathSegment, action=:none;
close=false)
```

Draw the Bézier path segment, and apply the action, such as `:none`

, `:stroke`

, `:fill`

, etc. By default the path is open.

`Luxor.makebezierpath`

— Function.`makebezierpath(pgon::AbstractArray{Point, 1}; smoothing=1)`

Return a Bézier path (a BezierPath) that represents a polygon (an array of points). The Bézier path is an array of segments (tuples of 4 points); each segment contains the four points that make up a section of the entire Bézier path. `smoothing`

determines how closely the curve follows the polygon. A value of 0 returns a straight-sided path; as values move above 1 the paths deviate further from the original polygon's edges.

`Luxor.pathtobezierpaths`

— Function.```
pathtobezierpaths(
; flat=true)
```

Convert the current path (which may consist of one or more paths) to an array of Bezier paths. Each Bezier path is, in turn, an array of path segments. Each path segment is a tuple of four points. A straight line is converted to a Bezier segment in which the control points are set to be the the same as the end points.

If `flat`

is true, use `getpathflat()`

rather than `getpath()`

.

**Example**

This code draws the Bezier segments and shows the control points as "handles", like a vector-editing program might.

```
@svg begin
fontface("MyanmarMN-Bold")
st = "goo"
thefontsize = 100
fontsize(thefontsize)
sethue("red")
fontsize(thefontsize)
textpath(st)
nbps = pathtobezierpaths()
for nbp in nbps
setline(.15)
sethue("grey50")
drawbezierpath(nbp, :stroke)
for p in nbp
sethue("red")
circle(p[2], 0.16, :fill)
circle(p[3], 0.16, :fill)
line(p[2], p[1], :stroke)
line(p[3], p[4], :stroke)
if p[1] != p[4]
sethue("black")
circle(p[1], 0.26, :fill)
circle(p[4], 0.26, :fill)
end
end
end
end
```

`Luxor.setbezierhandles`

— Function.```
setbezierhandles(bps::BezierPathSegment;
angles = [0.05, -0.1],
handles = [0.3, 0.3])
```

Return a new Bezier path segment with new locations for the Bezier control points in the Bezier path segment `bps`

.

`angles`

are the two angles that the "handles" make with the line direciton.

`handles`

are the lengths of the "handles". 0.3 is a typical value.

```
setbezierhandles(bezpath::BezierPath;
angles=[0 .05, -0.1],
handles=[0.3, 0.3])
```

Return a new Bezierpath with new locations for the Bezier control points in every Bezier path segment of the BezierPath in `bezpath`

.

`angles`

are the two angles that the "handles" make with the line direciton.

`handles`

are the lengths of the "handles". 0.3 is a typical value.

`Luxor.shiftbezierhandles`

— Function.```
shiftbezierhandles(bps::BezierPathSegment;
angles=[0.1, -0.1], handles=[1.1, 1.1])
```

Return a new BezierPathSegment that modifies the Bezier path in `bps`

by moving the control handles. The values in `angles`

increase the angle of the handles; the values in `handles`

modifies the lengths: 1 preserves the length, 0.5 halves the length of the handles, 2 doubles them.

`Luxor.brush`

— Function.```
brush(pt1, pt2, width=10;
strokes=10,
minwidth=0.01,
maxwidth=0.03,
twist = -1,
lowhandle = 0.3,
highhandle = 0.7,
randomopacity = true,
tidystart = false,
action = :fill)
```

Draw a composite brush stroke made up of some randomized individual filled Bezier paths.

There is a lot of randomness in this function. Results are unpredictable.

## Polygon information

`polyperimeter`

calculates the length of a polygon's perimeter.

```
p = box(O, 50, 50, vertices=true)
poly(p, :stroke)
text(string(round(polyperimeter(p, closed=false))), O.x, O.y + 60)
translate(200, 0)
poly(p, :stroke, close=true)
text(string(round(polyperimeter(p, closed=true))), O.x, O.y + 60)
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

`polyportion()`

and `polyremainder()`

return part of a polygon depending on the fraction you supply. For example, `polyportion(p, 0.5)`

returns the first half of polygon `p`

, `polyremainder(p, .75)`

returns the last quarter of it.

```
p = ngon(O, 100, 7, 0, vertices=true)
poly(p, :stroke, close=true)
setopacity(0.75)
setline(20)
sethue("red")
poly(polyportion(p, 0.25), :stroke)
setline(10)
sethue("green")
poly(polyportion(p, 0.5), :stroke)
setline(5)
sethue("blue")
poly(polyportion(p, 0.75), :stroke)
setline(1)
circle(polyremainder(p, 0.75)[1], 5, :stroke) # first point
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

`polydistances`

returns an array of the accumulated side lengths of a polygon.

```
julia> p = ngon(O, 100, 7, 0, vertices=true);
julia> polydistances(p)
8-element Array{Real,1}:
0.0000
86.7767
173.553
260.33
347.107
433.884
520.66
607.437
```

It's used by `polyportion()`

and `polyremainder()`

, and you can pre-calculate and pass them to these functions via keyword arguments for performance. By default the result includes the final closing segment (`closed=true`

).

These functions also make use of the `nearestindex()`

, which returns a tuple of: the index of the nearest value in an array of distances to a given value; and the excess value.

In this example, we want to find a point halfway round the perimeter of a triangle. Use `nearestindex()`

to find the index of the nearest vertex (`nidx`

, 2), and the surplus length, (`over`

, 100).

```
p = ngonside(O, 200, 3, vertices=true)
prettypoly(p, :stroke, close=true, vertexlabels = (n, l) -> label(string(n), :NW, offset=10))
# distances array
da = polydistances(p)
nidx, over = nearestindex(da, polyperimeter(p)/2)
sethue("red")
circle(p[nidx], 5, :stroke)
arrow(p[nidx],
between(p[nidx], p[nidx+1], over/distance(p[nidx], p[nidx+1])),
linewidth=2)
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

Of course, it's much easier to do `polyportion(p, 0.5)`

.

### Area of polygon

Use `polyarea()`

to find the area of a polygon. Of course, this only works for simple polygons; polygons that intersect themselves or have holes are not correctly processed.

This code draws some regular polygons and calculates their area, perimeter, and shows how near the ratio of perimeter over radius approaches 2π.

```
fontface("Georgia")
sethue("black")
setline(0.25)
outerframe = Table([500], [400, 200])
total = 30
properties = Table(fill(15, total), [20, 85, 85], outerframe[1, 2])
radius = 55
sethue("grey20")
for i in 3:total
text(string(i), properties[i, 1], halign=:right)
p = ngon(outerframe[1], radius, i, 0, vertices=true)
prettypoly(p, :stroke, close=true, () -> (sethue("red"); circle(O, 2, :fill)))
pa = polyarea(p)
pp = polyperimeter(p)
ppoverradius = pp/radius
text(string(Int(round(pa, digits=0))), properties[i, 2], halign=:left)
text(string(round(ppoverradius, digits=6)), properties[i, 3], halign=:left)
radius += 5
end
fontsize(10)
[text(["Sides", "Area", "Perimeter/Radius"][n], pt, halign=:center)
for (pt, n) in Table([20], [20, 85, 85], outerframe[2] - (0, 220))]
```

```
┌ Warning: Deprecated syntax `implicit assignment to global variable `radius``.
│ Use `global radius` instead.
└ @ nothing none:0
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

`Luxor.polyperimeter`

— Function.`polyperimeter(p::AbstractArray{Point, 1}; closed=true)`

Find the total length of the sides of polygon `p`

.

`Luxor.polyportion`

— Function.`polyportion(p::AbstractArray{Point, 1}, portion=0.5; closed=true, pdist=[])`

Return a portion of a polygon, starting at a value between 0.0 (the beginning) and 1.0 (the end). 0.5 returns the first half of the polygon, 0.25 the first quarter, 0.75 the first three quarters, and so on.

If you already have a list of the distances between each point in the polygon (the "polydistances"), you can pass them in `pdist`

, otherwise they'll be calculated afresh, using `polydistances(p, closed=closed)`

.

Use the complementary `polyremainder()`

function to return the other part.

`Luxor.polyremainder`

— Function.`polyremainder(p::AbstractArray{Point, 1}, portion=0.5; closed=true, pdist=[])`

Return the rest of a polygon, starting at a value between 0.0 (the beginning) and 1.0 (the end). 0.5 returns the last half of the polygon, 0.25 the last three quarters, 0.75 the last quarter, and so on.

If you already have a list of the distances between each point in the polygon (the "polydistances"), you can pass them in `pdist`

, otherwise they'll be calculated afresh, using `polydistances(p, closed=closed)`

.

Use the complementary `polyportion()`

function to return the other part.

`Luxor.polydistances`

— Function.`polydistances(p::AbstractArray{Point, 1}; closed=true)`

Return an array of the cumulative lengths of a polygon.

`Luxor.nearestindex`

— Function.`nearestindex(polydistancearray, value)`

Return a tuple of the index of the largest value in `polydistancearray`

less than `value`

, and the difference value. Array is assumed to be sorted.

(Designed for use with `polydistances()`

).

`Luxor.polyarea`

— Function.`polyarea(p::AbstractArray)`

Find the area of a simple polygon. It works only for polygons that don't self-intersect.

## Polygon intersections (WIP)

`intersectlinepoly(pt1, pt2, polygon)`

returns an array containing the points where a line from `pt1`

to `pt2`

crosses the perimeter of the `polygon`

.

```
setline(0.3)
sethue("thistle")
c = star(O, 120, 7, 0.2, vertices=true)
poly(c, :fillstroke, close=true)
for n in 1:15
pt1 = Point(rand(-250:250, 2)...)
pt2 = Point(rand(-250:250, 2)...)
ips = intersectlinepoly(pt1, pt2, c)
if !isempty(ips)
sethue("grey20")
line(pt1, pt2, :stroke)
randomhue()
circle.(ips, 2, :fill)
else
sethue("grey80")
line(pt1, pt2, :stroke)
end
end
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

`polyintersections`

calculates the intersection points of two polygons.

```
pentagon = ngon(O, 250, 5, vertices=true)
square = box(O + (80, 20), 280, 280, vertices=true)
poly(pentagon, :stroke, close=true)
poly(square, :stroke, close=true)
sethue("orange")
circle.(polyintersections(pentagon, square), 8, :fill)
sethue("green")
circle.(polyintersections(square, pentagon), 4, :fill)
```

```
┌ Warning: The function `cfunction` is now written as a macro `@cfunction`.
│ caller = get_stream_callback at Cairo.jl:145 [inlined]
└ @ Core Cairo.jl:145
```

The returned polygon includes all the points in the first (source) polygon plus the points where the source polygon overlaps the target polygon.

`Luxor.intersectlinepoly`

— Function.`intersectlinepoly(pt1::Point, pt2::Point, C)`

Return an array of the points where a line between pt1 and pt2 crosses polygon C.

`Luxor.polyintersections`

— Function.`polyintersections(S::AbstractArray{Point, 1}, C::AbstractArray{Point, 1})`

Return an array of the points in polygon S plus the points where polygon S crosses polygon C. Calls `intersectlinepoly()`

.