@eturnity/eturnity_maths
Advanced tools
Comparing version 7.42.0 to 7.42.1-EPDM-11570
{ | ||
"name": "@eturnity/eturnity_maths", | ||
"version": "7.42.0", | ||
"version": "7.42.1-EPDM-11570", | ||
"author": "Eturnity Team", | ||
@@ -19,2 +19,3 @@ "main": "src/index.js", | ||
"dependencies": { | ||
"cdt2d": "1.0.0", | ||
"lodash": "4.17.21", | ||
@@ -21,0 +22,0 @@ "planar-face-discovery": "2.0.7", |
@@ -29,3 +29,3 @@ import { earthRadius } from './config' | ||
x: xTile, | ||
y: yTile | ||
y: yTile, | ||
} | ||
@@ -52,3 +52,3 @@ } | ||
y: y_mm, | ||
z: tileAltitude | ||
z: tileAltitude, | ||
} | ||
@@ -58,3 +58,3 @@ let corner1 = { | ||
y: y_mm, | ||
z: tileAltitude | ||
z: tileAltitude, | ||
} | ||
@@ -64,3 +64,3 @@ let corner2 = { | ||
y: y_mm - size, | ||
z: tileAltitude | ||
z: tileAltitude, | ||
} | ||
@@ -70,3 +70,3 @@ let corner3 = { | ||
y: y_mm - size, | ||
z: tileAltitude | ||
z: tileAltitude, | ||
} | ||
@@ -84,2 +84,11 @@ return [corner3, corner0, corner1, corner2] | ||
} | ||
export function cartesianToLatLng(point, origin) { | ||
const deltaLatLng = distanceToDeltaLatLng(point.x, point.y, origin.latitude) | ||
return { | ||
latitude: origin.latitude + deltaLatLng.lat, | ||
longitude: origin.longitude + deltaLatLng.lng, | ||
} | ||
} | ||
export function deltaLatLngToDistance(deltaLat, deltaLng, latitude) { | ||
@@ -86,0 +95,0 @@ //x,y,z in ENU coords |
@@ -16,2 +16,4 @@ import { mmTolerance, polygonCloseTolerance } from './config' | ||
import concaveman from './lib/concaveman' | ||
import cdt2d from 'cdt2d' | ||
import { isSelfIntersecting } from './intersectionPolygon' | ||
@@ -297,2 +299,3 @@ export function getConcaveOutlines(selectedPanels, onePanelOutline) { | ||
} | ||
export function get2DBoundOfPolygon(vs) { | ||
@@ -313,51 +316,86 @@ const bound = { | ||
} | ||
export function getPointInsideOutline(vs, holes = []) { | ||
//draw an horizontal line between top and bottom | ||
const bound = get2DBoundOfPolygon(vs) | ||
const y = (bound.yMax + bound.yMin) / 2 | ||
let xOfCrossingEdgeY = [] | ||
for (let index = 0; index < vs.length; index++) { | ||
let nextIndex = (index + 1) % vs.length | ||
const A = vs[index] | ||
const B = vs[nextIndex] | ||
if ((A.y <= y && B.y > y) || (A.y >= y && B.y < y)) { | ||
let x = A.x + (B.x - A.x) * ((y - A.y) / (B.y - A.y)) | ||
xOfCrossingEdgeY.push(x) | ||
} | ||
export function getTriangleCentroid(A, B, C) { | ||
const x = (A.x + B.x + C.x) / 3 | ||
const y = (A.y + B.y + C.y) / 3 | ||
const z = (A.z + B.z + C.z) / 3 | ||
return { x, y, z } | ||
} | ||
/** | ||
* Gets a point inside the outline and outside all the holes | ||
* @param {Array<Point>} vs - outline | ||
* @param {Array<Outline>} firstLevelHoles - Exclude siblings and holes inside other holes | ||
* @returns {Point} | ||
*/ | ||
export function getPointInsideOutline(vs, firstLevelHoles = []) { | ||
firstLevelHoles = firstLevelHoles.filter((hole) => | ||
isPolygonInsidePolygon(hole, vs) | ||
) | ||
const isSelfIntersectingPolygon = isSelfIntersecting(vs, true) | ||
let result | ||
let points = [] | ||
let edges = [] | ||
// main outline | ||
for (let i = 0; i < vs.length; i++) { | ||
points.push(vs[i]) | ||
edges.push([vs[i], vs[(i + 1) % vs.length]]) | ||
} | ||
if (xOfCrossingEdgeY.length < 2) { | ||
console.error('not enaugh edge crossing mid cut', vs) | ||
return | ||
} | ||
xOfCrossingEdgeY.sort((a, b) => a - b) | ||
let xLeft = xOfCrossingEdgeY[0] | ||
let xRight = xOfCrossingEdgeY[1] | ||
//dichotomy to get point outside of holes | ||
for (let hole of holes) { | ||
if (isPolygonInsidePolygon(vs, hole)) { | ||
return { x: (xLeft + xRight) / 2, y } | ||
// holes | ||
firstLevelHoles.forEach((holeOutline) => { | ||
for (let i = 0; i < holeOutline.length; i++) { | ||
points.push(holeOutline[i]) | ||
edges.push([holeOutline[i], holeOutline[(i + 1) % holeOutline.length]]) | ||
} | ||
} | ||
let t = 0.1 | ||
let count = 0 | ||
let isInside = false | ||
let testPoint | ||
while (count < 10 && isInside == false) { | ||
count++ | ||
let x = xLeft + t * (xRight - xLeft) | ||
testPoint = { x, y } | ||
isInside = true | ||
for (let hole of holes) { | ||
if (isInsidePolygon(testPoint, hole)) { | ||
isInside = false | ||
break | ||
} | ||
}) | ||
// clean up duplicate nodes and map edges | ||
const pointsClean = [] | ||
points.forEach((point) => { | ||
const existingNode = pointsClean.find((p) => isSamePoint2D(p, point)) | ||
if (!existingNode) { | ||
pointsClean.push(point) | ||
} | ||
if (isInside) { | ||
return testPoint | ||
} else { | ||
t = (Math.cos((count * Math.PI) / 11) + 1) / 2 | ||
}) | ||
edges = edges.map((edge) => { | ||
return [ | ||
pointsClean.findIndex((point) => isSamePoint2D(edge[0], point)), | ||
pointsClean.findIndex((point) => isSamePoint2D(edge[1], point)), | ||
] | ||
}) | ||
const triangleIndices = cdt2d( | ||
pointsClean.map((point) => [point.x, point.y]), | ||
isSelfIntersectingPolygon ? [] : edges, | ||
{ exterior: isSelfIntersectingPolygon } | ||
) | ||
for (let i = 0; i < triangleIndices.length; i++) { | ||
const triangle = triangleIndices[i] | ||
const triangleOutline = [ | ||
points[triangle[0]], | ||
points[triangle[1]], | ||
points[triangle[2]], | ||
] | ||
const centroid = getTriangleCentroid(...triangleOutline) | ||
const triangleArea = calculateArea( | ||
triangleOutline.map((p) => ({ x: p.x, y: p.y, z: 0 })) | ||
) | ||
// avoid small triangles which can lead to points on edges | ||
if (isInsidePolygon(centroid, vs) && triangleArea > 1000) { | ||
result = centroid | ||
break | ||
} else if ( | ||
isInsidePolygon(centroid, vs) && | ||
triangleArea <= 1000 && | ||
!result | ||
) { | ||
// only register small triangle centroid if there is no result yet | ||
// continue the loop to find a better point | ||
result = centroid | ||
} | ||
} | ||
return testPoint | ||
return result | ||
} | ||
@@ -395,3 +433,7 @@ | ||
let result = false | ||
if (Math.abs(angle - 180) < 1 || isSamePoint2D(A, M) || isSamePoint2D(B, M)) { | ||
if ( | ||
Math.abs(angle - 180) < 0.001 || | ||
isSamePoint2D(A, M) || | ||
isSamePoint2D(B, M) | ||
) { | ||
result = true | ||
@@ -809,15 +851,15 @@ } | ||
visited.push(currentObjectIndex) | ||
let x = objects[currentObjectIndex].index[0] | ||
let y = objects[currentObjectIndex].index[1] | ||
let x = objects[currentObjectIndex].row_index | ||
let y = objects[currentObjectIndex].col_index | ||
const left = objects.findIndex( | ||
(o) => o.index[0] == x - 1 && o.index[1] == y | ||
(o) => o.row_index == x - 1 && o.col_index == y | ||
) | ||
const right = objects.findIndex( | ||
(o) => o.index[0] == x + 1 && o.index[1] == y | ||
(o) => o.row_index == x + 1 && o.col_index == y | ||
) | ||
const top = objects.findIndex( | ||
(o) => o.index[0] == x && o.index[1] == y - 1 | ||
(o) => o.row_index == x && o.col_index == y - 1 | ||
) | ||
const bottom = objects.findIndex( | ||
(o) => o.index[0] == x && o.index[1] == y + 1 | ||
(o) => o.row_index == x && o.col_index == y + 1 | ||
) | ||
@@ -849,15 +891,15 @@ if (left != -1 && visited.indexOf(left) == -1) queue.push(left) | ||
visited.push(currentObjectIndex) | ||
let x = objects[currentObjectIndex].index[0] | ||
let y = objects[currentObjectIndex].index[1] | ||
let x = objects[currentObjectIndex].row_index | ||
let y = objects[currentObjectIndex].col_index | ||
const left = objects.findIndex( | ||
(o) => o.index[0] == x - 1 && o.index[1] == y | ||
(o) => o.row_index == x - 1 && o.col_index == y | ||
) | ||
const right = objects.findIndex( | ||
(o) => o.index[0] == x + 1 && o.index[1] == y | ||
(o) => o.row_index == x + 1 && o.col_index == y | ||
) | ||
const top = objects.findIndex( | ||
(o) => o.index[0] == x && o.index[1] == y - 1 | ||
(o) => o.row_index == x && o.col_index == y - 1 | ||
) | ||
const bottom = objects.findIndex( | ||
(o) => o.index[0] == x && o.index[1] == y + 1 | ||
(o) => o.row_index == x && o.col_index == y + 1 | ||
) | ||
@@ -864,0 +906,0 @@ if (left != -1 && visited.indexOf(left) == -1) queue.push(left) |
@@ -136,7 +136,8 @@ import { | ||
function filterPolygons(polygons, fig1, fig2, mode) { | ||
var filtered = [] | ||
var c1, c2 | ||
var point | ||
var bigPolygons = removeSmallPolygons(polygons, 0.0001) | ||
function filterPolygons(outlineList, fig1, fig2, mode) { | ||
const polygons = outlineList.map(({ outline }) => outline) | ||
const filtered = [] | ||
let c1, c2 | ||
let point | ||
const bigPolygons = removeSmallPolygons(polygons, 0.0001) | ||
for (var i = 0; i < bigPolygons.length; i++) { | ||
@@ -147,2 +148,5 @@ point = getPointInsideOutline( | ||
) | ||
if (!point) { | ||
point = getPointInsideOutline(bigPolygons[i]) | ||
} | ||
c1 = isInsidePolygon(point, fig1) | ||
@@ -149,0 +153,0 @@ c2 = isInsidePolygon(point, fig2) |
@@ -19,6 +19,8 @@ import { Polygon } from './Polygon' | ||
} else if (layer == 'panel') { | ||
polygon.index = serializedPolygon.index | ||
polygon.row_index = serializedPolygon.row_index | ||
polygon.col_index = serializedPolygon.col_index | ||
polygon.moduleField = serializedPolygon.moduleField | ||
} else if (layer == 'user_deactivated_panel') { | ||
polygon.index = serializedPolygon.index | ||
polygon.row_index = serializedPolygon.row_index | ||
polygon.col_index = serializedPolygon.col_index | ||
polygon.moduleField = serializedPolygon.moduleField | ||
@@ -25,0 +27,0 @@ } |
@@ -41,2 +41,4 @@ import { translate2D, getDegree } from '../geometry' | ||
this.isParallel = true | ||
} else if (this.layer == 'roof') { | ||
this.moduleFields = [] | ||
} | ||
@@ -228,25 +230,27 @@ } | ||
} else if (this.layer == 'moduleField') { | ||
const modules = [] | ||
if (this.panels) { | ||
this.panels.forEach((p) => { | ||
modules.push({ | ||
id: p.id, | ||
index: p.index, | ||
outline: p.outline, | ||
status: p.status || 'active', | ||
clipped: p.clipped || false, | ||
}) | ||
}) | ||
} | ||
if (this.userDeactivatedPanels) { | ||
this.userDeactivatedPanels.forEach((p) => { | ||
modules.push({ | ||
id: p.id, | ||
index: p.index, | ||
outline: p.outline, | ||
status: p.status || 'user_deactivated', | ||
clipped: p.clipped || false, | ||
}) | ||
}) | ||
} | ||
// const modules = [] | ||
// if (this.panels) { | ||
// this.panels.forEach((p) => { | ||
// modules.push({ | ||
// id: p.id, | ||
// row_index: p.row_index, | ||
// col_index: p.col_index, | ||
// outline: p.outline, | ||
// status: p.status || 'active', | ||
// clipped: p.clipped || false, | ||
// }) | ||
// }) | ||
// } | ||
// if (this.userDeactivatedPanels) { | ||
// this.userDeactivatedPanels.forEach((p) => { | ||
// modules.push({ | ||
// id: p.id, | ||
// row_index: p.row_index, | ||
// col_index: p.col_index, | ||
// outline: p.outline, | ||
// status: p.status || 'user_deactivated', | ||
// clipped: p.clipped || false, | ||
// }) | ||
// }) | ||
// } | ||
extraSerialization.data = { | ||
@@ -261,10 +265,12 @@ ...this.data, | ||
: null | ||
extraSerialization.modules = modules | ||
// extraSerialization.modules = modules // needed? | ||
extraSerialization.needsOptimisation = this.needsOptimisation | ||
extraSerialization.priority = this.priority | ||
} else if (this.layer == 'panel') { | ||
extraSerialization.index = this.index | ||
extraSerialization.row_index = this.row_index | ||
extraSerialization.col_index = this.col_index | ||
extraSerialization.moduleField = { id: this.moduleField.id } | ||
} else if (this.layer == 'user_deactivated_panel') { | ||
extraSerialization.index = this.index | ||
extraSerialization.row_index = this.row_index | ||
extraSerialization.col_index = this.col_index | ||
extraSerialization.moduleField = { id: this.moduleField.id } | ||
@@ -339,3 +345,4 @@ } | ||
id: this.id, | ||
index: this.index, | ||
row_index: this.row_index, | ||
col_index: this.col_index, | ||
outline: this.outline.map((v) => [v.x, v.y, v.z]), | ||
@@ -342,0 +349,0 @@ moduleField: { |
@@ -18,2 +18,3 @@ import { PlanarFaceTree } from 'planar-face-discovery' | ||
calculateArea, | ||
getPointInsideOutline, | ||
} from './geometry' | ||
@@ -54,3 +55,5 @@ import { intersectOutlines } from './intersectionPolygon' | ||
const edgeList = getEdgeListSimple(nodeList, newEdges) | ||
const outlineList = getOutlineList(nodeList, edgeList) | ||
const outlineList = getOutlineList(nodeList, edgeList).map( | ||
({ outline }) => outline | ||
) | ||
@@ -113,3 +116,3 @@ const polygonList = [] | ||
const newPolygons = [] | ||
outlines.forEach((outline) => { | ||
outlines.forEach(({ outline, hitPoint }) => { | ||
// check if outline has points that are intersections to determine if it is split/untouched | ||
@@ -134,17 +137,4 @@ const outlineIntersections = [] | ||
const candidate = polygons[i] | ||
// check if all points are within the candidate | ||
let pointsInsidePolygon = 0 | ||
for (let j = 0; j < outline.length; j++) { | ||
const point = outline[j] | ||
const isInsideCandidate = | ||
isInsidePolygon(point, candidate.outline) || | ||
candidate.outline.findIndex((n) => | ||
isAlmostSamePoint2D(point, n, 50) | ||
) >= 0 | ||
if (!isInsideCandidate) { | ||
break | ||
} | ||
pointsInsidePolygon++ | ||
} | ||
if (pointsInsidePolygon === outline.length) { | ||
const isInsideCandidate = isInsidePolygon(hitPoint, candidate.outline) | ||
if (isInsideCandidate) { | ||
// add to original polygon candidate list if all points are inside | ||
@@ -331,3 +321,5 @@ originalPolygonCanditates.push(candidate) | ||
//4. run planar-face-discovery to get all cycles and rebuild our polygons | ||
const outlineList = getOutlineList(nodeList, edgeList, roofs) | ||
const outlineList = getOutlineList(nodeList, edgeList, roofs).map( | ||
({ outline }) => outline | ||
) | ||
const polygonList = [] | ||
@@ -787,10 +779,20 @@ for (let k = 0; k < outlineList.length; k++) { | ||
if (cycleTree.cycle.length) { | ||
cycles.push(cycleTree.cycle) | ||
cycles.push(cycleTree) | ||
} | ||
return cycles | ||
} else { | ||
return [cycleTree.cycle] | ||
return [cycleTree] | ||
} | ||
} | ||
function getOutlineFromCycle(cycle, points) { | ||
return cycle.map((i) => { | ||
return { | ||
x: points[i].x, | ||
y: points[i].y, | ||
z: points[i].z, | ||
} | ||
}) | ||
} | ||
export function getOutlineList(nodeList, edgeList, roofs = []) { | ||
@@ -813,15 +815,19 @@ const mode = 'outline' | ||
let outlines = cycles | ||
.map((cycle) => { | ||
return cycle.map((index) => { | ||
return { | ||
x: nodeList[index].x, | ||
y: nodeList[index].y, | ||
z: nodeList[index].z, | ||
} | ||
.map(({ cycle, children }) => { | ||
cycle.pop() | ||
const outline = getOutlineFromCycle(cycle, nodeList) | ||
const holes = children.map(({ cycle: holeCycle }) => { | ||
holeCycle.pop() | ||
return getOutlineFromCycle(holeCycle, nodeList) | ||
}) | ||
const hitPoint = getPointInsideOutline(outline, holes) | ||
return { | ||
outline, | ||
hitPoint, | ||
} | ||
}) | ||
.filter((outline) => outline.length > 0) | ||
outlines.forEach((outline) => outline.pop()) | ||
//check for existing roofs(for z value) | ||
outlines = outlines.map((outline) => { | ||
.filter(({ outline }) => outline.length > 0) | ||
// check for existing roofs(for z value) | ||
outlines = outlines.map(({ outline, hitPoint }) => { | ||
let roof = roofs.find((roof) => | ||
@@ -833,3 +839,3 @@ polygonsHaveSame2DOutline(outline, roof.outline) | ||
} | ||
return outline | ||
return { outline, hitPoint } | ||
}) | ||
@@ -836,0 +842,0 @@ return outlines |
@@ -26,4 +26,4 @@ import { getPointInsideOutline, isStrictlyInsidePolygon } from '../../index' | ||
const D = { x: 0, y: 10, z: 0 } | ||
const E = { x: 0, y: 3, z: 0 } | ||
const F = { x: 0, y: 6, z: 0 } | ||
const E = { x: 1, y: 3, z: 0 } | ||
const F = { x: 1, y: 6, z: 0 } | ||
const G = { x: 6, y: 9, z: 0 } | ||
@@ -30,0 +30,0 @@ const H = { x: 6, y: 6, z: 0 } |
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
192661
6423
9
2
+ Addedcdt2d@1.0.0
+ Addedbinary-search-bounds@2.0.5(transitive)
+ Addedcdt2d@1.0.0(transitive)
+ Addedrobust-in-sphere@1.2.1(transitive)
+ Addedrobust-orientation@1.2.1(transitive)
+ Addedrobust-scale@1.0.2(transitive)
+ Addedrobust-subtract@1.0.0(transitive)
+ Addedrobust-sum@1.0.0(transitive)
+ Addedtwo-product@1.0.2(transitive)
+ Addedtwo-sum@1.0.0(transitive)