티스토리 뷰

캔버스에 영역을 선택해야 한다.

문제는 좌표값을 받아서 테두리 따라 모양을 만들어야 하는데 위치 및 개수가 랜덤이다.

좌표 개수는 최소 4, 최대 6이라는 제한을 두게 된다.

 

이것 때문에 약 3일을 고민했다.

처음에는 선분끼리 겹치면 지우는 방법(ccw)을 택했는데 4각형은 괜찮았으나 5각형부터 문제였다.

그래서 다음에 선택한 것이  좌표간의 거리를 계산해 짧은 것만 남기는 방법을 택했는데 이것도 실패였다.

실패한 이미지1
실패한 이미지2

한참 고민하다 집단 지성의 힘을 빌리고자 카톡방에서 좌표 관련해서 잘 아는 사람을 찾았다.

중앙위치를 잡고 삼각형을 그려서 .... 어쩌구저쩌구 ...

생각해보니 메모리가 터질 것 같단 생각이 들었다.

대략 1시간 정도 졸다가 다시 생각해보니 '중앙 위치를 중심으로 시계방향으로 좌표를 정렬하면 되지 않을까?' 라는 생각이 스쳤다.

 

그리고 조언을 구했다.

같은 각도일 때 제대로 그려지지 않을 가능성이 있다며 2D라면 극좌표계를  써보라고 한다.

 

극좌표계의 점은 반지름(r)과 각(θ)으로 표현된다.
r는 극에서의 거리를 의미하고, θ는 0°(직교 좌표계의 x축 양의 방향에 해당)에서의 각도를 의미한다.
 참고

 

이걸 쓰면 세타(θ)를 기준으로 정렬하면 된다.

 

1. 점들의 극좌표를 계산한다.

const polarPoints = points.map((p) => {
    const dx = p.x - centerX;
    const dy = p.y - centerY;
    const distance = Math.sqrt(dx * dx + dy * dy);
    let theta = Math.atan2(dy, dx);
    theta *= 180 / Math.PI;
    return { point: p, distance, theta };
});

 

2. 세타 기준으로 정렬한다.

polarPoints.sort((a, b) => a.theta - b.theta );

 

3. 정렬된 점들의 좌표를 순서대로 저장한다.

const sortedPoints = polarPoints.map((pp) => {
    return pp.point;
});

 

4. 캔버스에 정렬된 좌표값에 따라 그림을 그린다.

ctx.beginPath();
ctx.moveTo(sortedPoints[0].x, sortedPoints[0].y);
for(let i = 1; i < sortedPoints.length; i++)
	ctx.lineTo(sortedPoints[i].x, sortedPoints[i].y);
ctx.lineTo(sortedPoints[0].x, sortedPoints[0].y);
ctx.stroke();
ctx.closePath();

성공한 이미지1
성공한 이미지2

 


인줄 알았는데 23.04.25(화) 오전 10시즘인가? 한 쪽 구석에 4개의 좌표를 찍었더니 사각형이 아닌 모래시계마냥 그려지는 것이 아닌가?

실패한 이미지 일부 영역 확대

각 좌표의 중심이 아닌 캔버스의 중심으로 계산을 해서 그런 것 같았다.

n개 좌표의 중심 좌표 구하기

let xSum = 0, ySum = 0;
points.forEach(p => {
    xSum += p.x;
    ySum += p.y;
})
centerX = xSum / points.length;
centerY = ySum / points.length;

이걸 먼저 수행 하고 점들의 극좌표를 계산했더니 원하는 대로 그려진다.

성공한 이미지 일부 영역 확대

 

완성.gif


23.05.08 내용추가++
mouse event로 좌표값 변경 기능까지 추가해주면

이렇게도 가능하다

반응형
LIST
댓글
링크
공지사항
최근에 올라온 글