A while back, I took a break from responsibilities and played around with generating 10Print and 10Print-like diagrams. For those not in the know, check out the website 10Print.org!
I did my 10Print renders using my own creative coding tool (which may be a future blog post), but today I’ll create the renders in the SVG format.
We’ll be using Rust, the svg
crate to generate SVGs, and the rand
crate for random number generation.
Variants
Let’s take a look at 10Print and some variants.
Original
- Consider the canvas as a grid of cells.
-
For each cell in the canvas, randomly draw a line which is either:
- From the top left to the bottom right
- From the bottom left to the top right
use rand::Rng;
use svg::{node::element::{path::Data, Path}, Node};
/// Draw a line from start (x1, y1) to end (x2, y2).
fn (: (, ), : (, )) -> Path {
Path::new().set("d", Data::new().move_to().line_to().close())
}
fn () -> <(), <dyn std::error::>> {
// 10Print parameters
let = 1000;
let = 500;
let = 20;
let = / ;
let = "output.svg";
// Create the svg document
let = (0, 0, , );
let mut = svg::Document::new().set("viewBox", );
let mut = rand::rngs::StdRng::seed_from_u64(8888);
for in (0..).( as ) {
for in (0..).( as ) {
// Half the time...
let = if .gen::<>() > 0.5 {
// Draw a top left -> bottom right line
((, ), ( + , + ));
} else {
// Draw a bottom left -> top right line
((, + ), ( + , ));
};
.append();
}
}
svg::save(, &)?;
(())
}
Output:
Pretty neat! With the two simple rules, we get an aesthetically pleasing tiled image. We can also bias the lines towards one diagonal or another by modifying the weight in the random number check:
Top-left to bottom right bias:
// Most of the time...
let line = if rng.gen::<>() > 0.2 {
// Draw a top left -> bottom right line
((x, y), (x + spacing, y + spacing))
} else {
Bottom-left to top-right bias:
// Very rarely...
let line = if rng.gen::<>() > 0.8 {
// Draw a top left -> bottom right line
((x, y), (x + spacing, y + spacing))
} else {
Orthogonal
What if, instead of diagonal lines, we drew straight lines instead?
let line = if rng.gen::<>() > 0.5 {
((x, y), (x + spacing, y))
} else {
((x, y), (x, y + spacing))
};
Pretty cool! It resembles a very biased and unfun maze.
Weave
This variant is similar to the Orthogonal variant, but with a different offset:
let line = if rng.gen::<>() > 0.5 {
((x + spacing / 2, y), (x + spacing / 2, y + spacing))
} else {
((x, y + spacing / 2), (x + spacing, y + spacing / 2))
};
Kinda looks like a basket weave, don’t you think?
Mondrian-ish
How about we connect the lines in the Weave variant?
let line = if rng.gen::<>() > 0.5 {
(
(x + spacing / 2, y - spacing / 2),
(x + spacing / 2, y + spacing * 3 / 2),
)
} else {
(
(x - spacing / 2, y + spacing / 2),
(x + spacing * 3 / 2, y + spacing / 2),
)
};
Budget Piet Mondrian art!
Bark
There’s no restriction on the number of separate “cases” we create, either. Here’s a version with 3 cases instead of two:
let rand_num = rng.gen::<>();
let line = if rand_num < 0.1 {
((x, y), (x + spacing, y + spacing))
} else if rand_num < 0.4 {
((x, y + spacing), (x + spacing, y))
} else {
((x + spacing, y), (x + spacing, y + spacing))
};
Color
Black and white images are too boring, so let’s inject some color into the renders.
First, we’ll make the line
function accept a color:
fn (: (, ), : (, ), : &) -> Path {
Path::new()
.set("stroke", )
.set("d", Data::new().move_to().line_to().close())
}
Then, we modify the calls to draw either a red line or a blue line:
let line = if rand_num > 0.5 {
// Draw a top left -> bottom right line
((x, y), (x + spacing, y + spacing), "red");
} else {
// Draw a bottom left -> top right line
((x, y + spacing), (x + spacing, y), "blue");
}
More 10Print Variants
Here are a few more 10Print variants. Try to create them yourself or be creative and make your own designs!
Note: This one is a recreation of Ian Witham’s 10Print variation.