OpenCPN Partial API docs
Loading...
Searching...
No Matches
IsoLine.cpp
Go to the documentation of this file.
1/**********************************************************************
2zyGrib: meteorological GRIB file viewer
3Copyright (C) 2008 - Jacques Zaninetti - http://www.zygrib.org
4
5This program is free software: you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation, either version 3 of the License, or
8(at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program. If not, see <http://www.gnu.org/licenses/>.
17***********************************************************************/
23#include "pi_gl.h" // Must included before anything using GL stuff
24
25#include "wx/wxprec.h"
26
27#ifndef WX_PRECOMP
28#include "wx/wx.h"
29#endif // precompiled headers
30
31// #include "chcanv.h"
32// #include "model/georef.h"
33#include <wx/graphics.h>
34
35#include "IsoLine.h"
36#include "GribSettingsDialog.h"
37#include "GribOverlayFactory.h"
38
39#ifdef __OCPN__ANDROID__
40#include "qdebug.h"
41#endif
42
43// static void GenerateSpline(int n, wxPoint points[]);
44// static void ClearSplineList();
45wxList ocpn_wx_spline_point_list;
46
47#include <wx/listimpl.cpp>
48WX_DEFINE_LIST(MySegList);
49WX_DEFINE_LIST(MySegListList);
50
51#ifndef PI
52#define PI 3.14159
53#endif
54#define CTRUE -1
55#define CFALSE 0
56
57/* Local variables for cohen_sutherland_line_clip: */
59 double xmin, xmax, ymin, ymax;
60};
61void CompOutCode(double x, double y, outcode *code,
62 struct LOC_cohen_sutherland_line_clip *LINK) {
63 /*Compute outcode for the point (x,y) */
64 *code = 0;
65 if (y > LINK->ymax)
66 *code = 1L << ((long)TOP);
67 else if (y < LINK->ymin)
68 *code = 1L << ((long)BOTTOM);
69 if (x > LINK->xmax)
70 *code |= 1L << ((long)RIGHT);
71 else if (x < LINK->xmin)
72 *code |= 1L << ((long)LEFT);
73}
74
75ClipResult cohen_sutherland_line_clip_d(double *x0, double *y0, double *x1,
76 double *y1, double xmin_, double xmax_,
77 double ymin_, double ymax_) {
78 /* Cohen-Sutherland clipping algorithm for line P0=(x1,y0) to P1=(x1,y1)
79 * and clip rectangle with diagonal from (xmin,ymin) to (xmax,ymax).*/
81 int done = CFALSE;
82 ClipResult clip = Visible;
83 outcode outcode0, outcode1, outcodeOut;
84 /*Outcodes for P0,P1, and whichever point lies outside the clip rectangle*/
85 double x = 0., y = 0.;
86
87 V.xmin = xmin_;
88 V.xmax = xmax_;
89 V.ymin = ymin_;
90 V.ymax = ymax_;
91 CompOutCode(*x0, *y0, &outcode0, &V);
92 CompOutCode(*x1, *y1, &outcode1, &V);
93 do {
94 if (outcode0 == 0 && outcode1 == 0) { /*Trivial accept and exit*/
95 done = CTRUE;
96 } else if ((outcode0 & outcode1) != 0) {
97 clip = Invisible;
98 done = CTRUE;
99 }
100 /*Logical intersection is true, so trivial reject and exit.*/
101 else {
102 clip = Visible;
103 /*Failed both tests, so calculate the line segment to clip;
104 * from an outside point to an intersection with clip edge.*/
105 /*At least one endpoint is outside the clip rectangle; pick it.*/
106 if (outcode0 != 0)
107 outcodeOut = outcode0;
108 else
109 outcodeOut = outcode1;
110 /*Now find intersection point;
111 * use formulas y=y0+slope*(x-x0),x=x0+(1/slope)*(y-y0).*/
112
113 if (((1L << ((long)TOP)) & outcodeOut) != 0) {
114 /*Divide line at top of clip rectangle*/
115 x = *x0 + (*x1 - *x0) * (V.ymax - *y0) / (*y1 - *y0);
116 y = V.ymax;
117 } else if (((1L << ((long)BOTTOM)) & outcodeOut) != 0) {
118 /*Divide line at bottom of clip rectangle*/
119 x = *x0 + (*x1 - *x0) * (V.ymin - *y0) / (*y1 - *y0);
120 y = V.ymin;
121 } else if (((1L << ((long)RIGHT)) & outcodeOut) != 0) {
122 /*Divide line at right edge of clip rectangle*/
123 y = *y0 + (*y1 - *y0) * (V.xmax - *x0) / (*x1 - *x0);
124 x = V.xmax;
125 } else if (((1L << ((long)LEFT)) & outcodeOut) != 0) {
126 /*Divide line at left edge of clip rectangle*/
127 y = *y0 + (*y1 - *y0) * (V.xmin - *x0) / (*x1 - *x0);
128 x = V.xmin;
129 }
130 /*Now we move outside point to intersection point to clip,
131 * and get ready for next pass.*/
132 if (outcodeOut == outcode0) {
133 *x0 = x;
134 *y0 = y;
135 CompOutCode(*x0, *y0, &outcode0, &V);
136 } else {
137 *x1 = x;
138 *y1 = y;
139 CompOutCode(*x1, *y1, &outcode1, &V);
140 }
141 }
142 } while (!done);
143 return clip;
144}
145
146ClipResult cohen_sutherland_line_clip_i(int *x0_, int *y0_, int *x1_, int *y1_,
147 int xmin_, int xmax_, int ymin_,
148 int ymax_) {
149 ClipResult ret;
150 double x0, y0, x1, y1;
151 x0 = *x0_;
152 y0 = *y0_;
153 x1 = *x1_;
154 y1 = *y1_;
155 ret =
156 cohen_sutherland_line_clip_d(&x0, &y0, &x1, &y1, (double)xmin_,
157 (double)xmax_, (double)ymin_, (double)ymax_);
158 *x0_ = (int)x0;
159 *y0_ = (int)y0;
160 *x1_ = (int)x1;
161 *y1_ = (int)y1;
162 return ret;
163}
164
165double round_msvc(double x) { return (floor(x + 0.5)); }
166
167//---------------------------------------------------------------
168IsoLine::IsoLine(double val, double coeff, double offset,
169 const GribRecord *rec_) {
170 if (wxGetDisplaySize().x > 0) {
171 m_pixelMM = PlugInGetDisplaySizeMM() / wxGetDisplaySize().x;
172 m_pixelMM = wxMax(.02, m_pixelMM); // protect against bad data
173 } else
174 m_pixelMM = 0.27; // semi-standard number...
175
176 value = val / coeff - offset;
177
178 rec = rec_;
179 W = rec_->getNi();
180 H = rec_->getNj();
181
182 //---------------------------------------------------------
183 // Génère la liste des segments.
184 extractIsoLine(rec_);
185
186 value = val;
187
188 if (trace.size() == 0) return;
189
190 // Join the isoline segments into a nice list
191 // Which is end-to-end continuous and unidirectional
192
193 // Create a master wxList of the trace list
194 std::list<Segment *>::iterator it;
195 for (it = trace.begin(); it != trace.end(); it++) {
196 Segment *seg = *it;
197 seg->bUsed = false;
198 m_seglist.Append(*it);
199 }
200
201 // Isoline may be discontinuous....
202 // So build a list of continuous segments
203 bool bdone = false;
204 while (!bdone) {
205 MySegList *ps = BuildContinuousSegment();
206
207 m_SegListList.Append(ps);
208
209 MySegList::Node *node;
210 Segment *seg;
211
212 // recreate the master list, removing used segs
213
214 node = m_seglist.GetFirst();
215 while (node) {
216 seg = node->GetData();
217 if (seg->bUsed) {
218 m_seglist.Erase(node);
219 node = m_seglist.GetFirst();
220 } else
221 node = node->GetNext();
222 }
223
224 if (0 == m_seglist.GetCount()) bdone = true;
225 }
226
229}
230//---------------------------------------------------------------
231IsoLine::~IsoLine() {
232 // printf("delete Isobar : press=%4.0f long=%d\n", pressure/100,
233 // trace.size());
234
235 std::list<Segment *>::iterator it;
236 for (it = trace.begin(); it != trace.end(); it++) {
237 delete *it;
238 *it = nullptr;
239 }
240 trace.clear();
241
242 m_SegListList.DeleteContents(true);
243 m_SegListList.Clear();
244}
245
246MySegList *IsoLine::BuildContinuousSegment(void) {
247 MySegList::Node *node;
248 Segment *seg;
249
250 MySegList *ret_list = new MySegList;
251
252 // Build a chain extending from the "2" end of the target segment
253 // The joined list, side 2...
254 MySegList segjoin2;
255
256 // Add any first segment to the list
257 node = m_seglist.GetFirst();
258 Segment *seg0 = node->GetData();
259 seg0->bUsed = true;
260 segjoin2.Append(seg0);
261
262 Segment *tseg = seg0;
263
264 while (tseg) {
265 bool badded = false;
266 Segment *seg;
267 node = m_seglist.GetFirst();
268 while (node) {
269 seg = node->GetData();
270
271 if ((!seg->bUsed) && (seg->py1 == tseg->py2) &&
272 (seg->px1 == tseg->px2)) // fits without reverse
273 {
274 seg->bUsed = true;
275 segjoin2.Append(seg);
276 badded = true;
277 break;
278 } else if ((!seg->bUsed) && (seg->py2 == tseg->py2) &&
279 (seg->px2 == tseg->px2)) // fits, needs reverse
280 {
281 seg->bUsed = true;
282 double a = seg->px2;
283 seg->px2 = seg->px1;
284 seg->px1 = a;
285 double b = seg->py2;
286 seg->py2 = seg->py1;
287 seg->py1 = b;
288 segjoin2.Append(seg);
289 badded = true;
290 break;
291 }
292
293 node = node->GetNext();
294 }
295 if (badded == true)
296 tseg = seg;
297 else
298 tseg = nullptr;
299 }
300
301 // Build a chain extending from the "1" end of the target segment
302 // The joined list, side 1...
303 MySegList segjoin1;
304
305 // Add the same first segment to the list
306 node = m_seglist.GetFirst();
307 seg0 = node->GetData();
308 seg0->bUsed = true;
309 segjoin1.Append(seg0);
310
311 tseg = seg0;
312
313 while (tseg) {
314 bool badded = false;
315 node = m_seglist.GetFirst();
316 while (node) {
317 seg = node->GetData();
318
319 if ((!seg->bUsed) && (seg->py2 == tseg->py1) &&
320 (seg->px2 == tseg->px1)) // fits without reverse
321 {
322 seg->bUsed = true;
323 segjoin1.Append(seg);
324 badded = true;
325 break;
326 } else if ((!seg->bUsed) && (seg->py1 == tseg->py1) &&
327 (seg->px1 == tseg->px1)) // fits, needs reverse
328 {
329 seg->bUsed = true;
330 double a = seg->px2;
331 seg->px2 = seg->px1;
332 seg->px1 = a;
333 double b = seg->py2;
334 seg->py2 = seg->py1;
335 seg->py1 = b;
336 segjoin1.Append(seg);
337 badded = true;
338 break;
339 }
340
341 node = node->GetNext();
342 }
343 if (badded == true)
344 tseg = seg;
345 else
346 tseg = nullptr;
347 }
348
349 // Now have two lists...
350
351 // Start with "1" side list,
352 // starting from the end, and skipping the first segment
353
354 int n1 = segjoin1.GetCount();
355 for (int i = n1 - 1; i > 0; i--) {
356 node = segjoin1.Item(i);
357 seg = node->GetData();
358 ret_list->Append(seg);
359 }
360
361 // Now add the "2"side list
362 int n2 = segjoin2.GetCount();
363 for (int i = 0; i < n2; i++) {
364 node = segjoin2.Item(i);
365 seg = node->GetData();
366 ret_list->Append(seg);
367 }
368
369 // And there it is
370
371 return ret_list;
372}
373
374//---------------------------------------------------------------
375void IsoLine::drawIsoLine(GRIBOverlayFactory *pof, wxDC *dc,
376 PlugIn_ViewPort *vp, bool bHiDef) {
377 int nsegs = trace.size();
378 if (nsegs < 1) return;
379
380 GetGlobalColor(_T ( "UITX1" ), &isoLineColor);
381
382#if wxUSE_GRAPHICS_CONTEXT
383 wxGraphicsContext *pgc = nullptr;
384#endif
385
386 if (dc) {
387 wxPen ppISO(isoLineColor, 2);
388
389#if wxUSE_GRAPHICS_CONTEXT
390 wxMemoryDC *pmdc;
391 pmdc = dynamic_cast<wxMemoryDC *>(dc);
392 pgc = wxGraphicsContext::Create(*pmdc);
393 pgc->SetPen(ppISO);
394#endif
395 dc->SetPen(ppISO);
396 } else { /* opengl */
397#ifdef ocpnUSE_GL
398 // #ifndef USE_ANDROID_GLES2
399 // if(m_pixelMM > 0.2){ // pixel size large enough to
400 // render well
401 // // Enable anti-aliased lines, at best quality
402 // glEnable( GL_LINE_SMOOTH );
403 // glEnable( GL_BLEND );
404 // glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
405 // glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
406 // glLineWidth( 2 );
407 // }
408 // else{
409 // glLineWidth( 0.4/m_pixelMM); // set a target line
410 // width by MM
411 // }
412 // #else
413 if (pof->m_oDC) {
414 wxPen ppISO(isoLineColor, 2);
415 pof->m_oDC->SetPen(ppISO);
416 }
417#endif
418 }
419
420 std::list<Segment *>::iterator it;
421
422 //---------------------------------------------------------
423 // Dessine les segments
424 //---------------------------------------------------------
425 for (it = trace.begin(); it != trace.end(); it++) {
426 Segment *seg = *it;
427
430 /* skip segments that go the wrong way around the world */
431 double sx1 = seg->px1, sx2 = seg->px2;
432 if (sx2 - sx1 > 180)
433 sx2 -= 360;
434 else if (sx1 - sx2 > 180)
435 sx1 -= 360;
436
437 if ((sx1 + 180 < vp->clon && sx2 + 180 > vp->clon) ||
438 (sx1 + 180 > vp->clon && sx2 + 180 < vp->clon) ||
439 (sx1 - 180 < vp->clon && sx2 - 180 > vp->clon) ||
440 (sx1 - 180 > vp->clon && sx2 - 180 < vp->clon))
441 continue;
442 }
443
444 wxPoint ab;
445 GetCanvasPixLL(vp, &ab, seg->py1, seg->px1);
446 wxPoint cd;
447 GetCanvasPixLL(vp, &cd, seg->py2, seg->px2);
448
449 if (dc) {
450#if wxUSE_GRAPHICS_CONTEXT
451 if (bHiDef && pgc)
452 pgc->StrokeLine(ab.x, ab.y, cd.x, cd.y);
453 else
454#endif
455 dc->DrawLine(ab.x, ab.y, cd.x, cd.y);
456 } else { /* opengl */
457#ifdef ocpnUSE_GL
458
459 if (pof->m_oDC) {
460 pof->m_oDC->DrawLine(ab.x, ab.y, cd.x, cd.y);
461 }
462
463#endif
464 }
465 }
466
467#if wxUSE_GRAPHICS_CONTEXT
468 delete pgc;
469#endif
470
471 // if(!dc) /* opengl */
472 // glEnd();
473}
474
475//---------------------------------------------------------------
476
477void IsoLine::drawIsoLineLabels(GRIBOverlayFactory *pof, wxDC *dc,
478 PlugIn_ViewPort *vp, int density, int first,
479 wxImage &imageLabel)
480
481{
482 std::list<Segment *>::iterator it;
483 int nb = first;
484 wxString label;
485
486 //---------------------------------------------------------
487 // Ecrit les labels
488 //---------------------------------------------------------
489 wxRect prev;
490 for (it = trace.begin(); it != trace.end(); it++, nb++) {
491 if (nb % density == 0) {
492 Segment *seg = *it;
493
494 // if(vp->vpBBox.PointInBox((seg->px1 + seg->px2)/2., (seg->py1
495 // + seg->py2)/2., 0.))
496 {
497 wxPoint ab;
498 GetCanvasPixLL(vp, &ab, seg->py1, seg->px1);
499 wxPoint cd;
500 GetCanvasPixLL(vp, &cd, seg->py1, seg->px1);
501
502 int w = imageLabel.GetWidth();
503 int h = imageLabel.GetHeight();
504
505 int label_offset = 6;
506 int xd = (ab.x + cd.x - (w + label_offset * 2)) / 2;
507 int yd = (ab.y + cd.y - h) / 2;
508
509 int x = xd - label_offset;
510 wxRect r(x, yd, w, h);
511 r.Inflate(w);
512 if (!prev.Intersects(r)) {
513 prev = r;
514
515 /* don't use alpha for isobars, for some reason draw bitmap ignores
516 the 4th argument (true or false has same result) */
517 wxImage img(w, h, imageLabel.GetData(), true);
518 dc->DrawBitmap(img, xd, yd, false);
519 }
520 }
521 }
522 }
523}
524
525void IsoLine::drawIsoLineLabelsGL(GRIBOverlayFactory *pof, PlugIn_ViewPort *vp,
526 int density, int first, wxString label,
527 wxColour &color, TexFont &texfont)
528
529{
530 std::list<Segment *>::iterator it;
531 int nb = first;
532
533#ifdef ocpnUSE_GL
534 glEnable(GL_BLEND);
535 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
536
537 //---------------------------------------------------------
538 // Ecrit les labels
539 //---------------------------------------------------------
540 wxRect prev;
541 for (it = trace.begin(); it != trace.end(); it++, nb++) {
542 if (nb % density == 0) {
543 Segment *seg = *it;
544
545 // if(vp->vpBBox.PointInBox((seg->px1 + seg->px2)/2., (seg->py1
546 // + seg->py2)/2., 0.))
547 {
548 wxPoint ab;
549 GetCanvasPixLL(vp, &ab, seg->py1, seg->px1);
550 wxPoint cd;
551 GetCanvasPixLL(vp, &cd, seg->py1, seg->px1);
552
553 int w, h;
554 texfont.GetTextExtent(label, &w, &h);
555
556 int label_offsetx = 6, label_offsety = 1;
557 int xd = (ab.x + cd.x - (w + label_offsetx * 2)) / 2;
558 int yd = (ab.y + cd.y - h) / 2;
559 int x = xd - label_offsetx, y = yd - label_offsety;
560 w += 2 * label_offsetx, h += 2 * label_offsety;
561
562 wxRect r(x, y, w, h);
563 r.Inflate(w);
564 if (!prev.Intersects(r)) {
565#if 1
566 prev = r;
567 if (pof->m_oDC) {
568 // pof->m_oDC->SetFont( *mfont );
569 pof->m_oDC->SetPen(*wxBLACK_PEN);
570 pof->m_oDC->SetBrush(color);
571 pof->m_oDC->DrawRectangle(x, y, w, h);
572 pof->m_oDC->DrawText(label, xd, yd);
573 }
574
575#else
576 prev = r;
577 glColor4ub(color.Red(), color.Green(), color.Blue(), color.Alpha());
578
579 /* draw bounding rectangle */
580 glBegin(GL_QUADS);
581 glVertex2i(x, y);
582 glVertex2i(x + w, y);
583 glVertex2i(x + w, y + h);
584 glVertex2i(x, y + h);
585 glEnd();
586
587 glColor3ub(0, 0, 0);
588
589 glBegin(GL_LINE_LOOP);
590 glVertex2i(x, y);
591 glVertex2i(x + w, y);
592 glVertex2i(x + w, y + h);
593 glVertex2i(x, y + h);
594 glEnd();
595
596 glEnable(GL_TEXTURE_2D);
597 texfont.RenderString(label, xd, yd);
598 glDisable(GL_TEXTURE_2D);
599#endif
600 }
601 }
602 }
603 }
604 glDisable(GL_BLEND);
605#endif
606}
607
608//==================================================================================
609// Segment
610//==================================================================================
611Segment::Segment(int I, int w, int J, char c1, char c2, char c3, char c4,
612 const GribRecord *rec, double pressure) {
613 traduitCode(I, w, J, c1, i, j);
614 traduitCode(I, w, J, c2, k, l);
615 traduitCode(I, w, J, c3, m, n);
616 traduitCode(I, w, J, c4, o, p);
617
618 intersectionAreteGrille(i, j, k, l, &px1, &py1, rec, pressure);
619 intersectionAreteGrille(m, n, o, p, &px2, &py2, rec, pressure);
620}
621//-----------------------------------------------------------------------
622void Segment::intersectionAreteGrille(int i, int j, int k, int l, double *x,
623 double *y, const GribRecord *rec,
624 double pressure) {
625 double xa, xb, ya, yb, pa, pb, dec;
626 pa = rec->getValue(i, j);
627 pb = rec->getValue(k, l);
628
629 rec->getXY(i, j, &xa, &ya);
630 rec->getXY(k, l, &xb, &yb);
631
632 // Abscisse
633 if (pb != pa)
634 dec = (pressure - pa) / (pb - pa);
635 else
636 dec = 0.5;
637 if (fabs(dec) > 1) dec = 0.5;
638 double xd = xb - xa;
639 if (xd < -180)
640 xd += 360;
641 else if (xd > 180)
642 xd -= 360;
643 *x = xa + xd * dec;
644
645 // Ordonnée
646 if (pb != pa)
647 dec = (pressure - pa) / (pb - pa);
648 else
649 dec = 0.5;
650 if (fabs(dec) > 1) dec = 0.5;
651 *y = ya + (yb - ya) * dec;
652}
653//---------------------------------------------------------------
654void Segment::traduitCode(int I, int w, int J, char c1, int &i, int &j) {
655 int Im1 = I ? I - 1 : w - 1;
656 switch (c1) {
657 case 'a':
658 i = Im1;
659 j = J - 1;
660 break;
661 case 'b':
662 i = I;
663 j = J - 1;
664 break;
665 case 'c':
666 i = Im1;
667 j = J;
668 break;
669 case 'd':
670 i = I;
671 j = J;
672 break;
673 default:
674 i = I;
675 j = J;
676 }
677}
678
679//-----------------------------------------------------------------------
680// Génère la liste des segments.
681// Les coordonnées sont les indices dans la grille du GribRecord
682//---------------------------------------------------------
683void IsoLine::extractIsoLine(const GribRecord *rec) {
684 int i, j, W, H;
685 double a, b, c, d;
686 W = rec->getNi();
687 H = rec->getNj();
688
689 int We = W;
690 if (rec->getLonMax() + rec->getDi() - rec->getLonMin() == 360) We++;
691
692 for (j = 1; j < H; j++) // !!!! 1 to end
693 {
694 a = rec->getValue(0, j - 1);
695 c = rec->getValue(0, j);
696 for (i = 1; i < We; i++, a = b, c = d) {
697 // x = rec->getX(i);
698 // y = rec->getY(j);
699
700 int ni = i;
701 if (i == W) ni = 0;
702 b = rec->getValue(ni, j - 1);
703 d = rec->getValue(ni, j);
704
705 if (a == GRIB_NOTDEF || b == GRIB_NOTDEF || c == GRIB_NOTDEF ||
706 d == GRIB_NOTDEF)
707 continue;
708
709 if ((a < value && b < value && c < value && d < value) ||
710 (a > value && b > value && c > value && d > value))
711 continue;
712
713 // Détermine si 1 ou 2 segments traversent la case ab-cd
714 // a b
715 // c d
716 //--------------------------------
717 // 1 segment en diagonale
718 //--------------------------------
719 if ((a <= value && b <= value && c <= value && d > value) ||
720 (a > value && b > value && c > value && d <= value))
721 trace.push_back(new Segment(ni, W, j, 'c', 'd', 'b', 'd', rec, value));
722 else if ((a <= value && c <= value && d <= value && b > value) ||
723 (a > value && c > value && d > value && b <= value))
724 trace.push_back(new Segment(ni, W, j, 'a', 'b', 'b', 'd', rec, value));
725 else if ((c <= value && d <= value && b <= value && a > value) ||
726 (c > value && d > value && b > value && a <= value))
727 trace.push_back(new Segment(ni, W, j, 'a', 'b', 'a', 'c', rec, value));
728 else if ((a <= value && b <= value && d <= value && c > value) ||
729 (a > value && b > value && d > value && c <= value))
730 trace.push_back(new Segment(ni, W, j, 'a', 'c', 'c', 'd', rec, value));
731 //--------------------------------
732 // 1 segment H ou V
733 //--------------------------------
734 else if ((a <= value && b <= value && c > value && d > value) ||
735 (a > value && b > value && c <= value && d <= value))
736 trace.push_back(new Segment(ni, W, j, 'a', 'c', 'b', 'd', rec, value));
737 else if ((a <= value && c <= value && b > value && d > value) ||
738 (a > value && c > value && b <= value && d <= value))
739 trace.push_back(new Segment(ni, W, j, 'a', 'b', 'c', 'd', rec, value));
740 //--------------------------------
741 // 2 segments en diagonale
742 //--------------------------------
743 else if (a <= value && d <= value && c > value && b > value) {
744 trace.push_back(new Segment(ni, W, j, 'a', 'b', 'b', 'd', rec, value));
745 trace.push_back(new Segment(ni, W, j, 'a', 'c', 'c', 'd', rec, value));
746 } else if (a > value && d > value && c <= value && b <= value) {
747 trace.push_back(new Segment(ni, W, j, 'a', 'b', 'a', 'c', rec, value));
748 trace.push_back(new Segment(ni, W, j, 'b', 'd', 'c', 'd', rec, value));
749 }
750 }
751 }
752}
753
754// ----------------------------------------------------------------------------
755// splines code lifted from wxWidgets
756// ----------------------------------------------------------------------------
757
758// ----------------------------------- spline code
759// ----------------------------------------
760
761void ocpn_wx_quadratic_spline(double a1, double b1, double a2, double b2,
762 double a3, double b3, double a4, double b4);
763void ocpn_wx_clear_stack();
764int ocpn_wx_spline_pop(double *x1, double *y1, double *x2, double *y2,
765 double *x3, double *y3, double *x4, double *y4);
766void ocpn_wx_spline_push(double x1, double y1, double x2, double y2, double x3,
767 double y3, double x4, double y4);
768static bool ocpn_wx_spline_add_point(double x, double y);
769
770#define half(z1, z2) ((z1 + z2) / 2.0)
771#define THRESHOLD 5
772
773/* iterative version */
774
775void ocpn_wx_quadratic_spline(double a1, double b1, double a2, double b2,
776 double a3, double b3, double a4, double b4) {
777 double xmid, ymid;
778 double x1, y1, x2, y2, x3, y3, x4, y4;
779
780 ocpn_wx_clear_stack();
781 ocpn_wx_spline_push(a1, b1, a2, b2, a3, b3, a4, b4);
782
783 while (ocpn_wx_spline_pop(&x1, &y1, &x2, &y2, &x3, &y3, &x4, &y4)) {
784 xmid = (double)half(x2, x3);
785 ymid = (double)half(y2, y3);
786 if (fabs(x1 - xmid) < THRESHOLD && fabs(y1 - ymid) < THRESHOLD &&
787 fabs(xmid - x4) < THRESHOLD && fabs(ymid - y4) < THRESHOLD) {
788 ocpn_wx_spline_add_point(x1, y1);
789 ocpn_wx_spline_add_point(xmid, ymid);
790 } else {
791 ocpn_wx_spline_push(xmid, ymid, (double)half(xmid, x3),
792 (double)half(ymid, y3), (double)half(x3, x4),
793 (double)half(y3, y4), x4, y4);
794 ocpn_wx_spline_push(x1, y1, (double)half(x1, x2), (double)half(y1, y2),
795 (double)half(x2, xmid), (double)half(y2, ymid), xmid,
796 ymid);
797 }
798 }
799}
800
801/* utilities used by spline drawing routines */
802
804 double x1, y1, x2, y2, x3, y3, x4, y4;
805} Stack;
806
807#define SPLINE_STACK_DEPTH 20
808static Stack ocpn_wx_spline_stack[SPLINE_STACK_DEPTH];
809static Stack *ocpn_wx_stack_top;
810static int ocpn_wx_stack_count;
811
812void ocpn_wx_clear_stack() {
813 ocpn_wx_stack_top = ocpn_wx_spline_stack;
814 ocpn_wx_stack_count = 0;
815}
816
817void ocpn_wx_spline_push(double x1, double y1, double x2, double y2, double x3,
818 double y3, double x4, double y4) {
819 ocpn_wx_stack_top->x1 = x1;
820 ocpn_wx_stack_top->y1 = y1;
821 ocpn_wx_stack_top->x2 = x2;
822 ocpn_wx_stack_top->y2 = y2;
823 ocpn_wx_stack_top->x3 = x3;
824 ocpn_wx_stack_top->y3 = y3;
825 ocpn_wx_stack_top->x4 = x4;
826 ocpn_wx_stack_top->y4 = y4;
827 ocpn_wx_stack_top++;
828 ocpn_wx_stack_count++;
829}
830
831int ocpn_wx_spline_pop(double *x1, double *y1, double *x2, double *y2,
832 double *x3, double *y3, double *x4, double *y4) {
833 if (ocpn_wx_stack_count == 0) return (0);
834 ocpn_wx_stack_top--;
835 ocpn_wx_stack_count--;
836 *x1 = ocpn_wx_stack_top->x1;
837 *y1 = ocpn_wx_stack_top->y1;
838 *x2 = ocpn_wx_stack_top->x2;
839 *y2 = ocpn_wx_stack_top->y2;
840 *x3 = ocpn_wx_stack_top->x3;
841 *y3 = ocpn_wx_stack_top->y3;
842 *x4 = ocpn_wx_stack_top->x4;
843 *y4 = ocpn_wx_stack_top->y4;
844 return (1);
845}
846
847static bool ocpn_wx_spline_add_point(double x, double y) {
848 wxPoint *point = new wxPoint;
849 point->x = (int)x;
850 point->y = (int)y;
851 ocpn_wx_spline_point_list.Append((wxObject *)point);
852 return true;
853}
854
855void GenSpline(wxList *points) {
856 wxPoint *p;
857 double cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4;
858 double x1, y1, x2, y2;
859
860 wxList::compatibility_iterator node = points->GetFirst();
861 if (!node)
862 // empty list
863 return;
864
865 p = (wxPoint *)node->GetData();
866
867 x1 = p->x;
868 y1 = p->y;
869
870 node = node->GetNext();
871 p = (wxPoint *)node->GetData();
872
873 x2 = p->x;
874 y2 = p->y;
875 cx1 = (double)((x1 + x2) / 2);
876 cy1 = (double)((y1 + y2) / 2);
877 cx2 = (double)((cx1 + x2) / 2);
878 cy2 = (double)((cy1 + y2) / 2);
879
880 ocpn_wx_spline_add_point(x1, y1);
881
882 while ((node = node->GetNext())
883#if !wxUSE_STL
884 != nullptr
885#endif // !wxUSE_STL
886 ) {
887 p = (wxPoint *)node->GetData();
888 x1 = x2;
889 y1 = y2;
890 x2 = p->x;
891 y2 = p->y;
892 cx4 = (double)(x1 + x2) / 2;
893 cy4 = (double)(y1 + y2) / 2;
894 cx3 = (double)(x1 + cx4) / 2;
895 cy3 = (double)(y1 + cy4) / 2;
896
897 ocpn_wx_quadratic_spline(cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4);
898
899 cx1 = cx4;
900 cy1 = cy4;
901 cx2 = (double)(cx1 + x2) / 2;
902 cy2 = (double)(cy1 + y2) / 2;
903 }
904
905 ocpn_wx_spline_add_point(cx1, cy1);
906 ocpn_wx_spline_add_point(x2, y2);
907}
908
909#if 0
910static void GenerateSpline(int n, wxPoint points[])
911{
912 wxList list;
913 for (int i =0; i < n; i++)
914 {
915 list.Append((wxObject*)&points[i]);
916 }
917
918 GenSpline(&list);
919}
920
921static void ClearSplineList()
922{
923 wxList::compatibility_iterator node = ocpn_wx_spline_point_list.GetFirst();
924 while (node)
925 {
926 wxPoint *point = (wxPoint *)node->GetData();
927 delete point;
928 ocpn_wx_spline_point_list.Erase(node);
929 node = ocpn_wx_spline_point_list.GetFirst();
930 }
931}
932#endif
GRIB Data Visualization and Rendering Factory.
GRIB Display Settings Configuration Interface.
GRIB Isobar and Isoline Generation System.
Factory class for creating and managing GRIB data visualizations.
Represents a meteorological data grid from a GRIB (Gridded Binary) file.
Definition GribRecord.h:182
double getDi() const
Returns the grid spacing in longitude (i) direction in degrees.
Definition GribRecord.h:460
void getXY(int i, int j, double *x, double *y) const
Converts grid indices to longitude/latitude coordinates.
Definition GribRecord.h:562
int getNi() const
Returns the number of points in the longitude (i) direction of the grid.
Definition GribRecord.h:448
int getNj() const
Returns the number of points in the latitude (j) direction of the grid.
Definition GribRecord.h:454
double getValue(int i, int j) const
Returns the data value at a specific grid point.
Definition GribRecord.h:480
IsoLine(double val, double coeff, double offset, const GribRecord *rec)
Definition IsoLine.cpp:168
Contains view parameters and status information for a chart display viewport.
double clon
Center longitude of the viewport in decimal degrees.
int m_projection_type
Chart projection type (PROJECTION_MERCATOR, etc.)
@ PI_PROJECTION_MERCATOR
Mercator projection, standard for navigation charts.
@ PI_PROJECTION_EQUIRECTANGULAR
Equirectangular/Plate Carrée projection, simple lat/lon grid.
double PlugInGetDisplaySizeMM()
Gets physical display size in millimeters.
void GetCanvasPixLL(PlugIn_ViewPort *vp, wxPoint *pp, double lat, double lon)
Converts lat/lon to canvas physical pixel coordinates.
OpenGL Platform Abstraction Layer.