this repo has no description
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

:lipstick: Make title source code prettier

+293 -231
+17 -45
paper/colorshed.svg
··· 1 1 <svg height="150" viewBox="0 0 150 150" width="150" xmlns="http://www.w3.org/2000/svg"> 2 - <rect fill="black" height="150" width="150" x="0" y="0"/> 2 + <rect fill="white" height="150" width="150" x="0" y="0"/> 3 3 <g class="layer" data-layer="root"> 4 - <g data-object="--orange_bg" style="fill: red;transform-box: fill-box;" transform-origin="25 75"> 5 - <rect height="50" width="50" x="0" y="50"/> 4 + <g data-object="--375667707799164755" style="fill: yellow;transform-box: fill-box;" transform-origin="125 25"> 5 + <rect height="50" width="50" x="100" y="0"/> 6 6 </g> 7 - <g data-object="--cyan_bg" style="fill: yellow;transform-box: fill-box;" transform-origin="75 25"> 8 - <rect height="50" width="50" x="50" y="0"/> 7 + <g data-object="--15614467315524568680" style="fill: blue;transform-box: fill-box;" transform-origin="25 25"> 8 + <rect height="50" width="50" x="0" y="0"/> 9 9 </g> 10 - <g data-object="--red_bg" style="fill: brown;transform-box: fill-box;" transform-origin="75 75"> 10 + <g data-object="--13587582764877368320" style="fill: red;transform-box: fill-box;" transform-origin="75 75"> 11 11 <rect height="50" width="50" x="50" y="50"/> 12 12 </g> 13 - <g data-object="--brown_bg" style="fill: purple;transform-box: fill-box;" transform-origin="125 75"> 14 - <rect height="50" width="50" x="100" y="50"/> 15 - </g> 16 - <g data-object="--yellow_bg" style="fill: orange;transform-box: fill-box;" transform-origin="125 25"> 17 - <rect height="50" width="50" x="100" y="0"/> 18 - </g> 19 - <g data-object="--purple_bg" style="fill: pink;transform-box: fill-box;" transform-origin="25 125"> 13 + <g data-object="--13518301373768647850" style="fill: purple;transform-box: fill-box;" transform-origin="25 125"> 20 14 <rect height="50" width="50" x="0" y="100"/> 21 15 </g> 22 - <g data-object="--pink_bg" style="fill: green;transform-box: fill-box;" transform-origin="75 125"> 23 - <rect height="50" width="50" x="50" y="100"/> 24 - </g> 25 - <g data-object="--green_bg" style="fill: blue;transform-box: fill-box;" transform-origin="125 125"> 16 + <g data-object="--16546396387867059567" style="fill: green;transform-box: fill-box;" transform-origin="125 125"> 26 17 <rect height="50" width="50" x="100" y="100"/> 27 18 </g> 28 - <g data-object="--blue_bg" style="fill: cyan;transform-box: fill-box;" transform-origin="25 25"> 29 - <rect height="50" width="50" x="0" y="0"/> 30 - </g> 31 - </g> 32 - <g class="layer" data-layer="circles"> 33 - <g data-object="--pink" style="fill: pink;transform-box: fill-box;" transform-origin="75 125"> 34 - <circle cx="75" cy="125" r="25"/> 19 + <g data-object="--3700631692136421255" style="fill: cyan;transform-box: fill-box;" transform-origin="75 25"> 20 + <rect height="50" width="50" x="50" y="0"/> 35 21 </g> 36 - <g data-object="--green" style="fill: green;transform-box: fill-box;" transform-origin="125 125"> 37 - <circle cx="125" cy="125" r="25"/> 22 + <g data-object="--6684871025456728689" style="fill: orange;transform-box: fill-box;" transform-origin="25 75"> 23 + <rect height="50" width="50" x="0" y="50"/> 38 24 </g> 39 - <g data-object="--orange" style="fill: orange;transform-box: fill-box;" transform-origin="25 75"> 40 - <circle cx="25" cy="75" r="25"/> 25 + <g data-object="--28759431767939479" style="fill: brown;transform-box: fill-box;" transform-origin="125 75"> 26 + <rect height="50" width="50" x="100" y="50"/> 41 27 </g> 42 - <g data-object="--red" style="fill: red;transform-box: fill-box;" transform-origin="75 75"> 43 - <circle cx="75" cy="75" r="25"/> 28 + <g data-object="--3231555540301764813" style="fill: pink;transform-box: fill-box;" transform-origin="75 125"> 29 + <rect height="50" width="50" x="50" y="100"/> 44 30 </g> 45 - <g data-object="--cyan" style="fill: cyan;transform-box: fill-box;" transform-origin="75 25"> 46 - <circle cx="75" cy="25" r="25"/> 47 - </g> 48 - <g data-object="--brown" style="fill: brown;transform-box: fill-box;" transform-origin="125 75"> 49 - <circle cx="125" cy="75" r="25"/> 50 31 </g> 51 - <g data-object="--blue" style="fill: blue;transform-box: fill-box;" transform-origin="25 25"> 52 - <circle cx="25" cy="25" r="25"/> 53 - </g> 54 - <g data-object="--yellow" style="fill: yellow;transform-box: fill-box;" transform-origin="125 25"> 55 - <circle cx="125" cy="25" r="25"/> 56 - </g> 57 - <g data-object="--purple" style="fill: purple;transform-box: fill-box;" transform-origin="25 125"> 58 - <circle cx="25" cy="125" r="25"/> 59 - </g> 60 - </g> 32 + <g class="layer" data-layer="circles"/> 61 33 <defs/> 62 34 </svg>
paper/dna-analysis-machine.png

This is a binary file and will not be displayed.

+17 -17
paper/grid.svg
··· 1 1 <svg height="170" viewBox="-10 -10 170 170" width="170" xmlns="http://www.w3.org/2000/svg"> 2 2 <rect fill="white" height="170" width="170" x="-10" y="-10"/> 3 3 <g class="layer" data-layer="root"> 4 - <g data-object="--(2, 1)" style="fill: black;transform-box: fill-box;" transform-origin="125 75"> 4 + <g data-object="--8044335407824616032" style="fill: black;transform-box: fill-box;" transform-origin="75 25"> 5 + <circle cx="50" cy="0" r="2"/> 6 + </g> 7 + <g data-object="--9123305004219188859" style="fill: black;transform-box: fill-box;" transform-origin="125 25"> 8 + <circle cx="100" cy="0" r="2"/> 9 + </g> 10 + <g data-object="--3033572172195199028" style="fill: black;transform-box: fill-box;" transform-origin="125 75"> 5 11 <circle cx="100" cy="50" r="2"/> 6 12 </g> 7 - <g data-object="--(0, 2)" style="fill: black;transform-box: fill-box;" transform-origin="25 125"> 13 + <g data-object="--16830775558836329687" style="fill: black;transform-box: fill-box;" transform-origin="25 125"> 8 14 <circle cx="0" cy="100" r="2"/> 9 15 </g> 10 - <g data-object="--(0, 0)" style="fill: black;transform-box: fill-box;" transform-origin="25 25"> 16 + <g data-object="--7389065806926974449" style="fill: black;transform-box: fill-box;" transform-origin="125 125"> 17 + <circle cx="100" cy="100" r="2"/> 18 + </g> 19 + <g data-object="--8186870064885101362" style="fill: black;transform-box: fill-box;" transform-origin="75 75"> 20 + <circle cx="50" cy="50" r="2"/> 21 + </g> 22 + <g data-object="--384886948574484973" style="fill: black;transform-box: fill-box;" transform-origin="25 25"> 11 23 <circle cx="0" cy="0" r="2"/> 12 24 </g> 13 - <g data-object="--(0, 1)" style="fill: black;transform-box: fill-box;" transform-origin="25 75"> 25 + <g data-object="--5611790767321655154" style="fill: black;transform-box: fill-box;" transform-origin="25 75"> 14 26 <circle cx="0" cy="50" r="2"/> 15 27 </g> 16 - <g data-object="--(2, 0)" style="fill: black;transform-box: fill-box;" transform-origin="125 25"> 17 - <circle cx="100" cy="0" r="2"/> 18 - </g> 19 - <g data-object="--(1, 2)" style="fill: black;transform-box: fill-box;" transform-origin="75 125"> 28 + <g data-object="--6576106397029605359" style="fill: black;transform-box: fill-box;" transform-origin="75 125"> 20 29 <circle cx="50" cy="100" r="2"/> 21 - </g> 22 - <g data-object="--(1, 0)" style="fill: black;transform-box: fill-box;" transform-origin="75 25"> 23 - <circle cx="50" cy="0" r="2"/> 24 - </g> 25 - <g data-object="--(2, 2)" style="fill: black;transform-box: fill-box;" transform-origin="125 125"> 26 - <circle cx="100" cy="100" r="2"/> 27 - </g> 28 - <g data-object="--(1, 1)" style="fill: black;transform-box: fill-box;" transform-origin="75 75"> 29 - <circle cx="50" cy="50" r="2"/> 30 30 </g> 31 31 </g> 32 32 <defs/>
paper/main.pdf

This is a binary file and will not be displayed.

+24 -10
paper/main.typ
··· 82 82 83 83 #pagebreak() 84 84 85 - #align(center, pad(y: 1.7em, image("./dna-analysis-machine.png", width: 100%))) 85 + #align(center, pad(y: 30%, image("./dna-analysis-machine.png", width: 100%))) 86 + 87 + #pagebreak() 86 88 87 - #include-function( 88 - "../src/examples.rs", 89 - "dna_analysis_machine", 90 - lang: "rust", 91 - transform: it => "use shapemaker::*\n\n" + it, 89 + #text( 90 + size: 0.88em, 91 + include-function( 92 + "../src/examples.rs", 93 + "dna_analysis_machine", 94 + lang: "rust", 95 + transform: it => "use shapemaker::*\n\n" + it, 96 + ), 92 97 ) 93 98 94 99 #pagebreak() ··· 196 201 J'ai donc laissé le public trouver ces œuvres, cachées à travers la ville, dans l'esprit des fameux _Spaces Invaders_ de Paris @spaceinvadersparis (qui d'ailleurs étendent leur colonisation bien au-delà de Paris, allant même jusqu'à l'ISS @spaceinvadersiss). 197 202 198 203 199 - #let work = (slug, caption, with-context: false, only-context: false, screenshot: true) => figure( 204 + #let work = ( 205 + slug, 206 + caption, 207 + with-context: false, 208 + only-context: false, 209 + screenshot: true, 210 + ) => figure( 200 211 caption: caption, 201 212 grid( 202 213 gutter: 0.5em, ··· 387 398 ], 388 399 codesnippet( 389 400 lang: "rust", 390 - size: 0.9em, 401 + size: 0.87em, 391 402 cut-around( 392 403 it => it.trim().starts-with("pub struct ObjectSizes"), 393 404 it => it == "}", ··· 1175 1186 #grid( 1176 1187 columns: (1fr, 1fr), 1177 1188 imagefigure("./hwccorrect.png", [Frame cible correcte]), 1178 - imagefigure("./hwcwrong.png", [Erreur dans le calcul des coordonnées des pixels: inversion de `%` et `/`]), 1189 + imagefigure( 1190 + "./hwcwrong.png", 1191 + [Erreur dans le calcul des coordonnées des pixels: inversion de `%` et `/`], 1192 + ), 1179 1193 ) 1180 1194 1181 - ==== Aller plus loin 1195 + === Aller plus loin 1182 1196 1183 1197 L'opération reste de loin la plus coûteuse de la chaîne de rendu. 1184 1198
+16 -16
paper/shapeshed.svg
··· 1 1 <svg height="170" viewBox="-10 -10 170 170" width="170" xmlns="http://www.w3.org/2000/svg"> 2 2 <rect fill="white" height="170" width="170" x="-10" y="-10"/> 3 3 <g class="layer" data-layer="root"> 4 - <g data-object="--6" style="fill: black;transform-box: fill-box;" transform-origin="150 100"> 5 - <path d="M100,50 L150,50 L150,100 z"/> 6 - </g> 7 - <g data-object="--7" style="fill: black;transform-box: fill-box;" transform-origin="25 125"> 8 - <rect height="50" width="50" x="0" y="100"/> 9 - </g> 10 - <g data-object="--3" style="stroke: black; fill: transparent;transform-box: fill-box;" transform-origin="150 50"> 11 - <path d="M100,50 Q100,0,150,0" stroke-width="5"/> 12 - </g> 13 - <g data-object="--8" style="fill: black;transform-box: fill-box;" transform-origin="125 175"> 4 + <g data-object="--5593412285880459161" style="fill: black;transform-box: fill-box;" transform-origin="125 175"> 14 5 <circle cx="100" cy="150" r="2"/> 15 6 </g> 16 - <g data-object="--2" style="stroke: black; fill: transparent;transform-box: fill-box;" transform-origin="100 50"> 7 + <g data-object="--6218439278630071607" style="fill: black;transform-box: fill-box;" transform-origin="25 75"> 8 + <circle cx="0" cy="50" r="5"/> 9 + </g> 10 + <g data-object="--16989226860394001755" style="stroke: black; fill: transparent;transform-box: fill-box;" transform-origin="100 50"> 17 11 <path d="M50,50 Q100,50,100,0" stroke-width="5"/> 18 12 </g> 19 - <g data-object="--5" style="stroke: black; fill: transparent;transform-box: fill-box;" transform-origin="100 100"> 13 + <g data-object="--11161750374348414010" style="fill: black;transform-box: fill-box;" transform-origin="25 25"> 14 + <circle cx="25" cy="25" r="25"/> 15 + </g> 16 + <g data-object="--7259427182471079587" style="stroke: black; fill: transparent;transform-box: fill-box;" transform-origin="100 100"> 20 17 <line stroke-width="5" x1="50" x2="100" y1="50" y2="100"/> 21 18 </g> 22 - <g data-object="--1" style="fill: black;transform-box: fill-box;" transform-origin="25 25"> 23 - <circle cx="25" cy="25" r="25"/> 19 + <g data-object="--2510629377858964867" style="fill: black;transform-box: fill-box;" transform-origin="150 100"> 20 + <path d="M100,50 L150,50 L150,100 z"/> 24 21 </g> 25 - <g data-object="--4" style="fill: black;transform-box: fill-box;" transform-origin="25 75"> 26 - <circle cx="0" cy="50" r="5"/> 22 + <g data-object="--12237004016842981411" style="stroke: black; fill: transparent;transform-box: fill-box;" transform-origin="150 50"> 23 + <path d="M100,50 Q100,0,150,0" stroke-width="5"/> 24 + </g> 25 + <g data-object="--6894602682874051675" style="fill: black;transform-box: fill-box;" transform-origin="25 125"> 26 + <rect height="50" width="50" x="0" y="100"/> 27 27 </g> 28 28 </g> 29 29 <defs/>
+5 -1
paper/template.typ
··· 97 97 [], 98 98 [*#author.name*], 99 99 [ 100 - #pad(left: 4pt, top: -4pt, image("orcid.svg", width: 8pt)) 100 + #pad( 101 + left: 4pt, 102 + top: -4pt, 103 + image("orcid.svg", width: 8pt), 104 + ) 101 105 ], 102 106 ), 103 107 )
+1 -1
rustfmt.toml
··· 1 - max_width = 80 1 + max_width = 82
+54 -89
src/examples.rs
··· 1 - use std::iter; 2 - 3 1 use crate::*; 4 - use rand::Rng; 2 + use std::iter; 5 3 6 4 pub fn shapes_shed() -> Canvas { 7 5 let mut canvas = Canvas::new(vec![]); ··· 9 7 canvas.set_grid_size(3, 3); 10 8 canvas.set_background(Color::White); 11 9 12 - let root = canvas.layer("root"); 13 - 14 - root.add_object("1", Object::BigCircle(Point(0, 0)).color(Color::Black)); 15 - root.add_object( 16 - "2", 17 - Object::CurveOutward(Point(1, 1), Point(2, 0), 5.0).color(Color::Black), 18 - ); 19 - root.add_object( 20 - "3", 21 - Object::CurveInward(Point(2, 1), Point(3, 0), 5.0).color(Color::Black), 22 - ); 23 - root.add_object("4", Object::SmallCircle(Point(0, 1)).color(Color::Black)); 24 - root.add_object( 25 - "5", 26 - Object::Line(Point(1, 1), Point(2, 2), 5.0).color(Color::Black), 27 - ); 28 - root.add_object( 29 - "6", 10 + canvas.layer("root").add_objects([ 11 + Object::BigCircle(Point(0, 0)).colored(Color::Black), 12 + Object::CurveOutward(Point(1, 1), Point(2, 0), 5.0).colored(Color::Black), 13 + Object::CurveInward(Point(2, 1), Point(3, 0), 5.0).colored(Color::Black), 14 + Object::SmallCircle(Point(0, 1)).colored(Color::Black), 15 + Object::Line(Point(1, 1), Point(2, 2), 5.0).colored(Color::Black), 16 + Object::Rectangle(Point(0, 2), Point(0, 2)).colored(Color::Black), 17 + Object::Dot(Point(2, 3)).colored(Color::Black), 30 18 Object::Polygon( 31 19 Point(2, 1), 32 20 vec![ ··· 34 22 LineSegment::Straight(Point(3, 2)), 35 23 ], 36 24 ) 37 - .color(Color::Black), 38 - ); 39 - root.add_object( 40 - "7", 41 - Object::Rectangle(Point(0, 2), Point(0, 2)).color(Color::Black), 42 - ); 43 - root.add_object("8", Object::Dot(Point(2, 3)).color(Color::Black)); 25 + .colored(Color::Black), 26 + ]); 44 27 45 28 canvas 46 29 } ··· 49 32 let mut canvas = Canvas::new(vec!["circles"]); 50 33 canvas.set_grid_size(3, 3); 51 34 canvas.canvas_outter_padding = 0; 35 + canvas.set_background(Color::White); 52 36 53 37 let all_colors = vec![ 54 38 Color::Blue, ··· 62 46 Color::Green, 63 47 ]; 64 48 65 - let foregrounds = all_colors.iter(); 66 - let backgrounds = all_colors.iter().cycle().skip(1); 67 - let colors = iter::zip(foregrounds, backgrounds); 68 - 69 - for ((color, bgcolor), point) in 70 - iter::zip(colors, canvas.world_region.iter()) 71 - { 72 - println!("{}: {:?} {:?}", point, color, bgcolor); 49 + for (color, point) in iter::zip(all_colors, canvas.world_region) { 73 50 canvas 74 - .layer("circles") 75 - .add_object(color.name(), Object::BigCircle(point).color(*color)); 76 - canvas.layer("root").add_object( 77 - format!("{}_bg", color.name()), 78 - Object::Rectangle(point, point).color(*bgcolor), 79 - ); 51 + .root() 52 + .add(Object::Rectangle(point, point).colored(color)); 80 53 } 81 54 82 55 canvas ··· 86 59 let mut canvas = Canvas::new(vec![]); 87 60 canvas.set_grid_size(3, 3); 88 61 canvas.set_background(Color::White); 89 - for point in canvas.world_region.iter() { 90 - canvas.root().add_object( 91 - point.to_string(), 92 - Object::Dot(point).color(Color::Black), 93 - ); 62 + 63 + for point in canvas.world_region { 64 + canvas.root().add(Object::Dot(point).colored(Color::Black)); 94 65 } 66 + 95 67 canvas 96 68 } 97 69 ··· 116 88 117 89 let draw_in = canvas.world_region.resized(-2, -2); 118 90 119 - let filaments_area = 91 + // Strands 92 + 93 + let strands_in = 120 94 Region::from_bottomleft(draw_in.bottomleft().translated(2, -1), (3, 3)) 121 95 .unwrap(); 122 96 123 - let red_circle_at = 97 + canvas.add_layer(canvas.n_random_curves_within(&strands_in, 30, "strands")); 98 + 99 + for (i, obj) in canvas.layer("strands").objects.values_mut().enumerate() { 100 + obj.recolor(if i % 2 == 0 { Color::Cyan } else { Color::Pink }); 101 + obj.filter(Filter::glow(4.0)); 102 + } 103 + 104 + // Red dot 105 + 106 + let red_dot = Object::BigCircle( 124 107 Region::from_topright(draw_in.topright().translated(-3, 0), (4, 3)) 125 108 .unwrap() 126 - .random_point(); 109 + .random_point(), 110 + ) 111 + .colored(Color::Red) 112 + .filtered(Filter::glow(5.0)); 127 113 128 - let mut hatches_layer = Layer::new("hatches"); 129 - let mut red_dot_layer = Layer::new("red dot"); 114 + canvas.new_layer("red dot").add(red_dot.clone()); 130 115 131 - for (i, point) in draw_in.iter().enumerate() { 132 - if filaments_area.contains(&point) { 116 + // Hatched circles & squares 117 + 118 + let hatches = canvas.new_layer("hatches"); 119 + 120 + for (i, point) in draw_in.except(&strands_in).enumerate() { 121 + if red_dot.region().contains(&point) { 133 122 continue; 134 123 } 135 - 136 - if point == red_circle_at { 137 - red_dot_layer.add_object( 138 - format!("red circle @ {}", point), 139 - Object::BigCircle(point) 140 - .color(Color::Red) 141 - .filter(Filter::glow(5.0)), 142 - ); 124 + if rand::random() { 125 + Object::BigCircle(point) 126 + } else { 127 + Object::Rectangle(point, point) 143 128 } 144 - 145 - hatches_layer.add_object( 146 - point, 147 - if rand::thread_rng().gen_bool(0.5) || point == red_circle_at { 148 - Object::BigCircle(point) 149 - } else { 150 - Object::Rectangle(point, point) 151 - } 152 - .paint(Fill::Hatched( 153 - Color::White, 154 - Angle(45.0), 155 - (i + 5) as f32 / 10.0, 156 - 0.25, 157 - )), 158 - ); 129 + .filled(Fill::Hatches( 130 + Color::White, 131 + Angle(45.0), 132 + (i + 5) as f32 / 10.0, 133 + 0.25, 134 + )) 135 + .add_to(hatches); 159 136 } 160 137 161 - let mut filaments = 162 - canvas.n_random_curves_within(&filaments_area, 30, "splines"); 163 - 164 - for (i, object) in filaments.objects.values_mut().enumerate() { 165 - object.recolor(if i % 2 == 0 { Color::Cyan } else { Color::Pink }); 166 - } 167 - 168 - filaments.filter_all_objects(Filter::glow(4.0)); 169 - 170 - canvas.layers.push(red_dot_layer); 171 - canvas.layers.push(hatches_layer); 172 - canvas.layers.push(filaments); 173 138 canvas 174 139 }
+50 -5
src/geometry/region.rs
··· 17 17 self.into() 18 18 } 19 19 20 + /// Iterates all points except the ones specified in the `except` region 21 + pub fn except<'a>( 22 + &self, 23 + except: &'a Region, 24 + ) -> impl Iterator<Item = Point> + use<'a> { 25 + self.iter().filter(move |p| !except.contains(p)) 26 + } 27 + 20 28 pub fn iter_lower_triangle(&self) -> impl Iterator<Item = Point> { 21 29 self.iter().filter(|Point(x, y)| x < y) 22 30 } ··· 43 51 current: Point, 44 52 } 45 53 54 + impl IntoIterator for Region { 55 + type Item = Point; 56 + type IntoIter = RegionIterator; 57 + 58 + fn into_iter(self) -> Self::IntoIter { 59 + self.iter() 60 + } 61 + } 62 + 46 63 impl Iterator for RegionIterator { 47 64 type Item = Point; 48 65 ··· 117 134 } 118 135 119 136 impl Region { 120 - pub fn new(start_x: usize, start_y: usize, end_x: usize, end_y: usize) -> Result<Self, Error> { 137 + pub fn new( 138 + start_x: usize, 139 + start_y: usize, 140 + end_x: usize, 141 + end_y: usize, 142 + ) -> Result<Self, Error> { 121 143 let region = Self { 122 144 start: (start_x, start_y).into(), 123 145 end: (end_x, end_y).into(), ··· 160 182 } 161 183 } 162 184 185 + pub fn merge<'a>(&'a self, other: &'a Region) -> Region { 186 + Region { 187 + start: Point( 188 + self.start.0.min(other.start.0), 189 + self.start.1.min(other.start.1), 190 + ), 191 + end: Point( 192 + self.end.0.max(other.end.0), 193 + self.end.1.max(other.end.1), 194 + ), 195 + } 196 + } 197 + 163 198 pub fn from_origin(end: Point) -> Result<Self> { 164 199 Self::new(0, 0, end.0, end.1) 165 200 } ··· 171 206 ) 172 207 } 173 208 174 - pub fn from_bottomleft(origin: Point, size: (usize, usize)) -> Result<Self> { 209 + pub fn from_bottomleft( 210 + origin: Point, 211 + size: (usize, usize), 212 + ) -> Result<Self> { 175 213 Self::from_topleft(origin.translated(0, -(size.1 as i32 - 1)), size) 176 214 } 177 215 178 - pub fn from_bottomright(origin: Point, size: (usize, usize)) -> Result<Self> { 216 + pub fn from_bottomright( 217 + origin: Point, 218 + size: (usize, usize), 219 + ) -> Result<Self> { 179 220 Self::from_points( 180 221 origin.translated_by(Point::from(size).translated(-1, -1)), 181 222 origin, ··· 186 227 Self::from_topleft(origin.translated(-(size.0 as i32 - 1), 0), size) 187 228 } 188 229 189 - pub fn from_center_and_size(center: Point, size: (usize, usize)) -> Result<Self> { 230 + pub fn from_center_and_size( 231 + center: Point, 232 + size: (usize, usize), 233 + ) -> Result<Self> { 190 234 let half_size = (size.0 / 2, size.1 / 2); 191 235 Self::new( 192 236 center.0 - half_size.0, ··· 282 326 self.start.1.max(within.start.1), 283 327 ) 284 328 .into(), 285 - end: (self.end.0.min(within.end.0), self.end.1.min(within.end.1)).into(), 329 + end: (self.end.0.min(within.end.0), self.end.1.min(within.end.1)) 330 + .into(), 286 331 } 287 332 } 288 333
+20 -14
src/graphics/canvas.rs
··· 31 31 pub world_region: Region, 32 32 33 33 /// Render cache for the SVG string. Prevents having to re-calculate a pixmap when the SVG hasn't changed. 34 - pub(crate) png_render_cache: Option<String>, 35 34 pub(crate) fontdb: Option<Arc<usvg::fontdb::Database>>, 36 35 } 37 36 ··· 93 92 94 93 self.layers.push(Layer::new(name)); 95 94 self.layers.last_mut().unwrap() 95 + } 96 + 97 + pub fn add_layer(&mut self, layer: Layer) { 98 + if self.layer_exists(&layer.name) { 99 + panic!("Layer {} already exists", layer.name); 100 + } 101 + 102 + self.layers.push(layer); 96 103 } 97 104 98 105 pub fn layer_or_empty(&mut self, name: &str) -> &mut Layer { ··· 172 179 match self.layer_safe(layer) { 173 180 None => Err(format!("Layer {} does not exist", layer)), 174 181 Some(layer) => { 175 - layer.add_object(name, ColoredObject::from((object, fill))); 182 + layer.set_object(name, ColoredObject::from((object, fill))); 176 183 Ok(()) 177 184 } 178 185 } ··· 205 212 layers: vec![], 206 213 world_region: Region::new(0, 0, 3, 3).unwrap(), 207 214 background: None, 208 - png_render_cache: None, 209 215 fontdb: None, 210 216 } 211 217 } ··· 291 297 self.layers 292 298 .iter() 293 299 .flat_map(|layer| layer.objects.iter().flat_map(|(_, o)| o.fill)) 294 - .filter(|fill| matches!(fill, Fill::Hatched(..) | Fill::Dotted(..))) 300 + .filter(|fill| matches!(fill, Fill::Hatches(..) | Fill::Dotted(..))) 295 301 .unique_by(|fill| fill.pattern_id()) 296 302 .collect() 297 303 } ··· 299 305 pub fn debug_region(&mut self, region: &Region, color: Color) { 300 306 let layer = self.layer_or_empty("debug plane"); 301 307 302 - layer.add_object( 308 + layer.set_object( 303 309 format!("{}_corner_ss", region).as_str(), 304 - Object::Dot(region.topleft()).color(color), 310 + Object::Dot(region.topleft()).colored(color), 305 311 ); 306 - layer.add_object( 312 + layer.set_object( 307 313 format!("{}_corner_se", region).as_str(), 308 - Object::Dot(region.topright().translated(1, 0)).color(color), 314 + Object::Dot(region.topright().translated(1, 0)).colored(color), 309 315 ); 310 - layer.add_object( 316 + layer.set_object( 311 317 format!("{}_corner_ne", region).as_str(), 312 - Object::Dot(region.bottomright().translated(1, 1)).color(color), 318 + Object::Dot(region.bottomright().translated(1, 1)).colored(color), 313 319 ); 314 - layer.add_object( 320 + layer.set_object( 315 321 format!("{}_corner_nw", region).as_str(), 316 - Object::Dot(region.bottomleft().translated(0, 1)).color(color), 322 + Object::Dot(region.bottomleft().translated(0, 1)).colored(color), 317 323 ); 318 - layer.add_object( 324 + layer.set_object( 319 325 format!("{}_region", region).as_str(), 320 326 Object::Rectangle(region.start, region.end) 321 - .paint(Fill::Translucent(color, 0.25)), 327 + .filled(Fill::Translucent(color, 0.25)), 322 328 ) 323 329 } 324 330 }
+4 -4
src/graphics/fill.rs
··· 4 4 pub enum Fill { 5 5 Solid(Color), 6 6 Translucent(Color, f32), 7 - Hatched(Color, Angle, f32, f32), 7 + Hatches(Color, Angle, f32, f32), 8 8 Dotted(Color, f32, f32), 9 9 } 10 10 ··· 25 25 } 26 26 27 27 fn bottom_up_hatches(color: Color, thickness: f32, spacing: f32) -> Self { 28 - Fill::Hatched(color, Angle(45.0), thickness, spacing) 28 + Fill::Hatches(color, Angle(45.0), thickness, spacing) 29 29 } 30 30 } 31 31 ··· 41 41 42 42 impl Fill { 43 43 pub fn pattern_id(&self) -> String { 44 - if let Fill::Hatched(color, angle, thickness, spacing) = self { 44 + if let Fill::Hatches(color, angle, thickness, spacing) = self { 45 45 return format!( 46 46 "pattern-hatched-{}-{}-{}-{}", 47 47 angle, ··· 61 61 colormapping: &ColorMapping, 62 62 ) -> Option<svg::node::element::Pattern> { 63 63 match self { 64 - Fill::Hatched(color, angle, size, thickness_ratio) => { 64 + Fill::Hatches(color, angle, size, thickness_ratio) => { 65 65 let thickness = size * (2.0 * thickness_ratio); 66 66 67 67 let pattern = svg::node::element::Pattern::new()
+49 -9
src/graphics/layer.rs
··· 59 59 } 60 60 61 61 pub fn remove_all_objects_in(&mut self, region: &Region) { 62 - self.objects 63 - .retain(|_, ColoredObject { object, .. }| !object.region().within(region)) 62 + self.objects.retain(|_, ColoredObject { object, .. }| { 63 + !object.region().within(region) 64 + }) 64 65 } 65 66 66 67 pub fn paint_all_objects(&mut self, fill: Fill) { ··· 78 79 } 79 80 80 81 pub fn move_all_objects(&mut self, dx: i32, dy: i32) { 81 - self.objects 82 - .iter_mut() 83 - .for_each(|(_, ColoredObject { object, .. })| object.translate(dx, dy)); 82 + self.objects.iter_mut().for_each( 83 + |(_, ColoredObject { object, .. })| object.translate(dx, dy), 84 + ); 84 85 self.flush(); 85 86 } 86 87 87 - pub fn add_object<N: Display>(&mut self, name: N, object: impl Into<ColoredObject>) { 88 + pub fn add_object_named<N: Display>( 89 + &mut self, 90 + name: N, 91 + object: impl Into<ColoredObject>, 92 + ) { 88 93 let name_str = format!("{}", name); 89 94 90 95 if self.objects.contains_key(&name_str) { ··· 94 99 self.set_object(name_str, object); 95 100 } 96 101 97 - pub fn set_object<N: Display>(&mut self, name: N, object: impl Into<ColoredObject>) { 102 + pub fn add(&mut self, object: impl Into<ColoredObject>) { 103 + self.add_object_named(format!("{}", rand::random::<usize>()), object); 104 + } 105 + 106 + pub fn set_object<N: Display>( 107 + &mut self, 108 + name: N, 109 + object: impl Into<ColoredObject>, 110 + ) { 98 111 let name_str = format!("{}", name); 99 112 100 113 self.objects.insert(name_str, object.into()); 101 114 self.flush(); 102 115 } 103 116 104 - pub fn filter_object(&mut self, name: &str, filter: Filter) -> Result<(), String> { 117 + pub fn filter_object( 118 + &mut self, 119 + name: &str, 120 + filter: Filter, 121 + ) -> Result<(), String> { 105 122 self.objects 106 123 .get_mut(name) 107 124 .ok_or(format!("Object '{}' not found", name))? ··· 119 136 120 137 pub fn replace_object(&mut self, name: &str, object: ColoredObject) { 121 138 self.remove_object(name); 122 - self.add_object(name, object); 139 + self.add_object_named(name, object); 140 + } 141 + 142 + pub fn add_objects( 143 + &mut self, 144 + objects: impl IntoIterator<Item = ColoredObject>, 145 + ) { 146 + for obj in objects { 147 + self.add(obj); 148 + } 149 + } 150 + 151 + /// Returns the effective region the layer occupies, by merging all its objects' regions. 152 + pub fn region(&self) -> Region { 153 + self.objects 154 + .values() 155 + .map(|object| object.region()) 156 + .fold(Region::default(), |acc, region| acc.merge(&region)) 157 + } 158 + } 159 + 160 + impl ColoredObject { 161 + pub fn add_to(self, layer: &mut Layer) { 162 + layer.add(self); 123 163 } 124 164 }
+28 -12
src/graphics/objects.rs
··· 30 30 } 31 31 32 32 impl Object { 33 - pub fn paint(self, fill: Fill) -> ColoredObject { 33 + pub fn filled(self, fill: Fill) -> ColoredObject { 34 34 ColoredObject::from((self, Some(fill))) 35 35 } 36 36 37 - pub fn color(self, color: Color) -> ColoredObject { 38 - ColoredObject::from((self, Some(Fill::Solid(color)))) 37 + pub fn colored(self, color: Color) -> ColoredObject { 38 + ColoredObject::from((self, None)).colored(color) 39 39 } 40 40 41 - pub fn filter(self, filter: Filter) -> ColoredObject { 42 - ColoredObject::from((self, None)).filter(filter) 41 + pub fn filtered(self, filter: Filter) -> ColoredObject { 42 + ColoredObject::from((self, None)).filtered(filter) 43 43 } 44 44 45 45 pub fn transform(self, transformation: Transformation) -> ColoredObject { 46 - ColoredObject::from((self, None)).transform(transformation) 46 + ColoredObject::from((self, None)).transformed(transformation) 47 47 } 48 48 } 49 49 ··· 56 56 } 57 57 58 58 impl ColoredObject { 59 - pub fn filter(mut self, filter: Filter) -> Self { 59 + pub fn filtered(mut self, filter: Filter) -> Self { 60 60 self.filters.push(filter); 61 61 self 62 62 } 63 63 64 - pub fn transform(mut self, transformation: Transformation) -> Self { 64 + pub fn transformed(mut self, transformation: Transformation) -> Self { 65 65 self.transformations.push(transformation); 66 66 self 67 67 } 68 68 69 + pub fn filled(mut self, fill: Fill) -> Self { 70 + self.fill = Some(fill); 71 + self 72 + } 73 + 74 + pub fn colored(mut self, color: Color) -> Self { 75 + self.fill = Some(Fill::Solid(color)); 76 + self 77 + } 78 + 69 79 pub fn clear_filters(&mut self) { 70 80 self.filters.clear(); 71 81 } 72 82 73 - pub fn repaint(&mut self, fill: Fill) { 83 + pub fn refill(&mut self, fill: Fill) { 74 84 self.fill = Some(fill); 75 85 } 76 86 77 87 pub fn recolor(&mut self, color: Color) { 78 88 self.fill = Some(Fill::Solid(color)) 89 + } 90 + 91 + pub fn filter(&mut self, filter: Filter) { 92 + self.filters.push(filter) 93 + } 94 + 95 + pub fn region(&self) -> Region { 96 + self.object.region() 79 97 } 80 98 } 81 99 ··· 237 255 pub fn fillable(&self) -> bool { 238 256 !matches!( 239 257 self, 240 - Object::Line(..) 241 - | Object::CurveInward(..) 242 - | Object::CurveOutward(..) 258 + Object::Line(..) | Object::CurveInward(..) | Object::CurveOutward(..) 243 259 ) 244 260 } 245 261
+4 -4
src/main.rs
··· 106 106 .each_frame(&|canvas, ctx| { 107 107 let center = canvas.world_region.center(); 108 108 canvas.root().clear(); 109 - canvas.root().add_object( 109 + canvas.root().set_object( 110 110 "text", 111 111 Object::CenteredText(center, ctx.timestamp.to_string(), 30.0) 112 - .color(Color::White), 112 + .colored(Color::White), 113 113 ); 114 - canvas.root().add_object( 114 + canvas.root().set_object( 115 115 "beat", 116 116 Object::CenteredText( 117 117 center.translated(0, 3), 118 118 format!("beat {}", ctx.beat), 119 119 30.0, 120 120 ) 121 - .color(Color::Cyan), 121 + .colored(Color::Cyan), 122 122 ); 123 123 Ok(()) 124 124 })
+1 -1
src/random/canvas.rs
··· 71 71 let hatchable = object.hatchable(); 72 72 objects.insert( 73 73 format!("{}#{}", name, i), 74 - object.paint(if hatchable { 74 + object.filled(if hatchable { 75 75 Fill::random_hatches(self.background) 76 76 } else { 77 77 Fill::random_solid(self.background)
+1 -1
src/random/fill.rs
··· 9 9 10 10 pub fn random_hatches(except: Option<Color>) -> Self { 11 11 let hatch_size = rand::thread_rng().gen_range(5..=100) as f32 * 1e-2; 12 - Fill::Hatched( 12 + Fill::Hatches( 13 13 random_color(except), 14 14 Angle(rand::thread_rng().gen_range(0.0..360.0)), 15 15 hatch_size,
+2 -2
src/rendering/fill.rs
··· 10 10 Fill::Translucent(color, opacity) => { 11 11 format!("fill: {}; opacity: {};", color.render(colormap), opacity) 12 12 } 13 - Fill::Dotted(..) | Fill::Hatched(..) => { 13 + Fill::Dotted(..) | Fill::Hatches(..) => { 14 14 format!("fill: url(#{});", self.pattern_id()) 15 15 } 16 16 } ··· 29 29 ) 30 30 } 31 31 Fill::Dotted(..) => unimplemented!(), 32 - Fill::Hatched(..) => unimplemented!(), 32 + Fill::Hatches(..) => unimplemented!(), 33 33 } 34 34 } 35 35 }