Skip to content

PointCloud3D · Surface3D ​

renderPointCloud3D는 서로 독립된 XYZ 샘플을 화면을 향하는 점으로 그리고, renderSurface3D는 Y-up 높이 그리드를 조명과 컬러맵이 적용된 면으로 그립니다. 힘 기반 그래프가 아니라 실제 X/Y/Z 축, 선택, 스트리밍이 필요한 측정 3D 데이터(스캔, 궤적, 시뮬레이션 결과)에 사용합니다.

라이브 예제

기본 사용법 ​

ts
import { Charturbo } from 'charturbo';

const canvas = document.querySelector('canvas')!;
// nodeCount / edgeCount 0: 기본 시드 그래프를 만들지 않음
const sdk = await Charturbo.create(canvas, { nodeCount: 0, edgeCount: 0 });

sdk.renderPointCloud3D([
  { name: 'Scan', data: new Float32Array([0, 1, 2, 3, 4, 5]), size: 5 },
  { name: 'Reference', data: [{ x: 1, y: 2, z: 3 }], color: '#ff9933', size: 10 },
], { title: 'XYZ 클라우드', legend: true });

// 궤도 회전 / 팬 / 줌, hover 툴팁, 키보드 탐색
sdk.enableNodeInteractions(canvas, { autoRender: true });
sdk.render();

면도 같은 방식입니다.

ts
sdk.renderSurface3D([{
  name: 'Height',
  data: { columns: 3, rows: 2, xStart: -1, xStep: 1, zStart: 0, zStep: 2,
    y: [0, 1, 0, 1, 2, 1] },
  colormap: 'viridis', lighting: 0.65,
  contours: { show: true, levels: 8, color: '#f8fafc' },
}], { title: 'Y-up 면', showGrid: true });
sdk.render();

두 메서드 모두 { pointCount, seriesCount }를 반환합니다. 좌표는 Y-up입니다. 원본 XYZ 값은 렌더 좌표와 분리해 보관하므로 툴팁, 키보드 포커스, CSV 내보내기는 항상 원본 값을 보여 줍니다. 내부 렌더 좌표는 Float32 정밀도입니다.

데이터 형식 ​

포인트 클라우드 (PointCloud3DSeries.data) ​

다음 형태 중 하나를 받습니다.

형태예
interleaved Float32Array 또는 Float64Arraynew Float32Array([x0, y0, z0, x1, y1, z1])
튜플 배열[[x, y, z], ...]
객체 배열[{ x, y, z }, ...]
길이가 같은 열{ x: ArrayLike<number>, y: ArrayLike<number>, z: ArrayLike<number> }

null 항목이나 유한하지 않은 좌표는 gap입니다. 그리지 않지만 나머지 샘플은 원본 ID를 유지합니다.

면 (Surface3DSeries.data) ​

ts
interface Surface3DGridData {
  columns: number; // 2 이상
  rows: number;    // 2 이상
  xStart: number; xStep: number; // 유한한 시작점, 0이 아닌 step (음수 가능)
  zStart: number; zStep: number;
  y: ArrayLike<number>;           // row-major: y[row * columns + column]
}

유한하지 않은 꼭짓점이 하나라도 있는 셀은 hole이 됩니다.

차트 하나는 전체 최대 4,000,000개 샘플을 담습니다.

옵션 ​

시리즈 옵션 ​

두 시리즈 공통:

옵션타입기본값설명
namestring범례·툴팁 이름
colorColorInput팔레트시리즈 색
visiblebooleantrue초기 표시 여부
opacitynumber시리즈 불투명도

PointCloud3DSeries:

옵션타입기본값설명
dataPointCloud3DDataXYZ 샘플 (위 참고)
sizenumber3점 지름, CSS px
colorBy{ values, colormap?, min?, max? }점마다 scalar 하나를 'viridis'(기본) 또는 'magma'로 매핑합니다. min / max는 색상 범위를 고정합니다

Surface3DSeries:

옵션타입기본값설명
dataSurface3DGridData높이 그리드
colormap'viridis' | 'magma''viridis'높이 컬러맵. colormap 없이 color만 지정하면 단색입니다
yMin / yMaxnumber데이터 범위컬러맵의 색상 범위
wireframebooleanfalse셀을 채우지 않고 격자선으로 그림
lightingnumber0.65평면 확산 조명, 0..1
contoursSurface3DContourOptions높이 등고선, 등고선 참고

차트 옵션 (Scientific3DChartOptions) ​

옵션타입기본값설명
titlestring차트 제목
descriptionstring접근성 설명
legendboolean | ChartLegendOptions시리즈 범례
gridChartGridStyle격자선 스타일
showGridboolean격자 표시
hoverChartHoverStylehover 마커 스타일
axes3d{ x?, y?, z? }축별 범위·이름·눈금·표시 여부
aspect'cube' | 'data' | [x, y, z]'cube'박스 비율
cameraScientific3DCameraOptions투영과 초기 자세

카메라는 정규화된 렌더 공간의 Y-up orbit 카메라입니다. resize 후에도 회전·팬·거리·투영 크기를 유지합니다.

축 범위 · 비율 · 직교 투영 ​

ts
sdk.renderPointCloud3D([{ data: measuredXYZ, size: 4 }], {
  axes3d: {
    x: { name: 'X', min: 0, max: 120, tickCount: 4 },
    y: { min: 0, max: 40, tickCount: 3 },
    z: { min: 0, max: 20, tickCount: 3 },
  },
  aspect: 'data', // 'cube'(기본) 또는 [2, 1, 0.5] 같은 상대 XYZ 박스 길이
  camera: { type: 'orthographic' }, // 기본은 perspective, orthoSize 생략 시 자동 framing
});

axes3d.x / y / z (Scientific3DAxisOptions):

옵션타입기본값설명
namestring축 이름
min / maxnumber데이터 범위데이터 단위의 고정 프레임 범위
tickCountnumber5정수 2..50, 범위 안에서 균등 간격
visiblebooleantruefalse면 이 축의 눈금과 그 방향 격자를 숨김
  • 생략한 끝점은 숨긴 시리즈를 포함한 모든 유한 샘플의 범위를 따릅니다. 양 끝을 모두 지정하면 min < max여야 합니다. 한쪽 끝만 고정했는데 자동 끝점이 그 값을 넘으면 자동 쪽을 고정 값의 5 %(최소 1 단위)만큼 넓힙니다.
  • 고정 범위는 축 프레임입니다. 범위 밖 geometry를 잘라내지 않습니다.
  • 축 라벨은 DOM 요소입니다. 전체 격자는 showGrid로 제어합니다.
  • aspect: 'cube'는 각 축을 [-60, 60]으로 독립 정규화합니다. aspect: 'data'는 축 범위 길이 비율을 써서 같은 데이터 단위를 같은 길이로 표현합니다. 숫자 튜플은 양수인 상대 박스 길이입니다. 가장 긴 변은 120 렌더 단위입니다. 툴팁·CSV·접근성 출력은 원본 XYZ를 유지합니다.
  • 스트리밍 중에는 고정 끝점을 유지하고 자동 끝점만 다시 계산합니다.

camera (Scientific3DCameraOptions):

옵션타입기본값설명
type'perspective' | 'orthographic''perspective'투영 방식
eye[x, y, z]데이터 박스에 맞춤렌더 공간의 눈 위치
target[x, y, z][0, 0, 0]렌더 공간의 orbit 대상. eye가 필요합니다
orthoSizenumber자동초기 세로 반높이 (직교 투영 전용, 양수). 휠 줌에 따라 바뀝니다

직교 피킹은 화면 위치마다 평행 광선을 사용합니다. getCameraState()가 projection과 orthoSize를 알려 줍니다. 다른 차트로 전환하면 perspective로 돌아갑니다.

등고선 ​

면 시리즈의 contours: { show?, levels?, color?, width?, opacity? }는 높이 등고선을 그립니다.

옵션타입기본값설명
showbooleanfalse등고선 표시
levelsnumber | number[]12정수 1..64: yMin..yMax(생략 시 유한 높이 범위) 안쪽을 균등 분할하고 양 끝은 제외합니다. 범위가 한 값이면 그 값 하나입니다. 유한 숫자 배열: 원본 Y 높이를 그대로 사용합니다
colorColorInput'#e2e8f0'선 색
widthnumber1.5CSS px 굵기가 아닌 시각적 가중치입니다. 선은 1px hairline이며 alpha는 min(1, opacity × colorAlpha × (0.75 + 0.15 × min(width, 4)))입니다
opacitynumber0.850..1

잘못된 값은 렌더 전에 거부합니다. 등고선은 렌더링하는 삼각형 위에서 marching triangles로 만들고 면과 같은 좌표 매핑을 쓰므로, 고정 axes3d·모든 aspect·직교 투영에서도 면 위에 놓입니다. hole 셀에는 그리지 않고, 시리즈 가시성·강조를 따르며, 깊이 테스트로 가려지지만 선택 대상은 아닙니다. updateSurface3D는 갱신된 높이와 yMin / yMax로 개수 기반 levels를 다시 계산합니다.

스트리밍 갱신 ​

ts
// 클라우드 시리즈 0에 추가. 반환값은 그 시리즈에 남은 샘플 수
sdk.appendPointCloud3D(0, new Float32Array([1, 2, 3, 4, 5, 6]), { maxPoints: 2400 });
// colorBy 시리즈는 추가한 점마다 colorValues 하나가 필요합니다:
// sdk.appendPointCloud3D(0, xyz, { maxPoints: 2400, colorValues: scalars });
sdk.render();

// 렌더한 면에 적용 (위 클라우드와는 다른 차트)
sdk.updateSurface3D(0, { mode: 'replaceY', y: heights, yMin: -1, yMax: 1 });
sdk.updateSurface3D(0, { mode: 'appendColumns', columns: 1, y: nextColumn });
sdk.updateSurface3D(0, { mode: 'appendRows', rows: 1, y: nextRow, scrollZ: false });
sdk.render();

appendPointCloud3D(seriesIndex, data, options?) ​

renderPointCloud3D와 같은 XYZ 형태를 받고, 시리즈에 남은 샘플 수를 반환합니다.

옵션타입기본값설명
maxPointsnumber0 (무제한)가장 최근 N개만 유지
colorValuesArrayLike<number>시리즈가 colorBy를 쓰면 필수(추가한 점마다 하나), 아니면 전달할 수 없음

컬러맵과 고정 min / max는 유지하고, 자동 색상 범위는 남은 scalar로 다시 계산합니다.

updateSurface3D(seriesIndex, update) ​

해당 시리즈의 { columns, rows, pointCount }를 반환합니다.

mode필드설명
'replaceY'y, yMin?, yMax?높이 전체(row-major, rows * columns)를 교체합니다. yMin / yMax는 이후 갱신에도 유지되고, 생략하면 현재 설정을 유지합니다
'appendColumns'columns, y, scrollX?y는 column-major strip: y[newColumn * rows + row]
'appendRows'rows, y, scrollZ?y는 row-major block: y[newRow * columns + column]

scrollX / scrollZ가 true(기본)이면 그리드 크기를 유지합니다. 가장 오래된 열/행을 버리고 원점을 추가 개수 × step만큼 옮깁니다. 창보다 큰 청크는 마지막 창 크기만 남깁니다. false면 원점을 고정하고 그리드를 늘립니다. 음수 step도 지원합니다.

갱신 규칙 ​

  • 갱신은 원자적입니다. 길이·범위·종류·시리즈 인덱스가 잘못됐거나 backend가 지원하지 않으면 현재 데이터를 바꾸지 않고 거부합니다.
  • 입력은 복사하므로 이후 배열을 바꿔도 차트에 영향이 없습니다. 청크 하나와 차트 전체는 각각 최대 4,000,000개 샘플입니다. 빈 append도 허용합니다(클라우드의 더 작은 maxPoints는 적용됨).
  • 카메라·스타일·가시성·강조·고정 축 끝점은 유지합니다. 자동 범위는 남은 데이터 전체로 다시 계산합니다. ID는 현재 시리즈 순서와 샘플 위치로 다시 매깁니다. 고정된 툴팁이나 키보드 포커스는 해제하고, CSV·접근성·툴팁은 갱신된 XYZ를 사용합니다.
  • 현재 구현은 SDK 메타데이터를 복사하고 변경된 시리즈를 WebAssembly에 다시 전달하며, Rust가 범위와 geometry를 다시 계산합니다. GPU 부분 업로드나 O(추가량) 비용은 보장하지 않습니다.

상호작용과 내보내기 ​

  • enableNodeInteractions(canvas, { autoRender: true })를 호출하면 궤도 회전(왼쪽 드래그), 팬(Shift / 오른쪽 / 가운데 드래그), 줌(휠)을 쓸 수 있습니다. 카메라 조작은 3D 네트워크 그래프를 참고하세요.
  • 포인트 클라우드 선택은 허용 반경 안에서 가장 앞에 있는 샘플을 반환합니다. 면 선택은 삼각형과 교차를 계산해 point3d: { x, y, z, column, row }를 반환합니다. wireframe 면도 셀 내부를 선택합니다.
  • 키보드 탐색과 CSV는 보간하지 않은 원본 꼭짓점 XYZ를 제공합니다.
  • SVG 내보내기는 래스터 이미지로 대체하고, PNG와 PDF는 현재 3D 화면을 담습니다.

프레임워크에서 사용 ​

React와 Vue는 같은 options로 type="pointCloud3d", type="surface3d"를 지원합니다. 갱신하려면 전체 데이터를 render API에 다시 전달합니다. 스트리밍에는 onReady(sdk) 또는 컴포넌트 ref의 whenReady()로 얻은 SDK에서 appendPointCloud3D / updateSurface3D와 render()를 호출하고, data / options prop 참조를 그대로 유지하세요. 새 참조를 넘기면 전체 render API가 다시 실행됩니다.

참고 ​

  • 현재 hover는 모든 점/셀을 검사합니다.
  • 반투명 geometry는 깊이를 기록하지만 깊이 정렬은 하지 않습니다. 정확한 다중 투명면 합성은 지원하지 않습니다.
  • 아직 제공하지 않는 기능: 3D 브러시와 동기화, 등고선 라벨, FOV / near / far / up 카메라 설정, nice ticks, GPU glyph 라벨, 독립 박스 토글.

함께 보기 ​

MIT License