dusthillresident - Sun 3 May 14:38:49 BST 2026 I wrote this routine for drawing polygons in photo images. I hope you'll find it interesting.
package require Tk
proc polygon {img colour points} {
set l [llength $points]
if {$l < 3} return
# Get the start and end of the polygon shape on the x axis
set xPoints [lmap i $points {lindex $i 0}]
set minX [::tcl::mathfunc::int [::tcl::mathfunc::min {*}$xPoints]]
set maxX [::tcl::mathfunc::int [::tcl::mathfunc::max {*}$xPoints]]
# Duplicate the first point at the end, in order for the last line segement to loop back to the first point
lappend points [lindex $points 0]
# Create the lines from the points
set lines {}
for {set i 0} {$i < $l} {incr i} {
lassign [lindex $points $i] x1 y1
lassign [lindex $points $i+1] x2 y2
if {$x1==$x2 && $y1 == $y2} continue
set x1 [expr {$x1+0.0000000000001}]; set x2 [expr {$x2+0.0000000000001}]; # stupid bodge fix.
set slope [expr { ($y2-$y1)/double($x2-$x1) }]
# We don't need to consider lines that are straight up vertical
if {$slope==-Inf || $slope==Inf} continue
lappend lines [list $x1 $x2 $y1 $slope ]
}
# For every vertical space
set maxX [expr {min([image width $img],$maxX)}]
for {set x [expr {max(0,$minX)}]} {$x < $maxX} {incr x} {
# Get the list of intersections for this vertical line
set intersections [lmap i $lines {
lassign $i x1 x2 y1 slope
if {$x<min($x1,$x2) || $x>max($x1,$x2)} continue
expr {$y1+$slope*($x-$x1)}
}]
set intersections [lsort -real $intersections]
if {[lindex $intersections 0]>=[image height $img]} continue
if {[lindex $intersections end]<=0} continue
# And draw the line for each polygon
set n [llength $intersections]
for {set j 0} {$j < ($n & -2)} {incr j 2} {
set y1 [expr {max(0,int([lindex $intersections $j]))}]
if {$y1>[image height $img]} break
set y2 [expr {min([image height $img],int([lindex $intersections $j+1])) }]
if {$y2<0} continue
$img put $colour -to $x $y1 [expr {$x+1}] $y2
}
}
}
# ------ demonstration ----------
set w 640
set h 480
expr {srand(0)}
proc rnd {x} {expr {int(rand()*$x)}}
set img [image create photo -width $w -height $h]
$img put black -to 0 0 $w $h
pack [button .b -text "Clear points" -command {set points {}; $img put black -to 0 0 $w $h}]
pack [label .l -image $img]
wm title . "Polygon example - click image to add points"
for {set i 0} {$i < 10} {incr i} {
lappend points [list [rnd $w] [rnd $h]]
}
polygon $img "#ff7f7f" $points
bind .l <ButtonPress-1> {
lappend points [list %x %y]
$img put black -to 0 0 $w $h
polygon $img "#ff7f7f" $points
}
dusthillresident Wed 6 May 23:59:04 BST 2026
The routine posted above was the first one I did, I did that one by myself.
Now, today, I started making a new one, and when I had bugs I asked chatgpt about it. So this one is 'vibe coded', I can't take any credit for it at all. I wasn't smart enough to do it on my own.
proc polygon {img colour points} {
if {[llength $points] < 3} {return}
set lines {}
lappend points [lindex $points 0]
for {set i 0} {$i < [llength $points]-1} {incr i} {
lassign [lmap j [join [list [lrange [lindex $points $i] 0 1] [lrange [lindex $points $i+1] 0 1]]] {expr {int($j)}}] x1 y1 x2 y2
if {$x1==$x2 && $y1==$y2} {continue}
lappend lines [list $x1 $y1 $x2 $y2 [expr { double($x2-$x1)/double($y2-$y1) }]]
}
if {[llength $lines] < 3} {return}
set minY [expr { max( 0, int([::tcl::mathfunc::min {*}[lmap i $points {lindex $i 1}]]) ) }]
set maxY [expr { min( [image height $img], int([::tcl::mathfunc::max {*}[lmap i $points {lindex $i 1}]]) ) }]
for {set y $minY} {$y < $maxY} {incr y} {
set intersects {}
foreach line $lines {
lassign $line x1 y1 x2 y2 s
if {$y>=min($y1,$y2) && $y<max($y1,$y2) } {
lappend intersects [expr { $x1 + $s*($y-$y1) }]
}
}
if { [llength $intersects] < 2 } {continue}
set intersects [lsort -real $intersects]
set intersects [lrange $intersects 0 [expr { [llength $intersects]-1-([llength $intersects]&1) }]]
foreach {x1 x2} $intersects {
if {$x2<=0 || $x1>=[image width $img]} {continue}
$img put $colour -to [expr { max( 0, int( $x1 ) ) }] $y [expr { min( [image width $img], int( $x2 ) ) }] [expr {$y+1}]
}
}
}
dusthillresident - Thu 7 May 19:16:17 BST 2026
Here's another one I did a couple of days ago on my laptop. I didn't post it until now because I only just now got my laptop out to retrieve the file. It seems like it works but I didn't test it very much. I hope you'll appreciate it.
proc polygon {img colour points} {
if {! [llength $points] } {return}
set points [join [lmap i $points {lrange $i 0 1}]]
# Loop back to start at the end
lappend points {*}[lrange $points 0 1]
# Create list of lines from points
set lines {}
for {set i 0} {$i < [llength $points]} {incr i 2} {
lassign [lrange $points $i $i+3] x1 y1 x2 y2
if {$x1==$x2 && $y1==$y2} {continue}
if {$x2 eq {} || $y2 eq {}} {continue}
lappend lines [list $x1 $y1 $x2 $y2 [expr { ($x2-$x1)/double($y2-$y1) }]]
}
# For each row, find the lines intersecting, and plot. Consider this list of intersection points:
# o o o o o o
# We need to plot so that it looks like this:
# o---o o---o o---o
# Let's go
set yPoints [lmap {x y} $points {set y}]
set minY [expr { int( max( 0, [::tcl::mathfunc::min {*}$yPoints ] ) ) }]
set maxY [expr { int( min( [image height $img], [::tcl::mathfunc::max {*}$yPoints] ) ) }]
unset yPoints
for {set y $minY} {$y < $maxY} {incr y} {
set intersects {}
foreach line $lines {
lassign $line x1 y1 x2 y2 slope
if { $slope==Inf || $slope==-Inf } {
if {$y==int($y1)} {lappend intersects $x1 $x2} else {continue}
} elseif { $y >= min($y1,$y2) && $y < max($y1,$y2) } {
lappend intersects [expr { max( 0, $x1 + $slope * ($y-$y1) ) }]
}
}
set intersects [lsort -real $intersects]
foreach {x1 x2} $intersects {
if {$x2 eq {}} {continue}
$img put $colour -to [expr {int($x1)}] $y [expr {int($x2)}] [expr {$y+1}]
}
}
}