mirror of
https://github.com/storytold/cadcraft.git
synced 2026-11-10 12:30:16 -06:00
* chore(clippy): enforce workspace Rust quality gates * refactor(doc): use explicit entity imports * refactor(constraints): split residual evaluation * refactor(fonts): split mtext layout * refactor(render): use explicit entity imports * fix(fonts): retain height for empty paragraphs * fix(doc): repair dimension and dynamic block links * refactor(render): split entity drawing helpers * docs: document workspace lint inheritance * refactor(render): isolate dimension geometry * refactor(engine): use explicit imports * refactor(engine): split complex edit paths into helpers * refactor(io): split DXF read and write helpers * refactor(ui): split canvas interaction and drawing * refactor(ui): split command and drawing helpers * test(i18n): split locale preference checks * fix(ui): use current egui scope id API * fix(dim): preserve text offset and ellipse grip behavior
125 lines
5.6 KiB
Rust
125 lines
5.6 KiB
Rust
use super::draw::entity;
|
|
use super::props::{resolve, resolve_common, transparency};
|
|
use super::text_entity::{mtext, place_text_entity};
|
|
use super::{Builder, Color, Ctx, Entity, EntityKind, Ink, Lineweight, Mat3, Vec2};
|
|
|
|
pub(crate) fn sub_ctx<'a>(ctx: &Ctx<'a>, e: &Entity, m: Mat3) -> Ctx<'a> {
|
|
let layer = if e.common.layer == "0" { ctx.block_layer.clone() } else { Some(e.common.layer.clone()) };
|
|
let color = match e.common.color {
|
|
Color::ByBlock => ctx.block_color,
|
|
Color::ByLayer => ctx.layer_color(layer.as_deref().unwrap_or("0")).unwrap_or(Color::Index(7)),
|
|
c => c,
|
|
};
|
|
let lw = match e.common.lineweight {
|
|
Lineweight::ByBlock => ctx.block_lw,
|
|
Lineweight::ByLayer => ctx.d.layer(layer.as_deref().unwrap_or("0")).map(|l| l.lineweight).unwrap_or(Lineweight::Default),
|
|
x => x,
|
|
};
|
|
let lt = match e.common.linetype.to_ascii_lowercase().as_str() {
|
|
"byblock" => ctx.block_ltype.clone(),
|
|
"bylayer" => ctx.d.layer(layer.as_deref().unwrap_or("0")).map(|l| l.linetype.clone()).unwrap_or_else(|| "Continuous".into()),
|
|
_ => e.common.linetype.clone(),
|
|
};
|
|
Ctx {
|
|
d: ctx.d,
|
|
xf: ctx.xf.then_before(m),
|
|
block_color: color,
|
|
block_layer: layer,
|
|
block_lw: lw,
|
|
block_ltype: lt,
|
|
depth: ctx.depth + 1,
|
|
top: ctx.top,
|
|
frozen: ctx.frozen,
|
|
vp_colors: ctx.vp_colors,
|
|
// The reference's own linetype scale (group 48) multiplies that of the objects inside,
|
|
// as LTSCALE does for the drawing (tests/block_ltscale.rs).
|
|
lt_factor: ctx.lt_factor * if e.common.ltscale.is_finite() && e.common.ltscale > 0.0 { e.common.ltscale } else { 1.0 },
|
|
block_pstyle: ctx.block_pstyle,
|
|
// The reference's own, set by the caller (`Builder::transparency` while it draws).
|
|
block_transparency: ctx.block_transparency,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn insert(b: &mut Builder, ctx: &Ctx, e: &Entity, ins: &cadcraft_doc::Insert, rgb: Ink) {
|
|
if ctx.depth >= cadcraft_doc::MAX_BLOCK_DEPTH {
|
|
return;
|
|
}
|
|
if let Some(blk) = ctx.d.block(&ins.block) {
|
|
let cols = ins.cols.clamp(1, 10_000);
|
|
let rows = ins.rows.clamp(1, 10_000);
|
|
'copies: for r in 0..rows {
|
|
for c in 0..cols {
|
|
if !b.expand() {
|
|
break 'copies;
|
|
}
|
|
let off = Vec2::new(ins.col_spacing * f64::from(c), ins.row_spacing * f64::from(r)).rotate(ins.rotation);
|
|
let m = Mat3::translate(off).then_before(ins.transform(blk.base.xy()));
|
|
let sub = Ctx { block_pstyle: b.named, block_transparency: b.transparency, ..sub_ctx(ctx, e, m) };
|
|
for be in blk.entities.iter() {
|
|
// Constant/visible attribute definitions inside blocks are not drawn; attribs are.
|
|
if matches!(be.kind, EntityKind::AttDef(_)) {
|
|
continue;
|
|
}
|
|
if !b.expand() {
|
|
break 'copies;
|
|
}
|
|
entity(b, &sub, be);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if b.opts.text {
|
|
// ATTMODE (ATTDISP): 0 shows no attributes, 1 the visible ones, 2 all of them.
|
|
let attmode = ctx.d.header.i64("ATTMODE", 1);
|
|
// The reference's transparency; each attribute draws with its own.
|
|
let reference = b.transparency;
|
|
for a in &ins.attribs {
|
|
if attmode == 0 || (a.invisible && attmode != 2) {
|
|
continue;
|
|
}
|
|
// A multiline attribute is drawn as its multiline text.
|
|
let multiline = a.multiline_text();
|
|
let sh = if multiline.is_none() { Some(place_text_entity(ctx.d, &a.text, &a.text.value).0) } else { None };
|
|
// Properties of the attribute's definition (as AutoCAD copies them to the attribute
|
|
// on insertion), except those set on the attribute itself: ByBlock = the reference's,
|
|
// layer 0 = the reference's layer.
|
|
let def = ctx.d.block(&ins.block).and_then(|blk| blk.entities.iter().find(|be| attdef_tag(be, &a.tag)));
|
|
let (ink, lw, shown, t) = match (def, a.props.is_empty()) {
|
|
(None, true) => (rgb, resolve(ctx, e, b.plotting).1, true, reference),
|
|
(def, _) => {
|
|
let c = a.props.over(&def.map(|d| d.common.clone()).unwrap_or_else(by_block));
|
|
let (ink, lw, _, _, shown, layer_t) = resolve_common(&sub_ctx(ctx, e, Mat3::IDENTITY), &c, b.plotting);
|
|
let t = if b.opts.transparency { transparency(c.transparency, layer_t, reference) } else { 0 };
|
|
(ink, lw, shown, t)
|
|
}
|
|
};
|
|
if shown {
|
|
b.transparency = t;
|
|
let lw = if b.opts.lineweights { lw } else { 0.0 };
|
|
match (&multiline, &sh) {
|
|
(Some(m), _) => mtext(b, ctx, m, ink, lw),
|
|
(None, Some(sh)) => b.shaped(ctx, ink, lw, sh),
|
|
(None, None) => {}
|
|
}
|
|
}
|
|
}
|
|
b.transparency = reference;
|
|
}
|
|
}
|
|
|
|
/// Properties that follow the block reference's (an attribute without a definition).
|
|
fn by_block() -> cadcraft_doc::Common {
|
|
cadcraft_doc::Common {
|
|
color: Color::ByBlock,
|
|
linetype: "ByBlock".into(),
|
|
lineweight: Lineweight::ByBlock,
|
|
transparency: cadcraft_doc::Transparency::ByBlock,
|
|
..cadcraft_doc::Common::default()
|
|
}
|
|
}
|
|
|
|
/// The attribute definition with tag `tag` (any case).
|
|
fn attdef_tag(e: &Entity, tag: &str) -> bool {
|
|
matches!(&e.kind, EntityKind::AttDef(d) if d.tag.eq_ignore_ascii_case(tag))
|
|
}
|