Richard Suchenwirth - In the weekend fun project for Describing and rendering flags in Tcl, some geometrical shapes were required: sun (Taiwan style: 12 triangles arranged around a circle), moon (crescent), and stars. Since these involve quite some arithmetics on coordinates, the tasks were delegated to a separate *geom* package, whose procedures give and take coordinates, but don't know about canvases or colors.

A five-point star is described as a polygon of ten points: five are the corners of a regular pentagon, the others are the crossing points of every pair of lines between the pentagon edges. Maybe this code can be reused in other ways, too - feel free to grab it!

namespace eval geom { variable p360 [expr atan(1.)*8/360] proc crosspoint {xa ya xb yb xc yc xd yd} { # compute crossing between two straight lines ("" if parallel) if {$xa==$xb} { set xres $xa ;# vertical - couldn't divide by deltax } else { set a [expr double($yb-$ya)/($xb-$xa)] set b [expr $yb-($a*$xb)] } if {$xc==$xd} { set xres $xc ;# vertical - couldn't divide by deltax } else { set c [expr double($yd-$yc)/($xd-$xc)] set d [expr $yd-($c*$xd)] } if {[info exists a] && [info exists c] && $a==$c} {return ""} if {![info exists a] && ![info exists c]} {return ""} if ![info exists xres] { set xres [expr double($d-$b)/($a-$c)] } if [info exists a] { set yres [expr $a*$xres+$b] } else { set yres [expr $c*$xres+$d] } list $xres $yres } proc star5 {x y r {skew 0}} { # compute coordinates for a five-point star variable p360 foreach {p angle} {A 0 B 72 C 144 D 216 E 288} { set rad [expr ($angle-$skew)*$p360] set $p [list [expr $x+$r*sin($rad)] [expr $y-$r*cos($rad)]] } set F [eval crosspoint $A $C $B $E] set G [eval crosspoint $B $D $A $C] set H [eval crosspoint $C $E $B $D] set I [eval crosspoint $D $A $C $E] set J [eval crosspoint $E $B $A $D] concat $A $F $B $G $C $H $D $I $E $J } proc sunrays {x y r {n 12}} { # rotated triangles around a circle variable p360 for {set i 0} {$i<$n} {incr i} { set rad [expr ($i*360./$n)*$p360] set rad1 [expr ($i*360./$n-170./$n)*$p360] set rad2 [expr ($i*360./$n+170./$n)*$p360] lappend res [list \ [expr $x+$r*sin($rad)] [expr $y-$r*cos($rad)] \ [expr $x+$r*0.67*sin($rad1)] [expr $y-$r*0.67*cos($rad1)] \ [expr $x+$r*0.67*sin($rad2)] [expr $y-$r*0.67*cos($rad2)] \ ] } set res } }

NB. This code contains no "moon". It is supposed to come here, but in the hurry I was in I put it directly into Describing and rendering flags in Tcl - it's just the superimposition of two circles anyway.

*MGS* - Here's an additional routine to draw an n-pointed star. It uses the *crosspoint* proc above:

# star -- # compute coordinates for an n-point star proc geom::star {x y r n} { variable p360 # Count how many nodes to skip set s [expr {($n - 1) / 2}] set n_even [string is integer [expr {$n / 2}]] set s_even [string is integer [expr {$s / 2}]] # Calculate all the nodes for {set node 0} {$node < $n} {incr node} { set angle [expr $node * 360.0 / $n] set rad [expr {$angle * $p360}] set nodex($node) [expr {$x+$r*sin($rad)}] set nodey($node) [expr {$y-$r*cos($rad)}] } # Get intersection of lines if { $n > 4 } { for {set node1 0} {$node1 < $n} {incr node1} { set node2 [expr $node1 + $s] if { $node2 >= $n } { set node2 [expr $node2 - $n] } set node4 [expr $node1 + 1] if { $node4 >= $n } { set node4 [expr $node4 - $n] } set node3 [expr $node4 - $s + $n] if { $node3 >= $n } { set node3 [expr $node3 - $n] } foreach {X Y} [crosspoint \ $nodex($node1) $nodey($node1) \ $nodex($node2) $nodey($node2) \ $nodex($node3) $nodey($node3) \ $nodex($node4) $nodey($node4)] { break } set sectx($node1) $X set secty($node1) $Y } } set return {} if { $n > 4 } { for {set node 0} {$node < $n} {incr node} { lappend return \ $nodex($node) $nodey($node) \ $sectx($node) $secty($node) } } else { for {set node 0} {$node < $n} {incr node} { lappend return \ $nodex($node) $nodey($node) } } return $return } # Demo code set W .test toplevel $W grid columnconfigure $W 1 -weight 1 canvas $W.c -width 200 -height 200 -xscrollcommand [list $W.x set] scrollbar $W.x -orient horizontal -command [list $W.c xview] grid $W.c -column 1 -row 1 -sticky nsew grid $W.x -column 1 -row 2 -sticky ew for {set n 3} {$n <= 20} {incr n} { set x [expr ($n - 2) * 100] set y 100 $W.c create polygon [geom::star $x $y 50 $n] \ -fill blue -outline red $W.c create text $x $y \ -text $n \ -fill yellow -anchor c \ -font {Helvetica 20 bold} } $W.c configure -scrollregion [$W.c bbox all]

I'm sure somebody can improve on this, though.

Here's a slicker routine for drawing a star (by Keith Vetter on c.l.t):

KPV - some explanation behind this code. My first version simply drew the five lines of a star which looked okay, but when I added a binding to it, it only "took" when clicking outside the central pentagon. It seems the code to determine what's *inside* (the winding rule) differs between the drawing code and the binding code.

The math behind this code is as follows: the angle to each of the 10 points is obvious, the distance to the outer points is given (*delta*), so we just need the distance of the inner points (*delta2*). My flash of insight came when I realized the Y value of the first inner point equals that of the next outer point. So to compute delta2, I just scaled the unit vector to that inner point until the Y value is what we know it should be, yielding the X value and then delta2.

KPV - I just reused this code recently and realized that I could simplify it.

proc MakeStar {x y delta} { set pi [expr {atan(1) * 4}] # Compute distance to inner corner #set x1 [expr {$delta * 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 }

US See also Xmas Stars

gold added pix, canvas with multipointed star proc above,

**LVwikignoming - 2010-06-11 08:11:29**

Hey gold, are all these alt= strings being added supposed to be rendered by the wiki software? I don't understand their purpose. gold alt pix of html as text alternative doesn't seem to work,I'll pull strings.Does not add much anyway.