forked from 1b-agent/opencadstudio-upstream
refactor(scene): group modules into role-based subfolders
Move the 30 flat modules under src/scene/ into six folders by role: convert (entity→geometry), text (fonts/shaping), model (render models), pick (hit-test/selection/grips/xclip), view (camera/transform/render), cache (block + property caches). pipeline/ is unchanged. Full path migration — all `scene::X` references become `scene::<group>::X`, no compatibility re-exports. render.rs items that were `pub(super)` (parent was scene) become `pub(in crate::scene)` to keep their original scene-wide visibility. No behavior change; build and tests are green. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
cd048165a9
commit
7fe7e0230d
160 files changed
+732
-715
No files matched your search
@@ -21,8 +21,8 @@ use std::sync::Arc;
|
||||
use acadrust::types::{Color as AcadColor, LineWeight, Transform, Vector3};
|
||||
use acadrust::{CadDocument, EntityType, Handle};
|
||||
|
||||
use crate::scene::tessellate;
|
||||
use crate::scene::wire_model::{SnapHint, TangentGeom, WireModel};
|
||||
use crate::scene::convert::tessellate;
|
||||
use crate::scene::model::wire_model::{SnapHint, TangentGeom, WireModel};
|
||||
|
||||
const MAX_NESTING_DEPTH: usize = 32;
|
||||
/// Skip wires whose world-AABB projects to fewer than this many pixels in
|
||||
@@ -392,7 +392,7 @@ fn build_nested_ref(
|
||||
// (`Batches::finalize`) so the same cached defn can serve renders
|
||||
// against different backgrounds without rebuilding.
|
||||
let (ins_color, ins_pat_len, ins_pat, ins_lw_px, _) =
|
||||
crate::scene::render::render_style_for(doc, &EntityType::Insert(nested_ins.clone()));
|
||||
crate::scene::view::render::render_style_for(doc, &EntityType::Insert(nested_ins.clone()));
|
||||
let _ = bg_color;
|
||||
|
||||
NestedRef {
|
||||
@@ -430,7 +430,7 @@ fn tessellate_sub_local(
|
||||
// with the per-render bg, so the cache no longer has to rebuild on
|
||||
// BACKGROUND / layout-switch — the dynamic adaptation tracks the
|
||||
// live bg at render time.
|
||||
let (sub_color, pat_len, pat, lw_px, aci) = crate::scene::render::render_style_for(doc, sub);
|
||||
let (sub_color, pat_len, pat, lw_px, aci) = crate::scene::view::render::render_style_for(doc, sub);
|
||||
let _ = bg_color;
|
||||
|
||||
let color_is_byblock = sub.common().color == AcadColor::ByBlock;
|
||||
@@ -1087,7 +1087,7 @@ impl Batches {
|
||||
// now so each render against a different bg gets the
|
||||
// right pure-black ↔ pure-white flip without rebuilding
|
||||
// the cached defn.
|
||||
let color = crate::scene::render::adapt_to_bg(b.color, bg_color);
|
||||
let color = crate::scene::view::render::adapt_to_bg(b.color, bg_color);
|
||||
WireModel {
|
||||
name: name.to_string(),
|
||||
points: b.points,
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
pub mod block_cache;
|
||||
pub mod properties;
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
use acadrust::{EntityType, Handle};
|
||||
|
||||
use crate::scene::object::{PropSection, PropValue, Property};
|
||||
use crate::scene::model::object::{PropSection, PropValue, Property};
|
||||
|
||||
pub fn general_section(entity: &EntityType) -> PropSection {
|
||||
let common = entity.common();
|
||||
@@ -5,7 +5,7 @@ use glam::Vec3;
|
||||
use truck_modeling::{Edge, Solid, Vertex, Wire};
|
||||
|
||||
use crate::entities::traits::EntityTypeOps;
|
||||
use crate::scene::wire_model::{SnapHint, TangentGeom};
|
||||
use crate::scene::model::wire_model::{SnapHint, TangentGeom};
|
||||
|
||||
/// One group of glyph strokes with its world-space origin stored in f64.
|
||||
/// Strokes are in glyph-local space (origin = [0,0]) so that the large
|
||||
@@ -26,8 +26,8 @@ use acadrust::entities::acis::{
|
||||
SatConeSurface, SatDocument, SatFace, SatPlaneSurface, SatSphereSurface, SatTorusSurface,
|
||||
};
|
||||
|
||||
use crate::scene::mesh_model::{MeshLodSet, MeshModel};
|
||||
use crate::scene::solid3d_tess::{
|
||||
use crate::scene::model::mesh_model::{MeshLodSet, MeshModel};
|
||||
use crate::scene::convert::solid3d_tess::{
|
||||
apply_body_transform, body_transform, collect_face_polygon, cone_axis_span, mesh_aabb,
|
||||
};
|
||||
|
||||
@@ -302,7 +302,7 @@ fn torus_faces(torus: &SatTorusSurface) -> Vec<Face> {
|
||||
/// Append meshes for every `spline-surface` face, reusing the truck
|
||||
/// BSplineSurface grid sampler in `spline_tess`.
|
||||
fn append_spline_faces(sat: &SatDocument, mesh: &mut MeshModel) {
|
||||
use crate::scene::solid3d_tess::LodConfig;
|
||||
use crate::scene::convert::solid3d_tess::LodConfig;
|
||||
for face in sat.faces() {
|
||||
let Some(surf_rec) = sat.resolve(face.surface()) else {
|
||||
continue;
|
||||
@@ -313,7 +313,7 @@ fn append_spline_faces(sat: &SatDocument, mesh: &mut MeshModel) {
|
||||
let mut verts: Vec<[f32; 3]> = Vec::new();
|
||||
let mut normals: Vec<[f32; 3]> = Vec::new();
|
||||
let mut indices: Vec<u32> = Vec::new();
|
||||
crate::scene::spline_tess::tess_spline_face(
|
||||
crate::scene::convert::spline_tess::tess_spline_face(
|
||||
sat,
|
||||
&face,
|
||||
LodConfig::HIGH,
|
||||
@@ -0,0 +1,7 @@
|
||||
pub mod acad_to_truck;
|
||||
pub mod acis_to_truck;
|
||||
pub mod truck_tess;
|
||||
pub mod tessellate;
|
||||
pub mod tess_util;
|
||||
pub mod solid3d_tess;
|
||||
pub mod spline_tess;
|
||||
@@ -20,7 +20,7 @@ use acadrust::entities::acis::{
|
||||
};
|
||||
use acadrust::entities::{Body, Region, Solid3D};
|
||||
|
||||
use crate::scene::mesh_model::{MeshLodSet, MeshModel};
|
||||
use crate::scene::model::mesh_model::{MeshLodSet, MeshModel};
|
||||
|
||||
/// Per-LOD sampling density. Higher values = finer mesh = more triangles.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
@@ -107,7 +107,7 @@ fn tessellate_sat(
|
||||
}
|
||||
}
|
||||
"spline-surface" => {
|
||||
crate::scene::spline_tess::tess_spline_face(
|
||||
crate::scene::convert::spline_tess::tess_spline_face(
|
||||
sat,
|
||||
&face,
|
||||
lod,
|
||||
@@ -141,7 +141,7 @@ fn tessellate_acis(
|
||||
color: [f32; 4],
|
||||
facet_res: f64,
|
||||
) -> Option<MeshLodSet> {
|
||||
if let Some(set) = crate::scene::acis_to_truck::tessellate_sat_truck(sat, name.clone(), color, facet_res) {
|
||||
if let Some(set) = crate::scene::convert::acis_to_truck::tessellate_sat_truck(sat, name.clone(), color, facet_res) {
|
||||
if std::env::var_os("OCS_TESS_DEBUG").is_some() {
|
||||
let tris = set.lods.first().map(|m| m.indices.len() / 3).unwrap_or(0);
|
||||
eprintln!("acis_tess[{name}]: truck ({tris} tris)");
|
||||
@@ -10,7 +10,7 @@
|
||||
use acadrust::entities::acis::{SatDocument, SatFace, SatRecord, SatToken};
|
||||
use truck_modeling::{BSplineSurface, KnotVec, ParametricSurface, ParametricSurface3D, Point3};
|
||||
|
||||
use crate::scene::solid3d_tess::LodConfig;
|
||||
use crate::scene::convert::solid3d_tess::LodConfig;
|
||||
|
||||
/// Tessellate one `spline-surface` face by sampling its B-spline surface.
|
||||
/// Appends triangles to the shared mesh buffers; a no-op when the surface
|
||||
@@ -1,5 +1,5 @@
|
||||
// Shared helpers used by per-entity tessellation impls in `crate::entities`
|
||||
// and the dispatcher in `crate::scene::tessellate`.
|
||||
// and the dispatcher in `crate::scene::convert::tessellate`.
|
||||
//
|
||||
// Anything cross-entity (colour resolution, arc sampling, chord tolerance)
|
||||
// lives here. Entity-specific helpers (dim format strings, mleader path
|
||||
@@ -8,7 +8,7 @@
|
||||
use acadrust::types::Color as AcadColor;
|
||||
use glam::Vec3;
|
||||
|
||||
use crate::scene::wire_model::{SnapHint, TangentGeom, WireModel};
|
||||
use crate::scene::model::wire_model::{SnapHint, TangentGeom, WireModel};
|
||||
|
||||
/// Output of the fallback per-entity geometry path used by entities not
|
||||
/// covered by the truck topology pipeline (Viewport, Insert, Hatch
|
||||
@@ -80,7 +80,7 @@ pub fn fill_chord_tol(radius: f64) -> f64 {
|
||||
/// passes, load-time builds), falls back to [`fill_chord_tol`] so we
|
||||
/// never under-sample.
|
||||
pub fn wire_chord_tol(radius: f64) -> f64 {
|
||||
match crate::scene::truck_tess::active_curve_tol() {
|
||||
match crate::scene::convert::truck_tess::active_curve_tol() {
|
||||
Some(t) => t.min(fill_chord_tol(radius)).max(1e-9),
|
||||
None => fill_chord_tol(radius),
|
||||
}
|
||||
@@ -18,11 +18,11 @@ use acadrust::types::Color as AcadColor;
|
||||
use acadrust::{CadDocument, EntityType, Handle};
|
||||
use glam::Vec3;
|
||||
|
||||
use crate::scene::acad_to_truck::{convert, TruckObject};
|
||||
use crate::scene::truck_tess::{
|
||||
use crate::scene::convert::acad_to_truck::{convert, TruckObject};
|
||||
use crate::scene::convert::truck_tess::{
|
||||
tessellate_edge, tessellate_vertex, tessellate_wire, TruckTessResult,
|
||||
};
|
||||
use crate::scene::wire_model::{SnapHint, WireModel};
|
||||
use crate::scene::model::wire_model::{SnapHint, WireModel};
|
||||
|
||||
// ── Public entry points ────────────────────────────────────────────────────
|
||||
|
||||
@@ -604,7 +604,7 @@ pub(crate) fn entity_z(entity: &EntityType) -> f32 {
|
||||
// function stays as the dispatcher used by the main `tessellate()` path.
|
||||
|
||||
use crate::entities::traits::FallbackTess;
|
||||
use crate::scene::tess_util::FallbackGeometry as Geometry;
|
||||
use crate::scene::convert::tess_util::FallbackGeometry as Geometry;
|
||||
|
||||
fn fallback_geometry(entity: &EntityType, world_offset: [f64; 3]) -> Geometry {
|
||||
match entity {
|
||||
File renamed without changes.
+114
-132
@@ -1,56 +1,38 @@
|
||||
pub mod acad_to_truck;
|
||||
pub mod block_cache;
|
||||
mod camera;
|
||||
pub mod complex_lt;
|
||||
pub mod lff;
|
||||
pub mod dispatch;
|
||||
pub mod font_face;
|
||||
pub mod grip;
|
||||
pub mod hatch_model;
|
||||
pub mod hatch_patterns;
|
||||
pub mod hit_test;
|
||||
pub mod image_model;
|
||||
pub mod mesh_model;
|
||||
pub mod model_solid;
|
||||
pub mod object;
|
||||
// Scene modules grouped by role:
|
||||
// convert — DXF/ACIS entities → truck solids & tessellated geometry
|
||||
// text — LFF stroke + TrueType font engines and shaping
|
||||
// model — per-entity GPU render models (wire, hatch, mesh, image, object)
|
||||
// pick — hit-testing, selection, grips, spatial index, xclip
|
||||
// view — camera, transforms, viewport, render pipeline driver
|
||||
// cache — block-definition and property caches
|
||||
pub mod cache;
|
||||
pub mod convert;
|
||||
pub mod model;
|
||||
pub mod pick;
|
||||
pub mod pipeline;
|
||||
pub mod properties;
|
||||
pub mod quadtree;
|
||||
pub mod text;
|
||||
pub mod view;
|
||||
|
||||
/// Result of `Scene::entity_index()`. The wire path queries `tree` for
|
||||
/// view-rect candidates and also always emits `unbounded_handles`
|
||||
/// (entities with no usable bbox — legacy `UNBOUNDED_AABB` sentinel).
|
||||
pub(super) struct EntityIndex {
|
||||
pub tree: quadtree::QuadTree,
|
||||
pub tree: pick::quadtree::QuadTree,
|
||||
pub unbounded_handles: Vec<Handle>,
|
||||
}
|
||||
pub(crate) mod render;
|
||||
mod selection;
|
||||
pub mod acis_to_truck;
|
||||
pub mod solid3d_tess;
|
||||
pub mod spline_tess;
|
||||
pub mod sysfont;
|
||||
pub mod ttf_glyph;
|
||||
pub mod tess_util;
|
||||
pub mod tessellate;
|
||||
pub mod transform;
|
||||
pub mod truck_tess;
|
||||
pub mod viewport_pane;
|
||||
pub mod wire_model;
|
||||
pub mod xclip;
|
||||
|
||||
use camera::Camera;
|
||||
pub use camera::Projection;
|
||||
pub use hatch_model::HatchModel;
|
||||
pub use image_model::ImageModel;
|
||||
pub use mesh_model::MeshLodSet;
|
||||
pub use object::{GripApply, GripDef};
|
||||
use view::camera::Camera;
|
||||
pub use view::camera::Projection;
|
||||
pub use model::hatch_model::HatchModel;
|
||||
pub use model::image_model::ImageModel;
|
||||
pub use model::mesh_model::MeshLodSet;
|
||||
pub use model::object::{GripApply, GripDef};
|
||||
pub use pipeline::uniforms::Uniforms;
|
||||
pub use pipeline::viewcube::{
|
||||
hit_test, hover_id, CubeRegion, VIEWCUBE_DRAW_PX, VIEWCUBE_PAD, VIEWCUBE_PX,
|
||||
};
|
||||
pub use selection::SelectionState;
|
||||
pub use wire_model::WireModel;
|
||||
pub use pick::selection::SelectionState;
|
||||
pub use model::wire_model::WireModel;
|
||||
|
||||
use crate::command::EntityTransform;
|
||||
use acadrust::entities::{Block, BlockEnd, Insert as DxfInsert};
|
||||
@@ -170,7 +152,7 @@ pub fn build_derived_caches(doc: &CadDocument) -> DerivedCaches {
|
||||
//
|
||||
// Cross-check the header against a per-entity AABB scan of MSPACE
|
||||
// (same `bounding_box()` API and same SANE_EXTENT/zero-placeholder
|
||||
// filters that `block_cache::build_defn` already uses for block defns)
|
||||
// filters that `cache::block_cache::build_defn` already uses for block defns)
|
||||
// and prefer the entity-scan when the header center drifts more than
|
||||
// 10× its own half-span away from the entity centroid.
|
||||
use rayon::prelude::*;
|
||||
@@ -219,8 +201,8 @@ pub fn build_derived_caches(doc: &CadDocument) -> DerivedCaches {
|
||||
} else {
|
||||
[0.0; 3]
|
||||
};
|
||||
let (raw, ..) = render::render_style_for(doc, e);
|
||||
let color = render::adapt_to_bg(raw, LOAD_BG);
|
||||
let (raw, ..) = view::render::render_style_for(doc, e);
|
||||
let color = view::render::adapt_to_bg(raw, LOAD_BG);
|
||||
let model = match e {
|
||||
EntityType::Hatch(dxf) => Scene::hatch_model_from_dxf(dxf, color, offset),
|
||||
EntityType::Solid(solid) => Some(Scene::solid_hatch_model(solid, color, offset)),
|
||||
@@ -250,8 +232,8 @@ pub fn build_derived_caches(doc: &CadDocument) -> DerivedCaches {
|
||||
.par_iter()
|
||||
.filter_map(|&handle| {
|
||||
let e = doc.get_entity(handle)?;
|
||||
let (raw, ..) = render::render_style_for(doc, e);
|
||||
let color = render::adapt_to_bg(raw, LOAD_BG);
|
||||
let (raw, ..) = view::render::render_style_for(doc, e);
|
||||
let color = view::render::adapt_to_bg(raw, LOAD_BG);
|
||||
crate::entities::solid3d::tessellate_volume(e, color, facet_res)
|
||||
.map(|m| (handle, offset_mesh_lod_set(m, world_offset)))
|
||||
})
|
||||
@@ -268,7 +250,7 @@ pub fn build_derived_caches(doc: &CadDocument) -> DerivedCaches {
|
||||
}
|
||||
}
|
||||
|
||||
/// Mirrors `block_cache::SANE_EXTENT` — wire coords past this magnitude
|
||||
/// Mirrors `cache::block_cache::SANE_EXTENT` — wire coords past this magnitude
|
||||
/// are treated as corruption rather than precision-relevant geometry.
|
||||
const WORLD_OFFSET_SANE_EXTENT: f64 = 1.0e8;
|
||||
|
||||
@@ -342,7 +324,7 @@ fn offset_centroid(
|
||||
return None;
|
||||
}
|
||||
// Skip block-defn sentinels and AttributeDefinition — same as
|
||||
// block_cache::build_defn. Their bboxes don't represent drawable
|
||||
// cache::block_cache::build_defn. Their bboxes don't represent drawable
|
||||
// MSPACE geometry.
|
||||
if matches!(
|
||||
e,
|
||||
@@ -735,7 +717,7 @@ pub struct Scene {
|
||||
/// Tessellated block definitions in block-local coords, keyed by geometry_epoch.
|
||||
/// Lets Insert tessellation transform-copy cached wires instead of
|
||||
/// clone+explode+re-tessellate per reference.
|
||||
block_defn_cache: RefCell<Option<(u64, Arc<block_cache::BlockCache>)>>,
|
||||
block_defn_cache: RefCell<Option<(u64, Arc<cache::block_cache::BlockCache>)>>,
|
||||
/// Spatial index + always-emit list for top-level entities
|
||||
/// (Phase 2.1). Lazily rebuilt by `entity_index()` on
|
||||
/// `geometry_epoch` change. See `EntityIndex` for what each side
|
||||
@@ -946,7 +928,7 @@ impl Scene {
|
||||
/// Get (or build on miss) the block-definition cache for the current epoch.
|
||||
/// Built single-threaded — recursive nested expansion makes parallelization
|
||||
/// fiddly and the cache only rebuilds when geometry actually changes.
|
||||
pub(super) fn block_cache_arc(&self) -> Arc<block_cache::BlockCache> {
|
||||
pub(super) fn block_cache_arc(&self) -> Arc<cache::block_cache::BlockCache> {
|
||||
{
|
||||
let cache = self.block_defn_cache.borrow();
|
||||
if let Some((epoch, ref arc)) = *cache {
|
||||
@@ -965,7 +947,7 @@ impl Scene {
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
let built = block_cache::BlockCache::build(&self.document, anno, bg);
|
||||
let built = cache::block_cache::BlockCache::build(&self.document, anno, bg);
|
||||
let arc = Arc::new(built);
|
||||
*self.block_defn_cache.borrow_mut() = Some((self.block_epoch, Arc::clone(&arc)));
|
||||
arc
|
||||
@@ -1035,7 +1017,7 @@ impl Scene {
|
||||
.get_entity(handle)
|
||||
.map(|e| self.render_style(e).0)
|
||||
.unwrap_or([0.8, 0.8, 0.85, 1.0]);
|
||||
if let Some(set) = crate::scene::model_solid::mesh_from_solid(&solid, color) {
|
||||
if let Some(set) = crate::scene::model::model_solid::mesh_from_solid(&solid, color) {
|
||||
self.meshes.insert(handle, set);
|
||||
}
|
||||
self.model_solids.insert(handle, solid);
|
||||
@@ -1328,7 +1310,7 @@ impl Scene {
|
||||
Some(HatchModel {
|
||||
world_origin: [0.0, 0.0],
|
||||
boundary: Arc::new(vec![[x0, y0], [x1, y0], [x1, y1], [x0, y1], [x0, y0]]),
|
||||
pattern: crate::scene::hatch_model::HatchPattern::Solid,
|
||||
pattern: crate::scene::model::hatch_model::HatchPattern::Solid,
|
||||
name: "SOLID".to_string(),
|
||||
color: self.paper_bg_color,
|
||||
angle_offset: 0.0,
|
||||
@@ -2115,7 +2097,7 @@ impl Scene {
|
||||
.meshes
|
||||
.iter()
|
||||
.filter_map(|(h, set)| set.lods.first().map(|m| (*h, m)));
|
||||
hit_test::mesh_click_hit(cursor, iter, view_proj, bounds)
|
||||
pick::hit_test::mesh_click_hit(cursor, iter, view_proj, bounds)
|
||||
}
|
||||
|
||||
/// Tessellate all non-invisible entities owned by `block_handle`.
|
||||
@@ -2297,7 +2279,7 @@ impl Scene {
|
||||
1.0
|
||||
};
|
||||
let blk_cache = self.block_cache_arc();
|
||||
let blk_ref: &block_cache::BlockCache = &blk_cache;
|
||||
let blk_ref: &cache::block_cache::BlockCache = &blk_cache;
|
||||
// Zoom-adaptive curve sampling for top-level Edge tessellation. Target
|
||||
// ~0.5 px chord height — far-out arcs that used to emit hundreds of
|
||||
// segments now collapse to a handful. The guard clears the override
|
||||
@@ -2306,11 +2288,11 @@ impl Scene {
|
||||
struct CurveTolGuard;
|
||||
impl Drop for CurveTolGuard {
|
||||
fn drop(&mut self) {
|
||||
crate::scene::truck_tess::set_curve_tol_override(None);
|
||||
crate::scene::convert::truck_tess::set_curve_tol_override(None);
|
||||
}
|
||||
}
|
||||
let _tol_guard = wpp.map(|w| {
|
||||
crate::scene::truck_tess::set_curve_tol_override(Some((w * 0.5) as f64));
|
||||
crate::scene::convert::truck_tess::set_curve_tol_override(Some((w * 0.5) as f64));
|
||||
CurveTolGuard
|
||||
});
|
||||
// Per-entity tessellation memo (Phase 2.2) — only on the culled Model
|
||||
@@ -2550,7 +2532,7 @@ impl Scene {
|
||||
}
|
||||
None => [-1.0, -1.0, 1.0, 1.0],
|
||||
};
|
||||
let mut tree = quadtree::QuadTree::new(root);
|
||||
let mut tree = pick::quadtree::QuadTree::new(root);
|
||||
for (h, ab) in items {
|
||||
tree.insert(h, ab);
|
||||
}
|
||||
@@ -3021,7 +3003,7 @@ impl Scene {
|
||||
continue;
|
||||
}
|
||||
|
||||
let adapted = render::adapt_to_bg(wire.color, self.paper_bg_color);
|
||||
let adapted = view::render::adapt_to_bg(wire.color, self.paper_bg_color);
|
||||
let [r, g, b, a] = adapted;
|
||||
let mut out = wire.clone();
|
||||
out.points = clipped;
|
||||
@@ -3521,7 +3503,7 @@ impl Scene {
|
||||
let color = layer_entry
|
||||
.map(|l| &l.color)
|
||||
.unwrap_or(&acadrust::types::Color::WHITE);
|
||||
let [r, g, b, _] = crate::scene::tess_util::aci_to_rgba(color);
|
||||
let [r, g, b, _] = crate::scene::convert::tess_util::aci_to_rgba(color);
|
||||
[r, g, b, 1.0]
|
||||
}
|
||||
|
||||
@@ -3589,7 +3571,7 @@ impl Scene {
|
||||
|
||||
let local = EntityTransform::Translate(-base);
|
||||
for (old_handle, mut entity) in source_entities {
|
||||
dispatch::apply_transform(&mut entity, &local);
|
||||
view::dispatch::apply_transform(&mut entity, &local);
|
||||
entity = crate::modules::home::modify::explode::normalize_entity_for_block(entity);
|
||||
entity.common_mut().handle = Handle::NULL;
|
||||
entity.common_mut().owner_handle = br_handle;
|
||||
@@ -3667,7 +3649,7 @@ impl Scene {
|
||||
m.color = self.render_style(e).0;
|
||||
if let EntityType::Hatch(dxf) = e {
|
||||
match &mut m.pattern {
|
||||
hatch_model::HatchPattern::Pattern(_) => {
|
||||
model::hatch_model::HatchPattern::Pattern(_) => {
|
||||
m.angle_offset = dxf.pattern_angle as f32;
|
||||
let anno = if self.current_layout == "Model" {
|
||||
self.annotation_scale
|
||||
@@ -3676,10 +3658,10 @@ impl Scene {
|
||||
};
|
||||
m.scale = dxf.pattern_scale as f32 * anno;
|
||||
}
|
||||
hatch_model::HatchPattern::Gradient { angle_deg, .. } => {
|
||||
model::hatch_model::HatchPattern::Gradient { angle_deg, .. } => {
|
||||
*angle_deg = dxf.pattern_angle.to_degrees() as f32;
|
||||
}
|
||||
hatch_model::HatchPattern::Solid => {}
|
||||
model::hatch_model::HatchPattern::Solid => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3708,8 +3690,8 @@ impl Scene {
|
||||
let selected = self.selected.contains(&ins.common.handle);
|
||||
// XCLIP: clip this insert's exploded hatch fills to the boundary,
|
||||
// matching how the line geometry is clipped in expand_insert.
|
||||
let clip_poly = xclip::insert_spatial_filter(&self.document, ins)
|
||||
.map(|sf| xclip::world_clip_polygon(sf, ins, hatch_offset));
|
||||
let clip_poly = pick::xclip::insert_spatial_filter(&self.document, ins)
|
||||
.map(|sf| pick::xclip::world_clip_polygon(sf, ins, hatch_offset));
|
||||
// Walk the full block tree: `explode_from_document` only descends
|
||||
// one level, so nested INSERTs are re-exploded here. Each level
|
||||
// bakes its transform into the children it returns, so nested
|
||||
@@ -3740,7 +3722,7 @@ impl Scene {
|
||||
Self::hatch_model_from_dxf(&dxf, color, hatch_offset)
|
||||
{
|
||||
if let Some(poly) = &clip_poly {
|
||||
let clipped = xclip::clip_hatch_boundary(
|
||||
let clipped = pick::xclip::clip_hatch_boundary(
|
||||
&model.boundary,
|
||||
model.world_origin,
|
||||
poly,
|
||||
@@ -3797,7 +3779,7 @@ impl Scene {
|
||||
}
|
||||
models.push(HatchModel {
|
||||
boundary: Arc::new(boundary),
|
||||
pattern: hatch_model::HatchPattern::Solid,
|
||||
pattern: model::hatch_model::HatchPattern::Solid,
|
||||
name: "SOLID".into(),
|
||||
color,
|
||||
angle_offset: 0.0,
|
||||
@@ -3861,7 +3843,7 @@ impl Scene {
|
||||
}
|
||||
models.push(HatchModel {
|
||||
boundary: Arc::new(boundary),
|
||||
pattern: hatch_model::HatchPattern::Solid,
|
||||
pattern: model::hatch_model::HatchPattern::Solid,
|
||||
name: "WIPEOUT_FILL".into(),
|
||||
color: fill_color,
|
||||
angle_offset: 0.0,
|
||||
@@ -3957,7 +3939,7 @@ impl Scene {
|
||||
// even on large UTM-scale drawings.
|
||||
let to_xy = |x: f64, y: f64| -> [f64; 2] {
|
||||
let (wx, wy, _) =
|
||||
crate::scene::transform::ocs_point_to_wcs((x, y, dxf.elevation), normal);
|
||||
crate::scene::view::transform::ocs_point_to_wcs((x, y, dxf.elevation), normal);
|
||||
[wx - ox, wy - oy]
|
||||
};
|
||||
if dxf.paths.is_empty() {
|
||||
@@ -4006,10 +3988,10 @@ impl Scene {
|
||||
boundary.push(to_xy(v0.x, v0.y));
|
||||
continue;
|
||||
};
|
||||
let segs = tess_util::arc_segments(
|
||||
let segs = convert::tess_util::arc_segments(
|
||||
arc.radius,
|
||||
arc.sweep.abs(),
|
||||
tess_util::fill_chord_tol(arc.radius),
|
||||
convert::tess_util::fill_chord_tol(arc.radius),
|
||||
);
|
||||
for j in 0..segs {
|
||||
let s = arc.sample(j as f64 / segs as f64);
|
||||
@@ -4027,15 +4009,15 @@ impl Scene {
|
||||
boundary.push(to_xy(line.end.x, line.end.y));
|
||||
}
|
||||
BoundaryEdge::CircularArc(arc) => {
|
||||
let (sa, span) = tess_util::arc_signed_span(
|
||||
let (sa, span) = convert::tess_util::arc_signed_span(
|
||||
arc.start_angle,
|
||||
arc.end_angle,
|
||||
arc.counter_clockwise,
|
||||
);
|
||||
let segs = tess_util::arc_segments(
|
||||
let segs = convert::tess_util::arc_segments(
|
||||
arc.radius,
|
||||
span.abs(),
|
||||
tess_util::fill_chord_tol(arc.radius),
|
||||
convert::tess_util::fill_chord_tol(arc.radius),
|
||||
);
|
||||
for i in 0..=segs {
|
||||
let t = sa + span * (i as f64 / segs as f64);
|
||||
@@ -4054,15 +4036,15 @@ impl Scene {
|
||||
.major_axis_endpoint
|
||||
.y
|
||||
.atan2(ell.major_axis_endpoint.x);
|
||||
let (sa, span) = tess_util::arc_signed_span(
|
||||
let (sa, span) = convert::tess_util::arc_signed_span(
|
||||
ell.start_angle,
|
||||
ell.end_angle,
|
||||
ell.counter_clockwise,
|
||||
);
|
||||
let segs = tess_util::arc_segments(
|
||||
let segs = convert::tess_util::arc_segments(
|
||||
r_maj,
|
||||
span.abs(),
|
||||
tess_util::fill_chord_tol(r_maj),
|
||||
convert::tess_util::fill_chord_tol(r_maj),
|
||||
);
|
||||
let (cr, sr) = (rot.cos(), rot.sin());
|
||||
for i in 0..=segs {
|
||||
@@ -4116,7 +4098,7 @@ impl Scene {
|
||||
let diag = ((sp_max_x - sp_min_x).powi(2)
|
||||
+ (sp_max_y - sp_min_y).powi(2))
|
||||
.sqrt();
|
||||
let tol = tess_util::fill_chord_tol(diag.max(1.0));
|
||||
let tol = convert::tess_util::fill_chord_tol(diag.max(1.0));
|
||||
|
||||
let mut sampled = false;
|
||||
if knot_ok {
|
||||
@@ -4193,7 +4175,7 @@ impl Scene {
|
||||
if boundary.is_empty() {
|
||||
return None;
|
||||
}
|
||||
boundary.truncate(hatch_model::MAX_HATCH_BOUNDARY_VERTS);
|
||||
boundary.truncate(model::hatch_model::MAX_HATCH_BOUNDARY_VERTS);
|
||||
|
||||
let pattern = if dxf.gradient_color.is_enabled() {
|
||||
let color2 = dxf
|
||||
@@ -4204,15 +4186,15 @@ impl Scene {
|
||||
.map(|(r, g, b)| [r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0, 1.0])
|
||||
.unwrap_or(color);
|
||||
let angle_deg = dxf.pattern_angle.to_degrees() as f32;
|
||||
hatch_model::HatchPattern::Gradient { angle_deg, color2 }
|
||||
model::hatch_model::HatchPattern::Gradient { angle_deg, color2 }
|
||||
} else if dxf.is_solid {
|
||||
hatch_model::HatchPattern::Solid
|
||||
model::hatch_model::HatchPattern::Solid
|
||||
} else {
|
||||
let pat_name = &dxf.pattern.name;
|
||||
if let Some(entry) = crate::scene::hatch_patterns::find(pat_name) {
|
||||
if let Some(entry) = crate::scene::model::hatch_patterns::find(pat_name) {
|
||||
entry.gpu.clone()
|
||||
} else {
|
||||
hatch_model::HatchPattern::Pattern(vec![hatch_model::PatFamily {
|
||||
model::hatch_model::HatchPattern::Pattern(vec![model::hatch_model::PatFamily {
|
||||
angle_deg: 0.0,
|
||||
x0: 0.0,
|
||||
y0: 0.0,
|
||||
@@ -4329,11 +4311,11 @@ impl Scene {
|
||||
};
|
||||
let model = match &kind {
|
||||
EntityType::Hatch(dxf) => {
|
||||
let color = tess_util::aci_to_rgba(&dxf.common.color);
|
||||
let color = convert::tess_util::aci_to_rgba(&dxf.common.color);
|
||||
Self::hatch_model_from_dxf(dxf, color, offset)
|
||||
}
|
||||
EntityType::Solid(solid) => {
|
||||
let color = tess_util::aci_to_rgba(&solid.common.color);
|
||||
let color = convert::tess_util::aci_to_rgba(&solid.common.color);
|
||||
Some(Self::solid_hatch_model(solid, color, offset))
|
||||
}
|
||||
_ => None,
|
||||
@@ -4415,7 +4397,7 @@ impl Scene {
|
||||
];
|
||||
HatchModel {
|
||||
boundary: std::sync::Arc::new(boundary),
|
||||
pattern: hatch_model::HatchPattern::Solid,
|
||||
pattern: model::hatch_model::HatchPattern::Solid,
|
||||
name: "SOLID".into(),
|
||||
color,
|
||||
angle_offset: 0.0,
|
||||
@@ -4430,7 +4412,7 @@ impl Scene {
|
||||
let mut dxf = DxfHatch::new();
|
||||
dxf.is_solid = matches!(
|
||||
model.pattern,
|
||||
crate::scene::hatch_model::HatchPattern::Solid
|
||||
crate::scene::model::hatch_model::HatchPattern::Solid
|
||||
);
|
||||
// The boundary points arrive in local render space (world_offset
|
||||
// already subtracted). The stored DXF entity must hold WCS, so add the
|
||||
@@ -4453,8 +4435,8 @@ impl Scene {
|
||||
let mut path = BoundaryPath::external();
|
||||
path.add_edge(BoundaryEdge::Polyline(edge));
|
||||
dxf.paths.push(path);
|
||||
if let Some(entry) = crate::scene::hatch_patterns::find(&model.name) {
|
||||
dxf.pattern = crate::scene::hatch_patterns::build_dxf_pattern(entry);
|
||||
if let Some(entry) = crate::scene::model::hatch_patterns::find(&model.name) {
|
||||
dxf.pattern = crate::scene::model::hatch_patterns::build_dxf_pattern(entry);
|
||||
}
|
||||
dxf.pattern_angle = model.angle_offset as f64;
|
||||
dxf.pattern_scale = if model.scale.abs() > 1e-6 {
|
||||
@@ -4768,7 +4750,7 @@ impl Scene {
|
||||
field: &str,
|
||||
) -> Option<String> {
|
||||
use crate::entities::traits::EntityTypeOps;
|
||||
use crate::scene::object::PropValue;
|
||||
use crate::scene::model::object::PropValue;
|
||||
match field {
|
||||
"layer" => Some(entity.common().layer.clone()),
|
||||
"color" => Some(Self::format_color(entity.common().color)),
|
||||
@@ -4992,7 +4974,7 @@ impl Scene {
|
||||
}
|
||||
for &h in handles {
|
||||
if let Some(entity) = self.document.get_entity_mut(h) {
|
||||
dispatch::apply_transform(entity, t);
|
||||
view::dispatch::apply_transform(entity, t);
|
||||
}
|
||||
if self.hatches.contains_key(&h) {
|
||||
let existing_color = self.hatches[&h].color;
|
||||
@@ -5048,12 +5030,12 @@ impl Scene {
|
||||
.collect();
|
||||
let mut new_handles = Vec::with_capacity(clones.len());
|
||||
for mut entity in clones {
|
||||
dispatch::apply_transform(&mut entity, t);
|
||||
view::dispatch::apply_transform(&mut entity, t);
|
||||
entity.common_mut().handle = Handle::NULL;
|
||||
let h = self.document.add_entity(entity).unwrap_or(Handle::NULL);
|
||||
if !h.is_null() {
|
||||
let new_model = if let Some(EntityType::Hatch(dxf)) = self.document.get_entity(h) {
|
||||
let color = tess_util::aci_to_rgba(&dxf.common.color);
|
||||
let color = convert::tess_util::aci_to_rgba(&dxf.common.color);
|
||||
Self::hatch_model_from_dxf(dxf, color, hatch_offset)
|
||||
} else {
|
||||
None
|
||||
@@ -5083,7 +5065,7 @@ impl Scene {
|
||||
.map(|p| [p.x, p.y, p.z]);
|
||||
|
||||
if let Some(entity) = self.document.get_entity_mut(handle) {
|
||||
dispatch::apply_grip(entity, grip_id, apply);
|
||||
view::dispatch::apply_grip(entity, grip_id, apply);
|
||||
}
|
||||
|
||||
// Translate MeshModel vertices by the same delta the grip applied.
|
||||
@@ -5121,7 +5103,7 @@ impl Scene {
|
||||
};
|
||||
match self.document.get_entity(handle) {
|
||||
Some(EntityType::Hatch(dxf)) => {
|
||||
let color = tess_util::aci_to_rgba(&dxf.common.color);
|
||||
let color = convert::tess_util::aci_to_rgba(&dxf.common.color);
|
||||
if let Some(model) = Self::hatch_model_from_dxf(dxf, color, hatch_offset) {
|
||||
self.hatches.insert(handle, model);
|
||||
} else {
|
||||
@@ -5129,7 +5111,7 @@ impl Scene {
|
||||
}
|
||||
}
|
||||
Some(EntityType::Solid(solid)) => {
|
||||
let color = tess_util::aci_to_rgba(&solid.common.color);
|
||||
let color = convert::tess_util::aci_to_rgba(&solid.common.color);
|
||||
self.hatches
|
||||
.insert(handle, Self::solid_hatch_model(solid, color, hatch_offset));
|
||||
}
|
||||
@@ -5262,7 +5244,7 @@ impl Scene {
|
||||
} else {
|
||||
vd.x.atan2(-vd.y)
|
||||
};
|
||||
let rotation = camera::yaw_pitch_to_quat(yaw, pitch, 0.0);
|
||||
let rotation = view::camera::yaw_pitch_to_quat(yaw, pitch, 0.0);
|
||||
let view_right = rotation * glam::Vec3::X;
|
||||
let view_up = rotation * glam::Vec3::Y;
|
||||
let base = if model_space {
|
||||
@@ -5288,7 +5270,7 @@ impl Scene {
|
||||
cam.yaw = yaw;
|
||||
cam.pitch = pitch;
|
||||
cam.fov_y = fov_y;
|
||||
cam.projection = camera::Projection::Orthographic;
|
||||
cam.projection = view::camera::Projection::Orthographic;
|
||||
drop(cam);
|
||||
self.camera_generation += 1;
|
||||
true
|
||||
@@ -5350,7 +5332,7 @@ impl Scene {
|
||||
} else {
|
||||
vd.x.atan2(-vd.y)
|
||||
};
|
||||
let rotation = camera::yaw_pitch_to_quat(yaw, pitch, 0.0);
|
||||
let rotation = view::camera::yaw_pitch_to_quat(yaw, pitch, 0.0);
|
||||
let view_right = rotation * glam::Vec3::X;
|
||||
let view_up = rotation * glam::Vec3::Y;
|
||||
// view_target is WCS; wire-space subtracts world_offset.
|
||||
@@ -5368,7 +5350,7 @@ impl Scene {
|
||||
rotation,
|
||||
distance,
|
||||
fov_y,
|
||||
projection: camera::Projection::Orthographic,
|
||||
projection: view::camera::Projection::Orthographic,
|
||||
yaw,
|
||||
pitch,
|
||||
})
|
||||
@@ -5581,7 +5563,7 @@ impl Scene {
|
||||
} else {
|
||||
vd.x.atan2(-vd.y)
|
||||
};
|
||||
let rotation = camera::yaw_pitch_to_quat(yaw, pitch, 0.0);
|
||||
let rotation = view::camera::yaw_pitch_to_quat(yaw, pitch, 0.0);
|
||||
let view_right = rotation * glam::Vec3::X;
|
||||
let view_up = rotation * glam::Vec3::Y;
|
||||
|
||||
@@ -5604,7 +5586,7 @@ impl Scene {
|
||||
cam.yaw = yaw;
|
||||
cam.pitch = pitch;
|
||||
cam.fov_y = fov_y;
|
||||
cam.projection = camera::Projection::Orthographic;
|
||||
cam.projection = view::camera::Projection::Orthographic;
|
||||
drop(cam);
|
||||
|
||||
self.camera_generation += 1;
|
||||
@@ -6387,8 +6369,8 @@ impl Scene {
|
||||
let mut sheet_cam = self.camera.borrow().clone();
|
||||
sheet_cam.yaw = 0.0;
|
||||
sheet_cam.pitch = std::f32::consts::FRAC_PI_2;
|
||||
sheet_cam.rotation = camera::yaw_pitch_to_quat(0.0, std::f32::consts::FRAC_PI_2, 0.0);
|
||||
sheet_cam.projection = camera::Projection::Orthographic;
|
||||
sheet_cam.rotation = view::camera::yaw_pitch_to_quat(0.0, std::f32::consts::FRAC_PI_2, 0.0);
|
||||
sheet_cam.projection = view::camera::Projection::Orthographic;
|
||||
out.push(ViewportInstance {
|
||||
handle: Handle::NULL,
|
||||
tile_idx: None,
|
||||
@@ -6523,7 +6505,7 @@ impl Scene {
|
||||
let mut m = model.clone();
|
||||
m.color = self.render_style(entity).0;
|
||||
if let EntityType::Hatch(dxf) = entity {
|
||||
if let hatch_model::HatchPattern::Pattern(_) = &m.pattern {
|
||||
if let model::hatch_model::HatchPattern::Pattern(_) = &m.pattern {
|
||||
m.angle_offset = dxf.pattern_angle as f32;
|
||||
m.scale = dxf.pattern_scale as f32;
|
||||
}
|
||||
@@ -6576,7 +6558,7 @@ impl Scene {
|
||||
}
|
||||
models.push(HatchModel {
|
||||
boundary: Arc::new(boundary),
|
||||
pattern: hatch_model::HatchPattern::Solid,
|
||||
pattern: model::hatch_model::HatchPattern::Solid,
|
||||
name: "WIPEOUT_FILL".into(),
|
||||
color: fill_color,
|
||||
angle_offset: 0.0,
|
||||
@@ -6592,7 +6574,7 @@ impl Scene {
|
||||
/// Build a Camera oriented and scaled to match a paper-space Viewport entity.
|
||||
/// Used by `active_viewports` to render model-space content through each
|
||||
/// content viewport's own view direction and scale.
|
||||
fn camera_for_viewport(&self, vp_handle: Handle) -> Option<camera::Camera> {
|
||||
fn camera_for_viewport(&self, vp_handle: Handle) -> Option<view::camera::Camera> {
|
||||
let vp = match self.document.get_entity(vp_handle) {
|
||||
Some(EntityType::Viewport(vp)) => vp,
|
||||
_ => return None,
|
||||
@@ -6615,7 +6597,7 @@ impl Scene {
|
||||
vd.x.atan2(-vd.y)
|
||||
};
|
||||
|
||||
let rotation = camera::yaw_pitch_to_quat(yaw, pitch, 0.0);
|
||||
let rotation = view::camera::yaw_pitch_to_quat(yaw, pitch, 0.0);
|
||||
|
||||
// view_target is in raw model/WCS coords; the GPU renderer works in
|
||||
// wire-space (model - world_offset), so subtract world_offset here.
|
||||
@@ -6671,12 +6653,12 @@ impl Scene {
|
||||
// ortho_size = distance * tan(fov_y/2) => distance = view_height/2 / tan(fov_y/2)
|
||||
let distance = ((view_height / 2.0) / (fov_y * 0.5).tan()).max(0.001);
|
||||
|
||||
Some(camera::Camera {
|
||||
Some(view::camera::Camera {
|
||||
target,
|
||||
rotation,
|
||||
distance,
|
||||
fov_y,
|
||||
projection: camera::Projection::Orthographic,
|
||||
projection: view::camera::Projection::Orthographic,
|
||||
yaw,
|
||||
pitch,
|
||||
})
|
||||
@@ -6999,7 +6981,7 @@ fn tessellate_entity(
|
||||
bg_color: [f32; 4],
|
||||
anno_scale: f32,
|
||||
e: &EntityType,
|
||||
block_cache: Option<&block_cache::BlockCache>,
|
||||
block_cache: Option<&cache::block_cache::BlockCache>,
|
||||
// World-space XY view AABB (post `world_offset` subtraction). When
|
||||
// `Some`, entities whose AABB doesn't intersect this rect are skipped.
|
||||
view_aabb: Option<[f32; 4]>,
|
||||
@@ -7021,14 +7003,14 @@ fn tessellate_entity(
|
||||
let ab = entity_aabb(e, world_offset);
|
||||
if ab != WireModel::UNBOUNDED_AABB {
|
||||
if let Some(view) = view_aabb {
|
||||
if block_cache::aabb_disjoint_xy(ab, view) {
|
||||
if cache::block_cache::aabb_disjoint_xy(ab, view) {
|
||||
return vec![];
|
||||
}
|
||||
}
|
||||
if let Some(wpp) = world_per_pixel {
|
||||
let w_px = (ab[2] - ab[0]).abs();
|
||||
let h_px = (ab[3] - ab[1]).abs();
|
||||
// Keep in sync with `block_cache::MIN_PIXEL_SIZE`.
|
||||
// Keep in sync with `cache::block_cache::MIN_PIXEL_SIZE`.
|
||||
// Text/MText have their own LOD ladder below
|
||||
// (baseline-line / greek / full) and must reach it
|
||||
// even when projected size is sub-5 px.
|
||||
@@ -7053,8 +7035,8 @@ fn tessellate_entity(
|
||||
// footprint doesn't drift when the camera
|
||||
// crosses the LOD threshold. See #19.
|
||||
let (entity_color, _, _, _, aci_idx) =
|
||||
render::render_style_for(document, e);
|
||||
let entity_color = render::adapt_to_bg(entity_color, bg_color);
|
||||
view::render::render_style_for(document, e);
|
||||
let entity_color = view::render::adapt_to_bg(entity_color, bg_color);
|
||||
if is_3d_entity {
|
||||
// `ab` is already in the local frame
|
||||
// (entity_aabb subtracted world_offset
|
||||
@@ -7116,7 +7098,7 @@ fn tessellate_entity(
|
||||
} else {
|
||||
(0.0_f32, [0.0f32; 8])
|
||||
};
|
||||
let mut wires = tessellate::tessellate(
|
||||
let mut wires = convert::tessellate::tessellate(
|
||||
document,
|
||||
h,
|
||||
e,
|
||||
@@ -7136,10 +7118,10 @@ fn tessellate_entity(
|
||||
}
|
||||
|
||||
let (entity_color, pattern_length, pattern, line_weight_px, aci) =
|
||||
render::render_style_for(document, e);
|
||||
let entity_color = render::adapt_to_bg(entity_color, bg_color);
|
||||
view::render::render_style_for(document, e);
|
||||
let entity_color = view::render::adapt_to_bg(entity_color, bg_color);
|
||||
let lt_scale = document.header.linetype_scale as f32 * e.common().linetype_scale as f32;
|
||||
let lt_name = render::linetype_name_for(document, e);
|
||||
let lt_name = view::render::linetype_name_for(document, e);
|
||||
// PSLTSCALE: scale linetype dashes by viewport anno_scale so they appear uniform in paper space.
|
||||
let pslt_factor = if document.header.paper_space_linetype_scaling {
|
||||
anno_scale
|
||||
@@ -7315,8 +7297,8 @@ fn tessellate_entity(
|
||||
|
||||
if let EntityType::Insert(ins) = e {
|
||||
// Resolve the INSERT's own style so ByBlock sub-entities can inherit it.
|
||||
let (ins_color, ins_pat_len, ins_pat, ins_lw_px, _) = render::render_style_for(document, e);
|
||||
let ins_color = render::adapt_to_bg(ins_color, bg_color);
|
||||
let (ins_color, ins_pat_len, ins_pat, ins_lw_px, _) = view::render::render_style_for(document, e);
|
||||
let ins_color = view::render::adapt_to_bg(ins_color, bg_color);
|
||||
let [ox, oy, oz] = world_offset;
|
||||
let ip = glam::Vec3::new(
|
||||
(ins.insert_point.x - ox) as f32,
|
||||
@@ -7332,7 +7314,7 @@ fn tessellate_entity(
|
||||
pattern_length: 0.0,
|
||||
pattern: [0.0; 8],
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![(ip, wire_model::SnapHint::Insertion)],
|
||||
snap_pts: vec![(ip, model::wire_model::SnapHint::Insertion)],
|
||||
tangent_geoms: vec![],
|
||||
key_vertices: vec![],
|
||||
aabb: WireModel::UNBOUNDED_AABB,
|
||||
@@ -7349,7 +7331,7 @@ fn tessellate_entity(
|
||||
.get(&ins.block_name)
|
||||
.map(|br| br.flags.is_xref || br.flags.is_xref_overlay)
|
||||
.unwrap_or(false);
|
||||
if let Some(mut wires) = block_cache::expand_insert(
|
||||
if let Some(mut wires) = cache::block_cache::expand_insert(
|
||||
cache,
|
||||
ins,
|
||||
h,
|
||||
@@ -7368,9 +7350,9 @@ fn tessellate_entity(
|
||||
// XCLIP: if this INSERT carries an enabled spatial filter,
|
||||
// clip the expanded block geometry to the boundary polygon so
|
||||
// only the portion inside the clip is drawn.
|
||||
if let Some(sf) = xclip::insert_spatial_filter(document, ins) {
|
||||
let poly = xclip::world_clip_polygon(sf, ins, world_offset);
|
||||
xclip::clip_wires(&mut wires, &poly);
|
||||
if let Some(sf) = pick::xclip::insert_spatial_filter(document, ins) {
|
||||
let poly = pick::xclip::world_clip_polygon(sf, ins, world_offset);
|
||||
pick::xclip::clip_wires(&mut wires, &poly);
|
||||
}
|
||||
|
||||
// Per-INSERT attribute values. The block defn carries the
|
||||
@@ -7413,7 +7395,7 @@ fn tessellate_entity(
|
||||
.map(crate::modules::home::modify::explode::normalize_insert_entity)
|
||||
.flat_map(|sub| {
|
||||
let (sub_color, sub_pattern_length, sub_pattern, sub_line_weight_px, sub_aci) =
|
||||
render::render_style_for_block_sub(
|
||||
view::render::render_style_for_block_sub(
|
||||
document,
|
||||
&sub,
|
||||
ins_color,
|
||||
@@ -7421,16 +7403,16 @@ fn tessellate_entity(
|
||||
ins_pat,
|
||||
ins_lw_px,
|
||||
);
|
||||
let sub_color = render::adapt_to_bg(sub_color, bg_color);
|
||||
let sub_color = view::render::adapt_to_bg(sub_color, bg_color);
|
||||
let sub_color = if is_xref && !sel {
|
||||
block_cache::fade_toward_bg(sub_color, bg_color)
|
||||
cache::block_cache::fade_toward_bg(sub_color, bg_color)
|
||||
} else {
|
||||
sub_color
|
||||
};
|
||||
let sub_aabb = entity_aabb(&sub, world_offset);
|
||||
let sub_pattern_length = sub_pattern_length * pslt_factor;
|
||||
let sub_pattern = sub_pattern.map(|v| v * pslt_factor);
|
||||
let mut wires = tessellate::tessellate(
|
||||
let mut wires = convert::tessellate::tessellate(
|
||||
document,
|
||||
h,
|
||||
&sub,
|
||||
@@ -7591,7 +7573,7 @@ fn tessellate_entity(
|
||||
}
|
||||
}
|
||||
|
||||
let mut bases = tessellate::tessellate(
|
||||
let mut bases = convert::tessellate::tessellate(
|
||||
document,
|
||||
h,
|
||||
e,
|
||||
@@ -7615,7 +7597,7 @@ fn tessellate_entity(
|
||||
// this path.
|
||||
if let Some(clt) = crate::linetypes::complex_lt(lt_name) {
|
||||
if let Some(base) = bases.first() {
|
||||
let mut wires = complex_lt::apply_along(
|
||||
let mut wires = text::complex_lt::apply_along(
|
||||
&base.name,
|
||||
&base.points,
|
||||
clt,
|
||||
|
||||
File renamed without changes.
@@ -7,7 +7,7 @@
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use crate::patterns::PatLineDef;
|
||||
use crate::scene::hatch_model::{HatchPattern, PatFamily};
|
||||
use crate::scene::model::hatch_model::{HatchPattern, PatFamily};
|
||||
use acadrust::entities::{HatchPattern as DxfPattern, HatchPatternLine};
|
||||
use acadrust::types::Vector2;
|
||||
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,7 @@
|
||||
pub mod wire_model;
|
||||
pub mod hatch_model;
|
||||
pub mod hatch_patterns;
|
||||
pub mod image_model;
|
||||
pub mod mesh_model;
|
||||
pub mod model_solid;
|
||||
pub mod object;
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
use truck_modeling::{builder, Point3, Rad, Solid, Vector3, Wire};
|
||||
|
||||
use crate::scene::mesh_model::{MeshLodSet, MeshModel};
|
||||
use crate::scene::model::mesh_model::{MeshLodSet, MeshModel};
|
||||
|
||||
const FULL: f64 = 7.0; // > 2π → builder closes the revolution
|
||||
|
||||
@@ -99,7 +99,7 @@ pub fn torus_solid(center: [f64; 3], major: f64, minor: f64) -> Solid {
|
||||
/// fallback draws these as a wireframe over the shaded mesh, and hit-testing
|
||||
/// uses their points).
|
||||
pub fn edge_wires(solid: &Solid) -> Vec<acadrust::entities::Wire> {
|
||||
use crate::scene::truck_tess::{tessellate_edge, TruckTessResult};
|
||||
use crate::scene::convert::truck_tess::{tessellate_edge, TruckTessResult};
|
||||
use acadrust::types::Vector3;
|
||||
let mut wires = Vec::new();
|
||||
for shell in solid.boundaries() {
|
||||
@@ -151,7 +151,7 @@ pub fn boolean(op: Bool, a: &Solid, b: &Solid) -> Option<Solid> {
|
||||
/// Tessellate a truck `Solid` into a single-LOD `MeshLodSet` (world-space,
|
||||
/// before world_offset is applied by the caller).
|
||||
pub fn mesh_from_solid(solid: &Solid, color: [f32; 4]) -> Option<MeshLodSet> {
|
||||
use crate::scene::truck_tess::{tessellate_solid, TruckTessResult};
|
||||
use crate::scene::convert::truck_tess::{tessellate_solid, TruckTessResult};
|
||||
match tessellate_solid(solid, [0.0; 3]) {
|
||||
TruckTessResult::Mesh {
|
||||
verts,
|
||||
File renamed without changes.
File renamed without changes.
@@ -4,7 +4,7 @@ use acadrust::Handle;
|
||||
use glam::{Mat4, Vec3};
|
||||
use iced::{Point, Rectangle};
|
||||
|
||||
use super::object::{GripDef, GripShape};
|
||||
use crate::scene::model::object::{GripDef, GripShape};
|
||||
|
||||
/// Pixel radius for grip hit-detection.
|
||||
pub const GRIP_THRESHOLD_PX: f32 = 8.0;
|
||||
@@ -10,9 +10,9 @@ use acadrust::Handle;
|
||||
use glam::{Mat4, Vec3};
|
||||
use iced::{Point, Rectangle};
|
||||
|
||||
use super::hatch_model::HatchModel;
|
||||
use super::mesh_model::MeshModel;
|
||||
use super::wire_model::WireModel;
|
||||
use crate::scene::model::hatch_model::HatchModel;
|
||||
use crate::scene::model::mesh_model::MeshModel;
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
|
||||
/// Pixel radius used for single-click wire detection.
|
||||
const CLICK_THRESHOLD_PX: f32 = 8.0;
|
||||
@@ -0,0 +1,5 @@
|
||||
pub mod hit_test;
|
||||
pub mod selection;
|
||||
pub mod grip;
|
||||
pub mod quadtree;
|
||||
pub mod xclip;
|
||||
File renamed without changes.
File renamed without changes.
@@ -15,7 +15,7 @@ use acadrust::objects::{ObjectType, SpatialFilter};
|
||||
use acadrust::types::{Handle, Vector3};
|
||||
use acadrust::CadDocument;
|
||||
|
||||
use crate::scene::wire_model::WireModel;
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
|
||||
const NAN3: [f32; 3] = [f32::NAN, f32::NAN, f32::NAN];
|
||||
|
||||
@@ -113,7 +113,7 @@ pub fn clip_wires(wires: &mut Vec<WireModel>, poly: &[[f32; 2]]) {
|
||||
/// Clip a hatch fill boundary to `poly`.
|
||||
///
|
||||
/// `boundary` is a hatch's NaN-separated loops in f32 offsets from
|
||||
/// `world_origin` (the [`HatchModel`](crate::scene::hatch_model::HatchModel)
|
||||
/// `world_origin` (the [`HatchModel`](crate::scene::model::hatch_model::HatchModel)
|
||||
/// representation); `poly` is the clip ring in the same world space the hatch
|
||||
/// occupies. Each loop is intersected with the clip polygon independently —
|
||||
/// the even-odd island structure is preserved because intersecting every loop
|
||||
@@ -18,7 +18,7 @@
|
||||
// render pass send the 3D side through a depth-only pipeline for
|
||||
// HiddenLine while keeping the 2D side fully visible.
|
||||
|
||||
use crate::scene::wire_model::WireModel;
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
use iced::wgpu;
|
||||
use iced::wgpu::util::DeviceExt;
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
// InstanceBuffer + Boundary + Family + Dash. Both stages share group
|
||||
// 1 with `read_only` access.
|
||||
|
||||
use crate::scene::hatch_model::{HatchModel, HatchPattern, PatFamily};
|
||||
use crate::scene::model::hatch_model::{HatchModel, HatchPattern, PatFamily};
|
||||
use iced::wgpu;
|
||||
use iced::wgpu::util::DeviceExt;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// binding 1 — BoundaryData (16384 bytes) boundary polygon vertices
|
||||
// binding 2 — FamilyBatchData (1296 bytes) up to 16 line families + 128 dash values
|
||||
|
||||
use crate::scene::hatch_model::{HatchModel, HatchPattern, PatFamily, MAX_HATCH_BOUNDARY_VERTS};
|
||||
use crate::scene::model::hatch_model::{HatchModel, HatchPattern, PatFamily, MAX_HATCH_BOUNDARY_VERTS};
|
||||
use iced::wgpu;
|
||||
use iced::wgpu::util::DeviceExt;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// binding 1 — sampler (bilinear filtering)
|
||||
// binding 2 — ImageParams (opacity uniform, 16 bytes)
|
||||
|
||||
use crate::scene::image_model::ImageModel;
|
||||
use crate::scene::model::image_model::ImageModel;
|
||||
use iced::wgpu;
|
||||
use iced::wgpu::util::DeviceExt;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
// ------
|
||||
// 40 B / vertex
|
||||
|
||||
use crate::scene::mesh_model::{MeshLodSet, MeshModel};
|
||||
use crate::scene::model::mesh_model::{MeshLodSet, MeshModel};
|
||||
use iced::wgpu;
|
||||
use iced::wgpu::util::DeviceExt;
|
||||
|
||||
|
||||
@@ -19,10 +19,10 @@ pub use uniforms::Uniforms;
|
||||
pub use viewcube::ViewCubePipeline;
|
||||
pub use wire_gpu::WireGpu;
|
||||
|
||||
use crate::scene::hatch_model::HatchModel;
|
||||
use crate::scene::image_model::ImageModel;
|
||||
use crate::scene::mesh_model::MeshLodSet;
|
||||
use crate::scene::wire_model::WireModel;
|
||||
use crate::scene::model::hatch_model::HatchModel;
|
||||
use crate::scene::model::image_model::ImageModel;
|
||||
use crate::scene::model::mesh_model::MeshLodSet;
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
|
||||
/// MSAA sample count for the main drawing pipelines.
|
||||
const MSAA_SAMPLES: u32 = 4;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::scene::camera::Camera;
|
||||
use crate::scene::view::camera::Camera;
|
||||
use iced::Rectangle;
|
||||
|
||||
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
// ------
|
||||
// 76 B / instance
|
||||
|
||||
use crate::scene::wire_model::WireModel;
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
use iced::wgpu;
|
||||
use rayon::prelude::*;
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
use crate::entities::text_support::resolve_dxf_special_chars;
|
||||
use crate::linetypes::{ComplexLt, LtSegment};
|
||||
use crate::scene::lff;
|
||||
use crate::scene::wire_model::WireModel;
|
||||
use crate::scene::text::lff;
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
|
||||
// ── Public entry point ────────────────────────────────────────────────────
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::scene::lff::{self, Glyph};
|
||||
use crate::scene::{sysfont, ttf_glyph};
|
||||
use crate::scene::text::lff::{self, Glyph};
|
||||
use crate::scene::text::{sysfont, ttf_glyph};
|
||||
|
||||
/// A glyph borrowed from a static LFF font or owned (cached `Arc`) from a TTF
|
||||
/// face. Derefs to [`Glyph`] either way.
|
||||
@@ -24,32 +24,32 @@ use std::sync::{Mutex, OnceLock};
|
||||
/// Every LibreCAD LFF font, keyed by its file stem (lower-case). Registered
|
||||
/// under the upper-cased stem and the font's `# Name:` header.
|
||||
const FONTS_SRC: &[(&str, &str)] = &[
|
||||
("cyrillic_ii", include_str!("../../assets/fonts/cyrillic_ii.lff")),
|
||||
("gothgbt", include_str!("../../assets/fonts/gothgbt.lff")),
|
||||
("gothgrt", include_str!("../../assets/fonts/gothgrt.lff")),
|
||||
("gothitt", include_str!("../../assets/fonts/gothitt.lff")),
|
||||
("greekc", include_str!("../../assets/fonts/greekc.lff")),
|
||||
("greeks", include_str!("../../assets/fonts/greeks.lff")),
|
||||
("iso3098", include_str!("../../assets/fonts/iso3098.lff")),
|
||||
("iso", include_str!("../../assets/fonts/iso.lff")),
|
||||
("italicc", include_str!("../../assets/fonts/italicc.lff")),
|
||||
("italict", include_str!("../../assets/fonts/italict.lff")),
|
||||
("ltypeshp", include_str!("../../assets/fonts/ltypeshp.lff")),
|
||||
("romanc", include_str!("../../assets/fonts/romanc.lff")),
|
||||
("romand", include_str!("../../assets/fonts/romand.lff")),
|
||||
("romans", include_str!("../../assets/fonts/romans.lff")),
|
||||
("romant", include_str!("../../assets/fonts/romant.lff")),
|
||||
("scriptc", include_str!("../../assets/fonts/scriptc.lff")),
|
||||
("scripts", include_str!("../../assets/fonts/scripts.lff")),
|
||||
("simplex", include_str!("../../assets/fonts/simplex.lff")),
|
||||
("standard", include_str!("../../assets/fonts/standard.lff")),
|
||||
("syastro", include_str!("../../assets/fonts/syastro.lff")),
|
||||
("symap", include_str!("../../assets/fonts/symap.lff")),
|
||||
("symath", include_str!("../../assets/fonts/symath.lff")),
|
||||
("symbol", include_str!("../../assets/fonts/symbol.lff")),
|
||||
("symeteo", include_str!("../../assets/fonts/symeteo.lff")),
|
||||
("symusic", include_str!("../../assets/fonts/symusic.lff")),
|
||||
("unicode", include_str!("../../assets/fonts/unicode.lff")),
|
||||
("cyrillic_ii", include_str!("../../../assets/fonts/cyrillic_ii.lff")),
|
||||
("gothgbt", include_str!("../../../assets/fonts/gothgbt.lff")),
|
||||
("gothgrt", include_str!("../../../assets/fonts/gothgrt.lff")),
|
||||
("gothitt", include_str!("../../../assets/fonts/gothitt.lff")),
|
||||
("greekc", include_str!("../../../assets/fonts/greekc.lff")),
|
||||
("greeks", include_str!("../../../assets/fonts/greeks.lff")),
|
||||
("iso3098", include_str!("../../../assets/fonts/iso3098.lff")),
|
||||
("iso", include_str!("../../../assets/fonts/iso.lff")),
|
||||
("italicc", include_str!("../../../assets/fonts/italicc.lff")),
|
||||
("italict", include_str!("../../../assets/fonts/italict.lff")),
|
||||
("ltypeshp", include_str!("../../../assets/fonts/ltypeshp.lff")),
|
||||
("romanc", include_str!("../../../assets/fonts/romanc.lff")),
|
||||
("romand", include_str!("../../../assets/fonts/romand.lff")),
|
||||
("romans", include_str!("../../../assets/fonts/romans.lff")),
|
||||
("romant", include_str!("../../../assets/fonts/romant.lff")),
|
||||
("scriptc", include_str!("../../../assets/fonts/scriptc.lff")),
|
||||
("scripts", include_str!("../../../assets/fonts/scripts.lff")),
|
||||
("simplex", include_str!("../../../assets/fonts/simplex.lff")),
|
||||
("standard", include_str!("../../../assets/fonts/standard.lff")),
|
||||
("syastro", include_str!("../../../assets/fonts/syastro.lff")),
|
||||
("symap", include_str!("../../../assets/fonts/symap.lff")),
|
||||
("symath", include_str!("../../../assets/fonts/symath.lff")),
|
||||
("symbol", include_str!("../../../assets/fonts/symbol.lff")),
|
||||
("symeteo", include_str!("../../../assets/fonts/symeteo.lff")),
|
||||
("symusic", include_str!("../../../assets/fonts/symusic.lff")),
|
||||
("unicode", include_str!("../../../assets/fonts/unicode.lff")),
|
||||
];
|
||||
|
||||
/// AutoCAD / DXF SHX font names → LFF stem. Names that already match a stem
|
||||
@@ -357,7 +357,7 @@ pub fn tessellate_text_run(
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let face = crate::scene::font_face::Face::resolve(font_name);
|
||||
let face = crate::scene::text::font_face::Face::resolve(font_name);
|
||||
let scale = height / 9.0;
|
||||
let wf = if width_factor < 0.0 {
|
||||
width_factor.clamp(-100.0, -0.01)
|
||||
@@ -407,7 +407,7 @@ pub fn tessellate_text_run(
|
||||
return;
|
||||
}
|
||||
let family = ttf_family.unwrap_or("");
|
||||
if let Some(run) = crate::scene::ttf_glyph::shape_run(family, seg) {
|
||||
if let Some(run) = crate::scene::text::ttf_glyph::shape_run(family, seg) {
|
||||
for g in &run.glyphs {
|
||||
emit_glyph(out, &g.strokes, *cursor_x);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
pub mod lff;
|
||||
pub mod font_face;
|
||||
pub mod sysfont;
|
||||
pub mod ttf_glyph;
|
||||
pub mod complex_lt;
|
||||
File renamed without changes.
@@ -16,8 +16,8 @@
|
||||
// boundary). Counters — the hole in "O" or "A" — come through as separate
|
||||
// closed contours; the wire renderer simply draws every contour.
|
||||
|
||||
use crate::scene::lff::Glyph;
|
||||
use crate::scene::sysfont;
|
||||
use crate::scene::text::lff::Glyph;
|
||||
use crate::scene::text::sysfont;
|
||||
use rustc_hash::FxHashMap as HashMap;
|
||||
use std::sync::{Arc, Mutex, OnceLock};
|
||||
|
||||
File renamed without changes.
@@ -5,8 +5,8 @@ use acadrust::{EntityType, Handle};
|
||||
|
||||
use crate::command::EntityTransform;
|
||||
use crate::entities::traits::EntityTypeOps;
|
||||
use crate::scene::object::{GripDef, PropSection};
|
||||
use crate::scene::properties;
|
||||
use crate::scene::model::object::{GripDef, PropSection};
|
||||
use crate::scene::cache::properties;
|
||||
|
||||
pub fn grips(entity: &EntityType) -> Vec<GripDef> {
|
||||
EntityTypeOps::grips(entity)
|
||||
@@ -71,7 +71,7 @@ pub fn apply_geom_prop(entity: &mut EntityType, field: &str, value: &str) {
|
||||
EntityTypeOps::apply_geom_prop(entity, field, value);
|
||||
}
|
||||
|
||||
pub fn apply_grip(entity: &mut EntityType, grip_id: usize, apply: crate::scene::object::GripApply) {
|
||||
pub fn apply_grip(entity: &mut EntityType, grip_id: usize, apply: crate::scene::model::object::GripApply) {
|
||||
EntityTypeOps::apply_grip(entity, grip_id, apply);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
pub mod camera;
|
||||
pub mod transform;
|
||||
pub mod viewport_pane;
|
||||
pub mod render;
|
||||
pub mod dispatch;
|
||||
@@ -11,10 +11,10 @@ use iced::{Rectangle, Size};
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::pipeline::viewcube::{hover_id, VIEWCUBE_PX};
|
||||
use super::pipeline::MultiPipeline;
|
||||
use super::tess_util;
|
||||
use super::{HatchModel, ImageModel, MeshLodSet, Scene, Uniforms, ViewportInstance, WireModel};
|
||||
use crate::scene::pipeline::viewcube::{hover_id, VIEWCUBE_PX};
|
||||
use crate::scene::pipeline::MultiPipeline;
|
||||
use crate::scene::convert::tess_util;
|
||||
use crate::scene::{HatchModel, ImageModel, MeshLodSet, Scene, Uniforms, ViewportInstance, WireModel};
|
||||
|
||||
// ── Camera hover state (shader::Program::State) ───────────────────────────
|
||||
|
||||
@@ -31,85 +31,85 @@ pub struct CameraState {
|
||||
/// own inner `Pipeline` instance drawn into its own rectangle.
|
||||
#[derive(Debug)]
|
||||
pub struct ViewportData {
|
||||
pub(super) wires: Arc<Vec<WireModel>>,
|
||||
pub(in crate::scene) wires: Arc<Vec<WireModel>>,
|
||||
/// Live command-preview / interim / grip-drag overlay wires. Kept out of
|
||||
/// the main `wires` buffer so a drag re-uploads only this small set each
|
||||
/// frame, never the resident base buffer. Drawn on top in the wire pass.
|
||||
pub(super) preview_wires: Arc<Vec<WireModel>>,
|
||||
pub(in crate::scene) preview_wires: Arc<Vec<WireModel>>,
|
||||
/// 3DFACE entity wires — separated so they are uploaded to the dedicated
|
||||
/// face3d pipeline (fill + batched edges) instead of N individual WireGpu.
|
||||
pub(super) face3d_wires: Arc<Vec<WireModel>>,
|
||||
pub(in crate::scene) face3d_wires: Arc<Vec<WireModel>>,
|
||||
/// Per-entity normalized draw-order depth (handle.value() → (0,1)), used
|
||||
/// by the wire / face3d pipelines as a clip-z bias. WireModels carry no
|
||||
/// depth field (84 construction sites); the bias is looked up by handle
|
||||
/// at GPU-upload time from this map instead.
|
||||
pub(super) draw_depths: Arc<rustc_hash::FxHashMap<u64, f32>>,
|
||||
pub(super) hatches: Arc<Vec<HatchModel>>,
|
||||
pub(in crate::scene) draw_depths: Arc<rustc_hash::FxHashMap<u64, f32>>,
|
||||
pub(in crate::scene) hatches: Arc<Vec<HatchModel>>,
|
||||
/// Wipeout fills — rendered in a separate pass AFTER wires.
|
||||
pub(super) wipeout_hatches: Arc<Vec<HatchModel>>,
|
||||
pub(super) images: Arc<Vec<ImageModel>>,
|
||||
pub(super) meshes: Arc<Vec<MeshLodSet>>,
|
||||
pub(super) uniforms: Uniforms,
|
||||
pub(in crate::scene) wipeout_hatches: Arc<Vec<HatchModel>>,
|
||||
pub(in crate::scene) images: Arc<Vec<ImageModel>>,
|
||||
pub(in crate::scene) meshes: Arc<Vec<MeshLodSet>>,
|
||||
pub(in crate::scene) uniforms: Uniforms,
|
||||
/// Camera rotation matrix derived from the quaternion.
|
||||
/// Used by the ViewCube pipeline — no gimbal lock.
|
||||
pub(super) cam_rotation: Mat4,
|
||||
pub(super) hover_region: Option<usize>,
|
||||
pub(super) show_viewcube: bool,
|
||||
pub(in crate::scene) cam_rotation: Mat4,
|
||||
pub(in crate::scene) hover_region: Option<usize>,
|
||||
pub(in crate::scene) show_viewcube: bool,
|
||||
/// Header.fill_mode (FILLMODE): when false, hatch / wipeout / face3d-fill
|
||||
/// uploads short-circuit so the renderer draws only wireframe.
|
||||
pub(super) fill_mode: bool,
|
||||
pub(in crate::scene) fill_mode: bool,
|
||||
/// Per-view "Wireframe vs Solid" toggle. When `true`, 3D face fills
|
||||
/// are dropped on the upload path so 3D faces draw as edges only.
|
||||
/// Hatch / wipeout uploads are deliberately *not* gated by this flag —
|
||||
/// the user toggle should only affect 3D solids, not 2D fills.
|
||||
pub(super) view_wireframe: bool,
|
||||
pub(in crate::scene) view_wireframe: bool,
|
||||
/// Whether the active render mode wants 3D mesh fills uploaded. Off
|
||||
/// in `Wireframe2D` / `Wireframe3D`; on for every shaded variant. Set
|
||||
/// at the same point `view_wireframe` is computed so the two stay in
|
||||
/// lock-step for the gating logic in `prepare()`.
|
||||
pub(super) mesh_fill: bool,
|
||||
pub(in crate::scene) mesh_fill: bool,
|
||||
/// Whether the active render mode wants 3D mesh / face edges
|
||||
/// rendered on top of fills. Most shaded modes turn this off; the
|
||||
/// `*WithEdges` variants and the pure wireframes leave it on.
|
||||
pub(super) show_3d_edges: bool,
|
||||
pub(in crate::scene) show_3d_edges: bool,
|
||||
/// HiddenLine routes 3D fills through a depth-only prepass so edges
|
||||
/// occluded by closer geometry are culled by the LessEqual depth
|
||||
/// test on the wire passes that follow.
|
||||
pub(super) hidden_line: bool,
|
||||
pub(super) geometry_epoch: u64,
|
||||
pub(in crate::scene) hidden_line: bool,
|
||||
pub(in crate::scene) geometry_epoch: u64,
|
||||
/// Camera generation captured when this Primitive was assembled. Paired
|
||||
/// with `geometry_epoch` so the per-frame scissor / LOD recompute runs.
|
||||
pub(super) camera_generation: u64,
|
||||
pub(in crate::scene) camera_generation: u64,
|
||||
/// Content id of `wires` (Phase 3.2). Stable across a pure pan that reuses
|
||||
/// the Model-tile tessellation, so `prepare` skips re-uploading the
|
||||
/// world-space wire buffer when only the camera moved. Non-tile and
|
||||
/// preview/interim frames carry a fresh id each time → always re-upload.
|
||||
pub(super) wire_content_id: u64,
|
||||
pub(in crate::scene) wire_content_id: u64,
|
||||
/// `selected ∪ hover` handles for the GPU xray overlay. The highlight is no
|
||||
/// longer baked into the wire tessellation, so `prepare` rebuilds the
|
||||
/// selected-wire batch by filtering `wires` against this set.
|
||||
pub(super) highlight_handles: Arc<rustc_hash::FxHashSet<acadrust::Handle>>,
|
||||
pub(in crate::scene) highlight_handles: Arc<rustc_hash::FxHashSet<acadrust::Handle>>,
|
||||
/// Bumped on selection / hover change. Paired with `wire_content_id` to
|
||||
/// decide when the xray overlay batch needs rebuilding.
|
||||
pub(super) selection_generation: u64,
|
||||
pub(in crate::scene) selection_generation: u64,
|
||||
/// Signature of the *selected set* only (not hover). Gates the static-buffer
|
||||
/// re-upload (hatch tint, issue #71) so a hover doesn't re-upload every
|
||||
/// hatch / face3d buffer on hatch-heavy drawings.
|
||||
pub(super) selected_sig: u64,
|
||||
pub(in crate::scene) selected_sig: u64,
|
||||
/// Screen rectangle this viewport fills, **normalized** to the widget
|
||||
/// bounds (each component in 0..1). A single full-widget view is
|
||||
/// `(0, 0, 1, 1)`; tiled / floating viewports are sub-rectangles.
|
||||
/// Normalized form lets `render()` derive the physical sub-clip from
|
||||
/// the surface clip without needing the scale factor.
|
||||
pub(super) screen_rect: Rectangle,
|
||||
pub(in crate::scene) screen_rect: Rectangle,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Primitive {
|
||||
/// One entry per viewport drawn this frame (≥1).
|
||||
pub(super) viewports: Vec<ViewportData>,
|
||||
pub(in crate::scene) viewports: Vec<ViewportData>,
|
||||
/// Background color used to clear each viewport's MSAA buffer.
|
||||
pub(super) bg_color: [f32; 4],
|
||||
pub(in crate::scene) bg_color: [f32; 4],
|
||||
}
|
||||
|
||||
/// Flags the render pipeline consumes, derived from
|
||||
@@ -428,7 +428,7 @@ fn crop_view_proj(view_proj: glam::Mat4, uo: f32, vo: f32, us: f32, vs: f32) ->
|
||||
|
||||
impl Scene {
|
||||
/// Returns (entity_color, pattern_length, pattern, line_weight_px, aci).
|
||||
pub(super) fn render_style(&self, e: &EntityType) -> ([f32; 4], f32, [f32; 8], f32, u8) {
|
||||
pub(in crate::scene) fn render_style(&self, e: &EntityType) -> ([f32; 4], f32, [f32; 8], f32, u8) {
|
||||
let (color, pl, pat, lw, aci) = render_style_for(&self.document, e);
|
||||
let bg = if self.current_layout == "Model" {
|
||||
self.bg_color
|
||||
@@ -443,7 +443,7 @@ impl Scene {
|
||||
|
||||
/// Resolves the effective linetype name for an entity, falling back to the
|
||||
/// layer's linetype when the entity's own linetype is "ByLayer".
|
||||
pub(super) fn linetype_name_for<'a>(document: &'a CadDocument, e: &'a EntityType) -> &'a str {
|
||||
pub(in crate::scene) fn linetype_name_for<'a>(document: &'a CadDocument, e: &'a EntityType) -> &'a str {
|
||||
let elt = &e.common().linetype;
|
||||
if elt.is_empty() || elt.eq_ignore_ascii_case("bylayer") {
|
||||
document
|
||||
@@ -458,7 +458,7 @@ pub(super) fn linetype_name_for<'a>(document: &'a CadDocument, e: &'a EntityType
|
||||
|
||||
/// Returns `(entity_color, pattern_length, pattern, line_weight_px, aci)` for
|
||||
/// an entity, resolving ByLayer color and linetype from the document.
|
||||
pub(super) fn render_style_for(
|
||||
pub(in crate::scene) fn render_style_for(
|
||||
document: &CadDocument,
|
||||
e: &EntityType,
|
||||
) -> ([f32; 4], f32, [f32; 8], f32, u8) {
|
||||
@@ -570,7 +570,7 @@ impl Scene {
|
||||
/// Model layout → one full-window viewport (more once tiled); paper
|
||||
/// layout → one viewport per floating content viewport. Each entry is
|
||||
/// rendered into its own screen rectangle by its own inner pipeline.
|
||||
pub(super) fn build_viewports(
|
||||
pub(in crate::scene) fn build_viewports(
|
||||
&self,
|
||||
bounds: Rectangle,
|
||||
model_render_mode: acadrust::entities::ViewportRenderMode,
|
||||
@@ -805,7 +805,7 @@ impl Scene {
|
||||
/// The cube draws in the top-right of the *active model tile* (which fills
|
||||
/// the canvas when there is a single tile), so the hover hit-test maps the
|
||||
/// cursor into that tile's local space and uses the tile's dimensions.
|
||||
pub(super) fn update_viewcube_state(
|
||||
pub(in crate::scene) fn update_viewcube_state(
|
||||
&self,
|
||||
state: &mut CameraState,
|
||||
bounds: Rectangle,
|
||||
@@ -828,7 +828,7 @@ impl Scene {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn viewcube_mouse_interaction(&self, state: &CameraState) -> mouse::Interaction {
|
||||
pub(in crate::scene) fn viewcube_mouse_interaction(&self, state: &CameraState) -> mouse::Interaction {
|
||||
if state.hover_region.is_some() {
|
||||
mouse::Interaction::Pointer
|
||||
} else {
|
||||
File renamed without changes.
@@ -7,7 +7,7 @@
|
||||
//! dedicated inner `Pipeline` (kept in the `MultiPipeline` outer).
|
||||
|
||||
use super::render::{CameraState, Primitive};
|
||||
use super::Scene;
|
||||
use crate::scene::Scene;
|
||||
use iced::widget::shader;
|
||||
use iced::{mouse, Event, Rectangle};
|
||||
|
||||
Reference in new issue
Block a user