1# draw_geometry.tcl --
2#    Draw and manipulate (plane) geometrical objects. Well, it is
3#    merely an illustration of what I mean by "geometrical drawing app"
4#
5#    Note:
6#    Not quite an example of the use of the Diagrams pacakge, but
7#    it is related
8#
9
10# PixelPoint --
11#    Compute pixel coordinates
12# Arguments:
13#    x         X-coordinate/point
14#    y         Y-coordinate/point
15#    type      Type of object
16# Result:
17#    List of pixel coordinates
18#
19proc PixelPoint {x {y {}} {type {}}} {
20    if { $y == {} } {
21        set px [lindex $x 1]
22        set py [lindex $x 2]
23        return [list [expr {$px*100+200}] [expr {-$py*100+200}]]
24    }
25    if { $type == "oval" } {
26        return [list [expr {$x*100+200-2}] [expr {-$y*100+200-2}] \
27                     [expr {$x*100+200+2}] [expr {-$y*100+200+2}]]
28    }
29}
30
31# point --
32#    Create (and draw) a point at given coordinates
33# Arguments:
34#    x         X-coordinate
35#    y         Y-coordinate
36# Result:
37#    A point at the given coordinates
38#
39proc point {x y} {
40    .c create oval [PixelPoint $x $y oval] -fill black
41    return [list POINT $x $y]
42}
43
44# line --
45#    Create (and draw) a line through two points
46# Arguments:
47#    point1    First point
48#    point2    Second point
49# Result:
50#    A line through the two points
51#
52proc line {point1 point2} {
53    .c create line [concat [PixelPoint $point1] [PixelPoint $point2]] -fill black
54    return [list LINE $point1 $point2]
55}
56
57# circle --
58#    Create (and draw) a circle at given coordinates
59# Arguments:
60#    point     Centre of the circle
61#    rad       Radius
62# Result:
63#    A circle at the given centre and given radius
64#
65proc circle {point rad} {
66    set x  [lindex $point 1]
67    set y  [lindex $point 2]
68    set p1 [list POINT [expr {$x+$rad}] [expr {$y+$rad}]]
69    set p2 [list POINT [expr {$x-$rad}] [expr {$y-$rad}]]
70    .c create oval [concat [PixelPoint $p1] [PixelPoint $p2]] -outline black
71    return [list CIRCLE $point $rad]
72}
73
74# distance --
75#    Compute the distance between two objects
76# Arguments:
77#    obj1      Point, line, ...
78#    obj2      Point, line, ...
79# Result:
80#    Distance between the given objects (now: only points)
81#
82proc distance {obj1 obj2} {
83    if { [lindex $obj1 0] == "POINT" } {
84        set px1 [lindex $obj1 1]
85        set py1 [lindex $obj1 2]
86        if { [lindex $obj2 0] == "POINT" } {
87            set px2 [lindex $obj2 1]
88            set py2 [lindex $obj2 2]
89            return [expr {hypot($px2-$px1,$py2-$py1)}]
90        } else {
91            error "Types unsupported"
92        }
93    } else {
94        error "Types unsupported"
95    }
96}
97
98# inprod --
99#    Compute the inproduct of two vectors
100# Arguments:
101#    vect1     First vector
102#    vect2     Second vector
103# Result:
104#    Inproduct
105#
106proc inprod {vect1 vect2} {
107    set vx1 [lindex $vect1 1]
108    set vy1 [lindex $vect1 2]
109    set vx2 [lindex $vect2 1]
110    set vy2 [lindex $vect2 2]
111
112    return [expr {$vx1*$vx2+$vy1*$vy2}]
113}
114
115# pointonline --
116#    Compute the coordinates of a point on a line
117# Arguments:
118#    line      Line in question
119#    lambda    Parameter value
120# Result:
121#    Point on the line
122#
123proc pointonline {line lambda} {
124    set v   [vectorfromline $line]
125
126    set vx  [lindex $v 1]
127    set vy  [lindex $v 2]
128    set px  [lindex $line 1 1]
129    set py  [lindex $line 1 2]
130    set x   [expr {$px+$lambda*$vx}]
131    set y   [expr {$py+$lambda*$vy}]
132
133    return [point $x $y] ;# Make it visible
134}
135
136# vectorfromline --
137#    Compute the directional vector of a line
138# Arguments:
139#    line      Line in question
140# Result:
141#    Vector in the direction of the line
142#
143proc vectorfromline {line} {
144    set px1 [lindex $line 1 1]
145    set py1 [lindex $line 1 2]
146    set px2 [lindex $line 2 1]
147    set py2 [lindex $line 2 2]
148    set vx  [expr {$px2-$px1}]
149    set vy  [expr {$py2-$py1}]
150
151    return [list VECTOR $vx $vy]
152}
153
154# diffvector --
155#    Compute the vector from one point to the next
156# Arguments:
157#    point1    First point
158#    point2    Second point
159# Result:
160#    Vector
161#
162proc diffvector {point1 point2} {
163    set px1 [lindex $point1 1]
164    set py1 [lindex $point1 2]
165    set px2 [lindex $point2 1]
166    set py2 [lindex $point2 2]
167    set vx  [expr {$px2-$px1}]
168    set vy  [expr {$py2-$py1}]
169
170    return [list VECTOR $vx $vy]
171}
172
173# normal --
174#    Compute the normal vector to another vector or a line
175# Arguments:
176#    obj       Directed object
177# Result:
178#    Vector normal to the direction of the object
179#
180proc normal {obj} {
181    if { [lindex $obj 0] == "LINE" } {
182        set obj [vectorfromline $obj]
183    }
184
185    set vy  [expr {-[lindex $obj 1]}]
186    set vx  [lindex $obj 2]
187    set len [expr {hypot($vx,$vy)}]
188
189    return [list VECTOR [expr {$vx/$len}] [expr {$vy/$len}]]
190}
191
192# intersect --
193#    Compute the intersection between two objects
194# Arguments:
195#    obj1      line, circle, ...
196#    obj2      line, circle, ...
197# Result:
198#    One point or a collection of points (now: only lines)
199#
200proc intersect {obj1 obj2} {
201    if { [lindex $obj1 0] == "LINE" } {
202        #
203        # Construct the equation for the line obj1
204        #
205        set n1 [normal $obj1]
206        set p1 [lindex $obj1 1]
207
208        if { [lindex $obj2 0] == "LINE" } {
209            #
210            # Get the parametrisation of the line obj2
211            #
212            set v2     [vectorfromline $obj2]
213            set p2     [lindex $obj2 1]
214            set lambda [expr {[inprod [diffvector $p2 $p1] $n1]/ \
215                              [inprod $v2 $n1]}]
216            return [pointonline $obj2 $lambda]
217        } else {
218            error "Types unsupported"
219        }
220    } else {
221        error "Types unsupported"
222    }
223}
224
225#
226# Create the standard canvas
227#
228pack [canvas .c -width 400 -height 300 -bg white]
229
230#
231# Simple illustration:
232# Define two lines, get their intersection and draw a circle with that
233# point as the centre.
234#
235
236circle [point  0 0] 1
237line   [point -3 0] [point 3 0]
238set p  [point [expr {cos(1)}] [expr {sin(1)}]]
239line   [point  1 0] $p
240line   [point -1 0] $p
241
242.c move all 0 -50
243update
244.c postscript -file circle.eps
245