TriMeshRef Triangulator::createMesh( Winding winding ) { TriMeshRef result = make_shared<TriMesh>( TriMesh::Format().positions( 2 ) ); tessTesselate( mTess.get(), (int)winding, TESS_POLYGONS, 3, 2, 0 ); result->appendVertices( (vec2*)tessGetVertices( mTess.get() ), tessGetVertexCount( mTess.get() ) ); result->appendIndices( (uint32_t*)( tessGetElements( mTess.get() ) ), tessGetElementCount( mTess.get() ) * 3 ); return result; }
ShapeMesh* ShapeTesselator::createMesh(int windingRule) { tessTesselate(tess, windingRule, TESS_POLYGONS, 3, 2, 0); ShapeMesh *mesh = new ShapeMesh; mesh->appendVertices((Vec2f*)tessGetVertices(tess), tessGetVertexCount(tess)); mesh->appendIndices((uint32_t*)tessGetElements(tess), tessGetElementCount(tess) * 3); return mesh; }
TriMesh Triangulator::calcMesh( Winding winding ) { TriMesh result( TriMesh::Format().positions( 2 ) ); tessTesselate( mTess.get(), (int)winding, TESS_POLYGONS, 3, 2, 0 ); result.appendVertices( (vec2*)tessGetVertices( mTess.get() ), tessGetVertexCount( mTess.get() ) ); result.appendIndices( (uint32_t*)( tessGetElements( mTess.get() ) ), tessGetElementCount( mTess.get() ) * 3 ); return result; }
//---------------------------------------------------------- void ofTessellator::performTessellation(ofPolyWindingMode polyWindingMode, vector<ofPolyline>& dstpoly, bool bIs2D ) { if (!tessTesselate(cacheTess, polyWindingMode, TESS_BOUNDARY_CONTOURS, 0, 3, 0)){ ofLogError("ofTessellator") << "performTesselation(): polyline boundary contours tessellation failed, winding mode " << polyWindingMode; return; } const ofDefaultVertexType* verts = (ofDefaultVertexType*)tessGetVertices(cacheTess); const TESSindex* elems = tessGetElements(cacheTess); const int nelems = tessGetElementCount(cacheTess); dstpoly.resize(nelems); for (int i = 0; i < nelems; ++i) { int b = elems[i*2]; int n = elems[i*2+1]; dstpoly[i].clear(); dstpoly[i].addVertices(&verts[b],n); dstpoly[i].setClosed(true); } }
//---------------------------------------------------------- void ofTessellator::performTessellation(ofPolyWindingMode polyWindingMode, vector<ofPolyline>& dstpoly, bool bIs2D ) { if (!tessTesselate(cacheTess, polyWindingMode, TESS_BOUNDARY_CONTOURS, 0, 3, 0)){ ofLog(OF_LOG_ERROR,"ofTessellator: tessellation failed"); return; } const ofPoint* verts = (ofPoint*)tessGetVertices(cacheTess); const TESSindex* elems = tessGetElements(cacheTess); const int nelems = tessGetElementCount(cacheTess); dstpoly.resize(nelems); for (int i = 0; i < nelems; ++i) { int b = elems[i*2]; int n = elems[i*2+1]; dstpoly[i].clear(); dstpoly[i].addVertexes(&verts[b],n); dstpoly[i].setClosed(true); } }
//---------------------------------------------------------- void ofTessellator::performTessellation(ofPolyWindingMode polyWindingMode, ofMesh& dstmesh, bool bIs2D ) { if (!tessTesselate(cacheTess, polyWindingMode, TESS_POLYGONS, 3, 3, 0)){ ofLogError("ofTessellator") << "performTessellation(): mesh polygon tessellation failed, winding mode " << polyWindingMode; return; } int numVertices = tessGetVertexCount( cacheTess ); int numIndices = tessGetElementCount( cacheTess )*3; dstmesh.clear(); dstmesh.addVertices((ofDefaultVertexType*)tessGetVertices(cacheTess),numVertices); dstmesh.addIndices((ofIndexType*)tessGetElements(cacheTess),numIndices); /*ofIndexType * tessElements = (ofIndexType *)tessGetElements(cacheTess); for(int i=0;i<numIndices;i++){ if(tessElements[i]!=TESS_UNDEF) dstmesh.addIndex(tessElements[i]); }*/ dstmesh.setMode(OF_PRIMITIVE_TRIANGLES); }
//----------------------------------------------------------------// void SafeTesselator::GetTriangles ( MOAIVertexBuffer& vtxBuffer, MOAIIndexBuffer& idxBuffer ) { ZLMemStream idxStream; ZLMemStream vtxStream; const int* elems = tessGetElements ( this->mTess ); const int nelems = tessGetElementCount ( this->mTess ); for ( int i = 0; i < nelems; ++i ) { const int* tri = &elems [ i * 3 ]; idxStream.Write < u32 >( tri [ 0 ]); idxStream.Write < u32 >( tri [ 1 ]); idxStream.Write < u32 >( tri [ 2 ]); } const float* verts = tessGetVertices ( this->mTess ); const int nverts = tessGetVertexCount ( this->mTess ); for ( int i = 0; i < nverts; ++i ) { const ZLVec2D& vert = (( const ZLVec2D* )verts )[ i ]; vtxStream.Write < float >( vert.mX ); vtxStream.Write < float >( vert.mY ); vtxStream.Write < float >( 0.0f ); vtxStream.Write < u32 >( 0xffffffff ); } idxStream.Seek ( 0, SEEK_SET ); vtxStream.Seek ( 0, SEEK_SET ); idxBuffer.CopyFromStream ( idxStream, 4 ); vtxBuffer.Clear (); vtxBuffer.Reserve ( vtxStream.GetLength ()); vtxBuffer.WriteStream ( vtxStream ); }
int PolyTessGeo::BuildTess(void) { // Setup the tesselator TESSalloc ma; TESStesselator* tess = 0; const int nvp = 3; // unsigned char* vflags = 0; #ifdef USE_POOL struct MemPool pool; unsigned char mem[4024*1024]; // int nvflags = 0; #else int allocated = 0; double t0 = 0, t1 = 0; #endif #ifdef USE_POOL pool.size = 0; pool.cap = sizeof(mem); pool.buf = mem; memset(&ma, 0, sizeof(ma)); ma.memalloc = poolAlloc; ma.memfree = poolFree; ma.userData = (void*)&pool; ma.extraVertices = 256; // realloc not provided, allow 256 extra vertices. #else memset(&ma, 0, sizeof(ma)); ma.memalloc = stdAlloc; ma.memfree = stdFree; ma.userData = (void*)&allocated; ma.extraVertices = 256; // realloc not provided, allow 256 extra vertices. #endif tess = tessNewTess(&ma); if (!tess) return -1; //tessSetOption(tess, TESS_CONSTRAINED_DELAUNAY_TRIANGULATION, 1); // Create the contour vertex arrays int iir, ip; // Get max number number of points(vertices) in any contour int npta = m_cntr[0]; npta += 2; // fluff for( iir=0 ; iir < m_ncnt-1 ; iir++){ int nptr = m_cntr[iir+1]; npta = wxMax(npta, nptr); } TESSreal *geoPt = (TESSreal *)malloc((npta) * 2 * sizeof(TESSreal)); // tess input vertex array TESSreal *ppt = geoPt; // Create input structures // Exterior Ring int npte = m_cntr[0]; unsigned int ptValid = npte; // Check and account for winding direction of ring double x0, y0, x, y; OGRPoint p; wxPoint2DDouble *pp = (wxPoint2DDouble *)m_vertexPtrArray[0]; bool cw = isRingClockwise(pp, m_cntr[0]); //pp++; // skip 0? if(cw) { x0 = pp->m_x; y0 = pp->m_y; } else { x0 = pp[npte-1].m_x; y0 = pp[npte-1].m_y; } // Transcribe points to vertex array, in proper order with no duplicates // also, accounting for tess_orient for(ip = 0 ; ip < npte ; ip++) { int pidx; if(cw) pidx = npte - ip - 1; else pidx = ip; x = pp[pidx].m_x; y = pp[pidx].m_y; if((fabs(x-x0) > EQUAL_EPS) || (fabs(y-y0) > EQUAL_EPS)) { if(m_tess_orient == TESS_VERT) { *ppt++ = x; *ppt++ = y; } else { *ppt++ = y; *ppt++ = x; } } else ptValid--; x0 = x; y0 = y; } // Apply LOD reduction int beforeLOD = ptValid; int afterLOD = beforeLOD; std::vector<bool> bool_keep; if(ptValid > 5 && (m_LOD_meters > .01)){ for(unsigned int i = 0 ; i < ptValid ; i++) bool_keep.push_back(false); // Keep a few key points bool_keep[0] = true; bool_keep[1] = true; bool_keep[ptValid-1] = true; bool_keep[ptValid-2] = true; DouglasPeuckerFI(geoPt, 1, ptValid-2, m_LOD_meters, bool_keep); // Create a new buffer float *LOD_result = (float *)malloc((npte) * 2 * sizeof(float)); float *plod = LOD_result; int kept_LOD =0; for(unsigned int i=0 ; i < ptValid ; i++){ if(bool_keep[i]){ float x = geoPt[i*2]; float y = geoPt[(i*2) + 1]; *plod++ = x; *plod++ = y; kept_LOD++; } } beforeLOD = ptValid; afterLOD = kept_LOD; tessAddContour(tess, 2, LOD_result, sizeof(float)*2, kept_LOD); free(LOD_result); } else { tessAddContour(tess, 2, geoPt, sizeof(float)*2, ptValid); } #if 1 // Now the interior contours for(iir=0; iir < m_ncnt-1; iir++) { ppt = geoPt; wxPoint2DDouble *pp = (wxPoint2DDouble *)m_vertexPtrArray[iir + 1]; int npti = m_cntr[iir+1]; ptValid = npti; // Check and account for winding direction of ring bool cw = isRingClockwise(pp, m_cntr[iir+1]); if(!cw) { x0 = pp[0].m_x; y0 = pp[0].m_y; } else { x0 = pp[npti-1].m_x; y0 = pp[npti-1].m_y; } // 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; x = pp[pidx].m_x; y = pp[pidx].m_y; if((fabs(x-x0) > EQUAL_EPS) || (fabs(y-y0) > EQUAL_EPS)) { if(m_tess_orient == TESS_VERT) { *ppt++ = x; *ppt++ = y; } else { *ppt++ = y; *ppt++ = x; } } else ptValid--; x0 = x; y0 = y; } tessAddContour(tess, 2, geoPt, sizeof(float)*2, ptValid); } #endif // Ready to kick off the tesselator TriPrim *pTPG_Last = NULL; TriPrim *pTPG_Head = NULL; //s_nvmax = 0; //OCPNStopWatchTess tt0; if (!tessTesselate(tess, TESS_WINDING_POSITIVE, TESS_POLYGONS, nvp, 2, 0)) return -1; double tessTime = 0; //tt0.GetTime(); // Tesselation all done, so... // Create the data structures // Make a linked list of TriPrims from tess output arrays const TESSreal* verts = tessGetVertices(tess); const int* vinds = tessGetVertexIndices(tess); const int* elems = tessGetElements(tess); const int nverts = tessGetVertexCount(tess); int nelems = tessGetElementCount(tess); bool skip = false; //skip = true; double stripTime = 0; //tt0.Reset(); int bytes_needed_vbo = 0; float *vbo = 0; if(m_bstripify && nelems && !skip){ STRIPERCREATE sc; sc.DFaces = (udword *)elems; sc.NbFaces = nelems; sc.AskForWords = false; sc.ConnectAllStrips = false; sc.OneSided = false; sc.SGIAlgorithm = false; Striper Strip; Strip.Init(sc); STRIPERRESULT sr; Strip.Compute(sr); stripTime = 0; //tt0.GetTime(); /* fprintf(stdout, "Number of strips: %d\n", sr.NbStrips); fprintf(stdout, "Number of points: %d\n", nelems); uword* Refs = (uword*)sr.StripRuns; for(udword i=0;i<sr.NbStrips;i++) { fprintf(stdout, "Strip %d: ", i); udword NbRefs = sr.StripLengths[i]; for(udword j=0;j<NbRefs;j++) { fprintf(stdout, "%d ", *Refs++); } fprintf(stdout, "\n"); } */ // calculate and allocate the final (float) VBO-like buffer for this entire feature int *Refs = (int *)sr.StripRuns; for(unsigned int i=0;i<sr.NbStrips;i++){ unsigned int NbRefs = sr.StripLengths[i]; // vertices per strip bytes_needed_vbo += NbRefs * 2 * sizeof(float); } vbo = (float *)malloc(bytes_needed_vbo); float *vbo_run = vbo; for(unsigned int i=0;i<sr.NbStrips;i++){ unsigned int NbRefs = sr.StripLengths[i]; // vertices per strip if(NbRefs >= 3){ // this is a valid primitive TriPrim *pTPG = new TriPrim; if(NULL == pTPG_Last){ pTPG_Head = pTPG; pTPG_Last = pTPG; } else{ pTPG_Last->p_next = pTPG; pTPG_Last = pTPG; } pTPG->p_next = NULL; pTPG->nVert = NbRefs; // pTPG->p_vertex = (double *)malloc(NbRefs * 2 * sizeof(double)); // GLdouble *pdd = (GLdouble*)pTPG->p_vertex; pTPG->p_vertex = (double *)vbo_run; // Preset LLBox bounding box limits double sxmax = -1e8; double sxmin = 1e8; double symax = -1e8; double symin = 1e8; if(NbRefs >3) pTPG->type = GL_TRIANGLE_STRIP; else pTPG->type = GL_TRIANGLES; // Add the first two points int vindex[3]; vindex[0] = *Refs++; vindex[1] = *Refs++; unsigned int np = 2; // for the first triangle for(unsigned int i = 2; i < NbRefs ; i++){ vindex[np] = *Refs++; np++; for (unsigned int j = 0; j < np; ++j){ double yd = verts[vindex[j]*2]; double xd = verts[vindex[j]*2+1]; // Calculate LLBox bounding box for each Tri-prim if(m_bmerc_transform){ double valx = ( xd * mx_rate ) + mx_offset; double valy = ( yd * my_rate ) + my_offset; // quickly convert to lat/lon double lat = ( 2.0 * atan ( exp ( valy/CM93_semimajor_axis_meters ) ) - PI/2. ) / DEGREE; double lon= ( valx / ( DEGREE * CM93_semimajor_axis_meters ) ); sxmax = wxMax(lon, sxmax); sxmin = wxMin(lon, sxmin); symax = wxMax(lat, symax); symin = wxMin(lat, symin); } else{ sxmax = wxMax(xd, sxmax); sxmin = wxMin(xd, sxmin); symax = wxMax(yd, symax); symin = wxMin(yd, symin); } // Append this point to TriPrim vbo *vbo_run++ = (xd) + m_feature_easting; // adjust to chart ref coordinates *vbo_run++ = (yd) + m_feature_northing; } // For // Compute the final LLbbox for this TriPrim chain double minlat, minlon, maxlat, maxlon; fromSM(sxmin, symin, m_feature_ref_lat, m_feature_ref_lon, &minlat, &minlon); fromSM(sxmax, symax, m_feature_ref_lat, m_feature_ref_lon, &maxlat, &maxlon); pTPG->tri_box.Set(minlat, minlon, maxlat, maxlon); // set for next single point np = 0; } } else Refs += sr.StripLengths[i]; } // for strips } else{ // not stripified m_nvertex_max = nverts; // record largest vertex count bytes_needed_vbo = nelems * nvp * 2 * sizeof(float); vbo = (float *)malloc(bytes_needed_vbo); float *vbo_run = vbo; for (int i = 0; i < nelems; ++i){ const int* p = &elems[i*nvp]; TriPrim *pTPG = new TriPrim; if(NULL == pTPG_Last){ pTPG_Head = pTPG; pTPG_Last = pTPG; } else{ pTPG_Last->p_next = pTPG; pTPG_Last = pTPG; } pTPG->p_next = NULL; pTPG->type = GL_TRIANGLES; pTPG->nVert = nvp; // pTPG->p_vertex = (double *)malloc(nvp * 2 * sizeof(double)); // GLdouble *pdd = (GLdouble*)pTPG->p_vertex; pTPG->p_vertex = (double *)vbo_run; // Preset LLBox bounding box limits double sxmax = -1e8; double sxmin = 1e8; double symax = -1e8; double symin = 1e8; for (size_t j = 0; j < nvp && p[j] != TESS_UNDEF; ++j){ double yd = verts[p[j]*2]; double xd = verts[p[j]*2+1]; // Calculate LLBox bounding box for each Tri-prim if(m_bmerc_transform){ double valx = ( xd * mx_rate ) + mx_offset; double valy = ( yd * my_rate ) + my_offset; // quickly convert to lat/lon double lat = ( 2.0 * atan ( exp ( valy/CM93_semimajor_axis_meters ) ) - PI/2. ) / DEGREE; double lon= ( valx / ( DEGREE * CM93_semimajor_axis_meters ) ); sxmax = wxMax(lon, sxmax); sxmin = wxMin(lon, sxmin); symax = wxMax(lat, symax); symin = wxMin(lat, symin); } else{ sxmax = wxMax(xd, sxmax); sxmin = wxMin(xd, sxmin); symax = wxMax(yd, symax); symin = wxMin(yd, symin); } // Append this point to TriPrim, converting to SM if called for if(m_b_senc_sm){ double easting, northing; toSM(yd, xd, m_ref_lat, m_ref_lon, &easting, &northing); *vbo_run++ = easting; *vbo_run++ = northing; } else{ *vbo_run++ = xd; *vbo_run++ = yd; } } pTPG->tri_box.Set(symin, sxmin, symax, sxmax); } } // stripify if(m_printStats){ int nTri = 0; int nStrip = 0; TriPrim *p_tp = pTPG_Head; while( p_tp ) { if(p_tp->type == GL_TRIANGLES) nTri++; if(p_tp->type == GL_TRIANGLE_STRIP) nStrip++; p_tp = p_tp->p_next; // pick up the next in chain } if((nTri + nStrip) > 10000){ printf("LOD: %d/%d\n", afterLOD, beforeLOD); printf("Tess time(ms): %f\n", tessTime); printf("Strip time(ms): %f\n", stripTime); printf("Primitives: Tri: %5d Strip: %5d Total: %5d\n", nTri, nStrip, nTri + nStrip); printf("\n"); } } m_ppg_head = new PolyTriGroup; m_ppg_head->m_bSMSENC = m_b_senc_sm; m_ppg_head->nContours = m_ncnt; m_ppg_head->pn_vertex = m_cntr; // pointer to array of poly vertex counts m_ppg_head->tri_prim_head = pTPG_Head; // head of linked list of TriPrims //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 int nptfinal = npta; // No longer need the full geometry in the SENC, nptfinal = 1; m_nwkb = (nptfinal +1) * 2 * sizeof(float); m_ppg_head->pgroup_geom = (float *)malloc(m_nwkb); float *vro = m_ppg_head->pgroup_geom; ppt = geoPt; float tx,ty; for(ip = 0 ; ip < nptfinal ; ip++) { if(TESS_HORZ == m_tess_orient) { ty = *ppt++; tx = *ppt++; } else { tx = *ppt++; ty = *ppt++; } if(m_b_senc_sm) { // Calculate SM from chart common reference point double easting, northing; toSM(ty, tx, 0/*ref_lat*/, 0/*ref_lon*/, &easting, &northing); *vro++ = easting; // x *vro++ = northing; // y } else { *vro++ = tx; // lon *vro++ = ty; // lat } ppt++; // skip z } m_ppg_head->bsingle_alloc = true; m_ppg_head->single_buffer = (unsigned char *)vbo; m_ppg_head->single_buffer_size = bytes_needed_vbo; m_ppg_head->data_type = DATA_TYPE_FLOAT; free (geoPt); // All allocated buffers are owned now by the m_ppg_head // And will be freed on dtor of this object if (tess) tessDeleteTess(tess); m_bOK = true; return 0; }
int main(int argc, char *argv[]) { GLFWwindow* window; const GLFWvidmode* mode; int width,height,i,j; struct SVGPath* bg; struct SVGPath* fg; struct SVGPath* it; float bounds[4],view[4],cx,cy,w,offx,offy; float t = 0.0f, pt = 0.0f; TESSalloc ma; TESStesselator* tess = 0; const int nvp = 6; unsigned char* vflags = 0; int nvflags = 0; #ifdef USE_POOL struct MemPool pool; unsigned char mem[1024*1024]; #else int allocated = 0; #endif TESS_NOTUSED(argc); TESS_NOTUSED(argv); if (!glfwInit()) { printf("Failed to init GLFW."); return -1; } printf("loading...\n"); // Load assets bg = svgParseFromFile("../Bin/bg.svg"); if (!bg) return -1; fg = svgParseFromFile("../Bin/fg.svg"); if (!fg) return -1; printf("go...\n"); // Flip y for (it = bg; it != NULL; it = it->next) for (i = 0; i < it->npts; ++i) it->pts[i*2+1] = -it->pts[i*2+1]; for (it = fg; it != NULL; it = it->next) for (i = 0; i < it->npts; ++i) it->pts[i*2+1] = -it->pts[i*2+1]; // Find FG bounds and center. bounds[0] = bounds[2] = fg->pts[0]; bounds[1] = bounds[3] = fg->pts[1]; for (it = fg; it != NULL; it = it->next) { for (i = 0; i < it->npts; ++i) { const float x = it->pts[i*2]; const float y = it->pts[i*2+1]; if (x < bounds[0]) bounds[0] = x; if (y < bounds[1]) bounds[1] = y; if (x > bounds[2]) bounds[2] = x; if (y > bounds[3]) bounds[3] = y; } } cx = (bounds[0]+bounds[2])/2; cy = (bounds[1]+bounds[3])/2; for (it = fg; it != NULL; it = it->next) { for (i = 0; i < it->npts; ++i) { it->pts[i*2] -= cx; it->pts[i*2+1] -= cy; } } // Find BG bounds. bounds[0] = bounds[2] = bg->pts[0]; bounds[1] = bounds[3] = bg->pts[1]; for (it = bg; it != NULL; it = it->next) { for (i = 0; i < it->npts; ++i) { const float x = it->pts[i*2]; const float y = it->pts[i*2+1]; if (x < bounds[0]) bounds[0] = x; if (y < bounds[1]) bounds[1] = y; if (x > bounds[2]) bounds[2] = x; if (y > bounds[3]) bounds[3] = y; } } #ifdef USE_POOL pool.size = 0; pool.cap = sizeof(mem); pool.buf = mem; memset(&ma, 0, sizeof(ma)); ma.memalloc = poolAlloc; ma.memfree = poolFree; ma.userData = (void*)&pool; ma.extraVertices = 256; // realloc not provided, allow 256 extra vertices. #else memset(&ma, 0, sizeof(ma)); ma.memalloc = stdAlloc; ma.memfree = stdFree; ma.userData = (void*)&allocated; ma.extraVertices = 256; // realloc not provided, allow 256 extra vertices. tess = tessNewTess(&ma); if (!tess) return -1; // Offset the foreground shape to center of the bg. offx = (bounds[2]+bounds[0])/2; offy = (bounds[3]+bounds[1])/2; for (it = fg; it != NULL; it = it->next) { for (i = 0; i < it->npts; ++i) { it->pts[i*2] += offx; it->pts[i*2+1] += offy; } } // Add contours. for (it = bg; it != NULL; it = it->next) tessAddContour(tess, 2, it->pts, sizeof(float)*2, it->npts); for (it = fg; it != NULL; it = it->next) tessAddContour(tess, 2, it->pts, sizeof(float)*2, it->npts); if (!tessTesselate(tess, TESS_WINDING_POSITIVE, TESS_POLYGONS, nvp, 2, 0)) return -1; printf("Memory used: %.1f kB\n", allocated/1024.0f); #endif mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); width = mode->width - 40; height = mode->height - 80; window = glfwCreateWindow(width, height, "Libtess2 Demo", NULL, NULL); if (!window) { glfwTerminate(); return -1; } glfwSetKeyCallback(window, key); glfwMakeContextCurrent(window); // Adjust bounds so that we get nice view of the bg. cx = (bounds[0]+bounds[2])/2; cy = (bounds[3]+bounds[1])/2; w = (bounds[2]-bounds[0])/2; view[0] = cx - w*1.2f; view[2] = cx + w*1.2f; view[1] = cy - w*1.2f*(float)height/(float)width; view[3] = cy + w*1.2f*(float)height/(float)width; glfwSetTime(0); while (!glfwWindowShouldClose(window)) { float ct = (float)glfwGetTime(); if (run) t += ct - pt; pt = ct; // Update and render glViewport(0, 0, width, height); glClearColor(0.3f, 0.3f, 0.32f, 1.0f); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_TEXTURE_2D); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(view[0],view[2],view[1],view[3],-1,1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); #ifdef USE_POOL pool.size = 0; // reset pool tess = tessNewTess(&ma); if (tess) { offx = (view[2]+view[0])/2 + sinf(t) * (view[2]-view[0])/2; offy = (view[3]+view[1])/2 + cosf(t*3.13f) * (view[3]-view[1])/6; for (it = fg; it != NULL; it = it->next) { for (i = 0; i < it->npts; ++i) { it->pts[i*2] += offx; it->pts[i*2+1] += offy; } } for (it = bg; it != NULL; it = it->next) tessAddContour(tess, 2, it->pts, sizeof(float)*2, it->npts); for (it = fg; it != NULL; it = it->next) tessAddContour(tess, 2, it->pts, sizeof(float)*2, it->npts); for (it = fg; it != NULL; it = it->next) { for (i = 0; i < it->npts; ++i) { it->pts[i*2] -= offx; it->pts[i*2+1] -= offy; } } // First combine contours and then triangulate, this removes unnecessary inner vertices. if (tessTesselate(tess, TESS_WINDING_POSITIVE, TESS_BOUNDARY_CONTOURS, 0, 0, 0)) { const float* verts = tessGetVertices(tess); const int* vinds = tessGetVertexIndices(tess); const int nverts = tessGetVertexCount(tess); const int* elems = tessGetElements(tess); const int nelems = tessGetElementCount(tess); if (nverts > nvflags) { if (vflags) free(vflags); nvflags = nverts; vflags = (unsigned char*)malloc(sizeof(unsigned char)*nvflags); } if (vflags) { // Vertex indices describe the order the indices were added and can be used // to map the tesselator output to input. Vertices marked as TESS_UNDEF // are the ones that were created at the intersection of segments. // That is, if vflags is set it means that the vertex comes from intersegment. for (i = 0; i < nverts; ++i) vflags[i] = vinds[i] == TESS_UNDEF ? 1 : 0; } for (i = 0; i < nelems; ++i) { int b = elems[i*2]; int n = elems[i*2+1]; tessAddContour(tess, 2, &verts[b*2], sizeof(float)*2, n); } if (!tessTesselate(tess, TESS_WINDING_POSITIVE, TESS_POLYGONS, nvp, 2, 0)) tess = 0; } else tess = 0; } #endif // Draw tesselated pieces. if (tess) { const float* verts = tessGetVertices(tess); const int* vinds = tessGetVertexIndices(tess); const int* elems = tessGetElements(tess); const int nverts = tessGetVertexCount(tess); const int nelems = tessGetElementCount(tess); // Draw polygons. glColor4ub(255,255,255,128); for (i = 0; i < nelems; ++i) { const int* p = &elems[i*nvp]; glBegin(GL_TRIANGLE_FAN); for (j = 0; j < nvp && p[j] != TESS_UNDEF; ++j) glVertex2f(verts[p[j]*2], verts[p[j]*2+1]); glEnd(); } glColor4ub(0,0,0,16); for (i = 0; i < nelems; ++i) { const int* p = &elems[i*nvp]; glBegin(GL_LINE_LOOP); for (j = 0; j < nvp && p[j] != TESS_UNDEF; ++j) glVertex2f(verts[p[j]*2], verts[p[j]*2+1]); glEnd(); } glColor4ub(0,0,0,128); glPointSize(3.0f); glBegin(GL_POINTS); for (i = 0; i < nverts; ++i) { if (vflags && vflags[vinds[i]]) glColor4ub(255,0,0,192); else glColor4ub(0,0,0,128); glVertex2f(verts[i*2], verts[i*2+1]); } glEnd(); glPointSize(1.0f); } glEnable(GL_DEPTH_TEST); glfwSwapBuffers(window); glfwPollEvents(); } if (tess) tessDeleteTess(tess); if (vflags) free(vflags); svgDelete(bg); svgDelete(fg); glfwTerminate(); return 0; }
const TESSreal* getVertices(TESStess* t) { return tessGetVertices(t->tess); }
void BoatDialog::OnPaintCrossOverChart(wxPaintEvent& event) { wxWindow *window = dynamic_cast<wxWindow*>(event.GetEventObject()); if(!window) return; wxGCDC dc(window); dc.SetBackgroundMode(wxTRANSPARENT); long index = SelectedPolar(); bool polar = !m_cPlotType->GetSelection(); int w, h; m_CrossOverChart->GetSize( &w, &h); dc.SetPen(wxPen(wxColor(0, 0, 0))); dc.SetBrush(*wxTRANSPARENT_BRUSH); dc.SetTextForeground(wxColour(0, 55, 75)); bool full = m_cbFullPlot->GetValue(); double scale; int xc = full ? w / 2 : 0; if(polar) { scale = wxMin(full ? w/2 : w, h/2) / 40.0; } for(double VW = 0; VW < 40; VW += 10) { if(polar) { dc.DrawCircle(xc, h/2, VW * scale); dc.DrawText(wxString::Format(_T("%.0f"), VW), xc, h/2+(int)VW*scale); } else { int y = h - VW * h / 40; dc.DrawLine(0, y, w, y); dc.DrawText(wxString::Format(_T("%.0f"), VW), 0, y); } } for(double H = 0; H < 180; H += 10) { if(polar) { double x = scale*sin(deg2rad(H)); double y = scale*cos(deg2rad(H)); if(H < 180) dc.DrawLine(xc - x, h/2 + y, xc + x, h/2 - y); wxString str = wxString::Format(_T("%.0f"), H); int sw, sh; dc.GetTextExtent(str, &sw, &sh); dc.DrawText(str, xc + .9*x - sw/2, h/2 - .9*y - sh/2); } else { int x = H * w / 180; dc.DrawLine(x, 0, x, h); dc.DrawText(wxString::Format(_T("%.0f"), H), x, 0); } } wxColour colors[] = {*wxRED, *wxGREEN, *wxBLUE, *wxCYAN, *wxYELLOW, wxColour(255, 0, 255)}; int c = 0; for(unsigned int i=0; i<m_Boat.Polars.size(); i++) { bool bold = i == index; // dc.SetPen(wxPen(colors[c], bold ? 1 : 3)); dc.SetPen(*wxTRANSPARENT_PEN); dc.SetBrush(wxColour(colors[c].Red(), colors[c].Green(), colors[c].Blue(), bold ? 230 : 60)); if(++c == (sizeof colors) / (sizeof *colors)) c = 0; bool tri = true; TESStesselator *tess = m_Boat.Polars[i].CrossOverRegion.Tesselate(tri); if(!tess) continue; const float* verts = tessGetVertices(tess); // const int* vinds = tessGetVertexIndices(tess); const int* elems = tessGetElements(tess); // const int nverts = tessGetVertexCount(tess); const int nelems = tessGetElementCount(tess); // Draw polygons. for (int i = 0; i < nelems; ++i) { if(tri) { const int* p = &elems[i*3]; wxPoint points[3]; for (unsigned j = 0; j < 3 && p[j] != TESS_UNDEF; ++j) { double H = verts[p[j]*2+0]; double VW = verts[p[j]*2+1]; points[j] = wxPoint(H * w / 180, h - VW * h / 40); } if(polar) { int count[3] = {CalcPolarPoints(points[0], points[1]), CalcPolarPoints(points[1], points[2]), CalcPolarPoints(points[2], points[0])}; wxPoint *pts = new wxPoint[count[0] + count[1] + count[2]]; int c = 0; for(int j = 0; j<3; j++) { int jp1 = j+1 == 3 ? 0 : j+1; for(int k=0; k<count[j]; k++) { double d = (double)k / count[j]; double px = points[j].x * (1-d) + points[jp1].x * d; double py = points[j].y * (1-d) + points[jp1].y * d; double H = px / w * 180; double VW = (h - py) / h * 40; pts[c++] = wxPoint(xc + scale*VW*sin(deg2rad(H)), h/2 - scale*VW*cos(deg2rad(H))); } } dc.DrawPolygon(c, pts); if(full) { for(int j = 0; j<c; j++) pts[j].x = 2*xc - pts[j].x; dc.DrawPolygon(c, pts); } delete [] pts; } else { dc.DrawPolygon(3, points); } } else { int b = elems[i*2]; int n = elems[i*2+1]; wxPoint pl; for(int j = 0; j<=n; j++) { int k = j < n ? j : 0; float H = verts[2*(b + k)+0], VW = verts[2*(b + k)+1]; wxPoint p0; if(polar) p0 = wxPoint(xc + scale*VW*sin(deg2rad(H)), h/2 - scale*VW*cos(deg2rad(H))); else p0 = wxPoint(H * w / 180, h - VW * h / 40); if(j > 0) dc.DrawLine(pl, p0); pl = p0; } } } tessDeleteTess(tess); } }