diff --git a/biome.json b/biome.json index 91653bd..95f4b71 100644 --- a/biome.json +++ b/biome.json @@ -1,7 +1,12 @@ { "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", "files": { - "include": ["src/**/*.ts", "spec/**/*.ts", "rollup.config.mjs", "vite.config.ts"] + "include": [ + "src/**/*.ts", + "spec/**/*.ts", + "rollup.config.mjs", + "vite.config.ts" + ] }, "organizeImports": { "enabled": true @@ -29,7 +34,7 @@ "enabled": true, "indentStyle": "space", "indentWidth": 2, - "lineWidth": 100 + "lineWidth": 80 }, "json": { "parser": { @@ -39,7 +44,7 @@ "enabled": true, "indentStyle": "space", "indentWidth": 2, - "lineWidth": 120 + "lineWidth": 80 } }, "javascript": { @@ -52,7 +57,7 @@ "arrowParentheses": "always", "indentStyle": "space", "indentWidth": 2, - "lineWidth": 120, + "lineWidth": 80, "quoteProperties": "asNeeded" } }, diff --git a/spec/tin.spec.ts b/spec/tin.spec.ts index 2863f23..d8fff91 100644 --- a/spec/tin.spec.ts +++ b/spec/tin.spec.ts @@ -52,10 +52,14 @@ const testSet = ({ isStateFull }: { isStateFull: boolean }) => { // After 0.7.3 Old format load test [compiled, loaded].forEach((target) => { // points - expect(treeWalk(expected.points, 5)).toEqual(treeWalk(target.points, 5)); + expect(treeWalk(expected.points, 5)).toEqual( + treeWalk(target.points, 5), + ); // edges - expect(treeWalk(expected.edges, 5)).toEqual(treeWalk(target.edges, 5)); + expect(treeWalk(expected.edges, 5)).toEqual( + treeWalk(target.edges, 5), + ); // weight buffer //expect(treeWalk(expected.weight_buffer.forw, 1)).toEqual( @@ -67,15 +71,21 @@ const testSet = ({ isStateFull }: { isStateFull: boolean }) => { // tins points expected.tins_points.forEach((expected_tins: any, index: number) => { - expect(sortTinsPoint(expected_tins)).toEqual(sortTinsPoint(target.tins_points[index])); + expect(sortTinsPoint(expected_tins)).toEqual( + sortTinsPoint(target.tins_points[index]), + ); }); // edge nodes - expect(treeWalk(expected.edgeNodes, 5)).toEqual(treeWalk(target.edgeNodes, 5)); + expect(treeWalk(expected.edgeNodes, 5)).toEqual( + treeWalk(target.edgeNodes, 5), + ); // kinks points if (expected.kinks_points) { - expect(sortKinksPoint(expected.kinks_points)).toEqual(sortKinksPoint(target.kinks_points)); + expect(sortKinksPoint(expected.kinks_points)).toEqual( + sortKinksPoint(target.kinks_points), + ); } }); @@ -131,11 +141,23 @@ const testSet = ({ isStateFull }: { isStateFull: boolean }) => { await tin.updateTinAsync(); expect(tin.xy).toEqual([50, 50]); expect(tin.wh).toEqual([100, 150]); - deepCloseTo(tin.transform([140, 150]) as number[], [277.25085848926574, -162.19095375292216], 7); - expect(tin.transform([277.25085848926574, -162.19095375292216], true)).toEqual([140, 150]); + deepCloseTo( + tin.transform([140, 150]) as number[], + [277.25085848926574, -162.19095375292216], + 7, + ); + expect( + tin.transform([277.25085848926574, -162.19095375292216], true), + ).toEqual([140, 150]); expect(tin.transform([200, 130])).toEqual(false); - expect(tin.transform([401.98029725204117, -110.95171624700066], true)).toEqual(false); - deepCloseTo(tin.transform([200, 130], false, true) as number[], [401.98029725204117, -110.95171624700066], 7); + expect( + tin.transform([401.98029725204117, -110.95171624700066], true), + ).toEqual(false); + deepCloseTo( + tin.transform([200, 130], false, true) as number[], + [401.98029725204117, -110.95171624700066], + 7, + ); }); }); @@ -183,7 +205,9 @@ const testSet = ({ isStateFull }: { isStateFull: boolean }) => { } catch (e) { err = e; } - expect(err).not.toEqual('Backward transform is not allowed if strict_status == "strict_error"'); + expect(err).not.toEqual( + 'Backward transform is not allowed if strict_status == "strict_error"', + ); tin.setStrictMode(Tin.MODE_STRICT); await tin.updateTinAsync(); expect(tin.strict_status).toEqual(Tin.STATUS_ERROR); @@ -193,7 +217,9 @@ const testSet = ({ isStateFull }: { isStateFull: boolean }) => { } catch (e) { err = e; } - expect(err).toEqual('Backward transform is not allowed if strict_status == "strict_error"'); + expect(err).toEqual( + 'Backward transform is not allowed if strict_status == "strict_error"', + ); }); }); @@ -238,7 +264,8 @@ const testSet = ({ isStateFull }: { isStateFull: boolean }) => { }; describe("Test for Tin function", () => testSet({ isStateFull: false })); -describe("Test for Tin function (StateFull)", () => testSet({ isStateFull: true })); +describe("Test for Tin function (StateFull)", () => + testSet({ isStateFull: true })); function treeWalk(obj: any, depth: number) { if (typeof obj === "object") { @@ -264,6 +291,14 @@ function sortTinsPoint(tins_points: any[][]) { function sortKinksPoint(kinks_points: number[][]) { return (treeWalk(kinks_points, 5) as number[][]).sort((a, b) => - a[0] === b[0] ? (a[1] === b[1] ? 0 : a[1] > b[1] ? 1 : -1) : a[0] > b[0] ? 1 : -1, + a[0] === b[0] + ? a[1] === b[1] + ? 0 + : a[1] > b[1] + ? 1 + : -1 + : a[0] > b[0] + ? 1 + : -1, ); } diff --git a/src/booleanPointInPolygon.ts b/src/booleanPointInPolygon.ts index 480b2e0..c45484e 100644 --- a/src/booleanPointInPolygon.ts +++ b/src/booleanPointInPolygon.ts @@ -1,4 +1,11 @@ -import type { BBox, Coord, Feature, MultiPolygon, Polygon, Properties } from "@turf/helpers"; +import type { + BBox, + Coord, + Feature, + MultiPolygon, + Polygon, + Properties, +} from "@turf/helpers"; import { getCoord, getGeom } from "@turf/invariant"; // http://en.wikipedia.org/wiki/Even%E2%80%93odd_rule @@ -28,7 +35,10 @@ import { getCoord, getGeom } from "@turf/invariant"; * turf.booleanPointInPolygon(pt, poly); * //= true */ -export default function booleanPointInPolygon( +export default function booleanPointInPolygon< + G extends Polygon | MultiPolygon, + P = Properties, +>( point: Coord, polygon: Feature | G, options: { @@ -75,7 +85,9 @@ export default function booleanPointInPolygon= pt[0] && bbox[3] >= pt[1]; + return ( + bbox[0] <= pt[0] && bbox[1] <= pt[1] && bbox[2] >= pt[0] && bbox[3] >= pt[1] + ); } // This function is based on https://github.com/rowanwins/point-in-polygon-hao diff --git a/src/constrained-tin.ts b/src/constrained-tin.ts index d335c20..fc7f6e9 100644 --- a/src/constrained-tin.ts +++ b/src/constrained-tin.ts @@ -12,7 +12,9 @@ export default function (points: FeatureCollection, edges: Edge[], z: string) { throw "Argument points must be FeatureCollection"; if (!Array.isArray(edges)) throw "Argument points must be Array of Array"; - const del_points = points.features.map((point) => (point.geometry as any).coordinates as number[]); + const del_points = points.features.map( + (point) => (point.geometry as any).coordinates as number[], + ); const del = Delaunator.from(del_points); let con: Constrainautor; const tris = []; diff --git a/src/index.ts b/src/index.ts index 7fad31f..f403f27 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,13 @@ import { featureCollection, lineString, point, polygon } from "@turf/helpers"; import intersect from "@turf/intersect"; import { getCoords } from "@turf/invariant"; import lineIntersect from "@turf/line-intersect"; -import type { Feature, FeatureCollection, Point, Polygon, Position } from "@turf/turf"; +import type { + Feature, + FeatureCollection, + Point, + Polygon, + Position, +} from "@turf/turf"; import union from "@turf/union"; import booleanPointInPolygon from "./booleanPointInPolygon"; import findIntersections from "./kinks"; @@ -155,7 +161,10 @@ export default class Tin { } setPoints(points: PointSet[]) { if (this.yaxisMode == Tin.YAXIS_FOLLOW) { - points = points.map((point) => [point[0], [point[1][0], -1 * point[1][1]]]); + points = points.map((point) => [ + point[0], + [point[1][0], -1 * point[1][1]], + ]); } this.points = points; this.tins = undefined; @@ -191,7 +200,10 @@ export default class Tin { this.indexedTins = undefined; } setCompiled(compiled: Compiled | CompiledLegacy) { - if (compiled.version || (!(compiled as any).tins && compiled.points && compiled.tins_points)) { + if ( + compiled.version || + (!(compiled as any).tins && compiled.points && compiled.tins_points) + ) { // 新コンパイルロジック // pointsはそのままpoints this.points = compiled.points; @@ -303,7 +315,9 @@ export default class Tin { // kinksを復元 if (compiled.kinks_points) { this.kinks = { - bakw: featureCollection(compiled.kinks_points.map((coord: Position) => point(coord))), + bakw: featureCollection( + compiled.kinks_points.map((coord: Position) => point(coord)), + ), }; } // yaxisModeを復元 @@ -381,7 +395,10 @@ export default class Tin { this.centroid!.forw!.properties!.target.geom, ]; // vertices_paramsの最初の値はそのまま保存 - compiled.vertices_params = [this.vertices_params!.forw![0], this.vertices_params!.bakw![0]]; + compiled.vertices_params = [ + this.vertices_params!.forw![0], + this.vertices_params!.bakw![0], + ]; // vertices_paramsの2番目の値(セントロイドと地図頂点の三角形ポリゴン)は、地図頂点座標のみ記録 compiled.vertices_points = []; const vertices = this.vertices_params!.forw![1]; @@ -395,17 +412,27 @@ export default class Tin { // tinは座標インデックスのみ記録 compiled.tins_points = [[]]; this.tins!.forw!.features.map((tin: Tri) => { - compiled.tins_points![0].push((["a", "b", "c"] as PropertyTriKey[]).map((idx) => tin.properties![idx]!.index)); + compiled.tins_points![0].push( + (["a", "b", "c"] as PropertyTriKey[]).map( + (idx) => tin.properties![idx]!.index, + ), + ); }); // 自動モードでエラーがある時(loose)は、逆方向のtinも記録。 // 厳格モードでエラーがある時(strict_error)は、エラー点情報(kinks)を記録。 if (this.strict_status == Tin.STATUS_LOOSE) { compiled.tins_points[1] = []; this.tins!.bakw!.features.map((tin: Tri) => { - compiled.tins_points![1].push((["a", "b", "c"] as PropertyTriKey[]).map((idx) => tin.properties![idx]!.index)); + compiled.tins_points![1].push( + (["a", "b", "c"] as PropertyTriKey[]).map( + (idx) => tin.properties![idx]!.index, + ), + ); }); } else if (this.strict_status == Tin.STATUS_ERROR) { - compiled.kinks_points = this.kinks!.bakw!.features.map((kink: Feature) => kink.geometry!.coordinates); + compiled.kinks_points = this.kinks!.bakw!.features.map( + (kink: Feature) => kink.geometry!.coordinates, + ); } // After 0.7.3: Freeze strict_mode & vertex_mode & Update yAxis logic compiled.yaxisMode = this.yaxisMode; @@ -439,14 +466,16 @@ export default class Tin { const forwEachBound = forw!.features.map((tri: Tri) => { let eachBound: Position[] = []; getCoords(tri)[0].map((point: Position) => { - if (forwBound.length === 0) forwBound = [Array.from(point), Array.from(point)]; + if (forwBound.length === 0) + forwBound = [Array.from(point), Array.from(point)]; else { if (point[0] < forwBound[0][0]) forwBound[0][0] = point[0]; if (point[0] > forwBound[1][0]) forwBound[1][0] = point[0]; if (point[1] < forwBound[0][1]) forwBound[0][1] = point[1]; if (point[1] > forwBound[1][1]) forwBound[1][1] = point[1]; } - if (eachBound.length === 0) eachBound = [Array.from(point), Array.from(point)]; + if (eachBound.length === 0) + eachBound = [Array.from(point), Array.from(point)]; else { if (point[0] < eachBound[0][0]) eachBound[0][0] = point[0]; if (point[0] > eachBound[1][0]) eachBound[1][0] = point[0]; @@ -458,31 +487,56 @@ export default class Tin { }); const forwXUnit = (forwBound[1][0] - forwBound[0][0]) / gridNum; const forwYUnit = (forwBound[1][1] - forwBound[0][1]) / gridNum; - const forwGridCache = forwEachBound.reduce((prev: number[][][], bound: Position[], index: number) => { - const normXMin = unitCalc(bound[0][0], forwBound[0][0], forwXUnit, gridNum); - const normXMax = unitCalc(bound[1][0], forwBound[0][0], forwXUnit, gridNum); - const normYMin = unitCalc(bound[0][1], forwBound[0][1], forwYUnit, gridNum); - const normYMax = unitCalc(bound[1][1], forwBound[0][1], forwYUnit, gridNum); - for (let cx = normXMin; cx <= normXMax; cx++) { - if (!prev[cx]) prev[cx] = []; - for (let cy = normYMin; cy <= normYMax; cy++) { - if (!prev[cx][cy]) prev[cx][cy] = []; - prev[cx][cy].push(index); + const forwGridCache = forwEachBound.reduce( + (prev: number[][][], bound: Position[], index: number) => { + const normXMin = unitCalc( + bound[0][0], + forwBound[0][0], + forwXUnit, + gridNum, + ); + const normXMax = unitCalc( + bound[1][0], + forwBound[0][0], + forwXUnit, + gridNum, + ); + const normYMin = unitCalc( + bound[0][1], + forwBound[0][1], + forwYUnit, + gridNum, + ); + const normYMax = unitCalc( + bound[1][1], + forwBound[0][1], + forwYUnit, + gridNum, + ); + for (let cx = normXMin; cx <= normXMax; cx++) { + if (!prev[cx]) prev[cx] = []; + for (let cy = normYMin; cy <= normYMax; cy++) { + if (!prev[cx][cy]) prev[cx][cy] = []; + prev[cx][cy].push(index); + } } - } - return prev; - }, []); + return prev; + }, + [], + ); const bakwEachBound = bakw!.features.map((tri: Tri) => { let eachBound: Position[] = []; getCoords(tri)[0].map((point: Position) => { - if (bakwBound.length === 0) bakwBound = [Array.from(point), Array.from(point)]; + if (bakwBound.length === 0) + bakwBound = [Array.from(point), Array.from(point)]; else { if (point[0] < bakwBound[0][0]) bakwBound[0][0] = point[0]; if (point[0] > bakwBound[1][0]) bakwBound[1][0] = point[0]; if (point[1] < bakwBound[0][1]) bakwBound[0][1] = point[1]; if (point[1] > bakwBound[1][1]) bakwBound[1][1] = point[1]; } - if (eachBound.length === 0) eachBound = [Array.from(point), Array.from(point)]; + if (eachBound.length === 0) + eachBound = [Array.from(point), Array.from(point)]; else { if (point[0] < eachBound[0][0]) eachBound[0][0] = point[0]; if (point[0] > eachBound[1][0]) eachBound[1][0] = point[0]; @@ -494,20 +548,43 @@ export default class Tin { }); const bakwXUnit = (bakwBound[1][0] - bakwBound[0][0]) / gridNum; const bakwYUnit = (bakwBound[1][1] - bakwBound[0][1]) / gridNum; - const bakwGridCache = bakwEachBound.reduce((prev: any, bound: any, index: number) => { - const normXMin = unitCalc(bound[0][0], bakwBound[0][0], bakwXUnit, gridNum); - const normXMax = unitCalc(bound[1][0], bakwBound[0][0], bakwXUnit, gridNum); - const normYMin = unitCalc(bound[0][1], bakwBound[0][1], bakwYUnit, gridNum); - const normYMax = unitCalc(bound[1][1], bakwBound[0][1], bakwYUnit, gridNum); - for (let cx = normXMin; cx <= normXMax; cx++) { - if (!prev[cx]) prev[cx] = []; - for (let cy = normYMin; cy <= normYMax; cy++) { - if (!prev[cx][cy]) prev[cx][cy] = []; - prev[cx][cy].push(index); + const bakwGridCache = bakwEachBound.reduce( + (prev: any, bound: any, index: number) => { + const normXMin = unitCalc( + bound[0][0], + bakwBound[0][0], + bakwXUnit, + gridNum, + ); + const normXMax = unitCalc( + bound[1][0], + bakwBound[0][0], + bakwXUnit, + gridNum, + ); + const normYMin = unitCalc( + bound[0][1], + bakwBound[0][1], + bakwYUnit, + gridNum, + ); + const normYMax = unitCalc( + bound[1][1], + bakwBound[0][1], + bakwYUnit, + gridNum, + ); + for (let cx = normXMin; cx <= normXMax; cx++) { + if (!prev[cx]) prev[cx] = []; + for (let cy = normYMin; cy <= normYMax; cy++) { + if (!prev[cx][cy]) prev[cx][cy] = []; + prev[cx][cy].push(index); + } } - } - return prev; - }, []); + return prev; + }, + [], + ); this.indexedTins = { forw: { gridNum, @@ -547,7 +624,11 @@ export default class Tin { } calcurateStrictTinAsync() { const edges = this.pointsSet.edges; - return Promise.all(this.tins!.forw!.features.map((tri: Tri) => Promise.resolve(counterTri(tri)))) + return Promise.all( + this.tins!.forw!.features.map((tri: Tri) => + Promise.resolve(counterTri(tri)), + ), + ) .then((tris: Tri[]) => { this.tins!.bakw = featureCollection(tris); }) @@ -556,10 +637,17 @@ export default class Tin { return Promise.all( this.tins!.forw!.features.map((forTri: Tri, index: number) => { const bakTri: Tri = this.tins!.bakw!.features[index]; - return Promise.resolve(insertSearchIndex(searchIndex, { forw: forTri, bakw: bakTri })); + return Promise.resolve( + insertSearchIndex(searchIndex, { forw: forTri, bakw: bakTri }), + ); }), ) - .then(() => Promise.all([overlapCheckAsync(searchIndex), Promise.resolve(searchIndex)])) + .then(() => + Promise.all([ + overlapCheckAsync(searchIndex), + Promise.resolve(searchIndex), + ]), + ) .catch((err) => { throw err; }); @@ -572,14 +660,22 @@ export default class Tin { if (overlapped.bakw[key] == "Not include case") return; const trises = searchIndex[key]; const forUnion = union(trises[0].forw, trises[1].forw); - const forConvex = convex(featureCollection([trises[0].forw, trises[1].forw])); + const forConvex = convex( + featureCollection([trises[0].forw, trises[1].forw]), + ); const forDiff = difference(forConvex!, forUnion!); if (forDiff) return; const splittedKey = key.split("-"); - if (splittedKey[0].match(/^[0-9]+$/) && splittedKey[1].match(/^[0-9]+$/)) { - const numberKey = splittedKey.map((key) => Number.parseInt(key)).sort((a, b) => (a < b ? -1 : 1)); + if ( + splittedKey[0].match(/^[0-9]+$/) && + splittedKey[1].match(/^[0-9]+$/) + ) { + const numberKey = splittedKey + .map((key) => Number.parseInt(key)) + .sort((a, b) => (a < b ? -1 : 1)); for (let i = 0; i < edges.length - 1; i++) { - if (numberKey[0] == edges[i][0] && numberKey[1] == edges[i][1]) return; + if (numberKey[0] == edges[i][0] && numberKey[1] == edges[i][1]) + return; } } const sharedVtx = splittedKey.map( @@ -601,13 +697,20 @@ export default class Tin { return { geom, prop }; }) .filter( - (vtx) => vtx.prop.index != sharedVtx[0].prop.index && vtx.prop.index != sharedVtx[1].prop.index, + (vtx) => + vtx.prop.index != sharedVtx[0].prop.index && + vtx.prop.index != sharedVtx[1].prop.index, )[0], ); removeSearchIndex(searchIndex, trises[0], this.tins); removeSearchIndex(searchIndex, trises[1], this.tins); sharedVtx.map((sVtx) => { - const newTriCoords = [sVtx.geom, nonSharedVtx[0].geom, nonSharedVtx[1].geom, sVtx.geom]; + const newTriCoords = [ + sVtx.geom, + nonSharedVtx[0].geom, + nonSharedVtx[1].geom, + sVtx.geom, + ]; const newTriProp = { a: sVtx.prop, b: nonSharedVtx[0].prop, @@ -615,13 +718,19 @@ export default class Tin { }; const newBakTri = polygon([newTriCoords], newTriProp); const newForTri = counterTri(newBakTri); - insertSearchIndex(searchIndex, { forw: newForTri, bakw: newBakTri }, this.tins); + insertSearchIndex( + searchIndex, + { forw: newForTri, bakw: newBakTri }, + this.tins, + ); }); }); return Promise.all( (["forw", "bakw"] as BiDirectionKey[]).map((direc) => new Promise((resolve) => { - const coords = this.tins![direc]!.features.map((poly) => poly.geometry!.coordinates[0]); + const coords = this.tins![direc]!.features.map( + (poly) => poly.geometry!.coordinates[0], + ); resolve(findIntersections(coords)); }).catch((err) => { throw err; @@ -636,8 +745,10 @@ export default class Tin { } else { this.strict_status = Tin.STATUS_ERROR; this.kinks = {}; - if (result[0].length > 0) this.kinks.forw = featureCollection(result[0]); - if (result[1].length > 0) this.kinks.bakw = featureCollection(result[1]); + if (result[0].length > 0) + this.kinks.forw = featureCollection(result[0]); + if (result[1].length > 0) + this.kinks.bakw = featureCollection(result[1]); } }) .catch((err) => { @@ -679,11 +790,14 @@ export default class Tin { const prev = arr[index - 1]; return Math.sqrt((node[0] - prev[0]) ** 2 + (node[1] - prev[1]) ** 2); }); - const sumLengths = eachLengths.reduce((prev: any, node: any, index: any) => { - if (index === 0) return [0]; - prev.push(prev[index - 1] + node); - return prev; - }, []); + const sumLengths = eachLengths.reduce( + (prev: any, node: any, index: any) => { + if (index === 0) return [0]; + prev.push(prev[index - 1] + node); + return prev; + }, + [], + ); return sumLengths.map((eachSum: any, index: any, arr: any) => { const ratio = eachSum / arr[arr.length - 1]; return [nodes[index], eachLengths[index], sumLengths[index], ratio]; @@ -694,23 +808,33 @@ export default class Tin { const anotherLengths = lengths[i ? 0 : 1]; return thisLengths .filter( - (val: any, index: any) => !(index === 0 || index === thisLengths.length - 1 || val[4] === "handled"), + (val: any, index: any) => + !( + index === 0 || + index === thisLengths.length - 1 || + val[4] === "handled" + ), ) .map((lengthItem: any) => { const node = lengthItem[0]; const ratio = lengthItem[3]; - const anotherSets = anotherLengths.reduce((prev: any, item: any, index: any, arr: any) => { - if (prev) return prev; - const next = arr[index + 1]; - if (item[3] === ratio) { - item[4] = "handled"; - return [item]; - } - if (item[3] < ratio && next[3] > ratio) return [item, next]; - return; - }, undefined); + const anotherSets = anotherLengths.reduce( + (prev: any, item: any, index: any, arr: any) => { + if (prev) return prev; + const next = arr[index + 1]; + if (item[3] === ratio) { + item[4] = "handled"; + return [item]; + } + if (item[3] < ratio && next[3] > ratio) return [item, next]; + return; + }, + undefined, + ); if (anotherSets.length === 1) { - return i === 0 ? [node, anotherSets[0][0], ratio] : [anotherSets[0][0], node, ratio]; + return i === 0 + ? [node, anotherSets[0][0], ratio] + : [anotherSets[0][0], node, ratio]; } else { const anotherPrev = anotherSets[0]; const anotherNext = anotherSets[1]; @@ -718,10 +842,14 @@ export default class Tin { const ratioAnother = anotherNext[3] - anotherPrev[3]; const ratioInEdge = ratioDelta / ratioAnother; const anotherNode = [ - (anotherNext[0][0] - anotherPrev[0][0]) * ratioInEdge + anotherPrev[0][0], - (anotherNext[0][1] - anotherPrev[0][1]) * ratioInEdge + anotherPrev[0][1], + (anotherNext[0][0] - anotherPrev[0][0]) * ratioInEdge + + anotherPrev[0][0], + (anotherNext[0][1] - anotherPrev[0][1]) * ratioInEdge + + anotherPrev[0][1], ]; - return i === 0 ? [node, anotherNode, ratio] : [anotherNode, node, ratio]; + return i === 0 + ? [node, anotherNode, ratio] + : [anotherNode, node, ratio]; } }); }) @@ -736,7 +864,10 @@ export default class Tin { if (index === 0) { edges.push([startEnd[0], pointsArray.forw.length - 1]); } else { - edges.push([pointsArray.forw.length - 2, pointsArray.forw.length - 1]); + edges.push([ + pointsArray.forw.length - 2, + pointsArray.forw.length - 1, + ]); } if (index === arr.length - 1) { edges.push([pointsArray.forw.length - 1, startEnd[1]]); @@ -762,14 +893,18 @@ export default class Tin { xy[0] <= this.xy![0] + this.wh![0] && xy[1] >= this.xy![1] && xy[1] <= this.xy![1] + this.wh![1]; - const inside = this.points.reduce((prev: any, curr: any) => prev && insideCheck(curr[0]), true); + const inside = this.points.reduce( + (prev: any, curr: any) => prev && insideCheck(curr[0]), + true, + ); if (!inside) { return new Promise((_resolve, reject) => { reject("SOME POINTS OUTSIDE"); }); } return new Promise((resolve) => { - if (strict != Tin.MODE_STRICT && strict != Tin.MODE_LOOSE) strict = Tin.MODE_AUTO; + if (strict != Tin.MODE_STRICT && strict != Tin.MODE_LOOSE) + strict = Tin.MODE_AUTO; let bbox: any = []; if (this.wh) { bbox = [ @@ -806,7 +941,11 @@ export default class Tin { const forCentroidFt: any = prevResults[2]; const pointsSetBbox: any = prevResults[3]; const pointsSet: any = pointsSetBbox[0]; - if (tinForCentroid.features.length == 0 || tinBakCentroid.features.length == 0) throw "TOO LINEAR1"; + if ( + tinForCentroid.features.length == 0 || + tinBakCentroid.features.length == 0 + ) + throw "TOO LINEAR1"; // Calcurating Forward/Backward Centroid const centroid = { forw: forCentroidFt.geometry.coordinates, @@ -820,7 +959,8 @@ export default class Tin { const convexBuf: any = {}; return Promise.all([ new Promise((resolve) => { - const forConvex = (convex(pointsSet.forw)!.geometry as any).coordinates[0]; + const forConvex = (convex(pointsSet.forw)!.geometry as any) + .coordinates[0]; let vconvex; try { vconvex = forConvex.map((forw: any) => ({ @@ -836,7 +976,8 @@ export default class Tin { resolve(undefined); }), new Promise((resolve) => { - const bakConvex = (convex(pointsSet.bakw)!.geometry as any).coordinates[0]; + const bakConvex = (convex(pointsSet.bakw)!.geometry as any) + .coordinates[0]; let vconvex; try { vconvex = bakConvex.map((bakw: any) => ({ @@ -868,22 +1009,33 @@ export default class Tin { const bakVertex = convexBuf[key].bakw; // Convexhullの各頂点に対し、重心からの差分を取る const vertexDelta = { - forw: [forVertex[0] - centroid.forw[0], forVertex[1] - centroid.forw[1]], + forw: [ + forVertex[0] - centroid.forw[0], + forVertex[1] - centroid.forw[1], + ], }; - (vertexDelta as any).bakw = [bakVertex[0] - centroid.bakw[0], bakVertex[1] - centroid.bakw[1]]; + (vertexDelta as any).bakw = [ + bakVertex[0] - centroid.bakw[0], + bakVertex[1] - centroid.bakw[1], + ]; // X軸方向、Y軸方向それぞれに対し、地図外郭XY座標との重心との比を取る const xRate = vertexDelta.forw[0] == 0 ? Number.POSITIVE_INFINITY - : ((vertexDelta.forw[0] < 0 ? minx : maxx) - centroid.forw[0]) / vertexDelta.forw[0]; + : ((vertexDelta.forw[0] < 0 ? minx : maxx) - centroid.forw[0]) / + vertexDelta.forw[0]; const yRate = vertexDelta.forw[1] == 0 ? Number.POSITIVE_INFINITY - : ((vertexDelta.forw[1] < 0 ? miny : maxy) - centroid.forw[1]) / vertexDelta.forw[1]; + : ((vertexDelta.forw[1] < 0 ? miny : maxy) - centroid.forw[1]) / + vertexDelta.forw[1]; // xRate, yRateが同じ値であれば重心と地図頂点を結ぶ線上に乗る if (Math.abs(xRate) / Math.abs(yRate) < 1.1) { const point = { - forw: [vertexDelta.forw[0] * xRate + centroid.forw[0], vertexDelta.forw[1] * xRate + centroid.forw[1]], + forw: [ + vertexDelta.forw[0] * xRate + centroid.forw[0], + vertexDelta.forw[1] * xRate + centroid.forw[1], + ], bakw: [ (vertexDelta as any).bakw[0] * xRate + centroid.bakw[0], (vertexDelta as any).bakw[1] * xRate + centroid.bakw[1], @@ -894,7 +1046,10 @@ export default class Tin { } if (Math.abs(yRate) / Math.abs(xRate) < 1.1) { const point = { - forw: [vertexDelta.forw[0] * yRate + centroid.forw[0], vertexDelta.forw[1] * yRate + centroid.forw[1]], + forw: [ + vertexDelta.forw[0] * yRate + centroid.forw[0], + vertexDelta.forw[1] * yRate + centroid.forw[1], + ], bakw: [ (vertexDelta as any).bakw[0] * yRate + centroid.bakw[0], (vertexDelta as any).bakw[1] * yRate + centroid.bakw[1], @@ -914,17 +1069,28 @@ export default class Tin { const forVertex = convexBuf[key].forw; const bakVertex = convexBuf[key].bakw; const vertexDelta = { - forw: [forVertex[0] - centroid.forw[0], forVertex[1] - centroid.forw[1]], + forw: [ + forVertex[0] - centroid.forw[0], + forVertex[1] - centroid.forw[1], + ], }; - (vertexDelta as any).bakw = [bakVertex[0] - centroid.bakw[0], centroid.bakw[1] - bakVertex[1]]; - if (vertexDelta.forw[0] == 0 || vertexDelta.forw[1] == 0) return prev; + (vertexDelta as any).bakw = [ + bakVertex[0] - centroid.bakw[0], + centroid.bakw[1] - bakVertex[1], + ]; + if (vertexDelta.forw[0] == 0 || vertexDelta.forw[1] == 0) + return prev; let index = 0; if (vertexDelta.forw[0] > 0) index += 1; if (vertexDelta.forw[1] > 0) index += 2; - (prev[index] as any[]).push([vertexDelta.forw, (vertexDelta as any).bakw]); + (prev[index] as any[]).push([ + vertexDelta.forw, + (vertexDelta as any).bakw, + ]); if (idx == array.length - 1) { // If some orthants have no Convex full polygon's vertices, use same average factor to every orthants - return prev.length == prev.filter((val) => val.length > 0).length && + return prev.length == + prev.filter((val) => val.length > 0).length && this.vertexMode == Tin.VERTEX_BIRDEYE ? prev : prev.reduce((pre, cur) => [pre[0].concat(cur)], [[]]); @@ -935,26 +1101,33 @@ export default class Tin { ) .map((item) => // Finalize calcuration of Average scaling factors and rotation factors - item.reduce((prev: number[] | null, curr: any, index: number, arr: any[]) => { - if (!prev) prev = [Number.POSITIVE_INFINITY, 0, 0]; - // if (!prev) prev = [0, 0, 0]; - // var distanceSum = prev[0] + Math.sqrt(Math.pow(curr[0][0], 2) + Math.pow(curr[0][1], 2)) / - // Math.sqrt(Math.pow(curr[1][0], 2) + Math.pow(curr[1][1], 2)); - let distanceSum = - Math.sqrt(curr[0][0] ** 2 + curr[0][1] ** 2) / Math.sqrt(curr[1][0] ** 2 + curr[1][1] ** 2); - distanceSum = distanceSum < prev[0] ? distanceSum : prev[0]; - const thetaDelta = Math.atan2(curr[0][0], curr[0][1]) - Math.atan2(curr[1][0], curr[1][1]); - const sumThetaX = prev[1] + Math.cos(thetaDelta); - const sumThetaY = prev[2] + Math.sin(thetaDelta); - if (index == arr.length - 1) { - // return [distanceSum / arr.length, Math.atan2(sumThetaY, sumThetaX)]; - return [distanceSum, Math.atan2(sumThetaY, sumThetaX)]; - } - return [distanceSum, sumThetaX, sumThetaY]; - }, null), + item.reduce( + (prev: number[] | null, curr: any, index: number, arr: any[]) => { + if (!prev) prev = [Number.POSITIVE_INFINITY, 0, 0]; + // if (!prev) prev = [0, 0, 0]; + // var distanceSum = prev[0] + Math.sqrt(Math.pow(curr[0][0], 2) + Math.pow(curr[0][1], 2)) / + // Math.sqrt(Math.pow(curr[1][0], 2) + Math.pow(curr[1][1], 2)); + let distanceSum = + Math.sqrt(curr[0][0] ** 2 + curr[0][1] ** 2) / + Math.sqrt(curr[1][0] ** 2 + curr[1][1] ** 2); + distanceSum = distanceSum < prev[0] ? distanceSum : prev[0]; + const thetaDelta = + Math.atan2(curr[0][0], curr[0][1]) - + Math.atan2(curr[1][0], curr[1][1]); + const sumThetaX = prev[1] + Math.cos(thetaDelta); + const sumThetaY = prev[2] + Math.sin(thetaDelta); + if (index == arr.length - 1) { + // return [distanceSum / arr.length, Math.atan2(sumThetaY, sumThetaX)]; + return [distanceSum, Math.atan2(sumThetaY, sumThetaX)]; + } + return [distanceSum, sumThetaX, sumThetaY]; + }, + null, + ), ); // "Using same average factor to every orthants" case - if (orthant.length == 1) orthant = [orthant[0], orthant[0], orthant[0], orthant[0]]; + if (orthant.length == 1) + orthant = [orthant[0], orthant[0], orthant[0], orthant[0]]; return [orthant, centroid, expandConvex, pointsSetBbox]; }) .then((prevResults) => { @@ -966,7 +1139,10 @@ export default class Tin { // Calcurating Backward Bounding box of map let verticesSet = orthant.map((delta: any, index: any) => { const forVertex = bbox[index]; - const forDelta = [forVertex[0] - centroid.forw[0], forVertex[1] - centroid.forw[1]]; + const forDelta = [ + forVertex[0] - centroid.forw[0], + forVertex[1] - centroid.forw[1], + ]; const forDistance = Math.sqrt(forDelta[0] ** 2 + forDelta[1] ** 2); const bakDistance = forDistance / delta[0]; const forTheta = Math.atan2(forDelta[0], forDelta[1]); @@ -989,14 +1165,22 @@ export default class Tin { expands.map((expand: any) => { const expandLine = lineString([centroid.bakw, expand.bakw]); const intersect = lineIntersect(side, expandLine); - if (intersect.features.length > 0 && intersect.features[0].geometry) { + if ( + intersect.features.length > 0 && + intersect.features[0].geometry + ) { const intersect_ = intersect.features[0]; const expandDist = Math.sqrt( - (expand.bakw[0] - centroid.bakw[0]) ** 2 + (expand.bakw[1] - centroid.bakw[1]) ** 2, + (expand.bakw[0] - centroid.bakw[0]) ** 2 + + (expand.bakw[1] - centroid.bakw[1]) ** 2, ); const onSideDist = Math.sqrt( - ((intersect_.geometry as any).coordinates[0] - centroid.bakw[0]) ** 2 + - ((intersect_.geometry as any).coordinates[1] - centroid.bakw[1]) ** 2, + ((intersect_.geometry as any).coordinates[0] - + centroid.bakw[0]) ** + 2 + + ((intersect_.geometry as any).coordinates[1] - + centroid.bakw[1]) ** + 2, ); const rate = expandDist / onSideDist; if (rate > expandRate[i]) expandRate[i] = rate; @@ -1030,7 +1214,9 @@ export default class Tin { } (this as any).pointsSet = pointsSet; this.tins = { - forw: rotateVerticesTriangle(constrainedTin(pointsSet.forw, pointsSet.edges, "target")), + forw: rotateVerticesTriangle( + constrainedTin(pointsSet.forw, pointsSet.edges, "target"), + ), }; let prom; if (strict == Tin.MODE_STRICT || strict == Tin.MODE_AUTO) { @@ -1040,8 +1226,14 @@ export default class Tin { } return prom .then(() => { - if (strict == Tin.MODE_LOOSE || (strict == Tin.MODE_AUTO && this.strict_status == Tin.STATUS_ERROR)) { - this.tins!.bakw = rotateVerticesTriangle(constrainedTin(pointsSet.bakw, pointsSet.edges, "target")); + if ( + strict == Tin.MODE_LOOSE || + (strict == Tin.MODE_AUTO && + this.strict_status == Tin.STATUS_ERROR) + ) { + this.tins!.bakw = rotateVerticesTriangle( + constrainedTin(pointsSet.bakw, pointsSet.edges, "target"), + ); // biome-ignore lint/performance/noDelete: delete this.kinks; this.strict_status = Tin.STATUS_LOOSE; @@ -1073,10 +1265,16 @@ export default class Tin { if (!booleanPointInPolygon(tpoint, this.boundsPolygon!)) return false; } const tins = backward ? this.tins!.bakw : this.tins!.forw; - const indexedTins = backward ? this.indexedTins!.bakw : this.indexedTins!.forw; - const verticesParams = backward ? this.vertices_params!.bakw : this.vertices_params!.forw; + const indexedTins = backward + ? this.indexedTins!.bakw + : this.indexedTins!.forw; + const verticesParams = backward + ? this.vertices_params!.bakw + : this.vertices_params!.forw; const centroid = backward ? this.centroid!.bakw : this.centroid!.forw; - const weightBuffer = backward ? this.pointsWeightBuffer!.bakw : this.pointsWeightBuffer!.forw; + const weightBuffer = backward + ? this.pointsWeightBuffer!.bakw + : this.pointsWeightBuffer!.forw; let stateTriangle = undefined, stateSetFunc = undefined; if (this.stateFull) { @@ -1136,9 +1334,13 @@ export default class Tin { alreadyChecked[key] = 1; const weight = Math.sqrt((toi[0] - toj[0]) ** 2 + (toi[1] - toj[1]) ** 2) / - Math.sqrt((fromi[0] - fromj[0]) ** 2 + (fromi[1] - fromj[1]) ** 2); - if (!weightBuffer[target]![indexi]) weightBuffer[target][indexi] = {}; - if (!weightBuffer[target]![indexj]) weightBuffer[target][indexj] = {}; + Math.sqrt( + (fromi[0] - fromj[0]) ** 2 + (fromi[1] - fromj[1]) ** 2, + ); + if (!weightBuffer[target]![indexi]) + weightBuffer[target][indexi] = {}; + if (!weightBuffer[target]![indexj]) + weightBuffer[target][indexj] = {}; weightBuffer[target]![indexi][key] = weight; weightBuffer[target]![indexj][key] = weight; } @@ -1155,21 +1357,26 @@ export default class Tin { const pointsWeightBuffer: WeightBufferBD = {}; calcTargets.map((target) => { pointsWeightBuffer[target] = {}; - if (this.strict_status == Tin.STATUS_STRICT) pointsWeightBuffer.bakw = {}; + if (this.strict_status == Tin.STATUS_STRICT) + pointsWeightBuffer.bakw = {}; Object.keys(weightBuffer[target]).map((vtx) => { - pointsWeightBuffer[target]![vtx] = Object.keys(weightBuffer[target][vtx]).reduce((prev, key, idx, arr) => { + pointsWeightBuffer[target]![vtx] = Object.keys( + weightBuffer[target][vtx], + ).reduce((prev, key, idx, arr) => { prev = prev + weightBuffer[target][vtx][key]; return idx == arr.length - 1 ? prev / arr.length : prev; }, 0); if (this.strict_status == Tin.STATUS_STRICT) - pointsWeightBuffer.bakw![vtx] = 1 / pointsWeightBuffer[target]![vtx]; + pointsWeightBuffer.bakw![vtx] = + 1 / pointsWeightBuffer[target]![vtx]; }); pointsWeightBuffer[target]!.c = [0, 1, 2, 3].reduce((prev, curr) => { const key = `b${curr}`; prev = prev + pointsWeightBuffer[target]![key]; return curr == 3 ? prev / 4 : prev; }, 0); - if (this.strict_status == Tin.STATUS_STRICT) pointsWeightBuffer.bakw!.c = 1 / pointsWeightBuffer[target]!.c; + if (this.strict_status == Tin.STATUS_STRICT) + pointsWeightBuffer.bakw!.c = 1 / pointsWeightBuffer[target]!.c; }); this.pointsWeightBuffer = pointsWeightBuffer; }) @@ -1259,8 +1466,13 @@ function vertexCalc(list: any, centroid: any) { const itemi = list[i]; const itemj = list[j]; const coord = itemi.geometry.coordinates; - const radian = Math.atan2(coord[0] - centCoord[0], coord[1] - centCoord[1]); - const coordinates = [centroid, itemi, itemj, centroid].map((point) => point.geometry.coordinates); + const radian = Math.atan2( + coord[0] - centCoord[0], + coord[1] - centCoord[1], + ); + const coordinates = [centroid, itemi, itemj, centroid].map( + (point) => point.geometry.coordinates, + ); const properties = { a: { geom: centroid.properties.target.geom, @@ -1361,7 +1573,10 @@ function transformTinArr(of: any, tri: any, weightBuffer: any) { } abv = nabv; } - return [abv * abd[0] + acv * acd[0] + ad[0], abv * abd[1] + acv * acd[1] + ad[1]]; + return [ + abv * abd[0] + acv * acd[0] + ad[0], + abv * abd[1] + acv * acd[1] + ad[1], + ]; } // biome-ignore lint/correctness/noUnusedVariables: @@ -1395,7 +1610,12 @@ function hit(point: Feature, tins: Tins): Tri | undefined { } } } -function unitCalc(coord: number, origin: number, unit: number, gridNum: number) { +function unitCalc( + coord: number, + origin: number, + unit: number, + gridNum: number, +) { let normCoord = Math.floor((coord - origin) / unit); if (normCoord >= gridNum) normCoord = gridNum - 1; return normCoord; @@ -1413,7 +1633,16 @@ function transform( stateSetFunc?: (tri?: Tri) => void, ): Feature { return point( - transformArr(apoint, tins, indexedTins, verticesParams, centroid, weightBuffer, stateTriangle, stateSetFunc), + transformArr( + apoint, + tins, + indexedTins, + verticesParams, + centroid, + weightBuffer, + stateTriangle, + stateSetFunc, + ), ); } function transformArr( @@ -1441,7 +1670,11 @@ function transformArr( const gridCache = indexedTins.gridCache; const normX = unitCalc(coords[0], xOrigin, xUnit, gridNum); const normY = unitCalc(coords[1], yOrigin, yUnit, gridNum); - const tinsKey = gridCache[normX] ? (gridCache[normX][normY] ? gridCache[normX][normY] : []) : []; + const tinsKey = gridCache[normX] + ? gridCache[normX][normY] + ? gridCache[normX][normY] + : [] + : []; tins = featureCollection(tinsKey.map((key: any) => tins.features[key])); } tin = hit(point, tins); @@ -1453,7 +1686,9 @@ function transformArr( useVerticesArr(point, verticesParams!, centroid!, weightBuffer!); } function counterTri(tri: Tri): Tri { - const coordinates = (["a", "b", "c", "a"] as PropertyTriKey[]).map((key) => tri.properties![key].geom); + const coordinates = (["a", "b", "c", "a"] as PropertyTriKey[]).map( + (key) => tri.properties![key].geom, + ); const geoms = tri.geometry!.coordinates[0]; const props = tri.properties!; const properties = { @@ -1481,30 +1716,34 @@ function indexesToTri( bakw = false, version?: number, ): Tri { - const points_: [Position[], string | number][] = indexes.map((index: number | string) => { - if (!version || version < 2.00703) index = normalizeNodeKey(index); - const point_base = Number.isFinite(index as any) - ? points[index as number] - : index === "c" - ? cent - : index === "b0" - ? bboxes[0] - : index === "b1" - ? bboxes[1] - : index === "b2" - ? bboxes[2] - : index === "b3" - ? bboxes[3] - : (() => { - const match = (index as string).match(/e(\d+)/); - if (match) { - const nodeIndex = Number.parseInt(match[1]); - return edgeNodes[nodeIndex]; - } - throw "Bad index value for indexesToTri"; - })(); - return bakw ? [[point_base![1], point_base![0]], index] : [[point_base![0], point_base![1]], index]; - }); + const points_: [Position[], string | number][] = indexes.map( + (index: number | string) => { + if (!version || version < 2.00703) index = normalizeNodeKey(index); + const point_base = Number.isFinite(index as any) + ? points[index as number] + : index === "c" + ? cent + : index === "b0" + ? bboxes[0] + : index === "b1" + ? bboxes[1] + : index === "b2" + ? bboxes[2] + : index === "b3" + ? bboxes[3] + : (() => { + const match = (index as string).match(/e(\d+)/); + if (match) { + const nodeIndex = Number.parseInt(match[1]); + return edgeNodes[nodeIndex]; + } + throw "Bad index value for indexesToTri"; + })(); + return bakw + ? [[point_base![1], point_base![0]], index] + : [[point_base![0], point_base![1]], index]; + }, + ); return buildTri(points_); } @@ -1514,9 +1753,17 @@ function normalizeNodeKey(index: number | string) { return index.replace(/^(c|e|b)(?:ent|dgeNode|box)(\d+)?$/, "$1$2"); } -function normalizeEdges(edges: Edge[] | EdgeLegacy[], version?: number): Edge[] { - if ((version && version >= 2.00703) || Array.isArray(edges[0])) return edges as Edge[]; - return (edges as EdgeLegacy[]).map((edge) => [edge.illstNodes, edge.mercNodes, edge.startEnd]); +function normalizeEdges( + edges: Edge[] | EdgeLegacy[], + version?: number, +): Edge[] { + if ((version && version >= 2.00703) || Array.isArray(edges[0])) + return edges as Edge[]; + return (edges as EdgeLegacy[]).map((edge) => [ + edge.illstNodes, + edge.mercNodes, + edge.startEnd, + ]); } function overlapCheckAsync(searchIndex: SearchIndex) { @@ -1528,8 +1775,15 @@ function overlapCheckAsync(searchIndex: SearchIndex) { const searchResult = searchIndex[key]; if (searchResult.length < 2) return resolve(undefined); (["forw", "bakw"] as BiDirectionKey[]).map((dir) => { - const result = intersect(searchResult[0][dir], searchResult[1][dir]); - if (!result || (result.geometry as any).type == "Point" || (result.geometry as any).type == "LineString") + const result = intersect( + searchResult[0][dir], + searchResult[1][dir], + ); + if ( + !result || + (result.geometry as any).type == "Point" || + (result.geometry as any).type == "LineString" + ) return resolve(undefined); resolve(undefined); }); @@ -1547,11 +1801,17 @@ function overlapCheckAsync(searchIndex: SearchIndex) { throw err; }); } -function insertSearchIndex(searchIndex: SearchIndex, tris: SearchTris, tins?: TinsBD) { +function insertSearchIndex( + searchIndex: SearchIndex, + tris: SearchTris, + tins?: TinsBD, +) { const keys = calcSearchKeys(tris.forw); const bakKeys = calcSearchKeys(tris.bakw); if (JSON.stringify(keys) != JSON.stringify(bakKeys)) - throw `${JSON.stringify(tris, null, 2)}\n${JSON.stringify(keys)}\n${JSON.stringify(bakKeys)}`; + throw `${JSON.stringify(tris, null, 2)}\n${JSON.stringify( + keys, + )}\n${JSON.stringify(bakKeys)}`; for (let i = 0; i < keys.length; i++) { const key = keys[i]; if (!searchIndex[key]) searchIndex[key] = []; @@ -1562,27 +1822,41 @@ function insertSearchIndex(searchIndex: SearchIndex, tris: SearchTris, tins?: Ti tins.bakw!.features.push(tris.bakw); } } -function removeSearchIndex(searchIndex: SearchIndex, tris: SearchTris, tins?: TinsBD) { +function removeSearchIndex( + searchIndex: SearchIndex, + tris: SearchTris, + tins?: TinsBD, +) { const keys = calcSearchKeys(tris.forw); const bakKeys = calcSearchKeys(tris.bakw); if (JSON.stringify(keys) != JSON.stringify(bakKeys)) - throw `${JSON.stringify(tris, null, 2)}\n${JSON.stringify(keys)}\n${JSON.stringify(bakKeys)}`; + throw `${JSON.stringify(tris, null, 2)}\n${JSON.stringify( + keys, + )}\n${JSON.stringify(bakKeys)}`; for (let i = 0; i < keys.length; i++) { const key = keys[i]; - const newArray = searchIndex[key].filter((eachTris: any) => eachTris.forw != tris.forw); + const newArray = searchIndex[key].filter( + (eachTris: any) => eachTris.forw != tris.forw, + ); if (newArray.length == 0) delete searchIndex[key]; else searchIndex[key] = newArray; } if (tins) { - let newArray = tins.forw!.features.filter((eachTri: Tri) => eachTri != tris.forw); + let newArray = tins.forw!.features.filter( + (eachTri: Tri) => eachTri != tris.forw, + ); tins.forw!.features = newArray; - newArray = tins.bakw!.features.filter((eachTri: Tri) => eachTri != tris.bakw); + newArray = tins.bakw!.features.filter( + (eachTri: Tri) => eachTri != tris.bakw, + ); tins.bakw!.features = newArray; } } function calcSearchKeys(tri: Tri): string[] { - const vtx = (["a", "b", "c"] as PropertyTriKey[]).map((key) => tri.properties![key].index); + const vtx = (["a", "b", "c"] as PropertyTriKey[]).map( + (key) => tri.properties![key].index, + ); return [ [0, 1], [0, 2], diff --git a/src/kinks.ts b/src/kinks.ts index 0fd5352..4f7cb32 100644 --- a/src/kinks.ts +++ b/src/kinks.ts @@ -4,12 +4,15 @@ import type { Position } from "@turf/turf"; export default function findIntersections(coords: Position[][]) { const arcs = new ArcCollection(coords); const xy = arcs.findSegmentIntersections(); - return dedupIntersections(xy).reduce((prev: any, apoint: any, index: any, array: any) => { - if (!prev) prev = {}; - prev[`${apoint.x}:${apoint.y}`] = apoint; - if (index !== array.length - 1) return prev; - return Object.keys(prev).map((key) => point([prev[key].x, prev[key].y])); - }, []); + return dedupIntersections(xy).reduce( + (prev: any, apoint: any, index: any, array: any) => { + if (!prev) prev = {}; + prev[`${apoint.x}:${apoint.y}`] = apoint; + if (index !== array.length - 1) return prev; + return Object.keys(prev).map((key) => point([prev[key].x, prev[key].y])); + }, + [], + ); } class ArcCollection { @@ -169,10 +172,12 @@ class ArcCollection { getAvgSegment2() { let dx = 0, dy = 0; - const count = this.forEachSegment((i: number, j: number, xx: number[], yy: number[]) => { - dx += Math.abs(xx[i] - xx[j]); - dy += Math.abs(yy[i] - yy[j]); - }); + const count = this.forEachSegment( + (i: number, j: number, xx: number[], yy: number[]) => { + dx += Math.abs(xx[i] - xx[j]); + dy += Math.abs(yy[i] - yy[j]); + }, + ); return [dx / count || 0, dy / count || 0]; } @@ -192,19 +197,24 @@ class ArcCollection { yrange = bounds.ymax - ymin, stripeCount = this.calcSegmentIntersectionStripeCount(), stripeSizes = new Uint32Array(stripeCount), - stripeId = stripeCount > 1 ? (y: number) => Math.floor(((stripeCount - 1) * (y - ymin)) / yrange) : () => 0; + stripeId = + stripeCount > 1 + ? (y: number) => Math.floor(((stripeCount - 1) * (y - ymin)) / yrange) + : () => 0; let i, j; // Count segments in each stripe - this.forEachSegment((id1: number, id2: number, _xx: number[], yy: number[]) => { - let s1 = stripeId(yy[id1]); - const s2 = stripeId(yy[id2]); - while (true) { - stripeSizes[s1] = stripeSizes[s1] + 2; - if (s1 == s2) break; - s1 += s2 > s1 ? 1 : -1; - } - }); + this.forEachSegment( + (id1: number, id2: number, _xx: number[], yy: number[]) => { + let s1 = stripeId(yy[id1]); + const s2 = stripeId(yy[id2]); + while (true) { + stripeSizes[s1] = stripeSizes[s1] + 2; + if (s1 == s2) break; + s1 += s2 > s1 ? 1 : -1; + } + }, + ); // Allocate arrays for segments in each stripe const stripeData = this.getUint32Array(utilsSum(stripeSizes)); @@ -218,21 +228,23 @@ class ArcCollection { // Assign segment ids to each stripe initializeArray(stripeSizes, 0); - this.forEachSegment((id1: number, id2: number, _xx: number[], yy: number[]) => { - let s1 = stripeId(yy[id1]); - const s2 = stripeId(yy[id2]); - let count; - let stripe; - while (true) { - count = stripeSizes[s1]; - stripeSizes[s1] = count + 2; - stripe = stripes[s1]; - stripe[count] = id1; - stripe[count + 1] = id2; - if (s1 == s2) break; - s1 += s2 > s1 ? 1 : -1; - } - }); + this.forEachSegment( + (id1: number, id2: number, _xx: number[], yy: number[]) => { + let s1 = stripeId(yy[id1]); + const s2 = stripeId(yy[id2]); + let count; + let stripe; + while (true) { + count = stripeSizes[s1]; + stripeSizes[s1] = count + 2; + stripe = stripes[s1]; + stripe[count] = id1; + stripe[count + 1] = id2; + if (s1 == s2) break; + s1 += s2 > s1 ? 1 : -1; + } + }, + ); // Detect intersections among segments in each stripe. const raw = this.getVertexData(), @@ -318,7 +330,23 @@ function initializeArray(arr: any, init: any) { function intersectSegments(ids: any, xx: any, yy: any) { const lim = ids.length - 2, intersections = []; - let s1p1, s1p2, s2p1, s2p2, s1p1x, s1p2x, s2p1x, s2p2x, s1p1y, s1p2y, s2p1y, s2p2y, hit, seg1, seg2, i, j; + let s1p1, + s1p2, + s2p1, + s2p2, + s1p1x, + s1p2x, + s2p1x, + s2p2x, + s1p1y, + s1p2y, + s2p1y, + s2p2y, + hit, + seg1, + seg2, + i, + j; // Sort segments by xmin, to allow efficient exclusion of segments with // non-overlapping x extents. @@ -363,14 +391,25 @@ function intersectSegments(ids: any, xx: any, yy: any) { } // test two candidate segments for intersection - hit = segmentIntersection(s1p1x, s1p1y, s1p2x, s1p2y, s2p1x, s2p1y, s2p2x, s2p2y); + hit = segmentIntersection( + s1p1x, + s1p1y, + s1p2x, + s1p2y, + s2p1x, + s2p1y, + s2p2x, + s2p2y, + ); if (hit) { seg1 = [s1p1, s1p2]; seg2 = [s2p1, s2p2]; intersections.push(formatIntersection(hit, seg1, seg2, xx, yy)); if (hit.length == 4) { // two collinear segments may have two endpoint intersections - intersections.push(formatIntersection(hit.slice(2), seg1, seg2, xx, yy)); + intersections.push( + formatIntersection(hit.slice(2), seg1, seg2, xx, yy), + ); } } } @@ -387,7 +426,16 @@ function intersectSegments(ids: any, xx: any, yy: any) { // If the segments are collinear and partially overlapping, each subsumed endpoint // is counted as an intersection (there will be one or two) // -function segmentIntersection(ax: any, ay: any, bx: any, by: any, cx: any, cy: any, dx: any, dy: any) { +function segmentIntersection( + ax: any, + ay: any, + bx: any, + by: any, + cx: any, + cy: any, + dx: any, + dy: any, +) { const hit = segmentHit(ax, ay, bx, by, cx, cy, dx, dy); let p = null; if (hit) { @@ -404,7 +452,16 @@ function segmentIntersection(ax: any, ay: any, bx: any, by: any, cx: any, cy: an // Source: Sedgewick, _Algorithms in C_ // (Tried various other functions that failed owing to floating point errors) -function segmentHit(ax: any, ay: any, bx: any, by: any, cx: any, cy: any, dx: any, dy: any) { +function segmentHit( + ax: any, + ay: any, + bx: any, + by: any, + cx: any, + cy: any, + dx: any, + dy: any, +) { return ( orient2D(ax, ay, bx, by, cx, cy) * orient2D(ax, ay, bx, by, dx, dy) <= 0 && orient2D(cx, cy, dx, dy, ax, ay) * orient2D(cx, cy, dx, dy, bx, by) <= 0 @@ -428,7 +485,16 @@ function determinant2D(a: any, b: any, c: any, d: any) { // Get intersection point if segments are non-collinear, else return null // Assumes that segments have been intersect -function crossIntersection(ax: any, ay: any, bx: any, by: any, cx: any, cy: any, dx: any, dy: any) { +function crossIntersection( + ax: any, + ay: any, + bx: any, + by: any, + cx: any, + cy: any, + dx: any, + dy: any, +) { let p = lineIntersection(ax, ay, bx, by, cx, cy, dx, dy); let nearest; if (p) { @@ -449,7 +515,16 @@ function crossIntersection(ax: any, ay: any, bx: any, by: any, cx: any, cy: any, return p; } -function lineIntersection(ax: any, ay: any, bx: any, by: any, cx: any, cy: any, dx: any, dy: any) { +function lineIntersection( + ax: any, + ay: any, + bx: any, + by: any, + cx: any, + cy: any, + dx: any, + dy: any, +) { const den = determinant2D(bx - ax, by - ay, dx - cx, dy - cy); const eps = 1e-18; let p; @@ -468,7 +543,16 @@ function lineIntersection(ax: any, ay: any, bx: any, by: any, cx: any, cy: any, return p; } -function findEndpointInRange(ax: any, ay: any, bx: any, by: any, cx: any, cy: any, dx: any, dy: any) { +function findEndpointInRange( + ax: any, + ay: any, + bx: any, + by: any, + cx: any, + cy: any, + dx: any, + dy: any, +) { let p = null; if (!outsideRange(ax, cx, dx) && !outsideRange(ay, cy, dy)) { p = [ax, ay]; @@ -518,7 +602,17 @@ function distanceSq(ax: any, ay: any, bx: any, by: any) { return dx * dx + dy * dy; } -function clampIntersectionPoint(p: any, ax: any, ay: any, bx: any, by: any, cx: any, cy: any, dx: any, dy: any) { +function clampIntersectionPoint( + p: any, + ax: any, + ay: any, + bx: any, + by: any, + cx: any, + cy: any, + dx: any, + dy: any, +) { // Handle intersection points that fall outside the x-y range of either // segment by snapping to nearest endpoint coordinate. Out-of-range // intersection points can be caused by floating point rounding errors @@ -548,7 +642,16 @@ function clampToCloseRange(a: any, b: any, c: any) { } // Assume segments s1 and s2 are collinear and overlap; find one or two internal endpoints -function collinearIntersection(ax: any, ay: any, bx: any, by: any, cx: any, cy: any, dx: any, dy: any) { +function collinearIntersection( + ax: any, + ay: any, + bx: any, + by: any, + cx: any, + cy: any, + dx: any, + dy: any, +) { const minX = Math.min(ax, bx, cx, dx), maxX = Math.max(ax, bx, cx, dx), minY = Math.min(ay, by, cy, dy), @@ -571,15 +674,33 @@ function collinearIntersection(ax: any, ay: any, bx: any, by: any, cx: any, cy: if (coords.length !== 2 && coords.length !== 4) { coords = null; //debug("Invalid collinear segment intersection", coords); - } else if (coords.length === 4 && coords[0] === coords[2] && coords[1] === coords[3]) { + } else if ( + coords.length === 4 && + coords[0] === coords[2] && + coords[1] === coords[3] + ) { // segs that meet in the middle don't count coords = null; } return coords; } -function endpointHit(ax: any, ay: any, bx: any, by: any, cx: any, cy: any, dx: any, dy: any) { - return (ax == cx && ay == cy) || (ax == dx && ay == dy) || (bx == cx && by == cy) || (bx == dx && by == dy); +function endpointHit( + ax: any, + ay: any, + bx: any, + by: any, + cx: any, + cy: any, + dx: any, + dy: any, +) { + return ( + (ax == cx && ay == cy) || + (ax == dx && ay == dy) || + (bx == cx && by == cy) || + (bx == dx && by == dy) + ); } function inside(x: any, minX: any, maxX: any) { @@ -666,7 +787,14 @@ function formatIntersection(xy: any, s1: any, s2: any, xx: any, yy: any) { return { x, y, a, b }; } -function formatIntersectingSegment(x: any, y: any, id1: any, id2: any, xx: any, yy: any) { +function formatIntersectingSegment( + x: any, + y: any, + id1: any, + id2: any, + xx: any, + yy: any, +) { let i = id1 < id2 ? id1 : id2, j = i === id1 ? id2 : id1; if (xx[i] == x && yy[i] == y) { @@ -713,10 +841,20 @@ class ArcIter { } } -function calcArcBounds(xx: Float64Array, yy: Float64Array, start: number, len: number) { +function calcArcBounds( + xx: Float64Array, + yy: Float64Array, + start: number, + len: number, +) { let i = start | 0; const n = Number.isNaN(len) ? xx.length - i : len + i; - let x: number, y: number, xmin: number, ymin: number, xmax: number, ymax: number; + let x: number, + y: number, + xmin: number, + ymin: number, + xmax: number, + ymax: number; if (n > 0) { xmin = xmax = xx[i]; ymin = ymax = yy[i];