10. 圆锥曲线统一演化

对应章节:第三章 综合复习
功能简介
通过一个参数(离心率 eee)的变化,展示圆锥曲线的统一性。

  • 0<e<10 < e < 10<e<1:椭圆
  • e=1e = 1e=1:抛物线
  • e>1e > 1e>1:双曲线
    屏幕上有一个动态变化的曲线,随着滑块从0滑动到2,曲线形状连续变化,完美体现圆锥曲线的统一定义(第二定义)。
    在这里插入图片描述
@Entry
@Component
struct ConicUnification {
  @State e: number = 0.5 // 离心率 e
  @State p: number = 50 // 焦准距 p
  private settings: RenderingContextSettings = new RenderingContextSettings(true)
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)

  build() {
    Column() {
      Text('圆锥曲线统一演示')
        .fontSize(24).fontWeight(FontWeight.Bold).margin({ bottom: 20 })

      Text(`离心率 e = ${this.e.toFixed(2)}`)
        .fontSize(18).margin({ bottom: 10 })

      Text(this.getStatusText())
        .fontSize(16).margin({ bottom: 20 })

      Stack() {
        Canvas(this.context)
          .width(400)
          .height(300)
          .onReady(() => this.draw())
      }
      .margin({ bottom: 20 })

      // 控制区域
      Column() {
        Text('调节离心率:')
          .fontSize(16).margin({ bottom: 10 })

        Row() {
          Text('e:')
            .width(30).fontSize(14)
          Slider({
            value: this.e,
            min: 0.01,
            max: 2.0,
            step: 0.01
          })
            .width(200)
            .onChange((value: number) => {
              this.e = value
              this.draw()
            })
          Text(this.e.toFixed(2))
            .width(50).fontSize(14)
        }
        .margin({ bottom: 20 })

        Text('圆锥曲线第二定义: 动点到焦点的距离与到准线的距离之比为离心率 e')
          .fontSize(14).fontColor('#666')
          .margin({ bottom: 10 })

        Text('类型:')
          .fontSize(14).fontColor('#666')
        Text('- 0 < e < 1: 椭圆')
          .fontSize(14).fontColor('#666')
        Text('- e = 1: 抛物线')
          .fontSize(14).fontColor('#666')
        Text('- e > 1: 双曲线')
          .fontSize(14).fontColor('#666')
      }
    }
  }

  getStatusText(): string {
    if (this.e < 1) {
      return `当前:椭圆 (0 < e < 1)`
    } else if (this.e === 1) {
      return `当前:抛物线 (e = 1)`
    } else {
      return `当前:双曲线 (e > 1)`
    }
  }

  private draw() {
    const width = 400
    const height = 300
    const centerX = 150 // 焦点位置
    const centerY = height / 2 // 中心位置

    // 清空画布
    this.context.clearRect(0, 0, width, height)
    this.context.fillStyle = '#F5F5F5'
    this.context.fillRect(0, 0, width, height)

    // 绘制坐标轴
    this.drawAxes(centerX, centerY, width, height)

    // 绘制准线
    const directrixX = centerX - this.p / this.e
    this.drawDirectrix(directrixX, height)

    // 绘制焦点
    this.drawFocus(centerX, centerY)

    // 绘制圆锥曲线(使用极坐标方程)
    this.drawConicSection(centerX, centerY, width, height)
  }

  private drawAxes(centerX: number, centerY: number, width: number, height: number) {
    // 绘制坐标轴
    this.context.strokeStyle = '#666666'
    this.context.lineWidth = 2

    // X轴
    this.context.beginPath()
    this.context.moveTo(0, centerY)
    this.context.lineTo(width, centerY)
    this.context.stroke()

    // Y轴
    this.context.beginPath()
    this.context.moveTo(centerX, 0)
    this.context.lineTo(centerX, height)
    this.context.stroke()

    // 绘制刻度
    this.context.font = '10px sans-serif'
    this.context.fillStyle = '#666666'
    this.context.textAlign = 'center'

    // X轴刻度
    for (let x = centerX; x < width; x += 50) {
      this.context.beginPath()
      this.context.moveTo(x, centerY - 5)
      this.context.lineTo(x, centerY + 5)
      this.context.stroke()
      if (x !== centerX) {
        this.context.fillText(((x - centerX) / 50).toString(), x, centerY + 20)
      }
    }

    // Y轴刻度
    for (let y = centerY; y < height; y += 50) {
      this.context.beginPath()
      this.context.moveTo(centerX - 5, y)
      this.context.lineTo(centerX + 5, y)
      this.context.stroke()
      if (y !== centerY) {
        this.context.fillText(((y - centerY) / 50).toString(), centerX - 20, y + 4)
      }
    }

    // 绘制原点
    this.context.fillText('O', centerX - 10, centerY + 15)
  }

  private drawDirectrix(directrixX: number, height: number) {
    // 绘制准线
    this.context.strokeStyle = '#95A5A6'
    this.context.lineWidth = 1
    this.context.setLineDash([5, 5])

    this.context.beginPath()
    this.context.moveTo(directrixX, 0)
    this.context.lineTo(directrixX, height)
    this.context.stroke()

    this.context.setLineDash([])

    // 绘制准线标签
    this.context.fillStyle = '#95A5A6'
    this.context.font = '14px sans-serif'
    this.context.textAlign = 'center'
    this.context.fillText('准线', directrixX, 20)
  }

  private drawFocus(focusX: number, focusY: number) {
    // 绘制焦点
    this.context.fillStyle = '#E74C3C'
    this.context.beginPath()
    this.context.arc(focusX, focusY, 6, 0, 2 * Math.PI)
    this.context.fill()

    // 绘制焦点标签
    this.context.fillStyle = '#E74C3C'
    this.context.font = '14px sans-serif'
    this.context.textAlign = 'center'
    this.context.fillText('F', focusX, focusY - 15)
  }

  private drawConicSection(centerX: number, centerY: number, width: number, height: number) {
    // 使用极坐标方程绘制圆锥曲线:r = ep / (1 - e*cosθ)
    this.context.strokeStyle = '#3498DB'
    this.context.lineWidth = 2
    this.context.beginPath()

    const step = 0.01 // 角度步长
    let firstPoint = true

    for (let angle = 0; angle < 2 * Math.PI; angle += step) {
      const cosTheta = Math.cos(angle)
      const denominator = 1 - this.e * cosTheta

      // 避免分母为零或负数(对于双曲线)
      if (denominator > 0.001) {
        const r = (this.e * this.p) / denominator
        const x = centerX + r * cosTheta
        const y = centerY + r * Math.sin(angle)

        // 检查点是否在画布内
        if (x >= 0 && x <= width && y >= 0 && y <= height) {
          if (firstPoint) {
            this.context.moveTo(x, y)
            firstPoint = false
          } else {
            this.context.lineTo(x, y)
          }
        }
      }
    }

    // 对于双曲线,绘制另一支
    if (this.e > 1) {
      firstPoint = true
      for (let angle = 0; angle < 2 * Math.PI; angle += step) {
        const cosTheta = Math.cos(angle)
        const denominator = 1 - this.e * cosTheta

        // 绘制双曲线的另一支(分母为负)
        if (denominator < -0.001) {
          const r = (this.e * this.p) / denominator
          const x = centerX + r * cosTheta
          const y = centerY + r * Math.sin(angle)

          // 检查点是否在画布内
          if (x >= 0 && x <= width && y >= 0 && y <= height) {
            if (firstPoint) {
              this.context.moveTo(x, y)
              firstPoint = false
            } else {
              this.context.lineTo(x, y)
            }
          }
        }
      }
    }

    this.context.stroke()
  }
}
Logo

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

更多推荐