Ball on swing simulation

by PhilQ

HTML

<script type="importmap">
	{
		"imports": {
			"three": "https://unpkg.com/[email protected]/build/three.module.js",
			"OrbitControls": "https://unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js"
		}
	}
</script>

<canvas id="canvas1"></canvas>

CSS

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

body {
	background: #1f2227;
}

canvas {
	display: block;
	margin: 150px auto 0;
	background: #1a1d21;
}

JavaScript

import * as THREE from 'three';

var upload,
	canvas,
	context,
	width = 600,
	height = 500,
	this_time,
	last_time,
	frame_time,
	duration = 0,
	is_running = true
;
var PI = Math.PI;
var TWO_PI = 2*Math.PI;
var gravity = new THREE.Vector3(0, 9.807/0.1, 0);
var orb;

class ball {
	constructor(params) {
		this.r = params.r;
		this.position = params.position;
		this.velocity = params.velocity;
		this.acceleration = params.acceleration;

		this.anchor = params.anchor;
		this.anchorDistanceMax = params.anchorDistanceMax;
		this.anchorDistanceMaxSq = Math.pow(this.anchorDistanceMax, 2);
		this.anchorSphere = new THREE.Sphere( this.anchor, this.anchorDistanceMax );

		this.mass = params.mass ? params.mass : 1;
		this.invMass = 1 / this.mass;

		this.new_position = this.position.clone();
		this._intersection = new THREE.Vector3();
		this._v1 = new THREE.Vector3();
		this._v2 = new THREE.Vector3();
		this._dp = new THREE.Vector3();
		this._ray = new THREE.Ray();
	}

	applyForce(force) {
		this.acceleration.addScaledVector(force, this.invMass);
	}

	update(dt) {
		// Accelerate
		this.velocity.addScaledVector( this.acceleration, dt );

		// Move
		this._dp.copy( this.velocity ).multiplyScalar( dt );

		// New position starting point
		this.new_position.copy( this.position );

		// Move until anchor line is at maximum (if needed)
		if (
			this._ray
			.set( this.position, this.velocity.clone().normalize() )
			.intersectSphere( this.anchorSphere, this._intersection )
		) {
			this._v1.copy( this._intersection ).sub( this.position );
			if ( this._v1.lengthSq() >= this._dp.lengthSq() ) {
				// Within or at max
				this.new_position.add( this._dp );
				this._dp.set(0, 0, 0);
			} else {
	//TODO? Bounce when reaching max: reflect velocity on anchor line normal?
				// Surpassing max
				this.new_position.copy( this._intersection );
				this._dp.sub( this._v1 );
			}
		}

		if ( this._dp.lengthSq() > 0 ) {
			// Convert velocity to angular velocity
			// See:...