Skip to content

Commit

Permalink
fix arrowhead decorations
Browse files Browse the repository at this point in the history
  • Loading branch information
cormullion committed Jul 5, 2021
1 parent 44a794c commit 73ae373
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 31 deletions.
9 changes: 8 additions & 1 deletion docs/src/assets/luxor-docs.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
@font-face {
font-family: JuliaMono-Regular;
src:
url("https://github.com/cormullion/juliamono/raw/master/webfonts/JuliaMono-Regular.woff2");
}

pre, code {
font-feature-settings: "calt" 0;
font-family: JuliaMono-Regular !important;
font-feature-settings: "calt" 1;
}

p > a:after {
Expand Down
27 changes: 27 additions & 0 deletions docs/src/example/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,30 @@ draw(depth)
```

The Point type is an immutable composite type containing `x` and `y` fields that specify a 2D point.

## A simple numberline

```@example
using Luxor # hide
@drawsvg begin
background("white")
fontsize(14)
setline(1)
ht = 10
map(f -> begin
pt = between(O - (200, 0), O + (200, 0), f)
if f * 100 % 50 == 0
ht = 10
text(string(f), pt + (0, 30), halign=:center)
else
ht = 4
end
line(pt + polar(ht, π/2), pt + polar(ht, -π/2), :stroke)
end,
0:0.05:1.0)
arrow(O - (220, 0), O + (220, 0))
end 800 200
```
44 changes: 40 additions & 4 deletions docs/src/howto/simplegraphics.md
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,11 @@ nothing # hide

### Decoration

The [`arrow`](@ref) functions allow you to specify decoration - graphics at a point 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 [`arrow`](@ref) functions allow you to specify
decorations - graphics at a point 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` :

```
function marker(r, t)
Expand All @@ -600,7 +604,10 @@ function marker(r, t)
end
```

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

```@example
using Luxor # hide
Expand Down Expand Up @@ -636,9 +643,38 @@ nothing # hide
```
![arrows with decoration](../assets/figures/arrowbezierdecoration.png)

Use the `decoration` keyword to specify a location other than the default 0.5.
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 the decoration point, and rotated to the slope of the curve at that point.
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.

```@example
using Luxor
function fletcher()
line(O, polar(30, deg2rad(220)), :stroke)
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,
decorate=fletcher,
arrowheadfunction = customarrowhead,
decoration=range(0., .1, length=3))
end
```

### Custom arrowheads

Expand Down
4 changes: 2 additions & 2 deletions docs/src/tutorial/basictutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ If you've already downloaded Julia, and have added the Luxor package successfull

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

Expand Down
86 changes: 62 additions & 24 deletions src/arrows.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ end
linewidth = 1.0,
arrowheadlength = 10,
arrowheadangle = pi/8,
decoration = 0.5,
decoration = 0.5 or range(),
decorate = nothing,
arrowheadfunction = nothing)
Expand All @@ -45,18 +45,52 @@ Arrows don't use the current linewidth setting
another value. It doesn't need stroking/filling, the shaft
is stroked and the head filled with the current color.
### Decoration
The `decorate` keyword argument accepts a function with zero
arguments that can execute code at locations on the arrow's
shaft. The inherited graphic environment is centered at each
point on the curve between 0 and 1 given by scalar or vector
`decoration`, and the x-axis is aligned with the direction
of the curve at that point.
arguments that can execute code at one or more locations on
the arrow's shaft. The inherited graphic environment is
centered at each point on the shaft between 0 and 1 given by
scalar or vector `decoration`, and the x-axis is aligned
with the direction of the curve at that point.
### Arrowheads
A triangular arrowhead is drawn by default. But you can pass
a function to the `arrowheadfunction` keyword argument that
accepts three arguments: the shaft end, the arrow head end,
and the shaft angle. Thsi allows you to draw any shape
arrowhead.
### Example
```
function redbluearrow(shaftendpoint, endpoint, shaftangle)
@layer begin
sethue("red")
sidept1 = shaftendpoint + polar(10, shaftangle + π/2 )
sidept2 = shaftendpoint - polar(10, shaftangle + π/2)
poly([sidept1, endpoint, sidept2], :fill)
sethue("blue")
poly([sidept1, endpoint, sidept2], :stroke, close=false)
end
end
@drawsvg begin
background("white")
arrow(O, O + (120, 120),
linewidth=4,
arrowheadlength=40,
arrowheadangle=π/7,
arrowheadfunction = redbluearrow)
arrow(O, 100, 3π/2, π,
linewidth=4,
arrowheadlength=20,
clockwise=false,arrowheadfunction=redbluearrow)
end 800 250
```
"""
function arrow(startpoint::Point, endpoint::Point;
linewidth = 1.0,
Expand Down Expand Up @@ -114,6 +148,7 @@ function arrow(startpoint::Point, endpoint::Point;
else
for decpos in decoration
decpoint = between(startpoint, endpoint, decpos)
# slope at this point
slp = slope(startpoint, endpoint)
@layer begin
translate(decpoint)
Expand Down Expand Up @@ -144,16 +179,17 @@ Arrows don't use the current linewidth setting
(`setline()`); you can specify the linewidth.
The `decorate` keyword argument accepts a zero-argument
function that can execute code at locations on the arrow's
shaft. The inherited graphic environment is centered at
points on the curve between 0 and 1 given by scalar or
vector `decoration`, and the x-axis is aligned with the
direction of the curve at that point.
function that can execute code at one or more locations on
the arrow's shaft. The inherited graphic environment is
centered at points on the shaft between 0 and 1 given by
scalar or vector `decoration`, and the x-axis is aligned
with the direction of the curve at that point.
A triangular arrowhead is drawn by default. But you can pass
a function to the `arrowheadfunction` keyword argument that
accepts three arguments: the shaft end, the arrow head end,
and the shaft angle. Thsi allows you to draw any shape arrowhead.
and the shaft angle. Thsi allows you to draw any shape
arrowhead.
"""
function arrow(centerpos::Point, radius, startangle, endangle;
linewidth = 1.0,
Expand Down Expand Up @@ -240,10 +276,10 @@ function arrow(centerpos::Point, radius, startangle, endangle;
for decpos in decoration
decorationangle = rescale(decpos, 0, 1, startangle, newendangle)
decorationpoint = Point(radius * cos(decorationangle), radius * sin(decorationangle))
perp = perpendicular(decorationpoint)
ptangle = atan(decorationpoint.y, decorationpoint.x)
@layer begin
translate(decorationpoint)
rotate(slope(decorationpoint, perp))
rotate(π/2 + ptangle)
decorate()
end
end
Expand All @@ -263,15 +299,17 @@ end
decorate = nothing
arrowheadfunction = nothing)
Draw a Bezier curved arrow, from `start` to `finish`, with control points `C1`
and `C2`. Arrow heads can be added/hidden by changing `startarrow` and
`finisharrow` options.
Draw a Bezier curved arrow, from `start` to `finish`, with
control points `C1` and `C2`. Arrow heads can be
added/hidden by changing `startarrow` and `finisharrow`
options.
The `decorate` keyword argument accepts a function that can execute code at
locations on the arrow's shaft. The inherited graphic environment is centered at
each point on the curve given by scalar or vector `decoration`, and the x-axis
is aligned with the direction of the curve at that point (TODO - more or less -
is it actually correct?).
The `decorate` keyword argument accepts a function that can
execute code at one or more locations on the arrow's shaft.
The inherited graphic environment is centered at each point
on the shaft given by scalar or vector `decoration`, and the
x-axis is aligned with the direction of the curve at that
point.
### Example
Expand Down Expand Up @@ -354,9 +392,9 @@ function arrow(start::Point, C1::Point, C2::Point, finish::Point, action=:stroke
if decorate === nothing
else
for decpos in decoration
# decpoint = between(start, finish, decpos)
decpoint = bezier(decpos, start, C1, C2, finish)
slp = slope(start, finish)
bp = bezier′(decpos, start, C1, C2, finish)
slp = atan(bp.y, bp.x)
@layer begin
translate(decpoint)
rotate(slp)
Expand Down

0 comments on commit 73ae373

Please sign in to comment.