API cam settings - Getting unexpected pass

Hello, I’m using the API engine to run a basic CAM outline operation with a single full-depth pass but I’m running into some issues. If i set my zThru setting to anything > 0, it generates correctly but i’m left with an extra pass of the zThru depth. On the other hand, if i set my zThru to 0, it generates an extra top pass.

This is what i’m expecting:

But this is what I’m getting:

I’m attaching the engine code and settings that I’m using below. Anyone have any ideas on what setting might need to change to turn that extra pass off, or does it seem like a bug in the API?

Here is the code I’m running in Kiri:Moto Javascript Engine API :

const STOCK_MARGIN = 10;
const CUT_THROUGH = 0;

new Engine()
.setListener(display_message)
.load("https://raw.githubusercontent.com/alzatin/A-Test-project-6/refs/heads/main/A-Test-project-6.stl")
// should to call widget.setTopZ here ideally
.then(eng => eng.setMode("CAM"))
.then(eng =>{ 
     const bounds = eng.widget.getBoundingBox();
      const x = bounds.max.x - bounds.min.x;
      const y = bounds.max.y - bounds.min.y;
      const z = bounds.max.z - bounds.min.z;
    return eng.setStock({
        x: x + STOCK_MARGIN,
        y: y + STOCK_MARGIN,
        z: z + CUT_THROUGH, // stock thickness = part thickness + cut-through
        center: {
          x: x / 2,
          y: y / 2,
          z: (z + CUT_THROUGH) / 2, // correct center for full stock thickness
        },
      })})
.then(eng => eng.moveTo(0, 0, 0))
.then(eng=> eng.setTools([{
    id: 1000,
    number: 1,
    type: "endmill",
    name: "end 1/4",
    metric: false,
    shaft_diam: 0.1,
    shaft_len: 1,
    flute_diam: 0.1,
    flute_len: 2,
    taper_tip: 0,
    order: 5
}]))
.then(eng => { 
    const passes = 1;
    const speed = 1500;
    const bounds = eng.widget.getBoundingBox();
      const z = bounds.max.z - bounds.min.z;
      const zBottom = -z; //- CUT_THROUGH; // cut through part thickness plus cut-through
      const down = Math.abs(zBottom) / passes; // Add small epsilon to avoid floating point errors causing extra pass

    console.log("CAM pass debug:", { passes, z, zBottom, down });
      return eng.setProcess({
        camEaseAngle: 10,
        camEaseDown: true,
        camZAnchor: "bottom",
        camDepthFirst: false,
        camZThru: CUT_THROUGH,
        camZClearance: 2.5,
        camZTop: 0, // top of stock
        camZPass: down, // depth per pass
        camZBottom: zBottom, // temp hack to get around setTopZ bug
        camToolInit: true,
        ops: [
          {
            type: "outline",
            tool: 1000,
            spindle: speed,
            step: 0.4,
            steps: 1,
            down: down, // correct depth per pass
            rate: 635,
            plunge: 51,
            dogbones: false,
            omitvoid: false,
            omitthru: true,
            outside: false,
            inside: false,
            wide: false,
            top: false,
            ov_topz: 0,
            ov_botz: 0,
            ov_conv: true,
          },
          /*{
            type: "outline",
            tool: 1000,
            spindle: 13000,
            step: 0.4,
            steps: 1,
            down: down, // correct depth per pass
            rate: 635,
            plunge: 51,
            dogbones: false,
            omitvoid: false,
            omitthru: false,
            outside: false,
            inside: false,
            wide: false,
            top: false,
            ov_topz: 0,
            ov_botz: 0,
            ov_conv: true,
          },*/
        ],
      });})
.then(eng => eng.setDevice({
    mode: "CAM",
    internal: 0,
    bedHeight: 2.5,
    bedWidth: 678.18,
    bedDepth: 1524,
    maxHeight: 150,
    originCenter: false,
    spindleMax: 24000,
    gcodePre: [
        "G20 ; set units to inches (required)",
        "G90 ; absolute position mode (required)"
    ],
    gcodePost: [
        "M05 ; spindle off",
        "M30 ; program end"
    ],
    gcodeDwell: [
        "G4 P{time} ; dwell for {time}ms"
    ],
    gcodeSpindle: [
        "M3 S{speed} ; spindle on at {spindle} rpm"
    ],
    gcodeChange: [
        "M05 ; spindle off",
        "M6 T{tool} ; change tool to '{tool_name}'",
        "G37; get tool offset with ETS"
    ],
    gcodeFExt: "nc",
    gcodeSpace: true,
    gcodeStrip: false,
    deviceName: "Tormach.24R",
    useLaser: false
}))
.then(eng => eng.slice())
.then(eng => eng.prepare())
.then(eng => eng.export())
.then(display_gcode);

sorry this has taken so long to get back to. I loaded your config in the 4.3 release and this is what I get now, which I believe is what you’re expecting?

If I use a smaller step down, I get this which seems to show it’s placing and detecting your object properly.

Yes, thank you that is what i’m expecting with no CUT_THROUGH. Any idea on what the extra pass could have been about, or does it relate to a recent bug fix?

It’s possible it was resolved in a recent bug fix, but I don’t have an automated test that can verify this.