react-native-mask-segment-c.../dist/utils/wallTextureSplit.js
a1518 3a3f07628d
Some checks failed
Deploy Docs to GitHub Pages / deploy (push) Has been cancelled
Publish to npm / publish (push) Has been cancelled
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-06 20:23:57 -07:00

2 lines
11 KiB
JavaScript

"use strict";var D=Object.defineProperty;var fn=Object.getOwnPropertyDescriptor;var bn=Object.getOwnPropertyNames;var mn=Object.prototype.hasOwnProperty;var ln=(n,e)=>{for(var o in e)D(n,o,{get:e[o],enumerable:!0})},sn=(n,e,o,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of bn(e))!mn.call(n,a)&&a!==o&&D(n,a,{get:()=>e[a],enumerable:!(i=fn(e,a))||i.enumerable});return n};var yn=n=>sn(D({},"__esModule",{value:!0}),n);var On={};ln(On,{WALL_SUB_LABEL_NONE:()=>_,absorbSmallWallGapsForLassoPolygons:()=>Fn,buildPickMapAfterWallSplit:()=>tn,dilatePickBuffer1px:()=>rn,isWallSubRegionName:()=>Ln,patchPickMapForManualWallSplit:()=>Wn,splitWallRegionsByTexture:()=>En});module.exports=yn(On);var Z=require("./freqLayerPrep"),nn=require("./maskSegmentRuntime"),en=require("./maskSemanticPalette");const _=255;function H(){return(0,nn.getMaskSegmentRuntimeConfig)().mask}function dn(n,e,o){let i=-1,a=-1;for(let b=0;b<o;b++){for(let m=0;m<e;m++)if(n[b*e+m]){i=m,a=b;break}if(i>=0)break}if(i<0)return[];const u=[[1,0],[1,-1],[0,-1],[-1,-1],[-1,0],[-1,1],[0,1],[1,1]],r=[];let l=i,f=a,c=7;for(let b=0;b<e*o;b++){r.push({x:l,y:f});let m=!1;for(let t=0;t<8;t++){const d=(c+1+t)%8,s=l+u[d][0],A=f+u[d][1];if(!(s<0||s>=e||A<0||A>=o)&&n[A*e+s]){l=s,f=A,c=(d+4)%8,m=!0;break}}if(!m||r.length>2&&l===i&&f===a)break}return r}function xn(n,e){if(n.length<=2)return[...n];const o=new Uint8Array(n.length);o[0]=1,o[n.length-1]=1;const i=(a,u)=>{if(u-a<=1)return;const r=n[u].x-n[a].x,l=n[u].y-n[a].y,f=r*r+l*l;let c=0,b=a;for(let m=a+1;m<u;m++){let t;if(f===0)t=Math.hypot(n[m].x-n[a].x,n[m].y-n[a].y);else{const d=Math.max(0,Math.min(1,((n[m].x-n[a].x)*r+(n[m].y-n[a].y)*l)/f));t=Math.hypot(n[m].x-(n[a].x+d*r),n[m].y-(n[a].y+d*l))}t>c&&(c=t,b=m)}c>e&&(o[b]=1,i(a,b),i(b,u))};return i(0,n.length-1),n.filter((a,u)=>o[u])}const An=2.5;function gn(n,e,o){const i=e*o,a=new Uint8Array(i),u=new Uint8Array(i);for(let r=0;r<i;r++){const l=r*3,f=(0,Z.bgrToLab)(n[l],n[l+1],n[l+2]);a[r]=f.a,u[r]=f.b}return{aMap:a,bMap:u}}function pn(n,e,o,i,a,u,r){const l=e*o,f=new Uint8Array(l);if(r<=0)return f;const c=e;for(let b=1;b<o-1;b++){const m=(b-1)*c,t=b*c,d=(b+1)*c;for(let s=1;s<c-1;s++){const A=t+s;if(a[A]!==i||u[A])continue;const h=m+(s-1),k=m+s,y=m+(s+1),p=t+(s-1),g=t+(s+1),U=d+(s-1),M=d+s,P=d+(s+1);let I=0;for(let B=0;B<3;B++){const E=n[h*3+B],z=n[k*3+B],F=n[y*3+B],L=n[p*3+B],R=n[g*3+B],w=n[U*3+B],G=n[M*3+B],N=n[P*3+B],W=-E+F-2*L+2*R-w+N,v=-E-2*z-F+w+2*G+N,q=Math.sqrt(W*W+v*v);q>I&&(I=q)}I>r&&(f[A]=1)}}return X(f,e,o)}function X(n,e,o){const i=new Uint8Array(n);for(let a=1;a<o-1;a++){const u=a*e;for(let r=1;r<e-1;r++){const l=u+r;n[l]||(n[l-1]||n[l+1]||n[(a-1)*e+r]||n[(a+1)*e+r])&&(i[l]=1)}}return i}function J(n,e){const o=n-128,i=e-128;return Math.sqrt(o*o+i*i)}function hn(n,e,o,i){const a=n-o,u=e-i;return a*a+u*u}function Un(n,e,o,i,a){const u=J(n,e),r=J(o,i),l=a*2.2;if(u<=a&&r>l||r<=a&&u>l)return!0;if(u>a&&r>a){const f=Math.atan2(e-128,n-128),c=Math.atan2(i-128,o-128);let b=Math.abs(f-c);if(b>Math.PI&&(b=2*Math.PI-b),b>Math.PI/4)return!0}return!1}function j(n,e,o,i,a,u){return Un(n,e,o,i,u)?!1:hn(n,e,o,i)<=a}function Mn(){return H().semanticColors.findIndex(e=>e.name==="wall")}function K(n,e,o,i){return e[i]?!1:n[i]===o}function Cn(n,e,o,i,a,u){const r=i*a,l=new Uint8Array(r);for(let t=0;t<r;t++)n[t]===o&&!e[t]&&(l[t]=1);let f=l;for(let t=0;t<u;t++)f=X(f,i,a);let c=f;for(let t=0;t<u;t++)c=Bn(c,i,a);const b=new Uint8Array(n);for(let t=0;t<r;t++)c[t]&&n[t]!==o&&!e[t]&&(b[t]=o);const m=new Uint8Array(e);for(let t=0;t<r;t++)c[t]&&n[t]!==o&&e[t]&&(b[t]=o,m[t]=0);return{labels:b,baseboardBinary:m}}function Bn(n,e,o){const i=new Uint8Array(n);for(let a=1;a<o-1;a++){const u=a*e;for(let r=1;r<e-1;r++){const l=u+r;n[l]&&(!n[u+(r-1)]||!n[u+(r+1)]||!n[(a-1)*e+r]||!n[(a+1)*e+r])&&(i[l]=0)}}return i}function kn(n,e,o,i,a,u,r,l,f,c){const b=r*l,m=new Int32Array(b);m.fill(-1);let t=0;const d=new Int32Array(b);for(let s=0;s<l;s++)for(let A=0;A<r;A++){const h=s*r+A;if(!K(n,e,o,h)||m[h]>=0)continue;const k=t;t+=1;const y=i[h],p=a[h];let g=y,U=p,M=1,P=0,I=0;for(d[I++]=h,m[h]=k;P<I;){const B=d[P++],E=B%r,z=B/r|0,F=g/M,L=U/M,R=[B-1,B+1,B-r,B+r];for(const w of R){if(w<0||w>=b)continue;const G=w%r;if(Math.abs(G-E)>1||!K(n,e,o,w)||u[w]||m[w]>=0)continue;const N=i[w],W=a[w],v=j(i[B],a[B],N,W,f*1.8,c),q=j(F,L,N,W,f,c),O=j(y,p,N,W,f*3,c);!v||!q||!O||(m[w]=k,g+=N,U+=W,M+=1,d[I++]=w)}}}return{compLabels:m,compCount:t}}function Q(n,e,o,i){const a=Array.from({length:e},(u,r)=>({label:r,area:0,bbox:{x:o,y:i,w:0,h:0}}));for(let u=0;u<i;u++)for(let r=0;r<o;r++){const l=u*o+r,f=n[l];if(f<0)continue;const c=a[f];c.area+=1,r<c.bbox.x&&(c.bbox.x=r),u<c.bbox.y&&(c.bbox.y=u);const b=r+1,m=u+1;b>c.bbox.x+c.bbox.w&&(c.bbox.w=b-c.bbox.x),m>c.bbox.y+c.bbox.h&&(c.bbox.h=m-c.bbox.y)}return a}function wn(n,e,o,i,a,u){const r=new Float64Array(i),l=new Float64Array(i),f=new Float64Array(i),c=a*u;for(let t=0;t<c;t++){const d=n[t];d<0||(r[d]+=e[t],l[d]+=o[t],f[d]+=1)}const b=new Float64Array(i),m=new Float64Array(i);for(let t=0;t<i;t++)f[t]>0?(b[t]=r[t]/f[t],m[t]=l[t]/f[t]):(b[t]=128,m[t]=128);return{meanA:b,meanB:m}}function Pn(n,e,o,i,a,u,r,l,f){const c=e.length,{meanA:b,meanB:m}=wn(n,o,i,c,a,u),t=new Map,d=(y,p)=>{if(y===p||y<0||p<0)return;let g=t.get(y);g||(g=new Map,t.set(y,g)),g.set(p,(g.get(p)??0)+1)};for(let y=0;y<u;y++)for(let p=0;p<a;p++){const g=y*a+p,U=n[g];if(!(U<0)){if(p+1<a){const M=n[g+1];M>=0&&d(U,M)}if(y+1<u){const M=n[g+a];M>=0&&d(U,M)}}}const s=new Int32Array(c);for(let y=0;y<c;y++)s[y]=y;const A=y=>{for(;s[y]!==y;)s[y]=s[s[y]],y=s[y];return y},h=(y,p)=>{const g=A(y),U=A(p);if(g===U)return;const M=e[g].area,P=e[U].area;M>=P?(s[U]=g,e[g].area+=e[U].area,e[U].area=0):(s[g]=U,e[U].area+=e[g].area,e[g].area=0)};for(let y=0;y<c;y++){if(e[y].area>=r)continue;const p=t.get(y);if(!p||p.size===0)continue;let g=-1,U=0;for(const[M,P]of p)P>U&&(U=P,g=M);if(g>=0){if(!j(b[y],m[y],b[g],m[g],l,f))continue;h(y,g)}}for(let y=0;y<c;y++){if(e[y].area<=0)continue;const p=t.get(y);if(!p||p.size===0)continue;let g=-1,U=0;for(const[M,P]of p)P>U&&(U=P,g=M);g<0||e[y].area>=e[g].area||h(y,g)}const k=a*u;for(let y=0;y<k;y++){const p=n[y];p<0||(n[y]=A(p))}}function Sn(n,e,o,i,a){const u=e.length,r=new Map,l=(t,d)=>{if(t===d)return;let s=r.get(t);s||(s=new Map,r.set(t,s)),s.set(d,(s.get(d)??0)+1)};for(let t=0;t<i;t++)for(let d=0;d<o;d++){const s=t*o+d,A=n[s];if(!(A<0)){if(d+1<o){const h=n[s+1];h>=0&&l(A,h)}if(t+1<i){const h=n[s+o];h>=0&&l(A,h)}}}const f=new Int32Array(u);for(let t=0;t<u;t++)f[t]=t;const c=t=>{for(;f[t]!==t;)f[t]=f[f[t]],t=f[t];return t},b=(t,d)=>{const s=c(t),A=c(d);s!==A&&(e[s].area>=e[A].area?(f[A]=s,e[s].area+=e[A].area,e[A].area=0):(f[s]=A,e[A].area+=e[s].area,e[s].area=0))};for(let t=0;t<u;t++){let d=!1;for(let s=0;s<u;s++){if(e[s].area<=0||e[s].area>=a)continue;const A=r.get(s);if(!A||A.size===0)continue;let h=-1,k=0;for(const[y,p]of A)f[y]===y&&p>k&&(k=p,h=y);h<0||(b(s,h),d=!0)}if(!d)break}const m=o*i;for(let t=0;t<m;t++){const d=n[t];d>=0&&(n[t]=c(d))}}function V(n,e,o){const i=e*o,a=new Map,u=new Int32Array(i);u.fill(-1);for(let f=0;f<i;f++){const c=n[f];if(c<0)continue;let b=a.get(c);b===void 0&&(b=a.size,a.set(c,b)),u[f]=b}const r=a.size,l=Q(u,r,e,o);return{labels:u,compCount:r,stats:l}}function tn(n,e,o,i,a,u,r,l){const f=r*l,c=new Uint8Array(f),m=u.get("baseboard"),t=m===void 0?0:m+1;for(let d=0;d<f;d++){if(e[d]){t>0&&(c[d]=t);continue}if(o>=0&&n[d]===o){if(i[d]!==_){const k=`wall-${i[d]+1}`,y=u.get(k);y!==void 0&&(c[d]=y+1)}continue}const s=n[d];if(s===255)continue;const A=a[s];if(!A)continue;const h=u.get(A);h!==void 0&&(c[d]=h+1)}return c}function Wn(n,e,o,i,a,u,r,l){const f=r*l,c=new Uint8Array(n);if(i<0)return c;for(let b=0;b<f;b++){if(o[b]||e[b]!==i)continue;const m=a[b];if(m===_){c[b]=0;continue}const t=`wall-${m+1}`,d=u.get(t);c[b]=d!==void 0?d+1:0}return c}function rn(n,e,o){const i=e*o,a=new Uint8Array(i);a.set(n);for(let u=1;u<o-1;u++)for(let r=1;r<e-1;r++){const l=u*e+r;if(n[l]!==0)continue;const f=[n[(u-1)*e+(r-1)],n[(u-1)*e+r],n[(u-1)*e+(r+1)],n[u*e+(r-1)],n[u*e+(r+1)],n[(u+1)*e+(r-1)],n[(u+1)*e+r],n[(u+1)*e+(r+1)]],c={};for(let b=0;b<8;b++){const m=f[b];m!==0&&(c[m]=(c[m]??0)+1)}for(const b of Object.keys(c)){const m=Number(b);if(c[m]>=4){a[l]=m;break}}}return a}const In=[[-1,-1],[0,-1],[1,-1],[-1,0],[1,0],[-1,1],[0,1],[1,1]];function Rn(n,e,o){if(n.w===0&&n.h===0){n.x=e,n.y=o,n.w=1,n.h=1;return}const i=n.x+n.w,a=n.y+n.h;e<n.x?(n.w=i-e,n.x=e):e+1>i&&(n.w=e+1-n.x),o<n.y?(n.h=a-o,n.y=o):o+1>a&&(n.h=o+1-n.y)}function Nn(n,e,o,i,a,u){a.fill(0);for(let r=0;r<e;r++)u[r]={x:o,y:i,w:0,h:0};for(let r=0;r<i;r++)for(let l=0;l<o;l++){const f=r*o+l,c=n[f];c===_||c>=e||(a[c]++,Rn(u[c],l,r))}}function Fn(n,e,o,i,a,u,r,l,f,c,b){if(e<=0||b<=0||r<0)return;const m=t=>a[t]!==r||u[t]||l[t]!==_?!1:n[t]===_;for(let t=0;t<e;t++)for(let d=0;d<b;d++){const s=[];for(let A=0;A<c;A++)for(let h=0;h<f;h++){const k=A*f+h;if(n[k]===t)for(const[y,p]of In){const g=h+y,U=A+p;if(g<0||g>=f||U<0||U>=c)continue;const M=U*f+g;m(M)&&s.push(M)}}if(s.length===0)break;for(const A of s)n[A]=t}Nn(n,e,f,c,o,i)}function En(n,e,o,i,a){const u=H();if(!u.splitWalls)return n;const r=n.regions.find(x=>x.name==="wall");if(!r)return n;const l=Mn();if(l<0)return n;const{labels:f,baseboardBinary:c,regions:b}=n,m=o*i;if(e.length<m*3)return n;const t=u.splitWallsCloseMaskRadius??3,d=t>0?Cn(f,c,l,o,i,t):{labels:f,baseboardBinary:c},s=d.labels,A=d.baseboardBinary,{aMap:h,bMap:k}=gn(e,o,i),y=pn(e,o,i,l,s,A,u.splitWallsEdgeBarrierThreshold??36),p=u.splitWallsColorDistSq,g=u.splitWallsNeutralChromaMax,U=Math.max(a,Math.floor(o*i*u.splitWallsMinAreaRatio)),{compLabels:M,compCount:P}=kn(s,A,l,h,k,y,o,i,p,g);if(P===0)return n;let I=Q(M,P,o,i);Pn(M,I,h,k,o,i,U,p,g);let B,E,z;{const x=V(M,o,i);Sn(x.labels,x.stats,o,i,U);const C=V(x.labels,o,i);B=C.labels,E=C.compCount,z=C.stats}if(E===0)return n;const F=z.map((x,C)=>({...x,origIdx:C})).filter(x=>x.area>0).sort((x,C)=>C.area-x.area).slice(0,u.splitWallsMaxCount),L=new Map;F.forEach((x,C)=>{L.set(x.origIdx,C)});const R=new Uint8Array(m);R.fill(_);for(let x=0;x<m;x++){const C=B[x];if(C<0)continue;const S=L.get(C);S!==void 0&&(R[x]=S)}const w=(0,en.getSemanticColorByName)("wall"),G=w?.hex??r.hex,N=w?.bgr??r.color,W=new Array(F.length);for(let x=0;x<m;x++){const C=B[x];if(C<0)continue;const S=L.get(C);S!==void 0&&(W[S]||(W[S]=new Uint8Array(m)),W[S][x]=1)}const v=b.filter(x=>x.name!=="wall"),q=F.map((x,C)=>{const S=W[C],$=S?dn(S,o,i):[],T=xn($,An),Y=T.length>=3?T:[{x:x.bbox.x,y:x.bbox.y},{x:x.bbox.x+x.bbox.w,y:x.bbox.y},{x:x.bbox.x+x.bbox.w,y:x.bbox.y+x.bbox.h},{x:x.bbox.x,y:x.bbox.y+x.bbox.h}];return{id:0,name:`wall-${C+1}`,hex:G,color:{...N},polygons:[Y],outlinePolygons:[Y],bbox:x.bbox,area:x.area}}),O=[...v,...q];O.sort((x,C)=>C.area-x.area),O.forEach((x,C)=>{x.id=C});const on=new Map(O.map(x=>[x.name,x.id])),an=u.semanticColors.map(x=>x.name),un=tn(f,c,l,R,an,on,o,i),cn=rn(un,o,i);for(const x of O){if(!/^wall-\d+$/.test(x.name))continue;const C=Number(x.name.slice(5))-1;let S=0;for(let $=0;$<m;$++)R[$]===C&&(S+=1);x.area=S}return{regions:O,pickMap:{buffer:cn,cols:o,rows:i},labels:f,baseboardBinary:c,segCols:o,segRows:i,wallSubLabels:R}}function Ln(n){return/^wall-\d+$/.test(n)}