6. 空间角与距离计算器

功能简介:利用空间向量计算异面直线所成角、直线与平面所成角、二面角,以及点到平面的距离。通过输入向量坐标,实时计算并展示结果,帮助学生掌握空间角与距离的向量计算方法。
在这里插入图片描述ArkTS代码

@Entry
@Component
struct SpaceVectorCalculator {
  @State private calculationType: string = 'skewLines' // skewLines, linePlane, dihedral, pointPlane
  
  // 异面直线相关向量
  @State private line1Point1: number[] = [0, 0, 0]
  @State private line1Vector: number[] = [1, 0, 0]
  @State private line2Point1: number[] = [0, 1, 0]
  @State private line2Vector: number[] = [0, 0, 1]
  
  // 直线与平面相关向量
  @State private linePoint: number[] = [0, 0, 0]
  @State private lineDir: number[] = [1, 0, 0]
  @State private planePoint: number[] = [0, 0, 0]
  @State private planeNormal: number[] = [0, 1, 0]
  
  // 二面角相关向量
  @State private plane1Point: number[] = [0, 0, 0]
  @State private plane1Normal: number[] = [1, 0, 0]
  @State private plane2Point: number[] = [0, 0, 0]
  @State private plane2Normal: number[] = [0, 1, 0]
  
  // 点到平面相关向量
  @State private pointCoords: number[] = [1, 1, 1]
  @State private planePointDistance: number[] = [0, 0, 0]
  @State private planeNormalDistance: number[] = [0, 1, 0]
  
  @State private result: string = ''

  build() {
    Column() {
      Text('� 空间向量计算工具')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 20 })

      // 计算类型选择
      Row() {
        Button('异面直线角')
          .fontSize(12)
          .onClick(() => this.calculationType = 'skewLines')
        Button('直线平面角')
          .fontSize(12)
          .onClick(() => this.calculationType = 'linePlane')
        Button('二面角')
          .fontSize(12)
          .onClick(() => this.calculationType = 'dihedral')
        Button('点到平面距离')
          .fontSize(12)
          .onClick(() => this.calculationType = 'pointPlane')
      }
      .margin({ bottom: 20 })

      // 输入区域
      this.renderInputSection()

      // 计算按钮
      Button('计算')
        .fontSize(16)
        .onClick(() => this.calculate())
        .margin({ top: 20, bottom: 20 })

      // 结果显示
      Column() {
        Text('计算结果:')
          .fontSize(16)
          .fontWeight(FontWeight.Bold)
          .margin({ bottom: 10 })
        Text(this.result)
          .fontSize(14)
          .fontColor('#2196F3')
          .padding(15)
          .backgroundColor('#f9f9f9')
          .borderRadius(8)
          .width('100%')
      }
      .margin({ top: 20 })
    }
    .padding(20)
    .width('100%')
    .height('100%')
  }

  @Builder
  renderInputSection() {
    Column() {
      if (this.calculationType === 'skewLines') {
        this.renderSkewLinesInput()
      } else if (this.calculationType === 'linePlane') {
        this.renderLinePlaneInput()
      } else if (this.calculationType === 'dihedral') {
        this.renderDihedralInput()
      } else if (this.calculationType === 'pointPlane') {
        this.renderPointPlaneInput()
      }
    }
  }

  @Builder
  renderSkewLinesInput() {
    Column() {
      Text('异面直线所成角计算')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 15 })

      // 直线1
      Column() {
        Text('直线1:')
          .fontSize(14)
          .fontWeight(FontWeight.Bold)
          .margin({ bottom: 10 })

        this.renderVectorInput('点1坐标:', this.line1Point1)
        this.renderVectorInput('方向向量:', this.line1Vector)
      }
      .margin({ bottom: 20 })

      // 直线2
      Column() {
        Text('直线2:')
          .fontSize(14)
          .fontWeight(FontWeight.Bold)
          .margin({ bottom: 10 })

        this.renderVectorInput('点1坐标:', this.line2Point1)
        this.renderVectorInput('方向向量:', this.line2Vector)
      }
    }
  }

  @Builder
  renderLinePlaneInput() {
    Column() {
      Text('直线与平面所成角计算')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 15 })

      // 直线
      Column() {
        Text('直线:')
          .fontSize(14)
          .fontWeight(FontWeight.Bold)
          .margin({ bottom: 10 })

        this.renderVectorInput('直线上一点:', this.linePoint)
        this.renderVectorInput('直线方向向量:', this.lineDir)
      }
      .margin({ bottom: 20 })

      // 平面
      Column() {
        Text('平面:')
          .fontSize(14)
          .fontWeight(FontWeight.Bold)
          .margin({ bottom: 10 })

        this.renderVectorInput('平面上一点:', this.planePoint)
        this.renderVectorInput('平面法向量:', this.planeNormal)
      }
    }
  }

  @Builder
  renderDihedralInput() {
    Column() {
      Text('二面角计算')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 15 })

      // 平面1
      Column() {
        Text('平面1:')
          .fontSize(14)
          .fontWeight(FontWeight.Bold)
          .margin({ bottom: 10 })

        this.renderVectorInput('平面上一点:', this.plane1Point)
        this.renderVectorInput('平面法向量:', this.plane1Normal)
      }
      .margin({ bottom: 20 })

      // 平面2
      Column() {
        Text('平面2:')
          .fontSize(14)
          .fontWeight(FontWeight.Bold)
          .margin({ bottom: 10 })

        this.renderVectorInput('平面上一点:', this.plane2Point)
        this.renderVectorInput('平面法向量:', this.plane2Normal)
      }
    }
  }

  @Builder
  renderPointPlaneInput() {
    Column() {
      Text('点到平面距离计算')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 15 })

      // 点
      this.renderVectorInput('点坐标:', this.pointCoords)

      // 平面
      Column() {
        Text('平面:')
          .fontSize(14)
          .fontWeight(FontWeight.Bold)
          .margin({ top: 20, bottom: 10 })

        this.renderVectorInput('平面上一点:', this.planePointDistance)
        this.renderVectorInput('平面法向量:', this.planeNormalDistance)
      }
    }
  }

  @Builder
  renderVectorInput(label: string, vector: number[]) {
    Row() {
      Text(label)
        .width(100)
      ForEach([0, 1, 2], (index: number) => {
        TextInput({
          text: vector[index].toString()
        })
          .type(InputType.Number)
          .width(60)
          .onChange((value: string) => {
            const num = parseFloat(value) || 0
            vector[index] = num
          })
      })
    }
    .margin({ bottom: 10 })
  }

  private calculate() {
    switch (this.calculationType) {
      case 'skewLines':
        this.calculateSkewLinesAngle()
        break
      case 'linePlane':
        this.calculateLinePlaneAngle()
        break
      case 'dihedral':
        this.calculateDihedralAngle()
        break
      case 'pointPlane':
        this.calculatePointPlaneDistance()
        break
    }
  }

  private calculateSkewLinesAngle() {
    // 计算异面直线所成角
    const v1 = this.line1Vector
    const v2 = this.line2Vector
    
    // 计算向量点积
    const dotProduct = v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]
    
    // 计算向量模长
    const v1Magnitude = Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2])
    const v2Magnitude = Math.sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2])
    
    if (v1Magnitude === 0 || v2Magnitude === 0) {
      this.result = '方向向量不能为零向量'
      return
    }
    
    // 计算夹角余弦值
    const cosTheta = Math.abs(dotProduct) / (v1Magnitude * v2Magnitude)
    
    // 计算角度(弧度转角度)
    const theta = Math.acos(cosTheta) * 180 / Math.PI
    
    this.result = `异面直线所成角: ${theta.toFixed(2)}°`
  }

  private calculateLinePlaneAngle() {
    // 计算直线与平面所成角
    const lineDir = this.lineDir
    const planeNormal = this.planeNormal
    
    // 计算向量点积
    const dotProduct = lineDir[0] * planeNormal[0] + lineDir[1] * planeNormal[1] + lineDir[2] * planeNormal[2]
    
    // 计算向量模长
    const lineMagnitude = Math.sqrt(lineDir[0] * lineDir[0] + lineDir[1] * lineDir[1] + lineDir[2] * lineDir[2])
    const normalMagnitude = Math.sqrt(planeNormal[0] * planeNormal[0] + planeNormal[1] * planeNormal[1] + planeNormal[2] * planeNormal[2])
    
    if (lineMagnitude === 0 || normalMagnitude === 0) {
      this.result = '方向向量或法向量不能为零向量'
      return
    }
    
    // 计算直线与法向量的夹角余弦值
    const cosPhi = Math.abs(dotProduct) / (lineMagnitude * normalMagnitude)
    
    // 计算直线与平面的夹角(90度减去直线与法向量的夹角)
    const phi = Math.acos(cosPhi) * 180 / Math.PI
    const theta = 90 - phi
    
    this.result = `直线与平面所成角: ${theta.toFixed(2)}°`
  }

  private calculateDihedralAngle() {
    // 计算二面角
    const n1 = this.plane1Normal
    const n2 = this.plane2Normal
    
    // 计算向量点积
    const dotProduct = n1[0] * n2[0] + n1[1] * n2[1] + n1[2] * n2[2]
    
    // 计算向量模长
    const n1Magnitude = Math.sqrt(n1[0] * n1[0] + n1[1] * n1[1] + n1[2] * n1[2])
    const n2Magnitude = Math.sqrt(n2[0] * n2[0] + n2[1] * n2[1] + n2[2] * n2[2])
    
    if (n1Magnitude === 0 || n2Magnitude === 0) {
      this.result = '法向量不能为零向量'
      return
    }
    
    // 计算二面角余弦值
    const cosTheta = dotProduct / (n1Magnitude * n2Magnitude)
    
    // 计算角度(弧度转角度)
    const theta = Math.acos(cosTheta) * 180 / Math.PI
    
    // 二面角范围是[0, 180],但通常取锐角或直角
    const finalAngle = theta <= 90 ? theta : 180 - theta
    
    this.result = `二面角: ${finalAngle.toFixed(2)}°`
  }

  private calculatePointPlaneDistance() {
    // 计算点到平面的距离
    const point = this.pointCoords
    const planePoint = this.planePointDistance
    const planeNormal = this.planeNormalDistance
    
    // 计算向量 PA
    const PA = [planePoint[0] - point[0], planePoint[1] - point[1], planePoint[2] - point[2]]
    
    // 计算向量点积
    const dotProduct = PA[0] * planeNormal[0] + PA[1] * planeNormal[1] + PA[2] * planeNormal[2]
    
    // 计算法向量模长
    const normalMagnitude = Math.sqrt(planeNormal[0] * planeNormal[0] + planeNormal[1] * planeNormal[1] + planeNormal[2] * planeNormal[2])
    
    if (normalMagnitude === 0) {
      this.result = '法向量不能为零向量'
      return
    }
    
    // 计算距离
    const distance = Math.abs(dotProduct) / normalMagnitude
    
    this.result = `点到平面的距离: ${distance.toFixed(3)}`
  }
}
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐