SOURCE

console 命令行工具 X clear

                    
>
console
<div id="container"></div>
<script type="module">
	 
			import { TrackballControls } from 'https://threejs.org/examples/jsm/controls/TrackballControls.js';
			import { CSS2DRenderer, CSS2DObject } from 'https://threejs.org/examples/jsm/renderers/CSS2DRenderer.js';

			let camera, scene, renderer, labelRenderer;
			let controls;
			
			let root;

			const offset = new THREE.Vector3(); // pdb 原子的 
			const offsetOther = new THREE.Vector3();

			const menu = document.getElementById( 'menu' );

			init();
			animate();

			function init() {
                let obj = document.getElementById( 'container' );
				let objw =  obj.clientWidth, objh = obj.clientHeight;

				scene = new THREE.Scene();
				scene.background = new THREE.Color( 0xffffff ); // 场景背景色

				// ax,by,cz,
				camera = new THREE.OrthographicCamera( -objw /2, objw / 2, objh / 2, -objh/2, 1, 10000 );
				camera.position.set(50, 50, 1000); //设置相机位置
				camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
				scene.add( camera );

				const light1 = new THREE.DirectionalLight( 0xffffff, 0.8 );
				light1.position.set( 1, 1, 1 );
				scene.add( light1 );

				const light2 = new THREE.DirectionalLight( 0xffffff, 0.5 );
				light2.position.set( - 1, - 1, 1 );
				scene.add( light2 );

                // let light1 = new THREE.AmbientLight( 0xffffff ); //环境光
				// scene.add( light1 );


				root = new THREE.Group();
				scene.add( root );


				renderer = new THREE.WebGLRenderer( { antialias: true } );
				renderer.setPixelRatio( window.devicePixelRatio );
				renderer.setSize( window.innerWidth, window.innerHeight );
				document.getElementById( 'container' ).appendChild( renderer.domElement );

				labelRenderer = new CSS2DRenderer();
				labelRenderer.setSize( window.innerWidth, window.innerHeight );
				labelRenderer.domElement.style.position = 'absolute';
				labelRenderer.domElement.style.top = '0px';
				labelRenderer.domElement.style.pointerEvents = 'none';
				document.getElementById( 'container' ).appendChild( labelRenderer.domElement );

				//
				controls = new TrackballControls( camera, renderer.domElement );
				controls.minDistance = 1000;
				controls.maxDistance = 2000;
				controls.rotateSpeed = 2; // 转动速度
				controls.zoomSpeed = 2; // 放大缩小速度
				controls.panSpeed = 1; //缩小速度

				let buffGeometry = new THREE.BoxGeometry( 1, 1, 1 );
				var geometry = new THREE.BufferGeometry();
					let	x = 4.42, y = 4.42, z = 2.91, scal = 75, leng=scal*6;
				// let	x = 379, y = 373, z = 537;
				x *= scal; // ax
				y *= scal; // cy
				z *= scal; // bz
				
				let d = [
				// 379.6613, 0.0045 ,-159.5722,
				// -67.0589,373.6930,-159.5719,
				// -1.6247, -1.9418 ,527.6072,
						0,0,0,
						x,0,0,
						0,0,0,
						0,y,0,
						0,0,0,
						0,0,z,
						x,0,z,
						x,0,0,
						x,y,0,
						0,y,0,
						0,y,z,
						x,y,z,
						x,y,0,
						x,y,z,
						0,y,z,
						0,0,z,
						x,0,z,
						x,y,z,
				];
				var vertices = new Float32Array(d); 
				var buff = new THREE.BufferAttribute( vertices, 3 );
				geometry.addAttribute( 'position', buff);
				var material = new THREE.PointsMaterial( { 
					color: 0x00fff0,
					size:20 } );
				var line = new THREE.Line( geometry, material ); 
				scene.add( line ); // 外框

				var positions = geometry.attributes.position.clone();
				const startbuff = new THREE.Vector3(); // 原子连接线的
				const endbuff = new THREE.Vector3();

				// 外框居中
				geometry.computeBoundingBox(); 
				geometry.boundingBox.getCenter( offsetOther ).negate();
				geometry.translate( offsetOther.x, offsetOther.y, offsetOther.z );
				// ax,cy,bz
				let dirs = [{
					name:'ax',
					color:'red',
					angTxt: 'r',
				}, {
					name:'by',
					color:'green',
					angTxt: '��',
				},{
					name:'cz',
					color:'blue',
					angTxt: '��',
					
				},], ofx= offsetOther.x,ofy= offsetOther.y,ofz= offsetOther.z, labelren=[];
				for ( let i = 0; i < 6; i += 2 ) {
					// 坐标轴移动
					startbuff.x = positions.getX( i ) + ofx;
					startbuff.y = positions.getY( i ) + ofy;
					startbuff.z = positions.getZ( i ) + ofz;

					endbuff.x = positions.getX( i + 1 ); 
					endbuff.y = positions.getY( i + 1 ); 
					endbuff.z = positions.getZ( i + 1 ); 
					
					let txtDis = endbuff.length();
					let color = i==0? dirs[0].color : (i==2? dirs[1].color: dirs[2].color);
					let angTxt = i==0? dirs[0].angTxt : (i==2? dirs[1].angTxt: dirs[2].angTxt);
					let arrow = new THREE.ArrowHelper( endbuff, startbuff, txtDis+50, color, 20,10);
			
					let text = document.createElement( 'div' );
					text.className = 'txt';
					text.textContent = i==0? dirs[0].name : (i==2? dirs[1].name: dirs[2].name);
					text.style.color = color;
					let label = new CSS2DObject( text );

					let dirlen = endbuff.clone();
					let dirlabel = label.clone();

					endbuff.addScaledVector(offsetOther,1.2); //endbuff = offsetOther*2+endbuff
					txtDis = endbuff.length();
					endbuff.normalize();
					label.translateOnAxis( endbuff , Math.ceil(txtDis/1.2));
					scene.add(label, arrow); //方向轴上面的文字

					dirlen.addScaledVector(offsetOther,2); //endbuff = offsetOther*3+endbuff
					txtDis = dirlen.length();
					dirlen.normalize();
					dirlabel.translateOnAxis( dirlen , Math.ceil(txtDis/2));

					labelren.push({
						line: dirlabel,
						angTxt,
					});
				
				}


				labelren.forEach((htem,h) => {
					let end = labelren[h+1]?labelren[h+1].line : labelren[0].line;
					let li = addLines(htem.line.position,end.position, offsetOther, htem.angTxt,h);
					scene.add( li.lineMesh); // 角度的线
				})


				window.addEventListener( 'resize', onWindowResize );

			}

			function addLines(v0, v3, point, txt,h) {
                // 计算向量夹角
                // angleTo返回该向量与向量v之间的角度
                let angle = v0.angleTo(v3)*(180/Math.PI); // 0 ~ Math.PI
                let aLen = angle * -0.4,	hLen = 1;

                let p0 = point.clone(); // new THREE.Vector3(0, 0, 0);

                // 法线向量,垂直于平面的直线(叫法线)所表示的向量为该平面的法向量
                let rayLine = new THREE.Ray(p0, getVCenter(v0.clone(), p0));
                let rayLine3 = new THREE.Ray(p0, getVCenter(v3.clone(), p0));

                // 顶点坐标
                // rayLine.at(1) 获得这一Ray上给定距离处的Vector3。
                // distanceTo 计算该向量到所传入的点 间的距离。
                let vtop = rayLine.at(rayLine.at(1).distanceTo(p0));
                let vtop3 = rayLine3.at(rayLine3.at(1).distanceTo(p0));

                // 控制点坐标
                let v1 = getLenVcetor(v0.clone(), vtop3, aLen);
                let v2 = getLenVcetor(v3.clone(), vtop3, aLen);

                // 绘制贝塞尔曲线
                let curve = new THREE.CubicBezierCurve3(v0, v1, v2, v3);
                let geo = new THREE.BufferGeometry().setFromPoints(  curve.getPoints(50) );
                let mat = new THREE.LineDashedMaterial({
                    color: 0x000000,
                    dashSize: 10,//短划线的大小
            gapSize: 6,//短划线之间的距离
                });
                let line = new THREE.Line(geo, mat); 
                line.computeLineDistances();//不可或缺的,若无,则线段不能显示为虚线

                let text2 = document.createElement( 'div' );
                text2.className = 'dirtxt';
                // text2.style.color = item.color;
                text2.textContent = txt;
                let label2 = new CSS2DObject( text2 );
                // label2.translateOnAxis( v2 , 1);
                label2.position.copy( v2 );

                if(h==0){
                    label2.translateY(-p0.distanceTo(v2)/2);
                    label2.translateX(p0.distanceTo(v3)/4)
                }
                if(h==1){
                    label2.translateZ(-p0.distanceTo(v3)/2);
                    label2.translateY(p0.distanceTo(v3))
                }
                if(h==2){
                    label2.translateX(-p0.distanceTo(v3)/2);
                    label2.translateZ(p0.distanceTo(v1)/2)
                }
                scene.add( label2 ); //方向轴上面的文字
                return {
                        curve: curve,
                        lineMesh: line
                };
			}

			// 计算v1,v2 的中点
			function getVCenter(v1, v2) {
					let v = v1.add(v2); // 向量相加
					return v.divideScalar(2); // 向量除以标量等于k,相当于以因子k来缩放向量的长度
			}

			// 计算V1,V2向量固定长度的点
			// lerp 朝着进行插值,两点间的缓冲效果
			function getLenVcetor(v1, v2, len) {
					let v1v2Len = v1.distanceTo(v2);
					return v1.lerp(v2, len / v1v2Len); 
			}
		  
			function onWindowResize() {

				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();

				renderer.setSize( window.innerWidth, window.innerHeight );
				labelRenderer.setSize( window.innerWidth, window.innerHeight );

				render();

			}

			function animate() {

				requestAnimationFrame( animate );
				controls.update();

				render();

			}

			function render() {

				renderer.render( scene, camera );
				labelRenderer.render( scene, camera );

			}

		</script>
	#container{
				width:1200px;
				height:600px;
				border:1px solid #ddd;
			}

本项目引用的自定义外部资源