/**********************************************************************
zyGrib: meteorological GRIB file viewer
Copyright (C) 2008 - Jacques Zaninetti - http://www.zygrib.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
***********************************************************************/
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif //precompiled headers
//#include "dychart.h" // for some compile time fixups
//#include "chcanv.h"
//#include "cutil.h"
//#include "georef.h"
#include
#include "IsoLine.h"
#include "GribSettingsDialog.h"
#include "GribOverlayFactory.h"
#ifdef __OCPN__ANDROID__
#include "qdebug.h"
#endif
//static void GenerateSpline(int n, wxPoint points[]);
//static void ClearSplineList();
wxList ocpn_wx_spline_point_list;
#include
WX_DEFINE_LIST(MySegList);
WX_DEFINE_LIST(MySegListList);
#ifndef PI
#define PI 3.14159
#endif
#define CTRUE -1
#define CFALSE 0
/* Local variables for cohen_sutherland_line_clip: */
struct LOC_cohen_sutherland_line_clip {
double xmin, xmax, ymin, ymax;
} ;
void CompOutCode (double x, double y, outcode *code, struct LOC_cohen_sutherland_line_clip *LINK)
{
/*Compute outcode for the point (x,y) */
*code = 0;
if (y > LINK->ymax)
*code = 1L << ((long)TOP);
else if (y < LINK->ymin)
*code = 1L << ((long)BOTTOM);
if (x > LINK->xmax)
*code |= 1L << ((long)RIGHT);
else if (x < LINK->xmin)
*code |= 1L << ((long)LEFT);
}
ClipResult cohen_sutherland_line_clip_d (double *x0, double *y0, double *x1, double *y1,
double xmin_, double xmax_, double ymin_, double ymax_)
{
/* Cohen-Sutherland clipping algorithm for line P0=(x1,y0) to P1=(x1,y1)
* and clip rectangle with diagonal from (xmin,ymin) to (xmax,ymax).*/
struct LOC_cohen_sutherland_line_clip V;
int done = CFALSE;
ClipResult clip = Visible;
outcode outcode0, outcode1, outcodeOut;
/*Outcodes for P0,P1, and whichever point lies outside the clip rectangle*/
double x=0., y=0.;
V.xmin = xmin_;
V.xmax = xmax_;
V.ymin = ymin_;
V.ymax = ymax_;
CompOutCode(*x0, *y0, &outcode0, &V);
CompOutCode(*x1, *y1, &outcode1, &V);
do {
if (outcode0 == 0 && outcode1 == 0) { /*Trivial accept and exit*/
done = CTRUE;
} else if ((outcode0 & outcode1) != 0) {
clip = Invisible;
done = CTRUE;
}
/*Logical intersection is true, so trivial reject and exit.*/
else {
clip = Visible;
/*Failed both tests, so calculate the line segment to clip;
* from an outside point to an intersection with clip edge.*/
/*At least one endpoint is outside the clip rectangle; pick it.*/
if (outcode0 != 0)
outcodeOut = outcode0;
else
outcodeOut = outcode1;
/*Now find intersection point;
* use formulas y=y0+slope*(x-x0),x=x0+(1/slope)*(y-y0).*/
if (((1L << ((long)TOP)) & outcodeOut) != 0) {
/*Divide line at top of clip rectangle*/
x = *x0 + (*x1 - *x0) * (V.ymax - *y0) / (*y1 - *y0);
y = V.ymax;
} else if (((1L << ((long)BOTTOM)) & outcodeOut) != 0) {
/*Divide line at bottom of clip rectangle*/
x = *x0 + (*x1 - *x0) * (V.ymin - *y0) / (*y1 - *y0);
y = V.ymin;
} else if (((1L << ((long)RIGHT)) & outcodeOut) != 0) {
/*Divide line at right edge of clip rectangle*/
y = *y0 + (*y1 - *y0) * (V.xmax - *x0) / (*x1 - *x0);
x = V.xmax;
} else if (((1L << ((long)LEFT)) & outcodeOut) != 0) {
/*Divide line at left edge of clip rectangle*/
y = *y0 + (*y1 - *y0) * (V.xmin - *x0) / (*x1 - *x0);
x = V.xmin;
}
/*Now we move outside point to intersection point to clip,
* and get ready for next pass.*/
if (outcodeOut == outcode0) {
*x0 = x;
*y0 = y;
CompOutCode(*x0, *y0, &outcode0, &V);
} else {
*x1 = x;
*y1 = y;
CompOutCode(*x1, *y1, &outcode1, &V);
}
}
} while (!done);
return clip;
}
ClipResult cohen_sutherland_line_clip_i (int *x0_, int *y0_, int *x1_, int *y1_,
int xmin_, int xmax_, int ymin_, int ymax_)
{
ClipResult ret;
double x0,y0,x1,y1;
x0 = *x0_;
y0 = *y0_;
x1 = *x1_;
y1 = *y1_;
ret = cohen_sutherland_line_clip_d (&x0, &y0, &x1, &y1,
(double)xmin_, (double)xmax_,
(double)ymin_, (double)ymax_);
*x0_ = (int)x0;
*y0_ = (int)y0;
*x1_ = (int)x1;
*y1_ = (int)y1;
return ret;
}
double round_msvc (double x)
{
return(floor(x + 0.5));
}
//---------------------------------------------------------------
IsoLine::IsoLine(double val, double coeff, double offset, const GribRecord *rec_)
{
if(wxGetDisplaySize().x > 0){
m_pixelMM = PlugInGetDisplaySizeMM() / wxGetDisplaySize().x;
m_pixelMM = wxMax(.02, m_pixelMM); // protect against bad data
}
else
m_pixelMM = 0.27; // semi-standard number...
value = val/coeff-offset;
rec = rec_;
W = rec_->getNi();
H = rec_->getNj();
//---------------------------------------------------------
// Génère la liste des segments.
extractIsoLine(rec_);
value = val;
if(trace.size() == 0)
return;
// Join the isoline segments into a nice list
// Which is end-to-end continuous and unidirectional
// Create a master wxList of the trace list
std::list::iterator it;
for (it=trace.begin(); it!=trace.end(); it++)
{
Segment *seg = *it;
seg->bUsed = false;
m_seglist.Append(*it);
}
// Isoline may be discontinuous....
// So build a list of continuous segments
bool bdone = false;
while(!bdone)
{
MySegList *ps = BuildContinuousSegment();
m_SegListList.Append(ps);
MySegList::Node *node;
Segment *seg;
// recreate the master list, removing used segs
node = m_seglist.GetFirst();
while(node)
{
seg = node->GetData();
if(seg->bUsed)
{
m_seglist.Erase(node);
node = m_seglist.GetFirst();
}
else
node = node->GetNext();
}
if(0 == m_seglist.GetCount())
bdone = true;
}
///printf("create Isobar : press=%4.0f long=%d\n", pressure/100, trace.size());
}
//---------------------------------------------------------------
IsoLine::~IsoLine()
{
//printf("delete Isobar : press=%4.0f long=%d\n", pressure/100, trace.size());
std::list::iterator it;
for (it=trace.begin(); it!=trace.end(); it++) {
delete *it;
*it = NULL;
}
trace.clear();
m_SegListList.DeleteContents(true);
m_SegListList.Clear();
}
MySegList *IsoLine::BuildContinuousSegment(void)
{
MySegList::Node *node;
Segment *seg;
MySegList *ret_list = new MySegList;
// Build a chain extending from the "2" end of the target segment
// The joined list, side 2...
MySegList segjoin2;
// Add any first segment to the list
node = m_seglist.GetFirst();
Segment *seg0 = node->GetData();
seg0->bUsed = true;
segjoin2.Append(seg0);
Segment *tseg = seg0;
while(tseg)
{
bool badded = false;
Segment *seg;
node = m_seglist.GetFirst();
while (node)
{
seg = node->GetData();
if((!seg->bUsed) && (seg->py1 == tseg->py2) && (seg->px1 == tseg->px2)) // fits without reverse
{
seg->bUsed = true;
segjoin2.Append(seg);
badded = true;
break;
}
else if((!seg->bUsed) && (seg->py2 == tseg->py2) && (seg->px2 == tseg->px2)) // fits, needs reverse
{
seg->bUsed = true;
double a = seg->px2; seg->px2 = seg->px1; seg->px1 = a;
double b = seg->py2; seg->py2 = seg->py1; seg->py1 = b;
segjoin2.Append(seg);
badded = true;
break;
}
node = node->GetNext();
}
if(badded == true)
tseg = seg;
else
tseg = NULL;
}
// Build a chain extending from the "1" end of the target segment
// The joined list, side 1...
MySegList segjoin1;
// Add the same first segment to the list
node = m_seglist.GetFirst();
seg0 = node->GetData();
seg0->bUsed = true;
segjoin1.Append(seg0);
tseg = seg0;
while(tseg)
{
bool badded = false;
node = m_seglist.GetFirst();
while (node)
{
seg = node->GetData();
if((!seg->bUsed) && (seg->py2 == tseg->py1) && (seg->px2 == tseg->px1)) // fits without reverse
{
seg->bUsed = true;
segjoin1.Append(seg);
badded = true;
break;
}
else if((!seg->bUsed) && (seg->py1 == tseg->py1) && (seg->px1 == tseg->px1)) // fits, needs reverse
{
seg->bUsed = true;
double a = seg->px2; seg->px2 = seg->px1; seg->px1 = a;
double b = seg->py2; seg->py2 = seg->py1; seg->py1 = b;
segjoin1.Append(seg);
badded = true;
break;
}
node = node->GetNext();
}
if(badded == true)
tseg = seg;
else
tseg = NULL;
}
// Now have two lists...
// Start with "1" side list,
// starting from the end, and skipping the first segment
int n1 = segjoin1.GetCount();
for(int i=n1 - 1 ; i > 0 ; i--)
{
node = segjoin1.Item( i );
seg = node->GetData();
ret_list->Append(seg);
}
// Now add the "2"side list
int n2 = segjoin2.GetCount();
for(int i=0 ; i < n2 ; i++)
{
node = segjoin2.Item(i);
seg = node->GetData();
ret_list->Append(seg);
}
// And there it is
return ret_list;
}
//---------------------------------------------------------------
void IsoLine::drawIsoLine(GRIBOverlayFactory *pof, wxDC *dc, PlugIn_ViewPort *vp, bool bHiDef)
{
int nsegs = trace.size();
if(nsegs < 1)
return;
GetGlobalColor ( _T ( "UITX1" ), &isoLineColor );
#if wxUSE_GRAPHICS_CONTEXT
wxGraphicsContext *pgc = NULL;
#endif
if(dc) {
wxPen ppISO ( isoLineColor, 2 );
#if wxUSE_GRAPHICS_CONTEXT
wxMemoryDC *pmdc;
pmdc= wxDynamicCast(dc, wxMemoryDC);
pgc = wxGraphicsContext::Create(*pmdc);
pgc->SetPen(ppISO);
#endif
dc->SetPen(ppISO);
} else { /* opengl */
#ifdef ocpnUSE_GL
//#ifndef USE_ANDROID_GLES2
// if(m_pixelMM > 0.2){ // pixel size large enough to render well
// // Enable anti-aliased lines, at best quality
// glEnable( GL_LINE_SMOOTH );
// glEnable( GL_BLEND );
// glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
// glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
// glLineWidth( 2 );
// }
// else{
// glLineWidth( 0.4/m_pixelMM); // set a target line width by MM
// }
//#else
if(pof->m_oDC){
wxPen ppISO ( isoLineColor, 2 );
pof->m_oDC->SetPen(ppISO);
}
#endif
}
std::list::iterator it;
//---------------------------------------------------------
// Dessine les segments
//---------------------------------------------------------
for (it=trace.begin(); it!=trace.end(); it++)
{
Segment *seg = *it;
if(vp->m_projection_type == PI_PROJECTION_MERCATOR ||
vp->m_projection_type == PI_PROJECTION_EQUIRECTANGULAR) {
/* skip segments that go the wrong way around the world */
double sx1 = seg->px1, sx2 = seg->px2;
if(sx2 - sx1 > 180)
sx2 -= 360;
else if(sx1 - sx2 > 180)
sx1 -= 360;
if((sx1+180 < vp->clon && sx2+180 > vp->clon) ||
(sx1+180 > vp->clon && sx2+180 < vp->clon) ||
(sx1-180 < vp->clon && sx2-180 > vp->clon) ||
(sx1-180 > vp->clon && sx2-180 < vp->clon))
continue;
}
wxPoint ab;
GetCanvasPixLL(vp, &ab, seg->py1, seg->px1);
wxPoint cd;
GetCanvasPixLL(vp, &cd, seg->py2, seg->px2);
if(dc) {
#if wxUSE_GRAPHICS_CONTEXT
if(bHiDef && pgc)
pgc->StrokeLine(ab.x, ab.y, cd.x, cd.y);
else
#endif
dc->DrawLine(ab.x, ab.y, cd.x, cd.y);
} else { /* opengl */
#ifdef ocpnUSE_GL
if(pof->m_oDC){
pof->m_oDC->DrawLine(ab.x, ab.y, cd.x, cd.y);
}
#endif
}
}
#if wxUSE_GRAPHICS_CONTEXT
delete pgc;
#endif
// if(!dc) /* opengl */
// glEnd();
}
//---------------------------------------------------------------
void IsoLine::drawIsoLineLabels(GRIBOverlayFactory *pof, wxDC *dc,
PlugIn_ViewPort *vp, int density, int first,
wxImage &imageLabel)
{
std::list::iterator it;
int nb = first;
wxString label;
//---------------------------------------------------------
// Ecrit les labels
//---------------------------------------------------------
wxRect prev;
for (it=trace.begin(); it!=trace.end(); it++,nb++)
{
if (nb % density == 0)
{
Segment *seg = *it;
// if(vp->vpBBox.PointInBox((seg->px1 + seg->px2)/2., (seg->py1 + seg->py2)/2., 0.))
{
wxPoint ab;
GetCanvasPixLL(vp, &ab, seg->py1, seg->px1);
wxPoint cd;
GetCanvasPixLL(vp, &cd, seg->py1, seg->px1);
int w = imageLabel.GetWidth();
int h = imageLabel.GetHeight();
int label_offset = 6;
int xd = (ab.x + cd.x-(w+label_offset * 2))/2;
int yd = (ab.y + cd.y - h)/2;
int x = xd - label_offset;
wxRect r(x ,yd ,w ,h);
r.Inflate(w);
if (!prev.Intersects(r)) {
prev = r;
/* don't use alpha for isobars, for some reason draw bitmap ignores
the 4th argument (true or false has same result) */
wxImage img(w, h, imageLabel.GetData(), true);
dc->DrawBitmap(img, xd, yd, false);
}
}
}
}
}
void IsoLine::drawIsoLineLabelsGL(GRIBOverlayFactory *pof,
PlugIn_ViewPort *vp, int density, int first,
wxString label, wxColour &color, TexFont &texfont)
{
std::list::iterator it;
int nb = first;
#ifdef ocpnUSE_GL
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
//---------------------------------------------------------
// Ecrit les labels
//---------------------------------------------------------
wxRect prev;
for (it=trace.begin(); it!=trace.end(); it++,nb++)
{
if (nb % density == 0)
{
Segment *seg = *it;
// if(vp->vpBBox.PointInBox((seg->px1 + seg->px2)/2., (seg->py1 + seg->py2)/2., 0.))
{
wxPoint ab;
GetCanvasPixLL(vp, &ab, seg->py1, seg->px1);
wxPoint cd;
GetCanvasPixLL(vp, &cd, seg->py1, seg->px1);
int w, h;
texfont.GetTextExtent(label, &w, &h);
int label_offsetx = 6, label_offsety = 1;
int xd = (ab.x + cd.x-(w+label_offsetx * 2))/2;
int yd = (ab.y + cd.y - h)/2;
int x = xd - label_offsetx, y = yd - label_offsety;
w += 2*label_offsetx, h += 2*label_offsety;
wxRect r(x,y, w,h);
r.Inflate(w);
if (!prev.Intersects(r))
{
#if 1
prev = r;
if(pof->m_oDC){
//m_oDC->SetFont( *mfont );
pof->m_oDC->SetPen( *wxBLACK_PEN );
pof->m_oDC->SetBrush( color );
pof->m_oDC->DrawRectangle( x, y, w, h );
pof->m_oDC->DrawText( label, xd, yd );
}
#else
prev = r;
glColor4ub(color.Red(), color.Green(), color.Blue(), color.Alpha());
/* draw bounding rectangle */
glBegin(GL_QUADS);
glVertex2i(x, y);
glVertex2i(x+w, y);
glVertex2i(x+w, y+h);
glVertex2i(x, y+h);
glEnd();
glColor3ub(0, 0, 0);
glBegin(GL_LINE_LOOP);
glVertex2i(x, y);
glVertex2i(x+w, y);
glVertex2i(x+w, y+h);
glVertex2i(x, y+h);
glEnd();
glEnable(GL_TEXTURE_2D);
texfont.RenderString(label, xd, yd);
glDisable(GL_TEXTURE_2D);
#endif
}
}
}
}
glDisable( GL_BLEND );
#endif
}
//==================================================================================
// Segment
//==================================================================================
Segment::Segment(int I, int w, int J,
char c1, char c2, char c3, char c4,
const GribRecord *rec, double pressure)
{
traduitCode(I, w, J, c1, i,j);
traduitCode(I, w, J, c2, k,l);
traduitCode(I, w, J, c3, m,n);
traduitCode(I, w, J, c4, o,p);
intersectionAreteGrille(i,j, k,l, &px1,&py1, rec, pressure);
intersectionAreteGrille(m,n, o,p, &px2,&py2, rec, pressure);
}
//-----------------------------------------------------------------------
void Segment::intersectionAreteGrille(int i,int j, int k,int l, double *x, double *y,
const GribRecord *rec, double pressure)
{
double xa, xb, ya, yb, pa, pb, dec;
pa = rec->getValue(i,j);
pb = rec->getValue(k,l);
rec->getXY(i, j, &xa, &ya);
rec->getXY(k, l, &xb, &yb);
// Abscisse
if (pb != pa)
dec = (pressure-pa)/(pb-pa);
else
dec = 0.5;
if (fabs(dec)>1)
dec = 0.5;
double xd = xb -xa;
if(xd < -180)
xd += 360;
else if(xd > 180)
xd -= 360;
*x = xa +xd*dec;
// Ordonnée
if (pb != pa)
dec = (pressure-pa)/(pb-pa);
else
dec = 0.5;
if (fabs(dec)>1)
dec = 0.5;
*y = ya +(yb -ya)*dec;
}
//---------------------------------------------------------------
void Segment::traduitCode(int I, int w, int J, char c1, int &i, int &j) {
int Im1 = I ? I-1 : w - 1;
switch (c1) {
case 'a': i=Im1; j=J-1; break;
case 'b': i=I ; j=J-1; break;
case 'c': i=Im1; j=J ; break;
case 'd': i=I ; j=J ; break;
default: i=I ; j=J ;
}
}
//-----------------------------------------------------------------------
// Génère la liste des segments.
// Les coordonnées sont les indices dans la grille du GribRecord
//---------------------------------------------------------
void IsoLine::extractIsoLine(const GribRecord *rec)
{
int i, j, W, H;
double a,b,c,d;
W = rec->getNi();
H = rec->getNj();
int We = W;
if(rec->getLonMax() + rec->getDi() - rec->getLonMin() == 360)
We++;
for (j=1; jgetValue( 0, j-1 );
c = rec->getValue( 0, j );
for (i=1; igetX(i);
// y = rec->getY(j);
int ni = i;
if (i == W)
ni = 0;
b = rec->getValue( ni, j-1 );
d = rec->getValue( ni, j );
if( a == GRIB_NOTDEF || b == GRIB_NOTDEF || c == GRIB_NOTDEF || d == GRIB_NOTDEF ) continue;
if ((a< value && b< value && c< value && d < value)
|| (a>value && b>value && c>value && d > value))
continue;
// Détermine si 1 ou 2 segments traversent la case ab-cd
// a b
// c d
//--------------------------------
// 1 segment en diagonale
//--------------------------------
if ((a<=value && b<=value && c<=value && d>value)
|| (a>value && b>value && c>value && d<=value))
trace.push_back(new Segment(ni,W,j, 'c','d', 'b','d', rec, value));
else if ((a<=value && c<=value && d<=value && b>value)
|| (a>value && c>value && d>value && b<=value))
trace.push_back(new Segment(ni,W,j, 'a','b', 'b','d', rec, value));
else if ((c<=value && d<=value && b<=value && a>value)
|| (c>value && d>value && b>value && a<=value))
trace.push_back(new Segment(ni,W,j, 'a','b', 'a','c', rec, value));
else if ((a<=value && b<=value && d<=value && c>value)
|| (a>value && b>value && d>value && c<=value))
trace.push_back(new Segment(ni,W,j, 'a','c', 'c','d', rec,value));
//--------------------------------
// 1 segment H ou V
//--------------------------------
else if ((a<=value && b<=value && c>value && d>value)
|| (a>value && b>value && c<=value && d<=value))
trace.push_back(new Segment(ni,W,j, 'a','c', 'b','d', rec,value));
else if ((a<=value && c<=value && b>value && d>value)
|| (a>value && c>value && b<=value && d<=value))
trace.push_back(new Segment(ni,W,j, 'a','b', 'c','d', rec,value));
//--------------------------------
// 2 segments en diagonale
//--------------------------------
else if (a<=value && d<=value && c>value && b>value) {
trace.push_back(new Segment(ni,W,j, 'a','b', 'b','d', rec,value));
trace.push_back(new Segment(ni,W,j, 'a','c', 'c','d', rec,value));
}
else if (a>value && d>value && c<=value && b<=value) {
trace.push_back(new Segment(ni,W,j, 'a','b', 'a','c', rec,value));
trace.push_back(new Segment(ni,W,j, 'b','d', 'c','d', rec,value));
}
}
}
}
// ----------------------------------------------------------------------------
// splines code lifted from wxWidgets
// ----------------------------------------------------------------------------
// ----------------------------------- spline code ----------------------------------------
void ocpn_wx_quadratic_spline(double a1, double b1, double a2, double b2,
double a3, double b3, double a4, double b4);
void ocpn_wx_clear_stack();
int ocpn_wx_spline_pop(double *x1, double *y1, double *x2, double *y2, double *x3,
double *y3, double *x4, double *y4);
void ocpn_wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3,
double x4, double y4);
static bool ocpn_wx_spline_add_point(double x, double y);
#define half(z1, z2) ((z1+z2)/2.0)
#define THRESHOLD 5
/* iterative version */
void ocpn_wx_quadratic_spline(double a1, double b1, double a2, double b2, double a3, double b3, double a4,
double b4)
{
register double xmid, ymid;
double x1, y1, x2, y2, x3, y3, x4, y4;
ocpn_wx_clear_stack();
ocpn_wx_spline_push(a1, b1, a2, b2, a3, b3, a4, b4);
while (ocpn_wx_spline_pop(&x1, &y1, &x2, &y2, &x3, &y3, &x4, &y4)) {
xmid = (double)half(x2, x3);
ymid = (double)half(y2, y3);
if (fabs(x1 - xmid) < THRESHOLD && fabs(y1 - ymid) < THRESHOLD &&
fabs(xmid - x4) < THRESHOLD && fabs(ymid - y4) < THRESHOLD) {
ocpn_wx_spline_add_point( x1, y1 );
ocpn_wx_spline_add_point( xmid, ymid );
} else {
ocpn_wx_spline_push(xmid, ymid, (double)half(xmid, x3), (double)half(ymid, y3),
(double)half(x3, x4), (double)half(y3, y4), x4, y4);
ocpn_wx_spline_push(x1, y1, (double)half(x1, x2), (double)half(y1, y2),
(double)half(x2, xmid), (double)half(y2, ymid), xmid, ymid);
}
}
}
/* utilities used by spline drawing routines */
typedef struct ocpn_wx_spline_stack_struct {
double x1, y1, x2, y2, x3, y3, x4, y4;
} Stack;
#define SPLINE_STACK_DEPTH 20
static Stack ocpn_wx_spline_stack[SPLINE_STACK_DEPTH];
static Stack *ocpn_wx_stack_top;
static int ocpn_wx_stack_count;
void ocpn_wx_clear_stack()
{
ocpn_wx_stack_top = ocpn_wx_spline_stack;
ocpn_wx_stack_count = 0;
}
void ocpn_wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
{
ocpn_wx_stack_top->x1 = x1;
ocpn_wx_stack_top->y1 = y1;
ocpn_wx_stack_top->x2 = x2;
ocpn_wx_stack_top->y2 = y2;
ocpn_wx_stack_top->x3 = x3;
ocpn_wx_stack_top->y3 = y3;
ocpn_wx_stack_top->x4 = x4;
ocpn_wx_stack_top->y4 = y4;
ocpn_wx_stack_top++;
ocpn_wx_stack_count++;
}
int ocpn_wx_spline_pop(double *x1, double *y1, double *x2, double *y2,
double *x3, double *y3, double *x4, double *y4)
{
if (ocpn_wx_stack_count == 0)
return (0);
ocpn_wx_stack_top--;
ocpn_wx_stack_count--;
*x1 = ocpn_wx_stack_top->x1;
*y1 = ocpn_wx_stack_top->y1;
*x2 = ocpn_wx_stack_top->x2;
*y2 = ocpn_wx_stack_top->y2;
*x3 = ocpn_wx_stack_top->x3;
*y3 = ocpn_wx_stack_top->y3;
*x4 = ocpn_wx_stack_top->x4;
*y4 = ocpn_wx_stack_top->y4;
return (1);
}
static bool ocpn_wx_spline_add_point(double x, double y)
{
wxPoint *point = new wxPoint ;
point->x = (int) x;
point->y = (int) y;
ocpn_wx_spline_point_list.Append((wxObject*)point);
return true;
}
void GenSpline( wxList *points )
{
wxPoint *p;
double cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4;
double x1, y1, x2, y2;
wxList::compatibility_iterator node = points->GetFirst();
if (!node)
// empty list
return;
p = (wxPoint *)node->GetData();
x1 = p->x;
y1 = p->y;
node = node->GetNext();
p = (wxPoint *)node->GetData();
x2 = p->x;
y2 = p->y;
cx1 = (double)((x1 + x2) / 2);
cy1 = (double)((y1 + y2) / 2);
cx2 = (double)((cx1 + x2) / 2);
cy2 = (double)((cy1 + y2) / 2);
ocpn_wx_spline_add_point(x1, y1);
while ((node = node->GetNext())
#if !wxUSE_STL
!= NULL
#endif // !wxUSE_STL
)
{
p = (wxPoint *)node->GetData();
x1 = x2;
y1 = y2;
x2 = p->x;
y2 = p->y;
cx4 = (double)(x1 + x2) / 2;
cy4 = (double)(y1 + y2) / 2;
cx3 = (double)(x1 + cx4) / 2;
cy3 = (double)(y1 + cy4) / 2;
ocpn_wx_quadratic_spline(cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4);
cx1 = cx4;
cy1 = cy4;
cx2 = (double)(cx1 + x2) / 2;
cy2 = (double)(cy1 + y2) / 2;
}
ocpn_wx_spline_add_point( cx1, cy1 );
ocpn_wx_spline_add_point( x2, y2 );
}
#if 0
static void GenerateSpline(int n, wxPoint points[])
{
wxList list;
for (int i =0; i < n; i++)
{
list.Append((wxObject*)&points[i]);
}
GenSpline(&list);
}
static void ClearSplineList()
{
wxList::compatibility_iterator node = ocpn_wx_spline_point_list.GetFirst();
while (node)
{
wxPoint *point = (wxPoint *)node->GetData();
delete point;
ocpn_wx_spline_point_list.Erase(node);
node = ocpn_wx_spline_point_list.GetFirst();
}
}
#endif