forked from 1b-agent/fieldcraft-upstream
mesh: geometry.features, mesh.plan and mesh.plan.apply from the feature graph (#402)
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
1 parent
8dd7446569
commit
efe90ec50e
12 files changed
+1449
-4
No files matched your search
@@ -100,6 +100,7 @@ is committed and listed here with its exports.
|
||||
| assets/icons/mesh.surface.check.svg, assets/icons/mesh.surface.check.vectorcraft | FieldCraft contributors | Original toolbar icon drawn with VectorCraft from `assets/icons/src/mesh.surface.check.json` (`cargo xtask icons`) | MIT OR Apache-2.0 |
|
||||
| assets/icons/mesh.tag.svg, assets/icons/mesh.tag.vectorcraft | FieldCraft contributors | Original toolbar icon drawn with VectorCraft from `assets/icons/src/mesh.tag.json` (`cargo xtask icons`) | MIT OR Apache-2.0 |
|
||||
| assets/icons/mesh.tet.generate.svg, assets/icons/mesh.tet.generate.vectorcraft | FieldCraft contributors | Original toolbar icon drawn with VectorCraft from `assets/icons/src/mesh.tet.generate.json` (`cargo xtask icons`) | MIT OR Apache-2.0 |
|
||||
| assets/icons/mesh.plan.svg, assets/icons/mesh.plan.vectorcraft | FieldCraft contributors | Original toolbar icon drawn with VectorCraft from `assets/icons/src/mesh.plan.json` (`cargo xtask icons`) | MIT OR Apache-2.0 |
|
||||
| assets/icons/mesh.planar.svg, assets/icons/mesh.planar.vectorcraft | FieldCraft contributors | Original toolbar icon drawn with VectorCraft from `assets/icons/src/mesh.planar.json` (`cargo xtask icons`) | MIT OR Apache-2.0 |
|
||||
| assets/icons/mesh.transition.svg, assets/icons/mesh.transition.vectorcraft | FieldCraft contributors | Original toolbar icon drawn with VectorCraft from `assets/icons/src/mesh.transition.json` (`cargo xtask icons`) | MIT OR Apache-2.0 |
|
||||
| assets/icons/mesh.untangle.svg, assets/icons/mesh.untangle.vectorcraft | FieldCraft contributors | Original toolbar icon drawn with VectorCraft from `assets/icons/src/mesh.untangle.json` (`cargo xtask icons`) | MIT OR Apache-2.0 |
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24">
|
||||
<title>mesh.plan</title>
|
||||
<g id="Layer_1" data-name="Layer 1">
|
||||
<path d="M12 3 L20 7 L20 16 L12 20 L4 16 L4 7 Z" fill="#2d4e70" stroke="#dde2ea" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4 7 L12 11 L20 7 M12 11 L12 20" fill="none" stroke="#dde2ea" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4 11.5 L12 15.5 L20 11.5 M8 9 L8 18 M16 9 L16 18" fill="none" stroke="#4f9ce8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 3 L12 5.4 M20 7 L15.2 7 M12 11 L12 8.6 M4 7 L8.8 7" fill="none" stroke="#4f9ce8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M8.8 7 C8.8 7.884 10.233 8.6 12 8.6 C13.767 8.6 15.2 7.884 15.2 7 C15.2 6.116 13.767 5.4 12 5.4 C10.233 5.4 8.8 6.116 8.8 7 Z" fill="none" stroke="#f0b44a" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
@@ -0,0 +1 @@
|
||||
{"shapes": [{"d": "M12 3 L20 7 L20 16 L12 20 L4 16 L4 7 Z", "fill": "soft", "stroke": "line"}, {"d": "M4 7 L12 11 L20 7 M12 11 L12 20", "stroke": "line"}, {"d": "M4 11.5 L12 15.5 L20 11.5 M8 9 L8 18 M16 9 L16 18", "stroke": "accent", "width": 1}, {"d": "M12 3 L12 5.4 M20 7 L15.2 7 M12 11 L12 8.6 M4 7 L8.8 7", "stroke": "accent", "width": 1}, {"d": "M8.8 7 A3.2 1.6 0 1 0 15.2 7 A3.2 1.6 0 1 0 8.8 7 Z", "stroke": "warm", "width": 1.5}]}
|
||||
@@ -244,7 +244,7 @@ pub static CATALOGUE: &[Capability] = &[
|
||||
Geometry,
|
||||
"Inspect",
|
||||
"List the open design's bodies, volumes and named faces, edges and vertices",
|
||||
&["geometry.centre"],
|
||||
&["geometry.centre", "geometry.features"],
|
||||
),
|
||||
ready("geometry.tessellate", "Tessellate", Geometry, "Inspect", "Tessellate a body into a face-tagged boundary surface"),
|
||||
ready(
|
||||
@@ -332,6 +332,14 @@ pub static CATALOGUE: &[Capability] = &[
|
||||
"One mesh of a partitioned part, a method per cell (hex, tet, or all hex as conforming blocks); cells joined across partitions",
|
||||
&["mesh.hex.blocks"],
|
||||
),
|
||||
ready_more(
|
||||
"mesh.plan",
|
||||
"Mesh Plan",
|
||||
Mesh,
|
||||
"Structured Hex",
|
||||
"A hex-meshing plan from the features that built a body (sweeps, blocks, O-grids, layers, imprints, copies); saved, editable, applied with the structured meshers",
|
||||
&["mesh.plan.apply"],
|
||||
),
|
||||
ready_more(
|
||||
"mesh.block",
|
||||
"Mesh Block",
|
||||
|
||||
@@ -1599,6 +1599,15 @@ pub const PARTITION: &[Field] = &[
|
||||
references("bodies", "Bodies", C::Body).help("The bodies to divide; every body the plane cuts when empty.").hint("every body the plane divides"),
|
||||
];
|
||||
pub const PARTITION_DELETE: &[Field] = existing!("Partition", C::Partition);
|
||||
pub const MESH_PLAN: &[Field] = &[
|
||||
reference("body", "Body", C::Body).help("The body of the design to plan (as opened, not a partition cell).").req(),
|
||||
text("name", "Name").help("The name the plan is saved under.").hint("Plan-<body>"),
|
||||
positive("size", "Element size", D::Millimetre).help("The largest element size; thin walls make it smaller.").hint("1/25 of the diagonal"),
|
||||
int("layers", "Layers through walls", 1, 10).help("Elements through the thinnest wall the features give (a shell, a plate).").hint("2"),
|
||||
int("max_elements", "Most elements", 1, 5_000_000).help("The size grows past the walls' when more elements would be needed.").hint("300000"),
|
||||
];
|
||||
pub const MESH_PLAN_APPLY: &[Field] = &[text("plan", "Plan").help("The name of a saved mesh plan (mesh.plan).").req()];
|
||||
pub const FEATURES: &[Field] = &[reference("body", "Body", C::Body).help("Only this body's features.").hint("every body")];
|
||||
pub const TESSELLATE: &[Field] = &[reference("body", "Body", C::Body).help("The body to tessellate.").req(), TOLERANCE];
|
||||
pub const IMPORT_MESH: &[Field] = &[path("path", "3MF file", false).help("A 3MF surface mesh with named faces.").req()];
|
||||
pub const FILE_OPEN: &[Field] = &[path("path", "Model", false).help("The FieldCraft model file to open.").req()];
|
||||
|
||||
@@ -33,6 +33,16 @@ pub const BODIES: CommandSpec = CommandSpec {
|
||||
run: bodies,
|
||||
};
|
||||
|
||||
pub const FEATURES: CommandSpec = CommandSpec {
|
||||
id: "geometry.features",
|
||||
label: "Feature Graph",
|
||||
params: r#"{"body"?: "Base" (default: every body)} — the features of the SolidCraft timeline that built each body of the open design, in order (read-only): each feature's `id` (`F<id>` in persistent names), `name`, `type` (extrude, box, cylinder, hole, shell, fillet, combine, pattern, mirror, split, …), `operation` (new_body, join, cut, intersect), `values` (its numeric inputs evaluated: mm, radians, counts), `placement` (a primitive's corner, base or centre), `direction` (an extrude's along the sketch normal, a cylinder's axis, a hole's drilling direction), `two_sided`, `sketch` (plane origin, axes and normal, and the profiles used: lines, arcs and circles in sketch coordinates, holes, curve ids), `transforms` (pattern and mirror copies, 4×4 row-major), `targets` and `tools`, the stored `definition`, the persistent `faces` and `edges` of the body it made, and `error` when it failed to rebuild. A partition cell is described by the body it was cut from"#,
|
||||
form: Some(super::forms::FEATURES),
|
||||
kind: CommandKind::Model,
|
||||
requires: &[Requirement::Design],
|
||||
run: features,
|
||||
};
|
||||
|
||||
pub const CENTRE: CommandSpec = CommandSpec {
|
||||
id: "geometry.centre",
|
||||
label: "Centre of Selection",
|
||||
@@ -170,6 +180,16 @@ fn bodies(s: &mut Session, _: &Value) -> Result<Value> {
|
||||
Ok(Value::Array(out))
|
||||
}
|
||||
|
||||
fn features(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let body = match p.get("body") {
|
||||
None | Some(Value::Null) => None,
|
||||
Some(v) => Some(v.as_str().ok_or_else(|| EngineError::param("body", ProblemKind::NotAName))?),
|
||||
};
|
||||
let design = s.design.as_deref().ok_or_else(|| EngineError::Params("open a design first".into()))?;
|
||||
let graph = design.features(body)?;
|
||||
Ok(json!({"bodies": graph}))
|
||||
}
|
||||
|
||||
/// Most geometry items and nodes `geometry.centre` takes.
|
||||
const MAX_CENTRE_ITEMS: usize = 10_000;
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ mod mirror;
|
||||
mod model;
|
||||
pub(crate) mod nodes;
|
||||
pub(crate) mod octree;
|
||||
pub(crate) mod plan;
|
||||
mod project;
|
||||
pub(crate) mod relax;
|
||||
pub mod replace;
|
||||
@@ -47,6 +48,7 @@ pub static ALL: &[CommandSpec] = &[
|
||||
model::INFO,
|
||||
geometry::OPEN,
|
||||
geometry::BODIES,
|
||||
geometry::FEATURES,
|
||||
geometry::CENTRE,
|
||||
geometry::TESSELLATE,
|
||||
geometry::IMPORT_MESH,
|
||||
@@ -83,6 +85,8 @@ pub static ALL: &[CommandSpec] = &[
|
||||
hex::HEX_BLOCKS,
|
||||
project::HEX_PROJECT,
|
||||
cells::CELLS,
|
||||
plan::PLAN,
|
||||
plan::APPLY,
|
||||
seeds::SEED_EDGE,
|
||||
seeds::SEED_CLEAR,
|
||||
seeds::SEED_LIST,
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -52,6 +52,7 @@ const REMESH: &[&str] = &[
|
||||
"mesh.hex.sweep",
|
||||
"mesh.hex.mapped",
|
||||
"mesh.hex.blocks",
|
||||
"mesh.plan.apply",
|
||||
"mesh.hex.octree",
|
||||
"mesh.cells",
|
||||
"mesh.block",
|
||||
|
||||
@@ -130,6 +130,7 @@ pub static MESHERS: &[(&str, Targets)] = &[
|
||||
("mesh.hex.blocks", listed_or_all),
|
||||
("mesh.hex.project", body_or_all),
|
||||
("mesh.cells", listed_or_all),
|
||||
("mesh.plan.apply", super::plan::plan_bodies),
|
||||
("mesh.planar", super::mesh::planar_body),
|
||||
("mesh.mirror", super::mirror::image_names),
|
||||
];
|
||||
@@ -173,7 +174,7 @@ fn made_kind(command: &str, params: &Value) -> Option<ElementKind> {
|
||||
Some(if params.get("order").and_then(Value::as_str) == Some("quadratic") { ElementKind::Tet10 } else { ElementKind::Tet4 })
|
||||
}
|
||||
"mesh.hex.project" => Some(ElementKind::Hex8),
|
||||
"mesh.hex.generate" | "mesh.hex.sweep" | "mesh.hex.mapped" | "mesh.hex.blocks" => {
|
||||
"mesh.hex.generate" | "mesh.hex.sweep" | "mesh.hex.mapped" | "mesh.hex.blocks" | "mesh.plan.apply" => {
|
||||
Some(if params.get("order").and_then(Value::as_i64) == Some(2) { ElementKind::Hex20 } else { ElementKind::Hex8 })
|
||||
}
|
||||
_ => None,
|
||||
@@ -313,6 +314,13 @@ mod tests {
|
||||
("mesh.hex.blocks", vec![tet_other.clone()], json!({"bodies": ["Box.1"], "size": 2.5}), "Box.1", "Box.2"),
|
||||
("mesh.hex.project", vec![tet_other.clone()], json!({"body": "Box.1", "size": 2.5, "merge": 0}), "Box.1", "Box.2"),
|
||||
("mesh.cells", vec![tet_other.clone()], json!({"bodies": ["Box.1"], "size": 2.5, "default": "hex"}), "Box.1", "Box.2"),
|
||||
(
|
||||
"mesh.plan.apply",
|
||||
vec![tet_other.clone(), json!({"command": "mesh.plan", "params": {"body": "Box.1", "size": 2.5}})],
|
||||
json!({"plan": "Plan-Box_1"}),
|
||||
"Box.1",
|
||||
"Box.2",
|
||||
),
|
||||
("mesh.planar", vec![tet_other.clone()], json!({"polygon": square, "size": 2.5, "name": "Box.1"}), "Box.1", "Box.2"),
|
||||
// The image's tets (meshed before) are replaced by the mirrored hexahedra.
|
||||
(
|
||||
|
||||
+3
-2
@@ -6,7 +6,7 @@ Every entry is a toolbar button. **Ready** entries run their command; **coming s
|
||||
shown greyed with the roadmap milestone that brings them (ROADMAP.md). Scripts, the command line,
|
||||
the control channel and AI agents over MCP (docs/mcp.md) run the same commands.
|
||||
|
||||
71 ready, 39 coming soon.
|
||||
72 ready, 39 coming soon.
|
||||
|
||||
## Geometry
|
||||
|
||||
@@ -14,7 +14,7 @@ the control channel and AI agents over MCP (docs/mcp.md) run the same commands.
|
||||
|---|---|---|---|
|
||||
| Import | Open Design — Open a SolidCraft design: its bodies with persistent face names | ready | `geometry.open` |
|
||||
| Import | Tagged Mesh — Read a face-tagged surface mesh (3MF) | ready | `geometry.import.mesh` |
|
||||
| Inspect | Bodies — List the open design's bodies, volumes and named faces, edges and vertices | ready | `geometry.bodies`, `geometry.centre` |
|
||||
| Inspect | Bodies — List the open design's bodies, volumes and named faces, edges and vertices | ready | `geometry.bodies`, `geometry.centre`, `geometry.features` |
|
||||
| Inspect | Tessellate — Tessellate a body into a face-tagged boundary surface | ready | `geometry.tessellate` |
|
||||
| Inspect | Check Surface — Weld, closed, manifold, orientation and self-intersection checks; feature edges | ready | `mesh.surface.check` |
|
||||
| Prepare | Datum Plane — A datum plane: principal plane and offset, three points or datum points, point and normal, normal to an axis | ready | `geometry.datum`, (`geometry.datum.edit`), (`geometry.datum.delete`), (`geometry.datum.suppress`) |
|
||||
@@ -37,6 +37,7 @@ the control channel and AI agents over MCP (docs/mcp.md) run the same commands.
|
||||
| Structured Hex | Quad Faces — All-quad meshes of faces on shared edge nodes | coming soon (M5) | |
|
||||
| Structured Hex | Hex Mesh — Structured hex mesh of a body: automatic, or extruded, swept, revolved or mapped (the Method drop-down) | ready | `mesh.hex.generate`, (`mesh.hex.sweep`), (`mesh.hex.mapped`) |
|
||||
| Structured Hex | Partitioned Mesh — One mesh of a partitioned part, a method per cell (hex, tet, or all hex as conforming blocks); cells joined across partitions | ready | `mesh.cells`, (`mesh.hex.blocks`) |
|
||||
| Structured Hex | Mesh Plan — A hex-meshing plan from the features that built a body (sweeps, blocks, O-grids, layers, imprints, copies); saved, editable, applied with the structured meshers | ready | `mesh.plan`, `mesh.plan.apply` |
|
||||
| Structured Hex | Mesh Block — A mapped block of hex, wedge or quad elements between corner points; projection blocking (a block topology projected onto the part) | ready | `mesh.block`, `mesh.hex.project` |
|
||||
| Structured Hex | Decompose — Find sweepable and mappable regions of 3D parts (extruded cross-sections are already cut into blocks by Hex Mesh) | coming soon (M5) | |
|
||||
| Structured Hex | Transition — Conforming tet–hex interfaces with pyramids | coming soon (M5) | |
|
||||
|
||||
Reference in new issue
Block a user