18 return ($x < 1e-10 && $x > -1e-10);
21 sub draw_para($$$$$) {
22 # Draw a parabola y=a(x-x0)^2 + y0
23 my ($cairo,$a,$x0,$y0,$ww) = @_;
26 $cairo->move_to($x0, $y0);
27 $cairo->line_to($x0, 0);
30 for (my $x=0; $x<$ww; $x++) {
31 my $y = $a*sqr($x-$x0) + $y0;
32 next if ($y < -10000 || $y > 10000);
34 $cairo->line_to($x, $y);
36 $cairo->move_to($x, $y);
44 my ($h0, $x0, $y0) = @_;
47 return (undef, $x0, $h0);
49 return (1/(2*$h), $x0, $h0 + $h/2);
53 sub solve_circ($$$$$$) {
54 my ($x0, $y0, $x1, $y1, $x2, $y2) = @_;
55 my $axc = ($x0 + $x1) / 2;
56 my $ayc = ($y0 + $y1) / 2;
59 my $bxc = ($x1 + $x2) / 2;
60 my $byc = ($y1 + $y2) / 2;
63 my $alpha = (($bxc-$axc)*(-$bdy) - ($byc-$ayc)*(-$bdx)) /
64 ($adx*(-$bdy) - $ady*(-$bdx));
65 my $x = $axc + $alpha*$adx;
66 my $y = $ayc + $alpha*$ady;
67 my $r = sqrt(sqr($x-$x0) + sqr($y-$y0));
71 sub para_isec($$$$$$) {
72 # Parameters of two parabolas
73 my ($a, $b, $c, $d, $e, $f) = @_;
74 if (!defined($a) || !defined($b)) {
75 # Either is degenerate
77 my $y = $a*sqr($e-$b) + $c;
78 if ($y < $f) { return [$e, $y]; } else { return; }
79 } elsif (defined($d)) {
80 my $y = $d*sqr($b-$e) + $f;
81 if ($y < $c) { return [$b, $y]; } else { return; }
85 # Coefficients of the corresponding quadratic equation
87 my $B = 2*($d*$e - $a*$b);
88 my $C = $a*sqr($b) - $d*sqr($e) + $c - $f;
89 # Is the equation linear?
91 if (is_zero($B)) { return; }
93 my $y = $a*sqr($x-$b) + $c;
96 # No, really quadratic. Consider its discriminant.
97 my $D = sqr($B) - 4*$A*$C;
100 } elsif (is_zero($D)) {
101 my $x = -$B / (2*$A);
102 my $y = $a*sqr($x-$b) + $c;
106 my $x1 = (-$B + $DD) / (2*$A);
107 my $x2 = (-$B - $DD) / (2*$A);
108 if ($x1 > $x2) { ($x1,$x2) = ($x2,$x1); }
109 my $y1 = $a*sqr($x1-$b) + $c;
110 my $y2 = $a*sqr($x2-$b) + $c;
111 return ([$x1,$y1], [$x2,$y2]); # In order left, right
115 my $area = Gtk2::DrawingArea->new();
119 my $win = $area->window;
120 my $alloc = $area->allocation;
121 my $w = $alloc->width;
122 my $h = $alloc->height;
124 $cairo = Gtk2::Gdk::Cairo::Context->create($win);
125 $cairo->rectangle(0, 0, $w, $h);
126 $cairo->set_source_rgb(0, 0, 0);
129 my @pts = ( [$w/3, $h-600], [$w/2, $h-700], [2*$w/3, $h-650] );
130 my ($cx, $cy, $cr) = solve_circ($pts[0]->[0], $pts[0]->[1], $pts[1]->[0], $pts[1]->[1], $pts[2]->[0], $pts[2]->[1]);
133 if (abs($ch-$h0) < 2) {
137 for (my $i=0; $i<$#pts; $i++) {
140 my $xc = ($p->[0] + $q->[0]) / 2;
141 my $yc = ($p->[1] + $q->[1]) / 2;
142 my $dx = ($q->[1] - $p->[1]);
143 my $dy = ($p->[0] - $q->[0]);
144 $cairo->set_source_rgb(0.5, 0, 0.5);
145 $cairo->move_to($xc - 10*$dx, $yc - 10*$dy);
146 $cairo->line_to($xc + 10*$dx, $yc + 10*$dy);
148 $cairo->set_source_rgb(0.5, 0.5, 0.5);
149 $cairo->move_to($p->[0], $p->[1]);
150 $cairo->line_to($q->[0], $q->[1]);
156 my $xc = ($p->[0] + $q->[0]) / 2;
157 my $yc = ($p->[1] + $q->[1]) / 2;
158 my $dx = ($q->[1] - $p->[1]);
159 my $dy = ($p->[0] - $q->[0]);
160 $cairo->set_source_rgb(0.5, 0, 0.5);
161 $cairo->move_to($xc - 10*$dx, $yc - 10*$dy);
162 $cairo->line_to($xc + 10*$dx, $yc + 10*$dy);
164 $cairo->set_source_rgb(0.5, 0.5, 0.5);
165 $cairo->move_to($p->[0], $p->[1]);
166 $cairo->line_to($q->[0], $q->[1]);
170 $cairo->set_source_rgb(1, 0, 1);
171 $cairo->arc($cx, $cy, $cr, 0, 2*$pi);
174 $cairo->set_source_rgb(1, 1, 1);
175 $cairo->move_to(0, $h0);
176 $cairo->line_to($w, $h0);
179 foreach my $p (@pts) {
180 $cairo->arc($p->[0], $p->[1], 2, 0, 2*$pi);
184 $cairo->set_source_rgb(0, 1, 0);
186 foreach my $p (@pts) {
187 next if ($p->[1] > $h0);
188 my ($pa,$px,$py) = solve_para($h0, $p->[0], $p->[1]);
189 draw_para($cairo, $pa, $px, $py, $w);
190 push @paras, [$pa,$px,$py];
195 for (my $i=0; $i<$#paras; $i++) {
197 my $q = $paras[$i+1];
198 my @isec = para_isec($p->[0], $p->[1], $p->[2], $q->[0], $q->[1], $q->[2]);
199 foreach my $pt (@isec) {
200 $cairo->set_source_rgb(1, 0, 0);
201 $cairo->arc($pt->[0], $pt->[1], 2, 0, 2*$pi);
208 $area->signal_connect("expose-event" => sub {
212 my $window = Gtk2::Window->new ('toplevel');
213 $window->signal_connect ("delete-event" => sub { Gtk2->main_quit });
214 $window->set_title("Brum");
215 $window->set_wmclass("anim", "Anim");
216 $window->set_default_size(640, 480);
217 $window->add ($area);
220 $window->signal_connect("key-press-event" => sub {
222 my $k = Gtk2::Gdk::keyval_name(0, $evt->keyval);
223 if ($k eq "space" || $k eq "Right" || $k eq "Return") {
226 } elsif ($k eq "q" || $k eq "Escape") {