Skip to content

Commit 19383a6

Browse files
authored
Fix inverted SVG arc sweep (#294)
Fixes #276 Replaces the SVG paths in the `svg` example with some off-the-shelf icons that users might expect to render correctly. With this change to `svg.rs`, they now render correctly. This is a breaking change, so anyone relying on the old behavior will need to invert the arc sweeps in their SVGs.
1 parent 40ac42f commit 19383a6

File tree

2 files changed

+63
-259
lines changed

2 files changed

+63
-259
lines changed

examples/svg.rs

Lines changed: 55 additions & 257 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use bevy::prelude::*;
1+
use bevy::{color, prelude::*};
22
use bevy_prototype_lyon::prelude::*;
33

44
fn main() {
@@ -8,269 +8,67 @@ fn main() {
88
.run();
99
}
1010

11-
#[derive(Component)]
12-
struct BlacksmithMarker;
13-
14-
#[derive(Component)]
15-
struct ToolShackMarker;
16-
1711
fn setup_system(mut commands: Commands) {
1812
commands.spawn((Camera2d, Msaa::Sample4));
1913

20-
commands
21-
.spawn((
22-
Name::new("Blacksmith"),
23-
BlacksmithMarker,
24-
Transform::from_translation(Vec3::new(-50., 0., 0.)),
25-
Visibility::default(),
26-
))
27-
//we split our art in this example to two children because our art is made out of 2 paths,
28-
//one path who's width is 4,
29-
//and another whose width is 2.5
30-
//the art style was approximated from https://www.kenney.nl/assets/cartography-pack
31-
.with_children(|parent| {
32-
let svg_doc_size = Vec2::new(512., 512.);
33-
34-
let svg = shapes::SvgPathShape {
35-
svg_path_string: BLACKSMITH_OUTLINE.to_owned(),
36-
svg_doc_size_in_px: svg_doc_size.to_owned(),
37-
};
38-
parent.spawn(ShapeBuilder::with(&svg).stroke((Color::BLACK, 4.0)).build());
14+
// The tolerance for tesselating curves, in the same units as the svg path
15+
let tolerance = 0.01;
16+
// Equivalent ot the "svg viewport"
17+
let svg_doc_size_in_px = Vec2::new(24., 24.);
3918

40-
let svg = shapes::SvgPathShape {
41-
svg_path_string: BLACKSMITH_DETAIL.to_owned(),
42-
svg_doc_size_in_px: svg_doc_size.to_owned(),
43-
};
44-
parent.spawn(ShapeBuilder::with(&svg).stroke((Color::BLACK, 2.5)).build());
45-
});
19+
let icon_fill = Fill {
20+
options: FillOptions::tolerance(tolerance),
21+
color: color::palettes::tailwind::GRAY_50.into(),
22+
};
23+
let icon_fill_grey = Fill {
24+
options: FillOptions::tolerance(tolerance),
25+
color: color::palettes::tailwind::GRAY_400.into(),
26+
};
27+
let scale = Vec2::splat(10.).extend(1.);
4628

47-
commands
48-
.spawn((
49-
Name::new("Shack"),
50-
ToolShackMarker,
51-
Transform {
52-
translation: Vec3::new(375., 0., 0.),
53-
scale: Vec3::new(0.1, 0.1, 1.),
54-
..Default::default()
55-
},
56-
Visibility::default(),
57-
))
58-
//we split our art in this example to two children because our art is made out of 2 paths,
59-
//one path who's width is 4,
60-
//and another whose width is 2.5
61-
//the art style was approximated from https://www.kenney.nl/assets/cartography-pack
62-
.with_children(|parent| {
63-
let svg_doc_size = Vec2::new(1000., 1000.);
29+
commands.spawn((
30+
ShapeBuilder::with(&shapes::SvgPathShape {
31+
svg_path_string: CHAT_CODE.to_owned(),
32+
svg_doc_size_in_px,
33+
})
34+
.fill(icon_fill)
35+
.build(),
36+
Transform {
37+
translation: Vec3::new(-200., 0., 0.),
38+
scale,
39+
..Default::default()
40+
},
41+
));
6442

65-
let svg = shapes::SvgPathShape {
66-
svg_path_string: SHACK.to_owned(),
67-
svg_doc_size_in_px: svg_doc_size.to_owned(),
68-
};
69-
parent.spawn(
70-
ShapeBuilder::with(&svg)
71-
.stroke((Color::BLACK, 20.0))
72-
.build(),
73-
);
43+
// A composite shape made of multiple SVG paths
7444

75-
// shack walls
76-
let svg = shapes::SvgPathShape {
77-
svg_path_string: SHACK_WALLS.to_owned(),
78-
svg_doc_size_in_px: svg_doc_size.to_owned(),
79-
};
80-
parent.spawn(
81-
ShapeBuilder::with(&svg)
82-
.stroke((Color::BLACK, 17.5))
83-
.build(),
84-
);
85-
});
86-
}
87-
88-
const BLACKSMITH_OUTLINE: &str = "m
89-
210.49052,219.61666
90-
c
91-
-54.97575,-3.12045
92-
-153.83891,-43.5046
93-
-181.900067,-79.34483
94-
41.944976,3.29834
95-
143.100787,1.42313
96-
185.138697,1.61897
97-
l
98-
6e-5,-0.003
99-
c
100-
41.78023,-0.87477
101-
200.563,-0.4537
102-
261.24529,0
103-
0.085,7.05106
104-
0.79737,22.71244
105-
1.07386,32.86306
106-
-42.04814,8.31883
107-
-101.90702,24.33338
108-
-128.45794,63.97855
109-
-10.53308,31.59203
110-
39.6912,45.827
111-
74.62215,55.19132
112-
1.14898,12.80889
113-
2.62233,32.62936
114-
2.46309,44.71853
115-
-75.4682,-0.86499
116-
-141.64601,-1.07063
117-
-209.86695,-1.35786
118-
-10.81491,-1.77566
119-
-6.66734,-23.1495
120-
-4.31819,-32.38456
121-
5.44628,-16.65332
122-
38.03788,-18.20507
123-
28.06768,-83.12367
124-
-7.29786,-2.58188
125-
-23.92259,-1.83114
126-
-28.06768,-2.15756";
45+
let planet_shape = ShapeBuilder::with(&shapes::SvgPathShape {
46+
svg_path_string: PLANET.to_owned(),
47+
svg_doc_size_in_px,
48+
})
49+
.fill(icon_fill)
50+
.build();
12751

128-
const BLACKSMITH_DETAIL: &str = "m 213.72921,141.88787 -4e-5,80.1576";
52+
let planet_mid_shape = ShapeBuilder::with(&shapes::SvgPathShape {
53+
svg_path_string: PLANET_MID.to_owned(),
54+
svg_doc_size_in_px,
55+
})
56+
.fill(icon_fill_grey)
57+
.build();
12958

130-
const SHACK: &str = "m
131-
254.47507,533.90714
132-
28.03554,-31.1502
133-
29.07393,-32.18938
134-
30.11225,-26.99742
135-
29.07391,-30.11185
136-
28.03556,-34.26547
137-
29.07391,-25.95885
138-
28.03556,-29.0741
139-
q
140-
13.49859,-16.61388
141-
21.80543,-21.80524
142-
l
143-
25.95885,-17.65243
144-
q
145-
20.76708,9.34498
146-
26.9972,26.99742
147-
6.2297,18.68994
148-
25.95885,35.30382
149-
l
150-
34.26568,29.07411
151-
31.15062,24.9205
152-
26.9972,23.88213
153-
24.92049,29.07412
154-
28.03556,37.38075
155-
q
156-
12.46024,18.69016
157-
22.84378,21.80522
158-
11.4219,4.15218
159-
28.03556,20.76687
160-
m
161-
-332.27326,332.27305
162-
2.07692,-44.64881
163-
v
164-
-40.496
165-
l
166-
-6.23054,-39.45766
167-
-3.11527,-42.57209
168-
1.03835,-35.30383
169-
6.23054,-46.72655
170-
44.64922,-3.1161
171-
38.4191,1.03627
172-
30.11226,-1.03627
173-
52.95605,3.1161
174-
q
175-
5.19218,20.76749
176-
-2.0767,43.61128
177-
-6.22972,22.84357
178-
1.03835,41.53437
179-
7.26806,18.68995
180-
3.11527,39.45682
181-
l
182-
-6.23054,46.72656
183-
q
184-
-1.03836,25.95884
185-
1.03835,35.30381
186-
l
187-
3.11527,42.5721
188-
m
189-
164.05971,-83.0681
190-
-33.22711,-1.03629
191-
-47.76428,1.03629
192-
-4.15362,-32.18855
193-
4.15362,-50.87956
194-
34.26567,1.03628
195-
48.80264,-1.03628
196-
m
197-
-498.40988,-83.06873
198-
30.11226,4.15217
199-
52.95606,-4.15217
200-
3.11505,33.22774
201-
q
202-
-3.11505,11.42189
203-
-3.11505,49.84099
204-
l
205-
-28.03557,1.03628
206-
-55.03275,-1.03628";
59+
commands.spawn((
60+
planet_shape,
61+
Transform {
62+
translation: Vec3::new(200., 0., 0.),
63+
scale,
64+
..default()
65+
},
66+
children![planet_mid_shape],
67+
));
68+
}
20769

208-
const SHACK_WALLS: &str = "m
209-
254.47507,866.18019
210-
q
211-
18.69037,-88.25945
212-
8.30683,-113.17996
213-
-9.34519,-24.92049
214-
-8.30683,-52.95625
215-
11.42188,-69.57013
216-
0,-83.06873
217-
v
218-
-83.06811
219-
l
220-
-34.26568,42.57292
221-
q
222-
-8.30684,13.49862
223-
-48.80263,40.49519
224-
l
225-
-49.841,-39.45683
226-
Q
227-
99.760328,557.78928
228-
88.33844,533.90714
229-
99.760328,499.64167
230-
136.10271,475.75953
231-
l
232-
67.49301,-53.99462
233-
57.10946,-62.30123
234-
q
235-
28.03557,-33.22712
236-
57.10947,-53.9946
237-
29.07391,-20.76688
238-
55.03276,-58.14762
239-
26.9972,-36.34218
240-
62.30124,-59.18595
241-
36.34239,-21.80524
242-
47.76428,-45.68738
243-
12.46024,-23.88276
244-
20.76708,-23.88276
245-
17.65201,12.46025
246-
43.61086,52.95626
247-
25.95885,40.49601
248-
65.4163,66.45486
249-
l
250-
72.68478,55.03235
251-
57.10946,58.14822
252-
60.22453,60.22453
253-
q
254-
36.34259,31.1502
255-
47.76427,60.22432
256-
11.42191,29.07412
257-
34.26569,45.68736
258-
23.88214,17.65244
259-
34.26589,16.61387
260-
l
261-
-43.61088,41.53437
262-
-39.45764,41.53374
263-
q
264-
-17.65203,-26.99657
265-
-38.4191,-40.49519
266-
l
267-
-44.64922,-42.57292
268-
v
269-
60.22453
270-
105.91231
271-
83.06811
272-
h
273-
-2.07671
274-
q
275-
-7.26826,4.15217
276-
2.07671,83.0681";
70+
// SVG paths by 480 Design under the CC BY 4.0 license
71+
// https://www.figma.com/community/file/1166831539721848736/solar-icons-set
72+
const CHAT_CODE: &str = "m13.087 21.388l.542-.916c.42-.71.63-1.066.968-1.262c.338-.197.763-.204 1.613-.219c1.256-.021 2.043-.098 2.703-.372a5 5 0 0 0 2.706-2.706C22 14.995 22 13.83 22 11.5v-1c0-3.273 0-4.91-.737-6.112a5 5 0 0 0-1.65-1.651C18.41 2 16.773 2 13.5 2h-3c-3.273 0-4.91 0-6.112.737a5 5 0 0 0-1.651 1.65C2 5.59 2 7.228 2 10.5v1c0 2.33 0 3.495.38 4.413a5 5 0 0 0 2.707 2.706c.66.274 1.447.35 2.703.372c.85.015 1.275.022 1.613.219c.337.196.548.551.968 1.262l.542.916c.483.816 1.69.816 2.174 0M14.97 7.299a.75.75 0 0 1 1.06 0l.209.209c.635.635 1.165 1.165 1.529 1.642c.384.503.654 1.035.654 1.68c0 .644-.27 1.176-.654 1.68c-.364.476-.894 1.006-1.53 1.642l-.208.208a.75.75 0 1 1-1.06-1.06l.171-.172c.682-.682 1.139-1.141 1.434-1.528c.283-.37.347-.586.347-.77s-.064-.4-.347-.77c-.295-.388-.752-.847-1.434-1.529l-.171-.171a.75.75 0 0 1 0-1.06m-.952-1.105a.75.75 0 1 0-1.449-.388l-2.588 9.66a.75.75 0 1 0 1.45.387zM9.03 7.3a.75.75 0 0 1 0 1.06l-.171.172c-.682.682-1.139 1.141-1.434 1.529c-.283.37-.347.585-.347.77c0 .184.064.4.347.77c.295.387.752.846 1.434 1.528l.171.171a.75.75 0 1 1-1.06 1.06l-.172-.17l-.037-.037c-.635-.636-1.165-1.165-1.529-1.643c-.384-.503-.654-1.035-.654-1.68c0-.644.27-1.176.654-1.68c.364-.476.894-1.006 1.53-1.641l.036-.037l.172-.172a.75.75 0 0 1 1.06 0";
73+
const PLANET: &str = "M21.206 15.912a41 41 0 0 0-.711.3l-.01.005c-.487.21-1.045.45-1.654.674c-1.226.454-2.693.86-4.322.86c-1.813 0-3.203-.486-4.317-1.02c-.43-.206-.829-.425-1.18-.617l-.272-.15c-.43-.232-.764-.399-1.062-.493a16.4 16.4 0 0 0-3.59-.677a16 16 0 0 0-1.453-.048l-.077.003h-.021l-.152.008a10.005 10.005 0 0 0 18.821 1.155M3.237 7.179l.297.302l.003.004l.019.018l.086.081c.079.072.2.18.36.31c.32.26.795.61 1.404.96c1.219.704 2.949 1.396 5.03 1.396c1.374 0 2.426-.394 3.318-.86c.355-.186.675-.377.993-.567l.275-.163c.392-.232.81-.468 1.234-.614a15 15 0 0 1 3.391-.743a11 11 0 0 1 1.155-.052A10 10 0 0 0 12 2a10 10 0 0 0-8.763 5.179";
74+
const PLANET_MID: &str = "M21.775 14.118Q22 13.092 22 12a10 10 0 0 0-.525-3.206l-.527-.038h-.011l-.051-.003a10 10 0 0 0-1.096.043a13.4 13.4 0 0 0-3.047.67c-.263.09-.563.252-.958.485l-.248.148c-.322.193-.69.413-1.088.62c-1.03.539-2.323 1.031-4.012 1.031c-2.418 0-4.407-.803-5.78-1.596a12 12 0 0 1-1.6-1.096a9 9 0 0 1-.48-.415a10.1 10.1 0 0 0-.498 4.628l.385-.02h.011l.027-.001a9 9 0 0 1 .45-.006c.303.002.733.014 1.253.055c1.037.08 2.447.277 3.923.742c.45.141.899.373 1.327.605l.299.163c.346.19.697.383 1.087.57c.98.47 2.144.871 3.668.871c1.383 0 2.662-.344 3.802-.766c.571-.21 1.099-.437 1.591-.65l.018-.007c.475-.204.937-.403 1.343-.538z";

src/shapes/svg.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,10 @@ impl Geometry<Builder> for SvgPathShape {
176176
Angle {
177177
radians: x_axis_rotation as f32,
178178
},
179-
ArcFlags { large_arc, sweep },
179+
ArcFlags {
180+
large_arc,
181+
sweep: !sweep,
182+
},
180183
get_point_after_offset(x, y, offset_x, offset_y),
181184
);
182185
} else {
@@ -185,7 +188,10 @@ impl Geometry<Builder> for SvgPathShape {
185188
Angle {
186189
radians: x_axis_rotation as f32,
187190
},
188-
ArcFlags { large_arc, sweep },
191+
ArcFlags {
192+
large_arc,
193+
sweep: !sweep,
194+
},
189195
get_corrected_relative_vector(x, y),
190196
);
191197
}

0 commit comments

Comments
 (0)