forked from 1b-agent/opencadstudio-upstream
fix(plot): correct layout hatch output
Keep Layout output on the selected physical sheet and preserve hatch lineweights and plot styles through paper, viewport, and nested block paths.\n\nRefs #607
This commit is contained in:
1 parent
a66940a3f0
commit
f0c1be0db1
12 files changed
+258
-57
No files matched your search
@@ -410,6 +410,12 @@ impl DocumentTab {
|
|||||||
l.max_limits = (297.0, 210.0);
|
l.max_limits = (297.0, 210.0);
|
||||||
l.min_extents = (0.0, 0.0, 0.0);
|
l.min_extents = (0.0, 0.0, 0.0);
|
||||||
l.max_extents = (297.0, 210.0, 0.0);
|
l.max_extents = (297.0, 210.0, 0.0);
|
||||||
|
l.paper_width = 297.0;
|
||||||
|
l.paper_height = 210.0;
|
||||||
|
l.plot_paper_units = 1;
|
||||||
|
l.plot_scale_numerator = 1.0;
|
||||||
|
l.plot_scale_denominator = 1.0;
|
||||||
|
l.paper_size = "ISO_A4_(297.00_x_210.00_MM)".into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-7
@@ -2294,9 +2294,23 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn layout_plot_params_for(&self, plot_area: &str) -> LayoutPlotParams {
|
fn layout_plot_params_for(&self, plot_area: &str) -> LayoutPlotParams {
|
||||||
|
use crate::io::paper_sizes::{sheet_mm, Orientation, PaperSize};
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
let scene = &self.tabs[i].scene;
|
let scene = &self.tabs[i].scene;
|
||||||
let paper_space = scene.current_layout != "Model";
|
let paper_space = scene.current_layout != "Model";
|
||||||
|
let selected_paper = match self.plot_dialog.paper.as_str() {
|
||||||
|
"A3" => PaperSize::A3,
|
||||||
|
"A2" => PaperSize::A2,
|
||||||
|
"A1" => PaperSize::A1,
|
||||||
|
"A0" => PaperSize::A0,
|
||||||
|
_ => PaperSize::A4,
|
||||||
|
};
|
||||||
|
let selected_orientation = if self.plot_dialog.orientation == "Portrait" {
|
||||||
|
Orientation::Portrait
|
||||||
|
} else {
|
||||||
|
Orientation::Landscape
|
||||||
|
};
|
||||||
|
let selected_sheet = sheet_mm(selected_paper, selected_orientation);
|
||||||
let (source_wires, hatches, wipeouts, mut group_splits) =
|
let (source_wires, hatches, wipeouts, mut group_splits) =
|
||||||
plot_scene_content(
|
plot_scene_content(
|
||||||
scene,
|
scene,
|
||||||
@@ -2380,11 +2394,24 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||||||
target_y / scale - min_y,
|
target_y / scale - min_y,
|
||||||
)
|
)
|
||||||
} else if plot_layout {
|
} else if plot_layout {
|
||||||
// Layout is the one Paper-only plot mode: reproduce the sheet
|
// Map the complete paper-space sheet to the physical paper
|
||||||
// exactly. Its lower-left paper bound maps to PDF (0, 0);
|
// selected in the dialog. Layout bounds can be stored in mm,
|
||||||
// persisted plot origins/centering are already represented by
|
// inches, or carry incomplete legacy metadata; deriving the
|
||||||
// the layout geometry and must not shift the sheet again.
|
// scale from the visible sheet avoids applying a stale unit
|
||||||
(mm_per_unit, -x0, -y0)
|
// factor twice while preserving loaded layouts exactly when
|
||||||
|
// their metadata is valid.
|
||||||
|
let bounds_w = (x1 - x0).max(1e-9);
|
||||||
|
let bounds_h = (y1 - y0).max(1e-9);
|
||||||
|
let scale = (selected_sheet.0 / bounds_w)
|
||||||
|
.min(selected_sheet.1 / bounds_h)
|
||||||
|
.max(1e-9);
|
||||||
|
let target_x = (selected_sheet.0 - bounds_w * scale) * 0.5;
|
||||||
|
let target_y = (selected_sheet.1 - bounds_h * scale) * 0.5;
|
||||||
|
(
|
||||||
|
scale,
|
||||||
|
target_x / scale - x0,
|
||||||
|
target_y / scale - y0,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// Legacy direct callers still get dialog positioning. Normal
|
// Legacy direct callers still get dialog positioning. Normal
|
||||||
// Display/Window paths use `area_plot_job` instead.
|
// Display/Window paths use `area_plot_job` instead.
|
||||||
@@ -2401,9 +2428,14 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||||||
};
|
};
|
||||||
(scale, target_x / scale - x0, target_y / scale - y0)
|
(scale, target_x / scale - x0, target_y / scale - y0)
|
||||||
};
|
};
|
||||||
|
let (base_page_w, base_page_h) = if plot_layout {
|
||||||
|
selected_sheet
|
||||||
|
} else {
|
||||||
|
(paper_w, paper_h)
|
||||||
|
};
|
||||||
let (page_w, page_h) = match rotation {
|
let (page_w, page_h) = match rotation {
|
||||||
90 | 270 => (paper_h, paper_w),
|
90 | 270 => (base_page_h, base_page_w),
|
||||||
_ => (paper_w, paper_h),
|
_ => (base_page_w, base_page_h),
|
||||||
};
|
};
|
||||||
// Layout plots keep the full physical sheet as the PDF page, but
|
// Layout plots keep the full physical sheet as the PDF page, but
|
||||||
// ink is restricted to the device's printable rectangle. Extents
|
// ink is restricted to the device's printable rectangle. Extents
|
||||||
|
|||||||
@@ -4199,6 +4199,12 @@ impl OpenCADStudio {
|
|||||||
l.max_limits = (297.0, 210.0);
|
l.max_limits = (297.0, 210.0);
|
||||||
l.min_extents = (0.0, 0.0, 0.0);
|
l.min_extents = (0.0, 0.0, 0.0);
|
||||||
l.max_extents = (297.0, 210.0, 0.0);
|
l.max_extents = (297.0, 210.0, 0.0);
|
||||||
|
l.paper_width = 297.0;
|
||||||
|
l.paper_height = 210.0;
|
||||||
|
l.plot_paper_units = 1;
|
||||||
|
l.plot_scale_numerator = 1.0;
|
||||||
|
l.plot_scale_denominator = 1.0;
|
||||||
|
l.paper_size = "ISO_A4_(297.00_x_210.00_MM)".into();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-6
@@ -302,6 +302,7 @@ fn build_pdf(
|
|||||||
ox,
|
ox,
|
||||||
oy,
|
oy,
|
||||||
plot_style,
|
plot_style,
|
||||||
|
scale,
|
||||||
options,
|
options,
|
||||||
normal_blend.as_ref(),
|
normal_blend.as_ref(),
|
||||||
);
|
);
|
||||||
@@ -642,6 +643,7 @@ fn emit_hatch(
|
|||||||
ox: f64,
|
ox: f64,
|
||||||
oy: f64,
|
oy: f64,
|
||||||
plot_style: Option<&PlotStyleTable>,
|
plot_style: Option<&PlotStyleTable>,
|
||||||
|
scale: f32,
|
||||||
options: PdfPlotOptions,
|
options: PdfPlotOptions,
|
||||||
normal_blend: Option<&ExtendedGraphicsStateId>,
|
normal_blend: Option<&ExtendedGraphicsStateId>,
|
||||||
) {
|
) {
|
||||||
@@ -659,6 +661,7 @@ fn emit_hatch(
|
|||||||
// Genuine colours are untouched; WIPEOUTS keep their paper-white mask.
|
// Genuine colours are untouched; WIPEOUTS keep their paper-white mask.
|
||||||
let is_wipeout = hatch.name == "WIPEOUT_FILL";
|
let is_wipeout = hatch.name == "WIPEOUT_FILL";
|
||||||
let mut screening = 1.0;
|
let mut screening = 1.0;
|
||||||
|
let mut lw_override = None;
|
||||||
let mut color_overridden = false;
|
let mut color_overridden = false;
|
||||||
if !is_wipeout {
|
if !is_wipeout {
|
||||||
if let Some(table) = plot_style {
|
if let Some(table) = plot_style {
|
||||||
@@ -670,6 +673,9 @@ fn emit_hatch(
|
|||||||
color_overridden = true;
|
color_overridden = true;
|
||||||
}
|
}
|
||||||
screening = table.resolve_screening(hatch.aci);
|
screening = table.resolve_screening(hatch.aci);
|
||||||
|
lw_override = table
|
||||||
|
.resolve_lineweight(hatch.aci)
|
||||||
|
.map(|mm| (mm * MM_TO_PT).max(0.1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -738,12 +744,16 @@ fn emit_hatch(
|
|||||||
HatchPattern::Gradient { color2, .. } => {
|
HatchPattern::Gradient { color2, .. } => {
|
||||||
// PDF gradients are stored in resource dictionaries; for the
|
// PDF gradients are stored in resource dictionaries; for the
|
||||||
// fast path we average the two colours, matching paper_canvas.
|
// fast path we average the two colours, matching paper_canvas.
|
||||||
let second = plotted_color(
|
let second = if color_overridden {
|
||||||
adapt_text_color([color2[0], color2[1], color2[2]]),
|
[r, g, b]
|
||||||
color2[3],
|
} else {
|
||||||
1.0,
|
plotted_color(
|
||||||
options,
|
adapt_text_color([color2[0], color2[1], color2[2]]),
|
||||||
);
|
color2[3],
|
||||||
|
screening,
|
||||||
|
options,
|
||||||
|
)
|
||||||
|
};
|
||||||
let avg = [
|
let avg = [
|
||||||
(r + second[0]) * 0.5,
|
(r + second[0]) * 0.5,
|
||||||
(g + second[1]) * 0.5,
|
(g + second[1]) * 0.5,
|
||||||
@@ -768,6 +778,26 @@ fn emit_hatch(
|
|||||||
icc_profile: None,
|
icc_profile: None,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
let physical = lw_override.unwrap_or_else(|| {
|
||||||
|
if options.object_lineweights {
|
||||||
|
(hatch.line_weight_px * LW_PX_TO_PT).max(0.1)
|
||||||
|
} else {
|
||||||
|
0.1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let divisor = if options.scale_lineweights {
|
||||||
|
1.0
|
||||||
|
} else {
|
||||||
|
scale.max(1e-6)
|
||||||
|
};
|
||||||
|
ops.push(Op::SetOutlineThickness {
|
||||||
|
pt: Pt(physical / divisor),
|
||||||
|
});
|
||||||
|
// Pattern dashes are already materialized by `pattern_segments`.
|
||||||
|
// Clear any linetype left by the preceding paper/model render group.
|
||||||
|
ops.push(Op::SetLineDashPattern {
|
||||||
|
dash: LineDashPattern::default(),
|
||||||
|
});
|
||||||
for [a, b_pt] in segments {
|
for [a, b_pt] in segments {
|
||||||
// `pattern_segments` returns absolute world f64; cancel the offset
|
// `pattern_segments` returns absolute world f64; cancel the offset
|
||||||
// before narrowing, as everywhere else in this file.
|
// before narrowing, as everywhere else in this file.
|
||||||
|
|||||||
@@ -239,6 +239,7 @@ impl HatchCommand {
|
|||||||
name: pat_name.into(),
|
name: pat_name.into(),
|
||||||
color: [0.75, 0.75, 0.75, 0.85],
|
color: [0.75, 0.75, 0.75, 0.85],
|
||||||
aci: 0,
|
aci: 0,
|
||||||
|
line_weight_px: 1.0,
|
||||||
angle_offset: 0.0,
|
angle_offset: 0.0,
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
world_origin: origin,
|
world_origin: origin,
|
||||||
@@ -407,6 +408,7 @@ impl GradientCommand {
|
|||||||
name: self.kind.dxf_name(self.invert).into(),
|
name: self.kind.dxf_name(self.invert).into(),
|
||||||
color: [0.30, 0.60, 0.95, 0.80],
|
color: [0.30, 0.60, 0.95, 0.80],
|
||||||
aci: 0,
|
aci: 0,
|
||||||
|
line_weight_px: 1.0,
|
||||||
angle_offset: 0.0,
|
angle_offset: 0.0,
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
world_origin: origin,
|
world_origin: origin,
|
||||||
@@ -611,6 +613,7 @@ impl CadCommand for BoundaryCommand {
|
|||||||
name: "SOLID".into(),
|
name: "SOLID".into(),
|
||||||
color: [0.45, 0.45, 0.45, 0.60],
|
color: [0.45, 0.45, 0.45, 0.60],
|
||||||
aci: 0,
|
aci: 0,
|
||||||
|
line_weight_px: 1.0,
|
||||||
angle_offset: 0.0,
|
angle_offset: 0.0,
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
world_origin: origin,
|
world_origin: origin,
|
||||||
|
|||||||
+126
-36
@@ -880,11 +880,14 @@ impl Scene {
|
|||||||
// emitted first so LessEqual layering keeps it underneath.
|
// emitted first so LessEqual layering keeps it underneath.
|
||||||
let mut backdrop: Option<HatchModel> = None;
|
let mut backdrop: Option<HatchModel> = None;
|
||||||
if let Some(e) = entity {
|
if let Some(e) = entity {
|
||||||
|
let style = self.render_style(e);
|
||||||
|
m.aci = style.4;
|
||||||
|
m.line_weight_px = style.3;
|
||||||
// A gradient's colour is its first stop (already baked into
|
// A gradient's colour is its first stop (already baked into
|
||||||
// the cached model); only solid / pattern fills take the
|
// the cached model); only solid / pattern fills take the
|
||||||
// entity's resolved colour.
|
// entity's resolved colour.
|
||||||
if !matches!(m.pattern, model::hatch_model::HatchPattern::Gradient { .. }) {
|
if !matches!(m.pattern, model::hatch_model::HatchPattern::Gradient { .. }) {
|
||||||
m.color = self.render_style(e).0;
|
m.color = style.0;
|
||||||
}
|
}
|
||||||
if let EntityType::Hatch(dxf) = e {
|
if let EntityType::Hatch(dxf) = e {
|
||||||
if let Some(bg) = crate::entities::hatch::background_color(dxf) {
|
if let Some(bg) = crate::entities::hatch::background_color(dxf) {
|
||||||
@@ -893,19 +896,38 @@ impl Scene {
|
|||||||
// ByLayer / ByBlock backgrounds resolve through the
|
// ByLayer / ByBlock backgrounds resolve through the
|
||||||
// normal style chain instead of the raw ACI table
|
// normal style chain instead of the raw ACI table
|
||||||
// (#415).
|
// (#415).
|
||||||
b.color = match bg {
|
let (bg_color, bg_aci) = match bg {
|
||||||
acadrust::types::Color::ByLayer => {
|
acadrust::types::Color::ByLayer => {
|
||||||
crate::scene::view::render::layer_render_style(
|
let layer = self.document.layers.get(&dxf.common.layer);
|
||||||
&self.document,
|
let aci = layer
|
||||||
&dxf.common.layer,
|
.and_then(|layer| match &layer.color {
|
||||||
|
acadrust::types::Color::Index(index) => Some(*index),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.unwrap_or(0);
|
||||||
|
(
|
||||||
|
crate::scene::view::render::layer_render_style(
|
||||||
|
&self.document,
|
||||||
|
&dxf.common.layer,
|
||||||
|
)
|
||||||
|
.color,
|
||||||
|
aci,
|
||||||
)
|
)
|
||||||
.color
|
|
||||||
}
|
|
||||||
acadrust::types::Color::ByBlock => self.render_style(e).0,
|
|
||||||
other => {
|
|
||||||
crate::scene::convert::tess_util::aci_to_rgba(&other)
|
|
||||||
}
|
}
|
||||||
|
acadrust::types::Color::ByBlock => (style.0, style.4),
|
||||||
|
acadrust::types::Color::Index(index) => (
|
||||||
|
crate::scene::convert::tess_util::aci_to_rgba(
|
||||||
|
&acadrust::types::Color::Index(index),
|
||||||
|
),
|
||||||
|
index,
|
||||||
|
),
|
||||||
|
other => (
|
||||||
|
crate::scene::convert::tess_util::aci_to_rgba(&other),
|
||||||
|
0,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
b.color = bg_color;
|
||||||
|
b.aci = bg_aci;
|
||||||
b.name = "SOLID".into();
|
b.name = "SOLID".into();
|
||||||
backdrop = Some(b);
|
backdrop = Some(b);
|
||||||
}
|
}
|
||||||
@@ -1120,16 +1142,27 @@ impl Scene {
|
|||||||
// hatches land in the correct world position. A depth guard keeps
|
// hatches land in the correct world position. A depth guard keeps
|
||||||
// a malformed cyclic block reference from looping forever.
|
// a malformed cyclic block reference from looping forever.
|
||||||
let normalize = crate::modules::draw::modify::explode::normalize_insert_entity;
|
let normalize = crate::modules::draw::modify::explode::normalize_insert_entity;
|
||||||
// Block-child colour inheritance sources (#221), kept RAW and
|
// Block-child style inheritance sources (#221), kept raw and
|
||||||
// adapted at the leaf. `ins_color` feeds ByBlock; `l0` (the
|
// adapted at the leaf. The INSERT style feeds ByBlock; `l0` (the
|
||||||
// INSERT's *layer* style) feeds the layer-0 rule. Both chain
|
// INSERT's layer style) feeds the layer-0 rule. Colour, ACI,
|
||||||
// through nested inserts, mirroring expand_insert.
|
// linetype, and lineweight all chain through nested inserts.
|
||||||
let ins_color =
|
let ins_style =
|
||||||
crate::scene::view::render::render_style_for(&self.document, entity).0;
|
crate::scene::view::render::render_style_for(&self.document, entity);
|
||||||
let l0 = crate::scene::view::render::layer_render_style(
|
let l0 = crate::scene::view::render::layer_render_style(
|
||||||
&self.document,
|
&self.document,
|
||||||
&ins.common.layer,
|
&ins.common.layer,
|
||||||
);
|
);
|
||||||
|
let layer_aci = |layer: &str| -> u8 {
|
||||||
|
self.document
|
||||||
|
.layers
|
||||||
|
.get(layer)
|
||||||
|
.and_then(|layer| match &layer.color {
|
||||||
|
acadrust::types::Color::Index(index) => Some(*index),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.unwrap_or(0)
|
||||||
|
};
|
||||||
|
let l0_aci = layer_aci(&ins.common.layer);
|
||||||
let [base_depth, half_gap] = depth_map
|
let [base_depth, half_gap] = depth_map
|
||||||
.get(&ins.common.handle.value())
|
.get(&ins.common.handle.value())
|
||||||
.copied()
|
.copied()
|
||||||
@@ -1145,11 +1178,13 @@ impl Scene {
|
|||||||
&& matches!(e, EntityType::Insert(ni)
|
&& matches!(e, EntityType::Insert(ni)
|
||||||
if crate::scene::annotative::annotative_offscale(&self.document, &ni.common))
|
if crate::scene::annotative::annotative_offscale(&self.document, &ni.common))
|
||||||
};
|
};
|
||||||
|
type ResolvedStyle = ([f32; 4], f32, [f32; 8], f32, u8);
|
||||||
let mut stack: Vec<(
|
let mut stack: Vec<(
|
||||||
EntityType,
|
EntityType,
|
||||||
usize,
|
usize,
|
||||||
[f32; 4],
|
ResolvedStyle,
|
||||||
crate::scene::view::render::InheritStyle,
|
crate::scene::view::render::InheritStyle,
|
||||||
|
u8,
|
||||||
(f32, f32),
|
(f32, f32),
|
||||||
)> = explode_including_dims(ins, &self.document)
|
)> = explode_including_dims(ins, &self.document)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -1158,9 +1193,26 @@ impl Scene {
|
|||||||
crate::scene::annotative::entity_for_active_context(&self.document, &e)
|
crate::scene::annotative::entity_for_active_context(&self.document, &e)
|
||||||
.into_owned()
|
.into_owned()
|
||||||
})
|
})
|
||||||
.map(|e| (normalize(e), 0usize, ins_color, l0, (base_depth, half_gap)))
|
.map(|e| {
|
||||||
|
(
|
||||||
|
normalize(e),
|
||||||
|
0usize,
|
||||||
|
ins_style,
|
||||||
|
l0,
|
||||||
|
l0_aci,
|
||||||
|
(base_depth, half_gap),
|
||||||
|
)
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
while let Some((sub, depth, sub_ins_color, sub_l0, (d_base, d_half))) = stack.pop() {
|
while let Some((
|
||||||
|
sub,
|
||||||
|
depth,
|
||||||
|
sub_ins_style,
|
||||||
|
sub_l0,
|
||||||
|
sub_l0_aci,
|
||||||
|
(d_base, d_half),
|
||||||
|
)) = stack.pop()
|
||||||
|
{
|
||||||
match sub {
|
match sub {
|
||||||
EntityType::Insert(nins) => {
|
EntityType::Insert(nins) => {
|
||||||
if depth >= 32 {
|
if depth >= 32 {
|
||||||
@@ -1180,21 +1232,27 @@ impl Scene {
|
|||||||
&self.document,
|
&self.document,
|
||||||
&nested_entity,
|
&nested_entity,
|
||||||
);
|
);
|
||||||
let child_ins_color = if !has_book_color
|
let mut child_ins_style =
|
||||||
|
crate::scene::view::render::render_style_for_block_sub(
|
||||||
|
&self.document,
|
||||||
|
&nested_entity,
|
||||||
|
sub_ins_style.0,
|
||||||
|
sub_ins_style.1,
|
||||||
|
sub_ins_style.2,
|
||||||
|
sub_ins_style.3,
|
||||||
|
sub_l0,
|
||||||
|
);
|
||||||
|
child_ins_style.4 = if !has_book_color
|
||||||
&& nins.common.color == Color::ByBlock
|
&& nins.common.color == Color::ByBlock
|
||||||
{
|
{
|
||||||
sub_ins_color
|
sub_ins_style.4
|
||||||
} else if !has_book_color
|
} else if !has_book_color
|
||||||
&& on_l0
|
&& on_l0
|
||||||
&& nins.common.color == Color::ByLayer
|
&& nins.common.color == Color::ByLayer
|
||||||
{
|
{
|
||||||
sub_l0.color
|
sub_l0_aci
|
||||||
} else {
|
} else {
|
||||||
crate::scene::view::render::render_style_for(
|
child_ins_style.4
|
||||||
&self.document,
|
|
||||||
&nested_entity,
|
|
||||||
)
|
|
||||||
.0
|
|
||||||
};
|
};
|
||||||
let child_l0 = if on_l0 {
|
let child_l0 = if on_l0 {
|
||||||
sub_l0
|
sub_l0
|
||||||
@@ -1204,6 +1262,11 @@ impl Scene {
|
|||||||
&nins.common.layer,
|
&nins.common.layer,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
let child_l0_aci = if on_l0 {
|
||||||
|
sub_l0_aci
|
||||||
|
} else {
|
||||||
|
layer_aci(&nins.common.layer)
|
||||||
|
};
|
||||||
// Nested insert: its slot = parent slot + its rank in
|
// Nested insert: its slot = parent slot + its rank in
|
||||||
// the parent block, subdivided by its own block size.
|
// the parent block, subdivided by its own block size.
|
||||||
let nd_base = d_base
|
let nd_base = d_base
|
||||||
@@ -1225,8 +1288,9 @@ impl Scene {
|
|||||||
stack.push((
|
stack.push((
|
||||||
normalize(e),
|
normalize(e),
|
||||||
depth + 1,
|
depth + 1,
|
||||||
child_ins_color,
|
child_ins_style,
|
||||||
child_l0,
|
child_l0,
|
||||||
|
child_l0_aci,
|
||||||
(nd_base, nd_half),
|
(nd_base, nd_half),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -1236,17 +1300,38 @@ impl Scene {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Resolve ByBlock / layer-0 inheritance for this block
|
// Resolve ByBlock / layer-0 inheritance for this block
|
||||||
// child, then adapt to the background (#221). Pattern /
|
// child, including the pattern-stroke lineweight and
|
||||||
// lineweight args are unused by a hatch's colour.
|
// effective ACI used by plot style tables.
|
||||||
let style = crate::scene::view::render::render_style_for_block_sub(
|
let hatch_entity = EntityType::Hatch(dxf.clone());
|
||||||
|
let mut style = crate::scene::view::render::render_style_for_block_sub(
|
||||||
&self.document,
|
&self.document,
|
||||||
&EntityType::Hatch(dxf.clone()),
|
&hatch_entity,
|
||||||
sub_ins_color,
|
sub_ins_style.0,
|
||||||
0.0,
|
sub_ins_style.1,
|
||||||
[0.0; 8],
|
sub_ins_style.2,
|
||||||
0.0,
|
sub_ins_style.3,
|
||||||
sub_l0,
|
sub_l0,
|
||||||
);
|
);
|
||||||
|
let has_book_color =
|
||||||
|
crate::scene::view::render::has_resolved_book_color(
|
||||||
|
&self.document,
|
||||||
|
&hatch_entity,
|
||||||
|
);
|
||||||
|
let on_l0 = crate::scene::view::render::is_effective_layer_zero(
|
||||||
|
&dxf.common.layer,
|
||||||
|
);
|
||||||
|
style.4 = if !has_book_color
|
||||||
|
&& dxf.common.color == acadrust::types::Color::ByBlock
|
||||||
|
{
|
||||||
|
sub_ins_style.4
|
||||||
|
} else if !has_book_color
|
||||||
|
&& on_l0
|
||||||
|
&& dxf.common.color == acadrust::types::Color::ByLayer
|
||||||
|
{
|
||||||
|
sub_l0_aci
|
||||||
|
} else {
|
||||||
|
style.4
|
||||||
|
};
|
||||||
let color = style.0;
|
let color = style.0;
|
||||||
let color =
|
let color =
|
||||||
crate::scene::view::render::adapt_to_bg(color, hatch_bg);
|
crate::scene::view::render::adapt_to_bg(color, hatch_bg);
|
||||||
@@ -1254,6 +1339,7 @@ impl Scene {
|
|||||||
Self::hatch_model_from_dxf(&dxf, color)
|
Self::hatch_model_from_dxf(&dxf, color)
|
||||||
{
|
{
|
||||||
model.aci = style.4;
|
model.aci = style.4;
|
||||||
|
model.line_weight_px = style.3;
|
||||||
// In-block rank → within the insert's depth slot.
|
// In-block rank → within the insert's depth slot.
|
||||||
model.draw_depth = d_base
|
model.draw_depth = d_base
|
||||||
+ depth_map
|
+ depth_map
|
||||||
@@ -1360,6 +1446,7 @@ impl Scene {
|
|||||||
name: "WIPEOUT_FILL".into(),
|
name: "WIPEOUT_FILL".into(),
|
||||||
color: fill_color,
|
color: fill_color,
|
||||||
aci: 0,
|
aci: 0,
|
||||||
|
line_weight_px: 1.0,
|
||||||
angle_offset: 0.0,
|
angle_offset: 0.0,
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
world_origin: fill_origin,
|
world_origin: fill_origin,
|
||||||
@@ -1478,6 +1565,7 @@ impl Scene {
|
|||||||
name: "WIPEOUT_FILL".into(),
|
name: "WIPEOUT_FILL".into(),
|
||||||
color: fill_color,
|
color: fill_color,
|
||||||
aci: 0,
|
aci: 0,
|
||||||
|
line_weight_px: 1.0,
|
||||||
angle_offset: 0.0,
|
angle_offset: 0.0,
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
world_origin: fill_origin,
|
world_origin: fill_origin,
|
||||||
@@ -2038,6 +2126,7 @@ impl Scene {
|
|||||||
// entity colour.
|
// entity colour.
|
||||||
color: gradient_color1.unwrap_or(color),
|
color: gradient_color1.unwrap_or(color),
|
||||||
aci: 0,
|
aci: 0,
|
||||||
|
line_weight_px: 1.0,
|
||||||
angle_offset: if prebaked { 0.0 } else { dxf.pattern_angle as f32 },
|
angle_offset: if prebaked { 0.0 } else { dxf.pattern_angle as f32 },
|
||||||
scale: if prebaked { 1.0 } else { dxf.pattern_scale as f32 },
|
scale: if prebaked { 1.0 } else { dxf.pattern_scale as f32 },
|
||||||
world_origin,
|
world_origin,
|
||||||
@@ -2343,6 +2432,7 @@ impl Scene {
|
|||||||
name: "SOLID".into(),
|
name: "SOLID".into(),
|
||||||
color,
|
color,
|
||||||
aci: 0,
|
aci: 0,
|
||||||
|
line_weight_px: 1.0,
|
||||||
angle_offset: 0.0,
|
angle_offset: 0.0,
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
world_origin,
|
world_origin,
|
||||||
|
|||||||
@@ -3160,6 +3160,7 @@ impl Scene {
|
|||||||
name: "SOLID".to_string(),
|
name: "SOLID".to_string(),
|
||||||
color: self.paper_bg_color,
|
color: self.paper_bg_color,
|
||||||
aci: 0,
|
aci: 0,
|
||||||
|
line_weight_px: 1.0,
|
||||||
angle_offset: 0.0,
|
angle_offset: 0.0,
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
// Draw-order bias is signed: entity fills/wires land in (-1, 1)
|
// Draw-order bias is signed: entity fills/wires land in (-1, 1)
|
||||||
|
|||||||
@@ -179,6 +179,9 @@ pub struct HatchModel {
|
|||||||
pub color: [f32; 4],
|
pub color: [f32; 4],
|
||||||
/// Effective indexed color used by plot style tables; 0 means RGB/default.
|
/// Effective indexed color used by plot style tables; 0 means RGB/default.
|
||||||
pub aci: u8,
|
pub aci: u8,
|
||||||
|
/// Effective display lineweight used by pattern strokes. PDF export converts
|
||||||
|
/// this back to a physical thickness unless a plot style overrides it.
|
||||||
|
pub line_weight_px: f32,
|
||||||
/// Pattern rotation offset in radians (from DXF `pattern_angle`).
|
/// Pattern rotation offset in radians (from DXF `pattern_angle`).
|
||||||
/// Applied on top of each family's base angle at render time.
|
/// Applied on top of each family's base angle at render time.
|
||||||
pub angle_offset: f32,
|
pub angle_offset: f32,
|
||||||
|
|||||||
@@ -379,6 +379,7 @@ impl Scene {
|
|||||||
let style = self.render_style(entity);
|
let style = self.render_style(entity);
|
||||||
m.color = style.0;
|
m.color = style.0;
|
||||||
m.aci = style.4;
|
m.aci = style.4;
|
||||||
|
m.line_weight_px = style.3;
|
||||||
if let EntityType::Hatch(dxf) = entity {
|
if let EntityType::Hatch(dxf) = entity {
|
||||||
// Only re-apply pattern_scale/angle for catalog-derived patterns
|
// Only re-apply pattern_scale/angle for catalog-derived patterns
|
||||||
// (empty stored lines). A pattern built from the hatch's own
|
// (empty stored lines). A pattern built from the hatch's own
|
||||||
@@ -459,6 +460,7 @@ impl Scene {
|
|||||||
let style = self.render_style(entity);
|
let style = self.render_style(entity);
|
||||||
hatch.color = style.0;
|
hatch.color = style.0;
|
||||||
hatch.aci = style.4;
|
hatch.aci = style.4;
|
||||||
|
hatch.line_weight_px = style.3;
|
||||||
if let EntityType::Hatch(dxf) = entity {
|
if let EntityType::Hatch(dxf) = entity {
|
||||||
if let model::hatch_model::HatchPattern::Pattern(_) = &hatch.pattern {
|
if let model::hatch_model::HatchPattern::Pattern(_) = &hatch.pattern {
|
||||||
if dxf.pattern.lines.is_empty() {
|
if dxf.pattern.lines.is_empty() {
|
||||||
@@ -516,6 +518,7 @@ impl Scene {
|
|||||||
name: "WIPEOUT_FILL".into(),
|
name: "WIPEOUT_FILL".into(),
|
||||||
color: self.paper_bg_color,
|
color: self.paper_bg_color,
|
||||||
aci: 0,
|
aci: 0,
|
||||||
|
line_weight_px: 1.0,
|
||||||
angle_offset: 0.0,
|
angle_offset: 0.0,
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
world_origin,
|
world_origin,
|
||||||
@@ -604,6 +607,7 @@ impl Scene {
|
|||||||
name: "WIPEOUT_FILL".into(),
|
name: "WIPEOUT_FILL".into(),
|
||||||
color: fill_color,
|
color: fill_color,
|
||||||
aci: 0,
|
aci: 0,
|
||||||
|
line_weight_px: 1.0,
|
||||||
angle_offset: 0.0,
|
angle_offset: 0.0,
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
world_origin: fill_origin,
|
world_origin: fill_origin,
|
||||||
|
|||||||
+32
-8
@@ -31,11 +31,14 @@ impl Scene {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let style = self.render_style(entity);
|
||||||
|
model.aci = style.4;
|
||||||
|
model.line_weight_px = style.3;
|
||||||
if !matches!(
|
if !matches!(
|
||||||
model.pattern,
|
model.pattern,
|
||||||
crate::scene::model::hatch_model::HatchPattern::Gradient { .. }
|
crate::scene::model::hatch_model::HatchPattern::Gradient { .. }
|
||||||
) {
|
) {
|
||||||
model.color = self.render_style(entity).0;
|
model.color = style.0;
|
||||||
}
|
}
|
||||||
if self.selected.contains(&handle) {
|
if self.selected.contains(&handle) {
|
||||||
model.color = [0.15, 0.55, 1.00, model.color[3]];
|
model.color = [0.15, 0.55, 1.00, model.color[3]];
|
||||||
@@ -50,17 +53,38 @@ impl Scene {
|
|||||||
let mut backdrop = model.clone();
|
let mut backdrop = model.clone();
|
||||||
backdrop.pattern =
|
backdrop.pattern =
|
||||||
crate::scene::model::hatch_model::HatchPattern::Solid;
|
crate::scene::model::hatch_model::HatchPattern::Solid;
|
||||||
backdrop.color = match background {
|
let (background_color, background_aci) = match background {
|
||||||
acadrust::types::Color::ByLayer => {
|
acadrust::types::Color::ByLayer => {
|
||||||
crate::scene::view::render::layer_render_style(
|
let layer = self.document.layers.get(&hatch.common.layer);
|
||||||
&self.document,
|
let aci = layer
|
||||||
&hatch.common.layer,
|
.and_then(|layer| match &layer.color {
|
||||||
|
acadrust::types::Color::Index(index) => Some(*index),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.unwrap_or(0);
|
||||||
|
(
|
||||||
|
crate::scene::view::render::layer_render_style(
|
||||||
|
&self.document,
|
||||||
|
&hatch.common.layer,
|
||||||
|
)
|
||||||
|
.color,
|
||||||
|
aci,
|
||||||
)
|
)
|
||||||
.color
|
|
||||||
}
|
}
|
||||||
acadrust::types::Color::ByBlock => self.render_style(entity).0,
|
acadrust::types::Color::ByBlock => (style.0, style.4),
|
||||||
other => crate::scene::convert::tess_util::aci_to_rgba(&other),
|
acadrust::types::Color::Index(index) => (
|
||||||
|
crate::scene::convert::tess_util::aci_to_rgba(
|
||||||
|
&acadrust::types::Color::Index(index),
|
||||||
|
),
|
||||||
|
index,
|
||||||
|
),
|
||||||
|
other => (
|
||||||
|
crate::scene::convert::tess_util::aci_to_rgba(&other),
|
||||||
|
0,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
backdrop.color = background_color;
|
||||||
|
backdrop.aci = background_aci;
|
||||||
backdrop.name = "SOLID".into();
|
backdrop.name = "SOLID".into();
|
||||||
models.push(backdrop);
|
models.push(backdrop);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -469,6 +469,7 @@ impl Scene {
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
wire.aci = hatch.aci;
|
wire.aci = hatch.aci;
|
||||||
|
wire.line_weight_px = hatch.line_weight_px;
|
||||||
wire.aabb = aabb;
|
wire.aabb = aabb;
|
||||||
pattern_wires.push(wire);
|
pattern_wires.push(wire);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ impl canvas::Program<Message> for HatchPatternPreview {
|
|||||||
name: String::new(),
|
name: String::new(),
|
||||||
color: [1.0; 4],
|
color: [1.0; 4],
|
||||||
aci: 0,
|
aci: 0,
|
||||||
|
line_weight_px: 1.0,
|
||||||
angle_offset: 0.0,
|
angle_offset: 0.0,
|
||||||
scale: hatch_preview_scale(&self.pattern),
|
scale: hatch_preview_scale(&self.pattern),
|
||||||
draw_depth: 0.0,
|
draw_depth: 0.0,
|
||||||
|
|||||||
Reference in new issue
Block a user