2026-07-01 06:56:13 +00:00
|
|
|
|
import type { StyleProp, ViewStyle } from 'react-native';
|
2026-06-26 07:23:52 +00:00
|
|
|
|
import type { SegmentRegion } from '../utils/maskSegmentation';
|
|
|
|
|
|
import type { MaskSemanticColor } from '../utils/maskSemanticPalette';
|
|
|
|
|
|
export type BgrColor = {
|
|
|
|
|
|
b: number;
|
|
|
|
|
|
g: number;
|
|
|
|
|
|
r: number;
|
|
|
|
|
|
};
|
|
|
|
|
|
export type MaskSegmentWatchState = 'init' | 'images_loaded' | 'mask_aligned' | 'mask_sampled' | 'regions_ready' | 'layers_ready' | 'interactive' | 'mask_paths_ready' | 'error';
|
|
|
|
|
|
export type MaskSegmentWatchDetail = {
|
|
|
|
|
|
regionCount?: number;
|
|
|
|
|
|
maskPathsReady?: boolean;
|
|
|
|
|
|
freqLayersReady?: boolean;
|
|
|
|
|
|
errorMessage?: string;
|
|
|
|
|
|
};
|
|
|
|
|
|
export type PipelinePreset = 'high' | 'medium' | 'low';
|
|
|
|
|
|
export type PipelineConfig = {
|
|
|
|
|
|
maxImageLongSide?: number;
|
2026-07-02 02:07:32 +00:00
|
|
|
|
/** low/high frequency LAB processing longest side (can be lower than maxImageLongSide, Shader stretch sampling) */
|
2026-06-26 07:23:52 +00:00
|
|
|
|
paintFreqMaxLongSide?: number;
|
|
|
|
|
|
originPreviewMaxLongSide?: number;
|
|
|
|
|
|
maskPathMaxLongSide?: number;
|
|
|
|
|
|
minContourArea?: number;
|
|
|
|
|
|
contourApproxEpsilon?: number;
|
|
|
|
|
|
maxRegions?: number;
|
|
|
|
|
|
};
|
|
|
|
|
|
export type MaskSegmentConfig = {
|
|
|
|
|
|
semanticColors?: MaskSemanticColor[];
|
|
|
|
|
|
baseboardMaxColorDist?: number;
|
|
|
|
|
|
blackThreshold?: number;
|
|
|
|
|
|
quantStep?: number;
|
|
|
|
|
|
baseboardStripQuantKeys?: string[];
|
|
|
|
|
|
wallQuantKeys?: string[];
|
|
|
|
|
|
cabinetQuantKeys?: string[];
|
|
|
|
|
|
maxRegionColors?: number;
|
|
|
|
|
|
secondarySemanticNames?: string[];
|
|
|
|
|
|
secondaryMinPixelRatio?: number;
|
|
|
|
|
|
junctionHRadiusPx?: number;
|
|
|
|
|
|
junctionVRadiusPx?: number;
|
|
|
|
|
|
kickBridgeHalfWPx?: number;
|
|
|
|
|
|
baseboardJunctionRowMarginPx?: number;
|
|
|
|
|
|
baseboardJunctionVReachPx?: number;
|
|
|
|
|
|
baseboardMinRunPx?: number;
|
2026-07-02 02:07:32 +00:00
|
|
|
|
/** only used in wall mask, split wall into wall-1, wall-2... by texture boundary */
|
2026-07-01 05:41:17 +00:00
|
|
|
|
splitWalls?: boolean;
|
2026-07-02 02:07:32 +00:00
|
|
|
|
/** wall mask only, max number of wall sub-regions */
|
2026-07-01 05:41:17 +00:00
|
|
|
|
splitWallsMaxCount?: number;
|
2026-07-02 02:07:32 +00:00
|
|
|
|
/** wall mask only, min area ratio (relative to seg total pixels) */
|
2026-07-01 05:41:17 +00:00
|
|
|
|
splitWallsMinAreaRatio?: number;
|
2026-07-02 02:07:32 +00:00
|
|
|
|
/** wall mask only, Lab chroma + high-frequency texture feature distance squared threshold (excluding brightness, reducing shadow impact) */
|
2026-07-01 05:41:17 +00:00
|
|
|
|
splitWallsColorDistSq?: number;
|
2026-07-02 02:07:32 +00:00
|
|
|
|
/** wall mask only, chroma smoothing radius (pixels), only used for texture energy calculation */
|
2026-07-01 05:41:17 +00:00
|
|
|
|
splitWallsChromaBlurRadius?: number;
|
2026-07-02 02:07:32 +00:00
|
|
|
|
/** wall mask only, low saturation (white/gray wall) junction radius, used to force separate colored walls */
|
2026-07-01 05:41:17 +00:00
|
|
|
|
splitWallsNeutralChromaMax?: number;
|
feat: add manual lasso wall splitting with magnetic edge-snapping and active contour refinement
- 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>
2026-07-07 03:23:57 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* Wall split: raw per-channel BGR Sobel gradient threshold for edge barriers
|
|
|
|
|
|
* (0 = disabled). Uses un-normalized 8‑bit Sobel magnitude on each B/G/R
|
|
|
|
|
|
* channel (max per pixel, range ≈ 0–1442). Visible wall seams ≈ 120–280,
|
|
|
|
|
|
* subtle lighting gradients ≈ 20–80. Default: 160.
|
|
|
|
|
|
*/
|
|
|
|
|
|
splitWallsEdgeBarrierThreshold?: number;
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Morphological close radius for wall mask holes before component labeling.
|
|
|
|
|
|
* Non-wall pixels inside the wall boundary (windows, doors, mask artefacts)
|
|
|
|
|
|
* are temporarily filled so the BFS can bridge across them. Default: 3.
|
|
|
|
|
|
* Set to 0 to disable.
|
|
|
|
|
|
*/
|
|
|
|
|
|
splitWallsCloseMaskRadius?: number;
|
|
|
|
|
|
/** When true, disables automatic texture-based wall splitting (splitWalls). Manual lasso partitioning is used instead. */
|
|
|
|
|
|
manualSplitWalls?: boolean;
|
|
|
|
|
|
/** wall mask only, max number of manual wall sub-regions defined by lasso */
|
|
|
|
|
|
manualSplitWallsMaxCount?: number;
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Manual lasso: morphological dilation radius (seg pixels) used to merge thin
|
|
|
|
|
|
* unassigned wall pockets adjacent to the drawn polygon.
|
|
|
|
|
|
*/
|
|
|
|
|
|
manualSplitWallsGapAbsorbDilatePx?: number;
|
|
|
|
|
|
/** When true, lasso mode uses edge-snapping (Sobel gradient + Dijkstra shortest-path). Default: false. */
|
|
|
|
|
|
magneticLasso?: boolean;
|
|
|
|
|
|
/** After End Lasso, run active contour refinement on each polygon to expand vertices outward toward wall-mask edges. Default: false. */
|
|
|
|
|
|
activeContourRefine?: boolean;
|
2026-06-26 07:23:52 +00:00
|
|
|
|
};
|
|
|
|
|
|
export type PaintConfig = {
|
|
|
|
|
|
palette?: BgrColor[];
|
|
|
|
|
|
colorBaseOpacity?: number;
|
|
|
|
|
|
lLightOpacity?: number;
|
|
|
|
|
|
textureOpacity?: number;
|
|
|
|
|
|
lLowBlurKernel?: number;
|
|
|
|
|
|
lLowContrast?: number;
|
|
|
|
|
|
lLowBrightness?: number;
|
|
|
|
|
|
lHighGain?: number;
|
|
|
|
|
|
maskFeatherColor?: number;
|
|
|
|
|
|
maskFeatherTexture?: number;
|
|
|
|
|
|
regionOverlayFill?: string;
|
|
|
|
|
|
regionOutlineStrokeWidth?: number;
|
|
|
|
|
|
};
|
|
|
|
|
|
export type InteractionConfig = {
|
|
|
|
|
|
kickMaskPickRadiusPx?: number;
|
|
|
|
|
|
pickMapSearchRadiusPx?: number;
|
|
|
|
|
|
thinStripPadding?: number;
|
|
|
|
|
|
regionPadding?: number;
|
|
|
|
|
|
initRegionFlashMs?: number;
|
|
|
|
|
|
enableInitRegionFlash?: boolean;
|
|
|
|
|
|
};
|
|
|
|
|
|
export type PaintedRegionRecord = {
|
|
|
|
|
|
regionId: number;
|
|
|
|
|
|
regionName: string;
|
|
|
|
|
|
color: BgrColor;
|
|
|
|
|
|
configJson?: Record<string, unknown>;
|
|
|
|
|
|
};
|
|
|
|
|
|
export type MaskSegmentSession = {
|
|
|
|
|
|
version: 1;
|
|
|
|
|
|
originUrl: string;
|
|
|
|
|
|
maskUrl: string;
|
|
|
|
|
|
painted: PaintedRegionRecord[];
|
|
|
|
|
|
paintHistory: number[];
|
|
|
|
|
|
currentColor?: BgrColor;
|
|
|
|
|
|
currentColorConfigJson?: Record<string, unknown>;
|
|
|
|
|
|
savedAt: number;
|
|
|
|
|
|
};
|
|
|
|
|
|
export type SavePaintResult = {
|
|
|
|
|
|
filePath: string;
|
|
|
|
|
|
width: number;
|
|
|
|
|
|
height: number;
|
|
|
|
|
|
paintedCount: number;
|
|
|
|
|
|
previewPath?: string;
|
|
|
|
|
|
};
|
|
|
|
|
|
export type SavePaintOptions = {
|
|
|
|
|
|
destDir?: string;
|
|
|
|
|
|
};
|
|
|
|
|
|
export type PaintSuccessPayload = {
|
|
|
|
|
|
kind: 'painted';
|
|
|
|
|
|
regionId: number;
|
|
|
|
|
|
regionName: string;
|
|
|
|
|
|
color: BgrColor;
|
|
|
|
|
|
configJson?: Record<string, unknown>;
|
|
|
|
|
|
};
|
|
|
|
|
|
export type PaintBrushRequiredPayload = {
|
|
|
|
|
|
kind: 'brush_required';
|
2026-07-02 02:07:32 +00:00
|
|
|
|
/** brush not selected, hint text */
|
2026-06-26 07:23:52 +00:00
|
|
|
|
hint: string;
|
|
|
|
|
|
regionId: number;
|
|
|
|
|
|
regionName: string;
|
|
|
|
|
|
};
|
|
|
|
|
|
export type PaintCallbackPayload = PaintSuccessPayload | PaintBrushRequiredPayload;
|
feat: add manual lasso wall splitting with magnetic edge-snapping and active contour refinement
- 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>
2026-07-07 03:23:57 +00:00
|
|
|
|
/** A lasso polygon defined by user tapping vertices on the wall mask area. Vertices are in normalized image coordinates (0..1). */
|
|
|
|
|
|
export type LassoPolygon = {
|
|
|
|
|
|
id: string;
|
|
|
|
|
|
vertices: {
|
|
|
|
|
|
x: number;
|
|
|
|
|
|
y: number;
|
|
|
|
|
|
}[];
|
|
|
|
|
|
isClosed: boolean;
|
|
|
|
|
|
};
|
|
|
|
|
|
/** Result of converting a lasso polygon into an actual wall sub-region. */
|
|
|
|
|
|
export type ManualWallPartition = {
|
|
|
|
|
|
id: string;
|
|
|
|
|
|
regionId: number;
|
|
|
|
|
|
regionName: string;
|
|
|
|
|
|
vertices: {
|
|
|
|
|
|
x: number;
|
|
|
|
|
|
y: number;
|
|
|
|
|
|
}[];
|
|
|
|
|
|
bbox: {
|
|
|
|
|
|
x: number;
|
|
|
|
|
|
y: number;
|
|
|
|
|
|
w: number;
|
|
|
|
|
|
h: number;
|
|
|
|
|
|
};
|
|
|
|
|
|
area: number;
|
|
|
|
|
|
};
|
2026-06-26 07:23:52 +00:00
|
|
|
|
export type MaskSegmentCanvasRef = {
|
|
|
|
|
|
reset: () => void;
|
|
|
|
|
|
swap: (showOrigin?: boolean) => void;
|
|
|
|
|
|
save: (options?: SavePaintOptions) => Promise<SavePaintResult>;
|
|
|
|
|
|
session: () => MaskSegmentSession;
|
|
|
|
|
|
loadSession: (session: MaskSegmentSession) => void;
|
|
|
|
|
|
setPaintColor: (color: BgrColor, configJson?: Record<string, unknown>) => void;
|
|
|
|
|
|
setMaskConfig: (config: MaskSegmentConfig) => void;
|
|
|
|
|
|
clearAllPaint: () => void;
|
|
|
|
|
|
/** Undo the most recent single coloring (paint) step. Distinct from clearAllPaint (full reset). */
|
|
|
|
|
|
undoSelection?: () => void;
|
|
|
|
|
|
resegment: () => Promise<void>;
|
|
|
|
|
|
getRegions: () => SegmentRegion[];
|
|
|
|
|
|
getPaintedRegions: () => PaintedRegionRecord[];
|
|
|
|
|
|
/** Returns the most recent auto-export or save() result, if any. */
|
|
|
|
|
|
getLastExport?: () => SavePaintResult | null;
|
feat: add manual lasso wall splitting with magnetic edge-snapping and active contour refinement
- 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>
2026-07-07 03:23:57 +00:00
|
|
|
|
/** Enter lasso mode — user can tap wall mask area to place polygon vertices. */
|
|
|
|
|
|
startLasso: () => void;
|
|
|
|
|
|
/** Exit lasso mode, convert all closed lasso polygons into wall-X sub-regions for painting. Returns the partition results. */
|
|
|
|
|
|
endLasso: () => ManualWallPartition[];
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Exit the current lasso editing session without saving regions.
|
|
|
|
|
|
* Discards in-progress vertices and closed polygons from this session only;
|
|
|
|
|
|
* previously committed manual wall partitions are kept.
|
|
|
|
|
|
*/
|
|
|
|
|
|
cancelLasso: () => void;
|
|
|
|
|
|
/** Get the current manual wall partitions (only valid after endLasso has been called). */
|
|
|
|
|
|
getManualRegions: () => ManualWallPartition[];
|
|
|
|
|
|
/** Delete a lasso polygon by its id. Committed partitions also drop paint on that region. */
|
|
|
|
|
|
deleteLasso: (id: string) => void;
|
2026-06-26 07:23:52 +00:00
|
|
|
|
};
|
|
|
|
|
|
export type MaskSegmentCanvasProps = {
|
|
|
|
|
|
originUrl?: string;
|
|
|
|
|
|
maskUrl?: string;
|
2026-07-02 02:07:32 +00:00
|
|
|
|
/** @deprecated use originUrl */
|
2026-06-26 07:23:52 +00:00
|
|
|
|
originImgPath?: string;
|
2026-07-02 02:07:32 +00:00
|
|
|
|
/** @deprecated use maskUrl */
|
2026-06-26 07:23:52 +00:00
|
|
|
|
maskImgPath?: string;
|
2026-07-02 02:07:32 +00:00
|
|
|
|
/** mask semantic recognition colors, initialization configuration; equivalent to maskConfig.semanticColors */
|
2026-06-26 07:23:52 +00:00
|
|
|
|
semanticColors?: MaskSemanticColor[];
|
2026-07-02 02:07:32 +00:00
|
|
|
|
/** partition dashed outline highlight color, initialization configuration; equivalent to paintConfig.regionOverlayFill */
|
2026-06-26 07:23:52 +00:00
|
|
|
|
regionOutlineColor?: string;
|
|
|
|
|
|
maskConfig?: MaskSegmentConfig;
|
|
|
|
|
|
/** Performance preset (high / medium / low). Merged with pipelineConfig overrides. */
|
|
|
|
|
|
pipelinePreset?: PipelinePreset;
|
|
|
|
|
|
pipelineConfig?: PipelineConfig;
|
|
|
|
|
|
paintConfig?: PaintConfig;
|
|
|
|
|
|
interactionConfig?: InteractionConfig;
|
|
|
|
|
|
initialSession?: MaskSegmentSession;
|
|
|
|
|
|
initialPaintColor?: BgrColor;
|
|
|
|
|
|
initialPaintConfigJson?: Record<string, unknown>;
|
|
|
|
|
|
disabled?: boolean;
|
|
|
|
|
|
style?: StyleProp<ViewStyle>;
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Max container height available for this component (px). When set, the SDK
|
|
|
|
|
|
* computes canvas dimensions as a fit-contain within (screenWidth - 20, maxHeight)
|
2026-07-01 06:56:13 +00:00
|
|
|
|
* instead of using the full image aspect at full screen width.
|
2026-06-26 07:23:52 +00:00
|
|
|
|
*/
|
|
|
|
|
|
maxHeight?: number;
|
|
|
|
|
|
onWatch?: (state: MaskSegmentWatchState, durationMs: number, detail?: MaskSegmentWatchDetail) => void;
|
|
|
|
|
|
onPaintCallback?: (payload: PaintCallbackPayload) => void;
|
|
|
|
|
|
onError?: (message: string, error?: unknown) => void;
|
|
|
|
|
|
/**
|
|
|
|
|
|
* When true, once the canvas reaches a ready interactive state (segmentation complete
|
|
|
|
|
|
* + any initialSession / painted colors applied), the SDK will automatically call its
|
|
|
|
|
|
* internal save pipeline to produce the recolored result image and fire onExported.
|
|
|
|
|
|
* This moves "auto-generate After preview" capability inside the SDK.
|
|
|
|
|
|
*/
|
|
|
|
|
|
autoExportOnReady?: boolean;
|
|
|
|
|
|
/** Fired by SDK when autoExportOnReady produced a result (the recolored file). */
|
|
|
|
|
|
onExported?: (result: SavePaintResult) => void;
|
|
|
|
|
|
};
|
|
|
|
|
|
export type { SegmentRegion, MaskSemanticColor };
|