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 ::;
use ::{::::{::, }, };
/// Draw a line from start (x1, y1) to end (x2, y2).
fn (: (, ), : (, )) -> {
::().("d", ::().().().())
}
fn () -> <(), <dyn ::::>> {
// 10Print parameters
let = 1000;
let = 500;
let = 20;
let = / ;
let = "output.svg";
// Create the svg document
let = (0, 0, , );
let mut = ::::().("viewBox", );
let mut = ::::::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
((, + ), ( + , ));
};
.();
}
}
::(, &)?;
(())
}
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 = 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 = 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 = 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 = 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 = 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 = rng.gen::<>();
let = if < 0.1 {
((x, y), (x + spacing, y + spacing))
} else if < 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 = 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.