Cesium实现3dtiles的平移、缩放,旋转

废话不多说,

上代码:

class EditTileset {
  constructor(options) {
    this.rotateEnabled = options.rotateEnabled !== undefined ? options.rotateEnabled : true; //控制旋转圆
    this.scaleEnabled = options.scaleEnabled !== undefined ? options.scaleEnabled : true; //控制缩放平面
    this.translateEnabled = options.translateEnabled !== undefined ? options.translateEnabled : true;//控制平移线
    this.xAxisLength = options.xAxisLength || options.radius;
    this.yAxisLength = options.yAxisLength || options.radius;
    this.zAxisLength = options.zAxisLength || options.radius;
    this.scaleAxisLength = options.scaleAxisLength || options.radius;
    this.rotatePlaneRadius = options.rotatePlaneRadius || options.radius;
    this.viewer = options.viewer || null;
    this.tileset = null;
    this.handler = null;
    this._primitives = []; // 存储所有控制图元
    this._activeAxis = null; // 当前激活的控制轴
    this.modelMatrix = null; // 模型矩阵
    this._mouseUpHandler = null; // 鼠标抬起事件处理器
    this._lastPosition = null; // 上一次鼠标位置
    this._inverseModelMatrix = new Cesium.Matrix4(); // 模型矩阵的逆矩阵
    
    // 预创建常用的向量和矩阵对象,避免频繁创建
    this._cartesian3_1 = new Cesium.Cartesian3();
    this._cartesian3_2 = new Cesium.Cartesian3();
    this._matrix4 = new Cesium.Matrix4();
    this._quaternion = new Cesium.Quaternion();
    this._matrix3 = new Cesium.Matrix3();
    
    // 事件回调
    this.postTransformEvent = {
      _listeners: [],
      addEventListener: function(callback) {
        this._listeners.push(callback);
      },
      removeEventListener: function(callback) {
        const index = this._listeners.indexOf(callback);
        if (index !== -1) {
          this._listeners.splice(index, 1);
        }
      },
      fire: function(modelMatrix) {
        this._listeners.forEach(callback => callback(modelMatrix));
      }
    };
  }

  /**
   * 将辅助编辑器添加到场景中
   * @param {*} viewer cesium实例
   */
  addTo(viewer) {
    this.viewer = viewer; // 保存viewer引用
    this.handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
    return this;
  }

  /**
   * 将编辑器绑定到tileset上
   * @param {*} tileset 瓦片集
   */
  bind(tileset) {
    this.tileset = tileset;
    this.modelMatrix = Cesium.Matrix4.clone(tileset.modelMatrix);
    Cesium.Matrix4.inverse(this.modelMatrix, this._inverseModelMatrix);
    this.addEventListener();
    this.createPrimitive();
    return this;
  }

  /**
   * 绑定位置(用于点位编辑)
   * @param {Cesium.Cartesian3} position 位置
   */
  bindPosition(position) {
    // 创建一个以该位置为中心的模型矩阵
    this.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(position);
    Cesium.Matrix4.inverse(this.modelMatrix, this._inverseModelMatrix);
    this.addEventListener();
    this.createPrimitive(false); // false表示不是模型,不需要旋转和缩放
    return this;
  }

  /**
   * 添加鼠标悬停事件
   */
  addEventListener() {
    // 鼠标按下
    this.handler.setInputAction((e) => {
      const pickResult = this.viewer.scene.pick(e.position);
      if (pickResult && pickResult.id && this._primitives.includes(pickResult.id)) {
        // 激活选中的控制器
        this._activeAxis = pickResult.id;
        this._lastPosition = e.position.clone(); // 使用clone确保不会被后续操作修改
        
        // 禁用相机控制,以便拖动控制器时不会移动相机
        this.viewer.scene.screenSpaceCameraController.enableRotate = false;
        this.viewer.scene.screenSpaceCameraController.enableTranslate = false;
        this.viewer.scene.screenSpaceCameraController.enableZoom = false;
        
        // 改变鼠标样式为抓取状态
        this.viewer.canvas.style.cursor = 'grabbing';
        
        // 添加移动事件 - 使用requestAnimationFrame提高流畅度
        let animationFrameId = null;
        let lastMoveEvent = null;
        
        const updateTransform = () => {
          if (lastMoveEvent && this._lastPosition) {
            this.transform(this._lastPosition, lastMoveEvent.endPosition);
            this._lastPosition = lastMoveEvent.endPosition.clone();
            lastMoveEvent = null;
          }
          
          if (this._activeAxis) {
            animationFrameId = requestAnimationFrame(updateTransform);
          }
        };
        
        this.handler.setInputAction((moveEvent) => {
          lastMoveEvent = moveEvent;
          if (!animationFrameId) {
            animationFrameId = requestAnimationFrame(updateTransform);
          }
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
        
        // 添加鼠标抬起事件
        this._mouseUpHandler = this.handler.setInputAction(() => {
          this._activeAxis = null;
          this._lastPosition = null;
          
          // 取消动画帧
          if (animationFrameId) {
            cancelAnimationFrame(animationFrameId);
            animationFrameId = null;
          }
          
          // 重新启用相机控制
          this.viewer.scene.screenSpaceCameraController.enableRotate = true;
          this.viewer.scene.screenSpaceCameraController.enableTranslate = true;
          this.viewer.scene.screenSpaceCameraController.enableZoom = true;
          
          // 移除移动事件
          this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
          
          // 重置鼠标样式
          this.viewer.canvas.style.cursor = 'default';
          
          // 触发变换完成事件
          if (this.tileset) {
            this.postTransformEvent.fire(this.tileset.modelMatrix);
          } else {
            this.postTransformEvent.fire(this.modelMatrix);
          }
        }, Cesium.ScreenSpaceEventType.LEFT_UP);
      } else if (pickResult && pickResult.primitive && this._primitives.includes(pickResult.primitive)) {
        // 激活选中的控制器
        this._activeAxis = pickResult.primitive;
        this._lastPosition = e.position.clone(); // 使用clone确保不会被后续操作修改
        
        // 禁用相机控制,以便拖动控制器时不会移动相机
        this.viewer.scene.screenSpaceCameraController.enableRotate = false;
        this.viewer.scene.screenSpaceCameraController.enableTranslate = false;
        this.viewer.scene.screenSpaceCameraController.enableZoom = false;
        
        // 改变鼠标样式为抓取状态
        this.viewer.canvas.style.cursor = 'grabbing';
        
        // 添加移动事件 - 使用requestAnimationFrame提高流畅度
        let animationFrameId = null;
        let lastMoveEvent = null;
        
        const updateTransform = () => {
          if (lastMoveEvent && this._lastPosition) {
            this.transform(this._lastPosition, lastMoveEvent.endPosition);
            this._lastPosition = lastMoveEvent.endPosition.clone();
            lastMoveEvent = null;
          }
          
          if (this._activeAxis) {
            animationFrameId = requestAnimationFrame(updateTransform);
          }
        };
        
        this.handler.setInputAction((moveEvent) => {
          lastMoveEvent = moveEvent;
          if (!animationFrameId) {
            animationFrameId = requestAnimationFrame(updateTransform);
          }
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
        
        // 添加鼠标抬起事件
        this._mouseUpHandler = this.handler.setInputAction(() => {
          this._activeAxis = null;
          this._lastPosition = null;
          
          // 取消动画帧
          if (animationFrameId) {
            cancelAnimationFrame(animationFrameId);
            animationFrameId = null;
          }
          
          // 重新启用相机控制
          this.viewer.scene.screenSpaceCameraController.enableRotate = true;
          this.viewer.scene.screenSpaceCameraController.enableTranslate = true;
          this.viewer.scene.screenSpaceCameraController.enableZoom = true;
          
          // 移除移动事件
          this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
          
          // 重置鼠标样式
          this.viewer.canvas.style.cursor = 'default';
          
          // 触发变换完成事件
          if (this.tileset) {
            this.postTransformEvent.fire(this.tileset.modelMatrix);
          } else {
            this.postTransformEvent.fire(this.modelMatrix);
          }
        }, Cesium.ScreenSpaceEventType.LEFT_UP);
      }
    }, Cesium.ScreenSpaceEventType.LEFT_DOWN);
    
    // 添加鼠标悬停事件
    this.handler.setInputAction((e) => {
      // 如果正在拖拽中,不处理悬停事件
      if (this._activeAxis) return;
      
      const pickResult = this.viewer.scene.pick(e.endPosition);
      
      // 重置所有控制器的样式
      this._primitives.forEach(primitive => {
        if (primitive instanceof Cesium.Entity) {
          if (primitive.point) {
            // 修复缩放控制点的大小
            primitive.point.pixelSize = 18;
            primitive.point.outlineWidth = 2;
          } else if (primitive.polyline) {
            // 重置线宽
            primitive.polyline.width = 3;
          }
        } else if (primitive instanceof Cesium.Primitive) {
          // 对于Primitive类型的控制器,无法直接修改属性
          // 这里不做处理,在拾取时会特殊处理
        }
      });
      
      // 如果鼠标悬停在控制器上,改变样式
      if (pickResult && pickResult.id && this._primitives.includes(pickResult.id)) {
        // 改变鼠标样式为小手
        this.viewer.canvas.style.cursor = 'pointer';
        
        // 放大当前悬停的控制器
        if (pickResult.id.point) {
          pickResult.id.point.pixelSize = 20;
          pickResult.id.point.outlineWidth = 3;
        } else if (pickResult.id.polyline) {
          // 增加线宽
          pickResult.id.polyline.width = 5;
        }
      } else if (pickResult && pickResult.primitive && this._primitives.includes(pickResult.primitive)) {
        // 改变鼠标样式为小手
        this.viewer.canvas.style.cursor = 'pointer';
      } else {
        // 恢复默认鼠标样式
        this.viewer.canvas.style.cursor = 'default';
      }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
  }

  /**
   * 鼠标移动
   * @param {Cesium.Cartesian2} sP 开始位置
   * @param {Cesium.Cartesian2} eP 结束位置
   */
  transform(sP, eP) {
    if (!this._activeAxis) return;
    
    // 根据控制器类型处理变换
    if (this._activeAxis.axisType === 'translate') {
      this.handleTranslate(sP, eP);
    } else if (this._activeAxis.axisType === 'rotate') {
      this.handleRotate(sP, eP);
    } else if (this._activeAxis.axisType === 'scale') {
      this.handleScale(sP, eP);
    }
    
    // 更新控制器位置
    this.updateControlsPosition();
  }

  /**
   * 处理平移变换
   * @param {Cesium.Cartesian2} sP 开始位置
   * @param {Cesium.Cartesian2} eP 结束位置
   */
  handleTranslate(sP, eP) {
    // 获取中心点
    const center = this.tileset ? 
      this.tileset.boundingSphere.center : 
      Cesium.Matrix4.getTranslation(this.modelMatrix, new Cesium.Cartesian3());
    
    // 获取选中的轴向
    const direction = this._activeAxis.direction;
    
    // 获取屏幕上的中心点
    const centerScreen = Cesium.SceneTransforms.wgs84ToWindowCoordinates(this.viewer.scene, center);
    if (!centerScreen) return;
    
    // 获取选中轴在世界坐标系中的方向向量
    let axisDirection;
    if (direction === 'x') {
      axisDirection = Cesium.Matrix4.getColumn(this.tileset ? this.tileset.modelMatrix : this.modelMatrix, 0, new Cesium.Cartesian3());
    } else if (direction === 'y') {
      axisDirection = Cesium.Matrix4.getColumn(this.tileset ? this.tileset.modelMatrix : this.modelMatrix, 1, new Cesium.Cartesian3());
    } else if (direction === 'z') {
      axisDirection = Cesium.Matrix4.getColumn(this.tileset ? this.tileset.modelMatrix : this.modelMatrix, 2, new Cesium.Cartesian3());
    }
    Cesium.Cartesian3.normalize(axisDirection, axisDirection);
    
    // 将轴方向投影到屏幕上
    const axisEnd = Cesium.Cartesian3.add(center, Cesium.Cartesian3.multiplyByScalar(axisDirection, 100, new Cesium.Cartesian3()), new Cesium.Cartesian3());
    const axisEndScreen = Cesium.SceneTransforms.wgs84ToWindowCoordinates(this.viewer.scene, axisEnd);
    
    if (!axisEndScreen) return;
    
    // 计算屏幕上的轴方向向量
    const screenAxisDirection = {
      x: axisEndScreen.x - centerScreen.x,
      y: axisEndScreen.y - centerScreen.y
    };
    const screenAxisLength = Math.sqrt(screenAxisDirection.x * screenAxisDirection.x + screenAxisDirection.y * screenAxisDirection.y);
    
    if (screenAxisLength < 0.001) return; // 轴在屏幕上投影太小,忽略
    
    // 归一化屏幕轴方向
    screenAxisDirection.x /= screenAxisLength;
    screenAxisDirection.y /= screenAxisLength;
    
    // 计算鼠标移动向量
    const mouseVector = {
      x: eP.x - sP.x,
      y: eP.y - sP.y
    };
    
    // 计算鼠标移动在轴方向上的投影长度
    const projectionLength = mouseVector.x * screenAxisDirection.x + mouseVector.y * screenAxisDirection.y;
    
    // 根据投影比例和轴方向计算世界坐标系中的偏移量
    let moveScale = -projectionLength * 0.3; // 默认方向相反
    
    // 特殊处理Z轴(蓝色轴),使其方向与鼠标上下移动一致
    if (direction === 'z') {
      moveScale = projectionLength * 0.3;
    }
    
    const worldDelta = Cesium.Cartesian3.multiplyByScalar(
      axisDirection,
      moveScale,
      new Cesium.Cartesian3()
    );
    
    // 应用平移
    this.translate(worldDelta.x, worldDelta.y, worldDelta.z);
  }

  /**
   * 处理旋转变换
   * @param {Cesium.Cartesian2} sP 开始位置
   * @param {Cesium.Cartesian2} eP 结束位置
   */
  handleRotate(sP, eP) {
    // 获取模型中心点
    const center = this.tileset ? this.tileset.boundingSphere.center : Cesium.Matrix4.getTranslation(this.modelMatrix, new Cesium.Cartesian3());
    
    // 获取屏幕上的中心点
    const centerScreen = Cesium.SceneTransforms.wgs84ToWindowCoordinates(this.viewer.scene, center);
    if (!centerScreen) return;
    
    // 计算开始和结束点相对于中心的向量
    const startVector = {
      x: sP.x - centerScreen.x,
      y: sP.y - centerScreen.y
    };
    const endVector = {
      x: eP.x - centerScreen.x,
      y: eP.y - centerScreen.y
    };
    
    // 计算旋转角度 (弧度)
    const startAngle = Math.atan2(startVector.y, startVector.x);
    const endAngle = Math.atan2(endVector.y, endVector.x);
    // let angle = endAngle - startAngle; // 移除负号,使旋转方向与鼠标一致
    let angle = startAngle - endAngle; // 移除负号,使旋转方向与鼠标一致
    
    // 确定旋转轴
    const direction = this._activeAxis.direction;
    let rx = 0, ry = 0, rz = 0;
    
    // 获取旋转轴在世界坐标系中的方向
    let rotationAxis;
    if (direction === 'x') {
      rotationAxis = Cesium.Matrix4.getColumn(this.tileset.modelMatrix, 0, this._cartesian3_2);
      rx = Cesium.Math.toDegrees(angle);
    } else if (direction === 'y') {
      rotationAxis = Cesium.Matrix4.getColumn(this.tileset.modelMatrix, 1, this._cartesian3_2);
      ry = Cesium.Math.toDegrees(angle);
    } else if (direction === 'z') {
      rotationAxis = Cesium.Matrix4.getColumn(this.tileset.modelMatrix, 2, this._cartesian3_2);
      rz = Cesium.Math.toDegrees(angle);
    }
    
    // 根据相机位置调整旋转方向
    const cameraPosition = this.viewer.camera.position;
    const toCamera = Cesium.Cartesian3.subtract(cameraPosition, center, this._cartesian3_1);
    Cesium.Cartesian3.normalize(toCamera, toCamera);
    
    // 如果轴指向相机,需要反转旋转方向
    if (Cesium.Cartesian3.dot(rotationAxis, toCamera) > 0) { // 修改判断条件,改变旋转方向的逻辑
      rx = -rx;
      ry = -ry;
      rz = -rz;
    }
    
    // 应用旋转
    this.rotate(rx, ry, rz);
  }

  /**
   * 处理缩放变换
   * @param {Cesium.Cartesian2} sP 开始位置
   * @param {Cesium.Cartesian2} eP 结束位置
   */
  handleScale(sP, eP) {
    if (!this.tileset) return; // 点位不支持缩放
    
    // 获取模型中心点
    const center = this.tileset.boundingSphere.center;
    
    // 获取选中的轴向
    const direction = this._activeAxis.direction;
    
    // 获取屏幕上的中心点
    const centerScreen = Cesium.SceneTransforms.wgs84ToWindowCoordinates(this.viewer.scene, center);
    if (!centerScreen) return;
    
    // 获取选中轴在模型坐标系中的方向向量
    let axisDirection;
    if (direction === 'x') {
      axisDirection = Cesium.Matrix4.getColumn(this.tileset.modelMatrix, 0, new Cesium.Cartesian3());
    } else if (direction === 'y') {
      axisDirection = Cesium.Matrix4.getColumn(this.tileset.modelMatrix, 1, new Cesium.Cartesian3());
    } else if (direction === 'z') {
      axisDirection = Cesium.Matrix4.getColumn(this.tileset.modelMatrix, 2, new Cesium.Cartesian3());
    }
    Cesium.Cartesian3.normalize(axisDirection, axisDirection);
    
    // 将轴方向投影到屏幕上
    const axisEnd = Cesium.Cartesian3.add(center, Cesium.Cartesian3.multiplyByScalar(axisDirection, 100, new Cesium.Cartesian3()), new Cesium.Cartesian3());
    const axisEndScreen = Cesium.SceneTransforms.wgs84ToWindowCoordinates(this.viewer.scene, axisEnd);
    
    if (!axisEndScreen) return;
    
    // 计算屏幕上的轴方向向量
    const screenAxisDirection = {
      x: axisEndScreen.x - centerScreen.x,
      y: axisEndScreen.y - centerScreen.y
    };
    const screenAxisLength = Math.sqrt(screenAxisDirection.x * screenAxisDirection.x + screenAxisDirection.y * screenAxisDirection.y);
    
    if (screenAxisLength < 0.001) return; // 轴在屏幕上投影太小,忽略
    
    // 归一化屏幕轴方向
    screenAxisDirection.x /= screenAxisLength;
    screenAxisDirection.y /= screenAxisLength;
    
    // 计算鼠标移动向量
    const mouseVector = {
      x: eP.x - sP.x,
      y: eP.y - sP.y
    };
    
    // 计算鼠标移动在轴方向上的投影长度
    const projectionLength = mouseVector.x * screenAxisDirection.x + mouseVector.y * screenAxisDirection.y;
    
    // 计算缩放因子 - 基于投影长度的相对变化
    let scaleFactor;
    if (direction === 'z') {
      // Z轴特殊处理:向上移动放大,向下移动缩小
      scaleFactor = 1.0 + projectionLength / 100.0;
    } else {
      // X轴和Y轴保持相反方向
      scaleFactor = 1.0 - projectionLength / 100.0;
    }
    
    // 处理特殊情况
    if (isNaN(scaleFactor) || !isFinite(scaleFactor) || Math.abs(scaleFactor - 1.0) < 0.001) {
      return;
    }
    
    // 限制缩放因子范围
    const limitedScaleFactor = Math.max(0.5, Math.min(2.0, scaleFactor));
    
    // 根据轴向应用缩放
    let sx = 1, sy = 1, sz = 1;
    
    if (direction === 'x') {
      sx = limitedScaleFactor;
    } else if (direction === 'y') {
      sy = limitedScaleFactor;
    } else if (direction === 'z') {
      sz = limitedScaleFactor;
    }
    
    // 应用缩放
    this.scale(sx, sy, sz);
  }

  /**
   * 更新控制器位置
   */
  updateControlsPosition() {
    // 移除旧的控制器
    this._primitives.forEach(primitive => {
      if (primitive instanceof Cesium.Entity) {
        this.viewer.entities.remove(primitive);
      } else {
        this.viewer.scene.primitives.remove(primitive);
      }
    });
    this._primitives = [];
    
    // 重新创建控制器
    this.createPrimitive(this.tileset !== null);
  }

  /**
   * 创建所有控制图元
   * @param {boolean} isModel 是否是模型(true为模型,false为点位)
   */
  createPrimitive(isModel = true) {
    // 获取中心点
    const center = this.tileset ? 
      this.tileset.boundingSphere.center : 
      Cesium.Matrix4.getTranslation(this.modelMatrix, new Cesium.Cartesian3());
    
    // 创建ENU坐标系
    const enuMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center);
    
    // 创建平移轴
    if (this.translateEnabled) {
      this.createTranslateAxis(center, enuMatrix);
    }
    
    // 创建旋转环
    if (isModel && this.rotateEnabled) {
      this.createRotateAxis(center, enuMatrix);
    }
    
    // 创建缩放控制器
    if (isModel && this.scaleEnabled) {
      this.createScaleAxis(center, enuMatrix);
    }
  }

  /**
   * 创建平移轴
   * @param {Cesium.Cartesian3} center 中心点
   * @param {Cesium.Matrix4} enuMatrix ENU坐标系矩阵
   */
  createTranslateAxis(center, enuMatrix) {
    // X轴 - 红色
    const xAxis = this.createAxis(
      center,
      new Cesium.Cartesian3(this.xAxisLength, 0, 0),
      Cesium.Color.RED,
      enuMatrix
    );
    xAxis.axisType = 'translate';
    xAxis.direction = 'x';
    xAxis.directionVector = new Cesium.Cartesian3(1, 0, 0);
    
    // Y轴 - 绿色
    const yAxis = this.createAxis(
      center,
      new Cesium.Cartesian3(0, this.yAxisLength, 0),
      Cesium.Color.GREEN,
      enuMatrix
    );
    yAxis.axisType = 'translate';
    yAxis.direction = 'y';
    yAxis.directionVector = new Cesium.Cartesian3(0, 1, 0);
    
    // Z轴 - 蓝色
    const zAxis = this.createAxis(
      center,
      new Cesium.Cartesian3(0, 0, this.zAxisLength),
      Cesium.Color.BLUE,
      enuMatrix
    );
    zAxis.axisType = 'translate';
    zAxis.direction = 'z';
    zAxis.directionVector = new Cesium.Cartesian3(0, 0, 1);
  }

  /**
   * 创建轴线
   * @param {Cesium.Cartesian3} center 中心点
   * @param {Cesium.Cartesian3} direction 方向向量
   * @param {Cesium.Color} color 颜色
   * @param {Cesium.Matrix4} enuMatrix ENU坐标系矩阵
   * @returns {Cesium.Entity} 轴线实体
   */
  createAxis(center, direction, color, enuMatrix) {
    // 计算轴线终点
    const endPoint = Cesium.Matrix4.multiplyByPoint(
      enuMatrix,
      direction,
      new Cesium.Cartesian3()
    );
    
    // 创建轴线实体
    const entity = this.viewer.entities.add({
      polyline: {
        positions: [center, endPoint],
        width: 3, // 默认线宽改小一点
        material: color,
        clampToGround: false,
        disableDepthTestDistance: Number.POSITIVE_INFINITY
      },
      // 确保控制器始终可见
      disableDepthTestDistance: Number.POSITIVE_INFINITY
    });
    
    this._primitives.push(entity);
    return entity;
  }

  /**
   * 创建旋转轴
   * @param {Cesium.Cartesian3} center 中心点
   * @param {Cesium.Matrix4} enuMatrix ENU坐标系矩阵
   */
  createRotateAxis(center, enuMatrix) {
    // X轴旋转环 - 红色
    this.createRotationRing(center, 'x', Cesium.Color.RED, enuMatrix);
    
    // Y轴旋转环 - 绿色
    this.createRotationRing(center, 'y', Cesium.Color.GREEN, enuMatrix);
    
    // Z轴旋转环 - 蓝色
    this.createRotationRing(center, 'z', Cesium.Color.BLUE, enuMatrix);
  }

  /**
   * 创建缩放控制器
   * @param {Cesium.Cartesian3} center 中心点
   * @param {Cesium.Matrix4} enuMatrix ENU坐标系矩阵
   */
  createScaleAxis(center, enuMatrix) {
    // 创建轴端点的缩放控制点
    const axisLength = this.scaleAxisLength * 0.8; // 缩放控制点距离中心的距离
    
    // X轴端点 - 红色
    this.createScalePoint(center, 'x', new Cesium.Cartesian3(axisLength, 0, 0), Cesium.Color.RED, enuMatrix);
    
    // Y轴端点 - 绿色
    this.createScalePoint(center, 'y', new Cesium.Cartesian3(0, axisLength, 0), Cesium.Color.GREEN, enuMatrix);
    
    // Z轴端点 - 蓝色
    this.createScalePoint(center, 'z', new Cesium.Cartesian3(0, 0, axisLength), Cesium.Color.BLUE, enuMatrix);
  }

  /**
   * 创建缩放控制点
   * @param {Cesium.Cartesian3} center 中心点
   * @param {string} axis 轴向 ('x', 'y', 'z', 'xyz')
   * @param {Cesium.Cartesian3} offset 偏移量
   * @param {Cesium.Color} color 颜色
   * @param {Cesium.Matrix4} enuMatrix ENU坐标系矩阵
   */
  createScalePoint(center, axis, offset, color, enuMatrix) {
    // 计算控制点位置
    const pointPosition = Cesium.Matrix4.multiplyByPoint(
      enuMatrix,
      offset,
      new Cesium.Cartesian3()
    );
    
    // 创建点实体
    const entity = this.viewer.entities.add({
      position: pointPosition,
      point: {
        color: color,
        pixelSize: 18,
        outlineColor: Cesium.Color.WHITE,
        outlineWidth: 2,
        disableDepthTestDistance: Number.POSITIVE_INFINITY
      },
      // 确保控制器始终可见
      disableDepthTestDistance: Number.POSITIVE_INFINITY
    });
    
    // 设置属性
    entity.axisType = 'scale';
    entity.direction = axis;
    
    // 设置方向向量和法向量
    if (axis === 'x') {
      entity.directionVector = new Cesium.Cartesian3(1, 0, 0);
      entity.normal = new Cesium.Cartesian3(1, 0, 0);
    } else if (axis === 'y') {
      entity.directionVector = new Cesium.Cartesian3(0, 1, 0);
      entity.normal = new Cesium.Cartesian3(0, 1, 0);
    } else if (axis === 'z') {
      entity.directionVector = new Cesium.Cartesian3(0, 0, 1);
      entity.normal = new Cesium.Cartesian3(0, 0, 1);
    } else if (axis === 'xyz') {
      entity.directionVector = new Cesium.Cartesian3(1, 1, 1);
      entity.normal = new Cesium.Cartesian3(1, 1, 1);
    }
    
    // 添加到控制器列表
    this._primitives.push(entity);
    
    return entity;
  }

  /**
   * 创建辅助编辑器的平移轴向
   * @param {Cesium.Cartesian3} center 中心点
   * @param {Cesium.Cartesian3} direction 方向
   * @param {Cesium.Color} color 颜色
   * @param {Cesium.Matrix4} enuMatrix ENU坐标系矩阵
   * @returns {Cesium.Primitive} 创建的图元
   */
  createAxis(center, direction, color, enuMatrix) {
    // 计算轴的起点和终点
    const start = center;
    const end = Cesium.Matrix4.multiplyByPoint(
      enuMatrix,
      direction,
      new Cesium.Cartesian3()
    );
    
    // 创建线段几何体
    const instance = new Cesium.GeometryInstance({
      geometry: new Cesium.PolylineGeometry({
        positions: [start, end],
        width: 5.0,
        vertexFormat: Cesium.PolylineColorAppearance.VERTEX_FORMAT
      }),
      attributes: {
        color: Cesium.ColorGeometryInstanceAttribute.fromColor(color),
        // 使用相同颜色,确保被遮挡时显示一致
        depthFailColor: Cesium.ColorGeometryInstanceAttribute.fromColor(color)
      },
      id: `axis-${color.toString()}`
    });
    
    // 创建图元
    const primitive = new Cesium.Primitive({
      geometryInstances: instance,
      appearance: new Cesium.PolylineColorAppearance({
        translucent: false
      }),
      allowPicking: true,
      // 确保控制器始终可见,不被3DTiles遮挡
      depthFailAppearance: new Cesium.PolylineColorAppearance({
        translucent: false
      }),
      // 增加拾取宽度,使轴更容易被选中
      asynchronous: false,
      // 增加拾取优先级
      pickPrimitive: true
    });
    
    // 添加到场景
    this.viewer.scene.primitives.add(primitive);
    this._primitives.push(primitive);
    
    return primitive;
  }

  /**
   * 创建旋转环
   * @param {Cesium.Cartesian3} center 中心点
   * @param {string} axis 旋转轴 ('x', 'y', 'z')
   * @param {Cesium.Color} color 颜色
   * @param {Cesium.Matrix4} enuMatrix ENU坐标系矩阵
   */
  createRotationRing(center, axis, color, enuMatrix) {
    // 创建圆环点集
    const positions = [];
    const segments = 60;
    const radius = this.rotatePlaneRadius;
    
    for (let i = 0; i <= segments; i++) {
      const theta = (i / segments) * Cesium.Math.TWO_PI;
      const x = radius * Math.cos(theta);
      const y = radius * Math.sin(theta);
      
      let position;
      if (axis === 'x') {
        position = new Cesium.Cartesian3(0, y, x);
      } else if (axis === 'y') {
        position = new Cesium.Cartesian3(x, 0, y);
      } else { // z
        position = new Cesium.Cartesian3(x, y, 0);
      }
      
      // 转换到世界坐标
      const worldPos = Cesium.Matrix4.multiplyByPoint(
        enuMatrix,
        position,
        new Cesium.Cartesian3()
      );
      
      positions.push(worldPos);
    }
    
    // 创建线段几何体
    const instance = new Cesium.GeometryInstance({
      geometry: new Cesium.PolylineGeometry({
        positions: positions,
        width: 5.0, // 增加线宽,使旋转环更粗
        vertexFormat: Cesium.PolylineColorAppearance.VERTEX_FORMAT
      }),
      attributes: {
        color: Cesium.ColorGeometryInstanceAttribute.fromColor(color),
        // 使用相同颜色,确保被遮挡时显示一致
        depthFailColor: Cesium.ColorGeometryInstanceAttribute.fromColor(color)
      },
      id: `rotate-${axis}`
    });
    
    // 创建图元
    const primitive = new Cesium.Primitive({
      geometryInstances: instance,
      appearance: new Cesium.PolylineColorAppearance({
        translucent: false
      }),
      allowPicking: true,
      // 确保控制器始终可见,不被3DTiles遮挡
      depthFailAppearance: new Cesium.PolylineColorAppearance({
        translucent: false
      }),
      // 增加拾取宽度,使旋转环更容易被选中
      asynchronous: false,
      // 增加拾取优先级
      pickPrimitive: true
    });
    
    // 设置属性
    primitive.axisType = 'rotate';
    primitive.direction = axis;
    
    // 设置方向向量
    if (axis === 'x') {
      primitive.directionVector = new Cesium.Cartesian3(1, 0, 0);
    } else if (axis === 'y') {
      primitive.directionVector = new Cesium.Cartesian3(0, 1, 0);
    } else { // z
      primitive.directionVector = new Cesium.Cartesian3(0, 0, 1);
    }
    
    // 添加到场景
    this.viewer.scene.primitives.add(primitive);
    this._primitives.push(primitive);
    
    return primitive;
  }
  
  /**
   * 平移模型
   * @param {number} dx x轴偏移量。单位:米
   * @param {number} dy y轴偏移量。单位:米
   * @param {number} dz z轴偏移量。单位:米
   */
  translate(dx, dy, dz) {
    if (dx === 0 && dy === 0 && dz === 0) return;
    
    // 获取中心点
    const origin = this.tileset ? 
      this.tileset.boundingSphere.center : 
      Cesium.Matrix4.getTranslation(this.modelMatrix, new Cesium.Cartesian3());
    
    // 以该点建立ENU坐标系
    const toWorldMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);
    
    // 该坐标系下平移后的位置
    const translatePosition = new Cesium.Cartesian3(dx, dy, dz);
    
    // 获取平移后位置的世界坐标
    const worldPosition = Cesium.Matrix4.multiplyByPoint(
      toWorldMatrix, 
      translatePosition, 
      new Cesium.Cartesian3()
    );
    
    // 计算世界坐标下的各个平移量
    const offset = Cesium.Cartesian3.subtract(
      worldPosition, 
      origin, 
      new Cesium.Cartesian3()
    );
    
    // 从世界坐标下的平移量计算世界坐标的平移矩阵
    const translateMatrix = Cesium.Matrix4.fromTranslation(offset);
    
    // 应用平移矩阵
    if (this.tileset) {
      this.tileset.modelMatrix = Cesium.Matrix4.multiply(
        translateMatrix, 
        this.tileset.modelMatrix, 
        new Cesium.Matrix4()
      );
    } else {
      this.modelMatrix = Cesium.Matrix4.multiply(
        translateMatrix, 
        this.modelMatrix, 
        new Cesium.Matrix4()
      );
    }
    
    // 触发变换事件
    this.postTransformEvent.fire(this.tileset ? this.tileset.modelMatrix : this.modelMatrix);
  }
  
  /**
   * 缩放模型
   * @param {number} sx x轴缩放倍数
   * @param {number} sy y轴缩放倍数
   * @param {number} sz z轴缩放倍数
   */
  scale(sx, sy, sz) {
    if (!this.tileset) return; // 点位不支持缩放
    if (sx <= 0 || sy <= 0 || sz <= 0) {
      console.error('缩放倍数必须大于0');
      return;
    }
    if (sx === 1 && sy === 1 && sz === 1) return;
    
    // 获取中心点
    const origin = this.tileset.boundingSphere.center;
    
    // 以该点建立ENU坐标系
    const toWorldMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);
    
    // 获取ENU矩阵的逆矩阵
    const toLocalMatrix = Cesium.Matrix4.inverse(toWorldMatrix, new Cesium.Matrix4());
    
    // 计算缩放矩阵
    const scaleMatrix = Cesium.Matrix4.fromScale(new Cesium.Cartesian3(sx, sy, sz));
    
    // ENU坐标系下的结果矩阵
    const localResultMatrix = Cesium.Matrix4.multiply(
      scaleMatrix, 
      toLocalMatrix, 
      new Cesium.Matrix4()
    );
    
    // 世界坐标系下的结果矩阵
    const worldResultMatrix = Cesium.Matrix4.multiply(
      toWorldMatrix, 
      localResultMatrix, 
      new Cesium.Matrix4()
    );
    
    // 应用结果
    this.tileset.modelMatrix = Cesium.Matrix4.multiply(
      worldResultMatrix, 
      this.tileset.modelMatrix, 
      new Cesium.Matrix4()
    );
    
    // 触发变换事件
    this.postTransformEvent.fire(this.tileset.modelMatrix);
  }
  
  /**
   * 旋转模型
   * @param {number} rx 绕X轴旋转的角度。单位:度
   * @param {number} ry 绕Y轴旋转的角度。单位:度
   * @param {number} rz 绕Z轴旋转的角度。单位:度
   */
  rotate(rx, ry, rz) {
    if (!this.tileset) return; // 点位不支持旋转
    if (rx === 0 && ry === 0 && rz === 0) return;
    
    // 获取中心点
    const origin = this.tileset.boundingSphere.center;
    
    // 以该点建立ENU坐标系
    const toWorldMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);
    
    // 获取ENU矩阵的逆矩阵
    const toLocalMatrix = Cesium.Matrix4.inverse(toWorldMatrix, new Cesium.Matrix4());
    
    // 计算旋转矩阵
    const rotateMatrix = Cesium.Matrix4.clone(Cesium.Matrix4.IDENTITY);
    
    if (rx !== 0) {
      const rotateXMatrix = Cesium.Matrix4.fromRotation(
        Cesium.Matrix3.fromRotationX(Cesium.Math.toRadians(rx))
      );
      Cesium.Matrix4.multiply(rotateXMatrix, rotateMatrix, rotateMatrix);
    }
    
    if (ry !== 0) {
      const rotateYMatrix = Cesium.Matrix4.fromRotation(
        Cesium.Matrix3.fromRotationY(Cesium.Math.toRadians(ry))
      );
      Cesium.Matrix4.multiply(rotateYMatrix, rotateMatrix, rotateMatrix);
    }
    
    if (rz !== 0) {
      const rotateZMatrix = Cesium.Matrix4.fromRotation(
        Cesium.Matrix3.fromRotationZ(Cesium.Math.toRadians(rz))
      );
      Cesium.Matrix4.multiply(rotateZMatrix, rotateMatrix, rotateMatrix);
    }
    
    // ENU坐标系下的结果矩阵
    const localResultMatrix = Cesium.Matrix4.multiply(
      rotateMatrix, 
      toLocalMatrix, 
      new Cesium.Matrix4()
    );
    
    // 世界坐标系下的结果矩阵
    const worldResultMatrix = Cesium.Matrix4.multiply(
      toWorldMatrix, 
      localResultMatrix, 
      new Cesium.Matrix4()
    );
    
    // 应用结果
    this.tileset.modelMatrix = Cesium.Matrix4.multiply(
      worldResultMatrix, 
      this.tileset.modelMatrix, 
      new Cesium.Matrix4()
    );
    
    // 触发变换事件
    this.postTransformEvent.fire(this.tileset.modelMatrix);
  }
  
  /**
   * 获取鼠标在场景中的世界坐标
   * @param {Cesium.Cartesian2} screenPos 屏幕坐标
   * @returns {Cesium.Cartesian3} 返回世界坐标
   */
  getWorldPosition(screenPos) {
    const ray = this.viewer.camera.getPickRay(screenPos);
    if (!ray) return null;
    
    const result = this.viewer.scene.globe.pick(ray, this.viewer.scene);
    if (!result) {
      // 如果没有与地球相交,则使用射线与平面相交的方法
      const center = this.tileset ? 
        this.tileset.boundingSphere.center : 
        Cesium.Matrix4.getTranslation(this.modelMatrix, new Cesium.Cartesian3());
      
      const plane = Cesium.Plane.fromPointNormal(
        center,
        this.viewer.camera.direction,
        new Cesium.Plane()
      );
      
      return Cesium.IntersectionTests.rayPlane(ray, plane);
    }
    
    return result;
  }
  
  /**
   * 获取射线与平面的交点
   * @param {Cesium.Cartesian2} pixel 屏幕坐标
   * @param {Cesium.Cartesian3} planeCenter 平面中心点
   * @param {Cesium.Cartesian3} planeNormal 平面法向量
   * @returns {Cesium.Cartesian3} 交点
   */
  getPositionInPlane(pixel, planeCenter, planeNormal) {
    // 获取射线
    const ray = this.viewer.camera.getPickRay(pixel);
    if (!ray) return null;
    
    // 创建平面
    const plane = Cesium.Plane.fromPointNormal(planeCenter, planeNormal, new Cesium.Plane());
    
    // 计算交点
    return Cesium.IntersectionTests.rayPlane(ray, plane);
  }
  
  /**
   * 销毁辅助编辑器
   */
  destroyed() {
    // 移除所有控制图元
    this._primitives.forEach(primitive => {
      if (primitive instanceof Cesium.Entity) {
        this.viewer.entities.remove(primitive);
      } else {
        this.viewer.scene.primitives.remove(primitive);
      }
    });
    this._primitives = [];
    
    // 销毁事件处理器
    if (this.handler) {
      this.handler.destroy();
      this.handler = null;
    }
    
    // 重置状态
    this._activeAxis = null;
    this.tileset = null;
    this.modelMatrix = null;
  }
}

使用:

tileset = new EditTileset({
      viewer: viewer,
      translateEnabled: true,
      rotateEnabled: true,
      scaleEnabled: true,
      radius: pick.primitive.boundingSphere.radius / 3
    })
    tileset.addTo(viewer)
    tileset.bind(pick.primitive)

难点:矩阵的变换。轴控制平移,环控制旋转,点控制缩放

你可能感兴趣的:(3d,前端,javascript)