Compare commits
20 Commits
8d91a78dee
...
1638d0ae33
| Author | SHA1 | Date | |
|---|---|---|---|
| 1638d0ae33 | |||
| 61dc83d7db | |||
| 884d674046 | |||
| 162b75bf2e | |||
| f08ec28ea3 | |||
| 67c15fc454 | |||
| 418616ba97 | |||
| b81540fe62 | |||
| 26202558a5 | |||
| b549693e8e | |||
| c1bd6db634 | |||
| 34a4980db8 | |||
| 188e6ee2f2 | |||
| d1045fa0df | |||
| 9ada34c020 | |||
| 4bf90e7dfb | |||
| 4ce66733dd | |||
| df4b2a889b | |||
| c2b4f5c7df | |||
| 14dc84502c |
1
last_updated.log
Executable file
1
last_updated.log
Executable file
@ -0,0 +1 @@
|
|||||||
|
1772531776
|
||||||
272
snippet.js
Normal file
272
snippet.js
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
};
|
||||||
272
snippet2.js
Normal file
272
snippet2.js
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
};
|
||||||
13
src/app/sample/page.jsx
Normal file
13
src/app/sample/page.jsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
'use client'
|
||||||
|
import { useContext } from 'react'
|
||||||
|
import { QcastContext } from '@/app/QcastProvider'
|
||||||
|
|
||||||
|
export default function SamplePage() {
|
||||||
|
const { setIsGlobalLoading } = useContext(QcastContext)
|
||||||
|
setIsGlobalLoading(false)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h1>Sample Page</h1>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
12
src/app/sample/sk/page.jsx
Normal file
12
src/app/sample/sk/page.jsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
import App from './App'
|
||||||
|
|
||||||
|
export default function SkPage() {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<App/>
|
||||||
|
</>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
80
src/app/sample/sk/styles.css
Normal file
80
src/app/sample/sk/styles.css
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
* {
|
||||||
|
font-family: monospace;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1000px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls button {
|
||||||
|
margin: 0 5px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls button:hover {
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
margin: 10px 0;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
background: white;
|
||||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculator-btn {
|
||||||
|
transition: all 0.1s ease-in-out;
|
||||||
|
}
|
||||||
|
.calculator-btn:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
/* 키패드 나타나고 사라지는 애니메이션 */
|
||||||
|
.keypad-container.hidden {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-10px) scale(0.95);
|
||||||
|
pointer-events: none; /* 숨겨졌을 때 클릭 방지 */
|
||||||
|
}
|
||||||
|
.keypad-container {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0) scale(1);
|
||||||
|
transition: opacity 150ms ease-out, transform 150ms ease-out;
|
||||||
|
}
|
||||||
@ -1748,7 +1748,7 @@ export default function StuffDetail() {
|
|||||||
<button type="button" className="btn-origin grey mr5" onClick={onTempSave} style={{ display: showButton }}>
|
<button type="button" className="btn-origin grey mr5" onClick={onTempSave} style={{ display: showButton }}>
|
||||||
{getMessage('stuff.detail.btn.tempSave')}
|
{getMessage('stuff.detail.btn.tempSave')}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" onClick={() => onValid('save')} className="btn-origin navy mr5" style={{ display: showButton }}>
|
<button type="button" onClick={() => onValid('save')} className="btn-origin navy mr5" style={{ display: showButton }}>
|
||||||
{getMessage('stuff.detail.btn.save')}
|
{getMessage('stuff.detail.btn.save')}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
@ -2163,7 +2163,7 @@ export default function StuffDetail() {
|
|||||||
onChange={(value) => form.setValue('verticalSnowCover', value)}
|
onChange={(value) => form.setValue('verticalSnowCover', value)}
|
||||||
options={{
|
options={{
|
||||||
allowNegative: false,
|
allowNegative: false,
|
||||||
allowDecimal: false
|
allowDecimal: false,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -2235,7 +2235,7 @@ export default function StuffDetail() {
|
|||||||
onChange={(value) => form.setValue('installHeight', value)}
|
onChange={(value) => form.setValue('installHeight', value)}
|
||||||
options={{
|
options={{
|
||||||
allowNegative: false,
|
allowNegative: false,
|
||||||
allowDecimal: false
|
allowDecimal: false,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -2448,7 +2448,7 @@ export default function StuffDetail() {
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr style={{ display: session?.storeLvl === '1' ? '' : 'none' }}>
|
||||||
<th>
|
<th>
|
||||||
<div className="flx-box">
|
<div className="flx-box">
|
||||||
<div className="title">
|
<div className="title">
|
||||||
@ -2759,7 +2759,7 @@ export default function StuffDetail() {
|
|||||||
onChange={(value) => form.setValue('verticalSnowCover', value)}
|
onChange={(value) => form.setValue('verticalSnowCover', value)}
|
||||||
options={{
|
options={{
|
||||||
allowNegative: false,
|
allowNegative: false,
|
||||||
allowDecimal: false
|
allowDecimal: false,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -2835,7 +2835,7 @@ export default function StuffDetail() {
|
|||||||
onChange={(value) => form.setValue('installHeight', value)}
|
onChange={(value) => form.setValue('installHeight', value)}
|
||||||
options={{
|
options={{
|
||||||
allowNegative: false,
|
allowNegative: false,
|
||||||
allowDecimal: false
|
allowDecimal: false,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -2966,7 +2966,7 @@ export default function StuffDetail() {
|
|||||||
<WindSelectPop setShowWindSpeedButtonValid={setShowWindSpeedButtonValid} prefName={form.watch('prefName')} windSpeedInfo={setWindSppedInfo} />
|
<WindSelectPop setShowWindSpeedButtonValid={setShowWindSpeedButtonValid} prefName={form.watch('prefName')} windSpeedInfo={setWindSppedInfo} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{estimatePopupOpen && <DocDownOptionPop planNo={popPlanNo} setEstimatePopupOpen={setEstimatePopupOpen} createStoreId={createSaleStoreId}/>}
|
{estimatePopupOpen && <DocDownOptionPop planNo={popPlanNo} setEstimatePopupOpen={setEstimatePopupOpen} createStoreId={createSaleStoreId} />}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,7 +39,7 @@ export default function StuffSubHeader({ type }) {
|
|||||||
if (isObjectNotEmpty(managementState)) {
|
if (isObjectNotEmpty(managementState)) {
|
||||||
if (managementState?.createSaleStoreId === 'T01') {
|
if (managementState?.createSaleStoreId === 'T01') {
|
||||||
if (session?.storeId !== 'T01') {
|
if (session?.storeId !== 'T01') {
|
||||||
//T01 계정이 작성한 안건 중 해당 판매점 ID가 열람 가능한 것에 한합니다.
|
//T01 계정이 작성한 안건 중 해당 판매점 ID가 열람 가능한 것에 한합니다.....
|
||||||
if(session?.storeId !== managementState?.saleStoreId){
|
if(session?.storeId !== managementState?.saleStoreId){
|
||||||
if(session?.storeId !== managementState?.firstAgentId) {
|
if(session?.storeId !== managementState?.firstAgentId) {
|
||||||
setButtonStyle('none')
|
setButtonStyle('none')
|
||||||
|
|||||||
@ -572,7 +572,6 @@ export function useCanvasSetting(executeEffect = true) {
|
|||||||
try {
|
try {
|
||||||
// roofsData가 단일 항목인 경우, 모든 추가된 지붕재(addedRoofs)를 사용하여 다중 항목으로 확장
|
// roofsData가 단일 항목인 경우, 모든 추가된 지붕재(addedRoofs)를 사용하여 다중 항목으로 확장
|
||||||
let roofMaterialsList = []
|
let roofMaterialsList = []
|
||||||
|
|
||||||
if (params.roofsData && params.roofsData.length === 1) {
|
if (params.roofsData && params.roofsData.length === 1) {
|
||||||
// 단일 항목인 경우 addedRoofs의 모든 항목을 사용
|
// 단일 항목인 경우 addedRoofs의 모든 항목을 사용
|
||||||
if (addedRoofs && addedRoofs.length > 0) {
|
if (addedRoofs && addedRoofs.length > 0) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user