mirror of
https://github.com/storytold/cadcraft.git
synced 2026-11-03 00:42:46 -06:00
A DIMENSION moved by an older AutoCAD keeps its *D block contents where they were and the move in DXF group 12, and is drawn translated by the insertion point. Group 12 was never read into Dimension, so the block rendered at its pre-move position and the extents reached there too. Read group 12 into a new Dimension::insertion_point, draw the block translated by it, move its bounds with it in the extents, test the block's text where it draws, and write group 12 back whenever the block written is the one read from the file. Regenerated geometry (edits drop the block link) sits at its definition points, so no offset is kept or written for it. Co-authored-by: rahul05ranjan <11793455+rahul05ranjan@users.noreply.github.com>
This commit is contained in:
@@ -52,6 +52,11 @@ pub struct Dimension {
|
||||
/// Anonymous block with the rendered geometry (from files); regenerated when edited.
|
||||
#[serde(default)]
|
||||
pub block: Option<String>,
|
||||
/// Insertion point (DXF group 12): a dimension moved by an older AutoCAD keeps its offset
|
||||
/// here with the `*D` block's contents left where they were, and is drawn translated by it.
|
||||
/// Zero once the geometry is regenerated from the definition points.
|
||||
#[serde(default)]
|
||||
pub insertion_point: Vec3,
|
||||
/// Per-dimension style overrides (DIMOVERRIDE): DimStyle field names (camelCase) → values,
|
||||
/// merged over the named style.
|
||||
#[serde(default, skip_serializing_if = "serde_json::Map::is_empty")]
|
||||
|
||||
@@ -212,6 +212,10 @@ fn bounds_in<'a>(d: &'a Drawing, e: &Entity, depth: usize, blocks: &mut HashMap<
|
||||
inner
|
||||
}
|
||||
};
|
||||
// The block draws translated by the insertion point (group 12), so its bounds
|
||||
// move with it (#2634); the cache above keeps the block's own bounds.
|
||||
let off = dm.insertion_point.xy();
|
||||
let inner = if off == Vec2::ZERO { inner } else { Bounds2 { min: inner.min + off, max: inner.max + off } };
|
||||
b = b.union(&inner);
|
||||
}
|
||||
b
|
||||
|
||||
@@ -55,6 +55,44 @@ fn frozen_layers_are_left_out_of_extents_and_layers_turned_off_are_not() {
|
||||
assert_eq!(d.extents(&Space::Model).max, Vec2::new(1.0, 1.0));
|
||||
}
|
||||
|
||||
/// DXF group 12: a dimension moved by an older AutoCAD keeps its `*D` block where it was and
|
||||
/// the move in the insertion point, and the block draws translated by it. Extents follow the
|
||||
/// drawing, not the stale block contents (#2634).
|
||||
#[test]
|
||||
fn dimension_insertion_point_moves_its_block_in_extents() {
|
||||
let moved = |ins: Vec3| {
|
||||
let mut d = Drawing::new_metric();
|
||||
let mut blk = Block::new("*D1");
|
||||
blk.entities.push(on_layer("0", line((850.0, 920.0), (860.0, 920.0)), 2));
|
||||
d.blocks.insert("*D1".into(), std::sync::Arc::new(blk));
|
||||
let dm = Dimension {
|
||||
kind: DimKind::Aligned,
|
||||
defpt: Vec3::new(5.0, 1.0, 0.0),
|
||||
text_mid: Vec3::ZERO,
|
||||
p13: Vec3::ZERO,
|
||||
p14: Vec3::new(10.0, 0.0, 0.0),
|
||||
p15: Vec3::ZERO,
|
||||
p16: Vec3::ZERO,
|
||||
text: String::new(),
|
||||
style: "Standard".into(),
|
||||
measurement: 0.0,
|
||||
text_rotation: 0.0,
|
||||
user_text_pos: false,
|
||||
block: Some("*D1".into()),
|
||||
insertion_point: ins,
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
};
|
||||
d.add(&Space::Model, Common::default(), EntityKind::Dimension(dm)).unwrap();
|
||||
d
|
||||
};
|
||||
// Without the offset the block's far-away contents reach the extents.
|
||||
assert!(moved(Vec3::ZERO).extents(&Space::Model).max.x > 800.0);
|
||||
// With it, the block draws at the definition points and the extents stay at the drawing.
|
||||
assert!(moved(Vec3::new(-850.0, -920.0, 0.0)).extents(&Space::Model).max.x < 20.0);
|
||||
}
|
||||
|
||||
/// ZOOM Extents in AutoCAD 2024 (accoreconsole, EXTMIN/EXTMAX read back; AutoCAD pads them by a
|
||||
/// few hundredths): a layer turned off counts and a frozen one does not, for an entity, for an
|
||||
/// entity inside a block, and for a block reference itself.
|
||||
@@ -637,6 +675,7 @@ fn invisible_dimblock_entity_is_ignored_in_dimension_extents() {
|
||||
overrides: Default::default(),
|
||||
assoc: vec![],
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -22,6 +22,7 @@ pub(super) fn dim(s: &Session, kind: DimKind, defpt: Vec2, p13: Vec2, p14: Vec2,
|
||||
overrides: s.doc().map(|d| d.dim_overrides()).unwrap_or_default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ fn dim(kind: DimKind, p13: Vec2, p14: Vec2, defpt: Vec2) -> EntityKind {
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -182,6 +183,7 @@ pub fn bracket() -> Drawing {
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
}),
|
||||
);
|
||||
add(
|
||||
@@ -203,6 +205,7 @@ pub fn bracket() -> Drawing {
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -117,11 +117,15 @@ fn in_text(d: &Drawing, e: &Entity, p: Vec2) -> bool {
|
||||
// A dimension with a block (as AutoCAD saves them) is drawn from that block: its text is
|
||||
// the text there, not the text the dimension's own geometry would place.
|
||||
EntityKind::Dimension(dm) => match dm.block.as_ref().and_then(|n| d.block(n)) {
|
||||
Some(blk) => blk.entities.iter().any(|be| match &be.kind {
|
||||
EntityKind::MText(t) => in_box(cadcraft_render::layout_mtext_entity(d, t).strokes.into_iter().flatten(), t.rotation, p),
|
||||
EntityKind::Text(_) => entity_bounds(d, be, 0).contains(p),
|
||||
_ => false,
|
||||
}),
|
||||
// The block draws translated by the insertion point (group 12): test there (#2634).
|
||||
Some(blk) => {
|
||||
let p = p - dm.insertion_point.xy();
|
||||
blk.entities.iter().any(|be| match &be.kind {
|
||||
EntityKind::MText(t) => in_box(cadcraft_render::layout_mtext_entity(d, t).strokes.into_iter().flatten(), t.rotation, p),
|
||||
EntityKind::Text(_) => entity_bounds(d, be, 0).contains(p),
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
None => {
|
||||
let g = cadcraft_render::dimension_in(d, dm);
|
||||
in_box(g.text.iter().flatten().chain(g.text_glyphs.iter().flatten().flatten()).copied(), g.text_angle, p)
|
||||
|
||||
@@ -208,6 +208,7 @@ fn derived_dimension_fields_are_not_differences() {
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
};
|
||||
let mut s = Session::new();
|
||||
s.doc_mut().unwrap().add(&Space::Model, Common::default(), EntityKind::Dimension(dim(30.0, 7.0))).unwrap();
|
||||
|
||||
@@ -314,6 +314,7 @@ fn a_named_block_whose_dimension_draws_another_block_shows_the_other_definition(
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
};
|
||||
let blocks = |dim_end: f64, first: u64| {
|
||||
let mut part = Block::new("PART");
|
||||
@@ -400,6 +401,7 @@ fn a_named_block_whose_dimension_block_has_another_generated_name_keeps_this_dra
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
};
|
||||
let mut part = Block::new("PART");
|
||||
part.entities.push(Entity::new(Handle(first), EntityKind::Dimension(dim)));
|
||||
@@ -514,6 +516,7 @@ fn a_dimension_block_copy_keeps_its_geometry_when_the_snapshot_is_saved() {
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
};
|
||||
let mut s = Session::new();
|
||||
let mut drawn = Block::new("*D1");
|
||||
@@ -833,6 +836,7 @@ fn a_dimension_whose_block_is_past_the_nesting_limit_is_drawn_again() {
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
};
|
||||
for k in 1..=MAX_BLOCK_DEPTH {
|
||||
let mut b = Block::new(&format!("B{k}"));
|
||||
|
||||
@@ -197,6 +197,7 @@ pub(super) fn entity_with_table_budget(kind: &str, tags: &[Tag], table_cells_lef
|
||||
text_rotation: t.fd(53, 0.0).to_radians(),
|
||||
user_text_pos: t.i(70).unwrap_or(0) & 128 != 0,
|
||||
block: t.s(2).filter(|s| !s.is_empty()),
|
||||
insertion_point: t.p(12),
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
|
||||
@@ -399,6 +399,12 @@ fn write_dimension_entity(w: &mut W, d: &Drawing, e: &Entity, owner: &str, paper
|
||||
let style = d.dim_style(&dm.style).cloned().unwrap_or_default();
|
||||
let g = cadcraft_render::dimension_geometry(dm, &style, d.header.f64("DIMSCALE", 1.0));
|
||||
w.p(11, if dm.user_text_pos { dm.text_mid } else { g.text_pos.to3(0.0) });
|
||||
// Group 12: a block read from a file keeps the dimension's moved-by offset (AutoCAD
|
||||
// draws the block translated by it); regenerated geometry already sits where the
|
||||
// definition points place it, so it writes no offset.
|
||||
if dm.block.is_some() && dm.insertion_point != Vec3::ZERO {
|
||||
w.p(12, dm.insertion_point);
|
||||
}
|
||||
let ty: i64 = match dm.kind {
|
||||
DimKind::Linear { .. } => 0,
|
||||
DimKind::Aligned => 1,
|
||||
|
||||
@@ -98,6 +98,7 @@ fn sample() -> Drawing {
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -558,6 +559,7 @@ fn dim(kind: DimKind, p13: Vec3, p14: Vec3) -> Dimension {
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ fn dim(kind: DimKind, defpt: Vec3, p13: Vec3, p14: Vec3, p15: Vec3, p16: Vec3, a
|
||||
overrides: Default::default(),
|
||||
assoc,
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,7 @@ fn s25(kind: DimKind, p13: Vec3, p14: Vec3, defpt: Vec3) -> Drawing {
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
};
|
||||
d.add(&Space::Model, Common::default(), EntityKind::Dimension(dm)).unwrap();
|
||||
d
|
||||
|
||||
@@ -27,6 +27,7 @@ fn linear(a: [f64; 2], b: [f64; 2], at: [f64; 2]) -> Dimension {
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +159,47 @@ fn reopened_dimension_reuses_its_block_name_and_handles() {
|
||||
assert_eq!(again.handseed, back.handseed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_moved_dimension_keeps_its_group_12_offset() {
|
||||
let mut d = Drawing::new_metric();
|
||||
// What an older AutoCAD left: the block contents where the dimension was measured, the
|
||||
// move kept in group 12 (insertion point), as #2634's drawing has it.
|
||||
let mut b = Block::new("*D3");
|
||||
let h = d.new_handle();
|
||||
b.entities.push(Entity::new(h, EntityKind::Line(Line { a: Vec3::new(150.0, 80.0, 0.0), b: Vec3::new(160.0, 80.0, 0.0) })));
|
||||
d.blocks.insert("*D3".into(), Arc::new(b));
|
||||
let mut dm = linear([0.0, 0.0], [10.0, 0.0], [5.0, 3.0]);
|
||||
dm.block = Some("*D3".into());
|
||||
dm.insertion_point = Vec3::new(-150.0, -80.0, 0.0);
|
||||
let h = d.add(&Space::Model, Common::default(), EntityKind::Dimension(dm)).unwrap();
|
||||
|
||||
// The block is written as read and the offset round-trips as group 12.
|
||||
let back = cadcraft_io::read_dxf(cadcraft_io::write_dxf(&d).as_bytes()).unwrap();
|
||||
let Some(EntityKind::Dimension(dm)) = back.entity(h).map(|e| &e.kind) else { panic!("dimension lost") };
|
||||
assert_eq!(dm.insertion_point, Vec3::new(-150.0, -80.0, 0.0));
|
||||
assert_eq!(dm.block.as_deref(), Some("*D3"));
|
||||
let p = pairs(&cadcraft_io::write_dxf(&d));
|
||||
let at = p.iter().position(|(c, v)| *c == 0 && v == "DIMENSION").unwrap();
|
||||
let own = &p[at..p[at + 1..].iter().position(|(c, _)| *c == 0).map(|k| at + 1 + k).unwrap()];
|
||||
assert!(own.iter().any(|(c, v)| *c == 12 && v.parse::<f64>() == Ok(-150.0)), "no group 12 x");
|
||||
assert!(own.iter().any(|(c, v)| *c == 22 && v.parse::<f64>() == Ok(-80.0)), "no group 12 y");
|
||||
|
||||
// Edited (edits drop the block link): geometry regenerated from the definition points,
|
||||
// so the save writes no offset.
|
||||
let mut edited = back.clone();
|
||||
edited
|
||||
.modify_entity(h, |e| {
|
||||
if let EntityKind::Dimension(dm) = &mut e.kind {
|
||||
dm.block = None;
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
let q = pairs(&cadcraft_io::write_dxf(&edited));
|
||||
let at = q.iter().position(|(c, v)| *c == 0 && v == "DIMENSION").unwrap();
|
||||
let own = &q[at..q[at + 1..].iter().position(|(c, _)| *c == 0).map(|k| at + 1 + k).unwrap()];
|
||||
assert!(!own.iter().any(|(c, _)| *c == 12), "regenerated geometry keeps a stale offset");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hostile_handle_seed_does_not_panic() {
|
||||
let mut d = Drawing::new_metric();
|
||||
|
||||
@@ -34,6 +34,7 @@ fn height_written(style_scale: f64, overrides: serde_json::Value) -> f64 {
|
||||
overrides: overrides.as_object().cloned().unwrap_or_default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
};
|
||||
d.add(&Space::Model, Common::default(), EntityKind::Dimension(dm)).unwrap();
|
||||
block_mtext_height(&cadcraft_io::write_dxf(&d))
|
||||
@@ -73,6 +74,7 @@ fn angular_drawing(separator_override: Option<&str>) -> Drawing {
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
};
|
||||
if let Some(separator) = separator_override {
|
||||
dm.overrides.insert("DIMDSEP".into(), serde_json::json!(separator));
|
||||
|
||||
@@ -24,6 +24,7 @@ fn dim(kind: DimKind, marks: DimMarks) -> EntityKind {
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks,
|
||||
insertion_point: Vec3::ZERO,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ fn dimension(overrides: serde_json::Value) -> EntityKind {
|
||||
overrides: overrides.as_object().cloned().unwrap_or_default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -174,6 +174,7 @@ fn linear() -> Dimension {
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -118,6 +118,7 @@ pub(crate) fn dimensions() -> Vec<Case> {
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: DimMarks::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
}),
|
||||
);
|
||||
let mut block = Block::new("*D42");
|
||||
|
||||
@@ -1034,6 +1034,7 @@ fn add_associated_dimension(d: &mut Drawing, a: cadcraft_doc::Handle, b: cadcraf
|
||||
overrides: Default::default(),
|
||||
assoc: vec![at("p13", a), at("p14", b)],
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
};
|
||||
d.add(&Space::Model, Common::default(), EntityKind::Dimension(dm)).expect("add dimension");
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ fn dimension(label: &str) -> Dimension {
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ fn lin(p1: (f64, f64), p2: (f64, f64), on: (f64, f64), rot: f64) -> Dimension {
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -313,7 +313,10 @@ fn draw_dimension(b: &mut Builder, ctx: &Ctx, e: &Entity, dm: &cadcraft_doc::Dim
|
||||
if let Some(blk) = dm.block.as_ref().and_then(|n| ctx.d.block(n))
|
||||
&& ctx.depth < cadcraft_doc::MAX_BLOCK_DEPTH
|
||||
{
|
||||
let sub = Ctx { block_pstyle: b.named, block_transparency: b.transparency, ..sub_ctx(ctx, e, Mat3::IDENTITY) };
|
||||
// DXF group 12: a moved dimension keeps its `*D` block where it was and the offset in
|
||||
// the insertion point, so the block draws translated by it (#2634).
|
||||
let off = dm.insertion_point.xy();
|
||||
let sub = Ctx { block_pstyle: b.named, block_transparency: b.transparency, ..sub_ctx(ctx, e, Mat3::translate(off)) };
|
||||
// DIMTFILL: the block's MTEXT carries no background mask here, so a background
|
||||
// around each of the block's texts is drawn before the block's first text or first
|
||||
// line of the text's marks, except for a text that a SOLID of the block covers.
|
||||
@@ -333,6 +336,10 @@ fn draw_dimension(b: &mut Builder, ctx: &Ctx, e: &Entity, dm: &cadcraft_doc::Dim
|
||||
.take(MAX_FILL_TEXTS)
|
||||
.filter(|be| !covered(be))
|
||||
.filter_map(|be| block_text_box(ctx.d, be, gap))
|
||||
.map(|mut corners| {
|
||||
corners.iter_mut().for_each(|c| *c += off);
|
||||
corners
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
let mut fill_pending = !boxes.is_empty();
|
||||
|
||||
@@ -370,6 +370,7 @@ fn lin_dim() -> cadcraft_doc::Dimension {
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ fn dim(kind: DimKind) -> Dimension {
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ fn text(vars: serde_json::Value) -> (String, String) {
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
};
|
||||
let mut st = DimStyle::default();
|
||||
let rejected = st.apply_fields(vars.as_object().unwrap());
|
||||
|
||||
@@ -26,6 +26,7 @@ fn dimension() -> Dimension {
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ fn linear(len: f64) -> Dimension {
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ fn dimensions_take_the_viewport_layer_colour() {
|
||||
overrides: Default::default(),
|
||||
assoc: Vec::new(),
|
||||
marks: Default::default(),
|
||||
insertion_point: Vec3::ZERO,
|
||||
};
|
||||
d.add(&Space::Model, on("A"), EntityKind::Dimension(dim)).unwrap();
|
||||
d.add(&Space::Model, on("A"), EntityKind::Line(Line { a: Vec3::new(0.0, -1.0, 0.0), b: Vec3::new(10.0, -1.0, 0.0) })).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user