Skip to content

Commit 0b20362

Browse files
committed
fix: 행성 UI 수정
1 parent 40984c7 commit 0b20362

3 files changed

Lines changed: 59 additions & 47 deletions

File tree

apps/www/src/components/bookmarks/graph/planet.tsx

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,13 @@ interface GraphProps {
1414
data: AllStarDTO;
1515
}
1616

17-
type Body = { mesh: THREE.Mesh; angle: number; center: THREE.Vector3; star: StarProps };
17+
interface Body {
18+
mesh: THREE.Mesh;
19+
angle: number;
20+
center: THREE.Vector3;
21+
star: StarProps;
22+
distance: number;
23+
}
1824

1925
export default function Planet({ onOpen, data }: GraphProps) {
2026
const { selectedType, selectedColor } = useBookmarkStore();
@@ -112,26 +118,29 @@ export default function Planet({ onOpen, data }: GraphProps) {
112118
});
113119

114120
const orbitRadius = 5;
115-
const orbitSpeed = 0.005;
116-
const loader = new THREE.TextureLoader();
121+
const maxDistance = 8; // 최대 거리
122+
const minDistance = 4; // 최소 거리
117123
const placementRange = 40;
118-
const centerRadius = 3;
124+
const centerRadius = 1.5;
119125
const minCenterDist = orbitRadius * 2 + centerRadius * 2;
120126
const centers: THREE.Vector3[] = [];
121127

122128
const bodies: Body[] = [];
123129

124130
function makeLabel(text: string): THREE.Sprite {
131+
// 최대 15자, 초과 시 ...
132+
const maxLabelLength = 15;
133+
const label = text.length > maxLabelLength ? text.slice(0, maxLabelLength) + "..." : text;
125134
const canvas = document.createElement("canvas");
126135
const ctx = canvas.getContext("2d")!;
127-
const fontSize = 48;
136+
const fontSize = 24;
128137
ctx.font = `${fontSize}px Arial`;
129-
const widthText = ctx.measureText(text).width;
138+
const widthText = ctx.measureText(label).width;
130139
canvas.width = widthText;
131140
canvas.height = fontSize;
132141
ctx.font = `${fontSize}px Arial`;
133142
ctx.fillStyle = "#ccc";
134-
ctx.fillText(text, 0, fontSize * 0.8);
143+
ctx.fillText(label, 0, fontSize * 0.8);
135144
const texture = new THREE.CanvasTexture(canvas);
136145
texture.needsUpdate = true;
137146
const material = new THREE.SpriteMaterial({
@@ -162,55 +171,72 @@ export default function Planet({ onOpen, data }: GraphProps) {
162171

163172
const cgeo = new THREE.SphereGeometry(centerRadius, 32, 32);
164173
const cmat = new THREE.MeshStandardMaterial({
165-
emissive: 0xffaa00,
174+
emissive: 0xd7bdce,
166175
emissiveIntensity: 0.7,
167-
color: 0xffaa00,
176+
color: 0xd7bdce,
168177
});
169178
const centerMesh = new THREE.Mesh(cgeo, cmat);
170179
centerMesh.position.copy(center);
171180
centerMesh.castShadow = true;
172181
centerMesh.receiveShadow = true;
173182
scene.add(centerMesh);
174183

184+
// shared keyword 라벨을 센터 구체 위에 표시
175185
const kwText = (groupKeywords[root] || []).join(", ");
176186
if (kwText) {
177187
const label = makeLabel(kwText);
178-
label.position.copy(center);
188+
label.position.set(center.x, center.y + centerRadius + 0.5, center.z);
179189
scene.add(label);
180190
}
181191

182-
const ring = new THREE.RingGeometry(orbitRadius - 0.5, orbitRadius + 0.5, 64);
183-
const ringMat = new THREE.MeshBasicMaterial({
184-
color: 0x888888,
185-
side: THREE.DoubleSide,
186-
transparent: true,
187-
opacity: 0.3,
188-
});
189-
const ringMesh = new THREE.Mesh(ring, ringMat);
190-
ringMesh.rotation.x = Math.PI / 2;
191-
ringMesh.position.copy(center);
192-
scene.add(ringMesh);
193-
194192
members.forEach((starId, j) => {
195193
const star = stars.find((s) => s.starId === starId)!;
196194
const size = 0.5 + (star.views / Math.max(...stars.map((s) => s.views))) * 0.5;
197195
const geo = new THREE.SphereGeometry(size, 32, 32);
198196

197+
// 각 star는 selectedColor로 표시
199198
const mat = new THREE.MeshStandardMaterial({ color: selectedColor });
200199
const mesh = new THREE.Mesh(geo, mat);
201200
mesh.userData = {
202201
starId: star.starId,
203202
title: star.title,
204203
faviconUrl: star.faviconUrl || "/velog.png",
205204
};
206-
const angle = (j / members.length) * Math.PI * 2;
207-
mesh.position.set(
208-
cx + orbitRadius * Math.cos(angle),
209-
0,
210-
cz + orbitRadius * Math.sin(angle)
211-
);
205+
206+
// lastAccessedAt을 기준으로 거리 계산
207+
const lastAccessed = new Date(star.lastAccessedAt).getTime();
208+
const now = new Date().getTime();
209+
const timeDiff = now - lastAccessed;
210+
const maxTimeDiff = 30 * 24 * 60 * 60 * 1000; // 30일
211+
const distance = minDistance + (maxDistance - minDistance) * (timeDiff / maxTimeDiff);
212+
213+
// 랜덤한 각도로 배치 및 공전 각도 부여
214+
const angle = Math.random() * Math.PI * 2;
215+
mesh.position.set(cx + distance * Math.cos(angle), 0, cz + distance * Math.sin(angle));
212216
scene.add(mesh);
213-
bodies.push({ mesh, angle, center: center.clone(), star });
217+
bodies.push({ mesh, angle, center: center.clone(), star, distance });
218+
219+
// ★ 행성 위에 title 라벨 추가
220+
const label = makeLabel(star.title);
221+
label.position.set(
222+
mesh.position.x,
223+
mesh.position.y + size + 0.5, // 구체 위에 띄우기
224+
mesh.position.z
225+
);
226+
scene.add(label);
227+
228+
// ★ 각 행성의 궤도 반지름에 맞는 얇은 띠 추가
229+
const orbitRingGeo = new THREE.RingGeometry(distance - 0.05, distance + 0.05, 64);
230+
const orbitRingMat = new THREE.MeshBasicMaterial({
231+
color: 0xcccccc,
232+
side: THREE.DoubleSide,
233+
transparent: true,
234+
opacity: 0.2,
235+
});
236+
const orbitRingMesh = new THREE.Mesh(orbitRingGeo, orbitRingMat);
237+
orbitRingMesh.rotation.x = Math.PI / 2;
238+
orbitRingMesh.position.copy(center);
239+
scene.add(orbitRingMesh);
214240
});
215241
});
216242

@@ -257,13 +283,6 @@ export default function Planet({ onOpen, data }: GraphProps) {
257283

258284
const animate = () => {
259285
requestAnimationFrame(animate);
260-
bodies.forEach((b) => {
261-
if (hovered !== b) {
262-
b.angle += orbitSpeed;
263-
b.mesh.position.x = b.center.x + orbitRadius * Math.cos(b.angle);
264-
b.mesh.position.z = b.center.z + orbitRadius * Math.sin(b.angle);
265-
}
266-
});
267286
controls.update();
268287
renderer.render(scene, camera);
269288
};

apps/www/src/types/star.ts

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
1-
export interface Star2DLeafProps {
2-
faviconUrl: string;
3-
lastAccessedAt: string;
4-
siteUrl: string;
5-
starId: string;
6-
summaryAI: string;
7-
thumbnailUrl: string;
8-
title: string;
9-
userMemo: string;
10-
views: number;
11-
}
1+
import { StarProps } from "@repo/types";
2+
3+
export interface Star2DLeafProps extends Omit<StarProps, "keywordList" | "categoryName"> {}
124

135
export interface Star2DNodeProps {
146
keyword: string;

packages/types/src/graph.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export interface StarProps {
99
views: number;
1010
keywordList: string[];
1111
faviconUrl: string;
12+
lastAccessedAt: string;
1213
}
1314

1415
export interface LinkProps {

0 commit comments

Comments
 (0)