- New magneticLasso module: Sobel energy map + Dijkstra shortest-path + Douglas-Peucker - New activeContour module: greedy snake + balloon force for polygon-to-edge refinement - wallTextureSplit: edge barrier mask, morphological mask hole closing, Moore boundary tracing, simplified polygon contours, manual pick map patching, gap absorption - MaskSegmentCanvas: full lasso gesture pipeline (tap vertices, drag, magnetic paths, close polygon, endLasso/cancelLasso/deleteLasso) - New maskConfig options: splitWallsEdgeBarrierThreshold, splitWallsCloseMaskRadius, manualSplitWalls, manualSplitWallsMaxCount, manualSplitWallsGapAbsorbDilatePx, magneticLasso, activeContourRefine - New ref methods: startLasso, endLasso, cancelLasso, getManualRegions, deleteLasso - New exported types: LassoPolygon, ManualWallPartition - RegionMaskData carries indexToName and wallSemanticIdx through downsample - Simplify README to point to documentation site - Update documentation site (EN + ZH-CN) with all new APIs and interaction guide - Example app: lasso mode toggles and operation buttons Co-authored-by: Cursor <cursoragent@cursor.com>
113 lines
4.2 KiB
TypeScript
113 lines
4.2 KiB
TypeScript
/**
|
||
* Magnetic Lasso — edge-snapping polygon placement for manual wall splitting.
|
||
*
|
||
* Pipeline:
|
||
* 1. buildEnergyMap → grayscale + downsample + Sobel gradient → energy grid
|
||
* 2. findShortestPath → Dijkstra 8-connected on low-energy (edge) pixels
|
||
* 3. extractCornerPoints → Douglas-Peucker simplification on raw path
|
||
* 4. upscalePath → map energy-space coords back to original image coords
|
||
*/
|
||
export type EnergyMap = {
|
||
/** Float32Array per-pixel energy values [0…1]; low = edge, high = flat */
|
||
map: Float32Array;
|
||
w: number;
|
||
h: number;
|
||
/** Downscale ratio: energyDim / sourceDim (≈ em.w / sourceCols) */
|
||
scale: number;
|
||
/** Optional 0/1 mask at energy resolution; 0 = blocked for pathfinding */
|
||
traversable?: Uint8Array;
|
||
};
|
||
/** Seg-resolution wall mask used to constrain lasso vertices. */
|
||
export type WallMaskSample = {
|
||
labels: Uint8Array;
|
||
baseboardBinary: Uint8Array;
|
||
cols: number;
|
||
rows: number;
|
||
wallSemanticIdx: number;
|
||
};
|
||
/** True when norm coords fall on a wall semantic pixel (excludes baseboard). */
|
||
export declare function isNormPointOnWallMask(normX: number, normY: number, mask: WallMaskSample): boolean;
|
||
export declare function filterVerticesToWallMask<T extends {
|
||
x: number;
|
||
y: number;
|
||
}>(vertices: T[], mask: WallMaskSample): T[];
|
||
/**
|
||
* Snap a normalized point to the nearest wall-mask boundary pixel when the
|
||
* touch falls within `snapRadiusSegPx` (segmentation resolution) of the edge.
|
||
*/
|
||
export declare function snapNormPointToWallEdge(normX: number, normY: number, mask: WallMaskSample, snapRadiusSegPx?: number): {
|
||
x: number;
|
||
y: number;
|
||
};
|
||
/**
|
||
* Prefer wall-mask corner pixels (L-shaped outer boundary), then plain edge.
|
||
* Used when the user taps without dragging.
|
||
*/
|
||
export declare function snapNormPointToWallCornerOrEdge(normX: number, normY: number, mask: WallMaskSample, snapRadiusSegPx?: number): {
|
||
x: number;
|
||
y: number;
|
||
};
|
||
/**
|
||
* During vertex drag: snap to corner/edge when near, otherwise keep interior
|
||
* wall points so the anchor can move freely on the wall mask.
|
||
*/
|
||
export declare function resolveLassoWallDragPoint(normX: number, normY: number, mask: WallMaskSample, snapRadiusSegPx?: number): {
|
||
x: number;
|
||
y: number;
|
||
} | null;
|
||
export declare function buildWallAllowedMask(labels: Uint8Array, baseboardBinary: Uint8Array, wallSemanticIdx: number): Uint8Array | null;
|
||
/**
|
||
* Build per-pixel energy map from BGR buffer.
|
||
* 1. Convert to grayscale via luminance weights
|
||
* 2. Downsample so longest side ≤ targetMaxSide
|
||
* 3. Apply Sobel 3×3 → gradient magnitude G
|
||
* 4. Energy = 1 / (1 + G), clamped to [0, 1]
|
||
*/
|
||
export declare function buildEnergyMap(bgrBuffer: Uint8Array, cols: number, rows: number, targetMaxSide?: number, allowedMask?: Uint8Array | null): EnergyMap;
|
||
/**
|
||
* Dijkstra shortest-path on 8-connected grid.
|
||
* Cost at each pixel = energy[pixel] * COST_SCALE (integer).
|
||
* Diagonal steps cost √2 × the neighbour's energy.
|
||
*
|
||
* Returns ordered path [start, …, end] in energy-map pixel space.
|
||
*/
|
||
export declare function findShortestPath(energy: Float32Array, energyW: number, energyH: number, sx: number, sy: number, ex: number, ey: number, traversable?: Uint8Array | null): {
|
||
x: number;
|
||
y: number;
|
||
}[];
|
||
/**
|
||
* Douglas-Peucker simplification. Keeps points where the perpendicular
|
||
* distance from the line segment exceeds epsilon.
|
||
*
|
||
* After DP, also enforces a minimum distance between consecutive anchors
|
||
* to avoid overly dense clusters.
|
||
*/
|
||
export declare function extractCornerPoints(path: {
|
||
x: number;
|
||
y: number;
|
||
}[], minDistance?: number, epsilon?: number): {
|
||
x: number;
|
||
y: number;
|
||
}[];
|
||
/** Map normalized image coords (0..1) to energy-map pixel coords. */
|
||
export declare function normToEnergyPoint(normX: number, normY: number, em: EnergyMap): {
|
||
x: number;
|
||
y: number;
|
||
};
|
||
/** Map energy-map pixel coords back to normalized image coords. */
|
||
export declare function energyPointsToNorm(points: {
|
||
x: number;
|
||
y: number;
|
||
}[], em: EnergyMap): {
|
||
x: number;
|
||
y: number;
|
||
}[];
|
||
/** Map energy-map pixel coords back to original image coords. */
|
||
export declare function upscalePath(points: {
|
||
x: number;
|
||
y: number;
|
||
}[], scale: number, originW: number, originH: number): {
|
||
x: number;
|
||
y: number;
|
||
}[];
|