12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199 |
- import * as THREE from 'three';
- import { threejsLessonUtils } from './threejs-lesson-utils.js';
- import { FontLoader } from '../../examples/jsm/loaders/FontLoader.js';
- import { ParametricGeometry } from '../../examples/jsm/geometries/ParametricGeometry.js';
- import { TextGeometry } from '../../examples/jsm/geometries/TextGeometry.js';
- {
- const darkColors = {
- lines: '#DDD',
- };
- const lightColors = {
- lines: '#000',
- };
- const darkMatcher = window.matchMedia( '(prefers-color-scheme: dark)' );
- const isDarkMode = darkMatcher.matches;
- const colors = isDarkMode ? darkColors : lightColors;
- const fontLoader = new FontLoader();
- const fontPromise = new Promise( ( resolve ) => {
- fontLoader.load( '/examples/fonts/helvetiker_regular.typeface.json', resolve );
- } );
- const diagrams = {
- BoxGeometry: {
- ui: {
- width: { type: 'range', min: 1, max: 10, precision: 1, },
- height: { type: 'range', min: 1, max: 10, precision: 1, },
- depth: { type: 'range', min: 1, max: 10, precision: 1, },
- widthSegments: { type: 'range', min: 1, max: 10, },
- heightSegments: { type: 'range', min: 1, max: 10, },
- depthSegments: { type: 'range', min: 1, max: 10, },
- },
- create( width = 8, height = 8, depth = 8 ) {
- return new THREE.BoxGeometry( width, height, depth );
- },
- create2( width = 8, height = 8, depth = 8, widthSegments = 4, heightSegments = 4, depthSegments = 4 ) {
- return new THREE.BoxGeometry(
- width, height, depth,
- widthSegments, heightSegments, depthSegments );
- },
- },
- CircleGeometry: {
- ui: {
- radius: { type: 'range', min: 1, max: 10, precision: 1, },
- segments: { type: 'range', min: 1, max: 50, },
- thetaStart: { type: 'range', min: 0, max: 2, mult: Math.PI },
- thetaLength: { type: 'range', min: 0, max: 2, mult: Math.PI },
- },
- create( radius = 7, segments = 24 ) {
- return new THREE.CircleGeometry( radius, segments );
- },
- create2( radius = 7, segments = 24, thetaStart = Math.PI * 0.25, thetaLength = Math.PI * 1.5 ) {
- return new THREE.CircleGeometry(
- radius, segments, thetaStart, thetaLength );
- },
- },
- ConeGeometry: {
- ui: {
- radius: { type: 'range', min: 1, max: 10, precision: 1, },
- height: { type: 'range', min: 1, max: 10, precision: 1, },
- radialSegments: { type: 'range', min: 1, max: 50, },
- heightSegments: { type: 'range', min: 1, max: 10, },
- openEnded: { type: 'bool', },
- thetaStart: { type: 'range', min: 0, max: 2, mult: Math.PI },
- thetaLength: { type: 'range', min: 0, max: 2, mult: Math.PI },
- },
- create( radius = 6, height = 8, radialSegments = 16 ) {
- return new THREE.ConeGeometry( radius, height, radialSegments );
- },
- create2( radius = 6, height = 8, radialSegments = 16, heightSegments = 2, openEnded = true, thetaStart = Math.PI * 0.25, thetaLength = Math.PI * 1.5 ) {
- return new THREE.ConeGeometry(
- radius, height,
- radialSegments, heightSegments,
- openEnded,
- thetaStart, thetaLength );
- },
- },
- CylinderGeometry: {
- ui: {
- radiusTop: { type: 'range', min: 0, max: 10, precision: 1, },
- radiusBottom: { type: 'range', min: 0, max: 10, precision: 1, },
- height: { type: 'range', min: 1, max: 10, precision: 1, },
- radialSegments: { type: 'range', min: 1, max: 50, },
- heightSegments: { type: 'range', min: 1, max: 10, },
- openEnded: { type: 'bool', },
- thetaStart: { type: 'range', min: 0, max: 2, mult: Math.PI },
- thetaLength: { type: 'range', min: 0, max: 2, mult: Math.PI },
- },
- create( radiusTop = 4, radiusBottom = 4, height = 8, radialSegments = 12 ) {
- return new THREE.CylinderGeometry(
- radiusTop, radiusBottom, height, radialSegments );
- },
- create2( radiusTop = 4, radiusBottom = 4, height = 8, radialSegments = 12, heightSegments = 2, openEnded = false, thetaStart = Math.PI * 0.25, thetaLength = Math.PI * 1.5 ) {
- return new THREE.CylinderGeometry(
- radiusTop, radiusBottom, height,
- radialSegments, heightSegments,
- openEnded,
- thetaStart, thetaLength );
- },
- },
- DodecahedronGeometry: {
- ui: {
- radius: { type: 'range', min: 1, max: 10, precision: 1, },
- detail: { type: 'range', min: 0, max: 5, precision: 0, },
- },
- create( radius = 7 ) {
- return new THREE.DodecahedronGeometry( radius );
- },
- create2( radius = 7, detail = 2 ) {
- return new THREE.DodecahedronGeometry( radius, detail );
- },
- },
- ExtrudeGeometry: {
- ui: {
- steps: { type: 'range', min: 1, max: 100, },
- depth: { type: 'range', min: 1, max: 20, precision: 1, },
- bevelEnabled: { type: 'bool', },
- bevelThickness: { type: 'range', min: 0.1, max: 3, },
- bevelSize: { type: 'range', min: 0.1, max: 3, },
- bevelSegments: { type: 'range', min: 0, max: 8, },
- },
- addConstCode: false,
- create( steps = 2, depth = 2, bevelEnabled = true, bevelThickness = 1, bevelSize = 1, bevelSegments = 2 ) {
- const shape = new THREE.Shape();
- const x = - 2.5;
- const y = - 5;
- shape.moveTo( x + 2.5, y + 2.5 );
- shape.bezierCurveTo( x + 2.5, y + 2.5, x + 2, y, x, y );
- shape.bezierCurveTo( x - 3, y, x - 3, y + 3.5, x - 3, y + 3.5 );
- shape.bezierCurveTo( x - 3, y + 5.5, x - 1.5, y + 7.7, x + 2.5, y + 9.5 );
- shape.bezierCurveTo( x + 6, y + 7.7, x + 8, y + 4.5, x + 8, y + 3.5 );
- shape.bezierCurveTo( x + 8, y + 3.5, x + 8, y, x + 5, y );
- shape.bezierCurveTo( x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5 );
- const extrudeSettings = {
- steps,
- depth,
- bevelEnabled,
- bevelThickness,
- bevelSize,
- bevelSegments,
- };
- const geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
- return geometry;
- },
- src: `
- const shape = new THREE.Shape();
- const x = -2.5;
- const y = -5;
- shape.moveTo(x + 2.5, y + 2.5);
- shape.bezierCurveTo(x + 2.5, y + 2.5, x + 2, y, x, y);
- shape.bezierCurveTo(x - 3, y, x - 3, y + 3.5, x - 3, y + 3.5);
- shape.bezierCurveTo(x - 3, y + 5.5, x - 1.5, y + 7.7, x + 2.5, y + 9.5);
- shape.bezierCurveTo(x + 6, y + 7.7, x + 8, y + 4.5, x + 8, y + 3.5);
- shape.bezierCurveTo(x + 8, y + 3.5, x + 8, y, x + 5, y);
- shape.bezierCurveTo(x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5);
- const extrudeSettings = {
- steps: 2, // ui: steps
- depth: 2, // ui: depth
- bevelEnabled: true, // ui: bevelEnabled
- bevelThickness: 1, // ui: bevelThickness
- bevelSize: 1, // ui: bevelSize
- bevelSegments: 2, // ui: bevelSegments
- };
- const geometry = THREE.ExtrudeGeometry(shape, extrudeSettings);
- `,
- create2( steps = 100 ) {
- const outline = new THREE.Shape( [
- [ - 2, - 0.1 ], [ 2, - 0.1 ], [ 2, 0.6 ],
- [ 1.6, 0.6 ], [ 1.6, 0.1 ], [ - 2, 0.1 ],
- ].map( p => new THREE.Vector2( ...p ) ) );
- const x = - 2.5;
- const y = - 5;
- const shape = new THREE.CurvePath();
- const points = [
- [ x + 2.5, y + 2.5 ],
- [ x + 2.5, y + 2.5 ], [ x + 2, y ], [ x, y ],
- [ x - 3, y ], [ x - 3, y + 3.5 ], [ x - 3, y + 3.5 ],
- [ x - 3, y + 5.5 ], [ x - 1.5, y + 7.7 ], [ x + 2.5, y + 9.5 ],
- [ x + 6, y + 7.7 ], [ x + 8, y + 4.5 ], [ x + 8, y + 3.5 ],
- [ x + 8, y + 3.5 ], [ x + 8, y ], [ x + 5, y ],
- [ x + 3.5, y ], [ x + 2.5, y + 2.5 ], [ x + 2.5, y + 2.5 ],
- ].map( p => new THREE.Vector3( ...p, 0 ) );
- for ( let i = 0; i < points.length; i += 3 ) {
- shape.add( new THREE.CubicBezierCurve3( ...points.slice( i, i + 4 ) ) );
- }
- const extrudeSettings = {
- steps,
- bevelEnabled: false,
- extrudePath: shape,
- };
- const geometry = new THREE.ExtrudeGeometry( outline, extrudeSettings );
- return geometry;
- },
- src2: `
- const outline = new THREE.Shape([
- [ -2, -0.1], [ 2, -0.1], [ 2, 0.6],
- [1.6, 0.6], [1.6, 0.1], [-2, 0.1],
- ].map(p => new THREE.Vector2(...p)));
- const x = -2.5;
- const y = -5;
- const shape = new THREE.CurvePath();
- const points = [
- [x + 2.5, y + 2.5],
- [x + 2.5, y + 2.5], [x + 2, y ], [x, y ],
- [x - 3, y ], [x - 3, y + 3.5], [x - 3, y + 3.5],
- [x - 3, y + 5.5], [x - 1.5, y + 7.7], [x + 2.5, y + 9.5],
- [x + 6, y + 7.7], [x + 8, y + 4.5], [x + 8, y + 3.5],
- [x + 8, y + 3.5], [x + 8, y ], [x + 5, y ],
- [x + 3.5, y ], [x + 2.5, y + 2.5], [x + 2.5, y + 2.5],
- ].map(p => new THREE.Vector3(...p, 0));
- for (let i = 0; i < points.length; i += 3) {
- shape.add(new THREE.CubicBezierCurve3(...points.slice(i, i + 4)));
- }
- const extrudeSettings = {
- steps: 100, // ui: steps
- bevelEnabled: false,
- extrudePath: shape,
- };
- const geometry = new THREE.ExtrudeGeometry(outline, extrudeSettings);
- return geometry;
- `,
- },
- IcosahedronGeometry: {
- ui: {
- radius: { type: 'range', min: 1, max: 10, precision: 1, },
- detail: { type: 'range', min: 0, max: 5, precision: 0, },
- },
- create( radius = 7 ) {
- return new THREE.IcosahedronGeometry( radius );
- },
- create2( radius = 7, detail = 2 ) {
- return new THREE.IcosahedronGeometry( radius, detail );
- },
- },
- LatheGeometry: {
- ui: {
- segments: { type: 'range', min: 1, max: 50, },
- phiStart: { type: 'range', min: 0, max: 2, mult: Math.PI },
- phiLength: { type: 'range', min: 0, max: 2, mult: Math.PI },
- },
- create() {
- const points = [];
- for ( let i = 0; i < 10; ++ i ) {
- points.push( new THREE.Vector2( Math.sin( i * 0.2 ) * 3 + 3, ( i - 5 ) * .8 ) );
- }
- return new THREE.LatheGeometry( points );
- },
- create2( segments = 12, phiStart = Math.PI * 0.25, phiLength = Math.PI * 1.5 ) {
- const points = [];
- for ( let i = 0; i < 10; ++ i ) {
- points.push( new THREE.Vector2( Math.sin( i * 0.2 ) * 3 + 3, ( i - 5 ) * .8 ) );
- }
- return new THREE.LatheGeometry(
- points, segments, phiStart, phiLength );
- },
- },
- OctahedronGeometry: {
- ui: {
- radius: { type: 'range', min: 1, max: 10, precision: 1, },
- detail: { type: 'range', min: 0, max: 5, precision: 0, },
- },
- create( radius = 7 ) {
- return new THREE.OctahedronGeometry( radius );
- },
- create2( radius = 7, detail = 2 ) {
- return new THREE.OctahedronGeometry( radius, detail );
- },
- },
- ParametricGeometry: {
- ui: {
- stacks: { type: 'range', min: 1, max: 50, },
- slices: { type: 'range', min: 1, max: 50, },
- },
- /*
- from: https://github.com/mrdoob/three.js/blob/b8d8a8625465bd634aa68e5846354d69f34d2ff5/examples/js/ParametricGeometries.js
- The MIT License
- Copyright © 2010-2018 three.js authors
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- */
- create( slices = 25, stacks = 25 ) {
- // from: https://github.com/mrdoob/three.js/blob/b8d8a8625465bd634aa68e5846354d69f34d2ff5/examples/js/ParametricGeometries.js
- function klein( v, u, target ) {
- u *= Math.PI;
- v *= 2 * Math.PI;
- u = u * 2;
- let x;
- let z;
- if ( u < Math.PI ) {
- x = 3 * Math.cos( u ) * ( 1 + Math.sin( u ) ) + ( 2 * ( 1 - Math.cos( u ) / 2 ) ) * Math.cos( u ) * Math.cos( v );
- z = - 8 * Math.sin( u ) - 2 * ( 1 - Math.cos( u ) / 2 ) * Math.sin( u ) * Math.cos( v );
- } else {
- x = 3 * Math.cos( u ) * ( 1 + Math.sin( u ) ) + ( 2 * ( 1 - Math.cos( u ) / 2 ) ) * Math.cos( v + Math.PI );
- z = - 8 * Math.sin( u );
- }
- const y = - 2 * ( 1 - Math.cos( u ) / 2 ) * Math.sin( v );
- target.set( x, y, z ).multiplyScalar( 0.75 );
- }
- return new ParametricGeometry(
- klein, slices, stacks );
- },
- },
- PlaneGeometry: {
- ui: {
- width: { type: 'range', min: 1, max: 10, precision: 1, },
- height: { type: 'range', min: 1, max: 10, precision: 1, },
- widthSegments: { type: 'range', min: 1, max: 10, },
- heightSegments: { type: 'range', min: 1, max: 10, },
- },
- create( width = 9, height = 9 ) {
- return new THREE.PlaneGeometry( width, height );
- },
- create2( width = 9, height = 9, widthSegments = 2, heightSegments = 2 ) {
- return new THREE.PlaneGeometry(
- width, height,
- widthSegments, heightSegments );
- },
- },
- PolyhedronGeometry: {
- ui: {
- radius: { type: 'range', min: 1, max: 10, precision: 1, },
- detail: { type: 'range', min: 0, max: 5, precision: 0, },
- },
- create( radius = 7, detail = 2 ) {
- const verticesOfCube = [
- - 1, - 1, - 1, 1, - 1, - 1, 1, 1, - 1, - 1, 1, - 1,
- - 1, - 1, 1, 1, - 1, 1, 1, 1, 1, - 1, 1, 1,
- ];
- const indicesOfFaces = [
- 2, 1, 0, 0, 3, 2,
- 0, 4, 7, 7, 3, 0,
- 0, 1, 5, 5, 4, 0,
- 1, 2, 6, 6, 5, 1,
- 2, 3, 7, 7, 6, 2,
- 4, 5, 6, 6, 7, 4,
- ];
- return new THREE.PolyhedronGeometry(
- verticesOfCube, indicesOfFaces, radius, detail );
- },
- },
- RingGeometry: {
- ui: {
- innerRadius: { type: 'range', min: 1, max: 10, precision: 1, },
- outerRadius: { type: 'range', min: 1, max: 10, precision: 1, },
- thetaSegments: { type: 'range', min: 1, max: 30, },
- phiSegments: { type: 'range', min: 1, max: 10, },
- thetaStart: { type: 'range', min: 0, max: 2, mult: Math.PI },
- thetaLength: { type: 'range', min: 0, max: 2, mult: Math.PI },
- },
- create( innerRadius = 2, outerRadius = 7, thetaSegments = 18 ) {
- return new THREE.RingGeometry(
- innerRadius, outerRadius, thetaSegments );
- },
- create2( innerRadius = 2, outerRadius = 7, thetaSegments = 18, phiSegments = 2, thetaStart = Math.PI * 0.25, thetaLength = Math.PI * 1.5 ) {
- return new THREE.RingGeometry(
- innerRadius, outerRadius,
- thetaSegments, phiSegments,
- thetaStart, thetaLength );
- },
- },
- ShapeGeometry: {
- ui: {
- curveSegments: { type: 'range', min: 1, max: 30, },
- },
- create() {
- const shape = new THREE.Shape();
- const x = - 2.5;
- const y = - 5;
- shape.moveTo( x + 2.5, y + 2.5 );
- shape.bezierCurveTo( x + 2.5, y + 2.5, x + 2, y, x, y );
- shape.bezierCurveTo( x - 3, y, x - 3, y + 3.5, x - 3, y + 3.5 );
- shape.bezierCurveTo( x - 3, y + 5.5, x - 1.5, y + 7.7, x + 2.5, y + 9.5 );
- shape.bezierCurveTo( x + 6, y + 7.7, x + 8, y + 4.5, x + 8, y + 3.5 );
- shape.bezierCurveTo( x + 8, y + 3.5, x + 8, y, x + 5, y );
- shape.bezierCurveTo( x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5 );
- return new THREE.ShapeGeometry( shape );
- },
- create2( curveSegments = 5 ) {
- const shape = new THREE.Shape();
- const x = - 2.5;
- const y = - 5;
- shape.moveTo( x + 2.5, y + 2.5 );
- shape.bezierCurveTo( x + 2.5, y + 2.5, x + 2, y, x, y );
- shape.bezierCurveTo( x - 3, y, x - 3, y + 3.5, x - 3, y + 3.5 );
- shape.bezierCurveTo( x - 3, y + 5.5, x - 1.5, y + 7.7, x + 2.5, y + 9.5 );
- shape.bezierCurveTo( x + 6, y + 7.7, x + 8, y + 4.5, x + 8, y + 3.5 );
- shape.bezierCurveTo( x + 8, y + 3.5, x + 8, y, x + 5, y );
- shape.bezierCurveTo( x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5 );
- return new THREE.ShapeGeometry( shape, curveSegments );
- },
- },
- SphereGeometry: {
- ui: {
- radius: { type: 'range', min: 1, max: 10, precision: 1, },
- widthSegments: { type: 'range', min: 1, max: 30, },
- heightSegments: { type: 'range', min: 1, max: 30, },
- phiStart: { type: 'range', min: 0, max: 2, mult: Math.PI },
- phiLength: { type: 'range', min: 0, max: 2, mult: Math.PI },
- thetaStart: { type: 'range', min: 0, max: 1, mult: Math.PI },
- thetaLength: { type: 'range', min: 0, max: 1, mult: Math.PI },
- },
- create( radius = 7, widthSegments = 12, heightSegments = 8 ) {
- return new THREE.SphereGeometry( radius, widthSegments, heightSegments );
- },
- create2( radius = 7, widthSegments = 12, heightSegments = 8, phiStart = Math.PI * 0.25, phiLength = Math.PI * 1.5, thetaStart = Math.PI * 0.25, thetaLength = Math.PI * 0.5 ) {
- return new THREE.SphereGeometry(
- radius,
- widthSegments, heightSegments,
- phiStart, phiLength,
- thetaStart, thetaLength );
- },
- },
- TetrahedronGeometry: {
- ui: {
- radius: { type: 'range', min: 1, max: 10, precision: 1, },
- detail: { type: 'range', min: 0, max: 5, precision: 0, },
- },
- create( radius = 7 ) {
- return new THREE.TetrahedronGeometry( radius );
- },
- create2( radius = 7, detail = 2 ) {
- return new THREE.TetrahedronGeometry( radius, detail );
- },
- },
- TextGeometry: {
- ui: {
- text: { type: 'text', maxLength: 30, },
- size: { type: 'range', min: 1, max: 10, precision: 1, },
- depth: { type: 'range', min: 1, max: 10, precision: 1, },
- curveSegments: { type: 'range', min: 1, max: 20, },
- // font', fonts ).onChange( generateGeometry );
- // weight', weights ).onChange( generateGeometry );
- bevelEnabled: { type: 'bool', },
- bevelThickness: { type: 'range', min: 0.1, max: 3, },
- bevelSize: { type: 'range', min: 0.1, max: 3, },
- bevelSegments: { type: 'range', min: 0, max: 8, },
- },
- addConstCode: false,
- create( text = 'three.js', size = 3, depth = 0.2, curveSegments = 12, bevelEnabled = true, bevelThickness = 0.15, bevelSize = 0.3, bevelSegments = 5 ) {
- return new Promise( ( resolve ) => {
- fontPromise.then( ( font ) => {
- resolve( new TextGeometry( text, {
- font: font,
- size,
- depth,
- curveSegments,
- bevelEnabled,
- bevelThickness,
- bevelSize,
- bevelSegments,
- } ) );
- } );
- } );
- },
- src: `
- const loader = new THREE.FontLoader();
- loader.load('../resources/threejs/fonts/helvetiker_regular.typeface.json', (font) => {
- const text = 'three.js'; // ui: text
- const geometry = new THREE.TextGeometry(text, {
- font: font,
- size: 3, // ui: size
- depth: 0.2, // ui: depth
- curveSegments: 12, // ui: curveSegments
- bevelEnabled: true, // ui: bevelEnabled
- bevelThickness: 0.15, // ui: bevelThickness
- bevelSize: 0.3, // ui: bevelSize
- bevelSegments: 5, // ui: bevelSegments
- });
- ...
- });
- `,
- },
- TorusGeometry: {
- ui: {
- radius: { type: 'range', min: 1, max: 10, precision: 1, },
- tubeRadius: { type: 'range', min: 1, max: 10, precision: 1, },
- radialSegments: { type: 'range', min: 1, max: 30, },
- tubularSegments: { type: 'range', min: 1, max: 100, },
- },
- create( radius = 5, tubeRadius = 2, radialSegments = 8, tubularSegments = 24 ) {
- return new THREE.TorusGeometry(
- radius, tubeRadius,
- radialSegments, tubularSegments );
- },
- },
- TorusKnotGeometry: {
- ui: {
- radius: { type: 'range', min: 1, max: 10, precision: 1, },
- tubeRadius: { type: 'range', min: 1, max: 10, precision: 1, },
- radialSegments: { type: 'range', min: 1, max: 30, },
- tubularSegments: { type: 'range', min: 1, max: 100, },
- p: { type: 'range', min: 1, max: 20, },
- q: { type: 'range', min: 1, max: 20, },
- },
- create( radius = 3.5, tubeRadius = 1.5, radialSegments = 8, tubularSegments = 64, p = 2, q = 3 ) {
- return new THREE.TorusKnotGeometry(
- radius, tubeRadius, tubularSegments, radialSegments, p, q );
- },
- },
- TubeGeometry: {
- ui: {
- tubularSegments: { type: 'range', min: 1, max: 100, },
- radius: { type: 'range', min: 1, max: 10, precision: 1, },
- radialSegments: { type: 'range', min: 1, max: 30, },
- closed: { type: 'bool', },
- },
- create( tubularSegments = 20, radius = 1, radialSegments = 8, closed = false ) {
- class CustomSinCurve extends THREE.Curve {
- constructor( scale ) {
- super();
- this.scale = scale;
- }
- getPoint( t ) {
- const tx = t * 3 - 1.5;
- const ty = Math.sin( 2 * Math.PI * t );
- const tz = 0;
- return new THREE.Vector3( tx, ty, tz ).multiplyScalar( this.scale );
- }
- }
- const path = new CustomSinCurve( 4 );
- return new THREE.TubeGeometry(
- path, tubularSegments, radius, radialSegments, closed );
- },
- },
- EdgesGeometry: {
- ui: {
- thresholdAngle: { type: 'range', min: 1, max: 180, },
- },
- create() {
- return {
- lineGeometry: new THREE.EdgesGeometry(
- new THREE.BoxGeometry( 8, 8, 8 ) ),
- };
- },
- create2( thresholdAngle = 1 ) {
- return {
- lineGeometry: new THREE.EdgesGeometry(
- new THREE.SphereGeometry( 7, 6, 3 ), thresholdAngle ),
- };
- },
- addConstCode: false,
- src: `
- const size = 8;
- const widthSegments = 2;
- const heightSegments = 2;
- const depthSegments = 2;
- const boxGeometry = new THREE.BoxGeometry(
- size, size, size,
- widthSegments, heightSegments, depthSegments);
- const geometry = new THREE.EdgesGeometry(boxGeometry);
- `,
- src2: `
- const radius = 7;
- const widthSegments = 6;
- const heightSegments = 3;
- const sphereGeometry = new THREE.SphereGeometry(
- radius, widthSegments, heightSegments);
- const thresholdAngle = 1; // ui: thresholdAngle
- const geometry = new THREE.EdgesGeometry(sphereGeometry, thresholdAngle);
- `,
- },
- WireframeGeometry: {
- ui: {
- widthSegments: { type: 'range', min: 1, max: 10, },
- heightSegments: { type: 'range', min: 1, max: 10, },
- depthSegments: { type: 'range', min: 1, max: 10, },
- },
- create( widthSegments = 2, heightSegments = 2, depthSegments = 2 ) {
- const size = 8;
- return {
- lineGeometry: new THREE.WireframeGeometry( new THREE.BoxGeometry(
- size, size, size,
- widthSegments, heightSegments, depthSegments ) ),
- };
- },
- addConstCode: false,
- src: `
- const size = 8;
- const widthSegments = 2; // ui: widthSegments
- const heightSegments = 2; // ui: heightSegments
- const depthSegments = 2; // ui: depthSegments
- const geometry = new THREE.WireframeGeometry(
- new THREE.BoxGeometry(
- size, size, size,
- widthSegments, heightSegments, depthSegments));
- `,
- },
- Points: {
- create() {
- const radius = 7;
- const widthSegments = 12;
- const heightSegments = 8;
- const geometry = new THREE.SphereGeometry( radius, widthSegments, heightSegments );
- const material = new THREE.PointsMaterial( {
- color: 'red',
- size: 0.2,
- } );
- const points = new THREE.Points( geometry, material );
- return {
- showLines: false,
- mesh: points,
- };
- },
- },
- PointsUniformSize: {
- create() {
- const radius = 7;
- const widthSegments = 12;
- const heightSegments = 8;
- const geometry = new THREE.SphereGeometry( radius, widthSegments, heightSegments );
- const material = new THREE.PointsMaterial( {
- color: 'red',
- size: 3 * window.devicePixelRatio,
- sizeAttenuation: false,
- } );
- const points = new THREE.Points( geometry, material );
- return {
- showLines: false,
- mesh: points,
- };
- },
- },
- SphereGeometryLow: {
- create( radius = 7, widthSegments = 5, heightSegments = 3 ) {
- return new THREE.SphereGeometry( radius, widthSegments, heightSegments );
- },
- },
- SphereGeometryMedium: {
- create( radius = 7, widthSegments = 24, heightSegments = 10 ) {
- return new THREE.SphereGeometry( radius, widthSegments, heightSegments );
- },
- },
- SphereGeometryHigh: {
- create( radius = 7, widthSegments = 50, heightSegments = 50 ) {
- return new THREE.SphereGeometry( radius, widthSegments, heightSegments );
- },
- },
- SphereGeometryLowSmooth: {
- create( radius = 7, widthSegments = 5, heightSegments = 3 ) {
- return new THREE.SphereGeometry( radius, widthSegments, heightSegments );
- },
- showLines: false,
- flatShading: false,
- },
- SphereGeometryMediumSmooth: {
- create( radius = 7, widthSegments = 24, heightSegments = 10 ) {
- return new THREE.SphereGeometry( radius, widthSegments, heightSegments );
- },
- showLines: false,
- flatShading: false,
- },
- SphereGeometryHighSmooth: {
- create( radius = 7, widthSegments = 50, heightSegments = 50 ) {
- return new THREE.SphereGeometry( radius, widthSegments, heightSegments );
- },
- showLines: false,
- flatShading: false,
- },
- PlaneGeometryLow: {
- create( width = 9, height = 9, widthSegments = 1, heightSegments = 1 ) {
- return new THREE.PlaneGeometry( width, height, widthSegments, heightSegments );
- },
- },
- PlaneGeometryHigh: {
- create( width = 9, height = 9, widthSegments = 10, heightSegments = 10 ) {
- return new THREE.PlaneGeometry( width, height, widthSegments, heightSegments );
- },
- },
- };
- function addLink( parent, name, href ) {
- const a = document.createElement( 'a' );
- a.setAttribute( 'target', '_blank' );
- a.href = href || `https://threejs.org/docs/#api/geometries/${name}`;
- const code = document.createElement( 'code' );
- code.textContent = name;
- a.appendChild( code );
- parent.appendChild( a );
- return a;
- }
- function addDeepLink( parent, name, href ) {
- const a = document.createElement( 'a' );
- a.href = href || `https://threejs.org/docs/#api/geometries/${name}`;
- a.textContent = name;
- a.className = 'deep-link';
- parent.appendChild( a );
- return a;
- }
- function addElem( parent, type, className, text ) {
- const elem = document.createElement( type );
- elem.className = className;
- if ( text ) {
- elem.textContent = text;
- }
- parent.appendChild( elem );
- return elem;
- }
- function addDiv( parent, className ) {
- return addElem( parent, 'div', className );
- }
- function createPrimitiveDOM( base ) {
- const name = base.dataset.primitive;
- const info = diagrams[ name ];
- if ( ! info ) {
- throw new Error( `no primitive ${name}` );
- }
- const text = base.innerHTML;
- base.innerHTML = '';
- const pair = addDiv( base, 'pair' );
- const elem = addDiv( pair, 'shape' );
- const right = addDiv( pair, 'desc' );
- addDeepLink( right, '#', `#${base.id}` );
- addLink( right, name );
- addDiv( right, '.note' ).innerHTML = text;
- // I get that this is super brittle. I think I'd have to
- // work through a bunch more examples to come up with a better
- // structure. Also, I don't want to generate actual code and
- // use eval. (maybe a bad striction)
- function makeExample( elem, createFn, src ) {
- const rawLines = createFn.toString().replace( /return (new THREE\.[a-zA-Z]+Geometry)/, 'const geometry = $1' ).split( /\n/ );
- const createRE = /^\s*(?:function *)*create\d*\((.*?)\)/;
- const indentRE = /^(\s*)[^\s]/;
- const m = indentRE.exec( rawLines[ 2 ] );
- const prefixLen = m[ 1 ].length;
- const m2 = createRE.exec( rawLines[ 0 ] );
- const argString = m2[ 1 ].trim();
- const trimmedLines = src
- ? src.split( '\n' ).slice( 1, - 1 )
- : rawLines.slice( 1, rawLines.length - 1 ).map( line => line.substring( prefixLen ) );
- if ( info.addConstCode !== false && argString ) {
- const lines = argString.split( ',' ).map( ( arg ) => {
- return `const ${arg.trim()}; // ui: ${arg.trim().split( ' ' )[ 0 ]}`;
- } );
- const lineNdx = trimmedLines.findIndex( l => l.indexOf( 'const geometry' ) >= 0 );
- trimmedLines.splice( lineNdx < 0 ? 0 : lineNdx, 0, ...lines );
- }
- addElem( base, 'pre', 'prettyprint showmods', trimmedLines.join( '\n' ) );
- createLiveImage( elem, { ...info, create: createFn }, name );
- }
- makeExample( elem, info.create, info.src );
- {
- let i = 2;
- for ( ;; ) {
- const createFn = info[ `create${i}` ];
- if ( ! createFn ) {
- break;
- }
- const shapeElem = addDiv( base, 'shape' );
- makeExample( shapeElem, createFn, info[ `src${i}` ] );
- ++ i;
- }
- }
- }
- function createDiagram( base ) {
- const name = base.dataset.diagram;
- const info = diagrams[ name ];
- if ( ! info ) {
- throw new Error( `no primitive ${name}` );
- }
- createLiveImage( base, info, name );
- }
- async function addGeometry( root, info, args = [] ) {
- const result = info.create( ...args );
- const promise = ( result instanceof Promise ) ? result : Promise.resolve( result );
- let diagramInfo = await promise;
- if ( diagramInfo instanceof THREE.BufferGeometry ) {
- const geometry = diagramInfo;
- diagramInfo = {
- geometry,
- };
- }
- const geometry = diagramInfo.geometry || diagramInfo.lineGeometry || diagramInfo.mesh.geometry;
- geometry.computeBoundingBox();
- const centerOffset = new THREE.Vector3();
- geometry.boundingBox.getCenter( centerOffset ).multiplyScalar( - 1 );
- let mesh = diagramInfo.mesh;
- if ( diagramInfo.geometry ) {
- if ( ! info.material ) {
- const material = new THREE.MeshPhongMaterial( {
- flatShading: info.flatShading === false ? false : true,
- side: THREE.DoubleSide,
- } );
- material.color.setHSL( Math.random(), .5, .5 );
- info.material = material;
- }
- mesh = new THREE.Mesh( diagramInfo.geometry, info.material );
- }
- if ( mesh ) {
- mesh.position.copy( centerOffset );
- root.add( mesh );
- }
- if ( info.showLines !== false ) {
- const lineMesh = new THREE.LineSegments(
- diagramInfo.lineGeometry || diagramInfo.geometry,
- new THREE.LineBasicMaterial( {
- color: diagramInfo.geometry ? 0xffffff : colors.lines,
- transparent: true,
- opacity: 0.5,
- } ) );
- lineMesh.position.copy( centerOffset );
- root.add( lineMesh );
- }
- }
- async function updateGeometry( root, info, params ) {
- const oldChildren = root.children.slice();
- await addGeometry( root, info, Object.values( params ) );
- oldChildren.forEach( ( child ) => {
- root.remove( child );
- child.geometry.dispose();
- } );
- }
- const primitives = {};
- async function createLiveImage( elem, info, name ) {
- const root = new THREE.Object3D();
- primitives[ name ] = primitives[ name ] || [];
- primitives[ name ].push( {
- root,
- info,
- } );
- await addGeometry( root, info );
- threejsLessonUtils.addDiagram( elem, { create: () => root } );
- }
- function getValueElem( commentElem ) {
- return commentElem.previousElementSibling &&
- commentElem.previousElementSibling.previousElementSibling &&
- commentElem.previousElementSibling.previousElementSibling.previousElementSibling;
- }
- threejsLessonUtils.onAfterPrettify( () => {
- document.querySelectorAll( '[data-primitive]' ).forEach( ( base ) => {
- const primitiveName = base.dataset.primitive;
- const infos = primitives[ primitiveName ];
- base.querySelectorAll( 'pre.prettyprint' ).forEach( ( shape, ndx ) => {
- const { root, info } = infos[ ndx ];
- const params = {};
- [ ...shape.querySelectorAll( 'span.com' ) ]
- .filter( span => span.textContent.indexOf( '// ui:' ) >= 0 )
- .forEach( ( span ) => {
- const nameRE = /ui: ([a-zA-Z0-9_]+) *$/;
- const name = nameRE.exec( span.textContent )[ 1 ];
- span.textContent = '';
- if ( ! info.ui ) {
- console.error(`no ui for ${primitiveName}:${ndx}`); // eslint-disable-line
- return;
- // throw new Error(`no ui for ${primitiveName}:${ndx}`);
- }
- const ui = info.ui[ name ];
- if ( ! ui ) {
- throw new Error( `no ui for ${primitiveName}:${ndx} param: ${name}` );
- }
- const valueElem = getValueElem( span );
- if ( ! valueElem ) {
- console.error(`no value element for ${primitiveName}:${ndx} param: ${name}`); // eslint-disable-line
- return;
- }
- const inputHolderHolder = document.createElement( 'div' );
- inputHolderHolder.className = 'input';
- const inputHolder = document.createElement( 'div' );
- span.appendChild( inputHolderHolder );
- inputHolderHolder.appendChild( inputHolder );
- switch ( ui.type ) {
- case 'range': {
- const valueRange = ui.max - ui.min;
- const input = document.createElement( 'input' );
- const inputMax = 100;
- input.type = 'range';
- input.min = 0;
- input.max = inputMax;
- const value = parseFloat( valueElem.textContent );
- params[ name ] = value * ( ui.mult || 1 );
- input.value = ( value - ui.min ) / valueRange * inputMax;
- inputHolder.appendChild( input );
- const precision = ui.precision === undefined ? ( valueRange > 4 ? 0 : 2 ) : ui.precision;
- const padding = ui.max.toFixed( precision ).length;
- input.addEventListener( 'input', () => {
- let newValue = input.value * valueRange / inputMax + ui.min;
- if ( precision === 0 ) {
- newValue = Math.round( newValue );
- }
- params[ name ] = newValue * ( ui.mult || 1 );
- valueElem.textContent = newValue.toFixed( precision ).padStart( padding, ' ' );
- updateGeometry( root, info, params );
- } );
- break;
- }
- case 'bool': {
- const input = document.createElement( 'input' );
- input.type = 'checkbox';
- params[ name ] = valueElem.textContent === 'true';
- input.checked = params[ name ];
- inputHolder.appendChild( input );
- input.addEventListener( 'change', () => {
- params[ name ] = input.checked;
- valueElem.textContent = params[ name ] ? 'true' : 'false';
- updateGeometry( root, info, params );
- } );
- break;
- }
- case 'text': {
- const input = document.createElement( 'input' );
- input.type = 'text';
- params[ name ] = valueElem.textContent.slice( 1, - 1 );
- input.value = params[ name ];
- input.maxlength = ui.maxLength || 50;
- inputHolder.appendChild( input );
- input.addEventListener( 'input', () => {
- params[ name ] = input.value;
- valueElem.textContent = `'${input.value}'`;
- updateGeometry( root, info, params );
- } );
- break;
- }
- default:
- throw new Error( `unknown type for ${primitiveName}:${ndx} param: ${name}` );
- }
- } );
- } );
- } );
- } );
- document.querySelectorAll( '[data-diagram]' ).forEach( createDiagram );
- document.querySelectorAll( '[data-primitive]' ).forEach( createPrimitiveDOM );
- }
|