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