mirror of
https://github.com/Doodle3D/Doodle3D-Slicer.git
synced 2024-11-21 21:27:56 +01:00
update combing
This commit is contained in:
parent
86eed64255
commit
7dceeda291
104
comb.js
104
comb.js
@ -1,9 +1,9 @@
|
|||||||
import { pointIsInsideConvex, decompose, findClosestPath, containLineInPath } from './src/sliceActions/helpers/comb.js';
|
import comb from './src/sliceActions/helpers/comb.js';
|
||||||
|
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
document.body.appendChild(canvas);
|
document.body.appendChild(canvas);
|
||||||
canvas.width = 610;
|
canvas.width = 800;
|
||||||
canvas.height = 610;
|
canvas.height = 800;
|
||||||
const context = canvas.getContext('2d');
|
const context = canvas.getContext('2d');
|
||||||
context.lineJoin = 'bevel';
|
context.lineJoin = 'bevel';
|
||||||
|
|
||||||
@ -21,27 +21,28 @@ function circle(radius = 10, x = 0, y = 0, clockWise = true, segments = 40) {
|
|||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
const START = { x: 30, y: 550 };
|
const START = { x: 200, y: 400 };
|
||||||
const END = { x: 400, y: 300 };
|
const END = { x: 400, y: 300 };
|
||||||
// const CONCAVE_POLYGON = [[
|
|
||||||
// { x: 10, y: 10 },
|
const POLYGON = [[
|
||||||
// { x: 600, y: 10 },
|
{ x: 10, y: 10 },
|
||||||
// { x: 500, y: 200 },
|
{ x: 600, y: 10 },
|
||||||
// { x: 600, y: 600 },
|
{ x: 500, y: 200 },
|
||||||
// { x: 10, y: 600 }
|
{ x: 600, y: 600 },
|
||||||
// ], [
|
{ x: 10, y: 600 }
|
||||||
// { x: 160, y: 120 },
|
], [
|
||||||
// { x: 120, y: 400 },
|
{ x: 160, y: 120 },
|
||||||
// { x: 400, y: 400 }
|
{ x: 120, y: 400 },
|
||||||
// ]];
|
{ x: 400, y: 400 }
|
||||||
const CONCAVE_POLYGON = [
|
]];
|
||||||
circle(300, 305, 305, true),
|
// const POLYGON = [
|
||||||
circle(40, 305, 105, false),
|
// circle(300, 305, 305, true, 4),
|
||||||
circle(40, 305, 205, false),
|
// circle(40, 305, 105, false, 4),
|
||||||
circle(40, 305, 305, false),
|
// circle(40, 305, 205, false, 4),
|
||||||
circle(40, 305, 405, false),
|
// circle(40, 305, 305, false, 4),
|
||||||
circle(40, 305, 505, false)
|
// circle(40, 305, 405, false, 4),
|
||||||
];
|
// circle(40, 305, 505, false, 4)
|
||||||
|
// ];
|
||||||
|
|
||||||
canvas.onmousedown = (event) => {
|
canvas.onmousedown = (event) => {
|
||||||
START.x = event.offsetX;
|
START.x = event.offsetX;
|
||||||
@ -56,20 +57,13 @@ canvas.onmousemove = (event) => {
|
|||||||
compute();
|
compute();
|
||||||
|
|
||||||
function compute() {
|
function compute() {
|
||||||
const { convexPolygons, vertices } = decompose(CONCAVE_POLYGON);
|
const path = comb(POLYGON, START, END);
|
||||||
const startPolygon = convexPolygons.findIndex(({ face }) => pointIsInsideConvex(START, face, vertices));
|
|
||||||
const endPolygon = convexPolygons.findIndex(({ face }) => pointIsInsideConvex(END, face, vertices));
|
|
||||||
if (startPolygon === -1 || endPolygon === -1) return;
|
|
||||||
|
|
||||||
const path = findClosestPath(convexPolygons, startPolygon, endPolygon);
|
|
||||||
if (!path) return;
|
|
||||||
const line = containLineInPath(path, START, END, vertices);
|
|
||||||
|
|
||||||
// draw
|
// draw
|
||||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
for (const shape of CONCAVE_POLYGON) {
|
for (const shape of POLYGON) {
|
||||||
let first = true;
|
let first = true;
|
||||||
for (const { x, y } of shape) {
|
for (const { x, y } of shape) {
|
||||||
if (first) {
|
if (first) {
|
||||||
@ -84,48 +78,12 @@ function compute() {
|
|||||||
context.fillStyle = 'lightgray';
|
context.fillStyle = 'lightgray';
|
||||||
context.fill();
|
context.fill();
|
||||||
|
|
||||||
context.fillStyle = 'black';
|
context.beginPath();
|
||||||
context.strokeStyle = 'black';
|
for (const { x, y } of path) {
|
||||||
context.textAlign = 'center';
|
context.lineTo(x, y);
|
||||||
context.textBaseline = 'middle';
|
|
||||||
context.lineWidth = 1;
|
|
||||||
context.font = '14px arial';
|
|
||||||
for (let i = 0; i < convexPolygons.length; i ++) {
|
|
||||||
const { face, center } = convexPolygons[i];
|
|
||||||
|
|
||||||
context.beginPath();
|
|
||||||
for (const index of face) {
|
|
||||||
const vertex = vertices[index];
|
|
||||||
context.lineTo(vertex.x, vertex.y);
|
|
||||||
}
|
|
||||||
context.closePath();
|
|
||||||
context.stroke();
|
|
||||||
|
|
||||||
context.fillText(i, center.x, center.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path) {
|
|
||||||
context.beginPath();
|
|
||||||
for (const { edge: [indexA, indexB] } of path) {
|
|
||||||
const pointA = vertices[indexA];
|
|
||||||
const pointB = vertices[indexB];
|
|
||||||
context.moveTo(pointA.x, pointA.y);
|
|
||||||
context.lineTo(pointB.x, pointB.y);
|
|
||||||
}
|
|
||||||
context.strokeStyle = 'blue';
|
|
||||||
context.lineWidth = 3;
|
|
||||||
context.stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line) {
|
|
||||||
context.beginPath();
|
|
||||||
for (const point of line) {
|
|
||||||
context.lineTo(point.x, point.y);
|
|
||||||
}
|
|
||||||
context.strokeStyle = 'green';
|
|
||||||
context.lineWidth = 2;
|
|
||||||
context.stroke();
|
|
||||||
}
|
}
|
||||||
|
context.lineWidth = 2;
|
||||||
|
context.stroke();
|
||||||
|
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.arc(START.x, START.y, 3, 0, Math.PI * 2);
|
context.arc(START.x, START.y, 3, 0, Math.PI * 2);
|
||||||
|
@ -1,228 +1,195 @@
|
|||||||
import { subtract, add, normalize, dot, distanceTo, divide, normal } from './vector2.js';
|
import { angle, subtract, distanceTo, normal } from './vector2.js';
|
||||||
import earcut from 'earcut';
|
|
||||||
|
|
||||||
const TRIANGULATED_OUTLINES = new WeakMap();
|
const graphs = new WeakMap();
|
||||||
export default function comb(outline, start, end) {
|
export default function comb(polygons, start, end) {
|
||||||
if (distanceTo(start, end) < 3) return [start, end];
|
if (!graphs.has(polygons)) graphs.set(polygons, createGraph(polygons));
|
||||||
|
let { edges, graph, points, normals } = graphs.get(polygons);
|
||||||
|
|
||||||
if (!TRIANGULATED_OUTLINES.has(outline)) TRIANGULATED_OUTLINES.set(outline, decompose(outline));
|
points = [...points, start, end];
|
||||||
const { convexPolygons, vertices } = TRIANGULATED_OUTLINES.get(outline);
|
graph = [...graph];
|
||||||
|
|
||||||
const startPolygon = convexPolygons.findIndex(({ face }) => pointIsInsideConvex(start, face, vertices));
|
const startNode = createNode(graph, points, edges, normals, start);
|
||||||
const endPolygon = convexPolygons.findIndex(({ face }) => pointIsInsideConvex(end, face, vertices));
|
const endNode = createNode(graph, points, edges, normals, end);
|
||||||
if (startPolygon === -1 || endPolygon === -1) return [start, end];
|
|
||||||
if (startPolygon === endPolygon) return [start, end];
|
|
||||||
|
|
||||||
const path = findClosestPath(convexPolygons, startPolygon, endPolygon);
|
let result;
|
||||||
if (!path) return [start, end];
|
if (graph[startNode].some(node => node.to === endNode)) {
|
||||||
|
result = [start, end];
|
||||||
const line = containLineInPath(path, start, end, vertices);
|
} else {
|
||||||
return line;
|
const path = shortestPath(graph, startNode, endNode);
|
||||||
}
|
if (path) {
|
||||||
|
result = path.map(index => points[index]);
|
||||||
function lineIntersection(a1, a2, b1, b2) {
|
} else {
|
||||||
// source: http://mathworld.wolfram.com/Line-LineIntersection.html
|
result = [start, end];
|
||||||
const intersection = {
|
}
|
||||||
x: ((a1.x * a2.y - a1.y * a2.x) * (b1.x - b2.x) - (a1.x - a2.x) * (b1.x * b2.y - b1.y * b2.x)) / ((a1.x - a2.x) * (b1.y - b2.y) - (a1.y - a2.y) * (b1.x - b2.x)),
|
|
||||||
y: ((a1.x * a2.y - a1.y * a2.x) * (b1.y - b2.y) - (a1.y - a2.y) * (b1.x * b2.y - b1.y * b2.x)) / ((a1.x - a2.x) * (b1.y - b2.y) - (a1.y - a2.y) * (b1.x - b2.x))
|
|
||||||
};
|
|
||||||
|
|
||||||
const intersectionA = subtract(intersection, a1);
|
|
||||||
const directionA = subtract(a2, a1);
|
|
||||||
const normalA = normalize(directionA);
|
|
||||||
const distanceA = dot(normalA, intersectionA);
|
|
||||||
if (distanceA < 0 || distanceA > dot(normalA, directionA)) return false;
|
|
||||||
|
|
||||||
const intersectionB = subtract(intersection, b1);
|
|
||||||
const directionB = subtract(b2, b1);
|
|
||||||
const normalB = normalize(directionB);
|
|
||||||
const distanceB = dot(normalB, intersectionB);
|
|
||||||
if (distanceB < 0 || distanceB > dot(normalB, directionB)) return false;
|
|
||||||
|
|
||||||
return intersection;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function pointIsInsideConvex(point, convex, vertices) {
|
|
||||||
for (let i = 0; i < convex.length; i ++) {
|
|
||||||
const vertexA = vertices[convex[i]];
|
|
||||||
const vertexB = vertices[convex[(i + 1) % convex.length]];
|
|
||||||
|
|
||||||
const n = normalize(normal(subtract(vertexB, vertexA)));
|
|
||||||
const p = subtract(point, vertexA);
|
|
||||||
|
|
||||||
if (dot(p, n) < 0) return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createGraph(polygons) {
|
||||||
|
const points = [];
|
||||||
|
const edges = [];
|
||||||
|
const nextPoints = new WeakMap();
|
||||||
|
const previousPoints = new WeakMap();
|
||||||
|
const normals = new WeakMap();
|
||||||
|
for (let i = 0; i < polygons.length; i ++) {
|
||||||
|
const polygon = polygons[i];
|
||||||
|
for (let j = 0; j < polygon.length; j ++) {
|
||||||
|
const point = polygon[j];
|
||||||
|
const nextPoint = polygon[(j + 1) % polygon.length];
|
||||||
|
const previousPoint = polygon[(j - 1 + polygon.length) % polygon.length];
|
||||||
|
|
||||||
|
points.push(point);
|
||||||
|
edges.push([point, nextPoint]);
|
||||||
|
nextPoints.set(point, nextPoint);
|
||||||
|
previousPoints.set(point, previousPoint);
|
||||||
|
|
||||||
|
normals.set(point, normal(subtract(nextPoint, point)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const graph = points.map(() => ([]));
|
||||||
|
for (let i = 0; i < points.length; i ++) {
|
||||||
|
const a = points[i];
|
||||||
|
|
||||||
|
for (let j = i + 1; j < points.length; j ++) {
|
||||||
|
const b = points[j];
|
||||||
|
const nextPoint = nextPoints.get(a);
|
||||||
|
const previousPoint = previousPoints.get(a);
|
||||||
|
|
||||||
|
if (lineIsVisible(previousPoint, nextPoint, edges, a, b)) {
|
||||||
|
const distance = distanceTo(a, b);
|
||||||
|
|
||||||
|
const connectNodeA = graph[i];
|
||||||
|
connectNodeA.push({ to: j, distance });
|
||||||
|
|
||||||
|
const connectNodeB = graph[j];
|
||||||
|
connectNodeB.push({ to: i, distance });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { graph, edges, points, normals };
|
||||||
|
}
|
||||||
|
|
||||||
|
function createNode(graph, points, edges, normals, point) {
|
||||||
|
const node = [];
|
||||||
|
const to = graph.length;
|
||||||
|
graph.push(node);
|
||||||
|
|
||||||
|
let previousPoint;
|
||||||
|
let nextPoint;
|
||||||
|
for (let j = 0; j < edges.length; j ++) {
|
||||||
|
const edge = edges[j];
|
||||||
|
if (pointOnLine(edge, point)) [previousPoint, nextPoint] = edge;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < graph.length; i ++) {
|
||||||
|
const b = points[i];
|
||||||
|
|
||||||
|
if (!lineIsVisible(previousPoint, nextPoint, edges, point, b)) continue;
|
||||||
|
|
||||||
|
const distance = distanceTo(point, b);
|
||||||
|
node.push({ to: i, distance });
|
||||||
|
|
||||||
|
graph[i] = [...graph[i], { to, distance }];
|
||||||
|
}
|
||||||
|
|
||||||
|
return to;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lineIsVisible(previousPoint, nextPoint, edges, a, b) {
|
||||||
|
if (b === nextPoint || b === previousPoint) return true;
|
||||||
|
|
||||||
|
if (previousPoint && nextPoint) {
|
||||||
|
const angleLine = angle(subtract(b, a));
|
||||||
|
const anglePrevious = angle(subtract(previousPoint, a));
|
||||||
|
const angleNext = angle(subtract(nextPoint, a));
|
||||||
|
if (betweenAngles(angleLine, anglePrevious, angleNext)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lineCrossesEdges(edges, a, b)) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function decompose(polygon) {
|
function lineCrossesEdges(edges, a, b) {
|
||||||
const vertices = polygon.reduce((points, path) => {
|
for (let i = 0; i < edges.length; i ++) {
|
||||||
points.push(...path);
|
const [c, d] = edges[i];
|
||||||
return points;
|
if (lineSegmentsCross(a, b, c, d)) return true;
|
||||||
}, []);
|
|
||||||
const flatVertices = vertices.reduce((points, { x, y }) => {
|
|
||||||
points.push(x, y);
|
|
||||||
return points;
|
|
||||||
}, []);
|
|
||||||
let offset = 0;
|
|
||||||
const holes = polygon
|
|
||||||
.map(path => offset += path.length)
|
|
||||||
.slice(0, -1);
|
|
||||||
|
|
||||||
const flatTrainglesIndexed = earcut(flatVertices, holes);
|
|
||||||
const convexPolygons = [];
|
|
||||||
for (let i = 0; i < flatTrainglesIndexed.length; i += 3) {
|
|
||||||
const face = [
|
|
||||||
flatTrainglesIndexed[i],
|
|
||||||
flatTrainglesIndexed[i + 1],
|
|
||||||
flatTrainglesIndexed[i + 2]
|
|
||||||
];
|
|
||||||
const center = divide(face.reduce((total, point) => {
|
|
||||||
if (!total) {
|
|
||||||
return vertices[point];
|
|
||||||
} else {
|
|
||||||
return add(total, vertices[point]);
|
|
||||||
}
|
|
||||||
}, null), face.length);
|
|
||||||
convexPolygons.push({
|
|
||||||
center,
|
|
||||||
face,
|
|
||||||
connects: []
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < convexPolygons.length; i ++) {
|
function lineSegmentsCross(a, b, c, d) {
|
||||||
for (let j = i + 1; j < convexPolygons.length; j ++) {
|
const denominator = ((b.x - a.x) * (d.y - c.y)) - ((b.y - a.y) * (d.x - c.x));
|
||||||
const triangleIndexedA = convexPolygons[i];
|
|
||||||
const triangleIndexedB = convexPolygons[j];
|
|
||||||
|
|
||||||
const overlap = [];
|
if (denominator === 0.0) return false;
|
||||||
triangleIndexedA.face.map(index => {
|
|
||||||
if (triangleIndexedB.face.includes(index)) overlap.push(index);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (overlap.length === 2) {
|
const numerator1 = ((a.y - c.y) * (d.x - c.x)) - ((a.x - c.x) * (d.y - c.y));
|
||||||
const distance = distanceTo(convexPolygons[i].center, convexPolygons[j].center);
|
const numerator2 = ((a.y - c.y) * (b.x - a.x)) - ((a.x - c.x) * (b.y - a.y));
|
||||||
triangleIndexedA.connects.push({ to: j, edge: overlap, distance });
|
if (numerator1 === 0.0 || numerator2 === 0.0) return false;
|
||||||
triangleIndexedB.connects.push({ to: i, edge: overlap, distance });
|
|
||||||
|
const r = numerator1 / denominator;
|
||||||
|
const s = numerator2 / denominator;
|
||||||
|
return (r > 0.0 && r < 1.0) && (s >= 0.0 && s <= 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeAngle(a) {
|
||||||
|
a %= Math.PI * 2;
|
||||||
|
return a > 0.0 ? a : a + Math.PI * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function betweenAngles(n, a, b) {
|
||||||
|
n = normalizeAngle(n);
|
||||||
|
a = normalizeAngle(a);
|
||||||
|
b = normalizeAngle(b);
|
||||||
|
return a < b ? a <= n && n <= b : a <= n || n <= b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// dijkstra's algorithm
|
||||||
|
function shortestPath(graph, start, end) {
|
||||||
|
const distances = graph.map(() => Infinity);
|
||||||
|
distances[start] = 0;
|
||||||
|
const traverse = [];
|
||||||
|
const queue = graph.map((node, i) => i);
|
||||||
|
|
||||||
|
while (queue.length > 0) {
|
||||||
|
let queueIndex;
|
||||||
|
let minDistance = Infinity;
|
||||||
|
for (let index = 0; index < queue.length; index ++) {
|
||||||
|
const nodeIndex = queue[index];
|
||||||
|
const distance = distances[nodeIndex];
|
||||||
|
if (distances[nodeIndex] < minDistance) {
|
||||||
|
queueIndex = index;
|
||||||
|
minDistance = distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const [nodeIndex] = queue.splice(queueIndex, 1);
|
||||||
|
const node = graph[nodeIndex];
|
||||||
|
|
||||||
|
for (let i = 0; i < node.length; i ++) {
|
||||||
|
const child = node[i];
|
||||||
|
const distance = distances[nodeIndex] + child.distance;
|
||||||
|
if (distance < distances[child.to]) {
|
||||||
|
distances[child.to] = distance;
|
||||||
|
traverse[child.to] = nodeIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { vertices, convexPolygons };
|
if (!traverse.hasOwnProperty(end)) return null;
|
||||||
|
|
||||||
|
const path = [end];
|
||||||
|
let nodeIndex = end;
|
||||||
|
do {
|
||||||
|
nodeIndex = traverse[nodeIndex];
|
||||||
|
path.push(nodeIndex);
|
||||||
|
} while (nodeIndex !== start);
|
||||||
|
|
||||||
|
return path.reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
// const distanceMap = new WeakMap();
|
function pointOnLine([a, b], point) {
|
||||||
// export function findClosestPath(convexPolygons, start, end, visited = [], path = [], distance = 0) {
|
return (a.x - point.x) * (a.y - point.y) === (b.x - point.x) * (b.y - point.y);
|
||||||
// if (start === end) return [];
|
|
||||||
//
|
|
||||||
// visited = [...visited, start];
|
|
||||||
//
|
|
||||||
// const { connects } = convexPolygons[start];
|
|
||||||
//
|
|
||||||
// const finish = connects.find(({ to }) => to === end);
|
|
||||||
// if (finish) return [...path, finish];
|
|
||||||
//
|
|
||||||
// const posibilities = [];
|
|
||||||
// for (let i = 0; i < connects.length; i ++) {
|
|
||||||
// const connect = connects[i];
|
|
||||||
// if (visited.includes(connect.to)) continue;
|
|
||||||
//
|
|
||||||
// const positibiltyDistance = distance + connect.distance;
|
|
||||||
// const posibility = findClosestPath(convexPolygons, connect.to, end, visited, [...path, connect], positibiltyDistance);
|
|
||||||
// if (posibility) {
|
|
||||||
// posibilities.push(posibility);
|
|
||||||
// distanceMap.set(posibility, positibiltyDistance);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (posibilities.length === 0) {
|
|
||||||
// return null;
|
|
||||||
// } else if (posibilities.length === 1) {
|
|
||||||
// return posibilities[0];
|
|
||||||
// } else if (posibilities.length > 1) {
|
|
||||||
// return posibilities.sort((a, b) => distanceMap.get(a) - distanceMap.get(b))[0];
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
const findKey = _key => ({ key }) => _key === key;
|
|
||||||
export function findClosestPath(map, start, end) {
|
|
||||||
// dijkstra's algorithm
|
|
||||||
const distances = { [start]: 0 };
|
|
||||||
const open = [{ key: 0, nodes: [start] }];
|
|
||||||
const predecessors = {};
|
|
||||||
|
|
||||||
while (open.length !== 0) {
|
|
||||||
const key = Math.min(...open.map(n => n.key).sort());
|
|
||||||
const bucket = open.find(findKey(key));
|
|
||||||
const node = bucket.nodes.shift();
|
|
||||||
const currentDistance = key;
|
|
||||||
const { connects } = map[node];
|
|
||||||
|
|
||||||
if (bucket.nodes.length === 0) open.splice(open.indexOf(bucket), 1);
|
|
||||||
|
|
||||||
for (let i = 0; i < connects.length; i ++) {
|
|
||||||
const { distance, to } = connects[i];
|
|
||||||
const totalDistance = distance + currentDistance;
|
|
||||||
const vertexDistance = distances[to];
|
|
||||||
|
|
||||||
if ((typeof vertexDistance === 'undefined') || (vertexDistance > totalDistance)) {
|
|
||||||
distances[to] = totalDistance;
|
|
||||||
|
|
||||||
let openNode = open.find(findKey(totalDistance));
|
|
||||||
if (!openNode) {
|
|
||||||
openNode = { key: totalDistance, nodes: [] };
|
|
||||||
open.push(openNode);
|
|
||||||
}
|
|
||||||
openNode.nodes.push(to);
|
|
||||||
|
|
||||||
predecessors[to] = node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof distances[end] === 'undefined') return null;
|
|
||||||
|
|
||||||
const nodes = [];
|
|
||||||
let node = end;
|
|
||||||
while (typeof node !== 'undefined') {
|
|
||||||
nodes.push(node);
|
|
||||||
node = predecessors[node];
|
|
||||||
}
|
|
||||||
nodes.reverse();
|
|
||||||
|
|
||||||
const path = [];
|
|
||||||
for (let i = 1; i < nodes.length; i ++) {
|
|
||||||
const from = nodes[i - 1];
|
|
||||||
const to = nodes[i];
|
|
||||||
|
|
||||||
const connection = map[from].connects.find(connect => connect.to === to);
|
|
||||||
path.push(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function containLineInPath(path, start, end, vertices) {
|
|
||||||
let line = [start];
|
|
||||||
|
|
||||||
for (let i = 0; i < path.length; i ++) {
|
|
||||||
const { edge: [indexA, indexB] } = path[i];
|
|
||||||
const vertexA = vertices[indexA];
|
|
||||||
const vertexB = vertices[indexB];
|
|
||||||
const lastPoint = line[line.length - 1];
|
|
||||||
|
|
||||||
const intersection = lineIntersection(lastPoint, end, vertexA, vertexB);
|
|
||||||
if (!intersection) {
|
|
||||||
const distanceA = distanceTo(lastPoint, vertexA) + distanceTo(vertexA, end);
|
|
||||||
const distanceB = distanceTo(lastPoint, vertexB) + distanceTo(vertexB, end);
|
|
||||||
const newPoint = distanceA < distanceB ? vertexA : vertexB;
|
|
||||||
|
|
||||||
// line = containLineInPath(path.slice(0, i), start, newPoint, vertices);
|
|
||||||
line.push(newPoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
line.push(end);
|
|
||||||
return line;
|
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ export const almostEquals = (a, b) => Math.abs(a.x - b.x) < 0.001 && Math.abs(a.
|
|||||||
export const dot = (a, b) => a.x * b.x + a.y * b.y;
|
export const dot = (a, b) => a.x * b.x + a.y * b.y;
|
||||||
export const length = (v) => Math.sqrt(v.x * v.x + v.y * v.y);
|
export const length = (v) => Math.sqrt(v.x * v.x + v.y * v.y);
|
||||||
export const distanceTo = (a, b) => length(subtract(a, b));
|
export const distanceTo = (a, b) => length(subtract(a, b));
|
||||||
|
export const angle = (v) => Math.atan2(v.y, v.x);
|
||||||
export const normalize = (v) => {
|
export const normalize = (v) => {
|
||||||
const l = length(v);
|
const l = length(v);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user