nice logo
[censuslogo] / src / logo.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <math.h>
5 #include <ctype.h>
6 #include <cgmath/cgmath.h>
7 #include "logo.h"
8
9 struct cpnode {
10         cgm_vec2 p;
11         struct cpnode *next;
12 };
13
14 #define CPLERP(res, a, b, t) \
15         do { \
16                 (res).x = (a).x + ((b).x - (a).x) * (t); \
17                 (res).y = (a).y + ((b).y - (a).y) * (t); \
18         } while(0)
19
20 static cgm_vec2 *cp;
21 static int numcp;
22
23 int init_logo(const char *fname)
24 {
25         FILE *fp;
26         char buf[256];
27         struct cpnode *cpn, *cplist = 0, *cptail = 0;
28         int idx;
29
30         if(!(fp = fopen(fname, "rb"))) {
31                 fprintf(stderr, "failed to load logo curve: %s\n", fname);
32                 return -1;
33         }
34         fgets(buf, sizeof buf, fp);
35         if(memcmp(buf, "GCURVES", 7) != 0) {
36                 fprintf(stderr, "invalid logo file: %s\n", fname);
37                 fclose(fp);
38                 return -1;
39         }
40
41         numcp = 0;
42         while(fgets(buf, sizeof buf, fp)) {
43                 char *ptr;
44                 cgm_vec2 v;
45
46                 ptr = buf;
47                 while(*ptr && isspace(*ptr)) ptr++;
48
49                 if(sscanf(ptr, "cp %f %f", &v.x, &v.y) == 2) {
50                         if(!(cpn = malloc(sizeof *cpn))) {
51                                 continue;
52                         }
53
54                         cpn->p.x = v.x;
55                         cpn->p.y = v.y;
56                         cpn->next = 0;
57
58                         if(cplist) {
59                                 cptail->next = cpn;
60                                 cptail = cpn;
61                         } else {
62                                 cplist = cptail = cpn;
63                         }
64                         numcp++;
65                 }
66         }
67         fclose(fp);
68
69         if(numcp < 4) {
70                 fprintf(stderr, "invalid logo file, found %d control points\n", numcp);
71                 return -1;
72         }
73
74         if(!(cp = malloc(numcp * sizeof *cp))) {
75                 fprintf(stderr, "failed to allocate curve of %d control points\n", numcp);
76                 while(cplist) {
77                         cpn = cplist;
78                         cplist = cplist->next;
79                         free(cpn);
80                 }
81                 return -1;
82         }
83
84         idx = 0;
85         while(cplist) {
86                 cpn = cplist;
87                 cplist = cplist->next;
88                 cp[idx++] = cpn->p;
89                 free(cpn);
90         }
91
92         return 0;
93 }
94
95 static void eval_seg(float *res, int a, int b, float t)
96 {
97         int prev, next;
98
99         if(numcp == 2) {
100                 res[0] = cp[a].x + (cp[b].x - cp[a].x) * t;
101                 res[1] = cp[a].y + (cp[b].y - cp[a].y) * t;
102                 return;
103         }
104
105         prev = a <= 0 ? a : a - 1;
106         next = b >= numcp - 1 ? b : b + 1;
107
108         res[0] = cgm_bspline(cp[prev].x, cp[a].x, cp[b].x, cp[next].x, t);
109         res[1] = cgm_bspline(cp[prev].y, cp[a].y, cp[b].y, cp[next].y, t);
110 }
111
112 void eval_logo(float *res, float t)
113 {
114         int idx0, idx1;
115         float t0, t1, dt;
116
117         if(!cp || numcp <= 0) {
118                 res[0] = res[1] = 0;
119                 return;
120         }
121         if(numcp == 1) {
122                 res[0] = cp[0].x;
123                 res[1] = cp[0].y;
124                 return;
125         }
126
127         if(t < 0.0f) t = 0.0f;
128         if(t > 1.0f) t = 1.0f;
129
130         idx0 = (int)floor(t * (numcp - 1));
131         if(idx0 > numcp - 2) idx0 = numcp - 2;
132         idx1 = idx0 + 1;
133
134         dt = 1.0f / (float)(numcp - 1);
135         t0 = (float)idx0 * dt;
136         t1 = (float)idx1 * dt;
137         t = (t - t0) / (t1 - t0);
138
139         eval_seg(res, idx0, idx1, t);
140 }