Kernel rig: export and print face cases in the import area

- file.export_step, export_stl, export_3mf, export_obj: a 30x20x10 box
  written and read back (the meshes converted to a solid): V 6000,
  A 2200, 6 faces, box (5,-10,0)-(35,10,10).
- file.export_urdf: a hinged arm on a grounded base: 3 links, 2 joints,
  and the base's mesh, written in metres, reads back as the
  1 x 0.5 x 0.2 box (V 0.1, A 1.6).
- body.print_face: a cylinder's flat end is accepted, its side refused.

PATHS adds the URDF writer and the print face modules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M41G8Nua7HvuFPZkmJ
This commit is contained in:
jyviko
2026-11-02 17:34:24 -05:00
parent c1f45cae2e
commit 0846954169
15 changed files with 503 additions and 1 deletions
+5 -1
View File
@@ -1,14 +1,18 @@
# The source files whose change reruns this area of the kernel rig (scripts/ci/kernel_rig_plan.py),
# besides the shared core in ../PATHS-ALL. One git pathspec glob a line, relative to the
# repository root; `#` starts a comment.
# STEP and IGES import and export, mesh import (3MF, STL, OBJ, glTF) and mesh-to-solid conversion.
# STEP and IGES import and export, mesh import and export (3MF, STL, OBJ, glTF, URDF), mesh-to-solid conversion
# and the print face a body is laid on.
crates/doc/src/print_face.rs
crates/engine/src/cmd/file.rs
crates/engine/src/cmd/file_import.rs
crates/engine/src/cmd/mesh.rs
crates/engine/src/cmd/print_face.rs
crates/io/src/formats.rs
crates/io/src/gltf_in.rs
crates/io/src/obj.rs
crates/io/src/threemf.rs
crates/io/src/urdf.rs
crates/kernel/src/iges_in.rs
crates/kernel/src/iges_in/**
crates/kernel/src/iges_out.rs
@@ -0,0 +1,22 @@
{
"sources": [],
"what": "a box written by Export 3MF, read back as a mesh and converted to a solid",
"source": "analytic",
"method": "box 30x20x10 from (5,-10,0): every face is flat, so its mesh is exact and the converted solid is the box: V = 6000, A = 2200, 6 faces, box (5,-10,0)-(35,10,10)",
"body_count": 1,
"faces": 6,
"volume_mm3": 6000.0,
"area_mm2": 2200.0,
"bbox": {
"min": [
5.0,
-10.0,
0.0
],
"max": [
35.0,
10.0,
10.0
]
}
}
@@ -0,0 +1,49 @@
{
"commands": [
{
"command": "solid.box",
"params": {
"length": 30,
"width": 20,
"height": 10,
"corner": [
5,
-10,
0
],
"operation": "new",
"body_name": "Block"
}
},
{
"command": "file.export_3mf",
"params": {
"path": "$OUT/cov-export-3mf-box.3mf"
}
},
{
"command": "file.new",
"params": {
"name": "Reimported"
}
},
{
"command": "file.insert_mesh",
"params": {
"path": "$OUT/cov-export-3mf-box.3mf"
}
},
{
"command": "component.activate",
"params": {
"component": "cov-export-3mf-box"
}
},
{
"command": "mesh.convert",
"params": {
"body": "Block"
}
}
]
}
@@ -0,0 +1,22 @@
{
"sources": [],
"what": "a box written by Export OBJ, read back as a mesh and converted to a solid",
"source": "analytic",
"method": "box 30x20x10 from (5,-10,0): every face is flat, so its mesh is exact and the converted solid is the box: V = 6000, A = 2200, 6 faces, box (5,-10,0)-(35,10,10)",
"body_count": 1,
"faces": 6,
"volume_mm3": 6000.0,
"area_mm2": 2200.0,
"bbox": {
"min": [
5.0,
-10.0,
0.0
],
"max": [
35.0,
10.0,
10.0
]
}
}
@@ -0,0 +1,49 @@
{
"commands": [
{
"command": "solid.box",
"params": {
"length": 30,
"width": 20,
"height": 10,
"corner": [
5,
-10,
0
],
"operation": "new",
"body_name": "Block"
}
},
{
"command": "file.export_obj",
"params": {
"path": "$OUT/cov-export-obj-box.obj"
}
},
{
"command": "file.new",
"params": {
"name": "Reimported"
}
},
{
"command": "file.insert_mesh",
"params": {
"path": "$OUT/cov-export-obj-box.obj"
}
},
{
"command": "component.activate",
"params": {
"component": "cov-export-obj-box"
}
},
{
"command": "mesh.convert",
"params": {
"body": "Block"
}
}
]
}
@@ -0,0 +1,22 @@
{
"sources": [],
"what": "a box written by Export STEP and read back",
"source": "analytic",
"method": "box 30x20x10 from (5,-10,0): V = 6000, A = 2(600 + 300 + 200) = 2200, 6 faces, box (5,-10,0)-(35,10,10). The re-imported body is graded, not the exported one (the design holds only the import)",
"body_count": 1,
"faces": 6,
"volume_mm3": 6000.0,
"area_mm2": 2200.0,
"bbox": {
"min": [
5.0,
-10.0,
0.0
],
"max": [
35.0,
10.0,
10.0
]
}
}
@@ -0,0 +1,37 @@
{
"commands": [
{
"command": "solid.box",
"params": {
"length": 30,
"width": 20,
"height": 10,
"corner": [
5,
-10,
0
],
"operation": "new",
"body_name": "Block"
}
},
{
"command": "file.export_step",
"params": {
"path": "$OUT/cov-export-step-box.step"
}
},
{
"command": "file.new",
"params": {
"name": "Reimported"
}
},
{
"command": "file.insert_step",
"params": {
"path": "$OUT/cov-export-step-box.step"
}
}
]
}
@@ -0,0 +1,22 @@
{
"sources": [],
"what": "a box written by Export STL, read back as a mesh and converted to a solid",
"source": "analytic",
"method": "box 30x20x10 from (5,-10,0): every face is flat, so its mesh is exact and the converted solid is the box: V = 6000, A = 2200, 6 faces, box (5,-10,0)-(35,10,10)",
"body_count": 1,
"faces": 6,
"volume_mm3": 6000.0,
"area_mm2": 2200.0,
"bbox": {
"min": [
5.0,
-10.0,
0.0
],
"max": [
35.0,
10.0,
10.0
]
}
}
@@ -0,0 +1,49 @@
{
"commands": [
{
"command": "solid.box",
"params": {
"length": 30,
"width": 20,
"height": 10,
"corner": [
5,
-10,
0
],
"operation": "new",
"body_name": "Block"
}
},
{
"command": "file.export_stl",
"params": {
"path": "$OUT/cov-export-stl-box.stl"
}
},
{
"command": "file.new",
"params": {
"name": "Reimported"
}
},
{
"command": "file.insert_mesh",
"params": {
"path": "$OUT/cov-export-stl-box.stl"
}
},
{
"command": "component.activate",
"params": {
"component": "cov-export-stl-box"
}
},
{
"command": "mesh.convert",
"params": {
"body": "cov-export-stl-box"
}
}
]
}
@@ -0,0 +1,20 @@
{
"sources": [],
"what": "an arm hinged on a grounded base written by Export URDF, and the base's mesh read back",
"source": "analytic",
"method": "Base (grounded): box 1000x500x200; Arm: box 400x100x100 on a revolute joint. The tree: base_link, Base fixed to it, Arm on the revolute: 3 links, 2 joints. Meshes are written in metres, so the base's STL read back as millimetres is the box 1 x 0.5 x 0.2: V = 0.1, A = 2(0.5 + 0.2 + 0.1) = 1.6, 6 faces",
"body_count": 1,
"faces": 6,
"volume_mm3": 0.1,
"area_mm2": 1.6,
"results": [
{
"step": 8,
"json": {
"format": "Urdf",
"links": 3,
"joints": 2
}
}
]
}
@@ -0,0 +1,135 @@
{
"commands": [
{
"command": "solid.box",
"params": {
"length": 1000,
"width": 500,
"height": 200,
"corner": [
-500,
-250,
-200
],
"operation": "new",
"body_name": "BaseBox"
}
},
{
"command": "solid.box",
"params": {
"length": 400,
"width": 100,
"height": 100,
"corner": [
0,
-50,
0
],
"operation": "new",
"body_name": "ArmBox"
}
},
{
"command": "component.create",
"params": {
"name": "Base",
"parent": "root",
"activate": false,
"grounded": true
}
},
{
"command": "component.move_bodies",
"params": {
"bodies": [
"BaseBox"
],
"component": "Base"
}
},
{
"command": "component.create",
"params": {
"name": "Arm",
"parent": "root",
"activate": false,
"grounded": false
}
},
{
"command": "component.move_bodies",
"params": {
"bodies": [
"ArmBox"
],
"component": "Arm"
}
},
{
"command": "joint.create",
"params": {
"type": "revolute",
"name": "Hinge",
"flip": true,
"a": {
"occurrence": "Base",
"point": [
0,
0,
0
],
"z": [
0,
0,
1
]
},
"b": {
"occurrence": "Arm",
"point": [
0,
0,
0
],
"z": [
0,
0,
1
]
},
"quiet": true
}
},
{
"command": "file.export_urdf",
"params": {
"path": "$OUT/cov-export-urdf-arm.urdf"
}
},
{
"command": "file.new",
"params": {
"name": "Reimported"
}
},
{
"command": "file.insert_mesh",
"params": {
"path": "$OUT/cov-export-urdf-arm-Base.stl"
}
},
{
"command": "component.activate",
"params": {
"component": "cov-export-urdf-arm-Base"
}
},
{
"command": "mesh.convert",
"params": {
"body": "cov-export-urdf-arm-Base"
}
}
]
}
@@ -0,0 +1,17 @@
{
"sources": [],
"what": "a cylinder's flat end marked as the face a slicer export lays it on",
"source": "analytic",
"method": "(3,4,20) is on the top end of the cylinder r 10, h 20 about z: a flat face, so it is marked, and the body is unchanged: V = pi*100*20 = 6283.1853, 3 faces",
"body_count": 1,
"faces": 3,
"volume_mm3": 6283.185307,
"results": [
{
"step": 2,
"json": {
"body": "Peg"
}
}
]
}
@@ -0,0 +1,23 @@
{
"commands": [
{
"command": "solid.cylinder",
"params": {
"radius": 10,
"height": 20,
"body_name": "Peg"
}
},
{
"command": "body.print_face",
"params": {
"body": "Peg",
"point": [
3,
4,
20
]
}
}
]
}
@@ -0,0 +1,8 @@
{
"sources": [],
"what": "a cylinder's side picked as the face a slicer export lays it on",
"source": "analytic",
"method": "(10,0,10) is on the side of the cylinder r 10, h 20 about z: a curved face, which no body can lie flat on: refused",
"outcome": "refused",
"refusal": "only a flat face can be the print face"
}
@@ -0,0 +1,23 @@
{
"commands": [
{
"command": "solid.cylinder",
"params": {
"radius": 10,
"height": 20,
"body_name": "Peg"
}
},
{
"command": "body.print_face",
"params": {
"body": "Peg",
"point": [
10,
0,
10
]
}
}
]
}