From: Revar Desmera <revarbat@gm...>  20070320 02:01:35

I've been writing a simple 2D CAD program in TCL/Tk, and I've found something odd in how the canvas widget draws bezier curves. That is, it's NOT drawing bezier curves. It's drawing something else. I've doublechecked my math twice, and written routines to generate linepaths via both straight up calculation and by Bezier subdivision. They agree with each other, and with the resources on Bezier curves I've found on the web. When I tell tk to draw the bezier from the original control points, though, and draw a line of similar spline steps using my calculated bezier, the two curves are not even close, except at starting and ending points. I'm talking dozens of pixels off here. I've included some example code showing the error.  Revar proc bezier_line {coords steps} { # Generates a number of points along a bezier curve, # using the cubic bezier formula. foreach {x0 y0 x1 y1 x2 y2 x3 y3} $coords break set xc [expr {3.0*($x1$x0)}] set xb [expr {3.0*($x2$x1)$xc}] set xa [expr {$x3$x0$xc$xb}] set yc [expr {3.0*($y1$y0)}] set yb [expr {3.0*($y2$y1)$yc}] set ya [expr {$y3$y0$yc$yb}] set coords {} set inc [expr {1.0/$steps}] for {set t 0.0} {$t < 1.0} {set t [expr {$t+$inc}]} { set mx [expr {$xa*$t*$t*$t + $xb*$t*$t + $xc*$t +$x0}] set my [expr {$ya*$t*$t*$t + $yb*$t*$t + $yc*$t +$y0}] lappend coords $mx $my } lappend coords $x3 $y3 return $coords } proc bezier_split {coords} { # Mathematically subdivides each bezier curve segment into two curve # segments that each exactly match half of the original curve. set outcoords {} foreach {x0 y0} [lrange $coords 0 1] break lappend outcoords $x0 $y0 foreach {x1 y1 x2 y2 x3 y3} [lrange $coords 2 end] { set mx01 [expr {($x0+$x1)/2.0}] set my01 [expr {($y0+$y1)/2.0}] set mx12 [expr {($x1+$x2)/2.0}] set my12 [expr {($y1+$y2)/2.0}] set mx23 [expr {($x2+$x3)/2.0}] set my23 [expr {($y2+$y3)/2.0}] set mx012 [expr {($mx01+$mx12)/2.0}] set my012 [expr {($my01+$my12)/2.0}] set mx123 [expr {($mx12+$mx23)/2.0}] set my123 [expr {($my12+$my23)/2.0}] set mx0123 [expr {($mx012+$mx123)/2.0}] set my0123 [expr {($my012+$my123)/2.0}] lappend outcoords $mx01 $my01 $mx012 $my012 $mx0123 $my0123 $mx123 $my123 $mx23 $my23 $x3 $y3 set x0 $x3 set y0 $y3 } return $outcoords } canvas .c width 400 height 500 pack .c set path [list 100 50 500 450 100 450 300 50] set calcpath [bezier_line $path 48] set calcpath2 [bezier_split $path] set calcpath2 [bezier_split $calcpath2] set calcpath2 [bezier_split $calcpath2] set calcpath2 [bezier_split $calcpath2] .c create line $path fill yellow smooth 1 splinesteps 16 width 5 .c create line $calcpath fill grey50 smooth 0 width 3 .c create line $calcpath2 fill red smooth 0 width 1 