三点定圆的Racket实现
一、数学解释
如何通过三个点确定一个圆?
这是一个有趣的几何问题,同时也是计算机图形学要处理的问题。
这里给出一个求解办法:
给出三个点:(spx,spy),(mpx,mpy),(epx,epy)
求解圆。
根据圆半径(r)、圆心(cpx,cpy)、圆上的一点(x,y)的关系:
(x - cpx)^2 + (y - cpy)^2 = r^2
得出三个等式:
1):(spx - cpx)^2 + (spy - cpy)^2 = r^2
2):(mpx - cpx)^2 + (mpy - cpy)^2 = r^2
3):(epx - cpx)^2 + (epy - cpy)^2 = r^2
解开等式,得:
1):spx^2 + cpx^2 - 2 * spx * cpx + spy^2 + cpy^2 - 2 * spy * cpy = r^2
2):mpx^2 + cpx^2 - 2 * mpx * cpx + mpy^2 + cpy^2 - 2 * mpy * cpy = r^2
3):epx^2 + cpx^2 - 2 * epx * cpx + epy^2 + cpy^2 - 2 * epy * cpy = r^2
执行等式相减1)-2),1)-3),得到:
4):cpx * (spx - mpx) + cpy * (spy - mpy) =[(spx^2 - mpx^2) + (spy^2 - mpy^2)] / 2
5):cpx * (spx - epx) + cpy * (spy - epy) =[(spx^2 - epx^2) + (spy^2 - epy^2)] / 2
进行代数替代,设:
smx = spx - mpx
smy = spy - mpy
sex = spx - epx
sey = spy - epy
smxy = [(spx^2 - mpx^2) + (spy^2 - mpy^2)] / 2
sexy = [(spx^2 - epx^2) + (spy^2 - epy^2)] / 2
简化等式为:
4):smx * cpx + smy * cpy = smxy
5):sex * cpx + sey * cpy = sexy
则:
6):cpx = (smxy - smy * cpy) / smx
再计算得到cpy:
cpy = (sexy * smx - sex * smxy) / (smx * sey - sex * smy)
则:
cpx = (smxy * sey - smy * sexy) / (smx * sey - sex * smy)
注:也可以直接将取得的cpy代入式(6)中取得cpx值,计算更简单。
最后计算r值:
r = [(spx - cpx)^2 + (spy - cpy)^2]^(1/2)
二、程序实现:
根据以上推导,实现如下:
;3p-circle.rkt
;三点确定圆:
#lang racket
(provide
(struct-out point)
3p-circle)
;Racket实现:==================
;点结构:
(struct point (x y))
;三点定圆:
(define (3p-circle sp mp ep)
(let* ([spx (point-x sp)]
[spy (point-y sp)]
[mpx (point-x mp)]
[mpy (point-y mp)]
[epx (point-x ep)]
[epy (point-y ep)]
[smx (- spx mpx)]
[smy (- spy mpy)]
[sex (- spx epx)]
[sey (- spy epy)]
[smxy (/
(+
(- (expt spx 2)
(expt mpx 2))
(- (expt spy 2)
(expt mpy 2)))
2)]
[sexy (/
(+
(- (expt spx 2)
(expt epx 2))
(- (expt spy 2)
(expt epy 2)))
2)]
[cpy (/
(- (* sexy smx)
(* smxy sex))
(- (* smx sey)
(* sex smy)))]
[cpx (/
(- smxy (* smy cpy))
smx)]
[r (sqrt
(+
(expt (- cpx epx) 2)
(expt (- cpy epy) 2)))])
(values (point cpx cpy) r)))
三、测试程序
;test.rkt
#lang racket/gui
(require racket/draw)
(require "3p-circle.rkt")
;测试:========================
;定义点:
(define sp (point 498 116))
(define mp (point 181 280))
(define ep (point 290 458))
;定义视图:
(define main-frame
(new frame%
[label "3点定圆测试"]
[width 800]
[height 600]
[border 5]))
(define canvas
(new canvas%
[parent main-frame]
[paint-callback
(lambda (canvas dc)
(draw-circle/3p dc))]))
;显示视图:
(send main-frame show #t)
;绘圆:
(define (draw-circle/3p dc)
(let-values ([(cp r)
(3p-circle sp mp ep)])
(send dc set-background "black")
(send dc clear)
;画圆:
(send dc set-pen "white" 1 'solid);'
(send dc set-brush "white" 'transparent);'
(let* ([x (- (point-x cp) r)]
[y (- (point-y cp) r)]
[w (* r 2)])
(send dc draw-ellipse x y w w))
;画标志点:
(send dc set-pen "white" 0 'transparent);'
(send dc set-brush "red" 'solid);'
(draw-3ps dc)
(send dc set-pen "red" 1 'solid );'
(draw-center dc cp)))
;画三个点:
(define (draw-3ps dc)
(draw-1p dc sp)
(draw-1p dc mp)
(draw-1p dc ep))
;画单个点:
(define (draw-1p dc p)
(send dc draw-ellipse
(point-x p)
(point-y p)
6 6))
;画圆心十字叉:
(define (draw-center dc cp)
(let ([x (point-x cp)]
[y (point-y cp)])
(send dc draw-line
x (- y 6)
x (+ y 6))
(send dc draw-line
(- x 6) y
(+ x 6) y)))
;运行结果:
-