1 /* output-fig.c - output autotrace splines in FIG 3.2 format
2
3 Copyright (C) 1999, 2000, 2001 Ian MacPhedran
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public License
7 as published by the Free Software Foundation; either version 2.1 of
8 the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18 USA. */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif /* Def: HAVE_CONFIG_H */
23
24 #include "output-fig.h"
25 #include "xstd.h"
26 #include "color.h"
27 #include "spline.h"
28
29 /* use FIG_X and FIG_Y to convert from local units (pixels) to FIG ones */
30 /* assume 1 pixel is equal to 1/80 inches (old FIG unit) */
31 /* Offset by 300 units (1/4 inch) */
32
33 #define FIG_X(x) (int)((x * 15.0) + 300.0)
34 #define FIG_Y(y) (int)(((ury - y) * 15.0) + 300.0)
35
36 /* the basic colours */
37 #define FIG_BLACK 0
38 #define FIG_BLUE 1
39 #define FIG_GREEN 2
40 #define FIG_CYAN 3
41 #define FIG_RED 4
42 #define FIG_MAGENTA 5
43 #define FIG_YELLOW 6
44 #define FIG_WHITE 7
45
46 static at_real bezpnt(at_real, at_real, at_real, at_real, at_real);
47 static void out_fig_splines(FILE *, spline_list_array_type, int, int, int, int,
48 at_exception_type *);
49 static int get_fig_colour(color_type, at_exception_type *);
50 static void fig_col_init(void);
51
52 /* colour information */
53 #define fig_col_hash(col_typ) ( ( (col_typ).r & 255 ) + ( (col_typ).g & 161 ) + ( (col_typ).b & 127 ) )
54
55 static struct {
56 unsigned int colour;
57 unsigned int alternate;
58 } fig_hash[544];
59
60 static struct {
61 unsigned char r,g,b;
62 int alternate;
63 } fig_colour_map[544];
64
65 static int LAST_FIG_COLOUR=32;
66 #define MAX_FIG_COLOUR 543
67
68 /* Bounding Box data and routines */
69 static float glob_min_x, glob_max_x, glob_min_y, glob_max_y;
70 static float loc_min_x, loc_max_x, loc_min_y, loc_max_y;
71 static int glo_bbox_flag=0,loc_bbox_flag=0,fig_depth;
72
fig_new_depth()73 static void fig_new_depth()
74 {
75 if (glo_bbox_flag == 0) {
76 glob_max_y = loc_max_y ; glob_min_y = loc_min_y ;
77 glob_max_y = loc_max_y ; glob_min_y = loc_min_y ;
78 glob_max_x = loc_max_x ; glob_min_x = loc_min_x ;
79 glo_bbox_flag = 1;
80 } else {
81 if ((loc_max_y <= glob_min_y) ||
82 (loc_min_y >= glob_max_y) ||
83 (loc_max_x <= glob_min_x) ||
84 (loc_min_x >= glob_max_x)) {
85 /* outside global bounds, increase global box */
86 if (loc_max_y > glob_max_y) glob_max_y = loc_max_y ;
87 if (loc_min_y < glob_min_y) glob_min_y = loc_min_y ;
88 if (loc_max_x > glob_max_x) glob_max_x = loc_max_x ;
89 if (loc_min_x < glob_min_x) glob_min_x = loc_min_x ;
90 } else {
91 /* inside global bounds, decrease depth and create new bounds */
92 glob_max_y = loc_max_y ; glob_min_y = loc_min_y ;
93 glob_max_x = loc_max_x ; glob_min_x = loc_min_x ;
94 if (fig_depth) fig_depth--; // don't let it get < 0
95 }
96 }
97 loc_bbox_flag = 0;
98 }
99
fig_addtobbox(float x,float y)100 static void fig_addtobbox(float x, float y)
101 {
102 if (loc_bbox_flag == 0) {
103 loc_max_y = y ; loc_min_y = y ;
104 loc_max_x = x ; loc_min_x = x ;
105 loc_bbox_flag = 1;
106 } else {
107 if (loc_max_y < y) loc_max_y = y ;
108 if (loc_min_y > y) loc_min_y = y ;
109 if (loc_max_x < x) loc_max_x = x ;
110 if (loc_min_x > x) loc_min_x = x ;
111 }
112 }
113
114
115 /* Convert Bezier Spline */
116
bezpnt(at_real t,at_real z1,at_real z2,at_real z3,at_real z4)117 static at_real bezpnt(at_real t, at_real z1, at_real z2, at_real z3, at_real z4)
118 {
119 at_real temp, t1;
120 /* Determine ordinate on Bezier curve at length "t" on curve */
121 if (t < (at_real) 0.0) { t = (at_real) 0.0; }
122 if (t > (at_real) 1.0) { t = (at_real) 1.0; }
123 t1 = ((at_real) 1.0 - t);
124 temp = t1*t1*t1*z1 + (at_real)3.0*t*t1*t1*z2 + (at_real)3.0*t*t*t1*z3 + t*t*t*z4;
125 return(temp);
126 }
127
out_fig_splines(FILE * file,spline_list_array_type shape,int llx,int lly,int urx,int ury,at_exception_type * exp)128 static void out_fig_splines(FILE * file, spline_list_array_type shape,
129 int llx, int lly, int urx, int ury,
130 at_exception_type * exp)
131 {
132 unsigned this_list;
133 /* int fig_colour, fig_depth, i; */
134 int fig_colour, fig_fill, fig_width, fig_subt, fig_spline_close, i;
135 int *spline_colours;
136
137 /*
138 add an array of colours for splines (one for each group)
139 create palette hash
140 */
141
142 /* Need to create hash table for colours */
143 XMALLOC(spline_colours, (sizeof(int)*SPLINE_LIST_ARRAY_LENGTH(shape)));
144
145 /* Preload the big 8 */
146 fig_col_init();
147
148 /* Load the colours from the splines */
149 for (this_list = 0; this_list < SPLINE_LIST_ARRAY_LENGTH (shape);
150 this_list++)
151 {
152 spline_list_type list = SPLINE_LIST_ARRAY_ELT (shape, this_list);
153 color_type curr_color = (list.clockwise && shape.background_color != NULL) ? *(shape.background_color) : list.color;
154 spline_colours[this_list] = get_fig_colour(curr_color, exp);
155 }
156 /* Output colours */
157 if (LAST_FIG_COLOUR > 32) {
158 for (i=32; i<LAST_FIG_COLOUR; i++) {
159 fprintf(file,"0 %d #%.2x%.2x%.2x\n",i,fig_colour_map[i].r,
160 fig_colour_map[i].g,fig_colour_map[i].b);
161 }
162 }
163 /* Each "spline list" in the array appears to be a group of splines */
164 fig_depth = SPLINE_LIST_ARRAY_LENGTH (shape) + 20;
165 if (fig_depth > 999) { fig_depth = 999; }
166
167 for (this_list = 0; this_list < SPLINE_LIST_ARRAY_LENGTH (shape);
168 this_list++)
169 {
170 unsigned this_spline;
171 spline_list_type list = SPLINE_LIST_ARRAY_ELT (shape, this_list);
172
173 /* store the spline points in two arrays, control weights in another */
174 int *pointx, *pointy;
175 at_real *contrl;
176 int pointcount=0,is_spline=0,j;
177 int maxlength=SPLINE_LIST_LENGTH (list) * 5 + 1;
178
179 XMALLOC (pointx, maxlength * sizeof (int));
180 XMALLOC (pointy, maxlength * sizeof (int));
181 XMALLOC (contrl, maxlength * sizeof (at_real));
182
183 if (list.clockwise) { fig_colour = FIG_WHITE; }
184 else { fig_colour = spline_colours[this_list]; }
185
186 fig_spline_close = 5;
187
188 for (this_spline = 0; this_spline < SPLINE_LIST_LENGTH (list);
189 this_spline++)
190 {
191 spline_type s = SPLINE_LIST_ELT (list, this_spline);
192
193 if (pointcount == 0) {
194 pointx[pointcount] = FIG_X(START_POINT(s).x);
195 pointy[pointcount] = FIG_Y(START_POINT(s).y);
196 contrl[pointcount] = (at_real) 0.0;
197 fig_addtobbox(START_POINT(s).x,START_POINT(s).y);
198 pointcount++;
199 }
200 /* Apparently START_POINT for one spline section is same as END_POINT
201 for previous section - should at_really test for this */
202 if (SPLINE_DEGREE(s) == LINEARTYPE)
203 {
204 pointx[pointcount] = FIG_X(END_POINT(s).x);
205 pointy[pointcount] = FIG_Y(END_POINT(s).y);
206 contrl[pointcount] = (at_real) 0.0;
207 fig_addtobbox(START_POINT(s).x,START_POINT(s).y);
208 pointcount++;
209 }
210 else /* Assume Bezier like spline */
211 {
212 /* Convert approximated bezier to interpolated X Spline */
213 at_real temp;
214 for (temp = (at_real) 0.2; temp < (at_real) 0.9; temp += (at_real) 0.2) {
215 pointx[pointcount] =
216 FIG_X(bezpnt(temp,START_POINT(s).x,CONTROL1(s).x,
217 CONTROL2(s).x,END_POINT(s).x));
218 pointy[pointcount] =
219 FIG_Y(bezpnt(temp,START_POINT(s).y,CONTROL1(s).y,
220 CONTROL2(s).y,END_POINT(s).y));
221 contrl[pointcount] = (at_real) -1.0;
222 pointcount++;
223 }
224 pointx[pointcount] = FIG_X(END_POINT(s).x);
225 pointy[pointcount] = FIG_Y(END_POINT(s).y);
226 contrl[pointcount] = (at_real) 0.0;
227 fig_addtobbox(START_POINT(s).x,START_POINT(s).y);
228 fig_addtobbox(CONTROL1(s).x,CONTROL1(s).y);
229 fig_addtobbox(CONTROL2(s).x,CONTROL2(s).y);
230 fig_addtobbox(END_POINT(s).x,END_POINT(s).y);
231 pointcount++;
232 is_spline=1;
233 }
234 }
235 if (shape.centerline) {
236 fig_fill = -1; fig_width = 1; fig_spline_close = 4;
237 } else {
238 /* Use zero width lines - unit width is too thick */
239 fig_fill = 20; fig_width = 0; fig_spline_close = 5;
240 }
241 if (is_spline != 0) {
242 fig_new_depth();
243 fprintf(file,"3 %d 0 %d %d %d %d 0 %d 0.00 0 0 0 %d\n",
244 fig_spline_close,
245 fig_width, fig_colour, fig_colour, fig_depth, fig_fill, pointcount);
246 /* Print out points */
247 j = 0;
248 for (i=0; i<pointcount; i++) {
249 j++; if (j == 1) {fprintf(file,"\t");}
250 fprintf(file,"%d %d ",pointx[i],pointy[i]);
251 if (j == 8) {fprintf(file,"\n"); j=0;}
252 }
253 if (j != 0) {fprintf(file,"\n");}
254 j = 0;
255 /* Print out control weights */
256 for (i=0; i<pointcount; i++) {
257 j++; if (j == 1) {fprintf(file,"\t");}
258 fprintf(file,"%f ",contrl[i]);
259 if (j == 8) {fprintf(file,"\n"); j=0;}
260 }
261 if (j != 0) {fprintf(file,"\n");}
262 } else {
263 /* Polygons can be handled better as polygons */
264 fig_subt = 3;
265 if (pointcount == 2) {
266 if ((pointx[0] == pointx[1]) && (pointy[0] == pointy[1])) {
267 /* Point */
268 fig_new_depth();
269 fprintf(file,"2 1 0 1 %d %d %d 0 -1 0.000 0 0 -1 0 0 1\n",
270 fig_colour, fig_colour, fig_depth);
271 fprintf(file,"\t%d %d\n",pointx[0],pointy[0]);
272 } else {
273 /* Line segment? */
274 fig_new_depth();
275 fprintf(file,"2 1 0 1 %d %d %d 0 -1 0.000 0 0 -1 0 0 2\n",
276 fig_colour, fig_colour, fig_depth);
277 fprintf(file,"\t%d %d %d %d\n",pointx[0],pointy[0],
278 pointx[1],pointy[1]);
279 }
280 } else {
281 if ((pointcount == 3) && (pointx[0] == pointx[2])
282 && (pointy[0] == pointy[2])){
283 /* Line segment? */
284 fig_new_depth();
285 fprintf(file,"2 1 0 1 %d %d %d 0 -1 0.000 0 0 -1 0 0 2\n",
286 fig_colour, fig_colour, fig_depth);
287 fprintf(file,"\t%d %d %d %d\n",pointx[0],pointy[0],
288 pointx[1],pointy[1]);
289 } else {
290 if ((pointx[0] != pointx[pointcount-1]) ||
291 (pointy[0] != pointy[pointcount-1])) {
292 if (shape.centerline) {
293 fig_subt = 1;
294 } else {
295 /* Need to have last point same as first for polygon */
296 pointx[pointcount] = pointx[0];
297 pointy[pointcount] = pointy[0];
298 pointcount++;
299 }
300 }
301 fig_new_depth();
302 fprintf(file,"2 %d 0 %d %d %d %d 0 %d 0.00 0 0 0 0 0 %d\n",
303 fig_subt, fig_width, fig_colour, fig_colour, fig_depth, fig_fill, pointcount);
304 /* Print out points */
305 j = 0;
306 for (i=0; i<pointcount; i++) {
307 j++; if (j == 1) {fprintf(file,"\t");}
308 fprintf(file,"%d %d ",pointx[i],pointy[i]);
309 if (j == 8) {fprintf(file,"\n"); j=0;}
310 }
311 if (j != 0) {fprintf(file,"\n");}
312 }
313 }
314 }
315 /* fig_depth--; */
316 if (fig_depth < 0) { fig_depth=0; }
317 free (pointx);
318 free (pointy);
319 free (contrl);
320 }
321 free(spline_colours);
322 return;
323 }
324
output_fig_writer(FILE * file,at_string name,int llx,int lly,int urx,int ury,at_output_opts_type * opts,spline_list_array_type shape,at_msg_func msg_func,at_address msg_data)325 int output_fig_writer(FILE* file, at_string name,
326 int llx, int lly, int urx, int ury,
327 at_output_opts_type * opts,
328 spline_list_array_type shape,
329 at_msg_func msg_func,
330 at_address msg_data)
331 {
332 at_exception_type exp = at_exception_new(msg_func, msg_data);
333 /* Output header */
334 fprintf(file,"#FIG 3.2\nLandscape\nCenter\nInches\nLetter\n100.00\nSingle\n-2\n1200 2\n");
335
336 /* Output data */
337 out_fig_splines(file, shape, llx, lly, urx, ury, &exp);
338 return 0;
339 }
340
341 /*
342 Create hash number
343 At hash number -> set fig number
344 If fig number already used, go to next fig number (alternate)
345 if alternate is 0, set next unused fig number
346 */
347
fig_col_init(void)348 static void fig_col_init(void)
349 {
350 int i;
351
352 for (i=0;i<544;i++) { fig_hash[i].colour = 0; fig_colour_map[i].alternate = 0; }
353
354 /* populate the first 8 primary colours */
355 /* Black */
356 fig_hash[0].colour = FIG_BLACK;
357 fig_colour_map[FIG_BLACK].r = 0;
358 fig_colour_map[FIG_BLACK].g = 0;
359 fig_colour_map[FIG_BLACK].b = 0;
360 /* White */
361 fig_hash[543].colour = FIG_WHITE;
362 fig_colour_map[FIG_WHITE].r = 255;
363 fig_colour_map[FIG_WHITE].g = 255;
364 fig_colour_map[FIG_WHITE].b = 255;
365 /* Red */
366 fig_hash[255].colour = FIG_RED;
367 fig_colour_map[FIG_RED].r = 255;
368 fig_colour_map[FIG_RED].g = 0;
369 fig_colour_map[FIG_RED].b = 0;
370 /* Green */
371 fig_hash[161].colour = FIG_GREEN;
372 fig_colour_map[FIG_GREEN].r = 0;
373 fig_colour_map[FIG_GREEN].g = 255;
374 fig_colour_map[FIG_GREEN].b = 0;
375 /* Blue */
376 fig_hash[127].colour = FIG_BLUE;
377 fig_colour_map[FIG_BLUE].r = 0;
378 fig_colour_map[FIG_BLUE].g = 0;
379 fig_colour_map[FIG_BLUE].b = 255;
380 /* Cyan */
381 fig_hash[198].colour = FIG_CYAN;
382 fig_colour_map[FIG_CYAN].r = 0;
383 fig_colour_map[FIG_CYAN].g = 255;
384 fig_colour_map[FIG_CYAN].b = 255;
385 /* Magenta */
386 fig_hash[382].colour = FIG_MAGENTA;
387 fig_colour_map[FIG_MAGENTA].r = 255;
388 fig_colour_map[FIG_MAGENTA].g = 0;
389 fig_colour_map[FIG_MAGENTA].b = 255;
390 /* Yellow */
391 fig_hash[416].colour = FIG_YELLOW;
392 fig_colour_map[FIG_YELLOW].r = 255;
393 fig_colour_map[FIG_YELLOW].g = 255;
394 fig_colour_map[FIG_YELLOW].b = 0;
395 }
396
397 /*
398 * Return the FIG colour index based on the RGB triplet.
399 * If unknown, create a new colour index and return that.
400 */
401
get_fig_colour(color_type this_colour,at_exception_type * exp)402 static int get_fig_colour(color_type this_colour, at_exception_type * exp)
403 {
404 int hash,i,this_ind;
405
406 hash = fig_col_hash(this_colour);
407
408 /* Special case: black _IS_ zero: */
409 if ((hash == 0) && (COLOR_EQUAL(fig_colour_map[0],this_colour)))
410 {return(0);}
411
412 if (fig_hash[hash].colour == 0) {
413 fig_hash[hash].colour = LAST_FIG_COLOUR;
414 fig_colour_map[LAST_FIG_COLOUR].r = this_colour.r;
415 fig_colour_map[LAST_FIG_COLOUR].g = this_colour.g;
416 fig_colour_map[LAST_FIG_COLOUR].b = this_colour.b;
417 LAST_FIG_COLOUR++;
418 if (LAST_FIG_COLOUR >= MAX_FIG_COLOUR) {
419 LOG1("Output-Fig: too many colours: %d", LAST_FIG_COLOUR);
420 at_exception_fatal(exp, "Output-Fig: too many colours");
421 return 0;
422 }
423 return(fig_hash[hash].colour);
424 } else {
425 i=0;
426 this_ind = fig_hash[hash].colour;
427 figcolloop:
428 /* If colour match return current colour */
429 if (COLOR_EQUAL(fig_colour_map[this_ind],this_colour)) {
430 return(this_ind);
431 }
432 /* If next colour zero - set it, return */
433 if (fig_colour_map[this_ind].alternate == 0) {
434 fig_colour_map[this_ind].alternate = LAST_FIG_COLOUR;
435 fig_colour_map[LAST_FIG_COLOUR].r = this_colour.r;
436 fig_colour_map[LAST_FIG_COLOUR].g = this_colour.g;
437 fig_colour_map[LAST_FIG_COLOUR].b = this_colour.b;
438 LAST_FIG_COLOUR++;
439 if (LAST_FIG_COLOUR >= MAX_FIG_COLOUR) {
440 LOG1("Output-Fig: too many colours: %d", LAST_FIG_COLOUR);
441 at_exception_fatal(exp, "Output-Fig: too many colours");
442 return 0;
443 }
444 return(fig_colour_map[this_ind].alternate);
445 }
446 /* Else get next colour */
447 this_ind = fig_colour_map[this_ind].alternate;
448 /* Sanity check ... if colour too big - abort */
449 if (i++ > MAX_FIG_COLOUR) {
450 LOG1("Output-Fig: too many colours (loop): %d", i);
451 at_exception_fatal(exp, "Output-Fig: too many colours (loop)");
452 return 0;
453 }
454 /* Else loop top */
455 goto figcolloop;
456 }
457 }
458
459