pub struct Rect {
pub x0: f64,
pub y0: f64,
pub x1: f64,
pub y1: f64,
}Expand description
A rectangle.
Fields§
§x0: f64The minimum x coordinate (left edge).
y0: f64The minimum y coordinate (top edge in y-down spaces).
x1: f64The maximum x coordinate (right edge).
y1: f64The maximum y coordinate (bottom edge in y-down spaces).
Implementations§
Source§impl Rect
impl Rect
Sourcepub const fn new(x0: f64, y0: f64, x1: f64, y1: f64) -> Rect
pub const fn new(x0: f64, y0: f64, x1: f64, y1: f64) -> Rect
A new rectangle from minimum and maximum coordinates.
Examples found in repository?
14fn main() {
15 let mut renderer = VelloRenderer::new().expect("vello renderer init");
16 let (w, h) = (512u32, 512u32);
17
18 {
19 let scene = renderer.scene();
20
21 // 1. Filled rectangle (solid brush)
22 let rect = Rect::new(40.0, 40.0, 240.0, 200.0).to_path(0.1);
23 let solid: Brush = rgb8(60, 120, 200).into();
24 scene.fill(
25 FillRule::NonZero,
26 Affine::IDENTITY,
27 &solid,
28 None,
29 &rect,
30 PickId::Skip,
31 );
32
33 // 2. Stroked open polyline with round caps/joins
34 let mut poly = Path::new();
35 poly.move_to(Point::new(60.0, 300.0));
36 poly.line_to(Point::new(140.0, 360.0));
37 poly.line_to(Point::new(200.0, 280.0));
38 poly.line_to(Point::new(260.0, 380.0));
39 let stroke = Stroke::new(8.0)
40 .with_caps(Cap::Round)
41 .with_join(Join::Round);
42 let line_brush: Brush = rgb8(220, 220, 220).into();
43 scene.stroke(
44 &stroke,
45 Affine::IDENTITY,
46 &line_brush,
47 None,
48 &poly,
49 PickId::Skip,
50 );
51
52 // 3. Gradient-filled circle
53 let circle = kurbo::Circle::new(Point::new(380.0, 150.0), 90.0).to_path(0.1);
54 let gradient = Gradient::new_linear(Point::new(290.0, 60.0), Point::new(470.0, 240.0))
55 .with_stops([rgb(0.95, 0.55, 0.15), rgb(0.15, 0.05, 0.45)].as_slice());
56 let grad_brush: Brush = gradient.into();
57 scene.fill(
58 FillRule::NonZero,
59 Affine::IDENTITY,
60 &grad_brush,
61 None,
62 &circle,
63 PickId::Skip,
64 );
65
66 // 4. Multiply layer with a translucent square overlapping the rect
67 let layer_clip = Rect::new(0.0, 0.0, w as f64, h as f64).to_path(0.1);
68 scene.push_layer(
69 BlendMode::new(Mix::Multiply, Compose::SrcOver),
70 1.0,
71 Affine::IDENTITY,
72 &layer_clip,
73 );
74 let overlap = Rect::new(160.0, 120.0, 360.0, 320.0).to_path(0.1);
75 let overlap_brush: Brush = rgba(1.0, 0.85, 0.2, 0.7).into();
76 scene.fill(
77 FillRule::NonZero,
78 Affine::IDENTITY,
79 &overlap_brush,
80 None,
81 &overlap,
82 PickId::Skip,
83 );
84 scene.pop_layer();
85 }
86
87 let mut pixels = vec![0u8; (w * h * 4) as usize];
88 let bg: Color = rgb8(20, 22, 28);
89 renderer
90 .render_to_buffer(w, h, bg, &mut pixels)
91 .expect("render");
92
93 let path = std::env::current_dir().unwrap().join("examples/hello.png");
94 hephaestus::image::write_png(&path, w, h, &pixels).expect("write png");
95 println!("wrote {}", path.display());
96}More examples
16fn main() {
17 let mut renderer = HybridRenderer::new().expect("hybrid renderer init");
18 let (w, h) = (512u32, 512u32);
19
20 {
21 let scene = renderer.scene();
22
23 // 1. Filled rectangle (solid brush)
24 let rect = Rect::new(40.0, 40.0, 240.0, 200.0).to_path(0.1);
25 let solid: Brush = rgb8(60, 120, 200).into();
26 scene.fill(
27 FillRule::NonZero,
28 Affine::IDENTITY,
29 &solid,
30 None,
31 &rect,
32 PickId::Skip,
33 );
34
35 // 2. Stroked open polyline with round caps/joins
36 let mut poly = Path::new();
37 poly.move_to(Point::new(60.0, 300.0));
38 poly.line_to(Point::new(140.0, 360.0));
39 poly.line_to(Point::new(200.0, 280.0));
40 poly.line_to(Point::new(260.0, 380.0));
41 let stroke = Stroke::new(8.0)
42 .with_caps(Cap::Round)
43 .with_join(Join::Round);
44 let line_brush: Brush = rgb8(220, 220, 220).into();
45 scene.stroke(
46 &stroke,
47 Affine::IDENTITY,
48 &line_brush,
49 None,
50 &poly,
51 PickId::Skip,
52 );
53
54 // 3. Gradient-filled circle
55 let circle = kurbo::Circle::new(Point::new(380.0, 150.0), 90.0).to_path(0.1);
56 let gradient = Gradient::new_linear(Point::new(290.0, 60.0), Point::new(470.0, 240.0))
57 .with_stops([rgb(0.95, 0.55, 0.15), rgb(0.15, 0.05, 0.45)].as_slice());
58 let grad_brush: Brush = gradient.into();
59 scene.fill(
60 FillRule::NonZero,
61 Affine::IDENTITY,
62 &grad_brush,
63 None,
64 &circle,
65 PickId::Skip,
66 );
67
68 // 4. Multiply layer with a translucent square overlapping the rect
69 let layer_clip = Rect::new(0.0, 0.0, w as f64, h as f64).to_path(0.1);
70 scene.push_layer(
71 BlendMode::new(Mix::Multiply, Compose::SrcOver),
72 1.0,
73 Affine::IDENTITY,
74 &layer_clip,
75 );
76 let overlap = Rect::new(160.0, 120.0, 360.0, 320.0).to_path(0.1);
77 let overlap_brush: Brush = rgba(1.0, 0.85, 0.2, 0.7).into();
78 scene.fill(
79 FillRule::NonZero,
80 Affine::IDENTITY,
81 &overlap_brush,
82 None,
83 &overlap,
84 PickId::Skip,
85 );
86 scene.pop_layer();
87 }
88
89 let mut pixels = vec![0u8; (w * h * 4) as usize];
90 let bg: Color = rgb8(20, 22, 28);
91 renderer
92 .render_to_buffer(w, h, bg, &mut pixels)
93 .expect("render");
94
95 let path = std::env::current_dir()
96 .unwrap()
97 .join("examples/hello_hybrid.png");
98 hephaestus::image::write_png(&path, w, h, &pixels).expect("write png");
99 println!("wrote {}", path.display());
100}100fn main() {
101 let mut renderer = VelloRenderer::new().expect("vello renderer init");
102 let registry = ShapeRegistry::with_builtins();
103
104 let glyph_brush: Brush = rgb8(60, 130, 220).into();
105 let chrome_brush: Brush = rgb8(80, 84, 96).into();
106 let line_brush: Brush = rgb8(190, 195, 205).into();
107
108 let glyph_world_stroke = 2.0;
109 let line_stroke = Stroke::new(2.5).with_caps(Cap::Butt);
110 let cell_stroke = Stroke::new(1.0);
111
112 {
113 let scene = renderer.scene();
114
115 for (i, name) in builtin::NAMES.iter().enumerate() {
116 let center = cell_center(i);
117 let cell = Rect::new(
118 center.x - CELL_W * 0.5 + 2.0,
119 center.y - CELL_H * 0.5 + 2.0,
120 center.x + CELL_W * 0.5 - 2.0,
121 center.y + CELL_H * 0.5 - 2.0,
122 )
123 .to_path(0.1);
124 scene.stroke(
125 &cell_stroke,
126 Affine::IDENTITY,
127 &chrome_brush,
128 None,
129 &cell,
130 PickId::Skip,
131 );
132
133 let shape = registry.get(name).expect("registered");
134 draw_shape_centered(scene, shape, center, 28.0, &glyph_brush, glyph_world_stroke);
135 }
136
137 let demos: &[(&str, f64)] = &[
138 ("arrow-closed", 18.0),
139 ("arrow-stealth", 22.0),
140 ("arrow-open", 18.0),
141 ("arrow-feather", 28.0),
142 ("arrow-dot", 14.0),
143 ];
144 let demo_top = GRID_TOP + (GRID_ROWS as f64) * CELL_H + 25.0;
145 let demo_spacing = 28.0;
146 let x_start = 60.0;
147 let x_end = (W as f64) - 60.0;
148
149 for (i, &(name, size)) in demos.iter().enumerate() {
150 let y = demo_top + (i as f64) * demo_spacing;
151 let start = Point::new(x_start, y);
152 let end = Point::new(x_end, y);
153
154 let direction = (end - start).normalize();
155 let shape = registry.get(name).expect("registered");
156
157 let mut line = hephaestus::Path::new();
158 line.move_to(start);
159 line.line_to(end);
160 scene.stroke(
161 &line_stroke,
162 Affine::IDENTITY,
163 &line_brush,
164 None,
165 &line,
166 PickId::Skip,
167 );
168
169 draw_shape_attached(
170 scene,
171 shape,
172 end,
173 direction,
174 size,
175 &glyph_brush,
176 glyph_world_stroke,
177 );
178 }
179 }
180
181 let mut pixels = vec![0u8; (W * H * 4) as usize];
182 let bg: Color = rgb8(20, 22, 28);
183 renderer
184 .render_to_buffer(W, H, bg, &mut pixels)
185 .expect("render");
186
187 let path = std::env::current_dir()
188 .unwrap()
189 .join("examples/shapes_demo.png");
190 hephaestus::image::write_png(&path, W, H, &pixels).expect("write png");
191 println!("wrote {}", path.display());
192}22fn main() {
23 let mut renderer = VelloRenderer::new().expect("vello renderer init");
24 let (w, h) = (640u32, 800u32);
25
26 {
27 let scene = renderer.scene();
28 let reference_brush: Brush = rgb8(80, 80, 80).into();
29 let dotted = Stroke::new(1.0).with_dashes(0.0, vec![3.0, 4.0]);
30
31 // ---------- strip 1: end-clipped, corner-rounded polyline ----------
32
33 let node_a_center = Point::new(120.0, 140.0);
34 let node_a_radius = 40.0;
35 let node_b = Rect::new(440.0, 100.0, 580.0, 180.0);
36
37 let node_brush: Brush = rgb8(60, 90, 140).into();
38 scene.fill(
39 FillRule::NonZero,
40 Affine::IDENTITY,
41 &node_brush,
42 None,
43 &kurbo::Circle::new(node_a_center, node_a_radius).to_path(0.1),
44 PickId::Skip,
45 );
46 scene.fill(
47 FillRule::NonZero,
48 Affine::IDENTITY,
49 &node_brush,
50 None,
51 &node_b.to_path(0.1),
52 PickId::Skip,
53 );
54
55 let raw = [
56 node_a_center,
57 Point::new(220.0, 60.0),
58 Point::new(340.0, 220.0),
59 Point::new(420.0, 80.0),
60 Point::new(node_b.center().x, node_b.center().y),
61 ];
62 let clipped = clip_polyline(
63 &raw,
64 Some(EndClip::Circle {
65 center: node_a_center,
66 radius: node_a_radius,
67 }),
68 Some(EndClip::Rect(node_b)),
69 );
70 let connector = round_corners(
71 &clipped,
72 false,
73 CornerRounding {
74 max_cut: 30.0,
75 ..Default::default()
76 },
77 );
78 let line_stroke = Stroke::new(4.0)
79 .with_caps(Cap::Round)
80 .with_join(Join::Round);
81 let line_brush: Brush = rgb8(230, 200, 120).into();
82 scene.stroke(
83 &line_stroke,
84 Affine::IDENTITY,
85 &line_brush,
86 None,
87 &connector,
88 PickId::Skip,
89 );
90 scene.stroke(
91 &dotted,
92 Affine::IDENTITY,
93 &reference_brush,
94 None,
95 &mk_polyline(&raw),
96 PickId::Skip,
97 );
98
99 // ---------- strip 2: polygon-with-hole, offset + rounded ----------
100
101 let outer = [
102 Point::new(120.0, 320.0),
103 Point::new(520.0, 320.0),
104 Point::new(520.0, 500.0),
105 Point::new(120.0, 500.0),
106 ];
107 let hole = [
108 Point::new(240.0, 370.0),
109 Point::new(400.0, 370.0),
110 Point::new(400.0, 450.0),
111 Point::new(240.0, 450.0),
112 ];
113 let rings: [&[Point]; 2] = [&outer, &hole];
114
115 let plain = polygon(&rings, PolygonOptions::default());
116 scene.stroke(
117 &dotted,
118 Affine::IDENTITY,
119 &reference_brush,
120 None,
121 &plain,
122 PickId::Skip,
123 );
124
125 let inflated_rings = offset_polygon(&rings, 15.0, 4.0);
126 let mut inflated = Path::new();
127 for r in &inflated_rings {
128 let sub = round_corners(r, true, CornerRounding::default());
129 for el in sub.iter() {
130 inflated.push(el);
131 }
132 }
133 let fill_brush: Brush = rgb8(180, 70, 90).into();
134 scene.fill(
135 FillRule::NonZero,
136 Affine::IDENTITY,
137 &fill_brush,
138 None,
139 &inflated,
140 PickId::Skip,
141 );
142
143 // ---------- strip 3: wedge & annular wedge with curve-aware rounding ----------
144
145 // Left: wedge with rounded line-to-arc corners.
146 let w_center = Point::new(170.0, 680.0);
147 let w_radius = 90.0;
148 let plain_wedge = wedge(w_center, w_radius, -PI / 2.0 - PI / 5.0, PI * 2.0 / 5.0);
149 scene.stroke(
150 &dotted,
151 Affine::IDENTITY,
152 &reference_brush,
153 None,
154 &plain_wedge,
155 PickId::Skip,
156 );
157 let rounded_wedge = round_path_corners(
158 &plain_wedge,
159 CornerRounding {
160 max_cut: 18.0,
161 ..Default::default()
162 },
163 );
164 let wedge_brush: Brush = rgb8(110, 160, 90).into();
165 scene.fill(
166 FillRule::NonZero,
167 Affine::IDENTITY,
168 &wedge_brush,
169 None,
170 &rounded_wedge,
171 PickId::Skip,
172 );
173
174 // Right: annular wedge with all four corners rounded.
175 let a_center = Point::new(450.0, 680.0);
176 let plain_aw = annular_wedge(a_center, 35.0, 100.0, -PI / 2.0 - PI / 4.0, PI / 2.0);
177 scene.stroke(
178 &dotted,
179 Affine::IDENTITY,
180 &reference_brush,
181 None,
182 &plain_aw,
183 PickId::Skip,
184 );
185 let rounded_aw = round_path_corners(
186 &plain_aw,
187 CornerRounding {
188 max_cut: 14.0,
189 ..Default::default()
190 },
191 );
192 let aw_brush: Brush = rgb8(180, 130, 90).into();
193 scene.fill(
194 FillRule::NonZero,
195 Affine::IDENTITY,
196 &aw_brush,
197 None,
198 &rounded_aw,
199 PickId::Skip,
200 );
201 }
202
203 let mut pixels = vec![0u8; (w * h * 4) as usize];
204 let bg: Color = rgb8(20, 22, 28);
205 renderer
206 .render_to_buffer(w, h, bg, &mut pixels)
207 .expect("render");
208
209 let path = std::env::current_dir()
210 .unwrap()
211 .join("examples/primitives_demo.png");
212 hephaestus::image::write_png(&path, w, h, &pixels).expect("write png");
213 println!("wrote {}", path.display());
214}143fn main() {
144 let (w, h) = (960u32, 1600u32);
145 let dpi = 96.0;
146 let bg: Color = rgb8(252, 252, 254);
147 // Sheet with three custom div classes — each demonstrates a
148 // different block-level styling axis.
149 let mut sheet = RichTextStyleSheet::new();
150 sheet.set(
151 "first-line-indent",
152 StyleDelta {
153 indent: Some(relative(2.0)),
154 ..StyleDelta::empty()
155 },
156 );
157 sheet.set(
158 "hanging-block",
159 StyleDelta {
160 hanging: Some(relative(2.5)),
161 ..StyleDelta::empty()
162 },
163 );
164 sheet.set(
165 "justified",
166 StyleDelta {
167 align: Some(HAlign::Justify),
168 ..StyleDelta::empty()
169 },
170 );
171 // Bright fill + contrasting halo. `color` forces parley to split
172 // the run at the span's edges so the outline stays contained.
173 sheet.set(
174 "haloed",
175 StyleDelta {
176 weight: Some(700),
177 color: Some(ThemeColor::Fixed(rgb8(230, 60, 60))),
178 text_stroke: Some(ThemeColor::Fixed(rgb8(255, 235, 205))),
179 text_stroke_width: Some(pt(2.0)),
180 ..StyleDelta::empty()
181 },
182 );
183 sheet.set(
184 "smcp",
185 StyleDelta {
186 features: Some(vec![FontFeatureSetting {
187 tag: *b"smcp",
188 value: 1,
189 }]),
190 ..StyleDelta::empty()
191 },
192 );
193 sheet.set(
194 "tnum",
195 StyleDelta {
196 features: Some(vec![FontFeatureSetting {
197 tag: *b"tnum",
198 value: 1,
199 }]),
200 ..StyleDelta::empty()
201 },
202 );
203 sheet.set(
204 "dashed-note",
205 StyleDelta {
206 border_color: Some(ThemeColor::Fixed(rgb8(160, 90, 40))),
207 border_width: Some(RichMargin::all(pt(1.5))),
208 border_type: Some(Arc::from(vec![
209 LinetypeStep::Dash(6.0),
210 LinetypeStep::Gap(3.0),
211 ])),
212 border_radius: Some(pt(4.0)),
213 padding: Some(RichMargin::all(pt(8.0))),
214 margin: Some(RichMargin {
215 top: pt(6.0),
216 right: pt(0.0),
217 bottom: pt(6.0),
218 left: pt(0.0),
219 }),
220 ..StyleDelta::empty()
221 },
222 );
223 sheet.set(
224 "stamped-note",
225 StyleDelta {
226 border_color: Some(ThemeColor::Fixed(rgb8(80, 130, 90))),
227 border_width: Some(RichMargin::all(pt(1.0))),
228 border_type: Some(Arc::from(vec![
229 LinetypeStep::Dash(6.0),
230 LinetypeStep::Gap(3.0),
231 LinetypeStep::Marker(Arc::from("circle")),
232 LinetypeStep::Gap(3.0),
233 ])),
234 padding: Some(RichMargin::all(pt(8.0))),
235 margin: Some(RichMargin {
236 top: pt(6.0),
237 right: pt(0.0),
238 bottom: pt(6.0),
239 left: pt(0.0),
240 }),
241 ..StyleDelta::empty()
242 },
243 );
244 sheet.set(
245 "rtl-quote",
246 StyleDelta {
247 text_direction: Some(Direction::Rtl),
248 padding: Some(RichMargin::all(pt(6.0))),
249 margin: Some(RichMargin {
250 top: pt(6.0),
251 right: pt(0.0),
252 bottom: pt(6.0),
253 left: pt(0.0),
254 }),
255 ..StyleDelta::empty()
256 },
257 );
258 sheet.set(
259 "l-shape",
260 StyleDelta {
261 border_color: Some(ThemeColor::Fixed(rgb8(60, 100, 160))),
262 // Top + left only. Same width on both so they collapse
263 // into one polyline through the top-left corner.
264 border_width: Some(RichMargin {
265 top: pt(2.0),
266 right: pt(0.0),
267 bottom: pt(0.0),
268 left: pt(2.0),
269 }),
270 padding: Some(RichMargin::all(pt(8.0))),
271 margin: Some(RichMargin {
272 top: pt(6.0),
273 right: pt(0.0),
274 bottom: pt(6.0),
275 left: pt(0.0),
276 }),
277 ..StyleDelta::empty()
278 },
279 );
280 let palette = Palette::default();
281 let base_style = TextStyle::new(13.0);
282 let base_brush: Color = rgb8(24, 24, 30);
283 // Column width for wrapping. Leave a 40px gutter on each side.
284 let column = (w as f32) - 80.0;
285 let run = RichTextRun::new_with_width(
286 SOURCE,
287 &base_style,
288 base_brush,
289 &sheet,
290 &palette,
291 dpi,
292 RichTextWidth::Fixed(column),
293 );
294 let mut renderer = VelloRenderer::new().expect("vello renderer init");
295 {
296 let scene = renderer.scene();
297 scene.clear();
298 // Faint dashed guide box showing the column bounds.
299 let guide_rect = Rect::new(
300 40.0,
301 40.0,
302 40.0 + column as f64,
303 40.0 + run.current_height() + 8.0,
304 );
305 let guide_path = rect_path(guide_rect);
306 let guide_stroke = Stroke::new(1.0);
307 scene.stroke(
308 &guide_stroke,
309 Affine::IDENTITY,
310 &Brush::Solid(rgb8(220, 220, 230)),
311 None,
312 &guide_path,
313 PickId::Skip,
314 );
315 // The block itself.
316 draw_rich_text(
317 scene,
318 &run,
319 40.0,
320 48.0,
321 RichAnchor::top_left(),
322 Affine::IDENTITY,
323 PickId::Skip,
324 );
325 }
326 let mut pixels = vec![0u8; (w * h * 4) as usize];
327 renderer
328 .render_to_buffer(w, h, bg, &mut pixels)
329 .expect("render");
330 let path = std::env::current_dir()
331 .unwrap()
332 .join("examples/rich_text_marquee_parity.png");
333 hephaestus::image::write_png(&path, w, h, &pixels).expect("write png");
334 println!("wrote {}", path.display());
335}Sourcepub fn from_points(p0: impl Into<Point>, p1: impl Into<Point>) -> Rect
pub fn from_points(p0: impl Into<Point>, p1: impl Into<Point>) -> Rect
A new rectangle from two points.
The result will have non-negative width and height.
Sourcepub fn from_origin_size(origin: impl Into<Point>, size: impl Into<Size>) -> Rect
pub fn from_origin_size(origin: impl Into<Point>, size: impl Into<Size>) -> Rect
A new rectangle from origin and size.
The result will have non-negative width and height.
Sourcepub fn from_center_size(center: impl Into<Point>, size: impl Into<Size>) -> Rect
pub fn from_center_size(center: impl Into<Point>, size: impl Into<Size>) -> Rect
A new rectangle from center and size.
Sourcepub fn with_origin(self, origin: impl Into<Point>) -> Rect
pub fn with_origin(self, origin: impl Into<Point>) -> Rect
Create a new Rect with the same size as self and a new origin.
Sourcepub fn with_size(self, size: impl Into<Size>) -> Rect
pub fn with_size(self, size: impl Into<Size>) -> Rect
Create a new Rect with the same origin as self and a new size.
Sourcepub const fn width(&self) -> f64
pub const fn width(&self) -> f64
The width of the rectangle.
Note: nothing forbids negative width.
Sourcepub const fn height(&self) -> f64
pub const fn height(&self) -> f64
The height of the rectangle.
Note: nothing forbids negative height.
Sourcepub const fn min_x(&self) -> f64
pub const fn min_x(&self) -> f64
Returns the minimum value for the x-coordinate of the rectangle.
Sourcepub const fn max_x(&self) -> f64
pub const fn max_x(&self) -> f64
Returns the maximum value for the x-coordinate of the rectangle.
Sourcepub const fn min_y(&self) -> f64
pub const fn min_y(&self) -> f64
Returns the minimum value for the y-coordinate of the rectangle.
Sourcepub const fn max_y(&self) -> f64
pub const fn max_y(&self) -> f64
Returns the maximum value for the y-coordinate of the rectangle.
Sourcepub const fn origin(&self) -> Point
pub const fn origin(&self) -> Point
The origin of the rectangle.
This is the top left corner in a y-down space and with non-negative width and height.
Sourcepub const fn is_zero_area(&self) -> bool
pub const fn is_zero_area(&self) -> bool
Whether this rectangle has zero area.
Sourcepub const fn center(&self) -> Point
pub const fn center(&self) -> Point
The center point of the rectangle.
Examples found in repository?
22fn main() {
23 let mut renderer = VelloRenderer::new().expect("vello renderer init");
24 let (w, h) = (640u32, 800u32);
25
26 {
27 let scene = renderer.scene();
28 let reference_brush: Brush = rgb8(80, 80, 80).into();
29 let dotted = Stroke::new(1.0).with_dashes(0.0, vec![3.0, 4.0]);
30
31 // ---------- strip 1: end-clipped, corner-rounded polyline ----------
32
33 let node_a_center = Point::new(120.0, 140.0);
34 let node_a_radius = 40.0;
35 let node_b = Rect::new(440.0, 100.0, 580.0, 180.0);
36
37 let node_brush: Brush = rgb8(60, 90, 140).into();
38 scene.fill(
39 FillRule::NonZero,
40 Affine::IDENTITY,
41 &node_brush,
42 None,
43 &kurbo::Circle::new(node_a_center, node_a_radius).to_path(0.1),
44 PickId::Skip,
45 );
46 scene.fill(
47 FillRule::NonZero,
48 Affine::IDENTITY,
49 &node_brush,
50 None,
51 &node_b.to_path(0.1),
52 PickId::Skip,
53 );
54
55 let raw = [
56 node_a_center,
57 Point::new(220.0, 60.0),
58 Point::new(340.0, 220.0),
59 Point::new(420.0, 80.0),
60 Point::new(node_b.center().x, node_b.center().y),
61 ];
62 let clipped = clip_polyline(
63 &raw,
64 Some(EndClip::Circle {
65 center: node_a_center,
66 radius: node_a_radius,
67 }),
68 Some(EndClip::Rect(node_b)),
69 );
70 let connector = round_corners(
71 &clipped,
72 false,
73 CornerRounding {
74 max_cut: 30.0,
75 ..Default::default()
76 },
77 );
78 let line_stroke = Stroke::new(4.0)
79 .with_caps(Cap::Round)
80 .with_join(Join::Round);
81 let line_brush: Brush = rgb8(230, 200, 120).into();
82 scene.stroke(
83 &line_stroke,
84 Affine::IDENTITY,
85 &line_brush,
86 None,
87 &connector,
88 PickId::Skip,
89 );
90 scene.stroke(
91 &dotted,
92 Affine::IDENTITY,
93 &reference_brush,
94 None,
95 &mk_polyline(&raw),
96 PickId::Skip,
97 );
98
99 // ---------- strip 2: polygon-with-hole, offset + rounded ----------
100
101 let outer = [
102 Point::new(120.0, 320.0),
103 Point::new(520.0, 320.0),
104 Point::new(520.0, 500.0),
105 Point::new(120.0, 500.0),
106 ];
107 let hole = [
108 Point::new(240.0, 370.0),
109 Point::new(400.0, 370.0),
110 Point::new(400.0, 450.0),
111 Point::new(240.0, 450.0),
112 ];
113 let rings: [&[Point]; 2] = [&outer, &hole];
114
115 let plain = polygon(&rings, PolygonOptions::default());
116 scene.stroke(
117 &dotted,
118 Affine::IDENTITY,
119 &reference_brush,
120 None,
121 &plain,
122 PickId::Skip,
123 );
124
125 let inflated_rings = offset_polygon(&rings, 15.0, 4.0);
126 let mut inflated = Path::new();
127 for r in &inflated_rings {
128 let sub = round_corners(r, true, CornerRounding::default());
129 for el in sub.iter() {
130 inflated.push(el);
131 }
132 }
133 let fill_brush: Brush = rgb8(180, 70, 90).into();
134 scene.fill(
135 FillRule::NonZero,
136 Affine::IDENTITY,
137 &fill_brush,
138 None,
139 &inflated,
140 PickId::Skip,
141 );
142
143 // ---------- strip 3: wedge & annular wedge with curve-aware rounding ----------
144
145 // Left: wedge with rounded line-to-arc corners.
146 let w_center = Point::new(170.0, 680.0);
147 let w_radius = 90.0;
148 let plain_wedge = wedge(w_center, w_radius, -PI / 2.0 - PI / 5.0, PI * 2.0 / 5.0);
149 scene.stroke(
150 &dotted,
151 Affine::IDENTITY,
152 &reference_brush,
153 None,
154 &plain_wedge,
155 PickId::Skip,
156 );
157 let rounded_wedge = round_path_corners(
158 &plain_wedge,
159 CornerRounding {
160 max_cut: 18.0,
161 ..Default::default()
162 },
163 );
164 let wedge_brush: Brush = rgb8(110, 160, 90).into();
165 scene.fill(
166 FillRule::NonZero,
167 Affine::IDENTITY,
168 &wedge_brush,
169 None,
170 &rounded_wedge,
171 PickId::Skip,
172 );
173
174 // Right: annular wedge with all four corners rounded.
175 let a_center = Point::new(450.0, 680.0);
176 let plain_aw = annular_wedge(a_center, 35.0, 100.0, -PI / 2.0 - PI / 4.0, PI / 2.0);
177 scene.stroke(
178 &dotted,
179 Affine::IDENTITY,
180 &reference_brush,
181 None,
182 &plain_aw,
183 PickId::Skip,
184 );
185 let rounded_aw = round_path_corners(
186 &plain_aw,
187 CornerRounding {
188 max_cut: 14.0,
189 ..Default::default()
190 },
191 );
192 let aw_brush: Brush = rgb8(180, 130, 90).into();
193 scene.fill(
194 FillRule::NonZero,
195 Affine::IDENTITY,
196 &aw_brush,
197 None,
198 &rounded_aw,
199 PickId::Skip,
200 );
201 }
202
203 let mut pixels = vec![0u8; (w * h * 4) as usize];
204 let bg: Color = rgb8(20, 22, 28);
205 renderer
206 .render_to_buffer(w, h, bg, &mut pixels)
207 .expect("render");
208
209 let path = std::env::current_dir()
210 .unwrap()
211 .join("examples/primitives_demo.png");
212 hephaestus::image::write_png(&path, w, h, &pixels).expect("write png");
213 println!("wrote {}", path.display());
214}Sourcepub fn contains(&self, point: impl Into<Point>) -> bool
pub fn contains(&self, point: impl Into<Point>) -> bool
Returns true if point lies within self.
Sourcepub const fn abs(&self) -> Rect
pub const fn abs(&self) -> Rect
Take absolute value of width and height.
The resulting rect has the same extents as the original, but is guaranteed to have non-negative width and height.
Sourcepub const fn union(&self, other: Rect) -> Rect
pub const fn union(&self, other: Rect) -> Rect
The smallest rectangle enclosing two rectangles.
Results are valid only if width and height are non-negative.
Sourcepub fn union_pt(&self, pt: impl Into<Point>) -> Rect
pub fn union_pt(&self, pt: impl Into<Point>) -> Rect
Compute the union with one point.
This method includes the perimeter of zero-area rectangles.
Thus, a succession of union_pt operations on a series of
points yields their enclosing rectangle.
Results are valid only if width and height are non-negative.
Sourcepub const fn intersect(&self, other: Rect) -> Rect
pub const fn intersect(&self, other: Rect) -> Rect
The intersection of two rectangles.
The result is zero-area if either input has negative width or height. The result always has non-negative width and height.
If you want to determine whether two rectangles intersect, use the
overlaps method instead.
Sourcepub const fn overlaps(&self, other: Rect) -> bool
pub const fn overlaps(&self, other: Rect) -> bool
Determines whether this rectangle overlaps with another in any way.
Note that the edge of the rectangle is considered to be part of itself, meaning that two rectangles that share an edge are considered to overlap.
Returns true if the rectangles overlap, false otherwise.
If you want to compute the intersection of two rectangles, use the
intersect method instead.
§Examples
use kurbo::Rect;
let rect1 = Rect::new(0.0, 0.0, 10.0, 10.0);
let rect2 = Rect::new(5.0, 5.0, 15.0, 15.0);
assert!(rect1.overlaps(rect2));
let rect1 = Rect::new(0.0, 0.0, 10.0, 10.0);
let rect2 = Rect::new(10.0, 0.0, 20.0, 10.0);
assert!(rect1.overlaps(rect2));Sourcepub const fn contains_rect(&self, other: Rect) -> bool
pub const fn contains_rect(&self, other: Rect) -> bool
Returns whether this rectangle contains another rectangle.
A rectangle is considered to contain another rectangle if the other rectangle is fully enclosed within the bounds of this rectangle.
§Examples
use kurbo::Rect;
let rect1 = Rect::new(0.0, 0.0, 10.0, 10.0);
let rect2 = Rect::new(2.0, 2.0, 4.0, 4.0);
assert!(rect1.contains_rect(rect2));Two equal rectangles are considered to contain each other.
use kurbo::Rect;
let rect = Rect::new(0.0, 0.0, 10.0, 10.0);
assert!(rect.contains_rect(rect));Sourcepub const fn inflate(&self, width: f64, height: f64) -> Rect
pub const fn inflate(&self, width: f64, height: f64) -> Rect
Expand a rectangle by a constant amount in both directions.
The logic simply applies the amount in each direction. If rectangle area or added dimensions are negative, this could give odd results.
Sourcepub fn ceil(self) -> Rect
pub fn ceil(self) -> Rect
Returns a new Rect,
with each coordinate value rounded up to the nearest integer,
unless they are already an integer.
§Examples
use kurbo::Rect;
let rect = Rect::new(3.3, 3.6, 3.0, -3.1).ceil();
assert_eq!(rect.x0, 4.0);
assert_eq!(rect.y0, 4.0);
assert_eq!(rect.x1, 3.0);
assert_eq!(rect.y1, -3.0);Sourcepub fn floor(self) -> Rect
pub fn floor(self) -> Rect
Returns a new Rect,
with each coordinate value rounded down to the nearest integer,
unless they are already an integer.
§Examples
use kurbo::Rect;
let rect = Rect::new(3.3, 3.6, 3.0, -3.1).floor();
assert_eq!(rect.x0, 3.0);
assert_eq!(rect.y0, 3.0);
assert_eq!(rect.x1, 3.0);
assert_eq!(rect.y1, -4.0);Sourcepub fn expand(self) -> Rect
pub fn expand(self) -> Rect
Returns a new Rect,
with each coordinate value rounded away from the center of the Rect
to the nearest integer, unless they are already an integer.
That is to say this function will return the smallest possible Rect
with integer coordinates that is a superset of self.
§Examples
use kurbo::Rect;
// In positive space
let rect = Rect::new(3.3, 3.6, 5.6, 4.1).expand();
assert_eq!(rect.x0, 3.0);
assert_eq!(rect.y0, 3.0);
assert_eq!(rect.x1, 6.0);
assert_eq!(rect.y1, 5.0);
// In both positive and negative space
let rect = Rect::new(-3.3, -3.6, 5.6, 4.1).expand();
assert_eq!(rect.x0, -4.0);
assert_eq!(rect.y0, -4.0);
assert_eq!(rect.x1, 6.0);
assert_eq!(rect.y1, 5.0);
// In negative space
let rect = Rect::new(-5.6, -4.1, -3.3, -3.6).expand();
assert_eq!(rect.x0, -6.0);
assert_eq!(rect.y0, -5.0);
assert_eq!(rect.x1, -3.0);
assert_eq!(rect.y1, -3.0);
// Inverse orientation
let rect = Rect::new(5.6, -3.6, 3.3, -4.1).expand();
assert_eq!(rect.x0, 6.0);
assert_eq!(rect.y0, -3.0);
assert_eq!(rect.x1, 3.0);
assert_eq!(rect.y1, -5.0);Sourcepub fn trunc(self) -> Rect
pub fn trunc(self) -> Rect
Returns a new Rect,
with each coordinate value rounded towards the center of the Rect
to the nearest integer, unless they are already an integer.
That is to say this function will return the biggest possible Rect
with integer coordinates that is a subset of self.
§Examples
use kurbo::Rect;
// In positive space
let rect = Rect::new(3.3, 3.6, 5.6, 4.1).trunc();
assert_eq!(rect.x0, 4.0);
assert_eq!(rect.y0, 4.0);
assert_eq!(rect.x1, 5.0);
assert_eq!(rect.y1, 4.0);
// In both positive and negative space
let rect = Rect::new(-3.3, -3.6, 5.6, 4.1).trunc();
assert_eq!(rect.x0, -3.0);
assert_eq!(rect.y0, -3.0);
assert_eq!(rect.x1, 5.0);
assert_eq!(rect.y1, 4.0);
// In negative space
let rect = Rect::new(-5.6, -4.1, -3.3, -3.6).trunc();
assert_eq!(rect.x0, -5.0);
assert_eq!(rect.y0, -4.0);
assert_eq!(rect.x1, -4.0);
assert_eq!(rect.y1, -4.0);
// Inverse orientation
let rect = Rect::new(5.6, -3.6, 3.3, -4.1).trunc();
assert_eq!(rect.x0, 5.0);
assert_eq!(rect.y0, -4.0);
assert_eq!(rect.x1, 4.0);
assert_eq!(rect.y1, -4.0);Sourcepub const fn scale_from_origin(self, factor: f64) -> Rect
pub const fn scale_from_origin(self, factor: f64) -> Rect
Scales the Rect by factor with respect to the origin (the point (0, 0)).
§Examples
use kurbo::Rect;
let rect = Rect::new(2., 2., 4., 6.).scale_from_origin(2.);
assert_eq!(rect.x0, 4.);
assert_eq!(rect.x1, 8.);Sourcepub fn to_rounded_rect(self, radii: impl Into<RoundedRectRadii>) -> RoundedRect
pub fn to_rounded_rect(self, radii: impl Into<RoundedRectRadii>) -> RoundedRect
Creates a new RoundedRect from this Rect and the provided
corner radius.
Sourcepub fn to_ellipse(self) -> Ellipse
pub fn to_ellipse(self) -> Ellipse
Returns the Ellipse that is bounded by this Rect.
Sourcepub const fn aspect_ratio_width(self) -> f64
pub const fn aspect_ratio_width(self) -> f64
The aspect ratio of this Rect.
This is defined as the width divided by the height. It measures the
“squareness” of the rectangle (a value of 1 is square).
If the height is 0, the output will be sign(self.width) * infinity.
If the width and height are both 0, then the output will be NaN.
Sourcepub fn aspect_ratio(&self) -> f64
👎Deprecated since 0.12.0: You should use aspect_ratio_width instead, as this method returns a potentially unexpected value.
pub fn aspect_ratio(&self) -> f64
You should use aspect_ratio_width instead, as this method returns a potentially unexpected value.
The inverse of the aspect ratio of this Rect.
Aspect ratios are usually defined as the ratio of the width to the height, but
this method incorrectly returns the ratio of height to width.
You should generally prefer aspect_ratio_width.
If the width is 0 the output will be sign(y1 - y0) * infinity.
If the width and height are both 0, the result will be NaN.
Sourcepub const fn inscribed_rect_with_aspect_ratio(&self, aspect_ratio: f64) -> Rect
pub const fn inscribed_rect_with_aspect_ratio(&self, aspect_ratio: f64) -> Rect
Returns the largest possible Rect with the given aspect_ratio
that is fully contained in self.
The aspect ratio is specified fractionally, as width / height.
The resulting rectangle will be centered if it is smaller than this rectangle.
§Examples
let outer = Rect::new(0.0, 0.0, 10.0, 20.0);
let inner = outer.inscribed_rect_with_aspect_ratio(1.0);
// The new `Rect` is a square centered at the center of `outer`.
assert_eq!(inner, Rect::new(0.0, 5.0, 10.0, 15.0));Sourcepub fn contained_rect_with_aspect_ratio(
&self,
inverse_aspect_ratio: f64,
) -> Rect
👎Deprecated since 0.12.0: You should use inscribed_rect_with_aspect_ratio instead, as this method expects an unusually defined parameter.
pub fn contained_rect_with_aspect_ratio( &self, inverse_aspect_ratio: f64, ) -> Rect
You should use inscribed_rect_with_aspect_ratio instead, as this method expects an unusually defined parameter.
Returns the largest possible Rect with the given inverse_aspect_ratio
that is fully contained in self.
Aspect ratios are usually defined as the ratio of the width to the height, but
this method accepts an aspect ratio specified fractionally as height / width.
You should generally prefer
inscribed_rect_with_aspect_ratio, which
takes a “normal” aspect ratio.
The resulting rectangle will be centered if it is smaller than this rectangle.
Sourcepub const fn get_coords(self, axis: Axis) -> (f64, f64)
pub const fn get_coords(self, axis: Axis) -> (f64, f64)
Get the members matching the given axis.
Sourcepub const fn get_coords_mut(&mut self, axis: Axis) -> (&mut f64, &mut f64)
pub const fn get_coords_mut(&mut self, axis: Axis) -> (&mut f64, &mut f64)
Get a mutable reference to the members matching the given axis.
Sourcepub const fn set_coords(&mut self, axis: Axis, v0: f64, v1: f64)
pub const fn set_coords(&mut self, axis: Axis, v0: f64, v1: f64)
Set the members matching the given axis to the given values.
Trait Implementations§
impl Copy for Rect
Source§impl RectExt for Rect
impl RectExt for Rect
Source§fn snap_to_tile_coordinates(self) -> Rect
fn snap_to_tile_coordinates(self) -> Rect
Source§impl Shape for Rect
impl Shape for Rect
Source§fn winding(&self, pt: Point) -> i32
fn winding(&self, pt: Point) -> i32
Note: this function is carefully designed so that if the plane is tiled with rectangles, the winding number will be nonzero for exactly one of them.
Source§type PathElementsIter<'iter> = RectPathIter
type PathElementsIter<'iter> = RectPathIter
path_elements method.Source§fn path_elements(&self, _tolerance: f64) -> RectPathIter
fn path_elements(&self, _tolerance: f64) -> RectPathIter
Source§fn bounding_box(&self) -> Rect
fn bounding_box(&self) -> Rect
Source§fn into_path(self, tolerance: f64) -> BezPathwhere
Self: Sized,
fn into_path(self, tolerance: f64) -> BezPathwhere
Self: Sized,
Source§fn path_segments(&self, tolerance: f64) -> Segments<Self::PathElementsIter<'_>> ⓘ
fn path_segments(&self, tolerance: f64) -> Segments<Self::PathElementsIter<'_>> ⓘ
Source§fn as_rounded_rect(&self) -> Option<RoundedRect>
fn as_rounded_rect(&self) -> Option<RoundedRect>
impl StructuralPartialEq for Rect
Auto Trait Implementations§
impl Freeze for Rect
impl RefUnwindSafe for Rect
impl Send for Rect
impl Sync for Rect
impl Unpin for Rect
impl UnsafeUnpin for Rect
impl UnwindSafe for Rect
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<T> Brush for T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.