123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528 |
- /* 引入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;
|