데이터 시각화를 하다보면 벤다이어그램이 쓰이는 경우가 생긴다. 그중 3개의 타겟을 가진 벤다이어그램을 그려보자.
사진과 같은 3개의 원을 그리기 위해서는 역정삼각형을 그려야한다. 각 꼭지점을 중심으로 같은 크기의 원을 3개 그리면된다.
밑변의 2점을 구하는건 쉽지만 꼭지점을 구하는 방법을 가만히 생각을 해봐야했다.
그래서 찾은 방법은 삼각형의 무게중심
이다.
삼각형의 세 중선은 한 점에서 만나고 점을 무게중심이라고한다. 무게중심은 세 주선을 각 꼭지점으로부터 2:1 내분한다.
이 공식을 통해 정삼각형의 높이를 구하고 무게중심을 SVG canvas으로 중심으로 고정한뒤 각 꼭지점의 좌표를 구하면 된다.
const x1 = centerX - side / 2;
const y1 = centerY - triangleHeight * (0.333);
const x2 = centerX + side / 2;
const y2 = centerY - triangleHeight * (0.333);
const x3 = centerX;
const y3 = centerY + triangleHeight * 0.666;
+) 위의 좌표되로 그리게 되면 서로의 중심을 지나는 벤 다이어그램이 그려지므로 임의의 거리를 더해주어 원의 거리를 이동해주었다.
1차로 만들고 보니 고려하지 못했던 부분이 떠올랐다.
데이터가 존재하는 각 부분에 이벤트가 필요하다면 어떻게 분리하지?
현재 그려진 벤 다이어그램 하나의 원으로 그려져 교집합을 이루는 부분들을 따로 분리할 수가 없다. 결국 3개의 원으로 벤 다이어그램을 그리는것이 아니라 데이터가 존재하는 영역별로 그려야하는 것이다.
svg롤 통해 그리기위해서는 그릴려는 위치의 시작점 좌표와 끝나는 좌표가 필요하다.
결국 중심을 잡을 수있는 중심점과 원들이 교차하는 교점의 좌표
를 구해야했다.
수학이론 검색하면 공통 현의 길이를 구하는 방법은 많이 나오지만 내가 딱 원하는 이론을 나오지 않아 헤매던 중 두원사이의 교점 구하기 (블로그) 포스팅을 발견하였다.
private circleTwoContactPosition = (x1: number, y1: number, x2: number, y2: number, side: number) => {
if (x1===0) {
return {cx1: 0, cy1: 0, cx2: 0, cy2: 0}
}
let xx = x2 - x1;
let yy = y2 - y1;
const D = Math.sqrt((xx ** 2) + (yy ** 2));//두 중심의 거리
const T1 = Math.acos((side * side - side * side + D * D) / (2 * side * D));
const T2 = Math.atan(yy / xx);
const T3 = x1 + side * Math.cos(T2 + T1); //AB
const T4 = y1 + side * Math.sin(T2 + T1); //AB
const T5 = x1 + side * Math.cos(T2 - T1); //AB
const T6 = y1 + side * Math.sin(T2 - T1); //AB
return {cx1: T3, cy1: T4, cx2: T5, cy2: T6}
}
3개의 원의 교점들의 좌표들을(6개) 구하기위해 함수를 따로 만들었다.
<circle cx={cX1} cy={cY1} r={controlSide/4} fill={"black"}/>
<circle cx={cX2} cy={cY2} r={controlSide/4} fill={"black"}/>
<circle cx={cX3} cy={cY3} r={controlSide/4} fill={"black"}/>
<circle cx={cX4} cy={cY4} r={controlSide/4} fill={"black"}/>
<circle cx={cX5} cy={cY5} r={controlSide/4} fill={"black"}/>
<circle cx={cX6} cy={cY6} r={controlSide/4} fill={"black"}/>
<path />
을 호를 그려보자엘립티컬 아크 를 활용했다.
A rx ry x축-회전각 큰-호-플래그 쓸기-방향-플래그 x y
a rx ry x축-회전각 큰-호-플래그 쓸기-방향-플래그 dx dy
<svg>
<path
d="M105,20 a20,20 0 1,1 50,25"
fill="none" stroke="#4286f0" stroke-width="8" />
</svg>
규칙과 호 그리는 개념을 잘 활용해 그려봤다.
<path className={"A"} d={`M ${cX5},${cY5} A ${side},${side} 0 0,1 ${cX4},${cY4} A${side},${side} 0 0,1 ${cX2},${cY2} A${side},${side} 0 1,0 ${cX5},${cY5}`} />
<path className={"B"} d={`M ${cX2},${cY2} A ${side},${side} 0 0,1 ${cX6},${cY6} A${side},${side} 0 0,1 ${cX3},${cY3} A${side},${side} 0 1,0 ${cX2},${cY2}`}/>
<path className={"C"} d={`M${cX3},${cY3}
A${side},${side} 0 0,1 ${cX1},${cY1}
A${side},${side} 0 0,1 ${cX5},${cY5}
A${side},${side} 0 1,0 ${cX3},${cY3}`}/>
<path className={"AB"} d={`M${cX4},${cY4}
A${side},${side} 0 0,1 ${cX2},${cY2}
A${side},${side} 0 0,1 ${cX6},${cY6}
A${side},${side} 0 0,0 ${cX4},${cY4}`}/>
<path className={"BC"} d={`M${cX6},${cY6}
A${side},${side} 0 0,1 ${cX3},${cY3}
A${side},${side} 0 0,1 ${cX1},${cY1}
A${side},${side} 0 0,0 ${cX6},${cY6}`}/>
<path className={"AC"} d={`M${cX4},${cY4}
A${side},${side} 0 0,0 ${cX5},${cY5}
A${side},${side} 0 0,0 ${cX1},${cY1}
A${side},${side} 0 0,1 ${cX4},${cY4}`}/>
<path className={"ABC"} d={`M${cX4},${cY4}
A${side},${side} 0 0,1 ${cX6},${cY6}
A${side},${side} 0 0,1 ${cX1},${cY1}
A${side},${side} 0 0,1 ${cX4},${cY4}`}/>
이렇게 모든 부분을 각각 따로 그리게되면 위 그림처럼 hover
와 같은 각각에 이벤트를 줄 수 있다.