qcast-front/snippet.js

273 lines
8.6 KiB
JavaScript

/**
* Renders an edge result (polygon) on the canvas
* @param {Object} res - Edge result object containing polygon vertices
* @param wallLineStart
* @param wallLineEnd
* @param {Array} res.Polygon - Array of vertex objects with X,Y coordinates
*/
const drawEdgeResult = (res, wallLineStart, wallLineEnd) => {
if (!ctx || res.Polygon.length < 2) return;
// 삼각형 polygon인지 확인 (3개의 점으로 구성)
const isTriangle = res.Polygon.length === 3;
if (isTriangle) {
// 삼각형인 경우 직선을 그리기
drawStraightLineFromSkeleton(res);
return;
}
const skelP0 = { x: res.Polygon[0].X, y: res.Polygon[0].Y };
const skelP1 = { x: res.Polygon[1].X, y: res.Polygon[1].Y };
// Helper function to check if a point is on a line segment
const isPointOnSegment = (p, q, r) => {
return (
q.x <= Math.max(p.x, r.x) &&
q.x >= Math.min(p.x, r.x) &&
q.y <= Math.max(p.y, r.y) &&
q.y >= Math.min(p.y, r.y)
);
};
// Helper function to check for collinearity
const areCollinear = (p, q, r) => {
const val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
return Math.abs(val) < 1e-9; // Use a small tolerance for float comparison
};
let isOverlapping = false;
for (let i = 0; i < points.length; i++) {
const polyP1 = { x: points[i][0], y: points[i][1] };
const polyP2 = { x: points[(i + 1) % points.length][0], y: points[(i + 1) % points.length][1] };
// Check if the skeleton edge is collinear with the polygon edge
// and if the skeleton points lie on the polygon edge segment.
if (areCollinear(polyP1, polyP2, skelP0) &&
areCollinear(polyP1, polyP2, skelP1) &&
isPointOnSegment(polyP1, skelP0, polyP2) &&
isPointOnSegment(polyP1, skelP1, polyP2)) {
isOverlapping = true;
break;
}
}
const p0 = res.Polygon[0];
const p1 = res.Polygon[1];
const dx = p1.X - p0.X;
const dy = p1.Y - p0.Y;
const len = Math.hypot(dx, dy);
if (len === 0) return;
// 정규화된 방향 벡터 계산
const dirX = dx / len;
const dirY = dy / len;
// First pass: Create a filled polygon (transparent fill)
ctx.globalAlpha = 0.2; // Fully transparent
ctx.beginPath();
ctx.fillStyle = 'green';
// Start from first vertex
ctx.moveTo(res.Polygon[0].X, res.Polygon[0].Y);
// Draw lines to each subsequent vertex
for (const v of res.Polygon) {
ctx.lineTo(v.X, v.Y);
}
ctx.closePath();
ctx.fill();
// Second pass: Draw the polygon outline
ctx.globalAlpha = 1; // Fully opaque
ctx.beginPath();
ctx.strokeStyle = isOverlapping ? 'red' : '#3C78D8'; // Blue color
ctx.lineWidth = 3; // 3px line width
// Redraw the same polygon shape
ctx.moveTo(res.Polygon[0].X, res.Polygon[0].Y);
for (const v of res.Polygon) {
const wX = wallLineStart.X;
const wY = wallLineStart.Y;
const wEndX = wallLineEnd.X;
const wEndY = wallLineEnd.Y;
console.log("1::", v.X, v.Y);
if ((v.X === wallLineStart.X && v.Y === wallLineEnd.Y) || (v.X === wallLineEnd.X && v.Y === wallLineStart.Y)) {
console.log(v.X, v.Y);
}else{
ctx.lineTo(v.X, v.Y);
}
ctx.lineTo(v.X, v.Y);
}
ctx.closePath();
ctx.stroke();
};
/**
* 삼각형 polygon 대신 직선을 그리는 함수
* @param {Object} res - Edge result object containing polygon vertices
*/
const drawStraightLineFromSkeleton = (res) => {
if (!ctx || res.Polygon.length !== 3) return;
// 삼각형의 세 점
const p1 = res.Polygon[0];
const p2 = res.Polygon[1];
const p3 = res.Polygon[2];
// 다각형의 경계에 있는 점들을 찾기 (원래 다각형의 변 위에 있는 점들)
const boundaryPoints = [];
const innerPoints = [];
[p1, p2, p3].forEach(point => {
let isOnBoundary = false;
// 원래 다각형의 각 변과 비교
for (let i = 0; i < points.length; i++) {
const lineStart = points[i];
const lineEnd = points[(i + 1) % points.length];
// 점이 이 변 위에 있는지 확인
if (isPointOnLine(point, lineStart, lineEnd)) {
isOnBoundary = true;
break;
}
}
if (isOnBoundary) {
boundaryPoints.push(point);
} else {
innerPoints.push(point);
}
});
// 경계점이 2개이고 내부점이 1개인 경우 (일반적인 삼각형 케이스)
if (boundaryPoints.length === 2 && innerPoints.length === 1) {
const innerPoint = innerPoints[0];
// 내부점에서 맞은편 경계까지의 직선을 그리기
drawExtendedLine(innerPoint, boundaryPoints);
}
};
/**
* 점이 선분 위에 있는지 확인하는 함수
*/
const isPointOnLine = (point, lineStart, lineEnd) => {
const tolerance = 1; // 허용 오차
// 점과 선분의 두 끝점 사이의 거리 합이 선분의 길이와 같은지 확인
const d1 = Math.hypot(point.X - lineStart[0], point.Y - lineStart[1]);
const d2 = Math.hypot(point.X - lineEnd[0], point.Y - lineEnd[1]);
const lineLength = Math.hypot(lineEnd[0] - lineStart[0], lineEnd[1] - lineStart[1]);
return Math.abs(d1 + d2 - lineLength) < tolerance;
};
/**
* 내부점에서 맞은편 경계까지 직선을 연장해서 그리는 함수
*/
const drawExtendedLine = (innerPoint, boundaryPoints) => {
if (boundaryPoints.length !== 2) return;
// 경계점들의 중점 계산 (삼각형의 밑변 중점)
const midPoint = {
X: (boundaryPoints[0].X + boundaryPoints[1].X) / 2,
Y: (boundaryPoints[0].Y + boundaryPoints[1].Y) / 2
};
// 내부점에서 중점으로의 방향 벡터 계산
const dirX = midPoint.X - innerPoint.X;
const dirY = midPoint.Y - innerPoint.Y;
const len = Math.hypot(dirX, dirY);
if (len === 0) return;
// 정규화된 방향 벡터
const normalizedDirX = dirX / len;
const normalizedDirY = dirY / len;
// 맞은편 경계와의 교점 찾기
const intersectionPoint = findIntersectionWithBoundary(innerPoint, normalizedDirX, normalizedDirY);
if (intersectionPoint) {
// 직선 그리기
ctx.beginPath();
ctx.strokeStyle = '#3C78D8'; // 파란색
ctx.lineWidth = 3;
ctx.moveTo(innerPoint.X, innerPoint.Y);
ctx.lineTo(intersectionPoint.X, intersectionPoint.Y);
ctx.stroke();
}
};
/**
* 점에서 특정 방향으로 다각형 경계와의 교점을 찾는 함수
*/
const findIntersectionWithBoundary = (startPoint, dirX, dirY) => {
let closestIntersection = null;
let minDistance = Infinity;
// 모든 다각형의 변과 교점 확인
for (let i = 0; i < points.length; i++) {
const lineStart = points[i];
const lineEnd = points[(i + 1) % points.length];
// 레이와 선분의 교점 계산
const intersection = getLineIntersection(
startPoint,
{ X: startPoint.X + dirX * 1000, Y: startPoint.Y + dirY * 1000 }, // 충분히 긴 레이
{ X: lineStart[0], Y: lineStart[1] },
{ X: lineEnd[0], Y: lineEnd[1] }
);
if (intersection) {
// 교점이 시작점에서 올바른 방향에 있는지 확인
const toIntersection = {
X: intersection.X - startPoint.X,
Y: intersection.Y - startPoint.Y
};
// 내적으로 방향 확인 (양수면 같은 방향)
const dotProduct = toIntersection.X * dirX + toIntersection.Y * dirY;
if (dotProduct > 0) { // 올바른 방향
const distance = Math.hypot(toIntersection.X, toIntersection.Y);
if (distance < minDistance) {
minDistance = distance;
closestIntersection = intersection;
}
}
}
}
return closestIntersection;
};
/**
* 두 선분의 교점을 계산하는 함수
*/
const getLineIntersection = (p1, p2, p3, p4) => {
const x1 = p1.X, y1 = p1.Y;
const x2 = p2.X, y2 = p2.Y;
const x3 = p3.X, y3 = p3.Y;
const x4 = p4.X, y4 = p4.Y;
const denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
if (Math.abs(denom) < 1e-10) return null; // 평행선
const t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / denom;
const u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / denom;
// 두 번째 선분(다각형의 변) 위에 교점이 있는지 확인
if (u >= 0 && u <= 1) {
return {
X: x1 + t * (x2 - x1),
Y: y1 + t * (y2 - y1)
};
}
return null;
};