Skip to content

Commit

Permalink
threedlib (#7)
Browse files Browse the repository at this point in the history
* chore: threedlib init

* chore: add test cases & examples

* fix: move shape & mark utils here
  • Loading branch information
xiaoiver authored Nov 13, 2023
1 parent bdaa6d7 commit 7bd1576
Show file tree
Hide file tree
Showing 31 changed files with 1,813 additions and 0 deletions.
5 changes: 5 additions & 0 deletions 3d/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
.DS_Store

esm
lib
3 changes: 3 additions & 0 deletions 3d/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"printWidth": 120
}
27 changes: 27 additions & 0 deletions 3d/__tests__/data/alphabet.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
letter,frequency
A,0.08167
B,0.01492
C,0.02782
D,0.04253
E,0.12702
F,0.02288
G,0.02015
H,0.06094
I,0.06966
J,0.00153
K,0.00772
L,0.04025
M,0.02406
N,0.06749
O,0.07507
P,0.01929
Q,0.00095
R,0.05987
S,0.06327
T,0.09056
U,0.02758
V,0.00978
W,0.0236
X,0.0015
Y,0.01974
Z,0.00074
407 changes: 407 additions & 0 deletions 3d/__tests__/data/cars2.csv

Large diffs are not rendered by default.

46 changes: 46 additions & 0 deletions 3d/__tests__/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<!doctype html>
<head>
<meta charset="utf-8" />
</head>
<script type="module">
import * as plots from "./plots/index.ts";

const select = createSelect(() => {
const { value } = select;
history.pushState({ value }, "", `?name=${value}`);
render();
});
const options = Object.keys(plots).map(createOption);
document.body.append(select);
select.append(...options);

const initialValue = new URL(location).searchParams.get("name");
if (plots[initialValue]) select.value = initialValue;

let preClear = null;
let preNode = null;
render();

function createSelect(onchange) {
const select = document.createElement("select");
select.style.margin = "1em";
select.onchange = onchange;
select.style.display = "block";
return select;
}

function createOption(key) {
const option = document.createElement("option");
option.value = key;
option.textContent = key;
return option;
}

async function render() {
if (typeof preClear === "function") preClear();
const fn = plots[select.value];
const [finished, destroy] = await fn({ container: "container" });
preClear = destroy;
}
</script>
<div id="container"></div>
77 changes: 77 additions & 0 deletions 3d/__tests__/plots/bar-perspective.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { CameraType } from "@antv/g";
import { Renderer as WebGLRenderer } from "@antv/g-webgl";
import { Plugin as ThreeDPlugin, DirectionalLight } from "@antv/g-plugin-3d";
import { Plugin as ControlPlugin } from "@antv/g-plugin-control";
import { Runtime, extend, corelib } from "@antv/g2";
import { threedlib } from "../../src";

export function BarPerspective(context) {
const { container } = context;

// Create a WebGL renderer.
const renderer = new WebGLRenderer();
renderer.registerPlugin(new ThreeDPlugin());
renderer.registerPlugin(new ControlPlugin());

const Chart = extend(Runtime, { ...corelib(), ...threedlib() });
const chart = new Chart({
container,
renderer,
depth: 400,
});

const data: { x: string; z: string; y: number; color: number }[] = [];
for (let x = 0; x < 5; ++x) {
for (let z = 0; z < 5; ++z) {
data.push({
x: `x-${x}`,
z: `z-${z}`,
y: 10 - x - z,
color: Math.random() < 0.33 ? 0 : Math.random() < 0.67 ? 1 : 2,
});
}
}

chart
.interval3D()
.data({
type: "inline",
value: data,
})
.encode("x", "x")
.encode("y", "y")
.encode("z", "z")
.encode("color", "color")
.encode("shape", "cube")
.coordinate({ type: "cartesian3D" })
.scale("x", { nice: true })
.scale("y", { nice: true })
.scale("z", { nice: true })
.legend(false)
.axis("x", { gridLineWidth: 2 })
.axis("y", { gridLineWidth: 2, titleBillboardRotation: -Math.PI / 2 })
.axis("z", { gridLineWidth: 2 })
.style("opacity", 0.7)
.style("cursor", "pointer");

const finished = chart.render().then(() => {
const { canvas } = chart.getContext();
if (!canvas) return;
const camera = canvas.getCamera();
camera.setPerspective(0.1, 5000, 50, 1280 / 960);
camera.setType(CameraType.ORBITING);
camera.rotate(-20, -20, 0);

// Add a directional light into scene.
const light = new DirectionalLight({
style: {
intensity: 2.5,
fill: "white",
direction: [-1, 0, 1],
},
});
canvas.appendChild(light);
});

return { finished, destroy: () => chart.destroy() };
}
76 changes: 76 additions & 0 deletions 3d/__tests__/plots/bar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { CameraType } from "@antv/g";
import { Renderer as WebGLRenderer } from "@antv/g-webgl";
import { Plugin as ThreeDPlugin, DirectionalLight } from "@antv/g-plugin-3d";
import { Plugin as ControlPlugin } from "@antv/g-plugin-control";
import { Runtime, extend, corelib } from "@antv/g2";
import { threedlib } from "../../src";

export function Bar(context) {
const { container } = context;

// Create a WebGL renderer.
const renderer = new WebGLRenderer();
renderer.registerPlugin(new ThreeDPlugin());
renderer.registerPlugin(new ControlPlugin());

const Chart = extend(Runtime, { ...corelib(), ...threedlib() });
const chart = new Chart({
container,
renderer,
depth: 400,
padding: 50,
});

const data: { x: string; z: string; y: number; color: number }[] = [];
for (let x = 0; x < 5; ++x) {
for (let z = 0; z < 5; ++z) {
data.push({
x: `x-${x}`,
z: `z-${z}`,
y: 10 - x - z,
color: Math.random() < 0.33 ? 0 : Math.random() < 0.67 ? 1 : 2,
});
}
}

chart
.interval3D()
.data({
type: "inline",
value: data,
})
.encode("x", "x")
.encode("y", "y")
.encode("z", "z")
.encode("color", "color")
.encode("shape", "cube")
.coordinate({ type: "cartesian3D" })
.scale("x", { nice: true })
.scale("y", { nice: true })
.scale("z", { nice: true })
.legend(false)
.axis("x", { gridLineWidth: 2 })
.axis("y", { gridLineWidth: 2, titleBillboardRotation: -Math.PI / 2 })
.axis("z", { gridLineWidth: 2 })
.style("opacity", 0.7);

const finished = chart.render().then(() => {
const { canvas } = chart.getContext();
if (!canvas) return;
const camera = canvas.getCamera();
camera.setType(CameraType.ORBITING);
camera.rotate(-20, -20, 0);

// Add a directional light into scene.
const light = new DirectionalLight({
style: {
intensity: 2.5,
fill: "white",
direction: [-1, 0, 1],
},
});
canvas.appendChild(light);
});

return { finished, destroy: () => chart.destroy() };
}
7 changes: 7 additions & 0 deletions 3d/__tests__/plots/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export { Scatter } from "./scatter";
export { ScatterPerspective } from "./scatter-perspective";
export { ScatterLegend } from "./scatter-legend";
export { Line } from "./line";
export { LinePerspective } from "./line-perspective";
export { Bar } from "./bar";
export { BarPerspective } from "./bar-perspective";
64 changes: 64 additions & 0 deletions 3d/__tests__/plots/line-perspective.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { CameraType } from "@antv/g";
import { Renderer as WebGLRenderer } from "@antv/g-webgl";
import { Plugin as ThreeDPlugin } from "@antv/g-plugin-3d";
import { Plugin as ControlPlugin } from "@antv/g-plugin-control";
import { Runtime, extend, corelib } from "@antv/g2";
import { threedlib } from "../../src";

export function LinePerspective(context) {
const { container } = context;

// Create a WebGL renderer.
const renderer = new WebGLRenderer();
renderer.registerPlugin(new ThreeDPlugin());
renderer.registerPlugin(new ControlPlugin());

const Chart = extend(Runtime, { ...corelib(), ...threedlib() });
const chart = new Chart({
container,
renderer,
depth: 400,
});

/**
* 3D Spiral
* @see https://plotly.com/javascript/3d-line-plots/
*/
const pointCount = 500;
let r: number;
const data: { x: number; y: number; z: number }[] = [];

for (let i = 0; i < pointCount; i++) {
r = i * (pointCount - i);
data.push({
x: r * Math.cos(i / 30),
y: r * Math.sin(i / 30),
z: i,
});
}

chart
.line3D()
.data(data)
.encode("x", "x")
.encode("y", "y")
.encode("z", "z")
.encode("size", 4)
.coordinate({ type: "cartesian3D" })
.scale("x", { nice: true })
.scale("y", { nice: true })
.scale("z", { nice: true })
.legend(false)
.axis("x", { gridLineWidth: 2 })
.axis("y", { gridLineWidth: 2, titleBillboardRotation: -Math.PI / 2 })
.axis("z", { gridLineWidth: 2 });

const finished = chart.render().then(() => {
const { canvas } = chart.getContext();
const camera = canvas!.getCamera();
camera.setPerspective(0.1, 5000, 45, 500 / 500);
camera.setType(CameraType.ORBITING);
});

return { finished, destroy: () => chart.destroy() };
}
64 changes: 64 additions & 0 deletions 3d/__tests__/plots/line.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { CameraType } from "@antv/g";
import { Renderer as WebGLRenderer } from "@antv/g-webgl";
import { Plugin as ThreeDPlugin } from "@antv/g-plugin-3d";
import { Plugin as ControlPlugin } from "@antv/g-plugin-control";
import { Runtime, extend, corelib } from "@antv/g2";
import { threedlib } from "../../src";

export function Line(context) {
const { container } = context;

// Create a WebGL renderer.
const renderer = new WebGLRenderer();
renderer.registerPlugin(new ThreeDPlugin());
renderer.registerPlugin(new ControlPlugin());

const Chart = extend(Runtime, { ...corelib(), ...threedlib() });
const chart = new Chart({
container,
renderer,
depth: 400,
});

/**
* 3D Line
* @see https://plotly.com/javascript/3d-line-plots/
*/
const pointCount = 31;
let r: number;
const data: { x: number; y: number; z: number }[] = [];

for (let i = 0; i < pointCount; i++) {
r = 10 * Math.cos(i / 10);
data.push({
x: r * Math.cos(i),
y: r * Math.sin(i),
z: i,
});
}

chart
.line3D()
.data(data)
.encode("x", "x")
.encode("y", "y")
.encode("z", "z")
.encode("size", 4)
.coordinate({ type: "cartesian3D" })
.scale("x", { nice: true })
.scale("y", { nice: true })
.scale("z", { nice: true })
.legend(false)
.axis("x", { gridLineWidth: 2 })
.axis("y", { gridLineWidth: 2, titleBillboardRotation: -Math.PI / 2 })
.axis("z", { gridLineWidth: 2 });

const finished = chart.render().then(() => {
const { canvas } = chart.getContext();
const camera = canvas!.getCamera();
camera.setType(CameraType.ORBITING);
camera.rotate(-20, -20, 0);
});

return { finished, destroy: () => chart.destroy() };
}
Loading

0 comments on commit 7bd1576

Please sign in to comment.