1 /****************************************************************************
2  * Copyright (C) 2008 by Matteo Franchin                                    *
3  *                                                                          *
4  * This file is part of Box.                                                *
5  *                                                                          *
6  *   Box is free software: you can redistribute it and/or modify it         *
7  *   under the terms of the GNU Lesser General Public License as published  *
8  *   by the Free Software Foundation, either version 3 of the License, or   *
9  *   (at your option) any later version.                                    *
10  *                                                                          *
11  *   Box is distributed in the hope that it will be useful,                 *
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of         *
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
14  *   GNU Lesser General Public License for more details.                    *
15  *                                                                          *
16  *   You should have received a copy of the GNU Lesser General Public       *
17  *   License along with Box.  If not, see <http://www.gnu.org/licenses/>.   *
18  ****************************************************************************/
19 
20 /* This window is a fake one, just to calculate the bounding box */
21 
22 /* #define DEBUG */
23 
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <assert.h>
27 
28 #include "error.h"
29 #include "types.h"
30 #include "graphic.h"
31 #include "fig.h"
32 #include "autoput.h"
33 #include "cmd.h"
34 #include "bb.h"
35 
36 /****************************************************************************
37  * C-implementation of the Bounding Box object (BoxGBBox).
38  * BoxGBBox corresponds to the BBox in the Box language.
39  */
40 
BoxGBBox_Init(BoxGBBox * bb)41 void BoxGBBox_Init(BoxGBBox *bb) {
42   bb->num = 0;
43   bb->min.x = bb->min.y = bb->max.x = bb->max.y = 0.0;
44 }
45 
BoxGBBox_Extend(BoxGBBox * bb,BoxPoint * p)46 void BoxGBBox_Extend(BoxGBBox *bb, BoxPoint *p) {
47   if (bb->num < 1) {
48     assert(bb->num == 0);
49     bb->min.x = bb->max.x = p->x;
50     bb->min.y = bb->max.y = p->y;
51 
52   } else {
53     if (p->x < bb->min.x) bb->min.x = p->x;
54     if (p->y < bb->min.y) bb->min.y = p->y;
55     if (p->x > bb->max.x) bb->max.x = p->x;
56     if (p->y > bb->max.y) bb->max.y = p->y;
57   }
58   ++bb->num;
59 }
60 
BoxGBBox_Fuse(BoxGBBox * dst,BoxGBBox * src)61 void BoxGBBox_Fuse(BoxGBBox *dst, BoxGBBox *src) {
62   if (src->num != 0) {
63     assert(src->num > 0);
64     BoxGBBox_Extend(dst, & src->min);
65     BoxGBBox_Extend(dst, & src->max);
66     dst->num += src->num - 2;
67   }
68 }
69 
BoxGBBox_Get_Volume(BoxGBBox * bb)70 BoxReal BoxGBBox_Get_Volume(BoxGBBox *bb) {
71   return (bb->max.x - bb->min.x)*(bb->max.y - bb->min.y);
72 }
73 
BoxGBBox_Extend_Margins(BoxGBBox * bb,BoxPoint * margin_min,BoxPoint * margin_max)74 void BoxGBBox_Extend_Margins(BoxGBBox *bb, BoxPoint *margin_min,
75                              BoxPoint *margin_max) {
76   bb->min.x -= margin_min->x;
77   bb->min.y -= margin_min->y;
78   bb->max.x += margin_max->x;
79   bb->max.y += margin_max->y;
80 }
81 
BoxGBBox_Extend_Margin(BoxGBBox * bb,BoxReal margin)82 void BoxGBBox_Extend_Margin(BoxGBBox *bb, BoxReal margin) {
83   bb->min.x -= margin;
84   bb->min.y -= margin;
85   bb->max.x += margin;
86   bb->max.y += margin;
87 }
88 
89 /***************************************************************************/
90 
91 /** BB specific window-data (used to store the bounding box). */
92 typedef struct {
93   int      enabled;
94   BoxGBBox bb_local,
95            bb_global;
96 
97 } MyBBWinData;
98 
99 /** Macro to access BB specific window data. */
100 #define MY_DATA(w) ((MyBBWinData *) (w)->data)
101 
My_Got_Point(BoxGWin * w,BoxReal x,BoxReal y)102 static void My_Got_Point(BoxGWin *w, BoxReal x, BoxReal y) {
103   MyBBWinData *my_data = MY_DATA(w);
104   if (my_data->enabled) {
105     BoxPoint p = {x, y};
106     BoxGBBox_Extend(& my_data->bb_local, & p);
107   }
108 }
109 
My_BB_Draw_Path(BoxGWin * w,DrawStyle * style)110 static void My_BB_Draw_Path(BoxGWin *w, DrawStyle *style) {
111   BoxGBBox *bb_local = & MY_DATA(w)->bb_local;
112   BoxGBBox *bb_global = & MY_DATA(w)->bb_global;
113   int do_border = (style->bord_width > 0.0);
114   BoxReal scale = style->scale;
115   if (do_border)
116     BoxGBBox_Extend_Margin(bb_local, 0.5*scale*style->bord_width);
117 
118   BoxGBBox_Fuse(bb_global, bb_local);
119   BoxGBBox_Init(bb_local);
120 }
121 
My_BB_Add_Line_Path(BoxGWin * w,BoxPoint * a,BoxPoint * b)122 static void My_BB_Add_Line_Path(BoxGWin *w, BoxPoint *a, BoxPoint *b) {
123 #ifdef DEBUG
124   printf("line\n");
125 #endif
126   My_Got_Point(w, a->x, a->y);
127   My_Got_Point(w, b->x, b->y);
128 }
129 
My_BB_Add_Join_Path(BoxGWin * w,BoxPoint * a,BoxPoint * b,BoxPoint * c)130 static void My_BB_Add_Join_Path(BoxGWin *w, BoxPoint *a, BoxPoint *b, BoxPoint *c) {
131 #ifdef DEBUG
132   printf("cong\n");
133 #endif
134   My_Got_Point(w, a->x, a->y); My_Got_Point(w, b->x, b->y);
135   My_Got_Point(w, c->x, c->y); My_Got_Point(w, a->x + c->x - b->x, a->y + c->y - b->y);
136 }
137 
My_BB_Add_Circle_Path(BoxGWin * w,BoxPoint * ctr,BoxPoint * a,BoxPoint * b)138 static void My_BB_Add_Circle_Path(BoxGWin *w, BoxPoint *ctr, BoxPoint *a, BoxPoint *b) {
139   BoxPoint va, vb;
140 #ifdef DEBUG
141   printf("circle\n");
142 #endif
143   va.x = a->x - ctr->x; va.y = a->y - ctr->y;
144   vb.x = b->x - ctr->x; vb.y = b->y - ctr->y;
145 
146   My_Got_Point(w, a->x + vb.x, a->y + vb.y);
147   My_Got_Point(w, a->x - vb.x, a->y - vb.y);
148   My_Got_Point(w, ctr->x - va.x - vb.x, ctr->y - va.y - vb.y);
149   My_Got_Point(w, ctr->x - va.x + vb.x, ctr->y - va.y + vb.y);
150 
151 }
152 
My_BB_Add_Text_Path(BoxGWin * w,BoxPoint * ctr,BoxPoint * left,BoxPoint * up,BoxPoint * from,const char * text)153 static void My_BB_Add_Text_Path(BoxGWin *w, BoxPoint *ctr, BoxPoint *left,
154                                 BoxPoint *up, BoxPoint *from,
155                                 const char *text) {
156   My_Got_Point(w, ctr->x, ctr->y);
157   My_Got_Point(w, left->x, left->y);
158   My_Got_Point(w, up->x, up->y);
159 }
160 
My_BB_Add_Fake_Point(BoxGWin * w,BoxPoint * p)161 static void My_BB_Add_Fake_Point(BoxGWin *w, BoxPoint *p) {
162   My_Got_Point(w, p->x, p->y);
163 }
164 
165 /** Used by My_BB_Interpret to pass data to the iterator
166  * My_BB_Interpret_Iter.
167  */
168 typedef struct {
169   BoxGWin    *win;
170   BoxGWinMap *map;
171 
172 } MyInterpretData;
173 
My_BB_Interpret_Iter(BoxGCmd cmd,BoxGCmdSig sig,int num_args,BoxGCmdArgKind * kinds,void ** args,BoxGCmdArg * aux,void * pass)174 static BoxGErr My_BB_Interpret_Iter(BoxGCmd cmd, BoxGCmdSig sig, int num_args,
175                                     BoxGCmdArgKind *kinds, void **args,
176                                     BoxGCmdArg *aux, void *pass) {
177   BoxGWin *w = ((MyInterpretData *) pass)->win;
178   BoxGWinMap *map = ((MyInterpretData *) pass)->map;
179   int do_include_points = 0;
180 
181   switch (cmd) {
182   case BOXGCMD_EXT_SET_AUTO_BBOX:
183     MY_DATA(w)->enabled = (int) *((BoxInt *) args[0]);
184     break;
185 
186   case BOXGCMD_EXT_UNSET_BBOX:
187     BoxGBBox_Init(& MY_DATA(w)->bb_local);
188     BoxGBBox_Init(& MY_DATA(w)->bb_global);
189     break;
190 
191   default:
192     do_include_points = 1;
193     break;
194   }
195 
196   if (do_include_points) {
197     int i;
198     for (i = 0; i < num_args; i++) {
199       void *arg = args[i];
200       BoxGCmdArgKind kind = kinds[i];
201       switch (kind) {
202       case BOXGCMDARGKIND_POINT:
203         {
204           BoxPoint p;
205           BoxGWinMap_Map_Point(map, & p, (BoxPoint *) arg);
206           My_Got_Point(w, p.x, p.y);
207         }
208         break;
209       default:
210         break;
211       }
212     }
213   }
214 
215   return BOXGERR_NO_ERR;
216 }
217 
My_BB_Interpret(BoxGWin * w,BoxGObj * obj,BoxGWinMap * map)218 static BoxTask My_BB_Interpret(BoxGWin *w, BoxGObj *obj, BoxGWinMap *map) {
219   MyInterpretData data;
220   data.win = w;
221   data.map = map;
222   return BoxGCmdIter_Iter(My_BB_Interpret_Iter, obj, & data);
223 }
224 
225 /** Set the default methods to the bb window */
bb_repair(BoxGWin * w)226 static void bb_repair(BoxGWin *w) {
227   BoxGWin_Block(w);
228   w->draw_path = My_BB_Draw_Path;
229   w->add_line_path = My_BB_Add_Line_Path;
230   w->add_join_path = My_BB_Add_Join_Path;
231   w->add_circle_path = My_BB_Add_Circle_Path;
232   w->add_text_path = My_BB_Add_Text_Path;
233   w->add_fake_point = My_BB_Add_Fake_Point;
234   w->interpret = My_BB_Interpret;
235 }
236 
BoxGBBox_Compute(BoxGBBox * bbox,BoxGWin * figure)237 int BoxGBBox_Compute(BoxGBBox *bbox, BoxGWin *figure) {
238   BoxGWin bb;
239   MyBBWinData bb_data;
240 
241   /* Set window-specific data (where the BBox will be stored). */
242   bb_data.enabled = 1;
243   BoxGBBox_Init(& bb_data.bb_global);
244   BoxGBBox_Init(& bb_data.bb_local);
245   bb.data = & bb_data;
246 
247   /* The procedure to handle the window are provided below: */
248   bb.quiet = 1;
249   bb.repair = bb_repair;
250   bb.repair(& bb);
251   bb.win_type_str = "bb";
252   BoxGWin_Fig_Draw_Fig(& bb, figure);
253   BoxGBBox_Fuse(& bb_data.bb_global, & bb_data.bb_local);
254   *bbox = bb_data.bb_global;
255   return (BoxGBBox_Get_Volume(& bb_data.bb_global) > 0.0);
256 }
257