/* 引入Cesium */ // import * as Cesium from 'Cesium'; /* 引入算法 */ import * as turf from "@turf/turf"; /** * 剖切 * 目前仅支持凸多边形,如果绘制的是凹多边形,可能裁剪结果不正确 */ class Cutting { /** * 默认初始化 * @param {Object} viewer 三维场景 */ constructor(viewer) { if (!viewer) throw new Cesium.DeveloperError('no viewer object!'); this._viewer = viewer; this._camera = this._viewer.camera; this._scene = this._viewer.scene; this.targetY = 0.0; this._mouseHandler(); } /** * @ignore 忽略注释,注释不生成Doc */ _mouseHandler() { let _self = this; let viewer = _self._viewer; let scene = _self._scene; let selectedPlane; let downHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas); // 监听鼠标按下时,选择平面并设置样式 downHandler.setInputAction(function(movement) { let pickedObject = scene.pick(movement.position); if (Cesium.defined(pickedObject) && Cesium.defined(pickedObject.id) && Cesium.defined(pickedObject.id.plane)) { // 获取选中平面对象 selectedPlane = pickedObject.id.plane; // 选中时修改平面的材质透明度 selectedPlane.material = Cesium.Color.RED.withAlpha(0.05); selectedPlane.outlineColor = Cesium.Color.RED; // 当鼠标选中平面后,禁止场景的拖拽 scene.screenSpaceCameraController.enableInputs = false; } }, Cesium.ScreenSpaceEventType.LEFT_DOWN); // 监听鼠标向上释放时,平面设置样式 let upHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas); upHandler.setInputAction(function() { if (Cesium.defined(selectedPlane)) { // 鼠标松开时复原平面的材质透明度 selectedPlane.material = Cesium.Color.RED.withAlpha(0.1); selectedPlane.outlineColor = Cesium.Color.RED; selectedPlane = undefined; } // 当鼠标松开选中平面后,开启场景的拖拽 scene.screenSpaceCameraController.enableInputs = true; }, Cesium.ScreenSpaceEventType.LEFT_UP); // 监听鼠标选中移动时,设置平面 let moveHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas); moveHandler.setInputAction(function(movement) { //当存在选中平面时执行 if (Cesium.defined(selectedPlane)) { // 移动起点的高度减去移动终点的高度 let deltaY = movement.startPosition.y - movement.endPosition.y; // 目标高度 _self.targetY += deltaY; } }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); } /** * 修改平面的高度 * @ignore 忽略注释,注释不生成Doc */ _createPlaneUpdateFunction(plane) { let _self = this; return function() { plane.distance = _self.targetY; return plane; }; } /** * 创建裁剪面 * @ignore 忽略注释,注释不生成Doc * @param p1 起始点 * @param p2 结束点 * @param inverseTransform 矩阵 * @returns {*} ClippingPlane裁剪面(面法向量,点到面的垂直距离) */ _createPlane(p1, p2, inverseTransform) { let _self = this; // 将仅包含经纬度信息的p1,p2,转换为相应坐标系的cartesian3对象 let p1C3 = _self._getOriginCoordinateSystemPoint(p1, inverseTransform); let p2C3 = _self._getOriginCoordinateSystemPoint(p2, inverseTransform); // 定义一个垂直向上的向量up let up = new Cesium.Cartesian3(0, 0, 10); // right 实际上就是由p1指向p2的向量 (这里是p2--》p1) let 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 let planeTmp = Cesium.Plane.fromPointNormal(p1C3, normal); return Cesium.ClippingPlane.fromPlane(planeTmp); } /** * 对点进行坐标转换 * @ignore 忽略注释,注释不生成Doc * @param point 点坐标 数组形式 * @param inverseTransform 转换举证 * @returns {*} ClippingPlane 裁切面 */ _getOriginCoordinateSystemPoint(point, inverseTransform) { let val = Cesium.Cartesian3.fromDegrees(point[0], point[1]) return Cesium.Matrix4.multiplyByPoint( inverseTransform, val, new Cesium.Cartesian3(0, 0, 0)) } /** * @ignore 忽略注释,注释不生成Doc * @param {Object} tileSet */ _getInverseTransform(tileSet) { let transform; // 3dTiles模型加载后的矩阵,可以f12打印查看:tileset.root.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()); } /** * @ignore 忽略注释,注释不生成Doc * @param {Object} polygon * @param {Object} isClockwise */ _isDirRes(polygon, isClockwise) { 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 } } /** * 通用对外公开函数 */ Object.assign(Cutting.prototype, /** @lends Cutting.prototype */ { /** * 模型剪切 * @param {Object} tileset */ activate(tileset) { let _self = this; let viewer = _self._viewer; //转换矩阵 let inverseTransform = _self._getInverseTransform(tileset) // clippingPlane集合 // _self.polygon = _self._isDirRes(_self.polygon, false) const clippingPlanes1 = [] // for (let i = 0; i < _self.polygon.length; i++) { // if (i === (_self.polygon.length - 1)) { clippingPlanes1.push(_self._createPlane([121.55814450142213, 37.39658788787028], [121.65814450142213, 37.49658788787028], inverseTransform)) // } else { // clippingPlanes1.push(_self._createPlane(_self.polygon[i], _self.polygon[i + 1], inverseTransform)) // } // } // 创建裁剪平面 let clippingPlanes = new Cesium.ClippingPlaneCollection({ //一组ClippingPlane对象,用于选择性地禁用每个平面外部的渲染。 planes: [ // 裁剪面两个参数的:第一个为平面法向量,第二个为原点到平面的垂直距离 new Cesium.ClippingPlane( //笛卡尔3:表示为三维空间的平面的法向量,x表示为该法向量在x轴上的分量,y表示为该法向量在y轴上的分量,z表示为该法向量在z轴上的分量 // new Cesium.Cartesian3(0.0, 0.0, -1.0), // new Cesium.Cartesian3(1, 0, 0), new Cesium.Cartesian3(0.0, 1.0, 0), -10 ), ], // planes: clippingPlanes1, //应用于裁剪对象的边缘的高光的宽度(以像素为单位) edgeWidth: 1.0, }); _self.tileset = tileset; tileset.clippingPlanes = clippingPlanes; //切割面 tileset.debugShowBoundingVolume = false; //打开包围卷 // tileset.modelMatrix = new Cesium.Matrix4.fromTranslation( // new Cesium.Cartesian3(15.0, -58.6, 50.825) // ); // tileset.modelMatrix = new Cesium.Matrix4.fromTranslation( // Cesium.Cartesian3.fromDegrees(121.55814450142213, 37.39658788787028) // ); // 当模型准备好时执行 return tileset.readyPromise .then(function() { let boundingSphere = tileset.boundingSphere; //外包球 let radius = boundingSphere.radius; //外包球半径 // 定位到模型,并设置相机的俯仰角和距离 viewer.zoomTo( tileset, new Cesium.HeadingPitchRange(0.5, -0.2, radius / 5) ); // let pointEntity = _self._viewer.entities.add({ // position: boundingSphere.center, // point: { // pixelSize: 0, // }, // }); // _self.LocateUtil.flyToEntity(pointEntity); if ( !Cesium.Matrix4.equals( tileset.root.transform, Cesium.Matrix4.IDENTITY ) ) { // The clipping plane is initially positioned at the tileset's root transform. // Apply an additional matrix to center the clipping plane on the bounding sphere center. //裁剪平面最初定位于tileset的根变换。 //应用一个额外的矩阵使裁剪平面在边界球中心居中。 const transformCenter = Cesium.Matrix4.getTranslation(tileset.root.transform, new Cesium.Cartesian3()); const transformCartographic = Cesium.Cartographic.fromCartesian(transformCenter); const boundingSphereCartographic = Cesium.Cartographic.fromCartesian(tileset.boundingSphere.center); const height = boundingSphereCartographic.height - transformCartographic.height; clippingPlanes.modelMatrix = Cesium.Matrix4.fromTranslation(new Cesium.Cartesian3(0.0, 0.0, height)); } for (let i = 0; i < clippingPlanes.length; ++i) { const plane = clippingPlanes.get(i); // let plane = _self._createPlane([121.55814450142213, 37.39658788787028], [121.65814450142213, 37.49658788787028], inverseTransform) const planeEntity = viewer.entities.add({ position: boundingSphere.center, // 笛卡儿坐标系的原点位置为模型外接圆的圆心 plane: { dimensions: new Cesium.Cartesian2( // 范围 radius / 10, radius / 10 ), material: Cesium.Color.WHITE.withAlpha(0.1), //设置材质透明度 plane: new Cesium.CallbackProperty( //使用回调函数,动态改变模型位置 _self._createPlaneUpdateFunction(plane), false ), outline: true, // 轮廓 outlineColor: Cesium.Color.WHITE, //轮廓颜色 }, }); // _self.planeEntities.push(planeEntity); } // // 遍历添加裁切面模型 // for (let i = 0; i < clippingPlanes.length; ++i) { // let plane = clippingPlanes.get(i); // let planeEntity = viewer.entities.add({ // // 笛卡儿坐标系的原点位置为模型外接圆的圆心 // position: boundingSphere.center, // plane: { // // 范围 // dimensions: new Cesium.Cartesian2( // radius / 10, // radius / 10 // ), // //设置材质透明度 // material: Cesium.Color.RED.withAlpha(0.1), // //使用回调函数,动态改变模型位置 // plane: new Cesium.CallbackProperty( // _self._createPlaneUpdateFunction(plane), // false // ), // // 轮廓 // outline: true, // //轮廓颜色 // outlineColor: Cesium.Color.RED, // }, // }); // } return tileset; }) }, /** * 添加3dTiles模型 */ addTiles(my3dtiles, pointsArray) { let _self = this; let viewer = _self._viewer; let tileset = my3dtiles; _self.polygon = pointsArray; _self.tileset = tileset; // 当模型准备好时执行 return tileset.readyPromise.then(function() { //转换矩阵 let inverseTransform = _self._getInverseTransform(tileset) // clippingPlane集合 _self.polygon = _self._isDirRes(_self.polygon, false) const clippingPlanes1 = [] for (let i = 0; i < _self.polygon.length; i++) { if (i === (_self.polygon.length - 1)) { clippingPlanes1.push(_self._createPlane(_self.polygon[i], _self.polygon[0], inverseTransform)) } else { clippingPlanes1.push(_self._createPlane(_self.polygon[i], _self.polygon[i + 1], inverseTransform)) } } // 创建裁剪平面 let clippingPlanes = new Cesium.ClippingPlaneCollection({ //一组ClippingPlane对象,用于选择性地禁用每个平面外部的渲染。 planes: clippingPlanes1, //应用于裁剪对象的边缘的高光的宽度(以像素为单位) edgeWidth: 1.0, edgeColor: Cesium.Color.RED, unionClippingRegions: false, //true 才能多个切割 }); _self.clippingPlanes = clippingPlanes; tileset.clippingPlanes = clippingPlanes; //切割面 tileset.debugShowBoundingVolume = false; //打开包围卷 let boundingSphere = tileset.boundingSphere; //外包球 let radius = boundingSphere.radius; //外包球半径 // 遍历添加裁切面模型 for (let i = 0; i < clippingPlanes.length; ++i) { let plane = clippingPlanes.get(i); let planeEntity = viewer.entities.add({ // 笛卡儿坐标系的原点位置为模型外接圆的圆心 position: boundingSphere.center, plane: { // 范围 dimensions: new Cesium.Cartesian2( radius / 10, radius / 10 ), //设置材质透明度 material: Cesium.Color.RED.withAlpha(0.1), //使用回调函数,动态改变模型位置 plane: new Cesium.CallbackProperty( _self._createPlaneUpdateFunction(plane), false ), // 轮廓 outline: true, //轮廓颜色 outlineColor: Cesium.Color.RED, }, }); } return tileset; }) }, /** * 添加3dTiles模型 */ addTiles2(my3dtiles, pointsArray) { let _self = this; let viewer = _self._viewer; let tileset = my3dtiles; _self.polygon = pointsArray; _self.tileset = tileset; // 3dTiles模型初始化位置的矩阵 let Matrix4 = Cesium.Matrix4.fromArray( [1, 5.551115123125783e-16, 5.898416033378595e-9, 0, -6.106226635438361e-16, 1, -1.1355608731111744e-8, 0, -5.898416061134171e-9, 1.1355608731111744e-8, 0.9999999999999999, 0, 9.912469893228263, -19.08345020748675, -14.613607150502503, 1 ] ); // 3dTiles模型加载后的矩阵,可以f12打印查看:tileset.root.transform let transform = Cesium.Matrix4.fromArray( [-0.8874246461620654, -0.46095281470464317, 0, 0, 0.2602796082288922, -0.5010893346724129, 0.8253266045740758, 0, -0.3804366214290463, 0.7324151700322881, 0.5646556435405804, 0, -2429070.591483741, 4676437.67731705, 3581165.448379543, 1 ]); //转换矩阵 let inverseTransform = Cesium.Matrix4.inverseTransformation(transform, new Cesium.Matrix4()); // clippingPlane集合 let clippingPlanes1 = []; for (let i = 0; i < _self.polygon.length - 1; i++) { let plane = _self.createPlane(_self.polygon[i], _self.polygon[i + 1], inverseTransform); clippingPlanes1.push(plane); } // 创建裁剪平面 let clippingPlanes = new Cesium.ClippingPlaneCollection({ //一组ClippingPlane对象,用于选择性地禁用每个平面外部的渲染。 planes: clippingPlanes1, //应用于裁剪对象的边缘的高光的宽度(以像素为单位) edgeWidth: 1.0, edgeColor: Cesium.Color.RED, unionClippingRegions: false, //true 才能多个切割 }); _self.clippingPlanes = clippingPlanes; // 当模型准备好时执行 return tileset.readyPromise.then(function() { tileset.clippingPlanes = clippingPlanes; //切割面 tileset.debugShowBoundingVolume = false; //打开包围卷 let boundingSphere = tileset.boundingSphere; //外包球 let radius = boundingSphere.radius; //外包球半径 // // 遍历添加裁切面模型 // for (let i = 0; i < clippingPlanes.length; ++i) { // let plane = clippingPlanes.get(i); // let planeEntity = viewer.entities.add({ // // 笛卡儿坐标系的原点位置为模型外接圆的圆心 // position: boundingSphere.center, // plane: { // // 范围 // dimensions: new Cesium.Cartesian2( // radius / 10, // radius / 10 // ), // //设置材质透明度 // material: Cesium.Color.RED.withAlpha(0.1), // //使用回调函数,动态改变模型位置 // plane: new Cesium.CallbackProperty( // _self._createPlaneUpdateFunction(plane), // false // ), // // 轮廓 // outline: true, // //轮廓颜色 // outlineColor: Cesium.Color.RED, // }, // }); // } return tileset; }) }, /** * 移除裁切面 */ toggleClipping() { let _self = this; // if (_self.isShowTileSet) { // _self.tileset.clippingPlanes = null; // _self.isShowTileSet = false; // } else { // _self.tileset._clippingPlanes = _self.clippingPlanes; // _self.isShowTileSet = true; // } _self.tileset = null; }, }); export default Cutting;