Skip to content

Commit

Permalink
Merge pull request #42 from pluots/further-rendering
Browse files Browse the repository at this point in the history
Add rendering support for more records
  • Loading branch information
tgross35 authored Jun 6, 2024
2 parents 834f21f + 67a6899 commit fcce3c7
Show file tree
Hide file tree
Showing 26 changed files with 1,409 additions and 468 deletions.
783 changes: 531 additions & 252 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ members = [
"altium-macros",
"ecad-cli",
"ecadg",
# "drawsvg",
]
4 changes: 2 additions & 2 deletions altium-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ proc-macro = true

[dependencies]
convert_case = "0.6.0"
proc-macro2 = "1.0.81"
proc-macro2 = "1.0.85"
quote = "1.0.36"
regex = "1.10.4"
syn = "2.0.60"
syn = "2.0.66"
# syn = { version = "2.0.22", features = ["extra-traits"] }

[package.metadata.release]
Expand Down
62 changes: 34 additions & 28 deletions altium-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,24 +120,28 @@ fn handle_field(
// Parse attributes that exist on the field
let mut field_attr_map = parse_attrs(field.attrs).unwrap_or_default();

// Convert is always allowed; just applied to all values if used for arrays
let convert = match field_attr_map.remove("convert") {
Some(conv_fn) => quote! { .map_err(Into::into).and_then(#conv_fn) },
None => TokenStream2::new(),
};

// Check if we need to parse an array
if let Some(arr_val) = field_attr_map.remove("array") {
let arr_val_str = arr_val.to_string();
if arr_val_str == "true" {
let count_ident = field_attr_map
.remove("count")
.expect("missing 'count' attribute");

let arr_map = field_attr_map
.remove("map")
.expect("missing 'map' attribute");

process_array(struct_ident, &field_ident, count_ident, arr_map, match_arms);
error_if_map_not_empty(&field_attr_map);
return;
} else if arr_val_str != "false" {
panic!("array must be `true` or `false` but got {arr_val_str}");
}
if let Some(arr_map) = field_attr_map.remove("array_map") {
let count_ident = field_attr_map
.remove("count")
.expect("missing 'count' attribute");

process_array(
struct_ident,
&field_ident,
count_ident,
arr_map,
convert,
match_arms,
);
error_if_map_not_empty(&field_attr_map);
return;
}

// We match a single literal, like `OwnerPartId`
Expand All @@ -148,11 +152,6 @@ fn handle_field(
None => create_key_name(&field_ident),
};

let convert = match field_attr_map.remove("convert") {
Some(conv_fn) => quote! { .map_err(Into::into).and_then(#conv_fn) },
None => TokenStream2::new(),
};

// If we haven't consumed all attributes, yell
error_if_map_not_empty(&field_attr_map);

Expand Down Expand Up @@ -327,7 +326,7 @@ fn parse_attr_map(map: TokenTree) -> Vec<(Ident, Ident)> {
}

fn error_if_map_not_empty(map: &BTreeMap<String, TokenTree>) {
assert!(map.is_empty(), "unexpected pairs {map:?}");
assert!(map.is_empty(), "unexpected attribute values {map:?}");
}

/// Handle special cases `Location` and `LocationFrac`.
Expand Down Expand Up @@ -389,6 +388,7 @@ fn process_array(
field_ident: &Ident,
count_ident_tt: TokenTree,
arr_map_tt: TokenTree,
convert: TokenStream2,
match_stmts: &mut Vec<TokenStream2>,
) {
let TokenTree::Literal(match_pat) = count_ident_tt else {
Expand Down Expand Up @@ -419,13 +419,19 @@ fn process_array(
.parse_as_utf8()
.or_context(|| format!(
"while extracting `{}` (`{}`) for `{}` (via proc macro array)",
String::from_utf8_lossy(match_val), #field_name_str, stringify!(#struct_ident)
String::from_utf8_lossy(match_val), #field_name_str,
stringify!(#struct_ident)
))?;

let parsed_val = val.parse_as_utf8().or_context(|| format!(
"while extracting `{}` (`{}`) for `{}` (via proc macro array)",
String::from_utf8_lossy(match_val), #field_name_str, stringify!(#struct_ident)
))?;
// TODO: scale
let parsed_val = val
.parse_as_utf8()
#convert
.or_context(|| format!(
"while extracting `{}` (`{}`) for `{}` (via proc macro array)",
String::from_utf8_lossy(match_val), #field_name_str,
stringify!(#struct_ident)
))?;

ret.#field_ident[idx - 1].#assign_value = parsed_val;
},
Expand Down
4 changes: 2 additions & 2 deletions altium/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ flate2 = "1.0.30"
image = { version = "0.25.1", default-features = false, features = ["png", "bmp", "jpeg"] }
lazy_static = "1.4.0"
log = "0.4.21"
num-traits = "0.2.18"
num-traits = "0.2.19"
num_enum = "0.7.2"
quick-xml = "0.31.0"
regex = "1.10.4"
rust-ini = "0.21.0"
serde = { version = "1.0.200", features = ["derive"] }
serde = { version = "1.0.203", features = ["derive"] }
serde-xml-rs = "0.6.0"
svg = "0.17.0"
uom = "0.36.0"
Expand Down
13 changes: 12 additions & 1 deletion altium/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::parse::{FromUtf8, ParseUtf8};
const SEP: u8 = b'|';
const KV_SEP: u8 = b'=';

/// Common coordinate type with x and y positions in nnaometers.
/// Common coordinate type with x and y positions in nanometers.
#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Location {
// These are nonpublic because we might want to combine `Location` and `LocationFract`
Expand All @@ -28,6 +28,7 @@ pub struct LocationFract {
pub y_fract: i32,
}

#[allow(clippy::cast_precision_loss)]
impl Location {
#[inline]
pub fn x(self) -> i32 {
Expand All @@ -39,6 +40,16 @@ impl Location {
self.y
}

#[inline]
pub fn x_f32(self) -> f32 {
self.x as f32
}

#[inline]
pub fn y_f32(self) -> f32 {
self.y as f32
}

#[must_use]
pub(crate) fn new(x: i32, y: i32) -> Self {
Self { x, y }
Expand Down
64 changes: 58 additions & 6 deletions altium/src/draw/canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,39 @@ use crate::{
};

/// Generic trait for something that can be drawn to. Beware, unstable!
///
/// These functions are allowed to return without doing anything if needed.
pub trait Canvas: crate::sealed::Sealed {
fn draw_text(&mut self, item: DrawText);
fn draw_line(&mut self, item: DrawLine);
fn draw_polygon(&mut self, item: DrawPolygon);
fn draw_rectangle(&mut self, item: DrawRectangle);
fn draw_image(&mut self, item: DrawImage);
fn draw_polyline(&mut self, item: DrawPolyLine) {
// Fallback to a default with the regular DrawLine

for window in item.locations.windows(2) {
let &[a, b] = window else { unreachable!() };

self.draw_line(DrawLine {
start: a,
end: b,
color: item.color,
width: item.width,
start_cap: item.start_cap,
end_cap: item.end_cap,
line_join: item.line_join,
});
}
}
fn draw_arc(&mut self, item: DrawArc);
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)]
#[derive(Clone, Copy, Debug, Default)]
pub enum LineCap {
/// Stop at the endpoint
#[default]
Expand All @@ -30,7 +50,7 @@ pub enum LineCap {
/// How two lines should be combined
///
/// See <https://svgwg.org/specs/strokes/#StrokeLinejoinProperty> for more.
#[derive(Clone, Debug, Default)]
#[derive(Clone, Copy, Debug, Default)]
pub enum LineJoin {
/// Sharp corners
#[default]
Expand All @@ -44,7 +64,7 @@ pub enum LineJoin {
}

/// Helper struct to write some text
#[derive(Clone, Debug, Default)]
#[derive(Clone, Copy, Debug, Default)]
pub struct DrawText<'a> {
pub x: i32,
pub y: i32,
Expand All @@ -56,7 +76,7 @@ pub struct DrawText<'a> {
pub rotation: Rotation90,
}

#[derive(Clone, Debug, Default)]
#[derive(Clone, Copy, Debug, Default)]
pub struct DrawLine {
pub start: Location,
pub end: Location,
Expand All @@ -67,7 +87,17 @@ pub struct DrawLine {
pub line_join: LineJoin,
}

#[derive(Clone, Debug, Default)]
#[derive(Clone, Copy, Debug, Default)]
pub struct DrawPolyLine<'a> {
pub locations: &'a [Location],
pub color: Rgb,
pub width: u32,
pub start_cap: LineCap,
pub end_cap: LineCap,
pub line_join: LineJoin,
}

#[derive(Clone, Copy, Debug, Default)]
pub struct DrawRectangle {
pub x: i32,
pub y: i32,
Expand All @@ -76,14 +106,36 @@ pub struct DrawRectangle {
pub fill_color: Rgb,
pub stroke_color: Rgb,
pub stroke_width: u32,
pub start_cap: LineCap,
pub end_cap: LineCap,
pub line_join: LineJoin,
}

#[derive(Clone, Debug, Default)]
#[derive(Clone, Copy, Debug, Default)]
pub struct DrawPolygon<'a> {
pub locations: &'a [Location],
pub fill_color: Rgb,
pub stroke_color: Rgb,
pub stroke_width: u32,
pub start_cap: LineCap,
pub end_cap: LineCap,
pub line_join: LineJoin,
}

#[derive(Clone, Copy, Debug, Default)]
pub struct DrawArc {
pub center: Location,
pub x_radius: u32,
pub y_radius: u32,
/// Radians
pub start_angle: f32,
/// Radians
pub end_angle: f32,
pub width: u32,
pub color: Rgb,
pub start_cap: LineCap,
pub end_cap: LineCap,
pub line_join: LineJoin,
}

pub struct DrawImage {}
2 changes: 2 additions & 0 deletions altium/src/draw/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ mod svg;

pub use canvas::{
Canvas,
DrawArc,
DrawImage,
DrawLine,
DrawPolyLine,
DrawPolygon,
DrawRectangle,
DrawText,
Expand Down
4 changes: 4 additions & 0 deletions altium/src/draw/svg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ impl Canvas for SvgCtx {
let cmt = svg::node::Comment::new(comment);
self.add_node(cmt);
}

fn draw_arc(&mut self, _item: super::DrawArc) {
// todo!()
}
}

/// Estimate the size of text
Expand Down
17 changes: 14 additions & 3 deletions altium/src/sch/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ impl Component {
let ctx = SchDrawCtx {
fonts: &self.fonts,
storage: &self.storage,
name: &self.name,
};

for record in &self.records {
Expand Down Expand Up @@ -94,8 +95,16 @@ impl Component {
}

/// Iterate over all records in this component
pub fn records(&self) -> impl Iterator<Item = &SchRecord> {
self.records.iter()
pub fn records(&self) -> &[SchRecord] {
self.records.as_slice()
}
}

impl Draw for &[SchRecord] {
type Context<'a> = SchDrawCtx<'a>;

fn draw<C: Canvas>(&self, canvas: &mut C, ctx: &Self::Context<'_>) {
self.iter().for_each(|r| r.draw(canvas, ctx));
}
}

Expand All @@ -106,7 +115,9 @@ impl Draw for Component {
let ctx = SchDrawCtx {
fonts: &self.fonts,
storage: &self.storage,
name: &self.name,
};
self.records.iter().for_each(|r| r.draw(canvas, &ctx));

self.records.as_slice().draw(canvas, &ctx);
}
}
Loading

0 comments on commit fcce3c7

Please sign in to comment.