1 | // =============================================================== // |
---|
2 | // // |
---|
3 | // File : AW_clipable.cxx // |
---|
4 | // Purpose : // |
---|
5 | // // |
---|
6 | // Institute of Microbiology (Technical University Munich) // |
---|
7 | // http://www.arb-home.de/ // |
---|
8 | // // |
---|
9 | // =============================================================== // |
---|
10 | |
---|
11 | #include "aw_window.hxx" |
---|
12 | #include "aw_root.hxx" |
---|
13 | #include "aw_common_xm.hxx" |
---|
14 | |
---|
15 | #include <arb_msg.h> |
---|
16 | |
---|
17 | using namespace AW; |
---|
18 | |
---|
19 | inline AW_pos clip_in_range(AW_pos low, AW_pos val, AW_pos high) { |
---|
20 | if (val <= low) return low; |
---|
21 | if (val >= high) return high; |
---|
22 | return val; |
---|
23 | } |
---|
24 | |
---|
25 | bool AW_clipable::box_clip(AW_pos x0, AW_pos y0, AW_pos x1, AW_pos y1, AW_pos& x0out, AW_pos& y0out, AW_pos& x1out, AW_pos& y1out) { |
---|
26 | // clip coordinates of a box |
---|
27 | |
---|
28 | aw_assert(x0 <= x1); |
---|
29 | aw_assert(y0 <= y1); |
---|
30 | |
---|
31 | if (x1<clip_rect.l || x0>clip_rect.r) return false; |
---|
32 | if (y1<clip_rect.t || y0>clip_rect.b) return false; |
---|
33 | |
---|
34 | if (completely_clipped()) return false; |
---|
35 | |
---|
36 | x0out = clip_in_range(clip_rect.l, x0, clip_rect.r); |
---|
37 | x1out = clip_in_range(clip_rect.l, x1, clip_rect.r); |
---|
38 | y0out = clip_in_range(clip_rect.t, y0, clip_rect.b); |
---|
39 | y1out = clip_in_range(clip_rect.t, y1, clip_rect.b); |
---|
40 | |
---|
41 | return true; |
---|
42 | } |
---|
43 | |
---|
44 | bool AW_clipable::box_clip(const Rectangle& rect, Rectangle& clippedRect) { // @@@ maybe return clippedRect as AW_screen_area |
---|
45 | if (completely_clipped()) return false; |
---|
46 | |
---|
47 | Rectangle clipRect(clip_rect, UPPER_LEFT_OUTLINE); |
---|
48 | if (rect.distinct_from(clipRect)) |
---|
49 | return false; |
---|
50 | |
---|
51 | clippedRect = rect.intersect_with(clipRect); |
---|
52 | return true; |
---|
53 | } |
---|
54 | |
---|
55 | inline Position rect_corner(const Rectangle& rect, int n) { |
---|
56 | if (n == 0) return rect.upper_left_corner(); |
---|
57 | if (n == 1) return rect.upper_right_corner(); |
---|
58 | if (n == 2) return rect.lower_right_corner(); |
---|
59 | return rect.lower_left_corner(); |
---|
60 | } |
---|
61 | |
---|
62 | bool AW_clipable::need_extra_clip_position(const AW::Position& p1, const AW::Position& p2, AW::Position& extra) { |
---|
63 | // calculates one extra needed position |
---|
64 | // |
---|
65 | // (may be caused by clipping, e.g. if a Position is clipped and the two adjacent lines cross different |
---|
66 | // sides of the clipping rectangle) |
---|
67 | // |
---|
68 | // Note: |
---|
69 | // - needs to be called until it returns false! (use 'extra' of 1st call as 'p1' for 2nd call, ...) |
---|
70 | // - returns corner of clipping rectangle adjacent to p1 |
---|
71 | |
---|
72 | bool yequal = nearlyEqual(p1.ypos(), p2.ypos()); |
---|
73 | if (!yequal) { |
---|
74 | bool xequal = nearlyEqual(p1.xpos(), p2.xpos()); |
---|
75 | if (!xequal) { |
---|
76 | Rectangle clipRect(clip_rect, UPPER_LEFT_OUTLINE); |
---|
77 | |
---|
78 | for (int c = 0; c<4; ++c) { |
---|
79 | Position corner = rect_corner(clipRect, c); |
---|
80 | if (nearlyEqual(p1.xpos(), corner.xpos()) && nearlyEqual(p2.ypos(), corner.ypos())) { |
---|
81 | extra = corner; |
---|
82 | return true; |
---|
83 | } |
---|
84 | if (nearlyEqual(p1.ypos(), corner.ypos()) && nearlyEqual(p2.xpos(), corner.xpos())) { |
---|
85 | extra = corner; |
---|
86 | return true; |
---|
87 | } |
---|
88 | } |
---|
89 | |
---|
90 | // fprintf(stderr, "Failed to find extra needed position:\n"); |
---|
91 | // AW_DUMP(p1); |
---|
92 | // AW_DUMP(p2); |
---|
93 | // AW_DUMP(clipRect); |
---|
94 | |
---|
95 | // happens rarely, no display bugs seen -> ignore |
---|
96 | } |
---|
97 | } |
---|
98 | return false; |
---|
99 | } |
---|
100 | |
---|
101 | bool AW_clipable::box_clip(int npos, const Position *pos, int& nclippedPos, Position*& clippedPos) { |
---|
102 | aw_assert(!clippedPos); |
---|
103 | |
---|
104 | bool is_visible = false; // indicates whether part of the polygon is visible |
---|
105 | |
---|
106 | nclippedPos = 0; |
---|
107 | const int MAX_POSS_POS = npos*2; |
---|
108 | clippedPos = new Position[MAX_POSS_POS]; |
---|
109 | |
---|
110 | for (int i = 0; i<npos; ++i) { |
---|
111 | int j = i+1; |
---|
112 | if (j == npos) j = 0; |
---|
113 | |
---|
114 | LineVector v(pos[i], pos[j]); |
---|
115 | LineVector vclipped; |
---|
116 | if (clip(v, vclipped)) { // drawn |
---|
117 | is_visible = true; |
---|
118 | if (!nclippedPos) { // first entry |
---|
119 | clippedPos[nclippedPos++] = vclipped.start(); |
---|
120 | clippedPos[nclippedPos++] = vclipped.head(); |
---|
121 | } |
---|
122 | else { |
---|
123 | if (nearlyEqual(clippedPos[nclippedPos-1], vclipped.start())) { // neither current nor last line was clipped at 'start' |
---|
124 | clippedPos[nclippedPos++] = vclipped.head(); |
---|
125 | } |
---|
126 | else { |
---|
127 | Position extra; |
---|
128 | if (need_extra_clip_position(clippedPos[nclippedPos-1], vclipped.start(), extra)) { |
---|
129 | clippedPos[nclippedPos++] = extra; |
---|
130 | } |
---|
131 | clippedPos[nclippedPos++] = vclipped.start(); |
---|
132 | clippedPos[nclippedPos++] = vclipped.head(); |
---|
133 | } |
---|
134 | } |
---|
135 | } |
---|
136 | if (j == 0 && nclippedPos>0) { // last line |
---|
137 | Position extra; |
---|
138 | if (need_extra_clip_position(clippedPos[nclippedPos-1], clippedPos[0], extra)) { |
---|
139 | clippedPos[nclippedPos++] = extra; |
---|
140 | } |
---|
141 | } |
---|
142 | } |
---|
143 | |
---|
144 | aw_assert(nclippedPos<=MAX_POSS_POS); |
---|
145 | |
---|
146 | return is_visible; |
---|
147 | } |
---|
148 | |
---|
149 | bool AW_clipable::clip(AW_pos x0, AW_pos y0, AW_pos x1, AW_pos y1, AW_pos& x0out, AW_pos& y0out, AW_pos& x1out, AW_pos& y1out) { |
---|
150 | // clip coordinates of a line |
---|
151 | |
---|
152 | int outcodeout; |
---|
153 | AW_pos x = 0; |
---|
154 | AW_pos y = 0; |
---|
155 | |
---|
156 | bool is_visible = false; // indicates whether part of line is visible |
---|
157 | bool done = false; // true soon as line is completely inside or outside rectangle |
---|
158 | |
---|
159 | while (!done) { |
---|
160 | int outcode0 = compoutcode(x0, y0); |
---|
161 | int outcode1 = compoutcode(x1, y1); |
---|
162 | |
---|
163 | if ((outcode0 | outcode1) == 0) { // line is inside the rectangle |
---|
164 | x0out = x0; y0out = y0; // clipped coordinates of line |
---|
165 | x1out = x1; y1out = y1; |
---|
166 | |
---|
167 | done = true; |
---|
168 | is_visible = true; |
---|
169 | } |
---|
170 | else if ((outcode0 & outcode1) != 0) { // line is outside the rectangle |
---|
171 | done = true; |
---|
172 | } |
---|
173 | else { // line overlaps with at least one rectangle border |
---|
174 | outcodeout = outcode0>0 ? outcode0 : outcode1; |
---|
175 | |
---|
176 | if ((outcodeout & 8) != 0) { // overlap at top |
---|
177 | x = x0+(x1-x0)*(clip_rect.t-y0)/(y1-y0); |
---|
178 | y = clip_rect.t; |
---|
179 | } |
---|
180 | else if ((outcodeout & 4) != 0) { // overlap at bottom |
---|
181 | x = x0+(x1-x0)*(clip_rect.b-y0)/(y1-y0); |
---|
182 | y = clip_rect.b; |
---|
183 | } |
---|
184 | else if ((outcodeout & 2) != 0) { // overlap at right side |
---|
185 | y = y0+(y1-y0)*(clip_rect.r-x0)/(x1-x0); |
---|
186 | x = clip_rect.r; |
---|
187 | } |
---|
188 | else if ((outcodeout & 1) != 0) { |
---|
189 | y = y0+(y1-y0)*(clip_rect.l-x0)/(x1-x0); // overlap at left side |
---|
190 | x = clip_rect.l; |
---|
191 | } |
---|
192 | |
---|
193 | // set corrected point and iterate : |
---|
194 | if (outcode0 > 0) { |
---|
195 | x0 = x; |
---|
196 | y0 = y; |
---|
197 | } |
---|
198 | else { |
---|
199 | x1 = x; |
---|
200 | y1 = y; |
---|
201 | } |
---|
202 | } |
---|
203 | } |
---|
204 | |
---|
205 | return is_visible; |
---|
206 | } |
---|
207 | |
---|
208 | bool AW_clipable::clip(const LineVector& line, LineVector& clippedLine) { |
---|
209 | AW_pos x0, y0, x1, y1; |
---|
210 | bool drawflag = clip(line.start().xpos(), line.start().ypos(), line.head().xpos(), line.head().ypos(), |
---|
211 | x0, y0, x1, y1); |
---|
212 | if (drawflag) clippedLine = LineVector(x0, y0, x1, y1); |
---|
213 | return drawflag; |
---|
214 | } |
---|
215 | void AW_clipable::set_bottom_clip_border(int bottom, bool allow_oversize) { |
---|
216 | clip_rect.b = bottom; |
---|
217 | if (!allow_oversize) { |
---|
218 | if (clip_rect.b > get_screen().b) clip_rect.b = get_screen().b; |
---|
219 | } |
---|
220 | else { |
---|
221 | set_bottom_font_overlap(true); // added 21.6.02 --ralf |
---|
222 | } |
---|
223 | } |
---|
224 | |
---|
225 | void AW_clipable::set_left_clip_border(int left, bool allow_oversize) { |
---|
226 | clip_rect.l = left; |
---|
227 | if (!allow_oversize) { |
---|
228 | if (clip_rect.l < get_screen().l) clip_rect.l = get_screen().l; |
---|
229 | } |
---|
230 | else { |
---|
231 | set_left_font_overlap(true); // added 21.6.02 --ralf |
---|
232 | } |
---|
233 | } |
---|
234 | |
---|
235 | void AW_clipable::set_right_clip_border(int right, bool allow_oversize) { |
---|
236 | clip_rect.r = right; |
---|
237 | if (!allow_oversize) { |
---|
238 | if (clip_rect.r > get_screen().r) clip_rect.r = get_screen().r; |
---|
239 | } |
---|
240 | else { |
---|
241 | set_right_font_overlap(true); // added to correct problem with last char skipped (added 21.6.02 --ralf) |
---|
242 | } |
---|
243 | } |
---|
244 | |
---|
245 | void AW_clipable::set_top_clip_border(int top, bool allow_oversize) { |
---|
246 | clip_rect.t = top; |
---|
247 | if (!allow_oversize) { |
---|
248 | if (clip_rect.t < get_screen().t) clip_rect.t = get_screen().t; |
---|
249 | } |
---|
250 | else { |
---|
251 | set_top_font_overlap(true); // added 21.6.02 --ralf |
---|
252 | } |
---|
253 | } |
---|
254 | |
---|
255 | |
---|
256 | int AW_clipable::reduceClipBorders(int top, int bottom, int left, int right) { |
---|
257 | // return 0 if no clipping area left |
---|
258 | if (top > clip_rect.t) clip_rect.t = top; |
---|
259 | if (bottom < clip_rect.b) clip_rect.b = bottom; |
---|
260 | if (left > clip_rect.l) clip_rect.l = left; |
---|
261 | if (right < clip_rect.r) clip_rect.r = right; |
---|
262 | |
---|
263 | return !(clip_rect.b<clip_rect.t || clip_rect.r<clip_rect.l); |
---|
264 | } |
---|
265 | |
---|
266 | void AW_clipable::set_cliprect_oversize(const AW_screen_area& rect, bool allow_oversize) { |
---|
267 | clip_rect = rect; |
---|
268 | |
---|
269 | const AW_screen_area& screen = get_screen(); |
---|
270 | if (!allow_oversize) { |
---|
271 | if (clip_rect.t < screen.t) clip_rect.t = screen.t; |
---|
272 | if (clip_rect.b > screen.b) clip_rect.b = screen.b; |
---|
273 | if (clip_rect.l < screen.l) clip_rect.l = screen.l; |
---|
274 | if (clip_rect.r > screen.r) clip_rect.r = screen.r; |
---|
275 | } |
---|
276 | |
---|
277 | set_font_overlap(false); |
---|
278 | |
---|
279 | if (allow_oversize) { // added 21.6.02 --ralf |
---|
280 | if (clip_rect.t < screen.t) set_top_font_overlap(true); |
---|
281 | if (clip_rect.b > screen.b) set_bottom_font_overlap(true); |
---|
282 | if (clip_rect.l < screen.l) set_left_font_overlap(true); |
---|
283 | if (clip_rect.r > screen.r) set_right_font_overlap(true); |
---|
284 | } |
---|
285 | } |
---|
286 | |
---|
287 | void AW_clipable::reduce_top_clip_border(int top) { |
---|
288 | if (top > clip_rect.t) clip_rect.t = top; |
---|
289 | } |
---|
290 | void AW_clipable::reduce_bottom_clip_border(int bottom) { |
---|
291 | if (bottom < clip_rect.b) clip_rect.b = bottom; |
---|
292 | } |
---|
293 | void AW_clipable::reduce_left_clip_border(int left) { |
---|
294 | if (left > clip_rect.l)clip_rect.l = left; |
---|
295 | } |
---|
296 | void AW_clipable::reduce_right_clip_border(int right) { |
---|
297 | if (right < clip_rect.r) clip_rect.r = right; |
---|
298 | } |
---|
299 | |
---|
300 | void AW_clipable::set_bottom_clip_margin(int bottom, bool allow_oversize) { |
---|
301 | clip_rect.b -= bottom; |
---|
302 | if (!allow_oversize) { |
---|
303 | if (clip_rect.b > get_screen().b) clip_rect.b = get_screen().b; |
---|
304 | } |
---|
305 | else { |
---|
306 | set_bottom_font_overlap(true); // added 21.6.02 --ralf |
---|
307 | } |
---|
308 | } |
---|
309 | bool AW_clipable::force_into_clipbox(const Position& pos, Position& forcedPos) { |
---|
310 | // force 'pos' inside 'clip_rect' |
---|
311 | if (completely_clipped()) return false; |
---|
312 | |
---|
313 | forcedPos.setx(clip_in_range(clip_rect.l, pos.xpos(), clip_rect.r)); |
---|
314 | forcedPos.sety(clip_in_range(clip_rect.t, pos.ypos(), clip_rect.b)); |
---|
315 | return true; |
---|
316 | } |
---|
317 | |
---|