一、数学解释
如何通过三个点确定一个圆?
这是一个有趣的几何问题,同时也是计算机图形学要处理的问题。
这里给出一个求解办法:
给出三个点:(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^22):(mpx - cpx)^2 + (mpy - cpy)^2 = r^23):(epx - cpx)^2 + (epy - cpy)^2 = r^2
解开等式,得:
1):spx^2 + cpx^2 - 2 * spx * cpx + spy^2 + cpy^2 - 2 * spy * cpy = r^22):mpx^2 + cpx^2 - 2 * mpx * cpx + mpy^2 + cpy^2 - 2 * mpy * cpy = r^23):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)] / 25):cpx * (spx - epx) + cpy * (spy - epy) =[(spx^2 - epx^2) + (spy^2 - epy^2)] / 2
进行代数替代,设:
smx = spx - mpxsmy = spy - mpysex = spx - epxsey = spy - epysmxy = [(spx^2 - mpx^2) + (spy^2 - mpy^2)] / 2sexy = [(spx^2 - epx^2) + (spy^2 - epy^2)] / 2
简化等式为:
4):smx * cpx + smy * cpy = smxy5):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-linex (- y 6)x (+ y 6))(send dc draw-line(- x 6) y(+ x 6) y)));运行结果:-