Skip to content

Commit

Permalink
Drawing with rounded caps
Browse files Browse the repository at this point in the history
  • Loading branch information
tgross35 committed May 2, 2024
1 parent baf6035 commit 8ba259e
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 26 deletions.
34 changes: 33 additions & 1 deletion altium/src/draw/canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,36 @@ pub trait Canvas: crate::sealed::Sealed {
fn add_comment<S: Into<String>>(&mut self, _comment: S) {}
}

/// Line ending.
///
/// See <https://docs.rs/lyon_tessellation/1.0.5/lyon_tessellation/enum.LineCap.html> for more.
#[derive(Clone, Debug, Default)]
pub enum LineCap {
/// Stop at the endpoint
#[default]
Butt,
/// Square past the endpoint
Square,
/// Rounded cap centered at the endpoint
Round,
}

/// How two lines should be combined
///
/// See <https://svgwg.org/specs/strokes/#StrokeLinejoinProperty> for more.
#[derive(Clone, Debug, Default)]
pub enum LineJoin {
/// Sharp corners
#[default]
Miter,
/// Miter but possibly don't come to a fine point
MiterClip,
/// Round over the join point
Round,
/// Square off the join point
Bevel,
}

/// Helper struct to write some text
#[derive(Clone, Debug, Default)]
pub struct DrawText<'a> {
Expand All @@ -32,7 +62,9 @@ pub struct DrawLine {
pub end: Location,
pub color: Rgb,
pub width: u32,
// pub width: Option<&'a str>,
pub start_cap: LineCap,
pub end_cap: LineCap,
pub line_join: LineJoin,
}

#[derive(Clone, Debug, Default)]
Expand Down
11 changes: 10 additions & 1 deletion altium/src/draw/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,16 @@
pub(crate) mod canvas;
mod svg;

pub use canvas::{Canvas, DrawImage, DrawLine, DrawPolygon, DrawRectangle, DrawText};
pub use canvas::{
Canvas,
DrawImage,
DrawLine,
DrawPolygon,
DrawRectangle,
DrawText,
LineCap,
LineJoin,
};

pub use self::svg::SvgCtx;
pub use crate::common::{Location, PosHoriz, PosVert, Rgb};
Expand Down
11 changes: 9 additions & 2 deletions altium/src/sch/record/draw.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! How to draw records, components, etc

use crate::common::{Location, PosHoriz, PosVert, Rgb, Rotation90, Visibility};
use crate::draw::canvas::DrawRectangle;
use crate::draw::canvas::{Canvas, DrawLine, DrawText};
use crate::draw::canvas::{DrawRectangle, LineCap};
use crate::draw::{Draw, DrawPolygon};
use crate::font::FontCollection;
use crate::sch::pin::SchPin;
Expand Down Expand Up @@ -87,7 +87,8 @@ impl Draw for SchPin {
end,
color: Rgb::black(),
width: 30000,
// ..Default::default()
start_cap: LineCap::Round,
..Default::default()
});

// Altium draws a small white plus at the pin's connect position, so we
Expand All @@ -97,13 +98,15 @@ impl Draw for SchPin {
end: end.add_x(-10000),
color: Rgb::white(),
width: 5000,
..Default::default()
});

canvas.draw_line(DrawLine {
start: end.add_y(10000),
end: end.add_y(-10000),
color: Rgb::white(),
width: 5000,
..Default::default()
});

// FIXME: use actual spacing & fonts from pin spec
Expand Down Expand Up @@ -187,6 +190,7 @@ impl Draw for record::PolyLine {
end: b,
color: self.color,
width: self.line_width * 4,
..Default::default()
});
}
}
Expand Down Expand Up @@ -240,6 +244,7 @@ impl Draw for record::Line {
end: Location::new(self.corner_x, self.corner_y),
color: self.color,
width: self.line_width,
..Default::default()
});
}
}
Expand Down Expand Up @@ -347,6 +352,7 @@ impl Draw for record::Bus {
end: b,
color: self.color,
width: self.line_width * 4,
..Default::default()
});
}
}
Expand All @@ -364,6 +370,7 @@ impl Draw for record::Wire {
end: b,
color: self.color,
width: self.line_width * 4,
..Default::default()
});
}
}
Expand Down
2 changes: 1 addition & 1 deletion ecadg/src/gfx/grid.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![allow(clippy::cast_sign_loss)]
#![allow(clippy::cast_possible_truncation)]

use std::mem::{self, offset_of};
use std::mem::{self};

use bytemuck::{Pod, Zeroable};
use eframe::egui_wgpu::{self, wgpu, RenderState};
Expand Down
85 changes: 64 additions & 21 deletions ecadg/src/gfx/tessellated.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![allow(dead_code, unused_imports)]
use std::mem;

use alt_to_lyon::ToLyonTy;
use altium::draw::{Canvas, Draw};
use bytemuck::{Pod, Zeroable};
use eframe::egui_wgpu::{self, wgpu, RenderState};
Expand Down Expand Up @@ -129,6 +130,7 @@ pub struct TessCtx {
stroke_ibo: wgpu::Buffer,
fill_geometry: VertexBuffers<TessVertex, u16>,
stroke_geometry: VertexBuffers<TessVertex, u16>,
/// Quantities to actually write if we need to pad them
prims_ubo: wgpu::Buffer,
globals_ubo: wgpu::Buffer,
bind_group: wgpu::BindGroup,
Expand Down Expand Up @@ -365,29 +367,21 @@ impl TessCtx {
);

if self.render_fill() {
queue.write_buffer(
&self.fill_vbo,
0,
bytemuck::cast_slice(&self.fill_geometry.vertices),
);
queue.write_buffer(
&self.fill_ibo,
0,
bytemuck::cast_slice(&self.fill_geometry.indices),
);
with_aligned_buf(&mut self.fill_geometry.vertices, |buf| {
queue.write_buffer(&self.fill_vbo, 0, buf)
});
with_aligned_buf(&mut self.fill_geometry.indices, |buf| {
queue.write_buffer(&self.fill_ibo, 0, buf)
});
}

if self.render_stroke() {
queue.write_buffer(
&self.stroke_vbo,
0,
bytemuck::cast_slice(&self.stroke_geometry.vertices),
);
queue.write_buffer(
&self.stroke_ibo,
0,
bytemuck::cast_slice(&self.stroke_geometry.indices),
);
with_aligned_buf(&mut self.stroke_geometry.vertices, |buf| {
queue.write_buffer(&self.stroke_vbo, 0, buf)
});
with_aligned_buf(&mut self.stroke_geometry.indices, |buf| {
queue.write_buffer(&self.stroke_ibo, 0, buf)
});
}
}

Expand Down Expand Up @@ -435,7 +429,10 @@ impl Canvas for TessCtx {
self.stroke_tess
.tessellate_path(
&path,
&STROKE_OPTIONS.with_line_width(item.width as f32),
&STROKE_OPTIONS
.with_line_width(item.width as f32)
.with_start_cap(item.start_cap.to_lyon_ty())
.with_end_cap(item.end_cap.to_lyon_ty()),
&mut BuffersBuilder::new(
&mut self.stroke_geometry,
WithColor(item.color.as_float_rgba()),
Expand Down Expand Up @@ -510,3 +507,49 @@ impl StrokeVertexConstructor<TessVertex> for WithColor {
}
}
}

/// Temporarily extend a buffer to be a multiple of [`wgpu::COPY_BUFFER_ALIGNMENT`] for use
/// as a slice
///
/// This is used because wgpu requires a buffer aligned to `COPY_BUFFER_ALIGNMENT` but we don't
/// want to zero-pad our tessellation buffers then forget about it (and wind up with extra
/// vertices / indices). So provide an aligned buffer only within the scope of a closure.
fn with_aligned_buf<T, F>(buf: &mut Vec<T>, f: F)
where
T: Default + bytemuck::NoUninit,
F: FnOnce(&[u8]),
{
let t_size: wgpu::BufferAddress = std::mem::size_of::<T>().try_into().unwrap();
let len: wgpu::BufferAddress = buf.len().try_into().unwrap();
let len_bytes = len * t_size;
// Next value that will meet the alignment
let target_len_bytes = len_bytes.next_multiple_of(wgpu::COPY_BUFFER_ALIGNMENT);
let to_add = (target_len_bytes - len_bytes).div_ceil(t_size);

// push temporary elements to meet the needed alignment
for _ in 0..to_add {
buf.push(T::default());
}

f(bytemuck::cast_slice(buf));

// Remove the temporary elements so we can continue appending to the buffer later
buf.truncate(buf.len() - (to_add as usize));
}

mod alt_to_lyon {
pub trait ToLyonTy<LyonTy> {
fn to_lyon_ty(&self) -> LyonTy;
}

impl ToLyonTy<lyon::path::LineCap> for altium::draw::LineCap {
fn to_lyon_ty(&self) -> lyon::path::LineCap {
use altium::draw::LineCap;
match self {
LineCap::Butt => lyon::path::LineCap::Butt,
LineCap::Square => lyon::path::LineCap::Square,
LineCap::Round => lyon::path::LineCap::Round,
}
}
}
}
1 change: 1 addition & 0 deletions ecadg/src/gfx/triangle.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![allow(unused)]
//! Just a demo triangle

use std::num::NonZeroU64;
Expand Down

0 comments on commit 8ba259e

Please sign in to comment.