1use crate::svg;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum Marker {
11 CircleOpen,
12 CircleFilled,
13 SquareOpen,
14 SquareFilled,
15 TriangleOpen,
16 TriangleFilled,
17 DiamondOpen,
18 DiamondFilled,
19 Cross,
20 Plus,
21 Asterisk,
22 Dot,
23}
24
25impl Marker {
26 pub const CYCLE: [Marker; 12] = [
27 Marker::CircleOpen,
28 Marker::SquareOpen,
29 Marker::TriangleOpen,
30 Marker::DiamondOpen,
31 Marker::Cross,
32 Marker::Plus,
33 Marker::CircleFilled,
34 Marker::SquareFilled,
35 Marker::TriangleFilled,
36 Marker::DiamondFilled,
37 Marker::Asterisk,
38 Marker::Dot,
39 ];
40
41 pub fn cycle(index: usize) -> Marker {
42 Self::CYCLE[index % Self::CYCLE.len()]
43 }
44}
45
46pub fn draw(buf: &mut String, marker: Marker, cx: f64, cy: f64, size: f64, stroke_width: f64) {
51 match marker {
52 Marker::CircleOpen => {
53 let attrs = format!(
54 " fill=\"none\" stroke=\"black\" stroke-width=\"{}\"",
55 svg::num(stroke_width)
56 );
57 svg::circle(buf, cx, cy, size, &attrs);
58 }
59 Marker::CircleFilled => {
60 svg::circle(buf, cx, cy, size, " fill=\"black\"");
61 }
62 Marker::SquareOpen => {
63 let attrs = format!(
64 " fill=\"none\" stroke=\"black\" stroke-width=\"{}\"",
65 svg::num(stroke_width)
66 );
67 svg::rect(buf, cx - size, cy - size, 2.0 * size, 2.0 * size, &attrs);
68 }
69 Marker::SquareFilled => {
70 svg::rect(
71 buf,
72 cx - size,
73 cy - size,
74 2.0 * size,
75 2.0 * size,
76 " fill=\"black\"",
77 );
78 }
79 Marker::TriangleOpen => {
80 let pts = triangle_points(cx, cy, size);
81 let attrs = format!(
82 " fill=\"none\" stroke=\"black\" stroke-width=\"{}\" stroke-linejoin=\"miter\"",
83 svg::num(stroke_width)
84 );
85 svg::polygon(buf, &pts, &attrs);
86 }
87 Marker::TriangleFilled => {
88 let pts = triangle_points(cx, cy, size);
89 svg::polygon(buf, &pts, " fill=\"black\"");
90 }
91 Marker::DiamondOpen => {
92 let pts = diamond_points(cx, cy, size);
93 let attrs = format!(
94 " fill=\"none\" stroke=\"black\" stroke-width=\"{}\"",
95 svg::num(stroke_width)
96 );
97 svg::polygon(buf, &pts, &attrs);
98 }
99 Marker::DiamondFilled => {
100 let pts = diamond_points(cx, cy, size);
101 svg::polygon(buf, &pts, " fill=\"black\"");
102 }
103 Marker::Cross => {
104 let attrs = format!(
105 " stroke=\"black\" stroke-width=\"{}\"",
106 svg::num(stroke_width)
107 );
108 svg::line(buf, cx - size, cy - size, cx + size, cy + size, &attrs);
109 svg::line(buf, cx - size, cy + size, cx + size, cy - size, &attrs);
110 }
111 Marker::Plus => {
112 let attrs = format!(
113 " stroke=\"black\" stroke-width=\"{}\"",
114 svg::num(stroke_width)
115 );
116 svg::line(buf, cx - size, cy, cx + size, cy, &attrs);
117 svg::line(buf, cx, cy - size, cx, cy + size, &attrs);
118 }
119 Marker::Asterisk => {
120 let attrs = format!(
121 " stroke=\"black\" stroke-width=\"{}\"",
122 svg::num(stroke_width)
123 );
124 svg::line(buf, cx - size, cy, cx + size, cy, &attrs);
125 svg::line(
126 buf,
127 cx - size * 0.5,
128 cy - size * 0.866,
129 cx + size * 0.5,
130 cy + size * 0.866,
131 &attrs,
132 );
133 svg::line(
134 buf,
135 cx - size * 0.5,
136 cy + size * 0.866,
137 cx + size * 0.5,
138 cy - size * 0.866,
139 &attrs,
140 );
141 }
142 Marker::Dot => {
143 svg::circle(buf, cx, cy, 0.8, " fill=\"black\"");
144 }
145 }
146}
147
148fn triangle_points(cx: f64, cy: f64, s: f64) -> [(f64, f64); 3] {
149 [
150 (cx, cy - s),
151 (cx + s * 0.866, cy + s * 0.5),
152 (cx - s * 0.866, cy + s * 0.5),
153 ]
154}
155
156fn diamond_points(cx: f64, cy: f64, s: f64) -> [(f64, f64); 4] {
157 [(cx, cy - s), (cx + s, cy), (cx, cy + s), (cx - s, cy)]
158}