Skip to content

Commit 189cb87

Browse files
committed
Create rough_anyrender crate
Signed-off-by: Nico Burns <nico@nicoburns.com>
1 parent 328a56b commit 189cb87

10 files changed

Lines changed: 4079 additions & 105 deletions

File tree

Cargo.lock

Lines changed: 3222 additions & 105 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ members = [
77
"crates/wgpu_context",
88
"crates/pixels_window_renderer",
99
"crates/softbuffer_window_renderer",
10+
"crates/rough_anyrender",
1011
"examples/winit",
1112
"examples/bunnymark",
1213
]

crates/rough_anyrender/Cargo.toml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
[package]
2+
name = "rough_anyrender"
3+
version = "0.1.0"
4+
edition = "2021"
5+
authors = ["orhanbalci@gmail.com <orhanbalci@gmail.com>"]
6+
description = "Draw Hand Sketched 2D Drawings Using Vello"
7+
repository = "https://github.com/orhanbalci/rough-rs.git"
8+
homepage = "https://github.com/orhanbalci"
9+
keywords = ["graphics", "bezier", "sketch", "2D", "vello"]
10+
categories = ["graphics"]
11+
license = "MIT"
12+
readme = "README.md"
13+
14+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
15+
16+
[dependencies]
17+
anyrender = { workspace = true }
18+
kurbo = { workspace = true }
19+
peniko = { workspace = true }
20+
roughr = { version = "0.12.0" }
21+
num-traits = "0.2"
22+
euclid = "0.22"
23+
palette = "0.7"
24+
25+
[dev-dependencies]
26+
anyrender_vello = { workspace = true }
27+
rand = "0.8"
28+
rand_distr = "0.4"
29+
svg_path_ops = { version = "0.11.0" }
30+
bevy = "0.17"
31+
bevy_vello = { git = "https://github.com/HaHa421/bevy_vello-bevy-0.17", rev = "41ab73de84b8e744c0bf304483d16d0b2a1cf024" }
32+
wgpu = "26"

crates/rough_anyrender/README.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# rough_anyrender
2+
3+
[![Crates.io](https://img.shields.io/crates/v/rough_vello.svg)](https://crates.io/crates/rough_vello)
4+
[![Documentation](https://docs.rs/rough_vello/badge.svg)](https://docs.rs/rough_vello)
5+
[![License](https://img.shields.io/github/license/orhanbalci/rough-rs.svg)](https://github.com/orhanbalci/rough-rs/LICENSE)
6+
7+
<!-- cargo-sync-readme start -->
8+
9+
10+
This crate is an adapter crate between [roughr](https://github.com/orhanbalci/rough-rs/main/roughr) and
11+
[anyrender](https://github.com/dioxuslabs/anyrender) crates. Converts from roughr drawing
12+
primitives to AnyRender's PaintScene types. Also has convenience traits for drawing onto AnyRender scenes. For more detailed
13+
information you can check roughr crate.
14+
15+
Below examples are output of [rough_vello](https://github.com/orhanbalci/rough-rs/tree/main/rough_vello) adapter.
16+
17+
## 📦 Cargo.toml
18+
19+
```toml
20+
[dependencies]
21+
rough_anyrender = "0.1"
22+
```
23+
24+
## 🔧 Example
25+
26+
### Rust Logo
27+
28+
```rust
29+
use rough_vello::VelloGenerator;
30+
use vello::Scene;
31+
use palette::Srgba;
32+
use roughr::core::{FillStyle, OptionsBuilder};
33+
34+
let options = OptionsBuilder::default()
35+
.stroke(Srgba::from_components((114u8, 87u8, 82u8, 255u8)).into_format())
36+
.fill(Srgba::from_components((254u8, 246u8, 201u8, 255)).into_format())
37+
.fill_style(FillStyle::Hachure)
38+
.fill_weight(1.0)
39+
.bowing(0.8)
40+
.build()
41+
.unwrap();
42+
43+
let generator = VelloGenerator::new(options);
44+
let rust_logo_svg_path = "..."; // SVG path data for the Rust logo
45+
let rust_logo_drawing = generator.path::<f32>(rust_logo_svg_path);
46+
47+
let mut scene = Scene::new();
48+
rust_logo_drawing.draw(&mut scene);
49+
```
50+
51+
### 🖨️ Output Rust Logo
52+
![rust_logo](https://raw.githubusercontent.com/orhanbalci/rough-rs/main/rough_vello/assets/rust_logo.png)
53+
54+
## Filler Implementation Status
55+
- [x] Hachure
56+
- [x] Zigzag
57+
- [x] Cross-Hatch
58+
- [x] Dots
59+
- [x] Dashed
60+
- [x] Zigzag-Line
61+
62+
## 🔭 Examples
63+
64+
For more examples have a look at the
65+
[examples](https://github.com/orhanbalci/rough-rs/tree/main/rough_vello/examples) folder.
66+
67+
## 🔌 Integration
68+
69+
### Bevy Integration
70+
71+
For Bevy game engine integration, you can use [bevy_vello](https://github.com/linebender/bevy_vello) which provides a Bevy plugin for vello. This allows you to render `rough_vello` drawings directly in your Bevy applications by converting the vello Scene to Bevy-compatible rendering.
72+
73+
```toml
74+
[dependencies]
75+
rough_vello = "0.1"
76+
bevy_vello = "0.1" # Check latest version
77+
bevy = "0.14" # Or latest compatible version
78+
```
79+
80+
<!-- cargo-sync-readme end -->
81+
82+
## 📝 License
83+
84+
Licensed under MIT License ([LICENSE](LICENSE)).
85+
86+
### 🚧 Contributions
87+
88+
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the MIT license, shall be licensed as above, without any additional terms or conditions.
210 KB
Loading
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
use anyrender_vello::VelloScenePainter;
2+
use bevy::prelude::*;
3+
use bevy_vello::{prelude::*, VelloPlugin};
4+
use palette::Srgba;
5+
use rough_anyrender::VelloGenerator;
6+
use roughr::core::{FillStyle, OptionsBuilder};
7+
8+
#[derive(Component)]
9+
struct AnimatedLogo {
10+
velocity: Vec2,
11+
rotation_speed: f32,
12+
scale_oscillation: f32,
13+
scale_phase: f32,
14+
}
15+
16+
fn main() {
17+
App::new()
18+
.add_plugins(DefaultPlugins)
19+
.add_plugins(VelloPlugin::default())
20+
.add_systems(Startup, setup_vector_graphics)
21+
.add_systems(Update, animate_logo)
22+
.run();
23+
}
24+
25+
fn setup_vector_graphics(mut commands: Commands) {
26+
// Set clear color for the background
27+
commands.insert_resource(ClearColor(Color::srgb(
28+
30.0 / 255.0,
29+
30.0 / 255.0,
30+
40.0 / 255.0,
31+
)));
32+
33+
let rust_logo_svg_path: String = "M 149.98 37.69 a 9.51 9.51 90 0 1 4.755 -8.236 c 2.9425 -1.6985 6.5675 -1.6985 9.51 0 A 9.51 9.51 90 0 1 169 37.69 c 0 5.252 -4.258 9.51 -9.51 9.51 s -9.51 -4.258 -9.51 -9.51 M 36.52 123.79 c 0 -5.252 4.2575 -9.51 9.51 -9.51 s 9.51 4.258 9.51 9.51 s -4.258 9.51 -9.51 9.51 s -9.51 -4.258 -9.51 -9.51 m 226.92 0.44 c 0 -5.252 4.258 -9.51 9.51 -9.51 s 9.51 4.258 9.51 9.51 s -4.2575 9.51 -9.51 9.51 s -9.51 -4.258 -9.51 -9.51 m -199.4 13.06 c 4.375 -1.954 6.3465 -7.0775 4.41 -11.46 l -4.22 -9.54 h 16.6 v 74.8 H 47.34 a 117.11 117.11 90 0 1 -3.79 -44.7 z m 69.42 1.84 v -22.05 h 39.52 c 2.04 0 14.4 2.36 14.4 11.6 c 0 7.68 -9.5 10.44 -17.3 10.44 z M 79.5 257.84 a 9.51 9.51 90 0 1 4.755 -8.236 c 2.9425 -1.6985 6.5675 -1.6985 9.51 0 a 9.51 9.51 90 0 1 4.755 8.236 c 0 5.252 -4.258 9.51 -9.51 9.51 s -9.51 -4.258 -9.51 -9.51 m 140.93 0.44 c 0 -5.252 4.2575 -9.51 9.51 -9.51 s 9.51 4.258 9.51 9.51 s -4.258 9.51 -9.51 9.51 s -9.51 -4.2575 -9.51 -9.51 m 2.94 -21.57 c -4.7 -1 -9.3 1.98 -10.3 6.67 l -4.77 22.28 c -31.0655 14.07 -66.7215 13.8985 -97.65 -0.47 l -4.77 -22.28 c -1 -4.7 -5.6 -7.68 -10.3 -6.67 l -19.67 4.22 c -3.655 -3.7645 -7.0525 -7.77 -10.17 -11.99 h 95.7 c 1.08 0 1.8 -0.2 1.8 -1.18 v -33.85 c 0 -1 -0.72 -1.18 -1.8 -1.18 h -28 V 170.8 h 30.27 c 2.76 0 14.77 0.8 18.62 16.14 l 5.65 25 c 1.8 5.5 9.13 16.53 16.93 16.53 h 49.4 c -3.3155 4.4345 -6.941 8.6285 -10.85 12.55 z m 53.14 -89.38 c 0.6725 6.7565 0.7565 13.559 0.25 20.33 h -12 c -1.2 0 -1.7 0.8 -1.7 1.97 v 5.52 c 0 13 -7.32 15.8 -13.74 16.53 c -6.1 0.7 -12.9 -2.56 -13.72 -6.3 c -3.6 -20.28 -9.6 -24.6 -19 -32.1 c 11.77 -7.48 24.02 -18.5 24.02 -33.27 c 0 -15.94 -10.93 -25.98 -18.38 -30.9 c -10.45 -6.9 -22.02 -8.27 -25.14 -8.27 H 72.75 a 117.1 117.1 90 0 1 65.51 -36.97 l 14.65 15.37 c 3.3 3.47 8.8 3.6 12.26 0.28 l 16.4 -15.67 c 33.8115 6.331 63.129 27.2085 80.17 57.09 l -11.22 25.34 c -1.9365 4.3825 0.035 9.506 4.41 11.46 z m 27.98 0.4 l -0.38 -3.92 l 11.56 -10.78 c 2.35 -2.2 1.47 -6.6 -1.53 -7.72 l -14.77 -5.52 l -1.16 -3.8 l 9.2 -12.8 c 1.88 -2.6 0.15 -6.75 -3 -7.27 l -15.58 -2.53 l -1.87 -3.5 l 6.55 -14.37 c 1.34 -2.93 -1.15 -6.67 -4.37 -6.55 l -15.8 0.55 l -2.5 -3.03 l 3.63 -15.4 c 0.73 -3.13 -2.44 -6.3 -5.57 -5.57 l -15.4 3.63 l -3.04 -2.5 l 0.55 -15.8 c 0.12 -3.2 -3.62 -5.7 -6.54 -4.37 l -14.36 6.55 l -3.5 -1.88 l -2.54 -15.58 c -0.5 -3.16 -4.67 -4.88 -7.27 -3 l -12.8 9.2 l -3.8 -1.15 l -5.52 -14.77 c -1.12 -3 -5.53 -3.88 -7.72 -1.54 l -10.78 11.56 l -3.92 -0.38 l -8.32 -13.45 c -1.68 -2.72 -6.2 -2.72 -7.87 0 l -8.32 13.45 l -3.92 0.38 l -10.8 -11.58 c -2.2 -2.34 -6.6 -1.47 -7.72 1.54 L 119.79 20.6 l -3.8 1.15 l -12.8 -9.2 c -2.6 -1.88 -6.76 -0.15 -7.27 3 l -2.54 15.58 l -3.5 1.88 l -14.36 -6.55 c -2.92 -1.33 -6.67 1.17 -6.54 4.37 l 0.55 15.8 l -3.04 2.5 l -15.4 -3.63 c -3.13 -0.73 -6.3 2.44 -5.57 5.57 l 3.63 15.4 l -2.5 3.03 l -15.8 -0.55 c -3.2 -0.1 -5.7 3.62 -4.37 6.55 l 6.55 14.37 l -1.88 3.5 l -15.58 2.53 c -3.16 0.5 -4.88 4.67 -3 7.27 l 9.2 12.8 l -1.16 3.8 l -14.77 5.52 c -3 1.12 -3.88 5.53 -1.53 7.72 l 11.56 10.78 l -0.38 3.92 l -13.45 8.32 c -2.72 1.68 -2.72 6.2 0 7.87 l 13.45 8.32 l 0.38 3.92 l -11.59 10.82 c -2.34 2.2 -1.47 6.6 1.53 7.72 l 14.77 5.52 l 1.16 3.8 l -9.2 12.8 c -1.87 2.6 -0.15 6.76 3 7.27 l 15.57 2.53 l 1.88 3.5 l -6.55 14.36 c -1.33 2.92 1.18 6.67 4.37 6.55 l 15.8 -0.55 l 2.5 3.04 l -3.63 15.4 c -0.73 3.12 2.44 6.3 5.57 5.56 l 15.4 -3.63 l 3.04 2.5 l -0.55 15.8 c -0.12 3.2 3.62 5.7 6.54 4.37 l 14.36 -6.55 l 3.5 1.88 l 2.54 15.57 c 0.5 3.17 4.67 4.88 7.27 3.02 l 12.8 -9.22 l 3.8 1.16 l 5.52 14.77 c 1.12 3 5.53 3.88 7.72 1.53 l 10.78 -11.56 l 3.92 0.4 l 8.32 13.45 c 1.68 2.7 6.18 2.72 7.87 0 l 8.32 -13.45 l 3.92 -0.4 l 10.78 11.56 c 2.2 2.35 6.6 1.47 7.72 -1.53 l 5.52 -14.77 l 3.8 -1.16 l 12.8 9.22 c 2.6 1.87 6.76 0.15 7.27 -3.02 l 2.54 -15.57 l 3.5 -1.88 l 14.36 6.55 c 2.92 1.33 6.66 -1.16 6.54 -4.37 l -0.55 -15.8 l 3.03 -2.5 l 15.4 3.63 c 3.13 0.73 6.3 -2.44 5.57 -5.56 l -3.63 -15.4 l 2.5 -3.04 l 15.8 0.55 c 3.2 0.13 5.7 -3.63 4.37 -6.55 l -6.55 -14.36 l 1.87 -3.5 l 15.58 -2.53 c 3.17 -0.5 4.9 -4.66 3 -7.27 l -9.2 -12.8 l 1.16 -3.8 l 14.77 -5.52 c 3 -1.13 3.88 -5.53 1.53 -7.72 l -11.56 -10.78 l 0.38 -3.92 l 13.45 -8.32 c 2.72 -1.68 2.73 -6.18 0 -7.87 z".into();
34+
35+
commands.spawn((Camera2d, VelloView));
36+
37+
// Create 10 different logo instances with varying options and movement patterns
38+
for i in 0..10 {
39+
let angle = (i as f32) * std::f32::consts::TAU / 10.0; // Distribute in circle
40+
let radius = 200.0;
41+
let x = angle.cos() * radius;
42+
let y = angle.sin() * radius;
43+
44+
// Generate different visual styles for each logo
45+
let hue = (i as f32) * 36.0; // Different hue for each logo (0-360 degrees)
46+
let stroke_color = Srgba::from_components((
47+
((hue.to_radians().cos() * 127.0 + 128.0) as u8),
48+
(((hue + 120.0).to_radians().cos() * 127.0 + 128.0) as u8),
49+
(((hue + 240.0).to_radians().cos() * 127.0 + 128.0) as u8),
50+
255u8,
51+
))
52+
.into_format();
53+
54+
let fill_color = Srgba::from_components((
55+
((hue.to_radians().sin() * 100.0 + 155.0) as u8),
56+
(((hue + 120.0).to_radians().sin() * 100.0 + 155.0) as u8),
57+
(((hue + 240.0).to_radians().sin() * 100.0 + 155.0) as u8),
58+
180u8,
59+
))
60+
.into_format();
61+
62+
let fill_styles = [
63+
FillStyle::Hachure,
64+
FillStyle::Solid,
65+
FillStyle::ZigZag,
66+
FillStyle::CrossHatch,
67+
FillStyle::Dots,
68+
];
69+
70+
let options = OptionsBuilder::default()
71+
.stroke(stroke_color)
72+
.fill(fill_color)
73+
.fill_style(fill_styles[i % fill_styles.len()])
74+
.fill_weight(0.5 + (i as f32) * 0.3)
75+
.bowing(0.2 + (i as f32) * 0.1)
76+
.roughness(0.5 + (i as f32) * 0.2)
77+
.stroke_width(1.0 + (i as f32) * 0.5)
78+
.build()
79+
.unwrap();
80+
81+
let generator = VelloGenerator::new(options);
82+
let rust_logo_drawing = generator.path::<f32>(rust_logo_svg_path.clone());
83+
let mut scene = vello::Scene::new();
84+
rust_logo_drawing.draw(&mut VelloScenePainter::new(&mut scene));
85+
86+
// Create different movement patterns for each logo
87+
let velocity = match i % 5 {
88+
0 => Vec2::new(50.0 + i as f32 * 10.0, 30.0 + i as f32 * 5.0), // Linear movement
89+
1 => Vec2::new(-40.0 - i as f32 * 8.0, 60.0 + i as f32 * 7.0), // Different linear
90+
2 => Vec2::new(80.0 + i as f32 * 12.0, -50.0 - i as f32 * 6.0), // Another linear
91+
3 => Vec2::new(-70.0 - i as f32 * 9.0, -40.0 - i as f32 * 4.0), // Diagonal
92+
_ => Vec2::new(45.0 + i as f32 * 11.0, 70.0 + i as f32 * 8.0), // Default
93+
};
94+
95+
let scale = 0.3 + (i as f32) * 0.07; // Different scales from 0.3 to 0.93
96+
97+
commands.spawn((
98+
VelloSceneBundle {
99+
scene: VelloScene::from(scene),
100+
transform: Transform::from_translation(Vec3::new(x, y, 0.0))
101+
.with_scale(Vec3::splat(scale)),
102+
..default()
103+
},
104+
AnimatedLogo {
105+
velocity,
106+
rotation_speed: (i as f32 + 1.0) * 0.3, // Different rotation speeds
107+
scale_oscillation: 0.1 + (i as f32) * 0.02, // Different scale oscillation
108+
scale_phase: (i as f32) * std::f32::consts::PI / 5.0, // Different phases
109+
},
110+
));
111+
}
112+
}
113+
114+
fn animate_logo(time: Res<Time>, mut query: Query<(&mut Transform, &mut AnimatedLogo)>) {
115+
let dt = time.delta_secs();
116+
117+
for (mut transform, logo) in query.iter_mut() {
118+
// Update position based on velocity
119+
transform.translation.x += logo.velocity.x * dt;
120+
transform.translation.y += logo.velocity.y * dt;
121+
122+
// Apply rotation
123+
transform.rotation *= Quat::from_rotation_z(logo.rotation_speed * dt);
124+
125+
// Apply scale oscillation
126+
let scale_factor =
127+
1.0 + logo.scale_oscillation * (time.elapsed_secs() + logo.scale_phase).sin();
128+
let base_scale =
129+
0.3 + (transform.translation.x.abs() + transform.translation.y.abs()) * 0.0001; // Vary base scale slightly
130+
transform.scale = Vec3::splat(scale_factor * base_scale);
131+
132+
// Simple screen wrapping (optional boundary handling)
133+
if transform.translation.x > 800.0 {
134+
transform.translation.x = -800.0;
135+
}
136+
if transform.translation.x < -800.0 {
137+
transform.translation.x = 800.0;
138+
}
139+
if transform.translation.y > 600.0 {
140+
transform.translation.y = -600.0;
141+
}
142+
if transform.translation.y < -600.0 {
143+
transform.translation.y = 600.0;
144+
}
145+
}
146+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use std::ops::DerefMut;
2+
3+
use bevy::prelude::*;
4+
use bevy_vello::{prelude::*, VelloPlugin};
5+
use palette::Srgba;
6+
use rough_vello::VelloGenerator;
7+
use roughr::core::{FillStyle, OptionsBuilder};
8+
9+
const CANVAS_WIDTH: u32 = 800;
10+
const CANVAS_HEIGHT: u32 = 600;
11+
12+
fn main() {
13+
App::new()
14+
.add_plugins(DefaultPlugins)
15+
.add_plugins(VelloPlugin::default())
16+
.add_systems(Startup, setup_vector_graphics)
17+
.run();
18+
}
19+
20+
fn setup_vector_graphics(mut commands: Commands) {
21+
// Set clear color for the background
22+
commands.insert_resource(ClearColor(Color::srgb(
23+
150.0 / 255.0,
24+
192.0 / 255.0,
25+
183.0 / 255.0,
26+
)));
27+
28+
// Create rough rectangle using Vello
29+
let options = OptionsBuilder::default()
30+
.stroke(Srgba::from_components((114u8, 87u8, 82u8, 255u8)).into_format())
31+
.fill(Srgba::from_components((254u8, 246u8, 201u8, 255)).into_format())
32+
.fill_style(FillStyle::ZigZagLine)
33+
.fill_weight(96.0 * 0.01)
34+
.bowing(0.8)
35+
.build()
36+
.unwrap();
37+
38+
let generator = VelloGenerator::new(options);
39+
let rect_width = 300.0;
40+
let rect_height = 200.0;
41+
// Position rectangle at the center of the canvas
42+
// For centering: (canvas_size - rect_size) / 2
43+
let rect = generator.rectangle::<f32>(
44+
(CANVAS_WIDTH as f32 - rect_width) / 2.0,
45+
(CANVAS_HEIGHT as f32 - rect_height) / 2.0,
46+
rect_width,
47+
rect_height,
48+
);
49+
50+
// Create Vello scene and add the rough rectangle
51+
let mut scene = vello::Scene::new();
52+
53+
// Draw the rough rectangle to the scene
54+
rect.draw(&mut scene);
55+
56+
commands.spawn((Camera2d, VelloView));
57+
commands.spawn(VelloSceneBundle {
58+
scene: VelloScene::from(scene),
59+
60+
transform: Transform::from_translation(Vec3::new(
61+
-(CANVAS_WIDTH as f32) / 2.0,
62+
(CANVAS_HEIGHT as f32) / 2.0,
63+
0.0,
64+
)),
65+
..default()
66+
});
67+
}

0 commit comments

Comments
 (0)