2
flame - cosmic recursive fractal flames
3
Copyright (C) 1992 Scott Draves <spot@cs.cmu.edu>
5
This program is free software; you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation; either version 2 of the License, or
8
(at your option) any later version.
10
This program is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
GNU General Public License for more details.
15
You should have received a copy of the GNU General Public License
16
along with this program; if not, write to the Free Software
17
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
#include <string.h> /* strcmp */
25
#include "libgimp/gimp.h"
29
#define CHOOSE_XFORM_GRAIN 100
33
* run the function system described by CP forward N generations.
34
* store the n resulting 3 vectors in POINTS. the initial point is passed
35
* in POINTS[0]. ignore the first FUSE iterations.
38
void iterate(cp, n, fuse, points)
44
int i, j, count_large = 0, count_nan = 0;
45
int xform_distrib[CHOOSE_XFORM_GRAIN];
46
double p[3], t, r, dr;
52
* first, set up xform, which is an array that converts a uniform random
53
* variable into one with the distribution dictated by the density
57
for (i = 0; i < NXFORMS; i++)
58
dr += cp->xform[i].density;
59
dr = dr / CHOOSE_XFORM_GRAIN;
62
t = cp->xform[0].density;
64
for (i = 0; i < CHOOSE_XFORM_GRAIN; i++) {
67
t += cp->xform[j].density;
73
for (i = -fuse; i < n; i++) {
74
int fn = xform_distrib[g_random_int_range (0, CHOOSE_XFORM_GRAIN) ];
77
if (p[0] > 100.0 || p[0] < -100.0 ||
78
p[1] > 100.0 || p[1] < -100.0)
83
#define coef cp->xform[fn].c
84
#define vari cp->xform[fn].var
86
/* first compute the color coord */
87
p[2] = (p[2] + cp->xform[fn].color) / 2.0;
89
/* then apply the affine part of the function */
90
tx = coef[0][0] * p[0] + coef[1][0] * p[1] + coef[2][0];
91
ty = coef[0][1] * p[0] + coef[1][1] * p[1] + coef[2][1];
94
/* then add in proportional amounts of each of the variations */
119
double r2 = tx * tx + ty * ty + 1e-6;
129
double r2 = tx * tx + ty * ty; /* /k here is fun */
132
double nx = c1 * tx - c2 * ty;
133
double ny = c2 * tx + c1 * ty;
141
double a, c1, c2, nx, ny;
142
if (tx < -EPS || tx > EPS ||
143
ty < -EPS || ty > EPS)
144
a = atan2(tx, ty); /* times k here is fun */
149
nx = c1 * tx - c2 * ty;
150
ny = c2 * tx + c1 * ty;
158
if (tx < -EPS || tx > EPS ||
159
ty < -EPS || ty > EPS)
160
nx = atan2(tx, ty) / G_PI;
164
ny = sqrt(tx * tx + ty * ty) - 1.0;
175
if (nx < 0.0) nx = nx * 2.0;
176
if (ny < 0.0) ny = ny / 2.0;
181
/* if fuse over, store it */
189
if ((count_large > 10 || count_nan > 10)
190
&& !getenv("PVM_ARCH"))
191
fprintf(stderr, "large = %d nan = %d\n", count_large, count_nan);
195
/* args must be non-overlapping */
196
void mult_matrix(s1, s2, d)
201
d[0][0] = s1[0][0] * s2[0][0] + s1[1][0] * s2[0][1];
202
d[1][0] = s1[0][0] * s2[1][0] + s1[1][0] * s2[1][1];
203
d[0][1] = s1[0][1] * s2[0][0] + s1[1][1] * s2[0][1];
204
d[1][1] = s1[0][1] * s2[1][0] + s1[1][1] * s2[1][1];
210
return s[0][0] * s[1][1] - s[0][1] * s[1][0];
213
void flip_matrix(m, h)
219
/* flip on horizontal axis */
227
/* flip on vertical axis */
237
void transpose_matrix(m)
246
void choose_evector(m, r, v)
256
} else if (b < -EPS) {
267
/* diagonalize the linear part of a 3x2 matrix. the evalues are returned
268
in r as either reals on the diagonal, or a complex pair. the evectors
269
are returned as a change of coords matrix. does not handle shearing
273
void diagonalize_matrix(m, r, v)
279
double m00, m10, m01, m11;
285
c = (m00 * m11) - (m01 * m10);
287
/* should use better formula, see numerical recipes */
289
double r0 = (-b + sqrt(d)) / 2.0;
290
double r1 = (-b - sqrt(d)) / 2.0;
296
choose_evector(m, r0, v + 0);
297
choose_evector(m, r1, v + 1);
298
} else if (d < -EPS) {
299
double uu = -b / 2.0;
300
double vv = sqrt(-d) / 2.0;
301
double w1r, w1i, w2r, w2i;
312
} else if (m01 < -EPS) {
330
double rr = -b / 2.0;
341
/* order the values so that the evector matrix has is positively
342
oriented. this is so that evectors never have to cross when we
343
interpolate them. it might mean that the values cross zero when they
344
wouldn't have otherwise (if they had different signs) but this is the
345
lesser of two evils */
346
if (det_matrix(v) < 0.0) {
354
void undiagonalize_matrix(r, v, m)
363
/* the unfortunate truth is that given we are using row vectors
364
the evectors should be stacked horizontally, but the complex
365
interpolation functions only work on rows, so we fix things here */
367
mult_matrix(r, v, t1);
369
t = 1.0 / det_matrix(v);
370
v_inv[0][0] = t * v[1][1];
371
v_inv[1][1] = t * v[0][0];
372
v_inv[1][0] = t * -v[1][0];
373
v_inv[0][1] = t * -v[0][1];
375
mult_matrix(v_inv, t1, t2);
377
/* the unforunate truth is that i have no idea why this is needed. sigh. */
378
transpose_matrix(t2);
380
/* switch v back to how it was */
389
void interpolate_angle(t, s, v1, v2, v3, tie, cross)
391
double *v1, *v2, *v3;
397
static double lastx, lasty;
399
/* take the shorter way around the circle... */
402
if (d > G_PI + EPS ||
403
(d > G_PI - EPS && tie))
407
if (d > G_PI + EPS ||
408
(d > G_PI - EPS && tie))
411
/* unless we are supposed to avoid crossing */
428
void interpolate_complex(t, s, r1, r2, r3, flip, tie, cross)
430
double r1[2], r2[2], r3[2];
431
int flip, tie, cross;
433
double c1[2], c2[2], c3[2];
434
double a1, a2, a3, d1, d2, d3;
449
/* convert to log space */
450
a1 = atan2(c1[1], c1[0]);
451
a2 = atan2(c2[1], c2[0]);
452
d1 = 0.5 * log(c1[0] * c1[0] + c1[1] * c1[1]);
453
d2 = 0.5 * log(c2[0] * c2[0] + c2[1] * c2[1]);
455
/* interpolate linearly */
456
interpolate_angle(t, s, &a1, &a2, &a3, tie, cross);
457
d3 = s * d1 + t * d2;
461
c3[0] = cos(a3) * d3;
462
c3[1] = sin(a3) * d3;
474
void interpolate_matrix(t, m1, m2, m3)
475
double m1[3][2], m2[3][2], m3[3][2];
480
double r1[2][2], r2[2][2], r3[2][2];
481
double v1[2][2], v2[2][2], v3[2][2];
482
diagonalize_matrix(m1, r1, v1);
483
diagonalize_matrix(m2, r2, v2);
485
/* handle the evectors */
486
interpolate_complex(t, s, v1 + 0, v2 + 0, v3 + 0, 0, 0, 0);
487
interpolate_complex(t, s, v1 + 1, v2 + 1, v3 + 1, 0, 0, 1);
489
/* handle the evalues */
490
interpolate_complex(t, s, r1 + 0, r2 + 0, r3 + 0, 0, 0, 0);
491
interpolate_complex(t, s, r1 + 1, r2 + 1, r3 + 1, 1, 1, 0);
493
undiagonalize_matrix(r3, v3, m3);
496
interpolate_complex(t, s, m1 + 0, m2 + 0, m3 + 0, 0, 0, 0);
497
interpolate_complex(t, s, m1 + 1, m2 + 1, m3 + 1, 1, 1, 0);
499
/* handle the translation part of the xform linearly */
500
m3[2][0] = s * m1[2][0] + t * m2[2][0];
501
m3[2][1] = s * m1[2][1] + t * m2[2][1];
504
#define INTERP(x) result->x = c0 * cps[i1].x + c1 * cps[i2].x
507
* create a control point that interpolates between the control points
508
* passed in CPS. for now just do linear. in the future, add control
509
* point types and other things to the cps. CPS must be sorted by time.
511
void interpolate(cps, ncps, time, result)
515
control_point *result;
524
if (cps[0].time >= time) {
527
} else if (cps[ncps - 1].time <= time) {
532
while (cps[i1].time < time)
536
if (time - cps[i1].time > -1e-7 &&
537
time - cps[i1].time < 1e-7) {
543
c0 = (cps[i2].time - time) / (cps[i2].time - cps[i1].time);
548
if (cps[i1].cmap_inter) {
549
for (i = 0; i < 256; i++) {
550
double spread = 0.15;
551
double d0, d1, e0, e1, c = 2 * G_PI * i / 256.0;
552
c = cos(c * cps[i1].cmap_inter) + 4.0 * c1 - 2.0;
553
if (c > spread) c = spread;
554
if (c < -spread) c = -spread;
555
d1 = (c + spread) * 0.5 / spread;
557
e0 = (d0 < 0.5) ? (d0 * 2) : (d1 * 2);
559
for (j = 0; j < 3; j++) {
560
result->cmap[i][j] = (d0 * cps[i1].cmap[i][j] +
561
d1 * cps[i2].cmap[i][j]);
562
#define bright_peak 2.0
565
result->cmap[i][j] *= 1.0 + bright_peak * d0;
567
result->cmap[i][j] *= 1.0 + bright_peak * d1;
569
result->cmap[i][j] = (e1 * result->cmap[i][j] +
575
for (i = 0; i < 256; i++) {
577
rgb2hsv(cps[i1].cmap[i], s);
578
rgb2hsv(cps[i2].cmap[i], t);
579
for (j = 0; j < 3; j++)
580
t[j] = c0 * s[j] + c1 * t[j];
581
hsv2rgb(t, result->cmap[i]);
585
result->cmap_index = -1;
591
INTERP(spatial_oversample);
594
INTERP(pixels_per_unit);
595
INTERP(spatial_filter_radius);
596
INTERP(sample_density);
600
for (i = 0; i < 2; i++)
601
for (j = 0; j < 2; j++) {
603
INTERP(wiggle[i][j]);
606
for (i = 0; i < NXFORMS; i++) {
608
INTERP(xform[i].density);
609
if (result->xform[i].density > 0)
610
result->xform[i].density = 1.0;
611
INTERP(xform[i].color);
612
for (j = 0; j < NVARS; j++)
613
INTERP(xform[i].var[j]);
615
for (j = 0; j < NVARS; j++)
616
t += result->xform[i].var[j];
618
for (j = 0; j < NVARS; j++)
619
result->xform[i].var[j] *= t;
621
interpolate_matrix(c1, cps[i1].xform[i].c, cps[i2].xform[i].c,
625
double rh_time = time * 2*G_PI / (60.0 * 30.0);
627
/* apply pulse factor. */
629
for (j = 0; j < 2; j++)
630
r += result->pulse[j][0] * sin(result->pulse[j][1] * rh_time);
631
for (j = 0; j < 3; j++) {
632
result->xform[i].c[j][0] *= r;
633
result->xform[i].c[j][1] *= r;
636
/* apply wiggle factor */
638
for (j = 0; j < 2; j++) {
639
double tt = result->wiggle[j][1] * rh_time;
640
double m = result->wiggle[j][0];
641
result->xform[i].c[0][0] += m * cos(tt);
642
result->xform[i].c[1][0] += m * -sin(tt);
643
result->xform[i].c[0][1] += m * sin(tt);
644
result->xform[i].c[1][1] += m * cos(tt);
653
* split a string passed in ss into tokens on whitespace.
654
* # comments to end of line. ; terminates the record
656
void tokenize(ss, argv, argc)
662
int i = 0, state = 0;
670
else if (!g_ascii_isspace(c)) {
676
if (g_ascii_isspace(c)) {
691
int compare_xforms(a, b)
698
aa[0][0] = a->c[0][0];
699
aa[0][1] = a->c[0][1];
700
aa[1][0] = a->c[1][0];
701
aa[1][1] = a->c[1][1];
702
bb[0][0] = b->c[0][0];
703
bb[0][1] = b->c[0][1];
704
bb[1][0] = b->c[1][0];
705
bb[1][1] = b->c[1][1];
708
if (ad < bd) return -1;
709
if (ad > bd) return 1;
714
#define streql(x,y) (!strcmp(x,y))
717
* given a pointer to a string SS, fill fields of a control point CP.
718
* return a pointer to the first unused char in SS. totally barfucious,
719
* must integrate with tcl soon...
722
void parse_control_point(ss, cp)
728
int set_cm = 0, set_image_size = 0, set_nbatches = 0, set_white_level = 0, set_cmap_inter = 0;
729
int set_spatial_oversample = 0;
730
double *slot = NULL, xf, cm, t, nbatches, white_level, spatial_oversample, cmap_inter;
731
double image_size[2];
733
for (i = 0; i < NXFORMS; i++) {
734
cp->xform[i].density = 0.0;
735
cp->xform[i].color = (i == 0);
736
cp->xform[i].var[0] = 1.0;
737
for (j = 1; j < NVARS; j++)
738
cp->xform[i].var[j] = 0.0;
739
cp->xform[i].c[0][0] = 1.0;
740
cp->xform[i].c[0][1] = 0.0;
741
cp->xform[i].c[1][0] = 0.0;
742
cp->xform[i].c[1][1] = 1.0;
743
cp->xform[i].c[2][0] = 0.0;
744
cp->xform[i].c[2][1] = 0.0;
746
for (j = 0; j < 2; j++) {
747
cp->pulse[j][0] = 0.0;
748
cp->pulse[j][1] = 60.0;
749
cp->wiggle[j][0] = 0.0;
750
cp->wiggle[j][1] = 60.0;
753
tokenize(ss, argv, &argc);
754
for (i = 0; i < argc; i++) {
755
if (streql("xform", argv[i]))
757
else if (streql("time", argv[i]))
759
else if (streql("brightness", argv[i]))
760
slot = &cp->brightness;
761
else if (streql("contrast", argv[i]))
762
slot = &cp->contrast;
763
else if (streql("gamma", argv[i]))
765
else if (streql("zoom", argv[i]))
767
else if (streql("image_size", argv[i])) {
770
} else if (streql("center", argv[i]))
772
else if (streql("pulse", argv[i]))
773
slot = (double *) cp->pulse;
774
else if (streql("wiggle", argv[i]))
775
slot = (double *) cp->wiggle;
776
else if (streql("pixels_per_unit", argv[i]))
777
slot = &cp->pixels_per_unit;
778
else if (streql("spatial_filter_radius", argv[i]))
779
slot = &cp->spatial_filter_radius;
780
else if (streql("sample_density", argv[i]))
781
slot = &cp->sample_density;
782
else if (streql("nbatches", argv[i])) {
785
} else if (streql("white_level", argv[i])) {
788
} else if (streql("spatial_oversample", argv[i])) {
789
slot = &spatial_oversample;
790
set_spatial_oversample = 1;
791
} else if (streql("cmap", argv[i])) {
794
} else if (streql("density", argv[i]))
795
slot = &cp->xform[(int)xf].density;
796
else if (streql("color", argv[i]))
797
slot = &cp->xform[(int)xf].color;
798
else if (streql("coefs", argv[i])) {
799
slot = cp->xform[(int)xf].c[0];
800
cp->xform[(int)xf].density = 1.0;
801
} else if (streql("var", argv[i]))
802
slot = cp->xform[(int)xf].var;
803
else if (streql("cmap_inter", argv[i])) {
807
*slot++ = atof(argv[i]);
810
cp->cmap_index = (int) cm;
811
get_cmap(cp->cmap_index, cp->cmap, 256);
813
if (set_image_size) {
814
cp->width = (int) image_size[0];
815
cp->height = (int) image_size[1];
818
cp->cmap_inter = (int) cmap_inter;
820
cp->nbatches = (int) nbatches;
821
if (set_spatial_oversample)
822
cp->spatial_oversample = (int) spatial_oversample;
824
cp->white_level = (int) white_level;
825
for (i = 0; i < NXFORMS; i++) {
827
for (j = 0; j < NVARS; j++)
828
t += cp->xform[i].var[j];
830
for (j = 0; j < NVARS; j++)
831
cp->xform[i].var[j] *= t;
833
qsort((char *) cp->xform, NXFORMS, sizeof(xform), compare_xforms);
836
void print_control_point(f, cp, quote)
841
char *q = quote ? "# " : "";
842
fprintf(f, "%stime %g\n", q, cp->time);
843
if (-1 != cp->cmap_index)
844
fprintf(f, "%scmap %d\n", q, cp->cmap_index);
845
fprintf(f, "%simage_size %d %d center %g %g pixels_per_unit %g\n",
846
q, cp->width, cp->height, cp->center[0], cp->center[1],
847
cp->pixels_per_unit);
848
fprintf(f, "%sspatial_oversample %d spatial_filter_radius %g",
849
q, cp->spatial_oversample, cp->spatial_filter_radius);
850
fprintf(f, " sample_density %g\n", cp->sample_density);
851
fprintf(f, "%snbatches %d white_level %d\n",
852
q, cp->nbatches, cp->white_level);
853
fprintf(f, "%sbrightness %g gamma %g cmap_inter %d\n",
854
q, cp->brightness, cp->gamma, cp->cmap_inter);
856
for (i = 0; i < NXFORMS; i++)
857
if (cp->xform[i].density > 0.0) {
858
fprintf(f, "%sxform %d density %g color %g\n",
859
q, i, cp->xform[i].density, cp->xform[i].color);
860
fprintf(f, "%svar", q);
861
for (j = 0; j < NVARS; j++)
862
fprintf(f, " %g", cp->xform[i].var[j]);
863
fprintf(f, "\n%scoefs", q);
864
for (j = 0; j < 3; j++)
865
fprintf(f, " %g %g", cp->xform[i].c[j][0], cp->xform[i].c[j][1]);
868
fprintf(f, "%s;\n", q);
871
/* returns a uniform variable from 0 to 1 */
872
double random_uniform01() {
873
return g_random_double ();
876
double random_uniform11() {
877
return g_random_double_range (-1, 1);
880
/* returns a mean 0 variance 1 random variable
881
see numerical recipies p 217 */
882
double random_gaussian() {
885
double fac, r, v1, v2;
889
v1 = random_uniform11();
890
v2 = random_uniform11();
891
r = v1 * v1 + v2 * v2;
892
} while (r >= 1.0 || r == 0.0);
893
fac = sqrt(-2.0 * log(r)/r);
903
copy_variation(control_point *cp0, control_point *cp1) {
905
for (i = 0; i < NXFORMS; i++) {
906
for (j = 0; j < NVARS; j++)
907
cp0->xform[i].var[j] =
908
cp1->xform[i].var[j];
914
#define random_distrib(v) ((v)[g_random_int_range (0, vlen(v))])
916
void random_control_point(cp, ivar)
921
static int xform_distrib[] = {
926
static int var_distrib[] = {
935
static int mixed_var_distrib[] = {
943
get_cmap(cmap_random, cp->cmap, 256);
945
nxforms = random_distrib(xform_distrib);
947
random_distrib(var_distrib) :
949
for (i = 0; i < nxforms; i++) {
951
cp->xform[i].density = 1.0 / nxforms;
952
cp->xform[i].color = i == 0;
953
for (j = 0; j < 3; j++)
954
for (k = 0; k < 2; k++)
955
cp->xform[i].c[j][k] = random_uniform11();
956
for (j = 0; j < NVARS; j++)
957
cp->xform[i].var[j] = 0.0;
959
cp->xform[i].var[var] = 1.0;
961
cp->xform[i].var[random_distrib(mixed_var_distrib)] = 1.0;
964
for (; i < NXFORMS; i++)
965
cp->xform[i].density = 0.0;
969
* find a 2d bounding box that does not enclose eps of the fractal density
970
* in each compass direction. works by binary search.
971
* this is stupid, it shouldjust use the find nth smallest algorithm.
973
void estimate_bounding_box(cp, eps, bmin, bmax)
979
int i, j, batch = (eps == 0.0) ? 10000 : 10.0/eps;
980
int low_target = batch * eps;
981
int high_target = batch - low_target;
982
point min, max, delta;
983
point *points = (point *) malloc(sizeof(point) * batch);
984
iterate(cp, batch, 20, points);
986
min[0] = min[1] = 1e10;
987
max[0] = max[1] = -1e10;
989
for (i = 0; i < batch; i++) {
990
if (points[i][0] < min[0]) min[0] = points[i][0];
991
if (points[i][1] < min[1]) min[1] = points[i][1];
992
if (points[i][0] > max[0]) max[0] = points[i][0];
993
if (points[i][1] > max[1]) max[1] = points[i][1];
996
if (low_target == 0) {
1004
delta[0] = (max[0] - min[0]) * 0.25;
1005
delta[1] = (max[1] - min[1]) * 0.25;
1007
bmax[0] = bmin[0] = min[0] + 2.0 * delta[0];
1008
bmax[1] = bmin[1] = min[1] + 2.0 * delta[1];
1010
for (i = 0; i < 14; i++) {
1013
for (j = 0; j < batch; j++) {
1014
if (points[j][0] < bmin[0]) n++;
1015
if (points[j][0] > bmax[0]) s++;
1016
if (points[j][1] < bmin[1]) w++;
1017
if (points[j][1] > bmax[1]) e++;
1019
bmin[0] += (n < low_target) ? delta[0] : -delta[0];
1020
bmax[0] += (s < high_target) ? delta[0] : -delta[0];
1021
bmin[1] += (w < low_target) ? delta[1] : -delta[1];
1022
bmax[1] += (e < high_target) ? delta[1] : -delta[1];
1023
delta[0] = delta[0] / 2.0;
1024
delta[1] = delta[1] / 2.0;
1026
fprintf(stderr, "%g %g %g %g\n", bmin[0], bmin[1], bmax[0], bmax[1]);
1030
fprintf(stderr, "%g %g %g %g\n", min[0], min[1], max[0], max[1]);
1034
/* use hill climberer to find smooth ordering of control points
1037
void sort_control_points(cps, ncps, metric)
1042
int niter = ncps * 1000;
1045
for (i = 0; i < niter; i++) {
1046
/* consider switching points with indexes n and m */
1047
n = g_random_int_range (0, ncps);
1048
m = g_random_int_range (0, ncps);
1050
same = (metric(cps + n, cps + (n - 1) % ncps) +
1051
metric(cps + n, cps + (n + 1) % ncps) +
1052
metric(cps + m, cps + (m - 1) % ncps) +
1053
metric(cps + m, cps + (m + 1) % ncps));
1055
swap = (metric(cps + n, cps + (m - 1) % ncps) +
1056
metric(cps + n, cps + (m + 1) % ncps) +
1057
metric(cps + m, cps + (n - 1) % ncps) +
1058
metric(cps + m, cps + (n + 1) % ncps));
1069
/* this has serious flaws in it */
1071
double standard_metric(cp1, cp2)
1072
control_point *cp1, *cp2;
1078
for (i = 0; i < NXFORMS; i++) {
1079
double var_dist = 0.0;
1080
double coef_dist = 0.0;
1081
for (j = 0; j < NVARS; j++) {
1082
t = cp1->xform[i].var[j] - cp2->xform[i].var[j];
1085
for (j = 0; j < 3; j++)
1086
for (k = 0; k < 2; k++) {
1087
t = cp1->xform[i].c[j][k] - cp2->xform[i].c[j][k];
1091
/* weight them equally for now. */
1092
dist += var_dist + coef_dist;
1106
diagonalize_matrix(m, r, v);
1107
fprintf(f, "entries = % 10f % 10f % 10f % 10f\n",
1108
m[0][0], m[0][1], m[1][0], m[1][1]);
1109
fprintf(f, "evalues = % 10f % 10f % 10f % 10f\n",
1110
r[0][0], r[0][1], r[1][0], r[1][1]);
1111
fprintf(f, "evectors = % 10f % 10f % 10f % 10f\n",
1112
v[0][0], v[0][1], v[1][0], v[1][1]);
1113
a = (v[0][0] * v[1][0] + v[0][1] * v[1][1]) /
1114
sqrt((v[0][0] * v[0][0] + v[0][1] * v[0][1]) *
1115
(v[1][0] * v[1][0] + v[1][1] * v[1][1]));
1116
fprintf(f, "theta = %g det = %g\n", a,
1117
m[0][0] * m[1][1] - m[0][1] * m[1][0]);
1125
double m1[3][2] = {-0.633344, -0.269064, 0.0676171, 0.590923, 0, 0};
1126
double m2[3][2] = {-0.844863, 0.0270297, -0.905294, 0.413218, 0, 0};
1130
double m1[3][2] = {-0.347001, -0.15219, 0.927161, 0.908305, 0, 0};
1131
double m2[3][2] = {-0.577884, 0.653803, 0.664982, -0.734136, 0, 0};
1135
double m1[3][2] = {1, 0, 0, 1, 0, 0};
1136
double m2[3][2] = {0, -1, 1, 0, 0, 0};
1140
double m1[3][2] = {1, 0, 0, 1, 0, 0};
1141
double m2[3][2] = {-1, 0, 0, -1, 0, 0};
1148
for (t = 0.0; t <= 1.0; t += 1.0/15.0) {
1150
fprintf(stderr, "%g--\n", t);
1151
interpolate_matrix(t, m1, m2, m3);
1152
/* stat_matrix(stderr, m3); */
1153
x = (i % 4) * 100 + 100;
1154
y = (i / 4) * 100 + 100;
1156
printf("%d %d %d %d %d arc ", x, y, 30, 0, 360);
1157
printf("%d %d moveto ", x, y);
1158
printf("%g %g rlineto ", m3[0][0] * 30, m3[0][1] * 30);
1159
printf("%d %d moveto ", x, y);
1160
printf("%g %g rlineto ", m3[1][0] * 30, m3[1][1] * 30);
1161
printf("stroke \n");
1163
printf("%g %g %d %d %d arc ", x + m3[0][0] * 30, y + m3[0][1] * 30, 3, 0, 360);
1164
printf("stroke \n");