• 立体solid.ts


    1 import {chunk} from '@mathigon/core';
    2 import {$html, $N, Browser, CustomElementView, register, slide} from '@mathigon/boost';
    3 import {create3D, Graphics3D} from './webgl';
    4 
    5 const STROKE_COLOR = 0x666666;
    6 const LINE_RADIUS = 0.012;
    7 const LINE_SEGMENTS = 4;
    8 const POINT_RADIUS = 0.08;
    import
     1 // Utilities
     2 
     3 type Vector = [number, number, number];
     4 
     5 // Custom methods on the THREE.Object3D class
     6 interface Object3D extends THREE.Object3D {
     7   setClipPlanes?: (planes: THREE.Plane[]) => void;
     8   updateGeometry?: (gep: THREE.Geometry) => void;
     9   updateEnds?: (f: Vector, t: Vector) => void;
    10 }
    11 
    12 function rotate($solid: Solid, animate = true, speed = 1) {
    13   // TODO Damping after mouse movement
    14   // TODO Better mouse-to-point mapping
    15 
    16   // Only Chrome is fast enough to support auto-rotation.
    17   const autoRotate = animate && Browser.isChrome && !Browser.isMobile;
    18   $solid.css('cursor', 'grab');
    19 
    20   let dragging = false;
    21   let visible = false;
    22 
    23   function frame() {
    24     if (visible && autoRotate) requestAnimationFrame(frame);
    25     $solid.scene.draw();
    26     if (!dragging) $solid.object.rotation.y += speed * 0.012;
    27   }
    28 
    29   if (autoRotate) {
    30     $solid.scene.$canvas.on('enterViewport', () => { visible = true; frame(); });
    31     $solid.scene.$canvas.on('exitViewport', () => { visible = false; });
    32   } else {
    33     setTimeout(frame);
    34   }
    35 
    36   // The 1.1 creates rotations that are slightly faster than the mouse/finger.
    37   const s = Math.PI / 2 / $solid.scene.$canvas.width * 1.1;
    38 
    39   slide($solid.scene.$canvas, {
    40     start() {
    41       dragging = true;
    42       $html.addClass('grabbing');
    43     },
    44     move(posn, start, last) {
    45       const d = posn.subtract(last).scale(s);
    46       const q = new THREE.Quaternion().setFromEuler(new THREE.Euler(d.y, d.x));
    47       $solid.object.quaternion.multiplyQuaternions(q, $solid.object.quaternion);
    48       $solid.trigger('rotate', {quaternion: $solid.object.quaternion});
    49       if (!autoRotate) frame();
    50     },
    51     end() {
    52       dragging = false;
    53       $html.removeClass('grabbing');
    54     }
    55   });
    56 }
    57 
    58 function createEdges(geometry: THREE.Geometry, material: THREE.Material, maxAngle?: number) {
    59   const obj = new THREE.Object3D();
    60   if (!maxAngle) return obj;
    61 
    62   const edges = new THREE.EdgesGeometry(geometry, maxAngle);
    63   const edgeData = edges.attributes.position.array as number[];
    64   const points = chunk(chunk(edgeData, 3).map(p => new THREE.Vector3(...p)), 2);
    65 
    66   for (const edge of points) {
    67     const curve = new THREE.LineCurve3(edge[0], edge[1]);
    68     const geometry = new THREE.TubeGeometry(curve, 1, LINE_RADIUS, LINE_SEGMENTS);
    69     obj.add(new THREE.Mesh(geometry, material));
    70   }
    71 
    72   return obj;
    73 }
    Utilities
     1 // Custom Element
     2 
     3 @register('x-solid')
     4 export class Solid extends CustomElementView {
     5   private isReady = false;
     6   object!: THREE.Object3D;
     7   scene!: Graphics3D;
     8 
     9   async ready() {
    10     const size = this.attr('size').split(',');
    11     const width = +size[0];
    12     const height = size.length > 1 ? +size[1] : width;
    13 
    14     this.css({ width + 'px', height: height + 'px'});
    15 
    16     this.scene = await create3D(this, 35, 2 * width, 2 * height);
    17     this.scene.camera.position.set(0, 3, 6);
    18     this.scene.camera.up = new THREE.Vector3(0, 1, 0);
    19     this.scene.camera.lookAt(new THREE.Vector3(0, 0, 0));
    20 
    21     const light1 = new THREE.AmbientLight(0x555555);
    22     this.scene.add(light1);
    23 
    24     const light2 = new THREE.PointLight(0xffffff);
    25     light2.position.set(3, 4.5, 6);
    26     this.scene.add(light2);
    27 
    28     this.object = new THREE.Object3D();
    29     this.scene.add(this.object);
    30 
    31     this.trigger('loaded');
    32     this.isReady = true;
    33   }
    34 
    35   addMesh(fn: (scene: Graphics3D) => THREE.Object3D[]|void) {
    36     if (this.isReady) {
    37       this.addMeshCallback(fn);
    38     } else {
    39       this.one('loaded', () => this.addMeshCallback(fn));
    40     }
    41   }
    42 
    43   addMeshCallback(fn: (scene: Graphics3D) => THREE.Object3D[]|void) {
    44     const items = fn(this.scene) || [];
    45     for (const i of items)  this.object.add(i);
    46 
    47     if (!this.hasAttr('static')) {
    48       const speed = +this.attr('rotate') || 1;
    49       rotate(this, this.hasAttr('rotate'), speed);
    50     }
    51 
    52     this.scene.draw();
    53   }
    54 
    55   rotate(q: THREE.Quaternion) {
    56     this.object.quaternion.set(q.x, q.y, q.z, q.w);
    57     this.scene.draw();
    58   }
    Custom Element
      1 // Element Creation Utilities
      2 
      3   addLabel(text: string, posn: Vector, color = STROKE_COLOR, margin = '') {
      4     const $label = $N('div', {text, class: 'label3d'});
      5     $label.css('color', '#' + color.toString(16).padStart(6, '0'));
      6     if (margin) $label.css('margin', margin);
      7 
      8     let posn1 = new THREE.Vector3(...posn);
      9     this.scene.$canvas.insertAfter($label);
     10 
     11     this.scene.onDraw(() => {
     12       const p = posn1.clone().applyQuaternion(this.object.quaternion)
     13           .add(this.object.position).project(this.scene.camera);
     14       $label.css('left', (1 + p.x) * this.scene.$canvas.width / 2 + 'px');
     15       $label.css('top', (1 - p.y) * this.scene.$canvas.height / 2 + 'px');
     16     });
     17 
     18     return {
     19       updatePosition(posn: Vector) {
     20         posn1 = new THREE.Vector3(...posn);
     21       }
     22     };
     23   }
     24 
     25   addArrow(from: Vector, to: Vector, color = STROKE_COLOR) {
     26     const material = new THREE.MeshBasicMaterial({color});
     27     const obj = new THREE.Object3D() as Object3D;
     28 
     29     const height = new THREE.Vector3(...from).distanceTo(new THREE.Vector3(...to));
     30     const line = new THREE.CylinderGeometry(0.02, 0.02, height - 0.3, 8, 1, true);
     31     obj.add(new THREE.Mesh(line, material));
     32 
     33     const start = new THREE.ConeGeometry(0.1, 0.15, 16, 1);
     34     start.translate(0, height/2 - 0.1, 0);
     35     obj.add(new THREE.Mesh(start, material));
     36 
     37     const end = new THREE.ConeGeometry(0.1, 0.15, 16, 1);
     38     end.rotateX(Math.PI);
     39     end.translate(0, -height/2 + 0.1, 0);
     40     obj.add(new THREE.Mesh(end, material));
     41 
     42     obj.updateEnds = function(f: Vector, t: Vector) {
     43       // TODO Support changing the height of the arrow.
     44       const q = new THREE.Quaternion();
     45       const v = new THREE.Vector3(t[0]-f[0], t[1]-f[1], t[2]-f[2]).normalize();
     46       q.setFromUnitVectors(new THREE.Vector3(0, 1, 0), v);
     47       obj.setRotationFromQuaternion(q);
     48       obj.position.set((f[0]+t[0])/2, (f[1]+t[1])/2, (f[2]+t[2])/2);
     49     };
     50 
     51     obj.updateEnds(from, to);
     52     this.object.add(obj);
     53     return obj;
     54   }
     55 
     56   addCircle(radius: number, color = STROKE_COLOR, segments = 64) {
     57     const path = new THREE.Curve<THREE.Vector3>();
     58     path.getPoint = function(t) {
     59       const a = 2 * Math.PI * t;
     60       return new THREE.Vector3(radius * Math.cos(a), radius * Math.sin(a), 0);
     61     };
     62 
     63     const material = new THREE.MeshBasicMaterial({color});
     64     const geometry = new THREE.TubeGeometry(path, segments, LINE_RADIUS, LINE_SEGMENTS);
     65 
     66     const mesh = new THREE.Mesh(geometry, material);
     67     this.object.add(mesh);
     68     return mesh;
     69   }
     70 
     71   addPoint(position: Vector, color = STROKE_COLOR) {
     72     const material = new THREE.MeshBasicMaterial({color});
     73     const geometry = new THREE.SphereGeometry(POINT_RADIUS, 16, 16);
     74 
     75     const mesh = new THREE.Mesh(geometry, material);
     76     mesh.position.set(...position);
     77     this.object.add(mesh);
     78   }
     79 
     80   addSolid(geo: THREE.Geometry, color: number, maxAngle = 5, flatShading = false) {
     81     const edgeMaterial = new THREE.LineBasicMaterial({color: 0xffffff});
     82     const edges = new THREE.EdgesGeometry(geo, maxAngle);
     83 
     84     const obj = new THREE.Object3D();
     85     obj.add(new THREE.LineSegments(edges, edgeMaterial));
     86     obj.add(new THREE.Mesh(geo, Solid.solidMaterial(color, flatShading)));
     87 
     88     this.object.add(obj);
     89     return obj;
     90   }
     91 
     92   // TODO merge addOutlined() and addWireframe(), by looking at
     93   //      geometry.isConeGeometry etc.
     94 
     95   // A translucent material with a solid border.
     96   addOutlined(geo: THREE.Geometry, color = 0xaaaaaa, maxAngle = 5, opacity = 0.1, strokeColor?: number) {
     97     const solidMaterial = Solid.translucentMaterial(color, opacity);
     98     const solid = new THREE.Mesh(geo, solidMaterial);
     99 
    100     const edgeMaterial = new THREE.MeshBasicMaterial({color: strokeColor || STROKE_COLOR});
    101     let edges = createEdges(geo, edgeMaterial, maxAngle);
    102 
    103     const obj = new THREE.Object3D() as Object3D;
    104     obj.add(solid, edges);
    105 
    106     obj.setClipPlanes = function(planes: THREE.Plane[]) {
    107       solidMaterial.clippingPlanes = planes;
    108     };
    109 
    110     obj.updateGeometry = function(geo: THREE.Geometry) {
    111       solid.geometry.dispose();
    112       solid.geometry = geo;
    113       obj.remove(edges);
    114       edges = createEdges(geo, edgeMaterial, maxAngle);
    115       obj.add(edges);
    116     };
    117 
    118     this.object.add(obj);
    119     return obj;
    120   }
    121 
    122   // Like .addOutlined, but we also add outlines for curved edges (e.g. of
    123   // a sphere or cylinder).
    124   addWireframe(geometry: THREE.Geometry, color = 0xaaaaaa, maxAngle = 5, opacity = 0.1) {
    125     const solid = this.addOutlined(geometry, color, maxAngle, opacity);
    126 
    127     const outlineMaterial = new THREE.MeshBasicMaterial({
    128       color: STROKE_COLOR,
    129       side: THREE.BackSide
    130     });
    131     outlineMaterial.onBeforeCompile = function(shader) {
    132       const token = '#include <begin_vertex>';
    133       const customTransform = '
    vec3 transformed = position + vec3(normal) * 0.02;
    ';
    134       shader.vertexShader = shader.vertexShader.replace(token,customTransform)
    135     };
    136     const outline = new THREE.Mesh(geometry, outlineMaterial);
    137 
    138     const knockoutMaterial = new THREE.MeshBasicMaterial({
    139       color: 0xffffff,
    140       side: THREE.BackSide
    141     });
    142     const knockout = new THREE.Mesh(geometry, knockoutMaterial);
    143 
    144     const obj = new THREE.Object3D() as Object3D;
    145     obj.add(solid, outline, knockout);
    146 
    147     obj.setClipPlanes = function(planes: THREE.Plane[]) {
    148       if (solid.setClipPlanes) solid.setClipPlanes(planes);
    149       for (const m of [outlineMaterial, knockoutMaterial])
    150         m.clippingPlanes = planes;
    151     };
    152 
    153     obj.updateGeometry = function(geo: THREE.Geometry) {
    154       if (solid.updateGeometry) solid.updateGeometry(geo);
    155       for (const mesh of [outline, knockout]) {
    156         mesh.geometry.dispose();
    157         mesh.geometry = geo;
    158       }
    159     };
    160 
    161     this.object.add(obj);
    162     return obj;
    163   }
    Element Creation Utilities
     1 // Materials
     2 
     3   static solidMaterial(color: number, flatShading = false) {
     4     return new THREE.MeshPhongMaterial({
     5       side: THREE.DoubleSide,
     6       transparent: true,
     7       opacity: 0.9,
     8       specular: 0x222222,
     9       // depthWrite: false,
    10       color, flatShading
    11     });
    12   }
    13 
    14   static translucentMaterial(color: number, opacity = 0.1) {
    15     return new THREE.MeshLambertMaterial({
    16       side: THREE.DoubleSide,
    17       transparent: true,
    18       depthWrite: false,
    19       opacity, color
    20     });
    21   }
    22 }
    Materials
  • 相关阅读:
    Android数据的四种存储方式SharedPreferences、SQLite、Content Provider和File
    android的五大布局(layout)
    json数据进行格式化
    将utf-8的中文或者字符都看成一个字符
    Mysql 中 trim 的用法
    生成密码函数
    Eclipse智能提示设置
    Java Jersey2使用总结
    Java对存储过程的调用方法
    Jersey框架
  • 原文地址:https://www.cnblogs.com/wangshixi12/p/12405513.html
Copyright © 2020-2023  润新知