cesium之平面裁切
初学者,原理可能没这么深入,不过基本了解实现的步骤的逻辑
基本的原理就是切面发法线方向为显示的部分,法线的反方向为裁切掉的部分
动态的改变distance来实现模型的裁切
实现主要用到的api有,ClippingPlaneCollection,ClippingPlane
以下奉上具体实现步骤
1.定义裁切平面数组
var planesDistance = [];//用于存放多个裁切面距离模型原点的距离
var clippingPlanesArray = [
new Cesium.ClippingPlane(new Cesium.Cartesian3(0.0, 0.0, -1.0), 0.0),//往下为裁切
new Cesium.ClippingPlane(new Cesium.Cartesian3(-1.0, 0.0, 0.0), 0.0),//侧面裁切
];
2.定义裁切平面集合
//debugger;
//定义裁切平面集合
clippingPlanes = new Cesium.ClippingPlaneCollection({
planes: clippingPlanesArray,//定义的切面数组//变成undid
edgeColor: Cesium.Color.WHITE, // 平面切割时模型的边缘颜色
edgeWidth: viewModel.edgeStylingEnabled ? 1.0 : 0.0,//边缘的宽度y
unionClippingRegions: true, //true 才能多个切割
enabled: true,//裁切平面是否处于活动
clippingPlanes
});
3.加载模型并制定裁切平面
var tileset = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({
url: url,
luminanceAtZenith: 0.2,
lightColor: new Cesium.Cartesian3(0.3, 0.3, 0.3),
clippingPlanes:clippingPlanes
}));
4.创建可视化裁切平面
tileset.debugShowBoundingVolume = viewModel.debugBoundingVolumesEnabled;
boundingSphere = tileset.boundingSphere;//3d模型的中心和半径
var radius = boundingSphere.radius;//
viewer.zoomTo(tileset, new Cesium.HeadingPitchRange(0.5, -0.2, radius * 4.0));
//加载plane可视化裁切平面
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.
var transformCenter = Cesium.Matrix4.getTranslation(tileset.root.transform, new Cesium.Cartesian3());//获取矩阵的平移部分
var transformCartographic = Cesium.Cartographic.fromCartesian(transformCenter);//结果对象中的值将以弧度表示。
var boundingSphereCartographic = Cesium.Cartographic.fromCartesian(tileset.boundingSphere.center);
var boundingSphereCartographicradius = Cesium.Cartographic.fromCartesian(radius);
var height = boundingSphereCartographic.height - transformCartographic.height;
console.log(boundingSphereCartographicradius);
var longitude = boundingSphereCartographic.longitude - transformCartographic.longitude;//经度
console.log(longitude * 111000);
var latitude = boundingSphereCartographic.latitude - transformCartographic.latitude;//维度
//下面决定了是否在中心点
clippingPlanes.modelMatrix = Cesium.Matrix4.fromTranslation(new Cesium.Cartesian3(40, 50, height));
}
//创建可视化对象
for (var i = 0; i < clippingPlanes.length; ++i) {
var plane = clippingPlanes.get(i);
viewer.entities.add({
name: i,
position: boundingSphere.center,//设置裁切面的位置,offset, 根据3dtiles同步调整裁切面高度
//PlaneGraphics.html
plane: {//每个裁切对象(distance为裁切面距离远点的高度)
dimensions: new Cesium.Cartesian2(radius, radius),//调整裁切面的长和宽
material: Cesium.Color.WHITE.withAlpha(0.1),//裁切面的颜色和透明度
plane: new Cesium.CallbackProperty(createPlaneUpdateFunction(plane, radius, i), false),
outline: true,//是否显示边框
outlineColor: Cesium.Color.WHITE,//边框颜色
}
});//add
}
}
5.写一个方法动态改变ClippingPlane距离原点的距离
function createPlaneUpdateFunction(plane, radius, name) {
return function () {
plane.distance = planesDistance[name] + (radius * 0.5);//切面距离原点的距离就等于移动的距离+模型的半径的一半
return plane;
};
}
6.定义鼠标事件
var ellipsoid = viewer.scene.globe.ellipsoid;
// 注册鼠标事件
var downHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);//鼠标点击事件
downHandler.setInputAction(function (movement) {
var position = viewer.scene.camera.pickEllipsoid(movement.position, viewer.scene.globe.ellipsoid);
//console.log(position);
var pickObject = scene.pick(movement.position);
//debugger;
if (Cesium.defined(pickObject) && Cesium.defined(pickObject.id) && Cesium.defined(pickObject.id.plane)) {
selectedPlane = pickObject.id.plane;//将entities
selectedPlane.name = pickObject.id.name//将name赋予selectedPlane
selectedPlane.material = Cesium.Color.WHITE.withAlpha(0.05);
selectedPlane.outlineColor = Cesium.Color.WHITE;//更换切面的颜色
selectedPlane.startPosition = movement.position;//
selectedPlane.startPosition1 = Cesium.Math.toDegrees((ellipsoid.cartesianToCartographic(position).longitude));//初始位置维度
scene.screenSpaceCameraController.enableInputs = false; // 取消默认的鼠标一切输入事件
}
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
// 注册鼠标松开事件
var upHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);//鼠标点击向上事件
upHandler.setInputAction(function () {
if (Cesium.defined(selectedPlane)) {//如果存在这个对象
selectedPlane.material = Cesium.Color.WHITE.withAlpha(0.6); // 恢复选中的切面颜色
selectedPlane.outlineColor = Cesium.Color.blue;//
selectedPlane = undefined;
}
scene.screenSpaceCameraController.enableInputs = true; // 恢复默认的鼠标一切输入事件
}, Cesium.ScreenSpaceEventType.LEFT_UP);
// 注册鼠标移动事件,
var moveHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);//鼠标点击移动事件
moveHandler.setInputAction(function (movement) {
//通过指定的椭球或者地图对应的坐标系,将鼠标的二维坐标转换为对应椭球体三维坐标
var cartesianEnd = viewer.camera.pickEllipsoid(movement.endPosition, ellipsoid);//笛卡尔积,获取鼠标移动结束世界坐标
var cartesianStart = viewer.camera.pickEllipsoid(movement.startPosition, ellipsoid);//笛卡尔积,获取鼠标开始移动时世界坐标
if (Cesium.defined(selectedPlane)) {
// console.log(selectedPlane.name);//做判断哪个面
if (selectedPlane.name == 0) {//往下
var deltaSize = movement.startPosition.y - movement.endPosition.y; // 计算鼠标移动的过程中产生的垂直高度距离
console.log("开始")
console.log("起始位置"+movement.startPosition.y);
console.log("结束为止"+movement.endPosition.y);
console.log("移动的幅度"+deltaSize);
planesDistance[selectedPlane.name] += deltaSize ;//更改鼠标移动的幅度//从初始位置开始算
console.log("结束")
}
if (selectedPlane.name == 1) {//侧面
if (cartesianEnd&&cartesianStart) {//能获取
//将笛卡尔坐标转换为地理(地图)坐标
var cartographiEnd = ellipsoid.cartesianToCartographic(cartesianEnd);
var cartographicStart = ellipsoid.cartesianToCartographic(cartesianStart);
//console.log(cartographic);
//将弧度转为度的十进制度表示,因为差值很小,保留20位小数
var longitudeStringEnd = (Cesium.Math.toDegrees(cartographiEnd.longitude).toFixed(20));/
var longitudeStringStart = (Cesium.Math.toDegrees(cartographicStart.longitude).toFixed(20));/
var longitudeGap=(longitudeStringEnd-longitudeStringStart)*100000;
console.log("两者之间的差距"+longitudeGap);
console.log("++===");/获取经度往上变大
/
planesDistance[selectedPlane.name] +=longitudeGap;//原来的距离加上移动的距离(很重要,决定了裁切面的移动方向)
} else {
// mouse_state.innerText = "";
}
}
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
总结:侧面的裁切面不能以屏幕坐标作为移动的依据,因为当旋转模型时,正负值会发生改变,切面移动方向会和鼠标移动的方向相反,
然后我个人的解决方案是获取地球坐标,然后再转换成经纬,再以鼠标移动时经纬度差作为侧面切面移动的依据,这样就可以解决以上的问题,如果大家有更好的解决方案欢迎跟我交流
还有个问题,就是我侧面的裁切面还没触碰模型就开始裁切,需要我手动去调,现在还没解决,知道解决方案的大神也欢迎给我指点