JSFiddle - React, Tailwind, and code Playground

by tfoller

HTML

<canvas id="main_canvas" width="550" height="550"></canvas>
<div id="ui">
  <button id="psw">...</button>
</div>

CSS

* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

.noselect {
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

#ctrl_over {
  position: absolute;
  opacity: 0.5;
  bottom: 9px;
  right: 5px;

  color: #aaa;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 13px;
}

#ctrl_sw {
  border: solid 1px #aaa;
  padding: 3px;
  margin-left: 10px;
  cursor: pointer;
  font-size: 11px;
  text-transform: uppercase;
}

#canv_wrap {
  position:relative;
  margin: 0px;
  padding: 0px;
  display: inline-block;
}

body {
  background: #112233;
  width: 100vw;
  height: 100vh;
}

button {
  padding: 3px;
  margin-left: 10px;
  color: #333;
  filter: invert(100%);
  cursor: pointer;
}

canvas {
  display: block;
  border: solid 1px grey;
  box-sizing: content-box;
  margin: 10px;
}

JavaScript

import * as THREE from 'https://alikim.com/_v1_jsm/three.module.js'

import * as CONTROLS from 'https://alikim.com/_v1_jsm/controls.js'

import { log, slog, get, getHTML } from 'https://alikim.com/_v1_jsm/utils.js'

const html = getHTML();

// 3D

const canvas = get('main_canvas');
const [w, h] = [canvas.width, canvas.height];
const renderer = new THREE.WebGLRenderer({
  antialias: true,
  canvas: canvas,
});
renderer.setSize(w, h);

const [near, far] = [1, 700];
const mid = -0.5 * (near + far);
const camera = new THREE.PerspectiveCamera(45, w / h, near, far);

const scene = new THREE.Scene();
scene.background = new THREE.Color(0, 0, 0.3);

const generatePoints = (radius, numPoints) => {
  const points = [];
  for (let i = 0; i < numPoints; i++) {
    const angle = (i / numPoints) * Math.PI * 2;
    const x = radius * Math.cos(angle);
    const y = radius * Math.sin(angle);
    const z = 0;    
    points.push(new THREE.Vector3(x, y, z));
  }
  return points;
};

const points = generatePoints(50, 20);

const geo = new THREE.BufferGeometry().setFromPoints(points);
const mat = new THREE.MeshBasicMaterial();

const pos_attr = geo.attributes.position;
const vert = pos_attr.array;

const planar = new THREE.LineLoop(geo, mat);
planar.position.z = mid;
scene.add(planar);

const hlp = new THREE.AxesHelper(100);
hlp.position.z = mid;
scene.add(hlp);

function render() { renderer.render(scene, camera) }

function maybeRender() { 

  for(let i = 0; i < points.length; i++) {
    const pnt = points[i].clone().applyQuaternion(camera.quaternion);
    const off = i * 3;
    vert[off] = pnt.x;
    vert[off + 1] = pnt.y;
    vert[off + 2] = pnt.z;
  }
  pos_attr.needsUpdate = true;

  render();
}

const createControls = () => {
  CONTROLS.create({
    cont: canvas, 
    cam: camera,
    type: 'Trackball', 
    overlay: true, 
    lookAt: [0, 0, mid],
    callback: maybeRender,
    sens: {p: 0.005},
  });
};

createControls();
render();