Demo of Perspective.
A ray-tracing engine written as an ExprTK Expression within Perspective.
Demo of Perspective.
A ray-tracing engine written as an ExprTK Expression within Perspective.
| perspective-viewer { | |
| overflow: visible; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| } | |
| perspective-viewer[theme="Material Light"], | |
| perspective-viewer[theme="Material Dark"] { | |
| --d3fc-positive--gradient: linear-gradient( | |
| #94d0ff, | |
| #8795e8, | |
| #966bff, | |
| #ad8cff, | |
| #c774e8, | |
| #c774a9, | |
| #ff6ad5, | |
| #ff6a8b, | |
| #ff8b8b, | |
| #ffa58b, | |
| #ffde8b, | |
| #cdde8b, | |
| #8bde8b, | |
| #20de8b | |
| ); | |
| } |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta | |
| name="viewport" | |
| content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no" | |
| /> | |
| <script type="module" src="https://cdn.jsdelivr.net/npm/@finos/perspective-viewer@1.8.1/dist/cdn/perspective-viewer.js"></script> | |
| <script type="module" src="https://cdn.jsdelivr.net/npm/@finos/perspective-viewer-datagrid@1.8.1/dist/cdn/perspective-viewer-datagrid.js"></script> | |
| <script type="module" src="https://cdn.jsdelivr.net/npm/@finos/perspective-viewer-d3fc@1.8.1/dist/cdn/perspective-viewer-d3fc.js"></script> | |
| <link rel="stylesheet" crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/@finos/perspective-viewer/dist/css/themes.css" /> | |
| <link rel="preload" href="https://cdn.jsdelivr.net/npm/@finos/perspective/dist/cdn/perspective.cpp.wasm" as="fetch" type="application/wasm" crossorigin="anonymous" /> | |
| <link rel="preload" href="https://cdn.jsdelivr.net/npm/@finos/perspective-viewer/dist/cdn/perspective_bg.wasm" as="fetch" type="application/wasm" crossorigin="anonymous" /> | |
| <link rel="preload" href="https://cdn.jsdelivr.net/npm/@finos/perspective/dist/cdn/perspective.worker.js" as="fetch" type="application/javascript" crossorigin="anonymous" /> | |
| <script type="module" src="index.js"></script> | |
| <link rel="stylesheet" href="index.css" /> | |
| </head> | |
| <body> | |
| <perspective-viewer id="viewer"></perspective-viewer> | |
| </body> | |
| </html> |
| /****************************************************************************** | |
| * | |
| * Copyright (c) 2017, the Perspective Authors. | |
| * | |
| * This file is part of the Perspective library, distributed under the terms of | |
| * the Apache License 2.0. The full license can be found in the LICENSE file. | |
| * | |
| */ | |
| import perspective from "https://cdn.jsdelivr.net/npm/@finos/perspective@1.8.1/dist/cdn/perspective.js"; | |
| const WIDTH = 200; | |
| const HEIGHT = 200; | |
| const vertices = [ | |
| [-100, -100, -100], | |
| [-100, -100, 100], | |
| [-100, 100, 100], | |
| [100, 100, -100], | |
| [-100, -100, -100], | |
| [-100, 100, -100], | |
| [100, -100, 100], | |
| [-100, -100, -100], | |
| [100, -100, -100], | |
| [100, 100, -100], | |
| [100, -100, -100], | |
| [-100, -100, -100], | |
| [-100, -100, -100], | |
| [-100, 100, 100], | |
| [-100, 100, -100], | |
| [100, -100, 100], | |
| [-100, -100, 100], | |
| [-100, -100, -100], | |
| [-100, 100, 100], | |
| [-100, -100, 100], | |
| [100, -100, 100], | |
| [100, 100, 100], | |
| [100, -100, -100], | |
| [100, 100, -100], | |
| [100, -100, -100], | |
| [100, 100, 100], | |
| [100, -100, 100], | |
| [100, 100, 100], | |
| [100, 100, -100], | |
| [-100, 100, -100], | |
| [100, 100, 100], | |
| [-100, 100, -100], | |
| [-100, 100, 100], | |
| [100, 100, 100], | |
| [-100, 100, 100], | |
| [100, -100, 100], | |
| ]; | |
| const colors = [3, 1, 2, 1, 3, 2, 6, 4, 4, 5, 5, 6]; | |
| function generate_scene() { | |
| let vertices2 = []; | |
| let colors2 = []; | |
| for (let i = 0; i < 50; i++) { | |
| const x_offset = Math.random() * 2000 - 1000; | |
| const y_offset = Math.random() * 2000 - 1000; | |
| const z_offset = Math.random() * 2000; | |
| for (const v in vertices) { | |
| const vertex = structuredClone(vertices[v]); | |
| vertex[0] += x_offset; | |
| vertex[1] += y_offset; | |
| vertex[2] += z_offset; | |
| vertices2.push(vertex); | |
| if (v % 3 === 0) { | |
| colors2.push(colors[(v / 3) % colors.length]); | |
| } | |
| } | |
| } | |
| return [vertices2, colors2]; | |
| } | |
| function generate_mandelbrot() { | |
| const [vertices2, colors2] = generate_scene(); | |
| return ` | |
| // color | |
| var d[3] := {floor("index" / ${HEIGHT}) - ${HEIGHT} / 2, "index" % ${HEIGHT} - ${HEIGHT} / 2, 50}; | |
| var p[3] := {0, 0, -500}; | |
| var vs[9 * ${vertices2.flat().length / 9}] := {${vertices2.flat().join(", ")}}; | |
| var cs[${colors2.flat().length}] := {${colors2.flat().join(", ")}}; | |
| var color := 0; | |
| var depth := 1000000; | |
| for (var i := 0; i < ${vertices2.flat().length / 9}; i += 1) { | |
| var v0[3] := {vs[i * 9], vs[i * 9 + 1], vs[i * 9 + 2]}; | |
| var v1[3] := {vs[i * 9 + 3], vs[i * 9 + 4], vs[i * 9 + 5]}; | |
| var v2[3] := {vs[i * 9 + 6], vs[i * 9 + 7], vs[i * 9 + 8]}; | |
| var e1[3]; | |
| diff3(v1, v0, e1); | |
| var e2[3]; | |
| diff3(v2, v0, e2); | |
| var h[3]; | |
| cross_product3(d, e2, h); | |
| var a := dot_product3(e1, h); | |
| if (a < -0.000001 or a > 0.000001) { | |
| var f := 1 / a; | |
| var s[3]; | |
| diff3(p, v0, s); | |
| var u := f * dot_product3(s, h); | |
| if (u > 0 and u < 1) { | |
| var q[3]; | |
| cross_product3(s, e1, q); | |
| var v := f * dot_product3(d, q); | |
| if (v > 0 and u + v < 1) { | |
| var t := f * dot_product3(e2, q); | |
| if (t > -0.0000001) { | |
| var t2 := 1 - u - v; | |
| var d1[3] := { | |
| t2 * v0[0] + u * v1[0] + v * v2[0], | |
| t2 * v0[1] + u * v1[1] + v * v2[1], | |
| t2 * v0[2] + u * v1[2] + v * v2[2] | |
| }; | |
| var d2[3]; | |
| diff3(d1, p, d2); | |
| var dist := norm3(d2); | |
| if (dist < depth) { | |
| depth := dist; | |
| color := cs[i]; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }; | |
| color | |
| `; | |
| } | |
| function generate_layout() { | |
| return { | |
| plugin: "Heatmap", | |
| settings: true, | |
| group_by: [`floor("index" / ${HEIGHT}) - ${HEIGHT} / 2`], | |
| split_by: [`"index" % ${HEIGHT} - ${HEIGHT} / 2`], | |
| columns: ["color"], | |
| expressions: [ | |
| generate_mandelbrot().trim(), | |
| `floor("index" / ${HEIGHT}) - ${HEIGHT} / 2`, | |
| `"index" % ${HEIGHT} - ${HEIGHT} / 2`, | |
| ], | |
| }; | |
| } | |
| async function generate_data(table) { | |
| let json = new Array(WIDTH * HEIGHT); | |
| for (let x = 0; x < WIDTH; ++x) { | |
| for (let y = 0; y < HEIGHT; ++y) { | |
| const index = x * HEIGHT + y; | |
| json[index] = { | |
| index, | |
| }; | |
| } | |
| } | |
| await table.replace(json); | |
| } | |
| window.addEventListener("DOMContentLoaded", async function () { | |
| const heatmap_plugin = await window.viewer.getPlugin("Heatmap"); | |
| heatmap_plugin.max_cells = 100000; | |
| const worker = perspective.worker(); | |
| const table = await worker.table({ | |
| index: "integer", | |
| }); | |
| generate_data(table); | |
| window.viewer.load(Promise.resolve(table)); | |
| await window.viewer.restore(generate_layout()); | |
| }); |