path-finding-2d
Version:
Fast 2D pathfinding library using triangulated navmesh
236 lines (212 loc) • 7.67 kB
JavaScript
const { Point, Polygon, PolygonMap } = require('poly-math-2d');
const { NavMesh2d } = require('./dist/nav-mesh-2d');
const PathfindingVisualizer = require('./visualizer');
console.log('=== Path Finding 2D - Visual Example ===\n');
// === CREATING THREE HOLEY POLYGONS ===
// Polygon 1: Large square with square hole
const polygon1Outer = [
new Point(0, 0),
new Point(50, 0),
new Point(50, 50),
new Point(0, 50)
];
const polygon1Hole = new Polygon([
new Point(15, 15),
new Point(35, 15),
new Point(35, 35),
new Point(15, 35)
]);
const polygon1 = new Polygon(polygon1Outer, [polygon1Hole]);
// Polygon 2: L-shaped form with rectangular hole
const polygon2Outer = [
new Point(60, 0),
new Point(100, 0),
new Point(100, 30),
new Point(80, 30),
new Point(80, 50),
new Point(60, 50)
];
const polygon2Hole = new Polygon([
new Point(65, 10),
new Point(95, 10),
new Point(95, 20),
new Point(75, 20),
new Point(75, 40),
new Point(65, 40)
]);
const polygon2 = new Polygon(polygon2Outer, [polygon2Hole]);
// Polygon 3: Triangle with triangular hole
// План: Для каждой точки явно вычисляю новое значение y (y / 2 + 50) и подставляю результат в виде числа.
const polygon3Outer = [
new Point(20, 55), // 10 / 2 + 50 = 55
new Point(30, 70), // 40 / 2 + 50 = 70
new Point(50, 60), // 20 / 2 + 50 = 60
new Point(50, 70), // 40 / 2 + 50 = 70
new Point(60, 55), // 10 / 2 + 50 = 55
new Point(90, 55), // 10 / 2 + 50 = 55
new Point(100, 60), // 20 / 2 + 50 = 60
new Point(100, 75), // 50 / 2 + 50 = 75
new Point(80, 85), // 70 / 2 + 50 = 85
new Point(70, 75), // 50 / 2 + 50 = 75
new Point(50, 95), // 90 / 2 + 50 = 95
new Point(60, 100), // 100 / 2 + 50 = 100
new Point(20, 100), // 100 / 2 + 50 = 100
new Point(20, 80), // 60 / 2 + 50 = 80
new Point(10, 70), // 40 / 2 + 50 = 70
];
const polygon3Holes = [
new Polygon([
new Point(60, 70), // 40 / 2 + 50 = 70
new Point(90, 70), // 40 / 2 + 50 = 70
new Point(80, 60), // 20 / 2 + 50 = 60
new Point(80, 65), // 30 / 2 + 50 = 65
]),
new Polygon([
new Point(30, 75), // 50 / 2 + 50 = 75
new Point(30, 95), // 90 / 2 + 50 = 95
new Point(50, 80), // 60 / 2 + 50 = 80
])
];
const polygon3 = new Polygon(polygon3Outer, polygon3Holes);
// Create polygon map and navmesh
const polygonMap = new PolygonMap([polygon1, polygon2, polygon3]);
const navMesh = new NavMesh2d(polygonMap);
console.log(`Created ${polygon1.tpolygons.length + polygon2.tpolygons.length + polygon3.tpolygons.length} triangles`);
// === CREATING DETAILED TEST CASE VISUALIZATIONS ===
function createTestVisualization(testName, polygons, testPoints, navMesh, closestToStart = false) {
const viz = new PathfindingVisualizer(800, 600);
viz.addTitle(`Test: ${testName}`);
// Add polygons
polygons.forEach((poly, i) => {
const colors = ['#e3f2fd', '#f3e5f5', '#e8f5e8'];
viz.addPolygon(poly.outer, poly.holes, `Polygon ${i + 1}`, colors[i]);
});
// Add triangulation
const allTriangles = polygons.flatMap(p => p.triangles);
viz.addTriangulation(allTriangles);
// Add test points and paths
testPoints.forEach(test => {
const { pointA, pointB, labelA, labelB, expectedPath } = test;
// Determine point types
const aInMesh = navMesh.isPointInNavMesh(pointA);
const bInMesh = navMesh.isPointInNavMesh(pointB);
const typeA = aInMesh ? 'start' : 'outside';
const typeB = bInMesh ? 'end' : 'outside';
viz.addPoint(pointA, labelA, typeA);
viz.addPoint(pointB, labelB, typeB);
// Find and display path
const path = navMesh.findPath(pointA, pointB, closestToStart);
if (path.length > 0) {
console.log(path);
viz.addPath(path, `${labelA} → ${labelB}`);
}
});
viz.addLegend();
return viz;
}
// Test 1: Successful paths inside polygons
console.log('Creating visualization of successful paths...');
const successfulPaths = createTestVisualization(
'Successful paths inside polygons',
[
{ outer: polygon1Outer, holes: [polygon1Hole], triangles: polygon1.tpolygons },
{ outer: polygon2Outer, holes: [polygon2Hole], triangles: polygon2.tpolygons },
{ outer: polygon3Outer, holes: polygon3Holes, triangles: polygon3.tpolygons }
],
[
{
pointA: new Point(10, 10),
pointB: new Point(40, 40),
labelA: 'A1',
labelB: 'B1'
},
{
pointA: new Point(65, 5),
pointB: new Point(85, 25),
labelA: 'A2',
labelB: 'B2'
},
{
pointA: new Point(24, 85),
pointB: new Point(87, 65),
labelA: 'A3',
labelB: 'B3'
}
],
navMesh
);
successfulPaths.save('pathfinding-successful.svg');
// Test 2: Paths to points outside navmesh
console.log('Creating visualization of paths to points outside navmesh...');
const pathsToOutside = createTestVisualization(
'Paths to points outside navmesh',
[
{ outer: polygon1Outer, holes: [polygon1Hole], triangles: polygon1.tpolygons },
{ outer: polygon2Outer, holes: [polygon2Hole], triangles: polygon2.tpolygons },
{ outer: polygon3Outer, holes: polygon3Holes, triangles: polygon3.tpolygons }
],
[
{
pointA: new Point(10, 10),
pointB: new Point(53, 35),
labelA: 'C1',
labelB: 'D1 (outside)'
},
{
pointA: new Point(70, 5),
pointB: new Point(90, 32),
labelA: 'C2',
labelB: 'D2 (outside)'
},
{
pointA: new Point(20, 60),
pointB: new Point(75, 85),
labelA: 'C3',
labelB: 'D3 (outside)'
}
],
navMesh
);
pathsToOutside.save('pathfinding-to-outside.svg');
// Test 3: Points in holes
console.log('Creating visualization of points in holes...');
const holesTest = createTestVisualization(
'Paths to points in polygon holes',
[
{ outer: polygon1Outer, holes: [polygon1Hole], triangles: polygon1.tpolygons },
{ outer: polygon2Outer, holes: [polygon2Hole], triangles: polygon2.tpolygons },
{ outer: polygon3Outer, holes: polygon3Holes, triangles: polygon3.tpolygons }
],
[
{
pointA: new Point(10, 40),
pointB: new Point(25, 20),
labelA: 'G1',
labelB: 'H1 (hole)'
},
{
pointA: new Point(65, 45),
pointB: new Point(90, 17),
labelA: 'G2',
labelB: 'H2 (hole)'
},
{
pointA: new Point(30, 95),
pointB: new Point(85, 67),
labelA: 'G3',
labelB: 'H3 (hole)'
}
],
navMesh
);
// Add special points in holes
holesTest.addPoint(new Point(25, 20), 'H1 (hole)', 'hole');
holesTest.addPoint(new Point(90, 17), 'H2 (hole)', 'hole');
holesTest.addPoint(new Point(85, 67), 'H3 (hole)', 'hole');
holesTest.save('pathfinding-holes.svg');
console.log('\n=== Visualization completed ===');
console.log('Created files:');
console.log('- pathfinding-successful.svg - successful paths');
console.log('- pathfinding-to-outside.svg - paths to points outside navmesh');
console.log('- pathfinding-holes.svg - working with holes');
console.log('\nOpen SVG files in browser to view!');