forked from 1b-agent/opencadstudio-upstream
feat: raster image render pipeline (IMAGE command + GPU textured quad)
- Add IMAGE / IMAGEATTACH / IM command with async file-picker (rfd) - src/scene/image_model.rs: ImageModel with RGBA pixel data + quad corners - src/scene/pipeline/image_gpu.rs: ImageGpu with wgpu texture, sampler, opacity uniform - src/scene/pipeline/mod.rs: image render pipeline (pass 2, between hatches and meshes) - src/scene/render.rs: include images in Primitive, call upload_images in prepare - src/scene/mod.rs: images HashMap, populate_images_from_document, cache on add_entity - src/modules/home/draw/raster_image.rs: ImageCommand (two-click: origin + width, aspect-correct preview) - src/io/mod.rs: pick_image_file() async helper (file dialog + dimension decode) - app: ImagePick / ImagePickResult messages, populate on file open and undo/redo Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
1 parent
3d1f355a5c
commit
4312b6c3a5
39 files changed
+702
-15
No files matched your search
@@ -87,6 +87,7 @@ impl CadCommand for AlignedDimensionCommand {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
})
|
||||
}
|
||||
@@ -119,6 +120,7 @@ fn preview_aligned(p1: Vec3, p2: Vec3, dim_pt: Vec3) -> WireModel {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
key_vertices: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
}
|
||||
}
|
||||
@@ -122,7 +122,8 @@ fn preview_wire(points: Vec<Vec3>) -> WireModel {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
key_vertices: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -86,6 +86,7 @@ fn preview_line(a: Vec3, b: Vec3) -> WireModel {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
key_vertices: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
}
|
||||
}
|
||||
@@ -128,6 +128,7 @@ impl CadCommand for DimBaselineCommand {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -125,6 +125,7 @@ impl CadCommand for DimContinueCommand {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -199,7 +199,8 @@ fn preview_wire(pts: &[Vec3]) -> WireModel {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
key_vertices: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -110,7 +110,8 @@ fn preview_wire(points: Vec<Vec3>) -> WireModel {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
key_vertices: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -152,7 +152,8 @@ fn preview_wire(pts: &[Vec3]) -> WireModel {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
key_vertices: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,7 @@ fn preview_wire(points: Vec<Vec3>) -> WireModel {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
key_vertices: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
}
|
||||
}
|
||||
@@ -130,7 +130,8 @@ impl CadCommand for TableCommand {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
key_vertices: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -83,6 +83,7 @@ impl CadCommand for ToleranceCommand {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -131,6 +131,7 @@ impl CadCommand for AttdefCommand {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -524,6 +524,7 @@ fn line_wire(from: Vec3, to: Vec3) -> WireModel {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
key_vertices: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
}
|
||||
}
|
||||
@@ -85,6 +85,7 @@ impl CadCommand for LineCommand {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ pub mod line;
|
||||
pub mod mline;
|
||||
pub mod point;
|
||||
pub mod polyline;
|
||||
pub mod raster_image;
|
||||
pub mod ray;
|
||||
pub mod revcloud;
|
||||
pub mod shapes;
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
// IMAGE / IMAGEATTACH command — place a raster image in the drawing.
|
||||
//
|
||||
// Workflow:
|
||||
// 1. File dialog opens (async, handled in update.rs).
|
||||
// 2. User picks insertion point (first click).
|
||||
// 3. User drags to pick width; height is computed from the image's aspect ratio.
|
||||
// 4. Entity is committed.
|
||||
|
||||
use acadrust::entities::RasterImage;
|
||||
use acadrust::types::Vector3;
|
||||
use acadrust::EntityType;
|
||||
use glam::Vec3;
|
||||
|
||||
use crate::command::{CadCommand, CmdResult};
|
||||
use crate::scene::wire_model::WireModel;
|
||||
|
||||
pub struct ImageCommand {
|
||||
file_path: String,
|
||||
pixel_width: u32,
|
||||
pixel_height: u32,
|
||||
origin: Option<Vec3>,
|
||||
}
|
||||
|
||||
impl ImageCommand {
|
||||
pub fn new(file_path: String, pixel_width: u32, pixel_height: u32) -> Self {
|
||||
Self { file_path, pixel_width, pixel_height, origin: None }
|
||||
}
|
||||
|
||||
fn aspect(&self) -> f64 {
|
||||
if self.pixel_height == 0 {
|
||||
1.0
|
||||
} else {
|
||||
self.pixel_width as f64 / self.pixel_height as f64
|
||||
}
|
||||
}
|
||||
|
||||
fn make_entity(&self, origin: Vec3, width_pt: Vec3) -> EntityType {
|
||||
let world_width = ((width_pt.x - origin.x) as f64).abs().max(0.001);
|
||||
let world_height = world_width / self.aspect();
|
||||
|
||||
let ins = Vector3::new(origin.x as f64, origin.z as f64, origin.y as f64);
|
||||
|
||||
let mut img = RasterImage::with_size(
|
||||
&self.file_path,
|
||||
ins,
|
||||
self.pixel_width as f64,
|
||||
self.pixel_height as f64,
|
||||
world_width,
|
||||
world_height,
|
||||
);
|
||||
img.flags = acadrust::entities::ImageDisplayFlags::SHOW_IMAGE
|
||||
| acadrust::entities::ImageDisplayFlags::USE_CLIPPING_BOUNDARY;
|
||||
EntityType::RasterImage(img)
|
||||
}
|
||||
}
|
||||
|
||||
impl CadCommand for ImageCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
"IMAGE"
|
||||
}
|
||||
|
||||
fn prompt(&self) -> String {
|
||||
if self.origin.is_none() {
|
||||
format!("IMAGE Specify insertion point ({}): ", short_name(&self.file_path))
|
||||
} else {
|
||||
"IMAGE Specify width (drag right):".into()
|
||||
}
|
||||
}
|
||||
|
||||
fn on_point(&mut self, pt: Vec3) -> CmdResult {
|
||||
if let Some(origin) = self.origin {
|
||||
let entity = self.make_entity(origin, pt);
|
||||
CmdResult::CommitAndExit(entity)
|
||||
} else {
|
||||
self.origin = Some(pt);
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
}
|
||||
|
||||
fn on_enter(&mut self) -> CmdResult {
|
||||
// If origin is set, place with a default width of 1 unit * pixel count / 100
|
||||
if let Some(origin) = self.origin {
|
||||
let default_w = (self.pixel_width as f64 / 100.0).max(1.0);
|
||||
let width_pt = Vec3::new(origin.x + default_w as f32, origin.y, origin.z);
|
||||
let entity = self.make_entity(origin, width_pt);
|
||||
CmdResult::CommitAndExit(entity)
|
||||
} else {
|
||||
CmdResult::Cancel
|
||||
}
|
||||
}
|
||||
|
||||
fn on_mouse_move(&mut self, pt: Vec3) -> Option<WireModel> {
|
||||
let origin = self.origin?;
|
||||
let world_width = (pt.x - origin.x).abs().max(0.001);
|
||||
let world_height = world_width / self.aspect() as f32;
|
||||
|
||||
let p0 = [origin.x, origin.y, origin.z];
|
||||
let p1 = [origin.x + world_width, origin.y, origin.z];
|
||||
let p2 = [origin.x + world_width, origin.y + world_height, origin.z];
|
||||
let p3 = [origin.x, origin.y + world_height, origin.z];
|
||||
|
||||
Some(WireModel {
|
||||
name: "image_preview".into(),
|
||||
points: vec![p0, p1, p2, p3, p0],
|
||||
color: WireModel::CYAN,
|
||||
selected: false,
|
||||
pattern_length: 0.0,
|
||||
pattern: [0.0; 8],
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn short_name(path: &str) -> &str {
|
||||
std::path::Path::new(path)
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.unwrap_or(path)
|
||||
}
|
||||
@@ -83,6 +83,7 @@ impl CadCommand for RayCommand {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
})
|
||||
}
|
||||
@@ -159,6 +160,7 @@ impl CadCommand for XLineCommand {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ impl CadCommand for RevCloudCommand {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -104,7 +104,8 @@ impl CadCommand for WipeoutCommand {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
key_vertices: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
})
|
||||
}
|
||||
WipeoutMode::Polygonal => {
|
||||
@@ -123,7 +124,8 @@ impl CadCommand for WipeoutCommand {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
key_vertices: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ impl CadCommand for AreaCommand {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ impl CadCommand for DistCommand {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -100,7 +100,8 @@ impl CadCommand for StretchCommand {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
key_vertices: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -95,6 +95,7 @@ impl CadCommand for MviewCommand {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ impl CadCommand for PlotWindowCommand {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ impl CadCommand for ZoomWindowCommand {
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user