user3951408
user3951408

Reputation:

Points on a plane in Three.js

I'm trying to write a function that will take three vertices and return a random point anywhere on the bounded plane formed by those vertices. So the function would be something like this:

function randomPointOnPlane(vertex1, vertex2, vertex3) {
    ... 
    return THREE.Vector3( x, y, z ); // random point on bounded plane
}

My approach has been to try to solve the system of equations given by ax+by+cz+d=0 for each vertex defining the bounded the plane. I haven't gotten very far with this though! How should I go about this? Thanks

Upvotes: 0

Views: 2333

Answers (3)

Stéphane Laurent
Stéphane Laurent

Reputation: 84709

To uniformly sample in a triangle, use this function:

function randomInTriangle(v1, v2, v3) {
  var r1 = Math.random();
  var r2 = Math.sqrt(Math.random());
  var a = 1 - r2;
  var b = r2 * (1 - r1);
  var c = r1 * r2;
  return (v1.clone().multiplyScalar(a)).add(v2.clone().multiplyScalar(b)).add(v3.clone().multiplyScalar(c));

function randomInTriangle(v1, v2, v3) {
  var r1 = Math.random();
  var r2 = Math.sqrt(Math.random());
  var a = 1 - r2;
  var b = r2 * (1 - r1);
  var c = r1 * r2;
  return (v1.clone().multiplyScalar(a)).add(v2.clone().multiplyScalar(b)).add(v3.clone().multiplyScalar(c));
}

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
scene.add(camera);

var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

var v0 = new THREE.Vector3(0, 0, 1);
var v1 = new THREE.Vector3(0, 1, 0);
var v2 = new THREE.Vector3(1, 0, 0);

var material = new THREE.PointsMaterial({
  color: 0xff0000,
  size: 0.05,
  opacity: 1
});
var geometry = new THREE.Geometry();
for (var i = 0; i < 100; i++) {
  var pt = randomInTriangle(v0, v1, v2);
  geometry.vertices.push(pt);
}
var pointCloud = new THREE.Points(geometry, material);
scene.add(pointCloud); 

function render() {
  renderer.render(scene, camera);
  pointCloud.rotation.x += 0.01;
  pointCloud.rotation.y += 0.01;
  requestAnimationFrame(render);
}

render();
<script src="https://threejs.org/build/three.min.js"></script>

Upvotes: 0

manthrax
manthrax

Reputation: 5036

//Heres an algo based on barycentric coordinates... https://adamswaab.wordpress.com/2009/12/11/random-point-in-a-triangle-barycentric-coordinates/

function randomPointInTriangle(vertex1, vertex2, vertex3) {
  var edgeAB = vertex2.clone().sub(vertex1)
  var edgeAC = vertex3.clone().sub(vertex1)
  var r = Math.random();
  var s = Math.random();

  if (r + s >= 1) {
    r = 1 - r
    s = 1 - s
  }
  return edgeAB.multiplyScalar(r).add(edgeAC.multiplyScalar(s)).add(vertex1)
  // random point in triangle
}

var renderer = new THREE.WebGLRenderer();
var w = 300;
var h = 200;
renderer.setSize(w, h);
document.body.appendChild(renderer.domElement);

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
  45, // Field of view
  w / h, // Aspect ratio
  0.1, // Near
  10000 // Far
);
camera.position.set(15, 10, 15);
camera.lookAt(scene.position);
controls = new THREE.OrbitControls(camera, renderer.domElement);

var light = new THREE.PointLight(0xFFFF00);
light.position.set(20, 20, 20);
scene.add(light);
var light1 = new THREE.AmbientLight(0x808080);
light1.position.set(20, 20, 20);
scene.add(light1);
var light2 = new THREE.PointLight(0x00FFFF);
light2.position.set(-20, 20, -20);
scene.add(light2);
var light3 = new THREE.PointLight(0xFF00FF);
light3.position.set(-20, -20, -20);
scene.add(light3);

var sphereGeom = new THREE.SphereGeometry(0.1, 8, 8);
sphereGeom.verticesNeedUpdate = true;
sphereGeom.computeFaceNormals();
sphereGeom.computeVertexNormals();


function randomPointInTriangle(vertex1, vertex2, vertex3) {
  var edgeAB = vertex2.clone().sub(vertex1)
  var edgeAC = vertex3.clone().sub(vertex1)
  var r = Math.random();
  var s = Math.random();

  if (r + s >= 1) {
    r = 1 - r
    s = 1 - s
  }
  return edgeAB.multiplyScalar(r).add(edgeAC.multiplyScalar(s)).add(vertex1)
  // random point in triangle
}


var material = new THREE.MeshLambertMaterial({
  color: 0x808080
});
var v0 = new THREE.Vector3(0, 0, -2)
var v1 = new THREE.Vector3(2, 0, 1)
var v2 = new THREE.Vector3(-2, 0, 1)

for (var i = 0; i < 500; i++) {
  var mesh = new THREE.Mesh(sphereGeom, material);
  mesh.position.copy(randomPointInTriangle(v0, v1, v2))
  scene.add(mesh);
}

renderer.setClearColor(0xdddddd, 1);


(function animate() {
  requestAnimationFrame(animate);
  controls.update();
  renderer.render(scene, camera);
})();
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/controls/OrbitControls.js"></script>

Upvotes: 0

Mugen87
Mugen87

Reputation: 31086

I suggest the following workflow: Use Plane.setFromCoplanarPoints() to create a THREE.Plane object from your three points. You can then create a random point in 3D space (e.g. via Math.random()) and project this point on the plane via Plane.projectPoint(). This would be the result of your intended function.

Upvotes: 2

Related Questions