Skip to content

Commit

Permalink
Sketch api (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
abey79 authored Sep 18, 2023
1 parent 075a55d commit d5db57a
Show file tree
Hide file tree
Showing 25 changed files with 326 additions and 147 deletions.
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ members = [
"vsvg",
"vsvg-cli",
"vsvg-viewer",
"vsvg-sketch",
"vsvg-multi",
]

Expand All @@ -25,3 +26,4 @@ serde = { version = "1", features = ["derive", "rc"] }
kurbo = "0.9.1"
rand = "0.8.5"
rand_chacha = "0.3.1"
anyhow = "1"
12 changes: 11 additions & 1 deletion vsvg-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ use std::error::Error;

use std::fmt::{Debug, Display, Formatter};

use crate::draw_state::{DrawState, LayerDrawer};
use std::path::PathBuf;
use vsvg::{Document, DrawState, LayerID};
use vsvg::{Document, DocumentTrait, LayerID};

/// A trait for types that can be used as command line arguments.
trait CommandArg: Clone + Into<CommandValue> + Send + Sync + Debug + 'static {}
Expand Down Expand Up @@ -39,6 +40,15 @@ pub(crate) struct State {
pub draw_layer: LayerID,
}

impl State {
pub(crate) fn draw(&mut self) -> LayerDrawer {
LayerDrawer {
state: &self.draw_state,
layer: self.document.get_mut(self.draw_layer),
}
}
}

impl Default for State {
fn default() -> Self {
Self {
Expand Down
36 changes: 10 additions & 26 deletions vsvg-cli/src/commands.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::cli::{CommandDesc, CommandValue};
use clap::{arg, value_parser, Arg, Id};
use std::collections::HashMap;
use vsvg::{DocumentTrait, IndexBuilder, Transforms};
use vsvg::{DocumentTrait, Draw, IndexBuilder, Transforms};

// https://stackoverflow.com/a/38361018/229511
macro_rules! count_items {
Expand Down Expand Up @@ -177,9 +177,7 @@ pub(crate) fn command_list() -> HashMap<Id, CommandDesc<'static>> {
f64,
|state, x1, y1, x2, y2, x3, y3, x4, y4| {
state
.document
.get_mut(state.draw_layer)
.draw(&state.draw_state)
.draw()
.cubic_bezier(*x1, *y1, *x2, *y2, *x3, *y3, *x4, *y4)
}
),
Expand All @@ -188,9 +186,7 @@ pub(crate) fn command_list() -> HashMap<Id, CommandDesc<'static>> {
f64,
|state, x1, y1, x2, y2, x3, y3| {
state
.document
.get_mut(state.draw_layer)
.draw(&state.draw_state)
.draw()
.quadratic_bezier(*x1, *y1, *x2, *y2, *x3, *y3)
}
),
Expand All @@ -199,9 +195,7 @@ pub(crate) fn command_list() -> HashMap<Id, CommandDesc<'static>> {
f64,
|state, x, y, rx, ry, start, sweep, rot_x| {
state
.document
.get_mut(state.draw_layer)
.draw(&state.draw_state)
.draw()
.arc(*x, *y, *rx, *ry, start.to_radians(), sweep.to_radians(), rot_x.to_radians())
}
),
Expand All @@ -210,9 +204,7 @@ pub(crate) fn command_list() -> HashMap<Id, CommandDesc<'static>> {
f64,
|state, x, y, r| {
state
.document
.get_mut(state.draw_layer)
.draw(&state.draw_state)
.draw()
.circle(*x, *y, *r)
}
),
Expand All @@ -221,9 +213,7 @@ pub(crate) fn command_list() -> HashMap<Id, CommandDesc<'static>> {
f64,
|state, x, y, rx, ry, rot_x| {
state
.document
.get_mut(state.draw_layer)
.draw(&state.draw_state)
.draw()
.ellipse(*x, *y, *rx, *ry, rot_x.to_radians())
}
),
Expand All @@ -232,27 +222,23 @@ pub(crate) fn command_list() -> HashMap<Id, CommandDesc<'static>> {
f64,
|state, x1, y1, x2, y2| {
state
.document
.get_mut(state.draw_layer)
.draw(&state.draw_state)
.draw()
.line(*x1, *y1, *x2, *y2)
}
),
command_decl!(
arg!(--drect [X] "Draw a rectangle with X, Y, W, H"),
f64,
|state, a, b, c, d| {
state.document.get_mut(state.draw_layer).draw(&state.draw_state).rect(*a, *b, *c, *d)
state.draw().rect(*a, *b, *c, *d)
}
),
command_decl!(
arg!(--drrect [X] "Draw a rounded rectangle with X, Y, W, H, TL, TR, BR, BL"),
f64,
|state, cx, cy, w, h, tl, tr, br, bl| {
state
.document
.get_mut(state.draw_layer)
.draw(&state.draw_state)
.draw()
.rounded_rect(*cx, *cy, *w, *h, *tl, *tr, *br, *bl)
}
),
Expand All @@ -261,9 +247,7 @@ pub(crate) fn command_list() -> HashMap<Id, CommandDesc<'static>> {
String,
|state, path| {
state
.document
.get_mut(state.draw_layer)
.draw(&state.draw_state)
.draw()
.svg_path(path)?
}
),
Expand Down
47 changes: 47 additions & 0 deletions vsvg-cli/src/draw_state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//! Structure to hold the state for the [`Draw`] API between commands.

use kurbo::Affine;
use vsvg::{
Draw, IntoBezPathTolerance, Layer, Path, PathMetadata, PathTrait, Transforms, DEFAULT_TOLERANCE,
};

#[derive(Debug)]
pub struct DrawState {
pub transform: Affine,
pub metadata: PathMetadata,

/// used to convert shapes to Béziers
pub tolerance: f64,
}

impl Default for DrawState {
fn default() -> Self {
Self {
transform: Affine::default(),
metadata: PathMetadata::default(),
tolerance: DEFAULT_TOLERANCE,
}
}
}

impl Transforms for DrawState {
fn transform(&mut self, affine: &Affine) -> &mut Self {
self.transform = *affine * self.transform;
self
}
}

pub struct LayerDrawer<'layer, 'state> {
pub(crate) state: &'state DrawState,
pub(crate) layer: &'layer mut Layer,
}

impl<'layer, 'state> Draw for LayerDrawer<'layer, 'state> {
fn add_path<T: IntoBezPathTolerance>(&mut self, path: T) -> &mut Self {
let mut path: Path = Path::from_tolerance(path, self.state.tolerance);
*path.metadata_mut() = self.state.metadata.clone();
path.apply_transform(self.state.transform);
self.layer.paths.push(path);
self
}
}
1 change: 1 addition & 0 deletions vsvg-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod cli;
mod commands;
mod draw_state;

use crate::commands::command_list;

Expand Down
16 changes: 16 additions & 0 deletions vsvg-sketch/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "vsvg-sketch"
version = "0.1.0-alpha.0"
edition = "2021"

[dependencies]
vsvg = { path = "../vsvg", features = ["geo"] }
vsvg-viewer = { path = "../vsvg-viewer", optional = true }
kurbo.workspace = true
anyhow.workspace = true

[dev-dependencies]

[features]
default = ["viewer"]
viewer = ["dep:vsvg-viewer"]
15 changes: 15 additions & 0 deletions vsvg-sketch/examples/basic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use vsvg_sketch::prelude::*;

fn main() -> Result {
Sketch::with_page_size(PageSize::A5)
.scale(Units::CM)
.translate(7.0, 6.0)
.circle(0.0, 0.0, 2.5)
.translate(1.0, 4.0)
.rotate_deg(45.0)
.rect(0., 0., 4.0, 1.0)
.show()?
.save("output.svg")?;

Ok(())
}
4 changes: 4 additions & 0 deletions vsvg-sketch/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod prelude;
pub mod sketch;

pub type Result = anyhow::Result<()>;
3 changes: 3 additions & 0 deletions vsvg-sketch/src/prelude.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub use crate::{sketch::Sketch, Result};
pub use vsvg::{Draw, PageSize, Transforms, Units};
pub use vsvg_viewer::show;
82 changes: 82 additions & 0 deletions vsvg-sketch/src/sketch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use kurbo::Affine;
use vsvg::path::IntoBezPathTolerance;
use vsvg::{
Document, DocumentTrait, Draw, LayerID, PageSize, Path, PathMetadata, Transforms,
DEFAULT_TOLERANCE,
};

pub struct Sketch {
document: Document,
transform: Affine,
target_layer: LayerID,
tolerance: f64,
path_metadata: PathMetadata,
}

impl Default for Sketch {
fn default() -> Self {
Self::new()
}
}
impl Sketch {
pub fn new() -> Self {
Self::with_document(Document::default())
}

pub fn with_page_size(page_size: PageSize) -> Self {
Self::with_document(Document::new_with_page_size(page_size))
}

pub fn with_document(mut document: Document) -> Self {
let target_layer = 0;
document.ensure_exists(target_layer);

Self {
document,
tolerance: DEFAULT_TOLERANCE,
transform: Affine::default(),
target_layer,
path_metadata: PathMetadata::default(),
}
}

pub fn set_layer(&mut self, layer_id: LayerID) -> &mut Self {
self.document.ensure_exists(layer_id);
self.target_layer = layer_id;
self
}

pub fn document(&self) -> &Document {
&self.document
}

pub fn show(&mut self) -> anyhow::Result<&mut Self> {
vsvg_viewer::show(self.document())?;
Ok(self)
}

pub fn save(&mut self, path: impl AsRef<std::path::Path>) -> anyhow::Result<&mut Self> {
let file = std::io::BufWriter::new(std::fs::File::create(path)?);
self.document.to_svg(file)?;
Ok(self)
}
}

impl Transforms for Sketch {
fn transform(&mut self, affine: &Affine) -> &mut Self {
self.transform *= *affine;
self
}
}

impl Draw for Sketch {
fn add_path<T: IntoBezPathTolerance>(&mut self, path: T) -> &mut Self {
let mut path: Path =
Path::from_tolerance_metadata(path, self.tolerance, self.path_metadata.clone());

path.apply_transform(self.transform);

self.document.push_path(self.target_layer, path);
self
}
}
1 change: 1 addition & 0 deletions vsvg-viewer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ serde.workspace = true
wgpu = "0.15.1"
bytemuck = {version = "1.13.1", features = [ "derive" ]}
cgmath = "0.18.0"
anyhow.workspace = true

[dev-dependencies] # mostly for examples
criterion = "0.4.0"
Expand Down
3 changes: 1 addition & 2 deletions vsvg-viewer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@ mod viewer;

use crate::engine::DocumentData;
use crate::viewer::Viewer;
use std::error::Error;
use std::sync::Arc;
use vsvg::Document;

/// Show a document in a window.
#[allow(clippy::missing_panics_doc)]
pub fn show(document: &Document) -> Result<(), Box<dyn Error>> {
pub fn show(document: &Document) -> anyhow::Result<()> {
let native_options = eframe::NativeOptions::default();
let document_data = Arc::new(DocumentData::new(document));

Expand Down
3 changes: 2 additions & 1 deletion vsvg/src/document/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ pub struct Document {
}

impl Transforms for Document {
fn transform(&mut self, affine: &kurbo::Affine) {
fn transform(&mut self, affine: &kurbo::Affine) -> &mut Self {
self.layers.iter_mut().for_each(|(_, layer)| {
layer.transform(affine);
});
self
}
}

Expand Down
Loading

0 comments on commit d5db57a

Please sign in to comment.