Skip to content

04 · Clearance offset

Grow every part and shrink the sheet by a clearance with nfp_offset_polygon, then nest the offset geometry — the spacing-via-offset workflow (the solver spacing param is ignored).

Project: Download 04_offset.zip — Windows run.bat, macOS/Linux ./run.command.

// 04 offset (clearance) — the right way to nest with a GAP between parts: grow every PART and shrink the
// SHEET by the clearance with nfp_offset_polygon, then nest the OFFSET geometry. nfp_nest / np_nest ignore
// the old `spacing` param on purpose — you offset UPSTREAM, exactly like the Grasshopper Geometry/Sheets
// components and the Rhino nest_geo.offset_nesting_boundary / nest_sheets.offset_sheet_boundary methods.
using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
struct NfpParams {
    public int placementType, rotations, mutationRate, populationSize, seed;
    public double curveTolerance, clipperScale, spacing, sheetSpacing, rotationLimit;
    public int useHoles, exploreConcave, clipByHull, clipByRects, simplify, mode, generations, numSeeds, useParallel;
    public double timeBudgetSecs;
    public int maxSheets, edgeSamples, compactionPasses, tryAllRotations, exactNfp;
}

static class Program {
    [DllImport("nfp_nest", CallingConvention = CallingConvention.Cdecl)]
    static extern int nfp_offset_polygon(int vertex_count, double[] xy, double delta, double miter_limit,
        int max_out_vertices, double[] out_xy);

    [DllImport("nfp_nest", CallingConvention = CallingConvention.Cdecl)]
    static extern int nfp_nest(int part_count, int[] pvc, double[] pxy, int[] pqty, int[] part_rotations,
        int[] phc, int[] phvc, double[] phxy, int sheet_count, int[] svc, double[] sxy,
        int[] shc, int[] shvc, double[] shxy, ref NfpParams p,
        double[] tx, double[] ty, double[] ang, int[] sid, int[] pidx, out int nSheets, out double fitness);

    // grow (delta>0) / shrink (delta<0) one closed ring; returns just the offset vertices.
    static double[] Offset(double[] xy, double delta) {
        var o = new double[64];
        int n = nfp_offset_polygon(xy.Length / 2, xy, delta, 2.0, 32, o);
        var r = new double[2 * n];
        Array.Copy(o, r, 2 * n);
        return r;
    }

    static void Main() {
        const double CLEARANCE = 4.0;                    // gap between parts AND from the sheet edge

        double[] p0    = { 0, 0, 30, 0, 30, 12, 0, 12 };
        double[] p1    = { 0, 0, 20, 0, 20, 20, 0, 20 };
        double[] sheet = { 0, 0, 120, 0, 120, 120, 0, 120 };

        // grow parts by CLEARANCE/2, shrink the sheet by CLEARANCE/2 -> neighbours keep CLEARANCE apart
        double[] g0 = Offset(p0, CLEARANCE * 0.5);
        double[] g1 = Offset(p1, CLEARANCE * 0.5);
        double[] gs = Offset(sheet, -CLEARANCE * 0.5);

        int[] pvc = { g0.Length / 2, g1.Length / 2 };
        double[] pxy = new double[g0.Length + g1.Length];
        Array.Copy(g0, 0, pxy, 0, g0.Length);
        Array.Copy(g1, 0, pxy, g0.Length, g1.Length);
        int[] pqty = { 4, 4 };                           // 4 copies each -> 8 instances
        int[] svc = { gs.Length / 2 };

        var p = new NfpParams { placementType = 1, rotations = 4, mutationRate = 10, populationSize = 10,
                                seed = 1, curveTolerance = 0.3, clipperScale = 1e7, mode = 1, generations = 10, useParallel = 1 };
        var tx = new double[8]; var ty = new double[8]; var ang = new double[8];
        var sid = new int[8]; var pidx = new int[8];
        int placed = nfp_nest(2, pvc, pxy, pqty, null, new int[2], null, null, 1, svc, gs,
                              new int[1], null, null, ref p, tx, ty, ang, sid, pidx, out int nSheets, out double fitness);

        // The poses fit the OFFSET parts; draw your ORIGINAL parts at (tx,ty,ang) and the CLEARANCE gap shows.
        Console.WriteLine($"clearance {CLEARANCE} -> placed {placed}/8 on {nSheets} sheet(s), fitness {fitness:F3}");
        for (int i = 0; i < 8; i++)
            Console.WriteLine($"  part {pidx[i]} -> sheet {sid[i]}  ({tx[i]:F2}, {ty[i]:F2}) @ {ang[i]:F1} deg");
    }
}