Anti-Grain Geometry Tutorial
agg_scanline_boolean_algebra.h
1 //----------------------------------------------------------------------------
2 // Anti-Grain Geometry - Version 2.4
3 // Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
4 //
5 // Permission to copy, use, modify, sell and distribute this software
6 // is granted provided this copyright notice appears in all copies.
7 // This software is provided "as is" without express or implied
8 // warranty, and with no claim as to its suitability for any purpose.
9 //
10 //----------------------------------------------------------------------------
11 // Contact: mcseem@antigrain.com
12 // mcseemagg@yahoo.com
13 // http://www.antigrain.com
14 //----------------------------------------------------------------------------
15 
16 #ifndef AGG_SCANLINE_BOOLEAN_ALGEBRA_INCLUDED
17 #define AGG_SCANLINE_BOOLEAN_ALGEBRA_INCLUDED
18 
19 #include <cstdlib>
20 #include "agg_basics.h"
21 
22 
23 namespace agg
24 {
25 
26  //-----------------------------------------------sbool_combine_spans_bin
27  // Functor.
28  // Combine two binary encoded spans, i.e., when we don't have any
29  // anti-aliasing information, but only X and Length. The function
30  // is compatible with any type of scanlines.
31  //----------------
32  template<class Scanline1,
33  class Scanline2,
34  class Scanline>
36  {
37  void operator () (const typename Scanline1::const_iterator&,
38  const typename Scanline2::const_iterator&,
39  int x, unsigned len,
40  Scanline& sl) const
41  {
42  sl.add_span(x, len, cover_full);
43  }
44  };
45 
46 
47 
48  //---------------------------------------------sbool_combine_spans_empty
49  // Functor.
50  // Combine two spans as empty ones. The functor does nothing
51  // and is used to XOR binary spans.
52  //----------------
53  template<class Scanline1,
54  class Scanline2,
55  class Scanline>
57  {
58  void operator () (const typename Scanline1::const_iterator&,
59  const typename Scanline2::const_iterator&,
60  int, unsigned,
61  Scanline&) const
62  {}
63  };
64 
65 
66 
67  //--------------------------------------------------sbool_add_span_empty
68  // Functor.
69  // Add nothing. Used in conbine_shapes_sub
70  //----------------
71  template<class Scanline1,
72  class Scanline>
74  {
75  void operator () (const typename Scanline1::const_iterator&,
76  int, unsigned,
77  Scanline&) const
78  {}
79  };
80 
81 
82  //----------------------------------------------------sbool_add_span_bin
83  // Functor.
84  // Add a binary span
85  //----------------
86  template<class Scanline1,
87  class Scanline>
89  {
90  void operator () (const typename Scanline1::const_iterator&,
91  int x, unsigned len,
92  Scanline& sl) const
93  {
94  sl.add_span(x, len, cover_full);
95  }
96  };
97 
98 
99 
100 
101  //-----------------------------------------------------sbool_add_span_aa
102  // Functor.
103  // Add an anti-aliased span
104  // anti-aliasing information, but only X and Length. The function
105  // is compatible with any type of scanlines.
106  //----------------
107  template<class Scanline1,
108  class Scanline>
110  {
111  void operator () (const typename Scanline1::const_iterator& span,
112  int x, unsigned len,
113  Scanline& sl) const
114  {
115  if(span->len < 0)
116  {
117  sl.add_span(x, len, *span->covers);
118  }
119  else
120  if(span->len > 0)
121  {
122  const typename Scanline1::cover_type* covers = span->covers;
123  if(span->x < x) covers += x - span->x;
124  sl.add_cells(x, len, covers);
125  }
126  }
127  };
128 
129 
130 
131 
132  //----------------------------------------------sbool_intersect_spans_aa
133  // Functor.
134  // Intersect two spans preserving the anti-aliasing information.
135  // The result is added to the "sl" scanline.
136  //------------------
137  template<class Scanline1,
138  class Scanline2,
139  class Scanline,
140  unsigned CoverShift = cover_shift>
142  {
143  enum cover_scale_e
144  {
145  cover_shift = CoverShift,
146  cover_size = 1 << cover_shift,
147  cover_mask = cover_size - 1,
148  cover_full = cover_mask
149  };
150 
151 
152  void operator () (const typename Scanline1::const_iterator& span1,
153  const typename Scanline2::const_iterator& span2,
154  int x, unsigned len,
155  Scanline& sl) const
156  {
157  unsigned cover;
158  const typename Scanline1::cover_type* covers1;
159  const typename Scanline2::cover_type* covers2;
160 
161  // Calculate the operation code and choose the
162  // proper combination algorithm.
163  // 0 = Both spans are of AA type
164  // 1 = span1 is solid, span2 is AA
165  // 2 = span1 is AA, span2 is solid
166  // 3 = Both spans are of solid type
167  //-----------------
168  switch((span1->len < 0) | ((span2->len < 0) << 1))
169  {
170  case 0: // Both are AA spans
171  covers1 = span1->covers;
172  covers2 = span2->covers;
173  if(span1->x < x) covers1 += x - span1->x;
174  if(span2->x < x) covers2 += x - span2->x;
175  do
176  {
177  cover = *covers1++ * *covers2++;
178  sl.add_cell(x++,
179  (cover == cover_full * cover_full) ?
180  cover_full :
181  (cover >> cover_shift));
182  }
183  while(--len);
184  break;
185 
186  case 1: // span1 is solid, span2 is AA
187  covers2 = span2->covers;
188  if(span2->x < x) covers2 += x - span2->x;
189  if(*(span1->covers) == cover_full)
190  {
191  sl.add_cells(x, len, covers2);
192  }
193  else
194  {
195  do
196  {
197  cover = *(span1->covers) * *covers2++;
198  sl.add_cell(x++,
199  (cover == cover_full * cover_full) ?
200  cover_full :
201  (cover >> cover_shift));
202  }
203  while(--len);
204  }
205  break;
206 
207  case 2: // span1 is AA, span2 is solid
208  covers1 = span1->covers;
209  if(span1->x < x) covers1 += x - span1->x;
210  if(*(span2->covers) == cover_full)
211  {
212  sl.add_cells(x, len, covers1);
213  }
214  else
215  {
216  do
217  {
218  cover = *covers1++ * *(span2->covers);
219  sl.add_cell(x++,
220  (cover == cover_full * cover_full) ?
221  cover_full :
222  (cover >> cover_shift));
223  }
224  while(--len);
225  }
226  break;
227 
228  case 3: // Both are solid spans
229  cover = *(span1->covers) * *(span2->covers);
230  sl.add_span(x, len,
231  (cover == cover_full * cover_full) ?
232  cover_full :
233  (cover >> cover_shift));
234  break;
235  }
236  }
237  };
238 
239 
240 
241 
242 
243 
244  //--------------------------------------------------sbool_unite_spans_aa
245  // Functor.
246  // Unite two spans preserving the anti-aliasing information.
247  // The result is added to the "sl" scanline.
248  //------------------
249  template<class Scanline1,
250  class Scanline2,
251  class Scanline,
252  unsigned CoverShift = cover_shift>
254  {
255  enum cover_scale_e
256  {
257  cover_shift = CoverShift,
258  cover_size = 1 << cover_shift,
259  cover_mask = cover_size - 1,
260  cover_full = cover_mask
261  };
262 
263 
264  void operator () (const typename Scanline1::const_iterator& span1,
265  const typename Scanline2::const_iterator& span2,
266  int x, unsigned len,
267  Scanline& sl) const
268  {
269  unsigned cover;
270  const typename Scanline1::cover_type* covers1;
271  const typename Scanline2::cover_type* covers2;
272 
273  // Calculate the operation code and choose the
274  // proper combination algorithm.
275  // 0 = Both spans are of AA type
276  // 1 = span1 is solid, span2 is AA
277  // 2 = span1 is AA, span2 is solid
278  // 3 = Both spans are of solid type
279  //-----------------
280  switch((span1->len < 0) | ((span2->len < 0) << 1))
281  {
282  case 0: // Both are AA spans
283  covers1 = span1->covers;
284  covers2 = span2->covers;
285  if(span1->x < x) covers1 += x - span1->x;
286  if(span2->x < x) covers2 += x - span2->x;
287  do
288  {
289  cover = cover_mask * cover_mask -
290  (cover_mask - *covers1++) *
291  (cover_mask - *covers2++);
292  sl.add_cell(x++,
293  (cover == cover_full * cover_full) ?
294  cover_full :
295  (cover >> cover_shift));
296  }
297  while(--len);
298  break;
299 
300  case 1: // span1 is solid, span2 is AA
301  covers2 = span2->covers;
302  if(span2->x < x) covers2 += x - span2->x;
303  if(*(span1->covers) == cover_full)
304  {
305  sl.add_span(x, len, cover_full);
306  }
307  else
308  {
309  do
310  {
311  cover = cover_mask * cover_mask -
312  (cover_mask - *(span1->covers)) *
313  (cover_mask - *covers2++);
314  sl.add_cell(x++,
315  (cover == cover_full * cover_full) ?
316  cover_full :
317  (cover >> cover_shift));
318  }
319  while(--len);
320  }
321  break;
322 
323  case 2: // span1 is AA, span2 is solid
324  covers1 = span1->covers;
325  if(span1->x < x) covers1 += x - span1->x;
326  if(*(span2->covers) == cover_full)
327  {
328  sl.add_span(x, len, cover_full);
329  }
330  else
331  {
332  do
333  {
334  cover = cover_mask * cover_mask -
335  (cover_mask - *covers1++) *
336  (cover_mask - *(span2->covers));
337  sl.add_cell(x++,
338  (cover == cover_full * cover_full) ?
339  cover_full :
340  (cover >> cover_shift));
341  }
342  while(--len);
343  }
344  break;
345 
346  case 3: // Both are solid spans
347  cover = cover_mask * cover_mask -
348  (cover_mask - *(span1->covers)) *
349  (cover_mask - *(span2->covers));
350  sl.add_span(x, len,
351  (cover == cover_full * cover_full) ?
352  cover_full :
353  (cover >> cover_shift));
354  break;
355  }
356  }
357  };
358 
359 
360  //---------------------------------------------sbool_xor_formula_linear
361  template<unsigned CoverShift = cover_shift>
363  {
364  enum cover_scale_e
365  {
366  cover_shift = CoverShift,
367  cover_size = 1 << cover_shift,
368  cover_mask = cover_size - 1
369  };
370 
371  static AGG_INLINE unsigned calculate(unsigned a, unsigned b)
372  {
373  unsigned cover = a + b;
374  if(cover > cover_mask) cover = cover_mask + cover_mask - cover;
375  return cover;
376  }
377  };
378 
379 
380  //---------------------------------------------sbool_xor_formula_saddle
381  template<unsigned CoverShift = cover_shift>
383  {
384  enum cover_scale_e
385  {
386  cover_shift = CoverShift,
387  cover_size = 1 << cover_shift,
388  cover_mask = cover_size - 1
389  };
390 
391  static AGG_INLINE unsigned calculate(unsigned a, unsigned b)
392  {
393  unsigned k = a * b;
394  if(k == cover_mask * cover_mask) return 0;
395 
396  a = (cover_mask * cover_mask - (a << cover_shift) + k) >> cover_shift;
397  b = (cover_mask * cover_mask - (b << cover_shift) + k) >> cover_shift;
398  return cover_mask - ((a * b) >> cover_shift);
399  }
400  };
401 
402 
403  //-------------------------------------------sbool_xor_formula_abs_diff
405  {
406  static AGG_INLINE unsigned calculate(unsigned a, unsigned b)
407  {
408  return unsigned(std::abs(int(a) - int(b)));
409  }
410  };
411 
412 
413 
414  //----------------------------------------------------sbool_xor_spans_aa
415  // Functor.
416  // XOR two spans preserving the anti-aliasing information.
417  // The result is added to the "sl" scanline.
418  //------------------
419  template<class Scanline1,
420  class Scanline2,
421  class Scanline,
422  class XorFormula,
423  unsigned CoverShift = cover_shift>
425  {
426  enum cover_scale_e
427  {
428  cover_shift = CoverShift,
429  cover_size = 1 << cover_shift,
430  cover_mask = cover_size - 1,
431  cover_full = cover_mask
432  };
433 
434 
435  void operator () (const typename Scanline1::const_iterator& span1,
436  const typename Scanline2::const_iterator& span2,
437  int x, unsigned len,
438  Scanline& sl) const
439  {
440  unsigned cover;
441  const typename Scanline1::cover_type* covers1;
442  const typename Scanline2::cover_type* covers2;
443 
444  // Calculate the operation code and choose the
445  // proper combination algorithm.
446  // 0 = Both spans are of AA type
447  // 1 = span1 is solid, span2 is AA
448  // 2 = span1 is AA, span2 is solid
449  // 3 = Both spans are of solid type
450  //-----------------
451  switch((span1->len < 0) | ((span2->len < 0) << 1))
452  {
453  case 0: // Both are AA spans
454  covers1 = span1->covers;
455  covers2 = span2->covers;
456  if(span1->x < x) covers1 += x - span1->x;
457  if(span2->x < x) covers2 += x - span2->x;
458  do
459  {
460  cover = XorFormula::calculate(*covers1++, *covers2++);
461  if(cover) sl.add_cell(x, cover);
462  ++x;
463  }
464  while(--len);
465  break;
466 
467  case 1: // span1 is solid, span2 is AA
468  covers2 = span2->covers;
469  if(span2->x < x) covers2 += x - span2->x;
470  do
471  {
472  cover = XorFormula::calculate(*(span1->covers), *covers2++);
473  if(cover) sl.add_cell(x, cover);
474  ++x;
475  }
476  while(--len);
477  break;
478 
479  case 2: // span1 is AA, span2 is solid
480  covers1 = span1->covers;
481  if(span1->x < x) covers1 += x - span1->x;
482  do
483  {
484  cover = XorFormula::calculate(*covers1++, *(span2->covers));
485  if(cover) sl.add_cell(x, cover);
486  ++x;
487  }
488  while(--len);
489  break;
490 
491  case 3: // Both are solid spans
492  cover = XorFormula::calculate(*(span1->covers), *(span2->covers));
493  if(cover) sl.add_span(x, len, cover);
494  break;
495 
496  }
497  }
498  };
499 
500 
501 
502 
503 
504  //-----------------------------------------------sbool_subtract_spans_aa
505  // Functor.
506  // Unite two spans preserving the anti-aliasing information.
507  // The result is added to the "sl" scanline.
508  //------------------
509  template<class Scanline1,
510  class Scanline2,
511  class Scanline,
512  unsigned CoverShift = cover_shift>
514  {
515  enum cover_scale_e
516  {
517  cover_shift = CoverShift,
518  cover_size = 1 << cover_shift,
519  cover_mask = cover_size - 1,
520  cover_full = cover_mask
521  };
522 
523 
524  void operator () (const typename Scanline1::const_iterator& span1,
525  const typename Scanline2::const_iterator& span2,
526  int x, unsigned len,
527  Scanline& sl) const
528  {
529  unsigned cover;
530  const typename Scanline1::cover_type* covers1;
531  const typename Scanline2::cover_type* covers2;
532 
533  // Calculate the operation code and choose the
534  // proper combination algorithm.
535  // 0 = Both spans are of AA type
536  // 1 = span1 is solid, span2 is AA
537  // 2 = span1 is AA, span2 is solid
538  // 3 = Both spans are of solid type
539  //-----------------
540  switch((span1->len < 0) | ((span2->len < 0) << 1))
541  {
542  case 0: // Both are AA spans
543  covers1 = span1->covers;
544  covers2 = span2->covers;
545  if(span1->x < x) covers1 += x - span1->x;
546  if(span2->x < x) covers2 += x - span2->x;
547  do
548  {
549  cover = *covers1++ * (cover_mask - *covers2++);
550  if(cover)
551  {
552  sl.add_cell(x,
553  (cover == cover_full * cover_full) ?
554  cover_full :
555  (cover >> cover_shift));
556  }
557  ++x;
558  }
559  while(--len);
560  break;
561 
562  case 1: // span1 is solid, span2 is AA
563  covers2 = span2->covers;
564  if(span2->x < x) covers2 += x - span2->x;
565  do
566  {
567  cover = *(span1->covers) * (cover_mask - *covers2++);
568  if(cover)
569  {
570  sl.add_cell(x,
571  (cover == cover_full * cover_full) ?
572  cover_full :
573  (cover >> cover_shift));
574  }
575  ++x;
576  }
577  while(--len);
578  break;
579 
580  case 2: // span1 is AA, span2 is solid
581  covers1 = span1->covers;
582  if(span1->x < x) covers1 += x - span1->x;
583  if(*(span2->covers) != cover_full)
584  {
585  do
586  {
587  cover = *covers1++ * (cover_mask - *(span2->covers));
588  if(cover)
589  {
590  sl.add_cell(x,
591  (cover == cover_full * cover_full) ?
592  cover_full :
593  (cover >> cover_shift));
594  }
595  ++x;
596  }
597  while(--len);
598  }
599  break;
600 
601  case 3: // Both are solid spans
602  cover = *(span1->covers) * (cover_mask - *(span2->covers));
603  if(cover)
604  {
605  sl.add_span(x, len,
606  (cover == cover_full * cover_full) ?
607  cover_full :
608  (cover >> cover_shift));
609  }
610  break;
611  }
612  }
613  };
614 
615 
616 
617 
618 
619 
620  //--------------------------------------------sbool_add_spans_and_render
621  template<class Scanline1,
622  class Scanline,
623  class Renderer,
624  class AddSpanFunctor>
625  void sbool_add_spans_and_render(const Scanline1& sl1,
626  Scanline& sl,
627  Renderer& ren,
628  AddSpanFunctor add_span)
629  {
630  sl.reset_spans();
631  typename Scanline1::const_iterator span = sl1.begin();
632  unsigned num_spans = sl1.num_spans();
633  for(;;)
634  {
635  add_span(span, span->x, std::abs((int)span->len), sl);
636  if(--num_spans == 0) break;
637  ++span;
638  }
639  sl.finalize(sl1.y());
640  ren.render(sl);
641  }
642 
643 
644 
645 
646 
647 
648 
649  //---------------------------------------------sbool_intersect_scanlines
650  // Intersect two scanlines, "sl1" and "sl2" and generate a new "sl" one.
651  // The combine_spans functor can be of type sbool_combine_spans_bin or
652  // sbool_intersect_spans_aa. First is a general functor to combine
653  // two spans without Anti-Aliasing, the second preserves the AA
654  // information, but works slower
655  //
656  template<class Scanline1,
657  class Scanline2,
658  class Scanline,
659  class CombineSpansFunctor>
660  void sbool_intersect_scanlines(const Scanline1& sl1,
661  const Scanline2& sl2,
662  Scanline& sl,
663  CombineSpansFunctor combine_spans)
664  {
665  sl.reset_spans();
666 
667  unsigned num1 = sl1.num_spans();
668  if(num1 == 0) return;
669 
670  unsigned num2 = sl2.num_spans();
671  if(num2 == 0) return;
672 
673  typename Scanline1::const_iterator span1 = sl1.begin();
674  typename Scanline2::const_iterator span2 = sl2.begin();
675 
676  while(num1 && num2)
677  {
678  int xb1 = span1->x;
679  int xb2 = span2->x;
680  int xe1 = xb1 + std::abs((int)span1->len) - 1;
681  int xe2 = xb2 + std::abs((int)span2->len) - 1;
682 
683  // Determine what spans we should advance in the next step
684  // The span with the least ending X should be advanced
685  // advance_both is just an optimization when we ending
686  // coordinates are the same and we can advance both
687  //--------------
688  bool advance_span1 = xe1 < xe2;
689  bool advance_both = xe1 == xe2;
690 
691  // Find the intersection of the spans
692  // and check if they intersect
693  //--------------
694  if(xb1 < xb2) xb1 = xb2;
695  if(xe1 > xe2) xe1 = xe2;
696  if(xb1 <= xe1)
697  {
698  combine_spans(span1, span2, xb1, xe1 - xb1 + 1, sl);
699  }
700 
701  // Advance the spans
702  //--------------
703  if(advance_both)
704  {
705  --num1;
706  --num2;
707  if(num1) ++span1;
708  if(num2) ++span2;
709  }
710  else
711  {
712  if(advance_span1)
713  {
714  --num1;
715  if(num1) ++span1;
716  }
717  else
718  {
719  --num2;
720  if(num2) ++span2;
721  }
722  }
723  }
724  }
725 
726 
727 
728 
729 
730 
731 
732 
733  //------------------------------------------------sbool_intersect_shapes
734  // Intersect the scanline shapes. Here the "Scanline Generator"
735  // abstraction is used. ScanlineGen1 and ScanlineGen2 are
736  // the generators, and can be of type rasterizer_scanline_aa<>.
737  // There function requires three scanline containers that can be of
738  // different types.
739  // "sl1" and "sl2" are used to retrieve scanlines from the generators,
740  // "sl" is ised as the resulting scanline to render it.
741  // The external "sl1" and "sl2" are used only for the sake of
742  // optimization and reusing of the scanline objects.
743  // the function calls sbool_intersect_scanlines with CombineSpansFunctor
744  // as the last argument. See sbool_intersect_scanlines for details.
745  //----------
746  template<class ScanlineGen1,
747  class ScanlineGen2,
748  class Scanline1,
749  class Scanline2,
750  class Scanline,
751  class Renderer,
752  class CombineSpansFunctor>
753  void sbool_intersect_shapes(ScanlineGen1& sg1, ScanlineGen2& sg2,
754  Scanline1& sl1, Scanline2& sl2,
755  Scanline& sl, Renderer& ren,
756  CombineSpansFunctor combine_spans)
757  {
758  // Prepare the scanline generators.
759  // If anyone of them doesn't contain
760  // any scanlines, then return.
761  //-----------------
762  if(!sg1.rewind_scanlines()) return;
763  if(!sg2.rewind_scanlines()) return;
764 
765  // Get the bounding boxes
766  //----------------
767  rect_i r1(sg1.min_x(), sg1.min_y(), sg1.max_x(), sg1.max_y());
768  rect_i r2(sg2.min_x(), sg2.min_y(), sg2.max_x(), sg2.max_y());
769 
770  // Calculate the intersection of the bounding
771  // boxes and return if they don't intersect.
772  //-----------------
773  rect_i ir = intersect_rectangles(r1, r2);
774  if(!ir.is_valid()) return;
775 
776  // Reset the scanlines and get two first ones
777  //-----------------
778  sl.reset(ir.x1, ir.x2);
779  sl1.reset(sg1.min_x(), sg1.max_x());
780  sl2.reset(sg2.min_x(), sg2.max_x());
781  if(!sg1.sweep_scanline(sl1)) return;
782  if(!sg2.sweep_scanline(sl2)) return;
783 
784  ren.prepare();
785 
786  // The main loop
787  // Here we synchronize the scanlines with
788  // the same Y coordinate, ignoring all other ones.
789  // Only scanlines having the same Y-coordinate
790  // are to be combined.
791  //-----------------
792  for(;;)
793  {
794  while(sl1.y() < sl2.y())
795  {
796  if(!sg1.sweep_scanline(sl1)) return;
797  }
798  while(sl2.y() < sl1.y())
799  {
800  if(!sg2.sweep_scanline(sl2)) return;
801  }
802 
803  if(sl1.y() == sl2.y())
804  {
805  // The Y coordinates are the same.
806  // Combine the scanlines, render if they contain any spans,
807  // and advance both generators to the next scanlines
808  //----------------------
809  sbool_intersect_scanlines(sl1, sl2, sl, combine_spans);
810  if(sl.num_spans())
811  {
812  sl.finalize(sl1.y());
813  ren.render(sl);
814  }
815  if(!sg1.sweep_scanline(sl1)) return;
816  if(!sg2.sweep_scanline(sl2)) return;
817  }
818  }
819  }
820 
821 
822 
823 
824 
825 
826 
827  //-------------------------------------------------sbool_unite_scanlines
828  // Unite two scanlines, "sl1" and "sl2" and generate a new "sl" one.
829  // The combine_spans functor can be of type sbool_combine_spans_bin or
830  // sbool_intersect_spans_aa. First is a general functor to combine
831  // two spans without Anti-Aliasing, the second preserves the AA
832  // information, but works slower
833  //
834  template<class Scanline1,
835  class Scanline2,
836  class Scanline,
837  class AddSpanFunctor1,
838  class AddSpanFunctor2,
839  class CombineSpansFunctor>
840  void sbool_unite_scanlines(const Scanline1& sl1,
841  const Scanline2& sl2,
842  Scanline& sl,
843  AddSpanFunctor1 add_span1,
844  AddSpanFunctor2 add_span2,
845  CombineSpansFunctor combine_spans)
846  {
847  sl.reset_spans();
848 
849  unsigned num1 = sl1.num_spans();
850  unsigned num2 = sl2.num_spans();
851 
852  typename Scanline1::const_iterator span1;// = sl1.begin();
853  typename Scanline2::const_iterator span2;// = sl2.begin();
854 
855  enum invalidation_e
856  {
857  invalid_b = 0xFFFFFFF,
858  invalid_e = invalid_b - 1
859  };
860 
861  // Initialize the spans as invalid
862  //---------------
863  int xb1 = invalid_b;
864  int xb2 = invalid_b;
865  int xe1 = invalid_e;
866  int xe2 = invalid_e;
867 
868  // Initialize span1 if there are spans
869  //---------------
870  if(num1)
871  {
872  span1 = sl1.begin();
873  xb1 = span1->x;
874  xe1 = xb1 + std::abs((int)span1->len) - 1;
875  --num1;
876  }
877 
878  // Initialize span2 if there are spans
879  //---------------
880  if(num2)
881  {
882  span2 = sl2.begin();
883  xb2 = span2->x;
884  xe2 = xb2 + std::abs((int)span2->len) - 1;
885  --num2;
886  }
887 
888 
889  for(;;)
890  {
891  // Retrieve a new span1 if it's invalid
892  //----------------
893  if(num1 && xb1 > xe1)
894  {
895  --num1;
896  ++span1;
897  xb1 = span1->x;
898  xe1 = xb1 + std::abs((int)span1->len) - 1;
899  }
900 
901  // Retrieve a new span2 if it's invalid
902  //----------------
903  if(num2 && xb2 > xe2)
904  {
905  --num2;
906  ++span2;
907  xb2 = span2->x;
908  xe2 = xb2 + std::abs((int)span2->len) - 1;
909  }
910 
911  if(xb1 > xe1 && xb2 > xe2) break;
912 
913  // Calculate the intersection
914  //----------------
915  int xb = xb1;
916  int xe = xe1;
917  if(xb < xb2) xb = xb2;
918  if(xe > xe2) xe = xe2;
919  int len = xe - xb + 1; // The length of the intersection
920  if(len > 0)
921  {
922  // The spans intersect,
923  // add the beginning of the span
924  //----------------
925  if(xb1 < xb2)
926  {
927  add_span1(span1, xb1, xb2 - xb1, sl);
928  xb1 = xb2;
929  }
930  else
931  if(xb2 < xb1)
932  {
933  add_span2(span2, xb2, xb1 - xb2, sl);
934  xb2 = xb1;
935  }
936 
937  // Add the combination part of the spans
938  //----------------
939  combine_spans(span1, span2, xb, len, sl);
940 
941 
942  // Invalidate the fully processed span or both
943  //----------------
944  if(xe1 < xe2)
945  {
946  // Invalidate span1 and eat
947  // the processed part of span2
948  //--------------
949  xb1 = invalid_b;
950  xe1 = invalid_e;
951  xb2 += len;
952  }
953  else
954  if(xe2 < xe1)
955  {
956  // Invalidate span2 and eat
957  // the processed part of span1
958  //--------------
959  xb2 = invalid_b;
960  xe2 = invalid_e;
961  xb1 += len;
962  }
963  else
964  {
965  xb1 = invalid_b; // Invalidate both
966  xb2 = invalid_b;
967  xe1 = invalid_e;
968  xe2 = invalid_e;
969  }
970  }
971  else
972  {
973  // The spans do not intersect
974  //--------------
975  if(xb1 < xb2)
976  {
977  // Advance span1
978  //---------------
979  if(xb1 <= xe1)
980  {
981  add_span1(span1, xb1, xe1 - xb1 + 1, sl);
982  }
983  xb1 = invalid_b; // Invalidate
984  xe1 = invalid_e;
985  }
986  else
987  {
988  // Advance span2
989  //---------------
990  if(xb2 <= xe2)
991  {
992  add_span2(span2, xb2, xe2 - xb2 + 1, sl);
993  }
994  xb2 = invalid_b; // Invalidate
995  xe2 = invalid_e;
996  }
997  }
998  }
999  }
1000 
1001 
1002 
1003 
1004  //----------------------------------------------------sbool_unite_shapes
1005  // Unite the scanline shapes. Here the "Scanline Generator"
1006  // abstraction is used. ScanlineGen1 and ScanlineGen2 are
1007  // the generators, and can be of type rasterizer_scanline_aa<>.
1008  // There function requires three scanline containers that can be
1009  // of different type.
1010  // "sl1" and "sl2" are used to retrieve scanlines from the generators,
1011  // "sl" is ised as the resulting scanline to render it.
1012  // The external "sl1" and "sl2" are used only for the sake of
1013  // optimization and reusing of the scanline objects.
1014  // the function calls sbool_unite_scanlines with CombineSpansFunctor
1015  // as the last argument. See sbool_unite_scanlines for details.
1016  //----------
1017  template<class ScanlineGen1,
1018  class ScanlineGen2,
1019  class Scanline1,
1020  class Scanline2,
1021  class Scanline,
1022  class Renderer,
1023  class AddSpanFunctor1,
1024  class AddSpanFunctor2,
1025  class CombineSpansFunctor>
1026  void sbool_unite_shapes(ScanlineGen1& sg1, ScanlineGen2& sg2,
1027  Scanline1& sl1, Scanline2& sl2,
1028  Scanline& sl, Renderer& ren,
1029  AddSpanFunctor1 add_span1,
1030  AddSpanFunctor2 add_span2,
1031  CombineSpansFunctor combine_spans)
1032  {
1033  // Prepare the scanline generators.
1034  // If anyone of them doesn't contain
1035  // any scanlines, then return.
1036  //-----------------
1037  bool flag1 = sg1.rewind_scanlines();
1038  bool flag2 = sg2.rewind_scanlines();
1039  if(!flag1 && !flag2) return;
1040 
1041  // Get the bounding boxes
1042  //----------------
1043  rect_i r1(sg1.min_x(), sg1.min_y(), sg1.max_x(), sg1.max_y());
1044  rect_i r2(sg2.min_x(), sg2.min_y(), sg2.max_x(), sg2.max_y());
1045 
1046  // Calculate the union of the bounding boxes
1047  //-----------------
1048  rect_i ur(1,1,0,0);
1049  if(flag1 && flag2) ur = unite_rectangles(r1, r2);
1050  else if(flag1) ur = r1;
1051  else if(flag2) ur = r2;
1052 
1053  if(!ur.is_valid()) return;
1054 
1055  ren.prepare();
1056 
1057  // Reset the scanlines and get two first ones
1058  //-----------------
1059  sl.reset(ur.x1, ur.x2);
1060  if(flag1)
1061  {
1062  sl1.reset(sg1.min_x(), sg1.max_x());
1063  flag1 = sg1.sweep_scanline(sl1);
1064  }
1065 
1066  if(flag2)
1067  {
1068  sl2.reset(sg2.min_x(), sg2.max_x());
1069  flag2 = sg2.sweep_scanline(sl2);
1070  }
1071 
1072  // The main loop
1073  // Here we synchronize the scanlines with
1074  // the same Y coordinate.
1075  //-----------------
1076  while(flag1 || flag2)
1077  {
1078  if(flag1 && flag2)
1079  {
1080  if(sl1.y() == sl2.y())
1081  {
1082  // The Y coordinates are the same.
1083  // Combine the scanlines, render if they contain any spans,
1084  // and advance both generators to the next scanlines
1085  //----------------------
1086  sbool_unite_scanlines(sl1, sl2, sl,
1087  add_span1, add_span2, combine_spans);
1088  if(sl.num_spans())
1089  {
1090  sl.finalize(sl1.y());
1091  ren.render(sl);
1092  }
1093  flag1 = sg1.sweep_scanline(sl1);
1094  flag2 = sg2.sweep_scanline(sl2);
1095  }
1096  else
1097  {
1098  if(sl1.y() < sl2.y())
1099  {
1100  sbool_add_spans_and_render(sl1, sl, ren, add_span1);
1101  flag1 = sg1.sweep_scanline(sl1);
1102  }
1103  else
1104  {
1105  sbool_add_spans_and_render(sl2, sl, ren, add_span2);
1106  flag2 = sg2.sweep_scanline(sl2);
1107  }
1108  }
1109  }
1110  else
1111  {
1112  if(flag1)
1113  {
1114  sbool_add_spans_and_render(sl1, sl, ren, add_span1);
1115  flag1 = sg1.sweep_scanline(sl1);
1116  }
1117  if(flag2)
1118  {
1119  sbool_add_spans_and_render(sl2, sl, ren, add_span2);
1120  flag2 = sg2.sweep_scanline(sl2);
1121  }
1122  }
1123  }
1124  }
1125 
1126 
1127 
1128 
1129 
1130 
1131 
1132 
1133  //-------------------------------------------------sbool_subtract_shapes
1134  // Subtract the scanline shapes, "sg1-sg2". Here the "Scanline Generator"
1135  // abstraction is used. ScanlineGen1 and ScanlineGen2 are
1136  // the generators, and can be of type rasterizer_scanline_aa<>.
1137  // There function requires three scanline containers that can be of
1138  // different types.
1139  // "sl1" and "sl2" are used to retrieve scanlines from the generators,
1140  // "sl" is ised as the resulting scanline to render it.
1141  // The external "sl1" and "sl2" are used only for the sake of
1142  // optimization and reusing of the scanline objects.
1143  // the function calls sbool_intersect_scanlines with CombineSpansFunctor
1144  // as the last argument. See combine_scanlines_sub for details.
1145  //----------
1146  template<class ScanlineGen1,
1147  class ScanlineGen2,
1148  class Scanline1,
1149  class Scanline2,
1150  class Scanline,
1151  class Renderer,
1152  class AddSpanFunctor1,
1153  class CombineSpansFunctor>
1154  void sbool_subtract_shapes(ScanlineGen1& sg1, ScanlineGen2& sg2,
1155  Scanline1& sl1, Scanline2& sl2,
1156  Scanline& sl, Renderer& ren,
1157  AddSpanFunctor1 add_span1,
1158  CombineSpansFunctor combine_spans)
1159  {
1160  // Prepare the scanline generators.
1161  // Here "sg1" is master, "sg2" is slave.
1162  //-----------------
1163  if(!sg1.rewind_scanlines()) return;
1164  bool flag2 = sg2.rewind_scanlines();
1165 
1166  // Get the bounding box
1167  //----------------
1168  rect_i r1(sg1.min_x(), sg1.min_y(), sg1.max_x(), sg1.max_y());
1169 
1170  // Reset the scanlines and get two first ones
1171  //-----------------
1172  sl.reset(sg1.min_x(), sg1.max_x());
1173  sl1.reset(sg1.min_x(), sg1.max_x());
1174  sl2.reset(sg2.min_x(), sg2.max_x());
1175  if(!sg1.sweep_scanline(sl1)) return;
1176 
1177  if(flag2) flag2 = sg2.sweep_scanline(sl2);
1178 
1179  ren.prepare();
1180 
1181  // A fake span2 processor
1183 
1184  // The main loop
1185  // Here we synchronize the scanlines with
1186  // the same Y coordinate, ignoring all other ones.
1187  // Only scanlines having the same Y-coordinate
1188  // are to be combined.
1189  //-----------------
1190  bool flag1 = true;
1191  do
1192  {
1193  // Synchronize "slave" with "master"
1194  //-----------------
1195  while(flag2 && sl2.y() < sl1.y())
1196  {
1197  flag2 = sg2.sweep_scanline(sl2);
1198  }
1199 
1200 
1201  if(flag2 && sl2.y() == sl1.y())
1202  {
1203  // The Y coordinates are the same.
1204  // Combine the scanlines and render if they contain any spans.
1205  //----------------------
1206  sbool_unite_scanlines(sl1, sl2, sl, add_span1, add_span2, combine_spans);
1207  if(sl.num_spans())
1208  {
1209  sl.finalize(sl1.y());
1210  ren.render(sl);
1211  }
1212  }
1213  else
1214  {
1215  sbool_add_spans_and_render(sl1, sl, ren, add_span1);
1216  }
1217 
1218  // Advance the "master"
1219  flag1 = sg1.sweep_scanline(sl1);
1220  }
1221  while(flag1);
1222  }
1223 
1224 
1225 
1226 
1227 
1228 
1229 
1230  //---------------------------------------------sbool_intersect_shapes_aa
1231  // Intersect two anti-aliased scanline shapes.
1232  // Here the "Scanline Generator" abstraction is used.
1233  // ScanlineGen1 and ScanlineGen2 are the generators, and can be of
1234  // type rasterizer_scanline_aa<>. There function requires three
1235  // scanline containers that can be of different types.
1236  // "sl1" and "sl2" are used to retrieve scanlines from the generators,
1237  // "sl" is ised as the resulting scanline to render it.
1238  // The external "sl1" and "sl2" are used only for the sake of
1239  // optimization and reusing of the scanline objects.
1240  //----------
1241  template<class ScanlineGen1,
1242  class ScanlineGen2,
1243  class Scanline1,
1244  class Scanline2,
1245  class Scanline,
1246  class Renderer>
1247  void sbool_intersect_shapes_aa(ScanlineGen1& sg1, ScanlineGen2& sg2,
1248  Scanline1& sl1, Scanline2& sl2,
1249  Scanline& sl, Renderer& ren)
1250  {
1252  sbool_intersect_shapes(sg1, sg2, sl1, sl2, sl, ren, combine_functor);
1253  }
1254 
1255 
1256 
1257 
1258 
1259  //--------------------------------------------sbool_intersect_shapes_bin
1260  // Intersect two binary scanline shapes (without anti-aliasing).
1261  // See intersect_shapes_aa for more comments
1262  //----------
1263  template<class ScanlineGen1,
1264  class ScanlineGen2,
1265  class Scanline1,
1266  class Scanline2,
1267  class Scanline,
1268  class Renderer>
1269  void sbool_intersect_shapes_bin(ScanlineGen1& sg1, ScanlineGen2& sg2,
1270  Scanline1& sl1, Scanline2& sl2,
1271  Scanline& sl, Renderer& ren)
1272  {
1274  sbool_intersect_shapes(sg1, sg2, sl1, sl2, sl, ren, combine_functor);
1275  }
1276 
1277 
1278 
1279 
1280 
1281  //-------------------------------------------------sbool_unite_shapes_aa
1282  // Unite two anti-aliased scanline shapes
1283  // See intersect_shapes_aa for more comments
1284  //----------
1285  template<class ScanlineGen1,
1286  class ScanlineGen2,
1287  class Scanline1,
1288  class Scanline2,
1289  class Scanline,
1290  class Renderer>
1291  void sbool_unite_shapes_aa(ScanlineGen1& sg1, ScanlineGen2& sg2,
1292  Scanline1& sl1, Scanline2& sl2,
1293  Scanline& sl, Renderer& ren)
1294  {
1298  sbool_unite_shapes(sg1, sg2, sl1, sl2, sl, ren,
1299  add_functor1, add_functor2, combine_functor);
1300  }
1301 
1302 
1303 
1304 
1305 
1306  //------------------------------------------------sbool_unite_shapes_bin
1307  // Unite two binary scanline shapes (without anti-aliasing).
1308  // See intersect_shapes_aa for more comments
1309  //----------
1310  template<class ScanlineGen1,
1311  class ScanlineGen2,
1312  class Scanline1,
1313  class Scanline2,
1314  class Scanline,
1315  class Renderer>
1316  void sbool_unite_shapes_bin(ScanlineGen1& sg1, ScanlineGen2& sg2,
1317  Scanline1& sl1, Scanline2& sl2,
1318  Scanline& sl, Renderer& ren)
1319  {
1323  sbool_unite_shapes(sg1, sg2, sl1, sl2, sl, ren,
1324  add_functor1, add_functor2, combine_functor);
1325  }
1326 
1327 
1328 
1329 
1330 
1331 
1332 
1333 
1334 
1335  //---------------------------------------------------sbool_xor_shapes_aa
1336  // Apply eXclusive OR to two anti-aliased scanline shapes. There's
1337  // a modified "Linear" XOR used instead of classical "Saddle" one.
1338  // The reason is to have the result absolutely conststent with what
1339  // the scanline rasterizer produces.
1340  // See intersect_shapes_aa for more comments
1341  //----------
1342  template<class ScanlineGen1,
1343  class ScanlineGen2,
1344  class Scanline1,
1345  class Scanline2,
1346  class Scanline,
1347  class Renderer>
1348  void sbool_xor_shapes_aa(ScanlineGen1& sg1, ScanlineGen2& sg2,
1349  Scanline1& sl1, Scanline2& sl2,
1350  Scanline& sl, Renderer& ren)
1351  {
1354  sbool_xor_spans_aa<Scanline1, Scanline2, Scanline,
1355  sbool_xor_formula_linear<> > combine_functor;
1356  sbool_unite_shapes(sg1, sg2, sl1, sl2, sl, ren,
1357  add_functor1, add_functor2, combine_functor);
1358  }
1359 
1360 
1361 
1362  //------------------------------------------sbool_xor_shapes_saddle_aa
1363  // Apply eXclusive OR to two anti-aliased scanline shapes.
1364  // There's the classical "Saddle" used to calculate the
1365  // Anti-Aliasing values, that is:
1366  // a XOR b : 1-((1-a+a*b)*(1-b+a*b))
1367  // See intersect_shapes_aa for more comments
1368  //----------
1369  template<class ScanlineGen1,
1370  class ScanlineGen2,
1371  class Scanline1,
1372  class Scanline2,
1373  class Scanline,
1374  class Renderer>
1375  void sbool_xor_shapes_saddle_aa(ScanlineGen1& sg1, ScanlineGen2& sg2,
1376  Scanline1& sl1, Scanline2& sl2,
1377  Scanline& sl, Renderer& ren)
1378  {
1381  sbool_xor_spans_aa<Scanline1,
1382  Scanline2,
1383  Scanline,
1384  sbool_xor_formula_saddle<> > combine_functor;
1385  sbool_unite_shapes(sg1, sg2, sl1, sl2, sl, ren,
1386  add_functor1, add_functor2, combine_functor);
1387  }
1388 
1389 
1390  //--------------------------------------sbool_xor_shapes_abs_diff_aa
1391  // Apply eXclusive OR to two anti-aliased scanline shapes.
1392  // There's the absolute difference used to calculate
1393  // Anti-Aliasing values, that is:
1394  // a XOR b : abs(a-b)
1395  // See intersect_shapes_aa for more comments
1396  //----------
1397  template<class ScanlineGen1,
1398  class ScanlineGen2,
1399  class Scanline1,
1400  class Scanline2,
1401  class Scanline,
1402  class Renderer>
1403  void sbool_xor_shapes_abs_diff_aa(ScanlineGen1& sg1, ScanlineGen2& sg2,
1404  Scanline1& sl1, Scanline2& sl2,
1405  Scanline& sl, Renderer& ren)
1406  {
1409  sbool_xor_spans_aa<Scanline1,
1410  Scanline2,
1411  Scanline,
1412  sbool_xor_formula_abs_diff> combine_functor;
1413  sbool_unite_shapes(sg1, sg2, sl1, sl2, sl, ren,
1414  add_functor1, add_functor2, combine_functor);
1415  }
1416 
1417 
1418 
1419  //--------------------------------------------------sbool_xor_shapes_bin
1420  // Apply eXclusive OR to two binary scanline shapes (without anti-aliasing).
1421  // See intersect_shapes_aa for more comments
1422  //----------
1423  template<class ScanlineGen1,
1424  class ScanlineGen2,
1425  class Scanline1,
1426  class Scanline2,
1427  class Scanline,
1428  class Renderer>
1429  void sbool_xor_shapes_bin(ScanlineGen1& sg1, ScanlineGen2& sg2,
1430  Scanline1& sl1, Scanline2& sl2,
1431  Scanline& sl, Renderer& ren)
1432  {
1436  sbool_unite_shapes(sg1, sg2, sl1, sl2, sl, ren,
1437  add_functor1, add_functor2, combine_functor);
1438  }
1439 
1440 
1441 
1442 
1443 
1444 
1445  //----------------------------------------------sbool_subtract_shapes_aa
1446  // Subtract shapes "sg1-sg2" with anti-aliasing
1447  // See intersect_shapes_aa for more comments
1448  //----------
1449  template<class ScanlineGen1,
1450  class ScanlineGen2,
1451  class Scanline1,
1452  class Scanline2,
1453  class Scanline,
1454  class Renderer>
1455  void sbool_subtract_shapes_aa(ScanlineGen1& sg1, ScanlineGen2& sg2,
1456  Scanline1& sl1, Scanline2& sl2,
1457  Scanline& sl, Renderer& ren)
1458  {
1461  sbool_subtract_shapes(sg1, sg2, sl1, sl2, sl, ren,
1462  add_functor, combine_functor);
1463  }
1464 
1465 
1466 
1467 
1468 
1469  //---------------------------------------------sbool_subtract_shapes_bin
1470  // Subtract binary shapes "sg1-sg2" without anti-aliasing
1471  // See intersect_shapes_aa for more comments
1472  //----------
1473  template<class ScanlineGen1,
1474  class ScanlineGen2,
1475  class Scanline1,
1476  class Scanline2,
1477  class Scanline,
1478  class Renderer>
1479  void sbool_subtract_shapes_bin(ScanlineGen1& sg1, ScanlineGen2& sg2,
1480  Scanline1& sl1, Scanline2& sl2,
1481  Scanline& sl, Renderer& ren)
1482  {
1485  sbool_subtract_shapes(sg1, sg2, sl1, sl2, sl, ren,
1486  add_functor, combine_functor);
1487  }
1488 
1489 
1490 
1491 
1492 
1493 
1494  //------------------------------------------------------------sbool_op_e
1495  enum sbool_op_e
1496  {
1497  sbool_or, //----sbool_or
1498  sbool_and, //----sbool_and
1499  sbool_xor, //----sbool_xor
1500  sbool_xor_saddle, //----sbool_xor_saddle
1501  sbool_xor_abs_diff, //----sbool_xor_abs_diff
1502  sbool_a_minus_b, //----sbool_a_minus_b
1503  sbool_b_minus_a //----sbool_b_minus_a
1504  };
1505 
1506 
1507 
1508 
1509 
1510 
1511  //----------------------------------------------sbool_combine_shapes_bin
1512  template<class ScanlineGen1,
1513  class ScanlineGen2,
1514  class Scanline1,
1515  class Scanline2,
1516  class Scanline,
1517  class Renderer>
1518  void sbool_combine_shapes_bin(sbool_op_e op,
1519  ScanlineGen1& sg1, ScanlineGen2& sg2,
1520  Scanline1& sl1, Scanline2& sl2,
1521  Scanline& sl, Renderer& ren)
1522  {
1523  switch(op)
1524  {
1525  case sbool_or : sbool_unite_shapes_bin (sg1, sg2, sl1, sl2, sl, ren); break;
1526  case sbool_and : sbool_intersect_shapes_bin(sg1, sg2, sl1, sl2, sl, ren); break;
1527  case sbool_xor :
1528  case sbool_xor_saddle :
1529  case sbool_xor_abs_diff: sbool_xor_shapes_bin (sg1, sg2, sl1, sl2, sl, ren); break;
1530  case sbool_a_minus_b : sbool_subtract_shapes_bin (sg1, sg2, sl1, sl2, sl, ren); break;
1531  case sbool_b_minus_a : sbool_subtract_shapes_bin (sg2, sg1, sl2, sl1, sl, ren); break;
1532  }
1533  }
1534 
1535 
1536 
1537 
1538  //-----------------------------------------------sbool_combine_shapes_aa
1539  template<class ScanlineGen1,
1540  class ScanlineGen2,
1541  class Scanline1,
1542  class Scanline2,
1543  class Scanline,
1544  class Renderer>
1545  void sbool_combine_shapes_aa(sbool_op_e op,
1546  ScanlineGen1& sg1, ScanlineGen2& sg2,
1547  Scanline1& sl1, Scanline2& sl2,
1548  Scanline& sl, Renderer& ren)
1549  {
1550  switch(op)
1551  {
1552  case sbool_or : sbool_unite_shapes_aa (sg1, sg2, sl1, sl2, sl, ren); break;
1553  case sbool_and : sbool_intersect_shapes_aa (sg1, sg2, sl1, sl2, sl, ren); break;
1554  case sbool_xor : sbool_xor_shapes_aa (sg1, sg2, sl1, sl2, sl, ren); break;
1555  case sbool_xor_saddle : sbool_xor_shapes_saddle_aa (sg1, sg2, sl1, sl2, sl, ren); break;
1556  case sbool_xor_abs_diff: sbool_xor_shapes_abs_diff_aa(sg1, sg2, sl1, sl2, sl, ren); break;
1557  case sbool_a_minus_b : sbool_subtract_shapes_aa (sg1, sg2, sl1, sl2, sl, ren); break;
1558  case sbool_b_minus_a : sbool_subtract_shapes_aa (sg2, sg1, sl2, sl1, sl, ren); break;
1559  }
1560  }
1561 
1562 }
1563 
1564 
1565 #endif
1566 
Definition: agg_arc.cpp:24