One-ring neighborhood

Interactive example using Three.js and three-mesh-halfedge.

by mvgician

HTML

<script type="importmap">
	{
		"imports": {
			"three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
      "three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/",
      "three-mesh-halfedge": "https://cdn.jsdelivr.net/npm/[email protected]/build/index.esm.js"
      }
	}
</script>

<div id="wrapper">
  <button type="button" id="button">
  <span class="primary">halfedge</span> = <span class="primary">halfedge</span>.<span class="secondary">twin</span>.<span class="tertiary">next</span>
  </button> 
</div>

CSS

body {
  padding: 0;
  margin: 0;
}

#wrapper {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  margin: auto;
  overflow: hidden;
}

.primary{
  color: #ddc000;
}

.secondary{
  color: #f78e20;
}

.tertiary{
  color: #e21;
}

JavaScript

import * as THREE from 'three'
import { HalfedgeDS, Vertex } from 'three-mesh-halfedge'

// renderer
const width = window.innerWidth
const height = window.innerHeight - 30 // hardcoded button height
const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setPixelRatio(2) // smoother (and thinner) lines
renderer.setSize(width, height)
document.getElementById('wrapper').prepend(renderer.domElement)

// camera
const aspect = (width / height)
const frustum = 1.25
const camera = new THREE.OrthographicCamera(-frustum * 0.5 * aspect, frustum * 0.5 * aspect, 0.5 * frustum, -frustum * 0.5, 0.5, 20)
camera.position.z = 5

// scene
const scene = new THREE.Scene()
scene.background = new THREE.Color(0xffffff)

// utils
const dir = new THREE.Vector3()
const triangle = new THREE.Triangle()
const sphere = new THREE.SphereGeometry(0.025)
const primaryColor = 0xFFFE000
const secondaryColor = 0xF78E20
const tertiaryColor = 0xee2211
const defaultColor = 0x333333
const backgroundColor = 0x111111

// simple hexagonal mesh
const geometry = new THREE.CircleGeometry(0.5, 6)
const mesh = new THREE.Mesh(geometry, new THREE.MeshNormalMaterial())
const wireframe = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: backgroundColor, wireframe: true }))
scene.add(mesh, wireframe)

// half-edge data structure
const struct = new HalfedgeDS()
struct.setFromGeometry(geometry)

// one-ring neighbor generator function
Vertex.prototype.getNeighbors = function * () {
  const start = this.halfedge
  let halfedge = start
  do {
    yield halfedge.twin.vertex
    halfedge = halfedge.twin.next
  } while (start !== halfedge)
  return null
}

// creates a sphere centered in the position of "vertex"
function createSphere (vertex) {
  const mesh = new THREE.Mesh(sphere, new THREE.MeshBasicMaterial({ color: backgroundColor }))
  mesh.position.copy(vertex.position)
  vertex.highlight = function (color) { mesh.material.color.setHex(color) }
  scene.add(mesh)
}

// create a sphere for...