cesium 模型裁剪分析(模型内部裁剪和外部裁剪)

14 篇文章 10 订阅
13 篇文章 0 订阅

一、模型裁剪分析

对3D Tiles模型进行裁剪分析,可任意绘制多边形范围进行裁剪,具体效果如下

外部裁剪效果图

内部裁剪效果图

 二、完整代码

<template>
  <div id="geologyClipPlanDiv" v-if="geologyClipPlanIsShow">
    <table style="text-align: right;">
      <tr>
        <td colspan="2" style="text-align: left">
          <span style="font-size: 18px; font-weight: 600;">模型裁剪分析</span>
          <div class="closerGeologyClipPlan" @click="handCloserGeologyClipPlan"></div>
        </td>
      </tr>
      <tr>
        <td colspan="2">
          <div
            style="height: 4px;background-color: rgba(29,164,220,0.6);margin: 4px;"
          ></div>
        </td>
      </tr>
      <tr>
        <td>裁剪类型:</td>
        <td>
          <el-radio v-model="modelType" label="0">外部裁剪</el-radio>
          <el-radio v-model="modelType" label="1">内部裁剪</el-radio>
        </td>
      </tr>
      <tr>
        <td colspan="2">
          <el-button size="mini" :disabled="isDrawGeologyClipPlan" @click="drawGeologyClipPlan">绘制裁剪范围</el-button>
          <el-button size="mini" @click="clearGeologyClipPlan">清除</el-button>
        </td>
      </tr>
    </table>
  </div>
</template>
<script>
import * as turf from '@turf/turf'

let geologyClipPlanObj = null
let handlerGeologyClipPlan = null
let floatingPointList = []
let activeShapePoints = [];

let floatingPoint = undefined;
let activeShape = undefined;

let my3dtiles = undefined
let drawList = []
let inverseTransform = undefined

export default {
  name: "geologyClipPlan",
  data() {
    return {
      geologyClipPlanIsShow: false,
      isDrawGeologyClipPlan: false,
      modelType: '0', // 开挖深度
    };
  },
  methods: {
    openGeologyClipPlan() {
      this.geologyClipPlanIsShow = true;
      const pTileset = window.viewer.scene.primitives.add(new Cesium.Cesium3DTileset({
        url: 'http://localhost/models/3dModels/dbjz/tileset.json',
        maximumScreenSpaceError: 16, // 最大的屏幕空间误差
      }))
      pTileset.readyPromise.then((palaceTileset) => {
        my3dtiles = palaceTileset
      })
    },

    handCloserGeologyClipPlan() {
      this.geologyClipPlanIsShow = false;
      this.clearGeologyClipPlan()
    },
    
    drawGeologyClipPlan(){
      this.clearGeologyClipPlan()
      this.isDrawGeologyClipPlan = true
      
      inverseTransform = this.getInverseTransform(my3dtiles)

      window.viewer._container.style.cursor = 'pointer';

      // 取消双击事件-追踪该位置
      window.viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);

      handlerGeologyClipPlan = new Cesium.ScreenSpaceEventHandler(window.viewer.scene.canvas);
      handlerGeologyClipPlan.setInputAction((event) => {
        
        if (!my3dtiles.clippingPlanes || !my3dtiles.clippingPlanes._planes.length) {
          const pick = window.viewer.scene.pickPosition(event.position)
          const pickWGS = this.cart3ToWGS(pick)
          const pickModel = window.viewer.scene.pick(event.position)
          if (pickModel) {
            drawList.push(pickWGS)

            if (activeShapePoints.length === 0) {
              floatingPoint = this.createPoint(pick);
              floatingPointList.push(floatingPoint)
              activeShapePoints.push(pick);
              var dynamicPositions = new Cesium.CallbackProperty(function () {
                return new Cesium.PolygonHierarchy(activeShapePoints);
              }, false);
              activeShape = this.drawShape(dynamicPositions);
            }
            activeShapePoints.push(pick);   
            floatingPointList.push(this.createPoint(pick))    
          }
        }
      }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

      handlerGeologyClipPlan.setInputAction((event) => {
        if (Cesium.defined(floatingPoint)) {
          var newPosition = window.viewer.scene.pickPosition(event.endPosition);
          if (Cesium.defined(newPosition)) {
            floatingPoint.position.setValue(newPosition);
            activeShapePoints.pop();
            activeShapePoints.push(newPosition);
          }
        }
      }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

      handlerGeologyClipPlan.setInputAction((event) => {
        if (!my3dtiles.clippingPlanes || !my3dtiles.clippingPlanes._planes.length) {
          if (drawList.length < 3) {
            this.$message({
              message: '提示:需要绘制三个以上点, 请继续绘制!',
              type: 'warning'
            });
          } else {
            this.terminateShape();
            const unionClippingRegions = this.modelType === '0' ? true : false 
            drawList = this.isDirRes(drawList,unionClippingRegions)
            const Planes = []
            for (let i = 0; i < drawList.length; i++) {
              if (i === (drawList.length - 1)) {
                Planes.push(this.createPlane(drawList[i], drawList[0], inverseTransform))
              } else {
                Planes.push(this.createPlane(drawList[i], drawList[i + 1], inverseTransform))
              }
            }
            console.log(Planes)
            const PlaneCollection = new Cesium.ClippingPlaneCollection({
              planes: Planes,
              unionClippingRegions // 再做优化
            })
            my3dtiles.clippingPlanes = PlaneCollection
          }

          handlerGeologyClipPlan.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
          handlerGeologyClipPlan.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK)
          handlerGeologyClipPlan.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE)
          handlerGeologyClipPlan = null
          this.isDrawGeologyClipPlan = false
          window.viewer._container.style.cursor = 'default';
        }
      }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
    },

    getInverseTransform(tileSet) {
      let transform
      const tmp = tileSet.root.transform
      if ((tmp && tmp.equals(Cesium.Matrix4.IDENTITY)) || !tmp) {
        transform = Cesium.Transforms.eastNorthUpToFixedFrame(tileSet.boundingSphere.center)
      } else {
        transform = Cesium.Matrix4.fromArray(tileSet.root.transform)
      }
      return Cesium.Matrix4.inverseTransformation(transform, new Cesium.Matrix4())
    },

    cart3ToWGS(cart3){
      return {
        lat: Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(cart3).latitude),
        lng: Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(cart3).longitude)
      }
    },

    createPlane(p1, p2, inverseTransform) {
      // 将仅包含经纬度信息的p1,p2,转换为相应坐标系的cartesian3对象
      const p1C3 = this.getOriginCoordinateSystemPoint(p1, inverseTransform)
      const p2C3 = this.getOriginCoordinateSystemPoint(p2, inverseTransform)

      // 定义一个垂直向上的向量up
      const up = new Cesium.Cartesian3(0, 0, 10)
      //  right 实际上就是由p1指向p2的向量
      const right = Cesium.Cartesian3.subtract(p2C3, p1C3, new Cesium.Cartesian3())

      // 计算normal, right叉乘up,得到平面法向量,这个法向量指向right的右侧
      let normal = Cesium.Cartesian3.cross(right, up, new Cesium.Cartesian3())
      normal = Cesium.Cartesian3.normalize(normal, normal)

      // 由于已经获得了法向量和过平面的一点,因此可以直接构造Plane,并进一步构造ClippingPlane
      const planeTmp = Cesium.Plane.fromPointNormal(p1C3, normal)
      return Cesium.ClippingPlane.fromPlane(planeTmp)
    },

    getOriginCoordinateSystemPoint(point, inverseTransform) {
      const val = Cesium.Cartesian3.fromDegrees(point.lng, point.lat)
      return Cesium.Matrix4.multiplyByPoint(
        inverseTransform, val, new Cesium.Cartesian3(0, 0, 0))
    },    
    
  
    clearGeologyClipPlan(){
      floatingPointList = []
      activeShapePoints = []
      if(geologyClipPlanObj){
        geologyClipPlanObj.clear()
        geologyClipPlanObj = null
      }
      this.isDrawGeologyClipPlan = false
      if(window.TilesetsList.length > 0){
        let tilestObj = window.TilesetsList[0].tileset
        tilestObj.clippingPlanes ? (tilestObj.clippingPlanes.removeAll(),tilestObj.clippingPlanes = undefined) : ''
      }
      
      my3dtiles = undefined
      drawList = []
    },

    isDirRes(polygon,isClockwise){
      debugger
      var lineStringList = [];
      polygon.forEach((p) => {
        lineStringList.push([p.lng, p.lat]);
      });

      var clockwiseRing = turf.lineString(lineStringList);
      let isR = turf.booleanClockwise(clockwiseRing)

      var points = [];
      if(isClockwise){
        if (!isR) {
          points = polygon
        } else {
          var count = 0;
          for (var ii = polygon.length - 1; ii >= 0; ii--) {
            points[count] = polygon[ii];
            count++;
          }
        }
      }else{
        if (isR) {
          points = polygon
        } else {
          var count = 0;
          for (var ii = polygon.length - 1; ii >= 0; ii--) {
            points[count] = polygon[ii];
            count++;
          }
        }
      }
      return points
    },

    drawShape(positionData) {
      var shape = window.viewer.entities.add({
          polygon: {
            hierarchy: positionData,
            material: new Cesium.ColorMaterialProperty(
              Cesium.Color.BLUE.withAlpha(0.2)
            ),
          },
        });
      return shape;
    },

    createPoint(worldPosition) {
      var point = window.viewer.entities.add({
        position: worldPosition,
        point: {
          color: Cesium.Color.RED,
          pixelSize: 5,
          heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, 
        },
      });
      return point;
    },

    terminateShape() {
      activeShapePoints.pop();
      var pol = this.drawShape(activeShapePoints);
      floatingPointList.forEach(p=>{
        window.viewer.entities.remove(p);
      })
      window.viewer.entities.remove(floatingPoint);
      window.viewer.entities.remove(activeShape);
      window.viewer.entities.remove(pol);
      floatingPoint = undefined;
      activeShape = undefined;
      
      activeShapePoints = [];
    },
  }
};
</script>

<style lang="scss" scoped>
.closerGeologyClipPlan {
  text-decoration: none;
  position: absolute;
  top: 20px;
  right: 10px;
  z-index: 20;
}
.closerGeologyClipPlan:after {
  // content: "✖";
  content: "\e60b";
  font-family: "iconfont";
  font-size: 22px;
  color: rgba($color: gray, $alpha: 0.8);
}

#geologyClipPlanDiv {
  color: rgba(29, 164, 220, 0.9);
  background: rgba(34, 69, 91, 0.7);
  border-radius: 6px;
  padding: 10px;

  .closerGeologyClipPlan {
    top: 1vh;
    right: 0.6vw;
    cursor: pointer;
  }

  /deep/ .el-button {
    background: rgba(18, 167, 204, 0.52);
    color: #cef2ff;
    border: 0px;
  }

  /deep/ .el-radio__inner{
    background-color:rgba(18, 167, 204, 0.82);
    border: 1px solid rgba(0,185,241,1);
  }
  /deep/ .el-radio{
    color: rgba(29, 164, 220, 0.9);
    margin-right: 10px;
  }
}

#geologyClipPlanDiv td {
  padding: 4px 2px;
}
</style>

  • 8
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论
Cesium中进行模型裁剪可以通过使用ClippingPlaneCollection和ClippingPlane来实现。首先,你需要创建一个ClippingPlaneCollection对象,其中包含一个或多个ClippingPlane对象。每个ClippingPlane对象定义了一个平面的方向和平面到原点的距离。然后,你可以将这个ClippingPlaneCollection对象应用于你想要进行裁剪模型上。 以下是在Cesium中进行模型裁剪的步骤: 1. 初始化地球并开启深度测试,这可以通过设置Cesium.Viewer对象的相关属性来实现。例如,可以设置`viewer.scene.globe.depthTestAgainstTerrain = true;`来开启深度测试。 2. 创建一个ClippingPlaneCollection对象,并定义一个或多个ClippingPlane对象。每个ClippingPlane对象需要指定平面的方向和平面到原点的距离。例如,可以使用以下代码创建一个包含一个平面的ClippingPlaneCollection对象: ```javascript var clippingPlanes = new Cesium.ClippingPlaneCollection({ planes: [ new Cesium.ClippingPlane(new Cesium.Cartesian3(0, 0, -1), 0) ], edgeColor: Cesium.Color.WHITE, edgeWidth: 0 }); ``` 这里的平面方向为`(0, 0, -1)`,即垂直于地球表面向下的方向,平面到原点的距离为0。 3. 将ClippingPlaneCollection对象应用于你想要进行裁剪模型上。具体的操作取决于你使用的模型类型。例如,如果你使用3D Tiles模型,可以使用`cesium3DTileset.clippingPlanes`属性将ClippingPlaneCollection对象赋值给cesium3DTileset对象进行裁剪。 综上所述,使用ClippingPlaneCollection和ClippingPlane对象可以在Cesium中实现模型裁剪。通过定义平面的方向和距离,并将ClippingPlaneCollection对象应用于模型上,你可以实现动态裁剪模型的效果。 请注意,以上提供的代码片段仅供参考,具体的实现方式可能因你的项目需求而有所不同。你可以参考Cesium的文档和示例来获取更详细的信息和代码示例。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Cesium专栏-裁剪效果(基于3dtiles模型,附源码下载)](https://blog.csdn.net/liguoweioo/article/details/120376678)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

向着太阳往前冲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值