uniquename 2013aug18For readers who do not have the time/facilities/whatever to setup and run the code below, here is an image that shows how this code draws the rounded corners for a given polygon. And you can see the choices of polygons according to the radiobuttons at the bottom of the GUI.
#----------------------------------------------------------------------
#
# RoundPoly -- Draw a polygon with rounded corners in the canvas, based
# off of ideas and code from "Drawing rounded rectangles"
#
# Parameters:
# w - Path name of the canvas
# xy - list of coordinates of the vertices of the polygon
# radii - list of radius of the bend each each vertex
# args - Other args suitable to a 'polygon' item on the canvas
#
# Results:
# Returns the canvas item number of the rounded polygon.
#
# Side effects:
# Creates a rounded polygon in the canvas.
#
#----------------------------------------------------------------------
proc RoundPoly {w xy radii args} {
set lenXY [llength $xy]
set lenR [llength $radii]
if {$lenXY != 2 * $lenR} {
error "wrong number of vertices and radii"
}
# Walk down vertices keeping previous, current and next
foreach {x0 y0} [lrange $xy end-1 end] break
foreach {x1 y1} $xy break
eval lappend xy [lrange $xy 0 1]
set knots {} ;# These are the control points
for {set i 0} {$i < $lenXY} {incr i 2} {
set radius [lindex $radii [expr {$i/2}]]
set r [winfo pixels $w $radius]
foreach {x2 y2} [lrange $xy [expr {$i + 2}] [expr {$i + 3}]] break
set z [_RoundPoly2 $x0 $y0 $x1 $y1 $x2 $y2 $r]
eval lappend knots $z
foreach {x0 y0} [list $x1 $y1] break ;# Current becomes previous
foreach {x1 y1} [list $x2 $y2] break ;# Next becomes current
}
set n [eval $w create polygon $knots -smooth 1 $args]
return $n
}
proc _RoundPoly2 {x0 y0 x1 y1 x2 y2 radius} {
set d [expr { 2 * $radius }]
set maxr 0.75
set v1x [expr {$x0 - $x1}]
set v1y [expr {$y0 - $y1}]
set v2x [expr {$x2 - $x1}]
set v2y [expr {$y2 - $y1}]
set vlen1 [expr {sqrt($v1x*$v1x + $v1y*$v1y)}]
set vlen2 [expr {sqrt($v2x*$v2x + $v2y*$v2y)}]
if {$d > $maxr * $vlen1} {
set d [expr {$maxr * $vlen1}]
}
if {$d > $maxr * $vlen2} {
set d [expr {$maxr * $vlen2}]
}
lappend xy [expr {$x1 + $d * $v1x/$vlen1}] [expr {$y1 + $d * $v1y/$vlen1}]
lappend xy $x1 $y1
lappend xy [expr {$x1 + $d * $v2x/$vlen2}] [expr {$y1 + $d * $v2y/$vlen2}]
return $xy
}
################################################################
#
# Demonstration code
# Code from Regular polygons
proc rp {x0 y0 x1 y1 {n 0}} {
set xm [expr {($x0+$x1)/2.}]
set ym [expr {($y0+$y1)/2.}]
set rx [expr {$xm-$x0}]
set ry [expr {$ym-$y0}]
if {$n==0} {
set n [expr {round(($rx+$ry)*0.5)}]
}
set step [expr {atan(1)*8/$n}]
set res ""
set th [expr {atan(1)*6}] ;#top
for {set i 0} {$i<$n} {incr i} {
lappend res \
[expr {$xm+$rx*cos($th)}] \
[expr {$ym+$ry*sin($th)}]
set th [expr {$th+$step}]
}
set res
}
# Code from Sun, moon, and stars
proc MakeStar {x y delta} {
set pi [expr {atan(1) * 4}]
# Compute distance to inner corner
#set x1 [expr {cos(54 * $pi/180)}] ;# Unit vector to inner point
set y1 [expr {sin(54 * $pi/180)}]
set y2 [expr {$delta * sin(18 * $pi/180)}] ;# Y value to match
set delta2 [expr {$y2 / $y1}]
# Now get all coordinates of the 5 outer and 5 inner points
for {set i 0} {$i < 10} {incr i} {
set d [expr {($i % 2) == 0 ? $delta : $delta2}]
set theta [expr {(90 + 36 * $i) * $pi / 180}]
set x1 [expr {$x + $d * cos($theta)}]
set y1 [expr {$y - $d * sin($theta)}]
lappend coords $x1 $y1
}
return $coords
}
proc doit { args } {
global rad nsides
# Get canvas dimensions shrunk by some
foreach who {x0 y0 x1 y1} val [.c cget -scrollregion] d {30 30 -30 -30} {
set $who [expr {$val + $d}]
}
if {$nsides == -1} { ;# Star
set xy [MakeStar 0 0 [expr {$x1 > $y1 ? $y1 : $x1}]]
} elseif {$nsides == 4} { ;# Want square not diamond
set xy [list $x0 $y0 $x1 $y0 $x1 $y1 $x0 $y1]
} elseif {$nsides == -4} { ;# Rectangle
set y0 [expr {$y0 / 2}]
set y1 [expr {$y1 / 2}]
set xy [list $x0 $y0 $x1 $y0 $x1 $y1 $x0 $y1]
} else { ;# Regular polygon
set xy [rp $x0 $y0 $x1 $y1 $nsides]
}
set radii {}
foreach {x y} $xy {
lappend radii $rad([expr {[llength $radii] & 1}])
}
.c delete poly
.c create poly $xy -fill gray90 -outline black -dash . -tags poly
RoundPoly .c $xy $radii -fill white -outline black -tags poly
.c create poly $xy -fill {} -outline black -dash . -tags poly
}
proc Recenter {W h w} {
set h [expr {$h / 2.0}] ; set w [expr {$w / 2.0}]
$W config -scrollregion [list -$w -$h $w $h]
doit
}
canvas .c -width 500 -height 500 -bd 2 -relief raised
frame .shapes -bd 2 -relief ridge
scale .rad1 -orient horizontal -label "Odd Vertex Radius" -variable rad(0) \
-from 0 -to 200 -command doit -relief ridge
scale .rad2 -orient horizontal -label "Even Vertex Radius" -variable rad(1) \
-from 0 -to 200 -command doit -relief ridge
image create photo ::img::blank -width 1 -height 1
button .about -image ::img::blank -highlightthickness 0 -command \
{tk_messageBox -message "Rounded Polygon\nby Keith Vetter, March 2003"}
place .about -in .shapes -relx 1 -rely 1 -anchor se
set row [set col 0]
foreach {name sides} {Triangle 3 Square 4 Rectangle -4 Pentagon 5 Hexagon 6
Heptagon 7 Octagon 8 Enneagon 9 Decagon 10 Star -1} {
radiobutton .shapes.p$name -text $name -variable nsides \
-command doit -value $sides -anchor w
grid .shapes.p$name -row $row -column $col -sticky ew
if {[incr col] == 5} {incr row ; set col 0}
}
grid .c - -row 0 -sticky news
grid .shapes - -sticky ew
grid .rad1 .rad2 -sticky ew
grid rowconfigure . 0 -weight 1
grid columnconfigure . {0 1} -weight 1
grid columnconfigure .shapes 100 -weight 1
bind .c <Configure> {Recenter %W %h %w}
set nsides 4
set rad(0) 150
set rad(1) 50
