iLab Neuromorphic Robotics Toolkit  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
GeometryImpl.H
Go to the documentation of this file.
1 /*! @file
2  @author Unknown
3  @copyright GNU Public License (GPL v3)
4  @section License
5  @verbatim
6  // ////////////////////////////////////////////////////////////////////////
7  // The iLab Neuromorphic Robotics Toolkit (NRT) //
8  // Copyright 2010-2012 by the University of Southern California (USC) //
9  // and the iLab at USC. //
10  // //
11  // iLab - University of Southern California //
12  // Hedco Neurociences Building, Room HNB-10 //
13  // Los Angeles, Ca 90089-2520 - USA //
14  // //
15  // See http://ilab.usc.edu for information about this project. //
16  // ////////////////////////////////////////////////////////////////////////
17  // This file is part of The iLab Neuromorphic Robotics Toolkit. //
18  // //
19  // The iLab Neuromorphic Robotics Toolkit is free software: you can //
20  // redistribute it and/or modify it under the terms of the GNU General //
21  // Public License as published by the Free Software Foundation, either //
22  // version 3 of the License, or (at your option) any later version. //
23  // //
24  // The iLab Neuromorphic Robotics Toolkit is distributed in the hope //
25  // that it will be useful, but WITHOUT ANY WARRANTY; without even the //
26  // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //
27  // PURPOSE. See the GNU General Public License for more details. //
28  // //
29  // You should have received a copy of the GNU General Public License //
30  // along with The iLab Neuromorphic Robotics Toolkit. If not, see //
31  // <http://www.gnu.org/licenses/>. //
32  // ////////////////////////////////////////////////////////////////////////
33  @endverbatim */
34 #include <algorithm>
35 #include <type_traits>
36 
37 namespace geometryimpl
38 {
39  // Determine whether or not a given color has an alpha channel of 0 (meaning we shouldn't even bother drawing the
40  // shape)
41  template<class T, template<typename> class ColorT>
42  typename std::enable_if<std::is_base_of<PixelWithAlpha<T>, ColorT<T>>::value, bool>::type
43  isNoOpColor(ColorT<T> const& color)
44  { if(color.alpha == 0) return true; return false; }
45 
46  template<class T, template<typename> class ColorT>
47  typename std::enable_if<!std::is_base_of<PixelWithAlpha<T>, ColorT<T>>::value, bool>::type
48  isNoOpColor(ColorT<T> const& color)
49  { return false; }
50 }
51 
52 // This helper function takes in a boolean pixelflag buffer that acts as a mask
53 // when drawing thick lines so that we don't mess up our alpha channels. It
54 // also speeds things up a bit by avoiding the extra pixel draws caused by the
55 // algorithm. In general, users should just call drawLine()
56 template<class T, template<typename> class PixDest,
57  class U, template<typename> class PixOutline>
58 void drawLineHelper(Image<PixDest<T>> & image, Line<int32> const& line, PixOutline<U> const& outlineColor,
59  uint thickness, bool * const pixelFlags)
60 {
61  // Clip the line so we don't waste time drawing outside of the image
62  Line<int32> cline = Rectangle<int32>(Point2D<int32>(thickness-1,thickness-1), image.dims()-(int32(thickness)-1)*2).clip(line);
63  if(cline == Line<int32>()) return;
64  PixDest<T> *arrayptr = image.begin();
65  PixelPainter<T, PixDest, U, PixOutline> painter(outlineColor);
66 
67  int32 x0 = cline.p1().x();
68  int32 y0 = cline.p1().y();
69  int32 x1 = cline.p2().x();
70  int32 y1 = cline.p2().y();
71 
72  int32 w = image.width();
73  int32 h = image.height();
74 
75  bool steep = abs(y1 - y0) > abs(x1 - x0);
76  if(steep)
77  {
78  std::swap(x0,y0);
79  std::swap(x1,y1);
80  }
81  if(x0 > x1)
82  {
83  std::swap(x0,x1);
84  std::swap(y0,y1);
85  }
86  int32 deltax = x1-x0;
87  int32 deltay = abs(y1 - y0);
88  int32 error = deltax / 2;
89  int32 ystep;
90  int32 y = y0;
91  if(y < y1) ystep = 1;
92  else ystep = -1;
93 
94  // Special case for 1px wide lines
95  if(thickness == 1)
96  {
97  if(steep)
98  {
99  for(int32 x=x0; x<=x1; x++)
100  {
101  size_t const index = x*w+y;
102  if (y > 0 && y < w && x > 0 && x < h)
103  {
104  painter.paint(arrayptr + index);
105  }
106  error = error - deltay;
107  if(error < 0)
108  {
109  y = y + ystep;
110  error = error + deltax;
111  }
112  }
113  }
114  else
115  {
116  for(int32 x=x0; x<=x1; x++)
117  {
118  size_t const index = y*w+x;
119  if (x > 0 && x < w && y > 0 && y < h)
120  {
121  painter.paint(arrayptr + index);
122  }
123  error = error - deltay;
124  if(error < 0)
125  {
126  y = y + ystep;
127  error = error + deltax;
128  }
129  }
130  }
131  return;
132  }
133 
134  for(int32 x=x0; x<=x1; x++)
135  {
136  int32 px=x, py=y;
137  if(steep) {px=y; py=x;}
138 
139  for (int32 yC = -int32(thickness); yC <= int32(thickness); ++yC)
140  {
141  int32 bound = int32(sqrtf(float(thickness*thickness - yC*yC)));
142  for (int32 xC = -bound; xC <= bound; ++xC)
143  {
144  const int32 x_ = xC+px;
145  const int32 y_ = yC+py;
146  const size_t index = y_*w+x_;
147 
148  if(x_ < w && y_ < h && x_ >= 0 && y_ >= 0 && !pixelFlags[index])
149  {
150  if ((int32)index > 0 && (int32)index < w*h )
151  {
152  painter.paint(arrayptr + index);
153  pixelFlags[index] = true;
154  }
155  }
156  }
157  }
158 
159  error = error - deltay;
160  if(error < 0)
161  {
162  y = y + ystep;
163  error = error + deltax;
164  }
165  }
166 }
167 
168 // Helper functions for Wu line drawing
169 namespace wu_internal
170 {
171  inline int32 ipart(double x) { return floor(x); }
172  inline int32 roundint(double x) { return ipart(x+0.5); }
173  inline double fpart(double x) { return x - ipart(x); }
174  inline double rfpart(double x) { return 1.0 - fpart(x); }
175 }
176 // ######################################################################
177 template<class T, class U>
178 inline void drawLineAA(Image<T> & image, Line<int32> const& line, U const& outlineColor)
179 {
180  float x1 = line.p1().x();
181  float y1 = line.p1().y();
182  float x2 = line.p2().x();
183  float y2 = line.p2().y();
184 
185  float dx = x2 - x1;
186  float dy = y2 - y1;
187  bool flipDraw = false;
188  if(abs(dx) < abs(dy))
189  {
190  flipDraw = true;
191  std::swap(x1,y1);
192  std::swap(x2,y2);
193  std::swap(dx,dy);
194  }
195  if(x2 < x1)
196  {
197  std::swap(x1,x2);
198  std::swap(y1,y2);
199  }
200  float gradient = dy / dx;
201 
202  // Handle first endpoint
203  float xend = floor(x1 + 0.5);
204  float yend = y1 + gradient * (xend - x1);
205  float xgap = wu_internal::rfpart(x1 + 0.5);
206  float xpxl1 = xend;
207  float ypxl1 = wu_internal::ipart(yend);
208  if(!flipDraw)
209  {
210  PixRGBA<byte> c1(outlineColor);
211  c1.setA(double(c1.a())*(wu_internal::rfpart (yend) * xgap));
212  image.draw(Point2D<int32>(xpxl1, ypxl1), c1);
213 
214  c1 = PixRGBA<byte>(outlineColor);
215  c1.setA(double(c1.a())*(wu_internal::fpart (yend) * xgap));
216  image.draw(Point2D<int32>(xpxl1, ypxl1 + 1), c1);
217  }
218  else
219  {
220  PixRGBA<byte> c1(outlineColor);
221  c1.setA(double(c1.a())*(wu_internal::rfpart (yend) * xgap));
222  image.draw(Point2D<int32>(ypxl1, xpxl1), c1);
223 
224  c1 = PixRGBA<byte>(outlineColor);
225  c1.setA(double(c1.a())*(wu_internal::fpart (yend) * xgap));
226  image.draw(Point2D<int32>(ypxl1 + 1, xpxl1),c1);
227  }
228  float intery = yend + gradient;
229 
230  // Handle second endpoint
231  xend = wu_internal::roundint(x2);
232  yend = y2 + gradient * (xend - x2);
233  xgap = wu_internal::fpart(x2 + 0.5);
234  float xpxl2 = xend;
235  float ypxl2 = wu_internal::ipart(yend);
236  if(!flipDraw)
237  {
238  PixRGBA<byte> c1(outlineColor);
239  c1.setA(double(c1.a())*(wu_internal::rfpart (yend) * xgap));
240  image.draw(Point2D<int32>(xpxl2, ypxl2), c1);
241 
242  c1 = PixRGBA<byte>(outlineColor);
243  c1.setA(double(c1.a())*(wu_internal::fpart (yend) * xgap));
244  image.draw(Point2D<int32>(xpxl2, ypxl2 + 1), c1);
245  }
246  else
247  {
248  PixRGBA<byte> c1(outlineColor);
249  c1.setA(double(c1.a())*(wu_internal::rfpart (yend) * xgap));
250  image.draw(Point2D<int32>(ypxl2, xpxl2), c1);
251 
252  c1 = PixRGBA<byte>(outlineColor);
253  c1.setA(double(c1.a())*(wu_internal::fpart (yend) * xgap));
254  image.draw(Point2D<int32>(ypxl2 + 1, xpxl2),c1);
255  }
256 
257  // Main loop
258  for(int32 x=xpxl1+1; x <=xpxl2-1; x++)
259  {
260  if(flipDraw)
261  {
262  PixRGBA<byte> c1(outlineColor);
263  c1.setA(double(c1.a())*wu_internal::rfpart(intery));
264  image.draw(Point2D<int32>(wu_internal::ipart(intery), x), c1);
265 
266  c1 = PixRGBA<byte>(outlineColor);
267  c1.setA(double(c1.a())*wu_internal::fpart(intery));
268  image.draw(Point2D<int32>(wu_internal::ipart(intery)+1, x), c1);
269  }
270  else
271  {
272  PixRGBA<byte> c1(outlineColor);
273  c1.setA(double(c1.a())*wu_internal::rfpart(intery));
274  image.draw(Point2D<int32>(x,wu_internal::ipart(intery)), c1);
275 
276  c1 = PixRGBA<byte>(outlineColor);
277  c1.setA(double(c1.a())*wu_internal::fpart(intery));
278  image.draw(Point2D<int32>(x,wu_internal::ipart(intery)+1), c1);
279  }
280  intery = intery + gradient;
281  }
282 }
283 
284 // ######################################################################
285 template<class T, template<typename> class SourceColorT,
286  class U, template<typename> class FillColorT>
287  void drawPolygonFill(Image<SourceColorT<T>> & image, Polygon<int32> const& polygon, FillColorT<U> const& fillColor, bool * const pixelFlags)
288 {
289  if(geometryimpl::isNoOpColor(fillColor)) return;
290  PixelPainter<T, SourceColorT, U, FillColorT> painter(fillColor);
291 
292  //! @todo The Image::ConvexPolyIter class will happily try to draw pixels outside of the image dims. Until this is
293  //fixed, we're just going to do some nasty clamping of the polygon points here.
294  std::vector<Point2D<int32>> clampedPoints = polygon.vertices();
295  for(Point2D<int32> & vertex : clampedPoints)
296  {
297  vertex.x() = nrt::clamped(vertex.x(), 0, image.width()-1);
298  vertex.y() = nrt::clamped(vertex.y(), 0, image.height()-1);
299  }
300  Polygon<int32> clampedPolygon(clampedPoints);
301 
302  typename nrt::Image<SourceColorT<T>>::convex_poly_iterator it = image.convex_poly_begin(clampedPolygon);
303  typename nrt::Image<SourceColorT<T>>::convex_poly_iterator end = image.convex_poly_end(clampedPolygon);
304  bool * flagsIt = pixelFlags;
305  for(; it!=end; it++, flagsIt++)
306  {
307  if(*flagsIt) continue;
308  painter.paint(&(*it));
309  *flagsIt = true;
310  }
311 }
312 
313 // ######################################################################
314 template<class T, template<typename> class SourceColorT,
315  class U, template<typename> class OutlineColorT,
316  class V, template<typename> class FillColorT>
317  void drawPolygon(Image<SourceColorT<T>> & image, Polygon<int32> const& polygon, OutlineColorT<U> const& outlineColor,
318  uint thickness, FillColorT<V> const& fillColor)
319 {
320  if(polygon.vertices().size() == 0) return;
321 
322  bool * pixelFlags = (bool*)malloc(image.size()*sizeof(bool));
323  memset(&pixelFlags[0], 0, sizeof(bool)*(image.size()));
324 
325  // Do a junky 'clipping' - just squash the vertices into the image. We should definitely implement a real polygon
326  // clipping algorithm
327  int const w = image.width();
328  int const h = image.width();
329  std::vector<Point2D<int32>> vertices = polygon.vertices();
330  for(Point2D<int32> & v : vertices)
331  {
332  v.x() = nrt::clamped(v.x(), 0, w);
333  v.y() = nrt::clamped(v.y(), 0, h);
334  }
335  Polygon<int32> clippedPoly(vertices);
336 
337  // Draw the polygon fill
338  drawPolygonFill(image, clippedPoly, fillColor, pixelFlags);
339 
340  // Draw the polygon outline
341  if(geometryimpl::isNoOpColor(outlineColor) == false)
342  {
343  for(size_t i=0; i<clippedPoly.vertices().size()-1; i++)
344  drawLineHelper(image,
345  Line<int32>(clippedPoly.vertices()[i], clippedPoly.vertices()[i+1]),
346  outlineColor, thickness, pixelFlags);
347  drawLineHelper(image,
348  Line<int32>(*(clippedPoly.vertices().end()-1), clippedPoly.vertices()[0]),
349  outlineColor, thickness, pixelFlags);
350  }
351 
352 
353  free(pixelFlags);
354 }
355 
356 // ######################################################################
357 template<class T, class U>
358 inline void drawPolygonAA(Image<T> & image, Polygon<int32> const& polygon, U const& outlineColor)
359 {
360  for(size_t i=0; i<polygon.vertices().size()-1; i++)
361  {
362  drawLineAA(image,
363  Line<int32>(polygon.vertices()[i], polygon.vertices()[i+1]), outlineColor);
364  }
365  drawLineAA(image,
366  Line<int32>(*(polygon.vertices().end()-1), polygon.vertices()[0]), outlineColor);
367 }
368 
369 // ######################################################################
370 template<class T, template<typename> class PixDest,
371  class U, template<typename> class PixOutline>
372 inline void drawLine(Image<PixDest<T>> & image, Line<int32> const& line, PixOutline<U> const& outlineColor,
373  uint thickness)
374 {
375  if(thickness > 1)
376  {
377  bool * pixelFlags = new bool[image.size()];
378  memset(&pixelFlags[0], 0, sizeof(bool)*(image.size()));
379  drawLineHelper(image, line, outlineColor, thickness, pixelFlags);
380  delete [] pixelFlags;
381  }
382  else
383  {
384  bool pixelFlags[0];
385  drawLineHelper(image, line, outlineColor, thickness, pixelFlags);
386  }
387 }
388 
389 // ######################################################################
390 template <class T, template<typename> class PixDest,
391  class U, template<typename> class PixOutline>
392 inline void drawDisk(Image<PixDest<T> >& dst, Circle<int32> const & circle, PixOutline<U> const& outlineColor)
393 {
394  bool pixelFlags[0];
395  drawDiskHelper(dst, circle, outlineColor, false, pixelFlags);
396 }
397 
398 // ######################################################################
399 template <class T, template<typename> class PixDest,
400  class U, template<typename> class PixOutline>
401 inline void drawDiskHelper(Image<PixDest<T> >& dst, Circle<int32> const & circle, PixOutline<U> const& outlineColor, bool checkOverlay, bool pixelFlags[])
402 {
403  assert(dst.size()>0);
404  const int32 w = dst.width();
405  PixelPainter<T, PixDest, U, PixOutline> painter(outlineColor);
406  PixDest<T> *arrptr = dst.begin();
407  const int32 offset = circle.center().y()*w+circle.center().x();
408  int32 radius = circle.radius();
409  if(checkOverlay)
410  {
411  if (radius == 1)
412  {
413  if (dst.coordsOk(circle.center()) && !pixelFlags[offset])
414  {
415  painter.paint(arrptr+offset);
416  pixelFlags[offset] = true;
417  }
418  return;
419  }
420  for (int32 y = -radius; y <= radius; ++y)
421  {
422  int32 bound = int32(sqrtf(float(radius*radius - y*y)));
423  for (int32 x = -bound; x <= bound; ++x)
424  {
425  int32 curOffset = offset + y*w+x;
426  if (dst.coordsOk(x + circle.center().x(), y + circle.center().y()) && !pixelFlags[curOffset])
427  {
428  painter.paint(arrptr+curOffset);
429  pixelFlags[curOffset] = true;
430  }
431  }
432  }
433  }
434  else
435  {
436  PixDest<T> *ctrptr = arrptr+offset;
437  if (radius == 1)
438  {
439  if (dst.coordsOk(circle.center())) painter.paint(ctrptr);
440  return;
441  }
442  for (int32 y = -radius; y <= radius; ++y)
443  {
444  int32 bound = int32(sqrtf(float(radius*radius - y*y)));
445  for (int32 x = -bound; x <= bound; ++x)
446  if (dst.coordsOk(x + circle.center().x(), y + circle.center().y()))
447  painter.paint(ctrptr + y*w+x);
448  }
449  }
450 }
451 
452 
453 // ######################################################################
454 template<class T, class U>
455 inline void drawCircle(Image<T> & image, Circle<int32> const& circle, U const& outlineColor, uint thickness)
456 {
457  Point2D<int32> p(circle.center());
458  Circle<int32> cc(p,thickness);
459  int32 radius = circle.radius();
460 
461  if (radius == 1)
462  {
463  if (image.coordsOk(circle.center()))
464  {
465  bool pixelFlags[0];
466  drawDiskHelper(image, cc, outlineColor, false, pixelFlags);
467  }
468  return;
469  }
470 
471  bool * pixelFlags = new bool[image.size()];
472  memset(&pixelFlags[0], 0, sizeof(bool)*(image.size()));
473 
474  nrt::Point2D<nrt::int32> offset(radius, 0);
475  drawDiskHelper(image, nrt::Circle<nrt::int32>(p - offset, thickness), outlineColor, true, pixelFlags);
476  drawDiskHelper(image, nrt::Circle<nrt::int32>(p + offset, thickness), outlineColor, true, pixelFlags);
477 
478  int32 bound1 = radius, bound2 = radius;
479  for (int y = 1; y <= radius; y++)
480  {
481  bound2 = bound1;
482  bound1 = int(sqrtf(float( (radius*radius) - (y*y) )));
483  for (int x = bound1; x <= bound2; x++)
484  {
485  drawDiskHelper(image, nrt::Circle<nrt::int32>(p + nrt::Point2D<nrt::int32>(-x, -y), thickness),
486  outlineColor, true, pixelFlags);
487  drawDiskHelper(image, nrt::Circle<nrt::int32>(p + nrt::Point2D<nrt::int32>( x, -y), thickness),
488  outlineColor, true, pixelFlags);
489  drawDiskHelper(image, nrt::Circle<nrt::int32>(p + nrt::Point2D<nrt::int32>( x, y), thickness),
490  outlineColor, true, pixelFlags);
491  drawDiskHelper(image, nrt::Circle<nrt::int32>(p + nrt::Point2D<nrt::int32>(-x, y), thickness),
492  outlineColor, true, pixelFlags);
493  }
494  }
495 
496  delete [] pixelFlags;
497 }
498 
499 // ######################################################################
500 template<class T, class U, class V>
501 inline void drawEllipse(Image<T> & image, Point2D<int32> const& center, const float radiusx, const float radiusy,
502  const float skew,
503  U const& outlineColor, uint thickness, V const& fillColor)
504 {
505 
506  //TODO: rewrite this and add the skew since this is faster
507  //int steps = 36;
508  //double a = double(M_PI*2.0)/double(steps);
509  //double sa = (double)sin(a);
510  //double ca = (double)cos(a);
511 
512  //double nx=1;
513  //double ny=0;
514  //Point2D<int32> lastPoint(center.x() + radiusx, center.y());
515  //for(int i=1; i<=steps; i++)
516  //{
517  // double tmp = ca*nx - sa*ny;
518  // ny = sa*nx + ca*ny;
519  // nx = tmp;
520  // Point2D<int32> newPoint(center.x()+(nx*radiusx),center.y()+(ny*radiusy));
521  // drawLine(image,
522  // Line<int32>(lastPoint, newPoint),
523  // outlineColor, thickness);
524  // lastPoint = newPoint;
525  //}
526 
527  uint steps = 32;
528  const double step = 2.0 * M_PI / double(steps);
529  float theta = 0.0;
530 
531  //Cache for speed up
532  double circX[steps];
533  double circY[steps];
534  for (uint i=0; i<steps; i++, theta += step)
535  {
536  circX[i] = sin(theta);
537  circY[i] = cos(theta);
538  }
539 
540  std::vector<Point2D<int32>> points;
541  for(uint i=0; i<steps; ++i)
542  points.push_back(Point2D<int32>(center.x() + radiusx*circX[i], center.y() + skew*circX[i] + radiusy*circY[i]));
543 
544  drawPolygon(image, Polygon<int32>::convexHull(points), outlineColor, thickness, fillColor);
545 }
546 
547 // ######################################################################
548 // drawRectangle and friends
549 // ######################################################################
550 
551 // ######################################################################
552 template<
553 class T, template<typename> class SourceColorT,
554  class V, template<typename> class FillColorT
555  >
556 inline void drawRectangleFill(Image<SourceColorT<T>> & image, Rectangle<int32> const& rect, FillColorT<V> const& fillColor, bool * const pixelFlags)
557 {
558  if(geometryimpl::isNoOpColor(fillColor)) return;
559  Rectangle<int32> clipRect = rect.intersected(image.bounds());
560  PixelPainter<T, SourceColorT, V, FillColorT> painter(fillColor);
561  typename nrt::Image<SourceColorT<T>>::rect_iterator it = image.rect_begin(clipRect);
562  typename nrt::Image<SourceColorT<T>>::rect_iterator end = image.rect_end(clipRect);
563  bool * flagsIt = pixelFlags;
564  for(; it!=end; it++, flagsIt++)
565  {
566  if(*flagsIt) continue;
567  painter.paint(&(*it));
568  *flagsIt = true;
569  }
570 }
571 
572 // ######################################################################
573 template<
574 class T, template<typename> class SourceColorT,
575  class U, template<typename> class OutlineColorT
576  >
577 inline void drawRectangleOutline(
578  Image<SourceColorT<T>> & image, Rectangle<int32> const& rect, uint thickness, OutlineColorT<U> const& outlineColor, bool * pixelFlags)
579 {
580  if(geometryimpl::isNoOpColor(outlineColor)) return;
581  drawLineHelper(image, Line<int32>(rect.topLeft(), rect.topRight()), outlineColor, thickness, pixelFlags);
582  drawLineHelper(image, Line<int32>(rect.topRight(), rect.bottomRight()), outlineColor, thickness, pixelFlags);
583  drawLineHelper(image, Line<int32>(rect.bottomRight(), rect.bottomLeft()), outlineColor, thickness, pixelFlags);
584  drawLineHelper(image, Line<int32>(rect.bottomLeft(), rect.topLeft()), outlineColor, thickness, pixelFlags);
585 }
586 
587 // ######################################################################
588 template<
589 class T, template<typename> class SourceColorT,
590  class U, template<typename> class OutlineColorT,
591  class V, template<typename> class FillColorT
592  >
593 inline void drawRectangle(Image<SourceColorT<T>> & image, Rectangle<int32> const& rect,
594  OutlineColorT<U> const& outlineColor, uint thickness,
595  FillColorT<V> const& fillColor)
596 {
597  bool * pixelFlags = (bool*)malloc(image.size()*sizeof(bool));
598  memset(pixelFlags, 0, sizeof(bool)*(image.size()));
599 
600  drawRectangleFill(image, rect, fillColor, pixelFlags);
601  drawRectangleOutline(image, rect, thickness, outlineColor, pixelFlags);
602 
603  free(pixelFlags);
604 }
605 
606 // ######################################################################
607 template<class T, class OutlineColorT>
608 inline void drawCross(Image<T> & image, Point2D<int32> const& center,
609  OutlineColorT const& outlineColor, uint32 radius, uint32 thickness, float angle)
610 {
611  bool * pixelFlags = new bool[image.size()];
612  memset(&pixelFlags[0], 0, sizeof(bool)*(image.size()));
613 
615  nrt::Point2D<int32>(center-nrt::Point2D<nrt::int32>(0, radius)));
616  drawLineHelper(image, nrt::rotate(vLine, angle), outlineColor, thickness, pixelFlags);
617 
620  drawLineHelper(image, nrt::rotate(hLine, angle), outlineColor, thickness, pixelFlags);
621 
622  delete [] pixelFlags;
623 }
624 
625 // ######################################################################
626 template<class T, class OutlineColorT>
627 inline void DrawArrow(Image<T> & image, int32 xs, int32 ys, int32 xe, int32 ye,
628  uint32 thickness, OutlineColorT const& outlineColor,
629  float headSizeRatio, float headAngle)
630 {
631  headAngle *= M_PI / 180.0f;
632 
633  // draw the main body
634  drawLine(image, Line<int32>(xs, ys, xe, ye), outlineColor, thickness);
635 
636  float const dx = xe - xs;
637  float const dy = ye - ys;
638 
639  float const len = sqrt( dx * dx + dy * dy );
640  float const head_size = headSizeRatio * len;
641 
642  // don't draw too small arrow head
643  if (head_size < 2.0f) return;
644 
645  float const angle = atan2(dy, dx) + M_PI;
646 
647  int32 const x3 = xe + head_size * cos(angle - headAngle);
648  int32 const y3 = ye + head_size * sin(angle - headAngle);
649  int32 const x4 = xe + head_size * cos(angle + headAngle);
650  int32 const y4 = ye + head_size * sin(angle + headAngle);
651 
652  drawLine(image, Line<int32>(xe, ye, x3, y3), outlineColor, thickness);
653  drawLine(image, Line<int32>(xe, ye, x4, y4), outlineColor, thickness);
654 }
655 
656 // ######################################################################
657 template<class T, class OutlineColorT>
658 inline void drawGrid( Image<T> & image, const int32 spacingX, const int32 spacingY,
659  const uint32 thickX, const uint32 thickY,
660  OutlineColorT const& outlineColor )
661 {
662  int32 xHigh = image.width() - 1;
663  int32 yHigh = image.height() - 1;
664 
665  // Draw vertical components
666  for( int32 xPos = thickX / 2; xPos < static_cast<int32>( image.width() - thickX / 2 ); xPos += spacingX )
667  drawLine( image, Line<int32>( xPos, 0, xPos, yHigh ), outlineColor, thickX );
668  // Draw horizontal components
669  for( int32 yPos = thickY / 2; yPos < static_cast<int32>( image.height() - thickY / 2 ); yPos += spacingY )
670  drawLine( image, Line<int32>( 0, yPos, xHigh, yPos ), outlineColor, thickY );
671 }
672 
673 // ######################################################################
674 template<class T, class OutlineColorT>
675 inline void drawGrid( Image<T> & image, const uint32 nx, const uint32 ny,
676  const uint32 thickness,
677  OutlineColorT const& outlineColor )
678 {
679  drawGrid( image, image.width() / nx, image.height() / ny, thickness, thickness, outlineColor );
680 }
681