Browse Source

增加threejs

caner 1 year ago
parent
commit
7ae86e47db

+ 6 - 0
README.md

@@ -6,4 +6,10 @@
 4. '/fly' 二维动态导向
 4. '/fly' 二维动态导向
 5. '/plantool' 平面图缩放
 5. '/plantool' 平面图缩放
 6. '/draw' canvas画图
 6. '/draw' canvas画图
+7. '/face' 2D SVG
+    ![image](./public/demo/7.png)
+8. '/leaflet' leaflet画图
+    ![image](./public/demo/8.png)
+9. '/three' threejs 3D
+    ![image](./public/demo/9.png)
 ```
 ```

+ 3 - 3
package.json

@@ -9,7 +9,6 @@
     "preview": "vite preview"
     "preview": "vite preview"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@turf/turf": "^7.0.0",
     "@wangeditor/editor": "^5.1.18",
     "@wangeditor/editor": "^5.1.18",
     "@wangeditor/editor-for-vue": "^5.1.12",
     "@wangeditor/editor-for-vue": "^5.1.12",
     "echarts": "^5.5.1",
     "echarts": "^5.5.1",
@@ -21,13 +20,14 @@
     "openseadragon": "^4.1.1",
     "openseadragon": "^4.1.1",
     "pinia": "^2.1.7",
     "pinia": "^2.1.7",
     "pinia-plugin-persist": "^1.0.0",
     "pinia-plugin-persist": "^1.0.0",
-    "simplify-js": "^1.2.4",
     "socket.io-client": "^4.7.5",
     "socket.io-client": "^4.7.5",
     "vue": "^3.4.31",
     "vue": "^3.4.31",
     "vue-router": "^4.4.0",
     "vue-router": "^4.4.0",
-    "xlsx": "^0.18.5"
+    "xlsx": "^0.18.5",
+    "three": "^0.145.0"
   },
   },
   "devDependencies": {
   "devDependencies": {
+    "@types/three": "^0.144.0",
     "@types/leaflet": "^1.9.12",
     "@types/leaflet": "^1.9.12",
     "@types/node": "^20.10.4",
     "@types/node": "^20.10.4",
     "@typescript-eslint/parser": "^7.16.0",
     "@typescript-eslint/parser": "^7.16.0",

BIN
public/demo/7.png


BIN
public/demo/8.png


BIN
public/demo/9.png


+ 5 - 4
src/pages/App.vue

@@ -29,11 +29,12 @@ console.log(
 1. '/' 多人画板
 1. '/' 多人画板
 2. '/chart' echart 码数表
 2. '/chart' echart 码数表
 3. '/edit' 富文本编辑
 3. '/edit' 富文本编辑
-4. '/fly' 二维动态导向
-5. '/plantool' 平面图缩放
+4. '/fly' SVG二维动态导向
+5. '/plantool' openseadragon平面图缩放
 6. '/draw' canvas画图
 6. '/draw' canvas画图
-7. '/face' canvas画图
-8. '/leaflet' leaflet画图`
+7. '/face' 2D SVG画图
+8. '/leaflet' leaflet画图
+9. '/three' threejs 3D画图`
 )
 )
 </script>
 </script>
 <style>
 <style>

File diff suppressed because it is too large
+ 88 - 0
src/pages/views/three/index.vue


+ 9 - 0
src/pages/views/three/route.ts

@@ -0,0 +1,9 @@
+import { RouteRecordRaw } from 'vue-router'
+import three from './index.vue'
+export default {
+  path: '/three',
+  meta: {
+    authorize: true
+  },
+  component: three
+} as RouteRecordRaw

+ 263 - 0
src/pages/views/three/three.service.ts

@@ -0,0 +1,263 @@
+import * as Three from 'three'
+import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
+
+export interface MapOption {
+  cameraP: { X: number, Y: number, Z: number },
+  isRotate: boolean
+}
+export default class ThreeService {
+  renderer = null as unknown as Three.WebGLRenderer // 渲染器
+
+  scene = null as unknown as Three.Scene // 场景
+
+  OrbitControls = null as unknown as OrbitControls // 控制器
+
+  camera = null as unknown as Three.PerspectiveCamera // 相机
+
+  dom = null as unknown as HTMLElement
+
+  THREE = Three
+
+  /**
+   * 初始化
+   * @param domID 原始domID
+   * @param OPTION cameraP相机位置+cameraL相机指向位置+LOS视距
+   */
+  initThree(domID: string, OPTION?: MapOption) {
+    // 默认参数
+    const { cameraP, isRotate } = {
+      cameraP: { X: 0, Y: 0, Z: 18 },
+      isRotate: true,
+      ...OPTION
+    }
+    const dom = document.getElementById(domID)
+    // 场景
+    this.scene = new Three.Scene()
+    // 渲染器+透明度
+    this.renderer = new Three.WebGLRenderer({
+      alpha: true,
+      antialias: true
+    })
+    this.renderer.setPixelRatio(window.devicePixelRatio) // 清晰度
+    this.renderer.setSize(dom!.clientWidth, dom!.clientHeight) // 场景大小
+    dom!.appendChild(this.renderer.domElement) // 元素中插入canvas对象
+    // 相机
+    this.camera = new Three.PerspectiveCamera(40, dom!.clientWidth / dom!.clientHeight, 1, 10000) // 透视相机
+    this.camera.position.set(cameraP.X, cameraP.Y, cameraP.Z)
+    this.camera.lookAt(new Three.Vector3(0, 0, 0))
+    // 控制器
+    this.OrbitControls = new OrbitControls(this.camera, this.renderer.domElement) // 控制器
+    this.OrbitControls.update()
+    this.OrbitControls.enabled = isRotate // 启用控制
+    // this.OrbitControls.enableRotate = true // 启用左键控制
+    // // this.OrbitControls.autoRotate = true //自动旋转
+    // this.OrbitControls.enableZoom = true // 启用缩放
+    // this.OrbitControls.maxDistance = 500 // 最大范围
+    // this.OrbitControls.minDistance = 600 // 最小范围
+    this.dom = dom as HTMLElement
+  }
+
+  /**
+   * 鼠标点击事件
+   * @returns data点中的元素
+   */
+  onMouseDblclick() {
+    let data = null
+    this.dom.addEventListener(
+      'click',
+      (event) => {
+        // 获取所有点中模型对象
+        const intersects = this.getIntersects(event, this.dom)
+        // 获取选中最近的 Mesh 对象
+        if (intersects.length !== 0 && intersects[0].object instanceof Three.Mesh) {
+          const selectObject = intersects[0].object
+          data = selectObject
+          console.log('选中', selectObject)
+        } else {
+          data = null
+          console.log('未选中 Mesh!')
+        }
+
+        //
+        const a = new Three.Vector3()
+        a.setFromMatrixPosition(this.camera.matrixWorld)
+        const b = new Three.Euler()
+        b.setFromQuaternion(this.camera.quaternion)
+        console.log('相机位置角度', a, b)
+      },
+      false
+    )
+    return data
+  }
+
+  /**
+   * 获取与射线相交的对象数组
+   * @param event 事件
+   * @param {*} dom
+   * @returns 返回选中的对象
+   */
+  getIntersects(event: MouseEvent, dom: HTMLElement) {
+    event.preventDefault()
+    const raycaster = new Three.Raycaster()
+    const mouse = new Three.Vector2()
+    // 通过鼠标点击位置,计算出 raycaster 所需点的位置,以屏幕为中心点,范围 -1 到 1
+    // event.offsetX为模块所在位置
+    mouse.x = (event.offsetX / dom.clientWidth) * 2 - 1
+    mouse.y = -(event.offsetY / dom.clientHeight) * 2 + 1
+    // 通过鼠标点击的位置(二维坐标)和当前相机的矩阵计算出射线位置
+    raycaster.setFromCamera(mouse, this.camera)
+    // 获取与射线相交的对象数组,其中的元素按照距离排序,越近的越靠前
+    const intersects = raycaster.intersectObjects(this.scene.children, true)
+    // 返回选中的对象
+    return intersects
+  }
+
+  /**
+   *  改变对象材质属性
+   * @param object
+   */
+  changeMaterial(object: { material: Three.MeshLambertMaterial; children: string | any[]; }) {
+    const color = 0xffffff * Math.random()
+    const material = new Three.MeshLambertMaterial({
+      color,
+      transparent: !object.material.transparent,
+      opacity: 0.8
+    })
+    object.material = material
+  }
+
+  /**
+   * 挤压成型
+   * @param arrList
+   * @param zoom
+   * @param depth
+   * @param OBJOPTION
+   * @returns
+   */
+  ExtrusionForming(arrList: { x: number, y: number }[], zoom: number, depth: any, OBJOPTION: Three.MeshBasicMaterialParameters | undefined) {
+    const shape = new Three.Shape()
+    // 绘制轮廓
+    for (let j = 0; j < arrList.length; j++) {
+      const item = arrList[j]
+      if (j === 0) {
+        shape.moveTo(item.x * zoom, item.y * zoom)
+      } else {
+        shape.lineTo(item.x * zoom, item.y * zoom)
+      }
+    }
+    // 挤压参数
+    const extrudesettings = {
+      steps: 2, // 细分数
+      depth, // 深度
+      bevelEnabled: false, // 是否斜角
+      bevelThickness: 1, // 斜角厚度
+      bevelSize: 0.5, // 斜角延申距离
+      bevelOffset: 0, // 边距
+      bevelSegments: 1 // 斜角分层数
+    }
+    const geometry = new Three.ExtrudeGeometry(shape, extrudesettings)
+
+    // 创建模型
+    const material = new Three.MeshBasicMaterial(OBJOPTION)
+
+    // const material = new Three.MeshPhongMaterial( { color: '#000' } )
+    // const img = new Three.TextureLoader().load('....')
+    // material.map = img
+    const mesh = new Three.Mesh(geometry, material)
+    return mesh
+  }
+
+  /**
+   * svg 路径转点位置
+   * svg path 画图 有方向判别,自行做判断
+   * @param DOMID DOM ID
+   * @returns overarr
+   */
+  SvgPathToPrint(DOMID: string) {
+    const svg = document.getElementById(DOMID)
+    const arrList = [] as any
+    for (let j = 0; j < svg!.childNodes.length; j++) {
+      const item = svg!.childNodes[j] as any
+      const obj = {
+        id: j,
+        list: []
+      } as any
+      if (item.nodeName === 'path') {
+        const length = item.getTotalLength()
+        for (let k = 0; k < length; k += 20) {
+          obj.list.push(item.getPointAtLength(k))
+        }
+        obj.list.push(item.getPointAtLength(length))
+        arrList.push(obj)
+      }
+    }
+
+    const newARR = [ arrList[0], arrList[1], arrList[2], arrList[3] ]
+    const overarr = [] as any
+    for (let z = 0; z < newARR.length; z++) {
+      const item = newARR[z] as any
+      if (!item) continue
+      // 汇总
+      for (let d = 0; d < item.list.length; d++) {
+        const itd = item.list[d]
+        overarr.push({
+          x: Math.floor(itd.x),
+          y: Math.floor(itd.y)
+        })
+      }
+    }
+    return overarr
+  }
+
+  /**
+   * 获取两点间的点
+   * @param Vector3
+   * @param num
+   * @returns
+   */
+  getPoints(Vector3: { s: (number | undefined)[]; e: (number | undefined)[]; }, num = 59) {
+    const curve = new Three.CatmullRomCurve3(
+      [
+        new Three.Vector3(Vector3.s[0], Vector3.s[1], Vector3.s[2]),
+        new Three.Vector3(Vector3.e[0], Vector3.e[1], Vector3.e[2])
+      ],
+      false,
+      'catmullrom',
+      0.5
+    )
+    // const points = curve.getPoints(60).reduce((arr, item) => {
+    //   return arr.concat(item.x, item.y, item.z)
+    // }, [])
+    const points = curve.getSpacedPoints(num)
+    return points
+  }
+
+  /**
+   * 设置中心
+   * @param obj
+   * @param option
+   * @returns
+   */
+  setObjCenter(obj: Three.Object3D<Three.Event>, option = { x: 0, y: 0, z: 0 }) {
+    const group = new Three.Object3D()
+    group.position.set(option.x, option.y, option.z)
+    obj.position.set(-option.x, -option.y, -option.z)
+    group.add(obj)
+    return group
+  }
+
+  /**
+   * 清楚缓存
+   */
+  clearThreeOBJ() {
+    // 清除所有缓存
+    this.scene.clear()
+    this.scene.remove()
+    this.renderer.dispose()
+    this.renderer.forceContextLoss();
+    (this.renderer as any).content = null
+    this.renderer.domElement?.remove()
+    this.renderer.domElement = null as any
+    Three.Cache.clear()
+  }
+}

Some files were not shown because too many files changed in this diff