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