diff --git a/build/pnltri.js b/build/pnltri.js index 5cddcc0..aa69f39 100644 --- a/build/pnltri.js +++ b/build/pnltri.js @@ -2183,7 +2183,17 @@ PNLTRI.MonoTriangulator.prototype = { while ( (frontVert != endVert) || (vertBackLogIdx > 1) ) { if (vertBackLogIdx > 0) { // vertBackLog is not empty - if ( PNLTRI.Math.ptsCrossProd( frontVert, vertBackLog[vertBackLogIdx-1], vertBackLog[vertBackLogIdx] ) > 0 ) { // TODO !! + var angle = PNLTRI.Math.ptsCrossProd( frontVert, vertBackLog[vertBackLogIdx-1], vertBackLog[vertBackLogIdx] ); // TODO !! + if ( Math.abs(angle) <= PNLTRI.Math.EPSILON_P ) { + // co-linear + if ( PNLTRI.Math.compare_pts_yx( frontVert, vertBackLog[vertBackLogIdx] ) != PNLTRI.Math.compare_pts_yx( vertBackLog[vertBackLogIdx], vertBackLog[vertBackLogIdx-1] ) ) { +// console.log("triangulate_monotone_polygon: colinear", frontVert.x - vertBackLog[vertBackLogIdx].x, frontVert.y - vertBackLog[vertBackLogIdx].y, +// vertBackLog[vertBackLogIdx].x - vertBackLog[vertBackLogIdx-1].x, vertBackLog[vertBackLogIdx].y - vertBackLog[vertBackLogIdx-1].y, +// frontVert, vertBackLog[vertBackLogIdx], vertBackLog[vertBackLogIdx-1] ); + angle = 1; // co-linear-reversal => create triangle + } + } + if ( angle > 0 ) { // convex corner: cut if off this.polyData.addTriangle( vertBackLog[vertBackLogIdx-1], vertBackLog[vertBackLogIdx], frontVert ); vertBackLogIdx--; diff --git a/build/pnltri.min.js b/build/pnltri.min.js index df4ee66..f63538b 100644 --- a/build/pnltri.min.js +++ b/build/pnltri.min.js @@ -1,21 +1,21 @@ // pnltri.js / raw.github.com/jahting/pnltri.js/master/LICENSE -'use strict';var r={ea:"1.4"};r.d={random:Math.random,fa:function(b){for(var c=b.length-1;0r.d.k)return 1;e=b.x-c.x;return er.d.k?1:0}};r.d.k=Math.pow(2,-43);r.d.F=-r.d.k;function v(b){this.da=[];this.q=[];this.M=0;this.G=[];this.o=[];this.W=[];if(b)for(var c=0,e=b.length;cf.length)console.log("Polygon has < 3 vertices!",f);else{for(var a=void 0,h=void 0,g=void 0,d=0;dr.d.k)return!1;var d;Math.abs(a.y-b.y)b)a=l.a,l.a.f= -m,m.b=l.b,p.a=null;else{a=l.a.r;var e=a.C,b=e?a.c:a.j,b=P(c,b);0b?(a=l.a,l.a.f=m,m.b=l.b,p.a=null):(a=e?a.m:a.n,b=e?a.j:a.c,b=P(c,b),0--h){console.log("ERR add_segment: infinite loop",l,c,b);return}if(!l.a&&!l.b){console.log("ERR add_segment: missing successors",l,c,b);return}g=l.t;g.s=c;g.A=null;u&&u.r==l.r?(p=l,m=u,m.l=l.l,g.left=new K(p),g.right=u.t):(y&&y.p==l.p?(m=l,p=y,p.l=l.l,g.left=y.t):(p=l,m=ba(b,l),g.left=new K(p)),g.right=new K(m));l.e&&l.f?l.D?(l.ba?(m.e=l.f,m.f=l.D,m.e.a=m,m.f.b=m):(p.f=l.e,p.e=l.D,p.e.a=p,p.f.b=p),p.D=m.D=null):l.u==k.u?(m.f.b=m,p.f=m.e= -null):m==l?(m.e=m.f,m.f=null,m.e.a=m):(p.f=p.e,p.e=null):e();l.a&&l.b?g=a():(g=l.a?l.a:l.b,f(g));p.r&&(p.r.J=m);m.p&&(m.p.K=p);p.r=m.p=c;c.J=p;c.K=m;l.l!=t.l?(y=p,u=m,l=g):l=null}c.v=!0}else console.log("ERR add_segment: missing trFirst.uX: ",k)} -function S(b,c){if(c)var e=b.c,f=b.j,a=b.H;else e=b.j,f=b.c,a=b.I;for(var h;a;)if(a.Y)h=e==a.Y?f:e,h=r.d.S(h,a.Y),a=-1==h?a.left:a.right;else if(a.s)e==a.s.c||e==a.s.j?Math.abs(e.y-f.y)h?a.right:e==a.s.c?a.right:a.left):(h=P(a.s,e),0h?a=a.right:(h=P(a.s,f),0h?a=a.right:(h=P(a.s,c?b.n.c:b.m.j),a=0n){do g.h=g.m,g=g.g=g.n;while(g!=h);a.G[0]=!1}else{do g.h=g.n,g=g.g=g.m;while(g!=h)}for(h=a=h;a.g!=a.h;){b:{var g=a.h.c.x,d=a.h.c.y,n=a.c.x,k=a.c.y,q=a.g.c.x,t=a.g.c.y,l=q-n,p=t-k,m=g-q,y=d-t,u=n-g,M=k-d;if(r.d.k>u*p-l*M)g=!1;else{for(var aa= -a.h.h,C=a.g;C!=aa;){var C=C.g,z=C.c.x,A=C.c.y,T=z-g,U=A-d;if(0!=T||0!=U){var V=z-n,W=A-k;if(0!=V||0!=W)if(z-=q,A-=t,(0!=z||0!=A)&&l*W-p*V>=r.d.F&&u*U-M*T>=r.d.F&&m*A-y*z>=r.d.F){g=!1;break b}}}g=!0}}if(g)B(f.i,a.h.c,a.c,a.g.c),a.h.g=a.g,a.g.h=a.h,h=a=a.g;else if(a=a.g,a==h){f=!1;break a}}f=!0}}if(!f){f=new Y(e);f.Q=new X(f.i);h=f.Q;a=h.i.q.concat();r.d.fa(a);g=0;d=h.i.M;if(1!=d)for(n=Array(d),k=a.concat(),q=0;qr.d.k)return 1;e=b.x-c.x;return er.d.k?1:0}};r.d.k=Math.pow(2,-43);r.d.F=-r.d.k;function v(b){this.da=[];this.q=[];this.N=0;this.G=[];this.o=[];this.W=[];if(b)for(var c=0,e=b.length;cf.length)console.log("Polygon has < 3 vertices!",f);else{for(var a=void 0,h=void 0,g=void 0,d=0;dr.d.k)return!1;var d;Math.abs(a.y-b.y)b)a=l.a,l.a.f= +n,n.b=l.b,q.a=null;else{a=l.a.r;var e=a.C,b=e?a.c:a.j,b=P(c,b);0b?(a=l.a,l.a.f=n,n.b=l.b,q.a=null):(a=e?a.m:a.n,b=e?a.j:a.c,b=P(c,b),0--h){console.log("ERR add_segment: infinite loop",l,c,b);return}if(!l.a&&!l.b){console.log("ERR add_segment: missing successors",l,c,b);return}g=l.t;g.s=c;g.A=null;u&&u.r==l.r?(q=l,n=u,n.l=l.l,g.left=new K(q),g.right=u.t):(y&&y.p==l.p?(n=l,q=y,q.l=l.l,g.left=y.t):(q=l,n=ba(b,l),g.left=new K(q)),g.right=new K(n));l.e&&l.f?l.D?(l.ba?(n.e=l.f,n.f=l.D,n.e.a=n,n.f.b=n):(q.f=l.e,q.e=l.D,q.e.a=q,q.f.b=q),q.D=n.D=null):l.u==k.u?(n.f.b=n,q.f=n.e= +null):n==l?(n.e=n.f,n.f=null,n.e.a=n):(q.f=q.e,q.e=null):e();l.a&&l.b?g=a():(g=l.a?l.a:l.b,f(g));q.r&&(q.r.K=n);n.p&&(n.p.L=q);q.r=n.p=c;c.K=q;c.L=n;l.l!=t.l?(y=q,u=n,l=g):l=null}c.v=!0}else console.log("ERR add_segment: missing trFirst.uX: ",k)} +function S(b,c){if(c)var e=b.c,f=b.j,a=b.I;else e=b.j,f=b.c,a=b.J;for(var h;a;)if(a.Y)h=e==a.Y?f:e,h=r.d.H(h,a.Y),a=-1==h?a.left:a.right;else if(a.s)e==a.s.c||e==a.s.j?Math.abs(e.y-f.y)h?a.right:e==a.s.c?a.right:a.left):(h=P(a.s,e),0h?a=a.right:(h=P(a.s,f),0h?a=a.right:(h=P(a.s,c?b.n.c:b.m.j),a=0m){do g.h=g.m,g=g.g=g.n;while(g!=h);a.G[0]=!1}else{do g.h=g.n,g=g.g=g.m;while(g!=h)}for(h=a=h;a.g!=a.h;){b:{var g=a.h.c.x,d=a.h.c.y,m=a.c.x,k=a.c.y,p=a.g.c.x,t=a.g.c.y,l=p-m,q=t-k,n=g-p,y=d-t,u=m-g,M=k-d;if(r.d.k>u*q-l*M)g=!1;else{for(var aa= +a.h.h,C=a.g;C!=aa;){var C=C.g,z=C.c.x,A=C.c.y,T=z-g,U=A-d;if(0!=T||0!=U){var V=z-m,W=A-k;if(0!=V||0!=W)if(z-=p,A-=t,(0!=z||0!=A)&&l*W-q*V>=r.d.F&&u*U-M*T>=r.d.F&&n*A-y*z>=r.d.F){g=!1;break b}}}g=!0}}if(g)B(f.i,a.h.c,a.c,a.g.c),a.h.g=a.g,a.g.h=a.h,h=a=a.g;else if(a=a.g,a==h){f=!1;break a}}f=!0}}if(!f){f=new Y(e);f.R=new X(f.i);h=f.R;a=h.i.q.concat();r.d.fa(a);g=0;d=h.i.N;if(1!=d)for(m=Array(d),k=a.concat(),p=0;p 1) ) { if (vertBackLogIdx > 0) { // vertBackLog is not empty - if ( PNLTRI.Math.ptsCrossProd( frontVert, vertBackLog[vertBackLogIdx-1], vertBackLog[vertBackLogIdx] ) > 0 ) { // TODO !! + var angle = PNLTRI.Math.ptsCrossProd( frontVert, vertBackLog[vertBackLogIdx-1], vertBackLog[vertBackLogIdx] ); // TODO !! + if ( Math.abs(angle) <= PNLTRI.Math.EPSILON_P ) { + // co-linear + if ( PNLTRI.Math.compare_pts_yx( frontVert, vertBackLog[vertBackLogIdx] ) != PNLTRI.Math.compare_pts_yx( vertBackLog[vertBackLogIdx], vertBackLog[vertBackLogIdx-1] ) ) { +// console.log("triangulate_monotone_polygon: colinear", frontVert.x - vertBackLog[vertBackLogIdx].x, frontVert.y - vertBackLog[vertBackLogIdx].y, +// vertBackLog[vertBackLogIdx].x - vertBackLog[vertBackLogIdx-1].x, vertBackLog[vertBackLogIdx].y - vertBackLog[vertBackLogIdx-1].y, +// frontVert, vertBackLog[vertBackLogIdx], vertBackLog[vertBackLogIdx-1] ); + angle = 1; // co-linear-reversal => create triangle + } + } + if ( angle > 0 ) { // convex corner: cut if off this.polyData.addTriangle( vertBackLog[vertBackLogIdx-1], vertBackLog[vertBackLogIdx], frontVert ); vertBackLogIdx--; diff --git a/test/MonoSplitterTest.js b/test/MonoSplitterTest.js index 3d91063..2124d20 100644 --- a/test/MonoSplitterTest.js +++ b/test/MonoSplitterTest.js @@ -18,7 +18,7 @@ PNLTRI.MonoSplitter.prototype.alyTrap_check = function ( inTrap, inFromUp, inFro ok( inTrap.monoDone, inTestName ); }; PNLTRI.MonoSplitter.prototype.mockSetup = function () { - + function mock_doSplit_check( inChain, inVertLow, inVertHigh, inLow2High ) { if ( mockDoChecks ) return mock_check( [ inChain, inVertLow, inVertHigh, inLow2High ] ); return null; @@ -30,12 +30,12 @@ PNLTRI.MonoSplitter.prototype.mockSetup = function () { this.doSplit = mock_doSplit_check; mock_check_off(); }; - + function test_MonoSplitter() { - + /* 4 Cases for "high points", mirror them for "low points" - - /* L + + /* L * / * -----*------------------/ * \ Trap / @@ -69,16 +69,16 @@ function test_MonoSplitter() { * TR_BR, TR_BLR: no diag * * TM_BL, TM_BM, TM_BR, TM_BLR: diag - * + * * TLR_BL, TLR_BR: no diag * TLR_BM: diag * * TLR_BLR: not possible */ - + /**************************************************************************/ - - /* + + /* * / * ------*--------------/ * \ / @@ -121,8 +121,8 @@ function test_MonoSplitter() { mock_set_expected( [ [ myTrap.uR, false, false, 0 ] ] ); myMono.alyTrap_check( myTrap, false, false, "TL_BL: from dR, no diag" ); } - - /* + + /* * \ * \--------------*---- * \ / @@ -162,8 +162,8 @@ function test_MonoSplitter() { myMono.alyTrap_check( myTrap, false, true, "TR_BR: from dL, no diag" ); } - /* - * / + /* + * / * ------*-----/ * \ / * \ / @@ -200,7 +200,7 @@ function test_MonoSplitter() { myMono.alyTrap_check( myTrap, true, false, "TL_BLR: from uR, no diag" ); } - /* + /* * \ * \-----*----- * \ / @@ -237,8 +237,8 @@ function test_MonoSplitter() { mock_set_expected( [ [ myTrap.uL, false, true, 0 ] ] ); myMono.alyTrap_check( myTrap, true, true, "TR_BLR: from uL, no diag" ); } - - /* + + /* * * --------*------ * / \ @@ -275,8 +275,8 @@ function test_MonoSplitter() { mock_set_expected( [ [ myTrap.dR, true, false, 0 ] ] ); myMono.alyTrap_check( myTrap, false, false, "TLR_BL: from dR, no diag" ); } - - /* + + /* * * --------*------ * / \ @@ -313,8 +313,8 @@ function test_MonoSplitter() { mock_set_expected( [ [ myTrap.dL, true, true, 0 ] ] ); myMono.alyTrap_check( myTrap, false, true, "TLR_BR: from dL, no diag" ); } - - /* + + /* * / * ------*------------------/ * \ / @@ -357,8 +357,8 @@ function test_MonoSplitter() { [ myTrap.uR, false, false, 7 ] ], [ 7 ] ); myMono.alyTrap_check( myTrap, false, true, "TL_BR: from dL, diag: vLow(right)->vHigh(left)" ); } - - /* + + /* * \ * \------------------*----- * \ / @@ -401,8 +401,8 @@ function test_MonoSplitter() { [ myTrap.uL, false, true, 7 ] ], [ 7 ] ); myMono.alyTrap_check( myTrap, false, false, "TR_BL: from dR, diag: vHigh(right)->vLow(left)" ); } - - /* + + /* * / * ------*------------------/ * \ / @@ -455,8 +455,8 @@ function test_MonoSplitter() { [ myTrap.uR, false, false, 7 ] ], [ 7 ] ); myMono.alyTrap_check( myTrap, false, true, "TL_BM: from dL, diag: vLow(middle)->vHigh(left)" ); } - - /* + + /* * \ * \------------------*---- * \ / @@ -509,8 +509,8 @@ function test_MonoSplitter() { [ myTrap.uL, false, true, 7 ] ], [ 7 ] ); myMono.alyTrap_check( myTrap, false, false, "TR_BM: from dR, diag: vHigh(right)->vLow(middle)" ); } - - /* + + /* * \ \ / / * \--------*---------/ * \ / @@ -563,8 +563,8 @@ function test_MonoSplitter() { [ myTrap.uL, false, true, 7 ] ], [ 7 ] ); myMono.alyTrap_check( myTrap, false, false, "TM_BL: from dR, diag: vHigh(middle)->vLow(left)" ); } - - /* + + /* * \ \ / / * \--------*---------/ * \ / @@ -617,8 +617,8 @@ function test_MonoSplitter() { [ myTrap.uR, false, false, 7 ] ], [ 7 ] ); myMono.alyTrap_check( myTrap, false, true, "TM_BR: from dL, diag: vLow(right)->vHigh(middle)" ); } - - /* + + /* * \ \ / / * \--------*---------/ * \ / @@ -685,7 +685,7 @@ function test_MonoSplitter() { myMono.alyTrap_check( myTrap, false, false, "TM_BM: from dR, diag: vHigh(middle)->vLow(middle)" ); } - /* + /* * ------*------- * / \ * / \ @@ -733,8 +733,8 @@ function test_MonoSplitter() { [ myTrap.dL, true, true, 7 ] ], [ 7 ] ); myMono.alyTrap_check( myTrap, false, false, "TLR_BM: from dR, diag: vHigh->vLow(middle)" ); } - - /* + + /* * \ \ / / * \--*--/ * \ / @@ -782,10 +782,10 @@ function test_MonoSplitter() { [ myTrap.uL, false, true, 7 ] ], [ 7 ] ); myMono.alyTrap_check( myTrap, true, false, "TM_BLR: from uR, diag: vHigh(middle)->vLow" ); } - + /**************************************************************************/ - /* + /* * \ * \------------------*---- * \ / @@ -845,7 +845,7 @@ function test_MonoSplitter() { [ myTrap.uL, false, true, 7 ] ], [ 7 ] ); myMono.alyTrap_check( myTrap, false, false, "TR_BM__c_CCW_h_CW: from dR, diag: vHigh(right)->vLow(middle)" ); } - + function test_TR_BM__c_CW_h_CCW() { var myPolygonData = new PNLTRI.PolygonData( [ [ // contour: CW @@ -898,7 +898,7 @@ function test_MonoSplitter() { [ myTrap.uL, false, true, 7 ] ], [ 7 ] ); myMono.alyTrap_check( myTrap, false, false, "TR_BM__c_CW_h_CCW: from dR, diag: vHigh(right)->vLow(middle)" ); } - + /**************************************************************************/ var testData = new PolygonTestdata(); @@ -932,7 +932,7 @@ function test_MonoSplitter() { } } - + test( "Polygon Monotone Splitter", function() { // contour: CCW; no hole // no diagonal diff --git a/test/MonoTriangulatorTest.js b/test/MonoTriangulatorTest.js index 0e48826..7f02624 100644 --- a/test/MonoTriangulatorTest.js +++ b/test/MonoTriangulatorTest.js @@ -6,11 +6,11 @@ function test_MonoTriangulator() { var testData = new PolygonTestdata(); - + /*************************************************************************** * Tests **************************************************************************/ - + function test_triangulate_all_polygons( inDataName, inDrawScale ) { var baseData = testData.get_polygon_with_holes( inDataName ); var expectedTriangList = testData.get_triangles( inDataName ); @@ -56,7 +56,7 @@ function test_MonoTriangulator() { function test_triangulate_monotone_polygon() { var myPolygonData, myMonoTriang, myMonoChain, triangList; - + // Helper function function initSeglistMonoChain( inPtList ) { myPolygonData = new PNLTRI.PolygonData( [ inPtList ] ); @@ -140,11 +140,45 @@ function test_MonoTriangulator() { equal( triangList.length, 0, "No Triangle from 1 vertex" ); } - + function test_triangulate_monotone_polygon_colinear() { + + var myPolygonData, myMonoTriang, myMonoChain, triangList; + + // Helper function + function initSeglistMonoChain( inPtList ) { + myPolygonData = new PNLTRI.PolygonData( [ inPtList ] ); + myPolygonData.initMonoChains(); + myMonoTriang = new PNLTRI.MonoTriangulator( myPolygonData ); + // + myMonoChain = myPolygonData.getSegments(); + triangList = myPolygonData.getTriangleList(); // unsorted results !! + } + + var myVertices; + // + // co-linear reversal + myVertices = [ { x:30,y:30 }, { x:10,y:10 }, { x:25,y:14 }, { x:20,y:20 } ]; + initSeglistMonoChain( myVertices ); + myMonoTriang.triangulate_monotone_polygon( myMonoChain[0] ); + equal( triangList.length, 2, "co-linear reversal #1: number" ); + deepEqual( triangList, [ [ 1, 2, 3 ], [ 1, 3, 0 ] ], "co-linear reversal #1: Triangles" ); +// drawPolygonLayers( { "poly": [ myVertices ], "triang": myPolygonData.triangles_2_polygons() } ); + // + // co-linear reversal + myVertices = [ { x:40,y:40 }, { x:29,y:37 }, { x:30,y:30 }, { x:20,y:20 } ]; + initSeglistMonoChain( myVertices ); + myMonoTriang.triangulate_monotone_polygon( myMonoChain[0] ); + equal( triangList.length, 2, "co-linear reversal #2: number" ); // TODO: Error + deepEqual( triangList, [ [ 2, 3, 0 ], [ 1, 2, 0 ] ], "co-linear reversal #2: Triangles" ); +// drawPolygonLayers( { "poly": [ myVertices ], "triang": myPolygonData.triangles_2_polygons() } ); + } + + test( "Triangulator for uni-Y-monotone Polygons", function() { test_triangulate_all_polygons( "square_3triangholes", 5 ); test_triangulate_all_polygons( "pt_3_diag_max", 4 ); test_triangulate_monotone_polygon(); + test_triangulate_monotone_polygon_colinear(); }); } diff --git a/test/TrapezoiderTest.js b/test/TrapezoiderTest.js index b82a8e9..75b3418 100644 --- a/test/TrapezoiderTest.js +++ b/test/TrapezoiderTest.js @@ -800,6 +800,8 @@ function test_QueryStructure() { ok( ( myQs.ptNode( { vFrom: secondPoint, vTo: { x: 4, y: 1 }, rootFrom: qsLR }, true ) == qs_tr3 ), "ptNode B: X-Node(qsLR), =vTo, horiz -> qs_tr3" ); } + /**************************************************************************/ + // // Cases of endpoints touching other segments // The touched segment is already inserted, now the endpoint of another segment, @@ -807,12 +809,6 @@ function test_QueryStructure() { // This is the opposite case to "test_add_segment_touching_N" // - // TODO: test all mirrored cases !!! - // if going back co-linear direction - // should be checked with the preceding segment - // ATTENTION: this needs to be looped since the - // preceeding segment can also be co-linear ... - function test_ptNode_touching() { var testPolygon = [ { x: 10, y: 30 }, { x: 20, y: 10 }, { x: 30, y: 40 } ]; @@ -846,7 +842,7 @@ function test_QueryStructure() { ok( ( testSegment.rootTo == qs_tr3 ), "ptNode_touching: up reverse, right -> qs_tr3" ); } - function test_ptNode_colinear_1() { // TODO: with vertical lines + function test_ptNode_colinear_inside() { // fully left side, CCW // @@ -874,16 +870,16 @@ function test_QueryStructure() { // prev(co-linear) segment myQs.segNodes( segListArray[3] ); - ok( ( segListArray[3].rootFrom == qs_tr1 ), "ptNode_colinear#1 A: prev, left -> qs_tr1" ); - ok( ( segListArray[3].rootTo == qs_tr1 ), "ptNode_colinear#1 A: prev reverse, left -> qs_tr1" ); + ok( ( segListArray[3].rootFrom == qs_tr1 ), "ptNode_colinear_inside A: prev, left -> qs_tr1" ); + ok( ( segListArray[3].rootTo == qs_tr1 ), "ptNode_colinear_inside A: prev reverse, left -> qs_tr1" ); // co-linear segment myQs.segNodes( segListArray[4] ); - ok( ( segListArray[4].rootFrom == qs_tr1 ), "ptNode_colinear#1 A: co-lin, left -> qs_tr1" ); - ok( ( segListArray[4].rootTo == qs_tr1 ), "ptNode_colinear#1 A: co-lin reverse, left -> qs_tr1" ); + ok( ( segListArray[4].rootFrom == qs_tr1 ), "ptNode_colinear_inside A: co-lin, left -> qs_tr1" ); + ok( ( segListArray[4].rootTo == qs_tr1 ), "ptNode_colinear_inside A: co-lin reverse, left -> qs_tr1" ); // next(co-linear) segment myQs.segNodes( segListArray[5] ); - ok( ( segListArray[5].rootFrom == qs_tr1 ), "ptNode_colinear#1 A: next, left -> qs_tr1" ); - ok( ( segListArray[5].rootTo == qs_tr1 ), "ptNode_colinear#1 A: next reverse, left -> qs_tr1" ); + ok( ( segListArray[5].rootFrom == qs_tr1 ), "ptNode_colinear_inside A: next, left -> qs_tr1" ); + ok( ( segListArray[5].rootTo == qs_tr1 ), "ptNode_colinear_inside A: next reverse, left -> qs_tr1" ); // // horizontal lines @@ -903,25 +899,60 @@ function test_QueryStructure() { // prev(prev(co-linear)) segment myQs.segNodes( segListArray[2] ); - ok( ( segListArray[2].rootFrom == qs_tr0 ), "ptNode_colinear#1 B: prev-prev, left -> qs_tr0" ); - ok( ( segListArray[2].rootTo == qs_tr0 ), "ptNode_colinear#1 B: prev-prev reverse, left -> qs_tr0" ); + ok( ( segListArray[2].rootFrom == qs_tr0 ), "ptNode_colinear_inside B: prev-prev, left -> qs_tr0" ); + ok( ( segListArray[2].rootTo == qs_tr0 ), "ptNode_colinear_inside B: prev-prev reverse, left -> qs_tr0" ); // prev(co-linear) segment myQs.segNodes( segListArray[3] ); - ok( ( segListArray[3].rootFrom == qs_tr0 ), "ptNode_colinear#1 B: prev, left -> qs_tr1" ); - ok( ( segListArray[3].rootTo == qs_tr1 ), "ptNode_colinear#1 B: prev reverse, left -> qs_tr1" ); + ok( ( segListArray[3].rootFrom == qs_tr0 ), "ptNode_colinear_inside B: prev, left -> qs_tr0" ); + ok( ( segListArray[3].rootTo == qs_tr1 ), "ptNode_colinear_inside B: prev reverse, left -> qs_tr1" ); // co-linear segment myQs.segNodes( segListArray[4] ); - ok( ( segListArray[4].rootFrom == qs_tr1 ), "ptNode_colinear#1 B: co-lin, left -> qs_tr1" ); - ok( ( segListArray[4].rootTo == qs_tr1 ), "ptNode_colinear#1 B: co-lin reverse, left -> qs_tr1" ); + ok( ( segListArray[4].rootFrom == qs_tr1 ), "ptNode_colinear_inside B: co-lin, left -> qs_tr1" ); + ok( ( segListArray[4].rootTo == qs_tr1 ), "ptNode_colinear_inside B: co-lin reverse, left -> qs_tr1" ); // next(co-linear) segment myQs.segNodes( segListArray[5] ); - ok( ( segListArray[5].rootFrom == qs_tr1 ), "ptNode_colinear#1 B: next, left -> qs_tr1" ); - ok( ( segListArray[5].rootTo == qs_tr0 ), "ptNode_colinear#1 B: next reverse, left -> qs_tr1" ); + ok( ( segListArray[5].rootFrom == qs_tr1 ), "ptNode_colinear_inside B: next, left -> qs_tr1" ); + ok( ( segListArray[5].rootTo == qs_tr0 ), "ptNode_colinear_inside B: next reverse, left -> qs_tr0" ); // next(next(co-linear)) segment myQs.segNodes( segListArray[0] ); - ok( ( segListArray[0].rootFrom == qs_tr0 ), "ptNode_colinear#1 B: next-next, left -> qs_tr0" ); - ok( ( segListArray[0].rootTo == qs_tr1 ), "ptNode_colinear#1 B: next-next reverse, left -> qs_tr1" ); + ok( ( segListArray[0].rootFrom == qs_tr0 ), "ptNode_colinear_inside B: next-next, left -> qs_tr0" ); + ok( ( segListArray[0].rootTo == qs_tr1 ), "ptNode_colinear_inside B: next-next reverse, left -> qs_tr1" ); + // + // vertical lines + var testPolygon = [ { x: 15, y: 15 }, { x: 30, y: 10 }, { x: 30, y: 40 }, + { x: 25, y: 35 }, { x: 30, y: 30 }, { x: 30, y: 20 } ]; + var myPolygonData = new PNLTRI.PolygonData( [ testPolygon ] ); + var segListArray = myPolygonData.getSegments(); + + var myQs = new PNLTRI.QueryStructure( myPolygonData ); + var myQsRoot = myQs.getRoot(); + + myQs.add_segment( segListArray[1] ); // touch line, 2nd touch line: segListArray[4] + var qs_tr1 = myQs.getTrapByIdx(1).sink; +// showDataStructure( myQsRoot ); +// drawTrapezoids( myQsRoot, false ); + + // prev(prev(co-linear)) segment + myQs.segNodes( segListArray[2] ); + ok( ( segListArray[2].rootFrom == qs_tr1 ), "ptNode_colinear_inside C: prev-prev, left -> qs_tr1" ); + ok( ( segListArray[2].rootTo == qs_tr1 ), "ptNode_colinear_inside C: prev-prev reverse, left -> qs_tr1" ); + // prev(co-linear) segment + myQs.segNodes( segListArray[3] ); + ok( ( segListArray[3].rootFrom == qs_tr1 ), "ptNode_colinear_inside C: prev, left -> qs_tr1" ); + ok( ( segListArray[3].rootTo == qs_tr1 ), "ptNode_colinear_inside C: prev reverse, left -> qs_tr1" ); + // co-linear segment + myQs.segNodes( segListArray[4] ); + ok( ( segListArray[4].rootFrom == qs_tr1 ), "ptNode_colinear_inside C: co-lin, left -> qs_tr1" ); + ok( ( segListArray[4].rootTo == qs_tr1 ), "ptNode_colinear_inside C: co-lin reverse, left -> qs_tr1" ); + // next(co-linear) segment + myQs.segNodes( segListArray[5] ); + ok( ( segListArray[5].rootFrom == qs_tr1 ), "ptNode_colinear_inside C: next, left -> qs_tr1" ); + ok( ( segListArray[5].rootTo == qs_tr1 ), "ptNode_colinear_inside C: next reverse, left -> qs_tr1" ); + // next(next(co-linear)) segment + myQs.segNodes( segListArray[0] ); + ok( ( segListArray[0].rootFrom == qs_tr1 ), "ptNode_colinear_inside C: next-next, left -> qs_tr1" ); + ok( ( segListArray[0].rootTo == qs_tr1 ), "ptNode_colinear_inside C: next-next reverse, left -> qs_tr1" ); // fully right side, CW // @@ -949,16 +980,16 @@ function test_QueryStructure() { // prev(co-linear) segment myQs.segNodes( segListArray[3] ); - ok( ( segListArray[3].rootFrom == qs_tr3 ), "ptNode_colinear#1 C: prev, right -> qs_tr3" ); - ok( ( segListArray[3].rootTo == qs_tr3 ), "ptNode_colinear#1 C: prev reverse, right -> qs_tr3" ); + ok( ( segListArray[3].rootFrom == qs_tr3 ), "ptNode_colinear_inside D: prev, right -> qs_tr3" ); + ok( ( segListArray[3].rootTo == qs_tr3 ), "ptNode_colinear_inside D: prev reverse, right -> qs_tr3" ); // co-linear segment myQs.segNodes( segListArray[4] ); - ok( ( segListArray[4].rootFrom == qs_tr3 ), "ptNode_colinear#1 C: co-lin, right -> qs_tr3" ); - ok( ( segListArray[4].rootTo == qs_tr3 ), "ptNode_colinear#1 C: co-lin reverse, right -> qs_tr3" ); + ok( ( segListArray[4].rootFrom == qs_tr3 ), "ptNode_colinear_inside D: co-lin, right -> qs_tr3" ); + ok( ( segListArray[4].rootTo == qs_tr3 ), "ptNode_colinear_inside D: co-lin reverse, right -> qs_tr3" ); // next(co-linear) segment myQs.segNodes( segListArray[5] ); - ok( ( segListArray[5].rootFrom == qs_tr3 ), "ptNode_colinear#1 C: next, right -> qs_tr3" ); - ok( ( segListArray[5].rootTo == qs_tr3 ), "ptNode_colinear#1 C: next reverse, right -> qs_tr3" ); + ok( ( segListArray[5].rootFrom == qs_tr3 ), "ptNode_colinear_inside D: next, right -> qs_tr3" ); + ok( ( segListArray[5].rootTo == qs_tr3 ), "ptNode_colinear_inside D: next reverse, right -> qs_tr3" ); // // horizontal lines @@ -978,27 +1009,63 @@ function test_QueryStructure() { // prev(prev(co-linear)) segment myQs.segNodes( segListArray[2] ); - ok( ( segListArray[2].rootFrom == qs_tr3 ), "ptNode_colinear#1 D: prev-prev, right -> qs_tr3" ); - ok( ( segListArray[2].rootTo == qs_tr2 ), "ptNode_colinear#1 D: prev-prev reverse, right -> qs_tr2" ); + ok( ( segListArray[2].rootFrom == qs_tr3 ), "ptNode_colinear_inside E: prev-prev, right -> qs_tr3" ); + ok( ( segListArray[2].rootTo == qs_tr2 ), "ptNode_colinear_inside E: prev-prev reverse, right -> qs_tr2" ); + // prev(co-linear) segment + myQs.segNodes( segListArray[3] ); + ok( ( segListArray[3].rootFrom == qs_tr2 ), "ptNode_colinear_inside E: prev, right -> qs_tr2" ); + ok( ( segListArray[3].rootTo == qs_tr3 ), "ptNode_colinear_inside E: prev reverse, right -> qs_tr3" ); + // co-linear segment + myQs.segNodes( segListArray[4] ); + ok( ( segListArray[4].rootFrom == qs_tr3 ), "ptNode_colinear_inside E: co-lin, right -> qs_tr3" ); + ok( ( segListArray[4].rootTo == qs_tr3 ), "ptNode_colinear_inside E: co-lin reverse, right -> qs_tr3" ); + // next(co-linear) segment + myQs.segNodes( segListArray[5] ); + ok( ( segListArray[5].rootFrom == qs_tr3 ), "ptNode_colinear_inside E: next, right -> qs_tr3" ); + ok( ( segListArray[5].rootTo == qs_tr2 ), "ptNode_colinear_inside E: next reverse, right -> qs_tr2" ); + // next(next(co-linear)) segment + myQs.segNodes( segListArray[0] ); + ok( ( segListArray[0].rootFrom == qs_tr2 ), "ptNode_colinear_inside E: next-next, right -> qs_tr2" ); + ok( ( segListArray[0].rootTo == qs_tr2 ), "ptNode_colinear_inside E: next-next reverse, right -> qs_tr2" ); + + // + // vertical lines + var testPolygon = [ { x: 45, y: 15 }, { x: 30, y: 10 }, { x: 30, y: 40 }, + { x: 35, y: 35 }, { x: 30, y: 30 }, { x: 30, y: 20 } ]; + var myPolygonData = new PNLTRI.PolygonData( [ testPolygon ] ); + var segListArray = myPolygonData.getSegments(); + + var myQs = new PNLTRI.QueryStructure( myPolygonData ); + var myQsRoot = myQs.getRoot(); + + myQs.add_segment( segListArray[1] ); // touch line, 2nd touch line: segListArray[4] + var qs_tr3 = myQs.getTrapByIdx(3).sink; +// showDataStructure( myQsRoot ); +// drawTrapezoids( myQsRoot, false ); + + // prev(prev(co-linear)) segment + myQs.segNodes( segListArray[2] ); + ok( ( segListArray[2].rootFrom == qs_tr3 ), "ptNode_colinear_inside F: prev-prev, left -> qs_tr3" ); + ok( ( segListArray[2].rootTo == qs_tr3 ), "ptNode_colinear_inside F: prev-prev reverse, left -> qs_tr3" ); // prev(co-linear) segment myQs.segNodes( segListArray[3] ); - ok( ( segListArray[3].rootFrom == qs_tr2 ), "ptNode_colinear#1 D: prev, right -> qs_tr2" ); - ok( ( segListArray[3].rootTo == qs_tr3 ), "ptNode_colinear#1 D: prev reverse, right -> qs_tr3" ); + ok( ( segListArray[3].rootFrom == qs_tr3 ), "ptNode_colinear_inside F: prev, left -> qs_tr3" ); + ok( ( segListArray[3].rootTo == qs_tr3 ), "ptNode_colinear_inside F: prev reverse, left -> qs_tr3" ); // co-linear segment myQs.segNodes( segListArray[4] ); - ok( ( segListArray[4].rootFrom == qs_tr3 ), "ptNode_colinear#1 D: co-lin, right -> qs_tr3" ); - ok( ( segListArray[4].rootTo == qs_tr3 ), "ptNode_colinear#1 D: co-lin reverse, right -> qs_tr3" ); + ok( ( segListArray[4].rootFrom == qs_tr3 ), "ptNode_colinear_inside F: co-lin, left -> qs_tr3" ); + ok( ( segListArray[4].rootTo == qs_tr3 ), "ptNode_colinear_inside F: co-lin reverse, left -> qs_tr3" ); // next(co-linear) segment myQs.segNodes( segListArray[5] ); - ok( ( segListArray[5].rootFrom == qs_tr3 ), "ptNode_colinear#1 D: next, right -> qs_tr3" ); - ok( ( segListArray[5].rootTo == qs_tr2 ), "ptNode_colinear#1 D: next reverse, right -> qs_tr2" ); + ok( ( segListArray[5].rootFrom == qs_tr3 ), "ptNode_colinear_inside F: next, left -> qs_tr3" ); + ok( ( segListArray[5].rootTo == qs_tr3 ), "ptNode_colinear_inside F: next reverse, left -> qs_tr3" ); // next(next(co-linear)) segment myQs.segNodes( segListArray[0] ); - ok( ( segListArray[0].rootFrom == qs_tr2 ), "ptNode_colinear#1 D: next-next, right -> qs_tr2" ); - ok( ( segListArray[0].rootTo == qs_tr2 ), "ptNode_colinear#1 D: next-next reverse, right -> qs_tr2" ); + ok( ( segListArray[0].rootFrom == qs_tr3 ), "ptNode_colinear_inside F: next-next, left -> qs_tr3" ); + ok( ( segListArray[0].rootTo == qs_tr3 ), "ptNode_colinear_inside F: next-next reverse, left -> qs_tr3" ); } - function test_ptNode_colinear_2() { + function test_ptNode_colinear_overlapping() { // overlapping left low, right high, CCW // @@ -1010,7 +1077,7 @@ function test_QueryStructure() { // +__ 2 // var testPolygon = [ { x: 25, y: 14 }, { x: 20, y: 20 }, { x: 40, y: 40 }, - { x: 35, y: 37 }, { x: 30, y: 30 }, { x: 10, y: 10 } ]; + { x: 29, y: 37 }, { x: 30, y: 30 }, { x: 10, y: 10 } ]; var myPolygonData = new PNLTRI.PolygonData( [ testPolygon ] ); var segListArray = myPolygonData.getSegments(); @@ -1025,16 +1092,16 @@ function test_QueryStructure() { // prev(co-linear) segment myQs.segNodes( segListArray[3] ); - ok( ( segListArray[3].rootFrom == qs_tr1 ), "ptNode_colinear#2 A: prev, left -> qs_tr1" ); - ok( ( segListArray[3].rootTo == qs_tr1 ), "ptNode_colinear#2 A: prev reverse, left -> qs_tr1" ); + ok( ( segListArray[3].rootFrom == qs_tr1 ), "ptNode_colinear_overlapping A: prev, left -> qs_tr1" ); + ok( ( segListArray[3].rootTo == qs_tr1 ), "ptNode_colinear_overlapping A: prev reverse, left -> qs_tr1" ); // co-linear segment myQs.segNodes( segListArray[4] ); - ok( ( segListArray[4].rootFrom == qs_tr1 ), "ptNode_colinear#2 A: co-lin, left -> qs_tr1" ); - ok( ( segListArray[4].rootTo == qs_tr2 ), "ptNode_colinear#2 A: co-lin reverse, left -> qs_tr2" ); + ok( ( segListArray[4].rootFrom == qs_tr1 ), "ptNode_colinear_overlapping A: co-lin, left -> qs_tr1" ); + ok( ( segListArray[4].rootTo == qs_tr2 ), "ptNode_colinear_overlapping A: co-lin reverse, left -> qs_tr2" ); // next(co-linear) segment myQs.segNodes( segListArray[5] ); - ok( ( segListArray[5].rootFrom == qs_tr2 ), "ptNode_colinear#2 A: next, left -> qs_tr2" ); - ok( ( segListArray[5].rootTo == qs_tr2 ), "ptNode_colinear#2 A: next reverse, left -> qs_tr2" ); + ok( ( segListArray[5].rootFrom == qs_tr2 ), "ptNode_colinear_overlapping A: next, left -> qs_tr2" ); + ok( ( segListArray[5].rootTo == qs_tr2 ), "ptNode_colinear_overlapping A: next reverse, left -> qs_tr2" ); // // other sequence @@ -1052,16 +1119,16 @@ function test_QueryStructure() { // prev(co-linear) segment myQs.segNodes( segListArray[0] ); - ok( ( segListArray[0].rootFrom == qs_tr3 ), "ptNode_colinear#2 B: prev, right -> qs_tr3" ); - ok( ( segListArray[0].rootTo == qs_tr3 ), "ptNode_colinear#2 B: prev reverse, right -> qs_tr3" ); + ok( ( segListArray[0].rootFrom == qs_tr3 ), "ptNode_colinear_overlapping B: prev, right -> qs_tr3" ); + ok( ( segListArray[0].rootTo == qs_tr3 ), "ptNode_colinear_overlapping B: prev reverse, right -> qs_tr3" ); // co-linear segment myQs.segNodes( segListArray[1] ); - ok( ( segListArray[1].rootFrom == qs_tr3 ), "ptNode_colinear#2 B: co-lin, right -> qs_tr3" ); - ok( ( segListArray[1].rootTo == qs_tr0 ), "ptNode_colinear#1 B: co-lin reverse, right -> qs_tr0" ); + ok( ( segListArray[1].rootFrom == qs_tr3 ), "ptNode_colinear_overlapping B: co-lin, right -> qs_tr3" ); + ok( ( segListArray[1].rootTo == qs_tr0 ), "ptNode_colinear_overlapping B: co-lin reverse, right -> qs_tr0" ); // next(co-linear) segment myQs.segNodes( segListArray[2] ); - ok( ( segListArray[2].rootFrom == qs_tr0 ), "ptNode_colinear#2 B: next, right -> qs_tr0" ); - ok( ( segListArray[2].rootTo == qs_tr0 ), "ptNode_colinear#1 B: next reverse, right -> qs_tr0" ); + ok( ( segListArray[2].rootFrom == qs_tr0 ), "ptNode_colinear_overlapping B: next, right -> qs_tr0" ); + ok( ( segListArray[2].rootTo == qs_tr0 ), "ptNode_colinear_overlapping B: next reverse, right -> qs_tr0" ); // overlapping right low, left high, CW @@ -1089,16 +1156,16 @@ function test_QueryStructure() { // prev(co-linear) segment myQs.segNodes( segListArray[3] ); - ok( ( segListArray[3].rootFrom == qs_tr3 ), "ptNode_colinear#2 C: prev, right -> qs_tr3" ); - ok( ( segListArray[3].rootTo == qs_tr3 ), "ptNode_colinear#2 C: prev reverse, right -> qs_tr3" ); + ok( ( segListArray[3].rootFrom == qs_tr3 ), "ptNode_colinear_overlapping C: prev, right -> qs_tr3" ); + ok( ( segListArray[3].rootTo == qs_tr3 ), "ptNode_colinear_overlapping C: prev reverse, right -> qs_tr3" ); // co-linear segment myQs.segNodes( segListArray[4] ); - ok( ( segListArray[4].rootFrom == qs_tr3 ), "ptNode_colinear#2 C: co-lin, right -> qs_tr3" ); - ok( ( segListArray[4].rootTo == qs_tr2 ), "ptNode_colinear#1 C: co-lin reverse, right -> qs_tr2" ); + ok( ( segListArray[4].rootFrom == qs_tr3 ), "ptNode_colinear_overlapping C: co-lin, right -> qs_tr3" ); + ok( ( segListArray[4].rootTo == qs_tr2 ), "ptNode_colinear_overlapping C: co-lin reverse, right -> qs_tr2" ); // next(co-linear) segment myQs.segNodes( segListArray[5] ); - ok( ( segListArray[5].rootFrom == qs_tr2 ), "ptNode_colinear#2 C: next, right -> qs_tr2" ); - ok( ( segListArray[5].rootTo == qs_tr2 ), "ptNode_colinear#1 C: next reverse, right -> qs_tr2" ); + ok( ( segListArray[5].rootFrom == qs_tr2 ), "ptNode_colinear_overlapping C: next, right -> qs_tr2" ); + ok( ( segListArray[5].rootTo == qs_tr2 ), "ptNode_colinear_overlapping C: next reverse, right -> qs_tr2" ); // // other sequence @@ -1116,18 +1183,149 @@ function test_QueryStructure() { // prev(co-linear) segment myQs.segNodes( segListArray[0] ); - ok( ( segListArray[0].rootFrom == qs_tr1 ), "ptNode_colinear#2 D: prev, left -> qs_tr1" ); - ok( ( segListArray[0].rootTo == qs_tr1 ), "ptNode_colinear#2 D: prev reverse, left -> qs_tr1" ); + ok( ( segListArray[0].rootFrom == qs_tr1 ), "ptNode_colinear_overlapping D: prev, left -> qs_tr1" ); + ok( ( segListArray[0].rootTo == qs_tr1 ), "ptNode_colinear_overlapping D: prev reverse, left -> qs_tr1" ); // co-linear segment myQs.segNodes( segListArray[1] ); - ok( ( segListArray[1].rootFrom == qs_tr1 ), "ptNode_colinear#2 D: co-lin, left -> qs_tr1" ); - ok( ( segListArray[1].rootTo == qs_tr0 ), "ptNode_colinear#1 D: co-lin reverse, left -> qs_tr0" ); + ok( ( segListArray[1].rootFrom == qs_tr1 ), "ptNode_colinear_overlapping D: co-lin, left -> qs_tr1" ); + ok( ( segListArray[1].rootTo == qs_tr0 ), "ptNode_colinear_overlapping D: co-lin reverse, left -> qs_tr0" ); // next(co-linear) segment myQs.segNodes( segListArray[2] ); - ok( ( segListArray[2].rootFrom == qs_tr0 ), "ptNode_colinear#2 D: next, left -> qs_tr0" ); - ok( ( segListArray[2].rootTo == qs_tr0 ), "ptNode_colinear#1 D: next reverse, left -> qs_tr0" ); + ok( ( segListArray[2].rootFrom == qs_tr0 ), "ptNode_colinear_overlapping D: next, left -> qs_tr0" ); + ok( ( segListArray[2].rootTo == qs_tr0 ), "ptNode_colinear_overlapping D: next reverse, left -> qs_tr0" ); } + function test_ptNode_colinear_reversal() { + + // overlapping left low, right high, CCW + // + // 0 + // -----------*--------------- myQsRoot + // + / + // 1 +/ 3 + // +/ + // -------*------------------- qsL + // 2 + // + var testPolygon = [ { x: 20, y: 20 }, { x: 40, y: 40 }, + { x: 29, y: 37 }, { x: 30, y: 30 } ]; + var myPolygonData = new PNLTRI.PolygonData( [ testPolygon ] ); + var segListArray = myPolygonData.getSegments(); + + var myQs = new PNLTRI.QueryStructure( myPolygonData ); + var myQsRoot = myQs.getRoot(); + + myQs.add_segment( segListArray[0] ); // touch line, 2nd touch line: segListArray[3] + var qs_tr1 = myQs.getTrapByIdx(1).sink; + var qs_tr3 = myQs.getTrapByIdx(3).sink; +// showDataStructure( myQsRoot ); +// drawTrapezoids( myQsRoot, false ); + + // prev(co-linear) segment + myQs.segNodes( segListArray[2] ); + ok( ( segListArray[2].rootFrom == qs_tr1 ), "ptNode_colinear_reversal A: prev, left -> qs_tr1" ); + ok( ( segListArray[2].rootTo == qs_tr1 ), "ptNode_colinear_reversal A: prev reverse, left -> qs_tr1" ); + // co-linear segment + myQs.segNodes( segListArray[3] ); + ok( ( segListArray[3].rootFrom == qs_tr1 ), "ptNode_colinear_reversal A: co-lin, left -> qs_tr1" ); + ok( ( segListArray[3].rootTo == qs_tr3 ), "ptNode_colinear_reversal A: co-lin reverse, left -> qs_tr1" ); // TODO Error + // next(base) segment + myQs.segNodes( segListArray[1] ); + ok( ( segListArray[1].rootFrom == qs_tr1 ), "ptNode_colinear_reversal A: next(base), left -> qs_tr1" ); + ok( ( segListArray[1].rootTo == qs_tr1 ), "ptNode_colinear_reversal A: next(base) reverse, left -> qs_tr1" ); + + // +/* // other sequence + var myPolygonData = new PNLTRI.PolygonData( [ testPolygon ] ); + var segListArray = myPolygonData.getSegments(); + + var myQs = new PNLTRI.QueryStructure( myPolygonData ); + var myQsRoot = myQs.getRoot(); + + myQs.add_segment( segListArray[4] ); // touch line, 2nd touch line: segListArray[1] + var qs_tr0 = myQs.getTrapByIdx(0).sink; + var qs_tr3 = myQs.getTrapByIdx(3).sink; +// showDataStructure( myQsRoot ); +// drawTrapezoids( myQsRoot, false ); + + // prev(co-linear) segment + myQs.segNodes( segListArray[0] ); + ok( ( segListArray[0].rootFrom == qs_tr3 ), "ptNode_colinear_reversal B: prev, right -> qs_tr3" ); + ok( ( segListArray[0].rootTo == qs_tr3 ), "ptNode_colinear_reversal B: prev reverse, right -> qs_tr3" ); + // co-linear segment + myQs.segNodes( segListArray[1] ); + ok( ( segListArray[1].rootFrom == qs_tr3 ), "ptNode_colinear_reversal B: co-lin, right -> qs_tr3" ); + ok( ( segListArray[1].rootTo == qs_tr0 ), "ptNode_colinear_reversal B: co-lin reverse, right -> qs_tr0" ); + // next(co-linear) segment + myQs.segNodes( segListArray[2] ); + ok( ( segListArray[2].rootFrom == qs_tr0 ), "ptNode_colinear_reversal B: next, right -> qs_tr0" ); + ok( ( segListArray[2].rootTo == qs_tr0 ), "ptNode_colinear_reversal B: next reverse, right -> qs_tr0" ); + + + // overlapping right low, left high, CW + // + // 0 + // ------------*-------------- myQsRoot + // / + // 1 /++ 3 + // ---------*+ qsL + // + 2 + // + + var testPolygon = [ { x: 5, y: 14 }, { x: 20, y: 20 }, { x: 40, y: 40 }, + { x: 45, y: 37 }, { x: 30, y: 30 }, { x: 10, y: 10 } ]; + var myPolygonData = new PNLTRI.PolygonData( [ testPolygon ] ); + var segListArray = myPolygonData.getSegments(); + + var myQs = new PNLTRI.QueryStructure( myPolygonData ); + var myQsRoot = myQs.getRoot(); + + myQs.add_segment( segListArray[1] ); // touch line, 2nd touch line: segListArray[4] + var qs_tr2 = myQs.getTrapByIdx(2).sink; + var qs_tr3 = myQs.getTrapByIdx(3).sink; +// showDataStructure( myQsRoot ); +// drawTrapezoids( myQsRoot, false ); + + // prev(co-linear) segment + myQs.segNodes( segListArray[3] ); + ok( ( segListArray[3].rootFrom == qs_tr3 ), "ptNode_colinear_reversal C: prev, right -> qs_tr3" ); + ok( ( segListArray[3].rootTo == qs_tr3 ), "ptNode_colinear_reversal C: prev reverse, right -> qs_tr3" ); + // co-linear segment + myQs.segNodes( segListArray[4] ); + ok( ( segListArray[4].rootFrom == qs_tr3 ), "ptNode_colinear_reversal C: co-lin, right -> qs_tr3" ); + ok( ( segListArray[4].rootTo == qs_tr2 ), "ptNode_colinear_reversal C: co-lin reverse, right -> qs_tr2" ); + // next(co-linear) segment + myQs.segNodes( segListArray[5] ); + ok( ( segListArray[5].rootFrom == qs_tr2 ), "ptNode_colinear_reversal C: next, right -> qs_tr2" ); + ok( ( segListArray[5].rootTo == qs_tr2 ), "ptNode_colinear_reversal C: next reverse, right -> qs_tr2" ); + + // + // other sequence + var myPolygonData = new PNLTRI.PolygonData( [ testPolygon ] ); + var segListArray = myPolygonData.getSegments(); + + var myQs = new PNLTRI.QueryStructure( myPolygonData ); + var myQsRoot = myQs.getRoot(); + + myQs.add_segment( segListArray[4] ); // touch line, 2nd touch line: segListArray[1] + var qs_tr0 = myQs.getTrapByIdx(0).sink; + var qs_tr1 = myQs.getTrapByIdx(1).sink; +// showDataStructure( myQsRoot ); +// drawTrapezoids( myQsRoot, false ); + + // prev(co-linear) segment + myQs.segNodes( segListArray[0] ); + ok( ( segListArray[0].rootFrom == qs_tr1 ), "ptNode_colinear_reversal D: prev, left -> qs_tr1" ); + ok( ( segListArray[0].rootTo == qs_tr1 ), "ptNode_colinear_reversal D: prev reverse, left -> qs_tr1" ); + // co-linear segment + myQs.segNodes( segListArray[1] ); + ok( ( segListArray[1].rootFrom == qs_tr1 ), "ptNode_colinear_reversal D: co-lin, left -> qs_tr1" ); + ok( ( segListArray[1].rootTo == qs_tr0 ), "ptNode_colinear_reversal D: co-lin reverse, left -> qs_tr0" ); + // next(co-linear) segment + myQs.segNodes( segListArray[2] ); + ok( ( segListArray[2].rootFrom == qs_tr0 ), "ptNode_colinear_reversal D: next, left -> qs_tr0" ); + ok( ( segListArray[2].rootTo == qs_tr0 ), "ptNode_colinear_reversal D: next reverse, left -> qs_tr0" ); +*/ } + /**************************************************************************/ @@ -1838,42 +2036,6 @@ function test_QueryStructure() { function test_add_segment_special_5() { - var myPolygonData = new PNLTRI.PolygonData( [ [ - { x: 105, y: 100.4 }, - { x: 104, y: 100.39999999999998 }, - { x: 103, y: 100.40000000000003 }, - { x: 102, y: 100.4 }, - { x: 101, y: 100.40000000000002 }, - ] ] ); - var segListArray = myPolygonData.getSegments(); -// showDataStructure( myPolygonData.getVertices(), [ 'sprev', 'snext', 'vertTo', 'segOut' ] ); -// showDataStructure( myPolygonData.getSegments(), [ 'sprev', 'snext', 'mprev', 'mnext' ] ); - // - var myQs = new PNLTRI.QueryStructure( myPolygonData ); - // - myQs.add_segment_consistently( segListArray[0], 'Spec_5 #1' ); - myQs.add_segment_consistently( segListArray[1], 'Spec_5 #2' ); - // complex case: EPSILON > rounding effect on coordinates - myQs.add_segment_consistently( segListArray[3], 'Spec_5 Main' ); - equal( myQs.nbTrapezoids(), 9, "Spec_5: Number of Trapezoids" ); - - myQs.check_trapezoid_neighbors( 0, null, null, 1, 3, "Spec_5: neighbors trap0" ); - myQs.check_trapezoid_neighbors( 1, 0, null, 2, null, "Spec_5: neighbors trap1" ); - myQs.check_trapezoid_neighbors( 2, 1, null, 4, null, "Spec_5: neighbors trap2" ); - myQs.check_trapezoid_neighbors( 3, null, 0, null, 5, "Spec_5: neighbors trap3" ); - myQs.check_trapezoid_neighbors( 4, 2, 5, 6, 8, "Spec_5: neighbors trap4" ); - myQs.check_trapezoid_neighbors( 5, null, 3, null, 4, "Spec_5: neighbors trap5" ); - myQs.check_trapezoid_neighbors( 6, 4, null, 7, null, "Spec_5: neighbors trap6" ); - myQs.check_trapezoid_neighbors( 7, 6, 8, null, null, "Spec_5: neighbors trap7" ); - myQs.check_trapezoid_neighbors( 8, null, 4, null, 7, "Spec_5: neighbors trap8" ); - -// var myQsRoot = myQs.getRoot(); -// showDataStructure( myQsRoot ); -// drawTrapezoids( myQsRoot, false, 0.4 ); - } - - function test_add_segment_special_6() { - var myPolygonData = new PNLTRI.PolygonData( [ [ { x:43, y:31 }, { x:41, y:29 }, { x:42, y:40 }, { x:36, y:24 }, @@ -1901,7 +2063,7 @@ function test_QueryStructure() { // drawTrapezoids( myQsRoot, false, 1 ); } - function test_add_segment_special_7() { + function test_add_segment_special_6() { var myPolygonData = new PNLTRI.PolygonData( [ [ { x:10, y:34 }, { x:36, y:12 }, { x:32, y:20 }, { x:28, y:24 }, @@ -1990,8 +2152,9 @@ function test_QueryStructure() { // test_ptNode(); test_ptNode_touching(); - test_ptNode_colinear_1(); - test_ptNode_colinear_2(); + test_ptNode_colinear_inside(); + test_ptNode_colinear_overlapping(); + test_ptNode_colinear_reversal(); // test_assign_depths(); // @@ -2013,9 +2176,8 @@ function test_QueryStructure() { test_add_segment_special_2(); test_add_segment_special_3(); test_add_segment_special_4(); -// test_add_segment_special_5(); // co-linear removed on input + test_add_segment_special_5(); test_add_segment_special_6(); - test_add_segment_special_7(); // for testing new polygons // test_add_segment_NEW(); // test_add_segment_Error();