| 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 | |
|---|