bool wxGCDC::DoGetPartialTextExtents(const wxString& text, wxArrayInt& widths) const { bool isMeasuringContext = false; wxGraphicsContext* context = m_graphicContext; if (!context) { context = wxGraphicsRenderer::GetDefaultRenderer()->CreateMeasuringContext(); isMeasuringContext = true; } widths.Clear(); widths.Add(0,text.Length()); if ( text.IsEmpty() ) return true; wxArrayDouble widthsD; context->GetPartialTextExtents( text, widthsD ); for ( size_t i = 0; i < widths.GetCount(); ++i ) widths[i] = (wxCoord)(widthsD[i] + 0.5); if (isMeasuringContext) delete context; return true; }
void CMusikLibrary::Query( const wxString & query, wxArrayInt & aReturn ,bool bClearArray ) { if(bClearArray) { aReturn.Clear(); //--- run the query ---// aReturn.Alloc( GetSongCount() ); } MusikDb::ResultCB cb(&aReturn, &db_callbackAddToIntArray); m_pDB->Exec( ConvQueryToMB( query ), cb ); }
void xLightsFrame::GetCheckedItems(wxArrayInt& chArray) { chArray.Clear(); int maxch = CheckListBoxTestChannels->GetCount(); for (int ch=0; ch < maxch; ch++) { if (CheckListBoxTestChannels->IsChecked(ch)) { chArray.Add(ch); } } }
bool wxGCDCImpl::DoGetPartialTextExtents(const wxString& text, wxArrayInt& widths) const { wxCHECK_MSG( m_graphicContext, false, wxT("wxGCDC(cg)::DoGetPartialTextExtents - invalid DC") ); widths.Clear(); widths.Add(0,text.Length()); if ( text.IsEmpty() ) return true; wxArrayDouble widthsD; m_graphicContext->GetPartialTextExtents( text, widthsD ); for ( size_t i = 0; i < widths.GetCount(); ++i ) widths[i] = (wxCoord)(widthsD[i] + 0.5); return true; }
// Build PolyTessGeo Object from OGR Polygon // Using OpenGL/GLU tesselator int PolyTessGeo::PolyTessGeoGL(OGRPolygon *poly, bool bSENC_SM, double ref_lat, double ref_lon) { #ifdef ocpnUSE_GL int iir, ip; int *cntr; GLdouble *geoPt; wxString sout; wxString sout1; wxString stemp; // Make a quick sanity check of the polygon coherence bool b_ok = true; OGRLineString *tls = poly->getExteriorRing(); if(!tls) { b_ok = false; } else { int tnpta = poly->getExteriorRing()->getNumPoints(); if(tnpta < 3 ) b_ok = false; } for( iir=0 ; iir < poly->getNumInteriorRings() ; iir++) { int tnptr = poly->getInteriorRing(iir)->getNumPoints(); if( tnptr < 3 ) b_ok = false; } if( !b_ok ) return ERROR_BAD_OGRPOLY; #ifdef __WXMSW__ // If using the OpenGL dlls provided with Windows, // load the dll and establish addresses of the entry points needed #ifdef USE_GLU_DLL if(!s_glu_dll_ready) { s_hGLU_DLL = LoadLibrary("glu32.dll"); if (s_hGLU_DLL != NULL) { s_lpfnTessProperty = (LPFNDLLTESSPROPERTY)GetProcAddress(s_hGLU_DLL,"gluTessProperty"); s_lpfnNewTess = (LPFNDLLNEWTESS)GetProcAddress(s_hGLU_DLL, "gluNewTess"); s_lpfnTessBeginContour = (LPFNDLLTESSBEGINCONTOUR)GetProcAddress(s_hGLU_DLL, "gluTessBeginContour"); s_lpfnTessEndContour = (LPFNDLLTESSENDCONTOUR)GetProcAddress(s_hGLU_DLL, "gluTessEndContour"); s_lpfnTessBeginPolygon = (LPFNDLLTESSBEGINPOLYGON)GetProcAddress(s_hGLU_DLL, "gluTessBeginPolygon"); s_lpfnTessEndPolygon = (LPFNDLLTESSENDPOLYGON)GetProcAddress(s_hGLU_DLL, "gluTessEndPolygon"); s_lpfnDeleteTess = (LPFNDLLDELETETESS)GetProcAddress(s_hGLU_DLL, "gluDeleteTess"); s_lpfnTessVertex = (LPFNDLLTESSVERTEX)GetProcAddress(s_hGLU_DLL, "gluTessVertex"); s_lpfnTessCallback = (LPFNDLLTESSCALLBACK)GetProcAddress(s_hGLU_DLL, "gluTessCallback"); s_glu_dll_ready = true; } else { return ERROR_NO_DLL; } } #endif #endif // Allocate a work buffer, which will be grown as needed #define NINIT_BUFFER_LEN 10000 s_pwork_buf = (GLdouble *)malloc(NINIT_BUFFER_LEN * 2 * sizeof(GLdouble)); s_buf_len = NINIT_BUFFER_LEN * 2; s_buf_idx = 0; // Create an array to hold pointers to allocated vertices created by "combine" callback, // so that they may be deleted after tesselation. s_pCombineVertexArray = new wxArrayPtrVoid; // Create tesselator GLUtessobj = gluNewTess(); // Register the callbacks gluTessCallback(GLUtessobj, GLU_TESS_BEGIN, (GLvoid (__CALL_CONVENTION *) ())&beginCallback); gluTessCallback(GLUtessobj, GLU_TESS_BEGIN, (GLvoid (__CALL_CONVENTION *) ())&beginCallback); gluTessCallback(GLUtessobj, GLU_TESS_VERTEX, (GLvoid (__CALL_CONVENTION *) ())&vertexCallback); gluTessCallback(GLUtessobj, GLU_TESS_END, (GLvoid (__CALL_CONVENTION *) ())&endCallback); gluTessCallback(GLUtessobj, GLU_TESS_COMBINE, (GLvoid (__CALL_CONVENTION *) ())&combineCallback); // gluTessCallback(GLUtessobj, GLU_TESS_ERROR, (GLvoid (__CALL_CONVENTION *) ())&errorCallback); // glShadeModel(GL_SMOOTH); gluTessProperty(GLUtessobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE ); // gluTess algorithm internally selects vertically oriented triangle strips and fans. // This orientation is not optimal for conventional memory-mapped raster display shape filling. // We can "fool" the algorithm by interchanging the x and y vertex values passed to gluTessVertex // and then reverting x and y on the resulting vertexCallbacks. // In this implementation, we will explicitely set the preferred orientation. //Set the preferred orientation tess_orient = TESS_HORZ; // prefer horizontal tristrips // PolyGeo BBox as lat/lon OGREnvelope Envelope; poly->getEnvelope(&Envelope); xmin = Envelope.MinX; ymin = Envelope.MinY; xmax = Envelope.MaxX; ymax = Envelope.MaxY; // Get total number of contours m_ncnt = 1; // always exterior ring int nint = poly->getNumInteriorRings(); // interior rings m_ncnt += nint; // Allocate cntr array cntr = (int *)malloc(m_ncnt * sizeof(int)); // Get total number of points(vertices) int npta = poly->getExteriorRing()->getNumPoints(); cntr[0] = npta; npta += 2; // fluff for( iir=0 ; iir < nint ; iir++) { int nptr = poly->getInteriorRing(iir)->getNumPoints(); cntr[iir+1] = nptr; npta += nptr + 2; } // printf("pPoly npta: %d\n", npta); geoPt = (GLdouble *)malloc((npta) * 3 * sizeof(GLdouble)); // vertex array // Grow the work buffer if necessary if((npta * 4) > s_buf_len) { s_pwork_buf = (GLdouble *)realloc(s_pwork_buf, npta * 4 * sizeof(GLdouble)); s_buf_len = npta * 4; } // Define the polygon gluTessBeginPolygon(GLUtessobj, NULL); // Create input structures // Exterior Ring int npte = poly->getExteriorRing()->getNumPoints(); cntr[0] = npte; GLdouble *ppt = geoPt; // Check and account for winding direction of ring bool cw = !(poly->getExteriorRing()->isClockwise() == 0); double x0, y0, x, y; OGRPoint p; if(cw) { poly->getExteriorRing()->getPoint(0, &p); x0 = p.getX(); y0 = p.getY(); } else { poly->getExteriorRing()->getPoint(npte-1, &p); x0 = p.getX(); y0 = p.getY(); } // Transcribe contour to an array of doubles, with duplicates eliminated double *DPbuffer = (double *)malloc(npte * 2 * sizeof(double)); double *DPrun = DPbuffer; int nPoints = npte; for(ip = 0 ; ip < npte ; ip++) { int pidx; if(cw) pidx = npte - ip - 1; else pidx = ip; poly->getExteriorRing()->getPoint(pidx, &p); x = p.getX(); y = p.getY(); if( ((fabs(x-x0) > EQUAL_EPS) || (fabs(y-y0) > EQUAL_EPS))) { GLdouble *ppt_temp = ppt; if(tess_orient == TESS_VERT) { *DPrun++ = x; *DPrun++ = y; } else { *DPrun++ = y; *DPrun++ = x; } x0 = x; y0 = y; } else nPoints--; } if(nPoints > 5 && (m_LOD_meters > .01)){ index_keep.Clear(); index_keep.Add(0); index_keep.Add(nPoints-1); index_keep.Add(1); index_keep.Add(nPoints-2); DouglasPeucker(DPbuffer, 1, nPoints-2, m_LOD_meters/(1852 * 60), &index_keep); // printf("DP Reduction: %d/%d\n", index_keep.GetCount(), nPoints); g_keep += index_keep.GetCount(); g_orig += nPoints; // printf("...................Running: %g\n", (double)g_keep/g_orig); } else { index_keep.Clear(); for(int i = 0 ; i < nPoints ; i++) index_keep.Add(i); } cntr[0] = index_keep.GetCount(); // Mark the keepers by adding a simple constant to X for(unsigned int i=0 ; i < index_keep.GetCount() ; i++){ int k = index_keep.Item(i); DPbuffer[2*k] += 2000.; } // Declare the gluContour and copy the points gluTessBeginContour(GLUtessobj); DPrun = DPbuffer; for(ip = 0 ; ip < nPoints ; ip++) { x = *DPrun++; y = *DPrun++; if(x > 1000.){ GLdouble *ppt_top = ppt; *ppt++ = x-2000; *ppt++ = y; *ppt++ = 0; gluTessVertex( GLUtessobj, ppt_top, ppt_top ) ; } } gluTessEndContour(GLUtessobj); free(DPbuffer); // Now the interior contours for(iir=0 ; iir < nint ; iir++) { gluTessBeginContour(GLUtessobj); int npti = poly->getInteriorRing(iir)->getNumPoints(); // Check and account for winding direction of ring bool cw = !(poly->getInteriorRing(iir)->isClockwise() == 0); if(!cw) { poly->getInteriorRing(iir)->getPoint(0, &p); x0 = p.getX(); y0 = p.getY(); } else { poly->getInteriorRing(iir)->getPoint(npti-1, &p); x0 = p.getX(); y0 = p.getY(); } // Transcribe points to vertex array, in proper order with no duplicates // also, accounting for tess_orient for(int ip = 0 ; ip < npti ; ip++) { OGRPoint p; int pidx; if(!cw) // interior contours must be cw pidx = npti - ip - 1; else pidx = ip; poly->getInteriorRing(iir)->getPoint(pidx, &p); x = p.getX(); y = p.getY(); if((fabs(x-x0) > EQUAL_EPS) || (fabs(y-y0) > EQUAL_EPS)) { GLdouble *ppt_temp = ppt; if(tess_orient == TESS_VERT) { *ppt++ = x; *ppt++ = y; } else { *ppt++ = y; *ppt++ = x; } *ppt++ = 0.0; gluTessVertex( GLUtessobj, ppt_temp, ppt_temp ) ; // printf("tess from Poly, internal vertex %d %g %g\n", ip, x, y); } else cntr[iir+1]--; x0 = x; y0 = y; } gluTessEndContour(GLUtessobj); } // Store some SM conversion data in static store, // for callback access s_ref_lat = ref_lat; s_ref_lon = ref_lon; s_bSENC_SM = bSENC_SM; s_bmerc_transform = false; // Ready to kick off the tesselator s_pTPG_Last = NULL; s_pTPG_Head = NULL; s_nvmax = 0; gluTessEndPolygon(GLUtessobj); // here it goes m_nvertex_max = s_nvmax; // record largest vertex count, updates in callback // Tesselation all done, so... // Create the data structures m_ppg_head = new PolyTriGroup; m_ppg_head->m_bSMSENC = s_bSENC_SM; m_ppg_head->nContours = m_ncnt; m_ppg_head->pn_vertex = cntr; // pointer to array of poly vertex counts m_ppg_head->data_type = DATA_TYPE_DOUBLE; // Transcribe the raw geometry buffer // Converting to float as we go, and // allowing for tess_orient // Also, convert to SM if requested // Recalculate the size of the geometry buffer int nptfinal = cntr[0] + 2; for(int i=0 ; i < nint ; i++) nptfinal += cntr[i+1] + 2; // No longer need the full geometry in the SENC, nptfinal = 1; m_nwkb = (nptfinal + 1) * 2 * sizeof(float); m_ppg_head->pgroup_geom = (float *)calloc(sizeof(float), (nptfinal + 1) * 2); float *vro = m_ppg_head->pgroup_geom; ppt = geoPt; float tx,ty; for(ip = 0 ; ip < nptfinal ; ip++) { if(TESS_HORZ == tess_orient) { ty = *ppt++; tx = *ppt++; } else { tx = *ppt++; ty = *ppt++; } if(bSENC_SM) { // Calculate SM from chart common reference point double easting, northing; toSM(ty, tx, ref_lat, ref_lon, &easting, &northing); *vro++ = easting; // x *vro++ = northing; // y } else { *vro++ = tx; // lon *vro++ = ty; // lat } ppt++; // skip z } m_ppg_head->tri_prim_head = s_pTPG_Head; // head of linked list of TriPrims // Convert the Triangle vertex arrays into a single memory allocation of floats // to reduce SENC size and enable efficient access later // First calculate the total byte size int total_byte_size = 2 * sizeof(float); TriPrim *p_tp = m_ppg_head->tri_prim_head; while( p_tp ) { total_byte_size += p_tp->nVert * 2 * sizeof(float); p_tp = p_tp->p_next; // pick up the next in chain } float *vbuf = (float *)malloc(total_byte_size); p_tp = m_ppg_head->tri_prim_head; float *p_run = vbuf; while( p_tp ) { float *pfbuf = p_run; GLdouble *pdouble_buf = (GLdouble *)p_tp->p_vertex; for( int i=0 ; i < p_tp->nVert * 2 ; ++i){ float x = (float)( *((GLdouble *)pdouble_buf) ); pdouble_buf++; *p_run++ = x; } free(p_tp->p_vertex); p_tp->p_vertex = (double *)pfbuf; p_tp = p_tp->p_next; // pick up the next in chain } m_ppg_head->bsingle_alloc = true; m_ppg_head->single_buffer = (unsigned char *)vbuf; m_ppg_head->single_buffer_size = total_byte_size; m_ppg_head->data_type = DATA_TYPE_FLOAT; gluDeleteTess(GLUtessobj); free( s_pwork_buf ); s_pwork_buf = NULL; free (geoPt); // Free up any "Combine" vertices created for(unsigned int i = 0; i < s_pCombineVertexArray->GetCount() ; i++) free (s_pCombineVertexArray->Item(i)); delete s_pCombineVertexArray; m_bOK = true; #endif // #ifdef ocpnUSE_GL return 0; }
// called on each Timer tick while Test dialog is open void xLightsFrame::OnTimerTest(long curtime) { static int LastNotebookSelection = -1; static int LastBgIntensity,LastFgIntensity,LastBgColor[3],LastFgColor[3],*ShimColor,ShimIntensity; static int LastSequenceSpeed; static int LastAutomatedTest; static long NextSequenceStart = -1; static TestFunctions LastFunc = OFF; static unsigned int interval, rgbCycle, TestSeqIdx; static wxArrayInt chArray,TwinkleState; static float frequency; int v,BgIntensity,FgIntensity,BgColor[3],FgColor[3]; unsigned int i; bool ColorChange; if (!xout) return; xout->TimerStart(curtime); int NotebookSelection = NotebookTest->GetSelection(); if (NotebookSelection != LastNotebookSelection) { LastNotebookSelection = NotebookSelection; CheckChannelList = true; TestSeqIdx=0; TestButtonsOff(); } if (TestFunc != LastFunc) { LastFunc = TestFunc; rgbCycle=0; CheckChannelList = true; NextSequenceStart = -1; } if (CheckChannelList) { // get list of checked channels xout->alloff(); GetCheckedItems(chArray); LastSequenceSpeed=-1; LastBgIntensity=-1; LastFgIntensity=-1; LastAutomatedTest=-1; for (i=0; i < 3; i++) { LastBgColor[i] = -1; LastFgColor[i] = -1; } if (!CheckBoxLightOutput->IsChecked()) { StatusBar1->SetStatusText(_("Testing disabled - Output to Lights is not checked")); } else if (TestFunc == OFF) { StatusBar1->SetStatusText(_("Testing off")); } else { StatusBar1->SetStatusText(wxString::Format(_("Testing %ld channels"),static_cast<long>(chArray.Count()))); } CheckChannelList = false; } if (TestFunc != OFF && chArray.Count() > 0) switch (NotebookSelection) { case 0: // standard tests v=SliderChaseSpeed->GetValue(); // 0-100 BgIntensity = SliderBgIntensity->GetValue(); FgIntensity = SliderFgIntensity->GetValue(); ColorChange = BgIntensity != LastBgIntensity || FgIntensity != LastFgIntensity; LastBgIntensity = BgIntensity; LastFgIntensity = FgIntensity; interval = 1600 - v*15; switch (TestFunc) { case DIM: if (ColorChange) { for (i=0; i < chArray.Count(); i++) { xout->SetIntensity(chArray[i], BgIntensity); } } break; case TWINKLE: if (LastSequenceSpeed < 0) { LastSequenceSpeed=0; TwinkleState.Clear(); for (i=0; i < chArray.Count(); i++) { TestSeqIdx = static_cast<int>(rand01()*TwinkleRatio); TwinkleState.Add(TestSeqIdx == 0 ? -1 : 1); } } for (i=0; i < TwinkleState.Count(); i++) { if (TwinkleState[i] < -1) { // background TwinkleState[i]++; } else if (TwinkleState[i] > 1) { // highlight TwinkleState[i]--; } else if (TwinkleState[i] == -1) { // was background, now highlight for random period TwinkleState[i]=static_cast<int>(rand01()*interval+100) / XTIMER_INTERVAL; xout->SetIntensity(chArray[i], FgIntensity); } else { // was on, now go to bg color for random period TwinkleState[i]=-static_cast<int>(rand01()*interval+100) / XTIMER_INTERVAL * (TwinkleRatio - 1); xout->SetIntensity(chArray[i], BgIntensity); } } break; case SHIMMER: if (ColorChange || curtime >= NextSequenceStart) { ShimIntensity = (ShimIntensity == FgIntensity) ? BgIntensity : FgIntensity; for (i=0; i < chArray.Count(); i++) { xout->SetIntensity(chArray[i], ShimIntensity); } } if (curtime >= NextSequenceStart) { NextSequenceStart = curtime + interval/2; } break; case CHASE: //StatusBar1->SetStatusText(wxString::Format(_("chase curtime=%ld, NextSequenceStart=%ld"),curtime,NextSequenceStart)); if (ColorChange || curtime >= NextSequenceStart) { for (i=0; i < chArray.Count(); i++) { v = (i % ChaseGrouping) == TestSeqIdx ? FgIntensity : BgIntensity; xout->SetIntensity(chArray[i], v); } } if (curtime >= NextSequenceStart) { NextSequenceStart = curtime + interval; TestSeqIdx = (TestSeqIdx + 1) % ChaseGrouping; if (TestSeqIdx >= chArray.Count()) TestSeqIdx=0; } StatusBar1->SetStatusText(wxString::Format(_("Testing %ld channels; chase now at ch# %d"),static_cast<long>(chArray.Count()), TestSeqIdx)); //show current ch# -DJ break; default: break; } break; case 1: // RGB tests v=SliderRgbChaseSpeed->GetValue(); // 0-100 BgColor[0] = SliderBgColorA->GetValue(); BgColor[1] = SliderBgColorB->GetValue(); BgColor[2] = SliderBgColorC->GetValue(); FgColor[0] = SliderFgColorA->GetValue(); FgColor[1] = SliderFgColorB->GetValue(); FgColor[2] = SliderFgColorC->GetValue(); interval = 1600 - v*15; for (ColorChange=false,i=0; i < 3; i++) { ColorChange |= (BgColor[i] != LastBgColor[i]); ColorChange |= (FgColor[i] != LastFgColor[i]); LastBgColor[i] = BgColor[i]; LastFgColor[i] = FgColor[i]; } switch (TestFunc) { case DIM: if (ColorChange) { for (i=0; i < chArray.Count(); i++) { xout->SetIntensity(chArray[i], BgColor[i % 3]); } } break; case TWINKLE: if (LastSequenceSpeed < 0) { LastSequenceSpeed=0; TwinkleState.Clear(); for (i=0; i < chArray.Count()-2; i+=3) { TestSeqIdx = static_cast<int>(rand01()*TwinkleRatio); TwinkleState.Add(TestSeqIdx == 0 ? -1 : 1); } } for (i=0; i < TwinkleState.Count(); i++) { if (TwinkleState[i] < -1) { // background TwinkleState[i]++; } else if (TwinkleState[i] > 1) { // highlight TwinkleState[i]--; } else if (TwinkleState[i] == -1) { // was background, now highlight for random period TwinkleState[i]=static_cast<int>(rand01()*interval+100) / XTIMER_INTERVAL; TestSeqIdx = i * 3; xout->SetIntensity(chArray[TestSeqIdx], FgColor[0]); xout->SetIntensity(chArray[TestSeqIdx+1], FgColor[1]); xout->SetIntensity(chArray[TestSeqIdx+2], FgColor[2]); } else { // was on, now go to bg color for random period TwinkleState[i]=-static_cast<int>(rand01()*interval+100) / XTIMER_INTERVAL * (TwinkleRatio - 1); TestSeqIdx = i * 3; xout->SetIntensity(chArray[TestSeqIdx], BgColor[0]); xout->SetIntensity(chArray[TestSeqIdx+1], BgColor[1]); xout->SetIntensity(chArray[TestSeqIdx+2], BgColor[2]); } } break; case SHIMMER: if (ColorChange || curtime >= NextSequenceStart) { ShimColor = (ShimColor == FgColor) ? BgColor : FgColor; for (i=0; i < chArray.Count(); i++) { xout->SetIntensity(chArray[i], ShimColor[i % 3]); } } if (curtime >= NextSequenceStart) { NextSequenceStart = curtime + interval/2; } break; case CHASE: if (ColorChange || curtime >= NextSequenceStart) { for (i=0; i < chArray.Count(); i++) { v = (i / 3 % ChaseGrouping) == TestSeqIdx ? FgColor[i % 3] : BgColor[i % 3]; xout->SetIntensity(chArray[i], v); } } if (curtime >= NextSequenceStart) { NextSequenceStart = curtime + interval; TestSeqIdx = (TestSeqIdx + 1) % ChaseGrouping; if (TestSeqIdx >= (chArray.Count()+2) / 3) TestSeqIdx=0; } StatusBar1->SetStatusText(wxString::Format(_("Testing %ld channels; chase now at ch# %d"),static_cast<long>(chArray.Count()), TestSeqIdx)); //show current ch# -DJ break; default: break; } break; case 2: // RGB Cycle v=SliderRgbCycleSpeed->GetValue(); // 0-100 if (TestFunc == DIM) { // color mixing if (v != LastSequenceSpeed) { frequency=v/1000.0 + 0.05; LastSequenceSpeed = v; } BgColor[0] = sin(frequency*TestSeqIdx + 0.0) * 127 + 128; BgColor[1] = sin(frequency*TestSeqIdx + 2.0) * 127 + 128; BgColor[2] = sin(frequency*TestSeqIdx + 4.0) * 127 + 128; TestSeqIdx++; for (i=0; i < chArray.Count(); i++) { xout->SetIntensity(chArray[i], BgColor[i % 3]); } } else { // RGB cycle if (v != LastSequenceSpeed) { interval = (101-v)*50; NextSequenceStart = curtime + interval; LastSequenceSpeed = v; } if (curtime >= NextSequenceStart) { for (i=0; i < chArray.Count(); i++) { switch (rgbCycle) { case 3: v=255; break; default: v = (i % 3) == rgbCycle ? 255 : 0; break; } xout->SetIntensity(chArray[i], v); } rgbCycle=(rgbCycle + 1) % ChaseGrouping; NextSequenceStart += interval; } } break; } xout->TimerEnd(); }