Create 3D Curve with ThreeJS - three.js

I'm new to THREE Js. I'm trying to draw something like this:
This is a simple curve in 3D. I draw a rectangular shape and then extrude 2 arcs.
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xf0f0f0);
var camera = new THREE.PerspectiveCamera();
var renderer = new THREE.WebGLRenderer();
function renderCurve(width, startX, startY) {
var delta = 0;
var frame = new THREE.Shape();
frame.moveTo(startX, startY);
frame.lineTo(startX + width, startY);
frame.lineTo(startX + width, startY + width);
frame.lineTo(startX, startY + width);
var hole = new THREE.Path();
hole.moveTo(startX + width + delta, startY + width + delta);
hole.absarc(
startX + width + delta,
startY + width + delta,
width / 2 + delta,
-Math.PI / 2,
-Math.PI,
true
);
hole.lineTo(startX + width + delta, startY + width + delta);
frame.holes.push(hole);
var holeExt = new THREE.Path();
holeExt.moveTo(startX + width, startY + width);
holeExt.absarc(
startX + width,
startY + width,
width,
-Math.PI / 2,
-Math.PI,
true
);
holeExt.lineTo(startX, startY);
frame.holes.push(holeExt);
var extrudeSettings = {
steps: 1,
depth: 1,
extrudeMaterial: 0,
bevelEnabled: false,
};
var curve = new THREE.ExtrudeGeometry(frame, extrudeSettings);
var material = new THREE.MeshBasicMaterial({
color: 0x000000,
});
var materialExtruded = new THREE.MeshBasicMaterial({
transparent:true,
opacity:1
});
var curveMesh = new THREE.Mesh(curve, [material, materialExtruded]);
scene.add(curveMesh);
}
function initScene() {
/**
* Camera
*/
const fov = 75;
camera = new THREE.PerspectiveCamera(
fov,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.x = 5;
camera.position.y = 10;
camera.position.z = 50;
/**
* Renderer
*/
renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
}
function renderAxes() {
var axeXMaterial = new THREE.LineBasicMaterial({
color: 0x00ff00,
});
var axeYMaterial = new THREE.LineBasicMaterial({
color: 0xff0000,
});
var axeZMaterial = new THREE.LineBasicMaterial({
color: 0x0000ff,
});
let points = [];
points.push(new THREE.Vector3(0, 0, 0));
points.push(new THREE.Vector3(10, 0, 0));
const axeXMesh = new THREE.Line(
new THREE.BufferGeometry().setFromPoints(points),
axeXMaterial
);
scene.add(axeXMesh);
points[1] = new THREE.Vector3(0, 10, 0);
const axeYMesh = new THREE.Line(
new THREE.BufferGeometry().setFromPoints(points),
axeYMaterial
);
scene.add(axeYMesh);
points[1] = new THREE.Vector3(0, 0, 10);
const axeZMesh = new THREE.Line(
new THREE.BufferGeometry().setFromPoints(points),
axeZMaterial
);
scene.add(axeZMesh);
}
function animate() {
requestAnimationFrame(animate);
camera.lookAt(scene.position);
renderer.render(scene, camera);
}
initScene();
renderCurve(10, 0, 0);
//renderAxes();
animate();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<h1 id="header"></h1>
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
My issue is that I don't want to see the extruded shape border. I only want the curve.
In fact, I want to extrude a BoxGeometry to get a curve in 3D, not just a shape.
Does anybody know how can I do that ?

Related

How to project a texture to curved surface?

Screen capture
I tried to make a 3/4 cylinder surface, and I made it from extruting by a ellipe path,
but when I tried to load a texture to the surface, It does not as I exprected: uniformly painted to the surface, it's stretched
I know it's about texture projection, But I dont know how to set options.
class EllipseCurve3 extends THREE.Curve {
ellipse = null
constructor (ellipse) {
super()
this.ellipse = ellipse
}
getPoint(t, optionalTarget = new THREE.Vector3()) {
const point = this.ellipse.getPoint(t, optionalTarget)
return new THREE.Vector3(
point.x,
point.y,
0
)
}
}
// Scene
const scene = new THREE.Scene();
var shape = new THREE.Shape();
shape.moveTo(0, 0);
shape.moveTo(0, 1);
shape.lineTo(50, 1);
shape.moveTo(50, 0);
shape.lineTo(0, 0);
// var curve = new THREE.CatmullRomCurve3([
// new THREE.Vector3(0, 0, 50),
// new THREE.Vector3(-50, 0, 0),
// new THREE.Vector3(0, 0, -50)
// ]);
const arc = new THREE.EllipseCurve(
0,
0, // ax, aY
100,
100, // xRadius, yRadius
0,
1.5 * Math.PI, // aStartAngle, aEndAngle
false, // aClockwise
0 // aRotation
),
path = new EllipseCurve3(arc)
geometry = new THREE.ExtrudeGeometry(shape, {
bevelEnabled: false,
extrudePath: path,
steps: 50,
depth: 5,
amount: 20,
material: 0,
extrudeMaterial: 1
});
// Set up lights
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const axesHelper = new THREE.AxesHelper(500);
scene.add(axesHelper);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.6);
directionalLight.position.set(100, 200, 100); // x, y, z
scene.add(directionalLight);
// Camera
const width = 200;
const height = width * (window.innerHeight / window.innerWidth);
const camera = new THREE.OrthographicCamera(
width / -2, // left
width / 2, // right
height / 2, // top
height / -2, // bottom
0.1, // near
1000 // far
);
camera.position.set(400, 400, 400);
camera.lookAt(0, 0, 0);
// Renderer
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
const controls = new THREE.OrbitControls(camera, renderer.domElement);
renderer.render(scene, camera);
// Add it to HTML
document.body.appendChild(renderer.domElement);
var textureLoader = new THREE.TextureLoader();
textureLoader.crossOrigin = true;
const picture = 'https://threejs.org/examples/textures/uv_grid_opengl.jpg'
textureLoader.load(picture, function(texture) {
// repeat pattern
texture.wrapS = texture.wrapT = THREE.MirroredRepeatWrapping;
// zoom in on pattern
texture.repeat.set(.01, .01);
// assign texture via MeshBasicMaterial
var material = new THREE.MeshBasicMaterial({
map: texture,
needsUpdate: true,
// transparent: true,
// premultipliedAlpha: true,
// side: THREE.DoubleSide,
// blending: THREE.AdditiveBlending
});
// var material = new THREE.MeshPhongMaterial({ color: 0x0048ff });
var mesh = new THREE.Mesh(geometry, material)
mesh.rotation.x = Math.PI / 2
// mesh.rotation.z = Math.PI / 2
scene.add(mesh)
scene.add(cube)
})
function render() {
renderer.render(scene, camera);
// Rotate out group
// svgGroup.rotation.y -= 0.005
controls.update();
requestAnimationFrame(render);
}
render();
Code here
https://codepen.io/mike-xu/pen/RwQeEXJ
"I know it's about texture projection" - It's about computing UV, based on vertices coordinates.
CylinderGeometry also may help to achieve the result you described. With less code, and more convenient and predictable way.
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three#0.136.0";
import {OrbitControls} from "https://cdn.skypack.dev/three#0.136.0/examples/jsm/controls/OrbitControls.js";
console.clear();
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 10, 10);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", event => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
let controls = new OrbitControls(camera, renderer.domElement);
scene.add(new THREE.AxesHelper(10));
let g = new THREE.CylinderGeometry(5, 5, 5, 100, 20, true, 0, Math.PI * 1.5);
let m = new THREE.MeshBasicMaterial({
side: THREE.DoubleSide,
map: new THREE.TextureLoader().load(
"https://threejs.org/examples/textures/uv_grid_opengl.jpg",
tex => {
tex.wrapS = tex.wrapT = THREE.MirroredRepeatWrapping;
tex.repeat.set(3, 1);
}
)
});
let c = new THREE.Mesh(g, m);
scene.add(c);
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
</script>

Threejs How to Add in and Out curves in a custom shape?

I have a simple shape made in 3js with 8 points. The shape is without curves. I am not very good in absarc or curves in Threejs. I have tried to search online but I didn't get exact example.
My shape output:
While I need output like this:
Just an option, using .absarc():
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 100);
camera.position.set(0, 0, 6);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var grid = new THREE.GridHelper(10, 10);
grid.rotation.x = -Math.PI * 0.5;
grid.position.set(0, 0, -0.01);
scene.add(grid);
var s = new THREE.Shape();
s.absarc(0, 0, 0.5, Math.PI * 0.5, -Math.PI * 0.5, true);
s.lineTo(0, -2);
s.absarc(-1, 0, Math.sqrt(2) * 2, -Math.PI * 0.25, Math.PI * 0.25, false);
s.lineTo(0, 2);
var sGeom = new THREE.ShapeBufferGeometry(s);
var sMat = new THREE.MeshBasicMaterial({
color: "teal"
});
var shape = new THREE.Mesh(sGeom, sMat);
scene.add(shape);
var lineGeom = new THREE.BufferGeometry().setFromPoints(s.getPoints());
var lineMat = new THREE.LineBasicMaterial({
color: "orange"
});
var outline = new THREE.LineLoop(lineGeom, lineMat);
scene.add(outline);
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

How do you plot elliptic paraboloid in ThreeJS

I am trying to create a elliptic paraboloid in threeJS using parametric curves. Here is the link to what I am looking to plot:
https://mathinsight.org/level_sets.
Here is what I have right now:
Here is the current function definition, however it seems to be producing a different result.
let planeCreator = function(u,v,w){
var height = 15;
var size = 20;
var u = u * height;
var v = (v * 2 * Math.PI);
var x = size * Math.sqrt(u) * Math.cos(v);
var y = u;
var z = size * Math.sqrt(u) * Math.sin(v);
w.set(x,-y,z);
};
let geometry = new THREE.ParametricBufferGeometry( planeCreator, 50, 50 );
geometry.center();
let material = new THREE.MeshPhongMaterial( {
color: 0xff0000,
side: THREE.DoubleSide,
wireframe: true
} );
let object = new THREE.Mesh( geometry, material );
scene.add( object );
Here is what is currently plotted:
As an option (without parametric geometries):
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(-4, 2, 5);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var planeGeom = new THREE.PlaneBufferGeometry(4, 4, 20, 20);
planeGeom.rotateX(-Math.PI * 0.5);
var v = new THREE.Vector3();
var positions = planeGeom.attributes.position;
for (var i = 0; i < positions.count; i++) {
v.fromBufferAttribute(positions, i);
positions.setY(i, (-(v.x * v.x) - (2 * v.z * v.z)) * 0.25);
}
planeGeom.center();
planeGeom.computeVertexNormals();
var ellipticParaboloidSurface = new THREE.Mesh(planeGeom, new THREE.MeshNormalMaterial({
side: THREE.DoubleSide
}));
scene.add(ellipticParaboloidSurface);
var boxHelper = new THREE.BoxHelper(ellipticParaboloidSurface, "yellow");
scene.add(boxHelper);
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
})
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Three js: Create a vertical ruler with measurements

I want to create a vertical ruler with measurements like the image below. can anyone help me with it.
var material = new THREE.LineBasicMaterial({
color: 0x07E1E1,
linewidth: 3
});
var geometry = new THREE.Geometry();
geometry.vertices.push(
new THREE.Vector3( 0, 0, 0 ),
new THREE.Vector3( 0, 1100, 0 ),
new THREE.Vector3( 0, 0, 0 )
);
var line = new THREE.Line( geometry, material );
scene.add( line );
line.position.set(-550, -550, 200);
But it is not getting as the image and also the measurements…
You can use LineBasicMaterial and Line elements.
var camera, scene, renderer, object;
init();
function init() {
var container;
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1100 );
camera.target = new THREE.Vector3( 0, 0, 0 );
scene = new THREE.Scene();
object = new THREE.Object3D();
var lineMtr = new THREE.LineBasicMaterial({ color: 0xffffff, linewidth: 3, opacity: 1 });
var geo = new THREE.Geometry();
geo.vertices.push(new THREE.Vector3(0, 10 ,3));
geo.vertices.push(new THREE.Vector3(0, 0 ,3));
var line = new THREE.Line(geo, lineMtr);
var i = 0, l = 10;
object.add(line);
while (i <= l) {
var geoSegm = new THREE.Geometry();
geoSegm.vertices.push(new THREE.Vector3(0.1, i, 3));
geoSegm.vertices.push(new THREE.Vector3(0, i, 3));
var lineSegm = new THREE.Line(geoSegm, lineMtr);
object.add(lineSegm);
var textSprite = makeTextSprite((i * 10).toString(), {r: 255, g: 255, b: 255, a: 255}, new THREE.Vector3(0.2, i, 3), Math.PI);
object.add(textSprite);
i++;
}
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
You can create a texture for a SpriteMaterial, then use Sprite for ruler texts.
function makeTextSprite(label, fontColor, pos, rot) {
var fontface = "Arial";
var fontsize = 100;
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
context.font = "Bold " + fontsize.toString() + "px " + fontface;
var metrics = context.measureText(label);
context.rotate(-Math.PI);
context.translate(-canvas.width, -canvas.height);
context.fillStyle = "rgba(" + fontColor.r + "," + fontColor.g + "," + fontColor.b + "," + fontColor.a + ")";
context.fillText(label, 0, 100);
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
texture.center = new THREE.Vector2(0.5, 0.5);
texture.rotation = Math.PI;
var spriteMaterial = new THREE.SpriteMaterial({
map: texture, color: 0xffffff
});
var sprite = new THREE.Sprite(spriteMaterial);
sprite.scale.set(0.25, 0.25, 0.25);
sprite.position.set(pos.x, pos.y, pos.z);
return sprite;
}
I used codes in that link http://jsfiddle.net/3mrzL75h/19/
In first look you can't seeing ruler, drag camera to right.

AxisHelper not showing colored

I'm trying to add an AxisHelper to my three.js project. I added it like this:
axes = new THREE.AxisHelper(100);
scene.add(axes);
It gets added, but it isn't colored It's solid white, and therefore hard to see. How can I make it the regular colors?
JSFiddle
var container;
var camera, scene, axis, renderer;
init();
animate();
function init() {
container = document.getElementById('container');
// Camera
camera = new THREE.OrthographicCamera(window.innerWidth / -2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / -2, -500, 1000);
camera.position.x = 200;
camera.position.y = 100;
camera.position.z = 200;
// Scene
scene = new THREE.Scene();
// Axis
axes = new THREE.AxisHelper(100);
scene.add(axes);
// Grid
var size = 500,
step = 50;
var geometry = new THREE.Geometry();
for (var i = -size; i <= size; i += step) {
geometry.vertices.push(new THREE.Vector3(-size, 0, i));
geometry.vertices.push(new THREE.Vector3(size, 0, i));
geometry.vertices.push(new THREE.Vector3(i, 0, -size));
geometry.vertices.push(new THREE.Vector3(i, 0, size));
}
var material = new THREE.LineBasicMaterial({
color: 0x000000,
opacity: 0.2
});
var line = new THREE.LineSegments(geometry, material);
scene.add(line);
// Cubes
var geometry = new THREE.BoxGeometry(50, 50, 50);
var material = new THREE.MeshLambertMaterial({
color: 0xffffff,
overdraw: 0.5
});
var cube = new THREE.Mesh(geometry, material);
cube.scale.y = 1;
cube.position.x = 0;
scene.add(cube);
// Lights
var ambientLight = new THREE.AmbientLight(Math.random() * 0x10);
scene.add(ambientLight);
var directionalLight = new THREE.DirectionalLight(Math.random() * 0xffffff);
directionalLight.position.x = Math.random() - 0.5;
directionalLight.position.y = Math.random() - 0.5;
directionalLight.position.z = Math.random() - 0.5;
directionalLight.position.normalize();
scene.add(directionalLight);
var directionalLight = new THREE.DirectionalLight(Math.random() * 0xffffff);
directionalLight.position.x = Math.random() - 0.5;
directionalLight.position.y = Math.random() - 0.5;
directionalLight.position.z = Math.random() - 0.5;
directionalLight.position.normalize();
scene.add(directionalLight);
// Render
renderer = new THREE.CanvasRenderer();
renderer.setClearColor(0xf0f0f0);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
// Events
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize() {
camera.left = window.innerWidth / -2;
camera.right = window.innerWidth / 2;
camera.top = window.innerHeight / 2;
camera.bottom = window.innerHeight / -2;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
var timer = Date.now() * 0.0001;
camera.position.x = Math.cos(timer) * 200;
camera.position.z = Math.sin(timer) * 200;
camera.lookAt(scene.position);
renderer.render(scene, camera);
}
<div id="container"></div>
<script src="https://rawgit.com/mrdoob/three.js/master/build/three.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/renderers/Projector.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/renderers/CanvasRenderer.js"></script>
You can get CanvasRenderer to properly render the colored axes of AxisHelper like so:
var axes = new THREE.AxisHelper( 100 );
axes.geometry = new THREE.Geometry().fromBufferGeometry( axes.geometry );
scene.add( axes );
CanvasRenderer has a bug, and does not properly render vertex colors when the geometry is BufferGeometry. The above is a work-around.
three.js r.82

Resources