-
-
Notifications
You must be signed in to change notification settings - Fork 290
/
bracket.jl
153 lines (124 loc) · 4.69 KB
/
bracket.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
"""
bracket(x1, y1, x2, y2; kwargs...)
bracket(x1s, y1s, x2s, y2s; kwargs...)
bracket(point1, point2; kwargs...)
bracket(vec_of_point_tuples; kwargs...)
Draws a bracket between each pair of points (x1, y1) and (x2, y2) with a text label at the midpoint.
By default each label is rotated parallel to the line between the bracket points.
## Attributes
$(ATTRIBUTES)
"""
@recipe(Bracket) do scene
Theme(
offset = 0,
width = 15,
text = "",
font = theme(scene, :font),
orientation = :up,
align = (:center, :center),
textoffset = automatic,
fontsize = theme(scene, :fontsize),
rotation = automatic,
color = theme(scene, :linecolor),
textcolor = theme(scene, :textcolor),
linewidth = theme(scene, :linewidth),
linestyle = :solid,
justification = automatic,
style = :curly,
)
end
Makie.convert_arguments(::Type{<:Bracket}, point1::VecTypes, point2::VecTypes) = ([(Point2f(point1), Point2f(point2))],)
Makie.convert_arguments(::Type{<:Bracket}, x1::Real, y1::Real, x2::Real, y2::Real) = ([(Point2f(x1, y1), Point2f(x2, y2))],)
function Makie.convert_arguments(::Type{<:Bracket}, x1::AbstractVector{<:Real}, y1::AbstractVector{<:Real}, x2::AbstractVector{<:Real}, y2::AbstractVector{<:Real})
points = broadcast(x1, y1, x2, y2) do x1, y1, x2, y2
(Point2f(x1, y1), Point2f(x2, y2))
end
(points,)
end
function Makie.plot!(pl::Bracket)
points = pl[1]
scene = parent_scene(pl)
textoffset_vec = Observable(Vec2f[])
bp = Observable(BezierPath[])
textpoints = Observable(Point2f[])
realtextoffset = lift(pl, pl.textoffset, pl.fontsize) do to, fs
return to === automatic ? Float32.(0.75 .* fs) : Float32.(to)
end
onany(pl, points, scene.camera.projectionview, pl.model, transform_func(pl),
scene.px_area, pl.offset, pl.width, pl.orientation, realtextoffset,
pl.style) do points, _, _, _, _, offset, width, orientation, textoff, style
empty!(bp[])
empty!(textoffset_vec[])
empty!(textpoints[])
broadcast_foreach(points, offset, width, orientation, textoff, style) do (_p1, _p2), offset, width, orientation, textoff, style
p1 = plot_to_screen(pl, _p1)
p2 = plot_to_screen(pl, _p2)
v = p2 - p1
d1 = normalize(v)
d2 = [0 -1; 1 0] * d1
orientation in (:up, :down) || error("Orientation must be :up or :down but is $(repr(orientation)).")
if (orientation == :up) != (d2[2] >= 0)
d2 = -d2
end
off = offset * d2
push!(textoffset_vec[], d2 * textoff)
b, textpoint = bracket_bezierpath(style, p1 + off, p2 + off, d2, width)
push!(textpoints[], textpoint)
push!(bp[], b)
end
notify(bp)
notify(textpoints)
end
notify(points)
autorotations = lift(pl, pl.rotation, textoffset_vec) do rot, tv
rots = Quaternionf[]
broadcast_foreach(rot, tv) do rot, tv
r = if rot === automatic
to_rotation(tv[2] >= 0 ? tv : -tv)
else
to_rotation(rot)
end
push!(rots, r)
end
return rots
end
# TODO: this works around `text()` not being happy if text="xyz" comes with one-element vector attributes
texts = lift(pl, pl.text) do text
return text isa AbstractString ? [text] : text
end
# Avoid scale!() / translate!() / rotate!() to affect these
series!(pl, bp; space = :pixel, solid_color = pl.color, linewidth = pl.linewidth,
linestyle = pl.linestyle, transformation = Transformation())
text!(pl, textpoints, text = texts, space = :pixel, align = pl.align, offset = textoffset_vec,
fontsize = pl.fontsize, font = pl.font, rotation = autorotations, color = pl.textcolor,
justification = pl.justification, model = Mat4f(I))
pl
end
data_limits(pl::Bracket) = mapreduce(union, pl[1][]) do points
Rect3f([points...])
end
bracket_bezierpath(style::Symbol, args...) = bracket_bezierpath(Val(style), args...)
function bracket_bezierpath(::Val{:curly}, p1, p2, d, width)
p12 = 0.5 * (p1 + p2) + width * d
c1 = p1 + width * d
c2 = p12 - width * d
c3 = p2 + width * d
b = BezierPath([
MoveTo(p1),
CurveTo(c1, c2, p12),
CurveTo(c2, c3, p2),
])
return b, p12
end
function bracket_bezierpath(::Val{:square}, p1, p2, d, width)
p12 = 0.5 * (p1 + p2) + width * d
c1 = p1 + width * d
c2 = p2 + width * d
b = BezierPath([
MoveTo(p1),
LineTo(c1),
LineTo(c2),
LineTo(p2),
])
return b, p12
end