1 //:
2 // \file
3 // \author Philip C. Pritchett, RRG, University of Oxford
4 // \date   24 Mar 1999
5 // \brief  See vgui_soview2D.h for a description of this file.
6 
7 #include <cmath>
8 #include <iostream>
9 #include "vgui_soview2D.h"
10 
11 #ifdef _MSC_VER
12 #  include "vcl_msvc_warnings.h"
13 #endif
14 
15 #include "vgl/vgl_distance.h"
16 #include "vnl/vnl_math.h" // for twopi
17 #include "vil/vil_image_view_base.h"
18 #include "vil/vil_new.h"
19 #include "vil1/vil1_image.h"
20 #include "vgui/vgui_range_map_params.h"
21 #include "vgui/vgui_gl.h"
22 #include "vgui/vgui_style.h"
23 #include "vgui/vgui_section_buffer.h"
24 #include <vgui/internals/vgui_draw_line.h>
25 
26 //--------------------------------------------------------------------------//
27 
28 std::ostream &
print(std::ostream & s) const29 vgui_soview2D_point::print(std::ostream & s) const
30 {
31   s << "[ vgui_soview2D_point " << x << ',' << y << ' ';
32   return vgui_soview2D::print(s) << " ]";
33 }
34 
35 void
draw() const36 vgui_soview2D_point::draw() const
37 {
38 #if 0
39   style->apply_point_size();
40 #endif // 0
41   glBegin(GL_POINTS);
42   glVertex2f(x, y);
43   glEnd();
44 }
45 
46 void
draw_select() const47 vgui_soview2D_point::draw_select() const
48 {
49   // It's much faster to draw a polygon than a point. (At least, it is
50   // on Win2000 OpenGL.) For selection, we just need to draw some
51   // little area in the vicinity of the point. We make the area really
52   // small to account for large zoom factors. In principle, we should
53   // take the zoom factor into account when determining the radius,
54   // but that's too much trouble. The radius should be large enough
55   // that it doesn't get swallowed up when added to x and to y.
56 
57   // This will allow a 10000x zoom before the "circle" gets bigger
58   // than one pixel. Should be good enough!
59   //
60   float const rad = 0.0001f;
61   glBegin(GL_POLYGON);
62   glVertex2f(x - rad, y - rad);
63   glVertex2f(x + rad, y - rad);
64   glVertex2f(x + rad, y + rad);
65   glVertex2f(x - rad, y + rad);
66   glEnd();
67 }
68 
69 float
distance_squared(float vx,float vy) const70 vgui_soview2D_point::distance_squared(float vx, float vy) const
71 {
72   float dx = x - vx;
73   float dy = y - vy;
74   return dx * dx + dy * dy;
75 }
76 
77 void
get_centroid(float * vx,float * vy) const78 vgui_soview2D_point::get_centroid(float * vx, float * vy) const
79 {
80   *vx = x;
81   *vy = y;
82 }
83 
84 void
translate(float tx,float ty)85 vgui_soview2D_point::translate(float tx, float ty)
86 {
87   x += tx;
88   y += ty;
89 }
90 
91 //--------------------------------------------------------------------------//
92 
93 std::ostream &
print(std::ostream & s) const94 vgui_soview2D_lineseg::print(std::ostream & s) const
95 {
96   s << "[ vgui_soview2D_lineseg " << x0 << ',' << y0 << " -- " << x1 << ',' << y1 << ' ';
97   return vgui_soview2D::print(s) << " ]";
98 }
99 
100 void
draw() const101 vgui_soview2D_lineseg::draw() const
102 {
103 #ifdef DEBUG
104   std::cerr << "vgui_soview2D_lineseg::draw() line id=" << id << '\n';
105 #endif
106 
107 #if 0
108   style->apply_line_width();
109 #endif // 0
110   glBegin(GL_LINES);
111   glVertex2f(x0, y0);
112   glVertex2f(x1, y1);
113   glEnd();
114 }
115 
116 float
distance_squared(float vx,float vy) const117 vgui_soview2D_lineseg::distance_squared(float vx, float vy) const
118 {
119   // Here we explicitly cast some parameters to type float to help
120   // the Borland compiler, which otherwise tries to use
121   // vgl_distance2_to_linesegment<const float>, presumably because
122   // this is a const member function so some of the parameters passed
123   // to vgl_distance2_to_linesegment are effectively of type const
124   // float.
125   return (float)vgl_distance2_to_linesegment(x0, y0, x1, y1, vx, vy);
126 }
127 
128 void
get_centroid(float * vx,float * vy) const129 vgui_soview2D_lineseg::get_centroid(float * vx, float * vy) const
130 {
131   *vx = (x0 + x1) / 2;
132   *vy = (y0 + y1) / 2;
133 }
134 
135 void
translate(float tx,float ty)136 vgui_soview2D_lineseg::translate(float tx, float ty)
137 {
138   x0 += tx;
139   y0 += ty;
140   x1 += tx;
141   y1 += ty;
142 }
143 
144 //--------------------------------------------------------------------------//
145 
~vgui_soview2D_group()146 vgui_soview2D_group::~vgui_soview2D_group()
147 {
148   for (unsigned int i = 0; i < ls.size(); i++)
149     if (ls[i])
150       delete ls[i];
151 
152   // clear vector
153   ls.clear();
154 }
155 
156 void
set_style(const vgui_style_sptr & s)157 vgui_soview2D_group::set_style(const vgui_style_sptr & s)
158 {
159   for (unsigned int i = 0; i < ls.size(); i++)
160     if (!ls[i]->get_style())
161       ls[i]->set_style(s);
162 
163   vgui_soview::set_style(s);
164 }
165 
166 std::ostream &
print(std::ostream & s) const167 vgui_soview2D_group::print(std::ostream & s) const
168 {
169   s << "[ vgui_soview2D_group ";
170 
171   for (unsigned int i = 0; i < ls.size(); i++)
172     ls[i]->print(s);
173 
174   return vgui_soview2D::print(s) << " ]";
175 }
176 
177 void
draw() const178 vgui_soview2D_group::draw() const
179 {
180   for (unsigned int i = 0; i < ls.size(); i++)
181     ls[i]->draw();
182 }
183 
184 void
draw_select() const185 vgui_soview2D_group::draw_select() const
186 {
187   for (unsigned int i = 0; i < ls.size(); i++)
188     ls[i]->draw_select();
189 }
190 
191 float
distance_squared(float vx,float vy) const192 vgui_soview2D_group::distance_squared(float vx, float vy) const
193 {
194   if (ls.size() == 0)
195     return -1e30f;
196 
197   float min = ls[0]->distance_squared(vx, vy);
198 
199   for (unsigned int i = 1; i < ls.size(); i++)
200   {
201     float d = ls[i]->distance_squared(vx, vy);
202     if (d < min)
203       min = d;
204   }
205 
206   return min;
207 }
208 
209 void
get_centroid(float * vx,float * vy) const210 vgui_soview2D_group::get_centroid(float * vx, float * vy) const
211 {
212   *vx = 0;
213   *vy = 0;
214   const int n = ls.size();
215 
216   for (int i = 0; i < n; i++)
217   {
218     float cx, cy;
219     ls[i]->get_centroid(&cx, &cy);
220     *vx += cx;
221     *vy += cy;
222   }
223 
224   float s = 1.0f / n;
225   *vx *= s;
226   *vy *= s;
227 }
228 
229 void
translate(float tx,float ty)230 vgui_soview2D_group::translate(float tx, float ty)
231 {
232   for (unsigned int i = 0; i < ls.size(); i++)
233     ls[i]->translate(tx, ty);
234 }
235 
236 //--------------------------------------------------------------------------//
237 
238 std::ostream &
print(std::ostream & s) const239 vgui_soview2D_infinite_line::print(std::ostream & s) const
240 {
241   s << "[ vgui_soview2D_infinite_line " << a << ',' << b << ',' << c << ' ';
242   return vgui_soview2D::print(s) << " ]";
243 }
244 
245 void
draw() const246 vgui_soview2D_infinite_line::draw() const
247 {
248   vgui_draw_line(a, b, c);
249 }
250 
251 float
distance_squared(float vx,float vy) const252 vgui_soview2D_infinite_line::distance_squared(float vx, float vy) const
253 {
254   float tmp = a * vx + b * vy + c;
255   return tmp * tmp / (a * a + b * b);
256 }
257 
258 void
get_centroid(float * vx,float * vy) const259 vgui_soview2D_infinite_line::get_centroid(float * vx, float * vy) const
260 {
261   *vx = 0;
262   *vy = 0;
263 }
264 
265 void
translate(float tx,float ty)266 vgui_soview2D_infinite_line::translate(float tx, float ty)
267 {
268   c += a * tx + b * ty;
269 }
270 
271 //--------------------------------------------------------------------------//
272 
273 constexpr int vgui__CIRCLE2D_LIST = 1;
274 
275 void
compile()276 vgui_soview2D_circle::compile()
277 {
278   glNewList(vgui__CIRCLE2D_LIST, GL_COMPILE);
279   glBegin(GL_LINE_LOOP);
280   for (unsigned int i = 0; i < 100; i++)
281   {
282     double angle = vnl_math::twopi * 0.01 * i;
283     glVertex2d(std::cos(angle), std::sin(angle));
284   }
285   glEnd();
286   glEndList();
287 }
288 
289 
290 std::ostream &
print(std::ostream & s) const291 vgui_soview2D_circle::print(std::ostream & s) const
292 {
293   s << "[ vgui_soview2D_circle " << x << ',' << y << " r" << r << ' ';
294   return vgui_soview2D::print(s) << " ]";
295 }
296 
297 void
draw() const298 vgui_soview2D_circle::draw() const
299 {
300   glBegin(GL_LINE_LOOP);
301   for (unsigned int i = 0; i < 100; i++)
302   {
303     double angle = vnl_math::twopi * 0.01 * i;
304     glVertex2d(x + r * std::cos(angle), y + r * std::sin(angle));
305   }
306   glEnd();
307 }
308 
309 float
distance_squared(float vx,float vy) const310 vgui_soview2D_circle::distance_squared(float vx, float vy) const
311 {
312   float dx = x - vx;
313   float dy = y - vy;
314 
315   // distance from point to centre
316   float dcentre = std::sqrt(dx * dx + dy * dy);
317 
318   // signed distance from point to circumference
319   float dcircum = dcentre - this->r;
320 
321   return dcircum * dcircum;
322 }
323 
324 void
get_centroid(float * vx,float * vy) const325 vgui_soview2D_circle::get_centroid(float * vx, float * vy) const
326 {
327   *vx = x;
328   *vy = y;
329 }
330 
331 void
translate(float tx,float ty)332 vgui_soview2D_circle::translate(float tx, float ty)
333 {
334   x += tx;
335   y += ty;
336 }
337 
338 //--------------------------------------------------------------------------------//
339 
340 std::ostream &
print(std::ostream & s) const341 vgui_soview2D_ellipse::print(std::ostream & s) const
342 {
343   s << "[ vgui_soview2D_ellipse " << x << ',' << y << " w" << w << " h" << h << " phi" << phi << ' ';
344   return vgui_soview2D::print(s) << " ]";
345 }
346 
347 void
draw() const348 vgui_soview2D_ellipse::draw() const
349 {
350   double px, py;
351 
352   glBegin(GL_LINE_LOOP);
353   for (unsigned int i = 0; i < 100; i++)
354   {
355     double angle = vnl_math::twopi * 0.01 * i;
356     px = w * std::cos(this->phi) * std::cos(angle) + h * std::sin(this->phi) * std::sin(angle);
357     py = h * std::cos(this->phi) * std::sin(angle) - w * std::sin(this->phi) * std::cos(angle);
358     glVertex2d(x + px, y + py);
359   }
360   glEnd();
361 }
362 
363 //:
364 // \todo not correctly implemented (assumes a circle)
365 float
distance_squared(float vx,float vy) const366 vgui_soview2D_ellipse::distance_squared(float vx, float vy) const
367 {
368   return (vx - x) * (vx - x) + (vy - y) * (vy - y);
369 }
370 
371 void
get_centroid(float * vx,float * vy) const372 vgui_soview2D_ellipse::get_centroid(float * vx, float * vy) const
373 {
374   *vx = x;
375   *vy = y;
376 }
377 
378 void
translate(float tx,float ty)379 vgui_soview2D_ellipse::translate(float tx, float ty)
380 {
381   x += tx;
382   y += ty;
383 }
384 
385 
386 //--------------------------------------------------------------------------------//
387 
vgui_soview2D_linestrip(unsigned n_,float const * x_,float const * y_)388 vgui_soview2D_linestrip::vgui_soview2D_linestrip(unsigned n_, float const * x_, float const * y_)
389   : n(n_)
390   , x(new float[n])
391   , y(new float[n])
392 {
393   for (unsigned i = 0; i < n; ++i)
394   {
395     x[i] = x_[i];
396     y[i] = y_[i];
397   }
398 }
399 
~vgui_soview2D_linestrip()400 vgui_soview2D_linestrip::~vgui_soview2D_linestrip()
401 {
402   n = 0;
403   delete[] x;
404   x = nullptr;
405   delete[] y;
406   y = nullptr;
407 }
408 
409 void
draw() const410 vgui_soview2D_linestrip::draw() const
411 {
412   glBegin(GL_LINE_STRIP);
413   for (unsigned i = 0; i < n; ++i)
414     glVertex2f(x[i], y[i]);
415   glEnd();
416 }
417 
418 std::ostream &
print(std::ostream & s) const419 vgui_soview2D_linestrip::print(std::ostream & s) const
420 {
421   return s << "[ a linestrip. FIXME ]";
422 }
423 
424 float
distance_squared(float vx,float vy) const425 vgui_soview2D_linestrip::distance_squared(float vx, float vy) const
426 {
427   double tmp = vgl_distance_to_non_closed_polygon(x, y, this->n, vx, vy);
428   return static_cast<float>(tmp * tmp);
429 }
430 
431 void
get_centroid(float * vx,float * vy) const432 vgui_soview2D_linestrip::get_centroid(float * vx, float * vy) const
433 {
434   *vx = 0;
435   *vy = 0;
436   for (unsigned i = 0; i < n; ++i)
437   {
438     *vx += x[i];
439     *vy += y[i];
440   }
441   float s = 1.0f / float(n);
442   *vx *= s;
443   *vy *= s;
444 }
445 
446 void
translate(float tx,float ty)447 vgui_soview2D_linestrip::translate(float tx, float ty)
448 {
449   for (unsigned i = 0; i < n; ++i)
450   {
451     x[i] += tx;
452     y[i] += ty;
453   }
454 }
455 
456 void
set_size(unsigned nn)457 vgui_soview2D_linestrip::set_size(unsigned nn)
458 {
459   if (nn < n)
460   {
461     n = nn;
462     return;
463   }
464 
465   // we know that n <= nn
466   float * nx = new float[nn];
467   float * ny = new float[nn];
468   for (unsigned i = 0; i < n; ++i)
469   {
470     nx[i] = x[i];
471     ny[i] = y[i];
472   }
473 
474   n = nn;
475   delete[] x;
476   x = nx;
477   delete[] y;
478   y = ny;
479 }
480 
481 //--------------------------------------------------------------------------------//
482 
vgui_soview2D_polygon(unsigned n_,float const * x_,float const * y_,bool fill)483 vgui_soview2D_polygon::vgui_soview2D_polygon(unsigned n_, float const * x_, float const * y_, bool fill)
484   : n(n_)
485   , x(new float[n])
486   , y(new float[n])
487   , filled(fill)
488 {
489   for (unsigned i = 0; i < n; ++i)
490   {
491     x[i] = x_[i];
492     y[i] = y_[i];
493   }
494 }
495 
~vgui_soview2D_polygon()496 vgui_soview2D_polygon::~vgui_soview2D_polygon()
497 {
498   n = 0;
499   delete[] x;
500   x = nullptr;
501   delete[] y;
502   y = nullptr;
503 }
504 
505 void
draw() const506 vgui_soview2D_polygon::draw() const
507 {
508   if (filled)
509   {
510     glBegin(GL_POLYGON);
511     for (unsigned i = 0; i < n; ++i)
512       glVertex2f(x[i], y[i]);
513     glEnd();
514   }
515   else
516   {
517     glBegin(GL_LINE_LOOP);
518     for (unsigned i = 0; i < n; ++i)
519       glVertex2f(x[i], y[i]);
520     glEnd();
521   }
522 }
523 
524 std::ostream &
print(std::ostream & s) const525 vgui_soview2D_polygon::print(std::ostream & s) const
526 {
527   return s << "[ a polygon. FIXME ]";
528 }
529 
530 float
distance_squared(float vx,float vy) const531 vgui_soview2D_polygon::distance_squared(float vx, float vy) const
532 {
533   double tmp = vgl_distance_to_closed_polygon(x, y, this->n, vx, vy);
534   return static_cast<float>(tmp * tmp);
535 }
536 
537 void
get_centroid(float * vx,float * vy) const538 vgui_soview2D_polygon::get_centroid(float * vx, float * vy) const
539 {
540   *vx = 0;
541   *vy = 0;
542   for (unsigned i = 0; i < n; ++i)
543   {
544     *vx += x[i];
545     *vy += y[i];
546   }
547   float s = 1.0f / float(n);
548   *vx *= s;
549   *vy *= s;
550 }
551 
552 void
translate(float tx,float ty)553 vgui_soview2D_polygon::translate(float tx, float ty)
554 {
555   for (unsigned i = 0; i < n; ++i)
556   {
557     x[i] += tx;
558     y[i] += ty;
559   }
560 }
561 
562 void
set_size(unsigned nn)563 vgui_soview2D_polygon::set_size(unsigned nn)
564 {
565   if (nn < n)
566   {
567     n = nn;
568     return;
569   }
570 
571   // we know that n <= nn
572   float * nx = new float[nn];
573   float * ny = new float[nn];
574   for (unsigned i = 0; i < n; ++i)
575   {
576     nx[i] = x[i];
577     ny[i] = y[i];
578   }
579 
580   n = nn;
581   delete[] x;
582   x = nx;
583   delete[] y;
584   y = ny;
585 }
586 
587 
588 //-----------------------------------------------------------
589 
vgui_soview2D_image(float in_x,float in_y,vil1_image const & img,bool in_blend,GLenum format,GLenum type)590 vgui_soview2D_image::vgui_soview2D_image(float in_x,
591                                          float in_y,
592                                          vil1_image const & img,
593                                          bool in_blend,
594                                          GLenum format,
595                                          GLenum type)
596   : x_(in_x)
597   , y_(in_y)
598   , w_(img.width())
599   , h_(img.height())
600   , blend_(in_blend)
601   , buffer_(new vgui_section_buffer(0, 0, w_, h_, format, type))
602 {
603   buffer_->apply(img, (vgui_range_map_params *)nullptr);
604 }
605 
vgui_soview2D_image(float in_x,float in_y,vil_image_view_base const & img,bool in_blend,GLenum format,GLenum type)606 vgui_soview2D_image::vgui_soview2D_image(float in_x,
607                                          float in_y,
608                                          vil_image_view_base const & img,
609                                          bool in_blend,
610                                          GLenum format,
611                                          GLenum type)
612   : x_(in_x)
613   , y_(in_y)
614   , w_(img.ni())
615   , h_(img.nj())
616   , blend_(in_blend)
617   , buffer_(new vgui_section_buffer(0, 0, w_, h_, format, type))
618 {
619   buffer_->apply(vil_new_image_resource_of_view(img), (vgui_range_map_params *)nullptr);
620 }
621 
~vgui_soview2D_image()622 vgui_soview2D_image::~vgui_soview2D_image()
623 {
624   delete buffer_;
625 }
626 
627 void
draw() const628 vgui_soview2D_image::draw() const
629 {
630   // Get the current blend state so we can restore it later
631   GLboolean blend_on;
632   glGetBooleanv(GL_BLEND, &blend_on);
633 
634   if (blend_)
635   {
636     glEnable(GL_BLEND);
637     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
638   }
639   else
640     glDisable(GL_BLEND);
641 
642   glTranslatef(x_, y_, 0.0f);
643   buffer_->draw_as_image() || buffer_->draw_as_rectangle();
644   glTranslatef(-x_, -y_, 0.0f);
645 
646   if (blend_on)
647     glEnable(GL_BLEND);
648   else
649     glDisable(GL_BLEND);
650 }
651 
652 std::ostream &
print(std::ostream & s) const653 vgui_soview2D_image::print(std::ostream & s) const
654 {
655   return s << "[ vgui_soview2D_image " << w_ << 'x' << h_ << ", blend=" << blend_ << " ]";
656 }
657 
658 float
distance_squared(float vx,float vy) const659 vgui_soview2D_image::distance_squared(float vx, float vy) const
660 {
661   float dx = (x_ + (w_ / 2.0f)) - vx;
662   float dy = (y_ + (h_ / 2.0f)) - vy;
663   return dx * dx + dy * dy;
664 }
665 
666 void
get_centroid(float * vx,float * vy) const667 vgui_soview2D_image::get_centroid(float * vx, float * vy) const
668 {
669   float x1 = x_ + (w_ / 2.0f);
670   float y1 = y_ + (h_ / 2.0f);
671 
672   *vx = x1;
673   *vy = y1;
674 }
675 
676 void
translate(float tx,float ty)677 vgui_soview2D_image::translate(float tx, float ty)
678 {
679   x_ += tx;
680   y_ += ty;
681 }
682