Harmonyos应用实例210:直线与圆的位置关系
·
2. 直线与圆的位置关系
功能简介:通过调整直线方程参数和圆的圆心、半径,实时展示直线与圆的位置关系(相离、相切、相交),计算圆心到直线的距离、交点坐标。帮助学生理解直线与圆位置关系的判定方法和几何意义。
ArkTS代码:
@Entry
@Component
struct LineCircleRelation {
@State private lineParams: number[] = [1, -1, 0] // ax + by + c = 0
@State private circleCenter: number[] = [0, 0]
@State private circleRadius: number = 5
@State private relation: string = ''
@State private distance: number = 0
@State private intersectionPoints: string[] = []
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
build() {
Column() {
Text('🔗 直线与圆的位置关系')
.fontSize(24).fontWeight(FontWeight.Bold).margin({ bottom: 20 })
Canvas(this.context)
.width(400).height(300)
.onReady(() => this.drawRelation())
Text('直线方程: ax + by + c = 0')
.fontSize(16).fontColor('#666').margin({ top: 20, bottom: 10 })
ForEach(['a', 'b', 'c'], (param: string, index: number) => {
Row() {
Text(`${param}: `)
.width(40)
Slider({
value: this.lineParams[index],
min: -10,
max: 10,
step: 0.1
})
.width(200)
.onChange((val: number) => {
this.lineParams[index] = val
this.calculateRelation()
})
Text(this.lineParams[index].toFixed(1))
.width(60)
}
.margin({ bottom: 10 })
})
Text('圆: (x - h)² + (y - k)² = r²')
.fontSize(16).fontColor('#666').margin({ top: 20, bottom: 10 })
Row() {
Text('圆心(h, k): ')
.width(80)
TextInput({
placeholder: 'h',
text: this.circleCenter[0].toString()
})
.width(80)
.onChange((v: string) => {
this.circleCenter[0] = parseFloat(v) || 0
this.calculateRelation()
})
TextInput({
placeholder: 'k',
text: this.circleCenter[1].toString()
})
.width(80)
.onChange((v: string) => {
this.circleCenter[1] = parseFloat(v) || 0
this.calculateRelation()
})
}
.margin({ bottom: 10 })
Row() {
Text('半径 r: ')
.width(80)
Slider({
value: this.circleRadius,
min: 1,
max: 10,
step: 0.1
})
.width(200)
.onChange((val: number) => {
this.circleRadius = val
this.calculateRelation()
})
Text(this.circleRadius.toFixed(1))
.width(60)
}
.margin({ bottom: 20 })
Text(this.relation)
.fontSize(16).fontColor('#2196F3').margin({ bottom: 10 })
Text(`圆心到直线的距离: d = ${this.distance.toFixed(2)}`)
.fontSize(14).fontColor('#666')
if (this.intersectionPoints.length > 0) {
Text('交点坐标:')
.fontSize(14).fontColor('#666').margin({ top: 10, bottom: 5 })
ForEach(this.intersectionPoints, (point: string) => {
Text(point)
.fontSize(14).fontColor('#666')
})
}
}
.padding(20)
}
private calculateRelation() {
const a = this.lineParams[0]
const b = this.lineParams[1]
const c = this.lineParams[2]
const h = this.circleCenter[0]
const k = this.circleCenter[1]
// 计算圆心到直线的距离
this.distance = Math.abs(a * h + b * k + c) / Math.sqrt(a * a + b * b)
// 确定位置关系
if (this.distance > this.circleRadius) {
this.relation = '位置关系: 相离'
this.intersectionPoints = []
} else if (Math.abs(this.distance - this.circleRadius) < 0.01) {
this.relation = '位置关系: 相切'
this.calculateIntersectionPoints()
} else {
this.relation = '位置关系: 相交'
this.calculateIntersectionPoints()
}
this.drawRelation()
}
private calculateIntersectionPoints() {
const a = this.lineParams[0]
const b = this.lineParams[1]
const c = this.lineParams[2]
const h = this.circleCenter[0]
const k = this.circleCenter[1]
const r = this.circleRadius
this.intersectionPoints = []
// 处理特殊情况
if (Math.abs(a) < 0.001 && Math.abs(b) < 0.001) {
return
}
if (Math.abs(a) < 0.001) {
// 直线平行于x轴: y = -c/b
const y = -c / b
const discriminant = r * r - (y - k) * (y - k)
if (discriminant >= -0.001) {
if (Math.abs(discriminant) < 0.001) {
// 相切
const x = h
this.intersectionPoints.push(`P: (${x.toFixed(2)}, ${y.toFixed(2)})`)
} else {
// 相交
const sqrtDiscriminant = Math.sqrt(discriminant)
const x1 = h - sqrtDiscriminant
const x2 = h + sqrtDiscriminant
this.intersectionPoints.push(`P1: (${x1.toFixed(2)}, ${y.toFixed(2)})`)
this.intersectionPoints.push(`P2: (${x2.toFixed(2)}, ${y.toFixed(2)})`)
}
}
} else if (Math.abs(b) < 0.001) {
// 直线平行于y轴: x = -c/a
const x = -c / a
const discriminant = r * r - (x - h) * (x - h)
if (discriminant >= -0.001) {
if (Math.abs(discriminant) < 0.001) {
// 相切
const y = k
this.intersectionPoints.push(`P: (${x.toFixed(2)}, ${y.toFixed(2)})`)
} else {
// 相交
const sqrtDiscriminant = Math.sqrt(discriminant)
const y1 = k - sqrtDiscriminant
const y2 = k + sqrtDiscriminant
this.intersectionPoints.push(`P1: (${x.toFixed(2)}, ${y1.toFixed(2)})`)
this.intersectionPoints.push(`P2: (${x.toFixed(2)}, ${y2.toFixed(2)})`)
}
}
} else {
// 一般情况:解方程组
const m = -a / b
const n = -c / b
// 展开后得到:ax² + bx + c = 0
const A = 1 + m * m
const B = -2 * h + 2 * m * (k - n)
const C = h * h + (k - n) * (k - n) - r * r
const discriminant = B * B - 4 * A * C
if (discriminant >= -0.001) {
if (Math.abs(discriminant) < 0.001) {
// 相切
const x = -B / (2 * A)
const y = m * x + n
this.intersectionPoints.push(`P: (${x.toFixed(2)}, ${y.toFixed(2)})`)
} else {
// 相交
const sqrtDiscriminant = Math.sqrt(discriminant)
const x1 = (-B - sqrtDiscriminant) / (2 * A)
const x2 = (-B + sqrtDiscriminant) / (2 * A)
const y1 = m * x1 + n
const y2 = m * x2 + n
this.intersectionPoints.push(`P1: (${x1.toFixed(2)}, ${y1.toFixed(2)})`)
this.intersectionPoints.push(`P2: (${x2.toFixed(2)}, ${y2.toFixed(2)})`)
}
}
}
}
private drawRelation() {
const width = 400
const height = 300
const centerX = width / 2
const centerY = height / 2
const scale = 20 // 缩放因子
const ctx = this.context
ctx.clearRect(0, 0, width, height)
// 绘制坐标系
this.drawCoordinateSystem(ctx, centerX, centerY, scale)
// 绘制圆
this.drawCircle(ctx, centerX, centerY, scale)
// 绘制直线
this.drawLine(ctx, centerX, centerY, scale)
// 绘制交点
this.drawIntersectionPoints(ctx, centerX, centerY, scale)
// 绘制圆心到直线的距离
this.drawDistance(ctx, centerX, centerY, scale)
}
private drawCoordinateSystem(ctx: CanvasRenderingContext2D, centerX: number, centerY: number, scale: number) {
// 绘制坐标轴
ctx.strokeStyle = '#666666'
ctx.lineWidth = 2
// X轴
ctx.beginPath()
ctx.moveTo(0, centerY)
ctx.lineTo(400, centerY)
ctx.stroke()
// Y轴
ctx.beginPath()
ctx.moveTo(centerX, 0)
ctx.lineTo(centerX, 300)
ctx.stroke()
// 绘制刻度
ctx.font = '10px sans-serif'
ctx.fillStyle = '#666666'
ctx.textAlign = 'center'
// X轴刻度
for (let x = centerX; x < 400; x += scale * 2) {
ctx.beginPath()
ctx.moveTo(x, centerY - 5)
ctx.lineTo(x, centerY + 5)
ctx.stroke()
const value = (x - centerX) / scale
ctx.fillText(value.toString(), x, centerY + 20)
}
for (let x = centerX; x > 0; x -= scale * 2) {
ctx.beginPath()
ctx.moveTo(x, centerY - 5)
ctx.lineTo(x, centerY + 5)
ctx.stroke()
const value = (x - centerX) / scale
ctx.fillText(value.toString(), x, centerY + 20)
}
// Y轴刻度
for (let y = centerY; y < 300; y += scale * 2) {
ctx.beginPath()
ctx.moveTo(centerX - 5, y)
ctx.lineTo(centerX + 5, y)
ctx.stroke()
const value = (centerY - y) / scale
ctx.fillText(value.toString(), centerX - 20, y + 4)
}
for (let y = centerY; y > 0; y -= scale * 2) {
ctx.beginPath()
ctx.moveTo(centerX - 5, y)
ctx.lineTo(centerX + 5, y)
ctx.stroke()
const value = (centerY - y) / scale
ctx.fillText(value.toString(), centerX - 20, y + 4)
}
// 绘制原点
ctx.fillText('O', centerX - 10, centerY + 15)
}
private drawCircle(ctx: CanvasRenderingContext2D, centerX: number, centerY: number, scale: number) {
// 绘制圆
const h = this.circleCenter[0]
const k = this.circleCenter[1]
const screenX = centerX + h * scale
const screenY = centerY - k * scale
const screenRadius = this.circleRadius * scale
ctx.strokeStyle = '#3498DB'
ctx.lineWidth = 2
ctx.beginPath()
ctx.arc(screenX, screenY, screenRadius, 0, 2 * Math.PI)
ctx.stroke()
// 绘制圆心
ctx.fillStyle = '#E74C3C'
ctx.beginPath()
ctx.arc(screenX, screenY, 4, 0, 2 * Math.PI)
ctx.fill()
// 绘制圆心标签
ctx.fillStyle = '#E74C3C'
ctx.font = '14px sans-serif'
ctx.textAlign = 'center'
ctx.fillText('C', screenX, screenY - 10)
}
private drawLine(ctx: CanvasRenderingContext2D, centerX: number, centerY: number, scale: number) {
// 绘制直线 ax + by + c = 0
const a = this.lineParams[0]
const b = this.lineParams[1]
const c = this.lineParams[2]
ctx.strokeStyle = '#27AE60'
ctx.lineWidth = 2
// 计算直线与画布边界的交点
let x1: number, y1: number, x2: number, y2: number
if (Math.abs(b) < 0.001) {
// 直线垂直于x轴
x1 = x2 = -c / a
y1 = -10
y2 = 10
} else {
// 计算直线与左右边界的交点
x1 = -10
y1 = (-a * x1 - c) / b
x2 = 10
y2 = (-a * x2 - c) / b
}
// 转换为屏幕坐标
const screenX1 = centerX + x1 * scale
const screenY1 = centerY - y1 * scale
const screenX2 = centerX + x2 * scale
const screenY2 = centerY - y2 * scale
ctx.beginPath()
ctx.moveTo(screenX1, screenY1)
ctx.lineTo(screenX2, screenY2)
ctx.stroke()
// 绘制直线方程
ctx.fillStyle = '#27AE60'
ctx.font = '14px sans-serif'
ctx.textAlign = 'left'
ctx.fillText(`${a.toFixed(1)}x + ${b.toFixed(1)}y + ${c.toFixed(1)} = 0`, 20, 30)
}
private drawIntersectionPoints(ctx: CanvasRenderingContext2D, centerX: number, centerY: number, scale: number) {
// 绘制交点
ctx.fillStyle = '#F39C12'
ctx.font = '14px sans-serif'
ctx.textAlign = 'center'
for (let i = 0; i < this.intersectionPoints.length; i++) {
const pointStr = this.intersectionPoints[i]
// 从字符串中提取坐标
const match = pointStr.match(/\(([^,]+),\s*([^)]+)\)/)
if (match) {
const x = parseFloat(match[1])
const y = parseFloat(match[2])
const screenX = centerX + x * scale
const screenY = centerY - y * scale
// 绘制交点
ctx.beginPath()
ctx.arc(screenX, screenY, 4, 0, 2 * Math.PI)
ctx.fill()
// 绘制交点标签
ctx.fillText(`P${i+1}`, screenX, screenY - 10)
}
}
}
private drawDistance(ctx: CanvasRenderingContext2D, centerX: number, centerY: number, scale: number) {
// 绘制圆心到直线的距离
const a = this.lineParams[0]
const b = this.lineParams[1]
const c = this.lineParams[2]
const h = this.circleCenter[0]
const k = this.circleCenter[1]
if (Math.abs(a) < 0.001 && Math.abs(b) < 0.001) {
return
}
// 计算垂足坐标
const denominator = a * a + b * b
const footX = (b * (b * h - a * k) - a * c) / denominator
const footY = (a * (-b * h + a * k) - b * c) / denominator
// 转换为屏幕坐标
const screenCenterX = centerX + h * scale
const screenCenterY = centerY - k * scale
const screenFootX = centerX + footX * scale
const screenFootY = centerY - footY * scale
// 绘制垂线
ctx.strokeStyle = '#95A5A6'
ctx.lineWidth = 1
ctx.setLineDash([5, 5])
ctx.beginPath()
ctx.moveTo(screenCenterX, screenCenterY)
ctx.lineTo(screenFootX, screenFootY)
ctx.stroke()
ctx.setLineDash([])
// 绘制距离标签
ctx.fillStyle = '#95A5A6'
ctx.font = '12px sans-serif'
ctx.textAlign = 'center'
const midX = (screenCenterX + screenFootX) / 2
const midY = (screenCenterY + screenFootY) / 2
ctx.fillText(`d = ${this.distance.toFixed(2)}`, midX, midY - 10)
}
}
更多推荐


所有评论(0)