/* Hyperbolic Tessellations Copyright (C) 2007 Dmitry Brant http://dmitrybrant.com */ //--------------------------------------------------------------------------- #ifndef mygraphics_H #define mygraphics_H #include "gl\gl.h" #include "mycomplex.h" #include "mathfuncs.h" // base renderer class class MyGraphics{ public: double scaleX; double scaleY; double translateX; double translateY; int windowWidth; int windowHeight; double r, g, b; MyGraphics(double x0, double x1, double y0, double y1): scaleX(1.0), scaleY(1.0), translateX(0.0), translateY(0.0) { } //--------------------------------------------------- void setDimensions(int width, int height){ windowWidth = width; windowHeight = height; } //--------------------------------------------------- void fit(double x0, double x1, double y0, double y1, double left, double right, double bottom, double top){ scaleX = (right-left) / (x1-x0); scaleY = (top-bottom) / (y1-y0); translateX = left - scaleX*x0; translateY = bottom - scaleY*y0; resize(); } //--------------------------------------------------- void fitToWindow(double x0, double x1, double y0, double y1){ fit(x0, x1, y0, y1, 0.5, double(windowWidth) - 0.5, double(windowHeight) - 0.5, 0.5); } //--------------------------------------------------- void translate(double x, double y){ translateX += x*scaleX; translateY += y*scaleY; } //--------------------------------------------------- void pick(double x, double y, complex& result){ result[0] = (x-translateX)/scaleX; result[1] = (y-translateY)/scaleY; } //--------------------------------------------------- void unpick(double x, double y, complex& result){ result[0] = x*scaleX + translateX; result[1] = y*scaleY + translateY; } //--------------------------------------------------- bool isTrivial(complex p0, complex p1, complex p2){ int x0=p0[0]*scaleX + translateX; int x1=p1[0]*scaleX + translateX; int x2=p2[0]*scaleX + translateX; int y0=p0[1]*scaleY + translateY; int y1=p1[1]*scaleY + translateY; int y2=p2[1]*scaleY + translateY; int maxX, maxY, minX, minY; if(x0>x1){ if(x0>x2) maxX = x0; else maxX = x2; }else{ if(x1>x2) maxX = x1; else maxX = x2; } if(y0>y1){ if(y0>y2) maxY = y0; else maxY = y2; }else{ if(y1>y2) maxY = y1; else maxY = y2; } if(x0& normalAndTangent, complex& scratch, complex& result) { // coeff of normal vector: -(1-cos(t*c))/c scratch[0] = -cosf1_over_x(t*c) * t; // coeff of tangent vector: sin(t*c)/c scratch[1] = sin_over_x(t*c) * t; vxm(result, scratch, normalAndTangent); result[0] += focusX; result[1] += focusY; } // findPointOnArc void smartDrawArcNonClipped( double focusX, double focusY, // a point on the arc double focusAngleRadians, // from center to focus double c, // curvature, i.e. 1/radius double start, double end) { int nSegs = MAX(10, (int)(abs((end-start)*c) / (1*(PI/180)))); // 1 degree-- delicate tradeoff, if we make it too fine, it looks all warbly because the endpoints are snapped to unit pixel coords XXX but this comment is no longer true since we are using our own antialiased line procedure double cosFocusAngle = cos(focusAngleRadians); double sinFocusAngle = sin(focusAngleRadians); Vector normalAndTangent(2); normalAndTangent[0][0] = cosFocusAngle; normalAndTangent[0][1] = sinFocusAngle; normalAndTangent[1][0] = -sinFocusAngle; normalAndTangent[1][1] = cosFocusAngle; complex prevPoint; complex point; complex scratch; for (int i = 0; i < nSegs+1; ++i) { double t = LERP(start, end, (double)i/nSegs); findPointOnArc(t, focusX, focusY, c, normalAndTangent, scratch, point); if (i > 0) this->drawLine(prevPoint[0], prevPoint[1], point[0], point[1]); prevPoint = point; } } // smartDrawArcNonClipped //--------------------------------------------------- void smartDrawArcClipped(double focusX, double focusY, double focusAngleRadians, // from center to focus double c, double start, double end, double clipX0, double clipX1, double clipY0, double clipY1, double slack) { //System.out.println(" in smartDrawArcClipped"); // I don't know how to clip analytically yet, // so do the following instead: // while an endpoint is outside the clip area, // cut off that length of the arc. // This will exponentially approach the clip rectangle // (or exponentially recede from it). // Stop when our distance from the clip rectangle // is <= slack (typically 2 pixels). double clipMinX = MIN(clipX0, clipX1); double clipMaxX = MAX(clipX0, clipX1); double clipMinY = MIN(clipY0, clipY1); double clipMaxY = MAX(clipY0, clipY1); if (c * abs(end-start) > 2*PI){ start = 0.; end = 2*PI / c; } int dir = (start <= end ? 1 : -1); double normalX = cos(focusAngleRadians); double normalY = sin(focusAngleRadians); Vector normalAndTangent(2); normalAndTangent[0][0] = normalX; normalAndTangent[0][1] = normalY; normalAndTangent[1][0] = -normalY; normalAndTangent[1][1] = normalX; complex point; complex scratch; while(true){ findPointOnArc(start, focusX, focusY, c, normalAndTangent, scratch, point); double startX = point[0]; double startY = point[1]; //System.out.println("startX = "+startX); //System.out.println("startY = "+startY); double dist = MAX4(startX-clipMaxX, clipMinX-startX, startY-clipMaxY, clipMinY-startY); //System.out.println("dist = "+dist); if (dist <= slack) break; start += dir * dist; //System.out.println("new start="+start); if (dir * (end-start) < 0) { //System.out.println(" out smartDrawArcClipped (without drawing)"); return; } } while (true) { //System.out.println("start="+start); //System.out.println("end="+end); findPointOnArc(end, focusX, focusY, c, normalAndTangent, scratch, point); double endX = point[0]; double endY = point[1]; //System.out.println("endX = "+endX); //System.out.println("endY = "+endY); double dist = MAX4(endX-clipMaxX, clipMinX-endX, endY-clipMaxY, clipMinY-endY); if (dist <= slack) break; end -= dir * dist; //System.out.println("new end="+end); if (dir * (end-start) < 0) { //System.out.println(" out smartDrawArcClipped (without drawing."); return; } } // Prevent huge circles that start and end in the clip rectangle. // Note this doesn't clip away all arc segments // that leave the clip rectangle, but it does // guarantee that any it misses won't be much larger // than the clip rectangle (and usually much smaller). { double mid = (start+end)*.5; findPointOnArc(mid, focusX, focusY, c, normalAndTangent, scratch, point); double midX = point[0]; double midY = point[1]; double dist = MAX4(midX-clipMaxX, clipMinX-midX, midY-clipMaxY, clipMinY-midY); if (dist > slack) { smartDrawArcClipped(focusX, focusY, focusAngleRadians, c, start, mid, clipX0, clipX1, clipY0, clipY1, slack); smartDrawArcClipped(focusX, focusY, focusAngleRadians, c, mid, end, clipX0, clipX1, clipY0, clipY1, slack); //System.out.println(" out smartDrawArcClipped"); return; } } smartDrawArcNonClipped(focusX,focusY, focusAngleRadians, c, start,end); //System.out.println(" out smartDrawArcClipped"); } // smartDrawArcClipped //--------------------------------------------------- void drawArc(double x, double y, double width, double height, double startRadians, double arcRadians) { double centerX = x + width*.5; double centerY = y + height*.5; double radius = .25*(width+height); double curvature = 1./radius; double focusAngleRadians = startRadians; double start = 0.; double end = arcRadians * radius; double focusX = centerX + radius * cos(focusAngleRadians); double focusY = centerY + radius * sin(focusAngleRadians); smartDrawArcNonClipped(focusX, focusY, focusAngleRadians, curvature, start, end); return; } //--------------------------------------------------- void smartDrawArc(double focusX, double focusY, double focusAngleRadians, double c, double start, double end) { //System.out.println("scaleX="+scaleX); //System.out.println("scaleY="+scaleY); //System.out.println(" in smartDrawArc"); smartDrawArcClipped(focusX, focusY, focusAngleRadians, c, start, end, (0-translateX)/scaleX, (windowWidth-translateX)/scaleX, (0-translateY)/scaleY, (windowHeight-translateY)/scaleY, 2./((abs(scaleX)+abs(scaleY))*.5)); // 2 pixels //System.out.println(" out smartDrawArc"); } // smartDrawArc //--------------------------------------------------- //functions to be overridden by descendants virtual void beginDraw() { } //OpenGL will definitely use these two virtual void endDraw() { } virtual void setColor(unsigned long color) = 0; virtual int getColor() = 0; virtual void fillWindow(unsigned long color) = 0; virtual void drawLine(double x0, double y0, double x1, double y1) = 0; virtual void drawPoint(double x, double y, int nPixels) = 0; virtual void setLineThickness(int t) = 0; virtual void drawString(String s, double x, double y) = 0; virtual void drawStringCentered(String str, double x, double y) = 0; virtual void floodFill(double x, double y) = 0; virtual void doPaint(HWND handle) { } virtual void resize() { } virtual void setAntiAlias(bool OnOff) { } }; //----------------------------------------------------------------- //Windows GDI driver (well, actually it's Borland's API) class MyGraphicsGDI : public MyGraphics { private: Graphics::TBitmap* backBuffer; bool antiAlias; public: MyGraphicsGDI(double x0, double x1, double y0, double y1): MyGraphics(x0, x1, y0, y1) { backBuffer = new Graphics::TBitmap; backBuffer->PixelFormat = pf24bit; windowWidth = 100; windowHeight = 100; antiAlias = false; fitToWindow(x0, x1, y0, y1); } //--------------------------------------------------- ~MyGraphicsGDI(){ delete backBuffer; } //--------------------------------------------------- void setAntiAlias(bool OnOff){ antiAlias = OnOff; } void fillWindow(unsigned long color){ backBuffer->Canvas->Brush->Color = (TColor)color; backBuffer->Canvas->FillRect(::Rect(0, 0, windowWidth, windowHeight)); } //--------------------------------------------------- void drawLine_old(double x0, double y0, double x1, double y1){ backBuffer->Canvas->MoveTo((int)(x0 * scaleX + translateX), (int)(y0 * scaleY + translateY)); backBuffer->Canvas->LineTo((int)(x1 * scaleX + translateX), (int)(y1 * scaleY + translateY)); } //--------------------------------------------------- //useless? void drawPoint(double x, double y, int nPixels){ int x0 = (int)(x * scaleX + translateX - .5*nPixels); int y0 = (int)(y * scaleY + translateY - .5*nPixels); //backBuffer->Canvas->FillRect(::Rect(x0, y0, nPixels, nPixels)); SetPixel(backBuffer->Canvas->Handle, x0, y0, (COLORREF)backBuffer->Canvas->Pen->Color); } //--------------------------------------------------- void setColor(unsigned long color){ backBuffer->Canvas->Pen->Color = (TColor)color; r = (color & 0xFF); g = (color & 0xFF00) >> 8; b = (color & 0xFF0000) >> 16; } //--------------------------------------------------- int getColor(){ return backBuffer->Canvas->Pen->Color; } //--------------------------------------------------- void setLineThickness(int t){ backBuffer->Canvas->Pen->Width = t; } //--------------------------------------------------- void resize(){ backBuffer->Width = windowWidth; backBuffer->Height = windowHeight; } //--------------------------------------------------- void doPaint(HWND handle){ BitBlt(handle, 0, 0, windowWidth, windowHeight, backBuffer->Canvas->Handle, 0, 0, SRCCOPY); } //--------------------------------------------------- void drawString(String s, double x, double y){ backBuffer->Canvas->Font->Color = backBuffer->Canvas->Pen->Color; backBuffer->Canvas->TextOut((int)(x * scaleX + translateX), (int)(y * scaleY + translateY), s); } void drawStringCentered(String s, double x, double y){ backBuffer->Canvas->Font->Color = backBuffer->Canvas->Pen->Color; backBuffer->Canvas->TextOut((int)(x * scaleX + translateX)-backBuffer->Canvas->TextWidth(s)/2, (int)(y * scaleY + translateY)-backBuffer->Canvas->TextHeight(s)/2, s); } //--------------------------------------------------- void floodFill(double x, double y){ int X=(int)(x * scaleX + translateX), Y=(int)(y * scaleY + translateY); if(backBuffer->Canvas->Pixels[X][Y] == backBuffer->Canvas->Pen->Color) return; backBuffer->Canvas->Brush->Color = (TColor)0xAA0070; //backBuffer->Canvas->Pen->Color; backBuffer->Canvas->FloodFill(X, Y, backBuffer->Canvas->Pen->Color, fsBorder); } //--------------------------------------------------- //Set an alpha-pixel void SetPixelA(int x1, int y1, int col1, int a1){ int r1 = (col1 & 0xFF); int g1 = (col1 & 0xFF00) >> 8; int b1 = (col1 & 0xFF0000) >> 16; int col2, r, g, b, diff; col2 = GetPixel(backBuffer->Canvas->Handle, x1, y1); //col2 = backBuffer->Canvas->Pixels[x1][y1]; b = (col2 >> 16) & 0xFF; g = (col2 >> 8) & 0xFF; r = col2 & 0xFF; diff = ((b1 - b) * a1) >> 8; b = b + diff; diff = ((g1 - g) * a1) >> 8; g = g + diff; diff = ((r1 - r) * a1) >> 8; r = r + diff; SetPixel(backBuffer->Canvas->Handle, x1, y1, RGB(r, g, b)); } //--------------------------------------------------- void drawLine(double x0, double y0, double x1, double y1){ if (antiAlias){ if (x0 == x1 && y0 == y1){ drawPoint(x0, y0, 1); return; } x0 = (x0 * scaleX + translateX - .5); y0 = (y0 * scaleY + translateY - .5); x1 = (x1 * scaleX + translateX - .5); y1 = (y1 * scaleY + translateY - .5); int savedColor = backBuffer->Canvas->Pen->Color; if(abs(y1-y0) >= abs(x1-x0)){ //int Y0 = (int)(y0+.5); //int Y1 = (int)(y1+.5); int Y0 = (int)(y0); int Y1 = (int)(y1); int dirY = (Y0 <= Y1 ? 1 : -1); double slope = (x1-x0)/(y1-y0); for(int Y = Y0; Y != Y1; Y += dirY){ double x = x0 + (Y-y0) * slope; int X = (int)floor(x); double frac = x-X; int alpha = (int)(frac * 256.); SetPixelA(X, Y, savedColor, 255-alpha); SetPixelA(X+1, Y, savedColor, alpha); } }else{ //int X0 = (int)(x0+.5); //int X1 = (int)(x1+.5); int X0 = (int)(x0); int X1 = (int)(x1); int dirX = (X0 <= X1 ? 1 : -1); double slope = (y1-y0)/(x1-x0); for (int X = X0; X != X1; X += dirX){ double y = y0 + (X-x0) * slope; int Y = (int)floor(y); double frac = y-Y; int alpha = (int)(frac * 256.); SetPixelA(X, Y, savedColor, 255-alpha); SetPixelA(X, Y+1, savedColor, alpha); } } }else{ int X=(x1 * scaleX + translateX), Y=(y1 * scaleY + translateY); SetPixel(backBuffer->Canvas->Handle, X, Y, backBuffer->Canvas->Pen->Color); backBuffer->Canvas->MoveTo((int)(x0 * scaleX + translateX), (int)(y0 * scaleY + translateY)); backBuffer->Canvas->LineTo(X, Y); } } //--------------------------------------------------- }; //--------------------------------------------------------------------------- //OpenGL driver class MyGraphicsOGL : public MyGraphics { private: HWND myWindow; HGLRC ghRC; HDC ghDC; double thickness; public: MyGraphicsOGL(HWND window, double x0, double x1, double y0, double y1): MyGraphics(x0, x1, y0, y1) { myWindow = window; ghDC = GetDC(myWindow); windowWidth = 100; windowHeight = 100; thickness = 1.0; static PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), 1, // Version number PFD_DRAW_TO_WINDOW | // Format must support window PFD_SUPPORT_OPENGL | // Format must support OpenGL PFD_DOUBLEBUFFER, // Must support double buffering PFD_TYPE_RGBA, // Request an RGBA format 32, // Select our color depth 0, 0, 0, 0, 0, 0, // Color bits ignored 0, // No alpha buffer 0, // Shift bit ignored 0, // No accumulation buffer 0, 0, 0, 0, // Accumulation bits ignored 16, // 16Bit Z-Buffer (Depth buffer) 0, // No stencil buffer 0, // No auxiliary buffer PFD_MAIN_PLANE, // Main drawing layer 0, // Reserved 0, 0, 0 // Layer masks ignored }; int pixelformat; if((pixelformat = ChoosePixelFormat(ghDC, &pfd)) == 0){ MessageBox(myWindow, "ChoosePixelFormat failed", "Error", MB_ICONEXCLAMATION); return; } if (SetPixelFormat(ghDC, pixelformat, &pfd) == FALSE){ MessageBox(myWindow, "SetPixelFormat failed", "Error", MB_ICONEXCLAMATION); return; } ghRC = wglCreateContext(ghDC); wglMakeCurrent(ghDC, ghRC); glShadeModel(GL_SMOOTH); // Enable smooth shading glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black background glClearDepth(1.0f); // Depth buffer setup glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); // Set line antialiasing glEnable(GL_BLEND); // Enable blending glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Type of blending to use glLineWidth(1.0f); glDisable(GL_LINE_SMOOTH); // Disable antialiasing for now fitToWindow(x0, x1, y0, y1); } ~MyGraphicsOGL(){ if(ghRC) wglDeleteContext(ghRC); if(ghDC) ReleaseDC(myWindow, ghDC); } void fillWindow(unsigned long color){ glClearColor((color&0xFF)/255.0,((color&0xFF00)>>8)/255.0,((color&0xFF0000)>>16)/255.0, 0.5f); } void drawLine(double x0, double y0, double x1, double y1){ glVertex2d(x0 * scaleX + translateX, y0 * scaleY + translateY); glVertex2d(x1 * scaleX + translateX, y1 * scaleY + translateY); } void drawPoint(double x, double y, int nPixels){ glVertex2d(x * scaleX + translateX, y * scaleY + translateY); glVertex2d(x * scaleX + translateX + nPixels, y * scaleY + translateY + nPixels); } void setColor(unsigned long color){ r = (color & 0xFF); g = (color & 0xFF00) >> 8; b = (color & 0xFF0000) >> 16; glColor3f(r/255.0,g/255.0,b/255.0); } int getColor(){ int ret=0; ret += ((int)b) << 16; ret += ((int)g) << 8; ret += ((int)r); return ret; } void setLineThickness(int t){ thickness = double(t); glLineWidth(thickness); } //--------------------------------------------------- void resize(){ if (windowHeight==0) windowHeight=1; glViewport(0,0,windowWidth,windowHeight); // Reset the current viewport glMatrixMode(GL_PROJECTION); // Select the projection matrix glLoadIdentity(); // Reset the projection matrix glOrtho(0.0f,windowWidth,windowHeight,0.0f,-1.0f,1.0f); // Create ortho 640x480 view (0,0 At top left) glMatrixMode(GL_MODELVIEW); // Select the modelview matrix glLoadIdentity(); // Reset the modelview matrix } void setAntiAlias(bool OnOff){ if(OnOff){ glLineWidth(thickness); glEnable(GL_LINE_SMOOTH); // Enable antialiasing }else{ glLineWidth(thickness); glDisable(GL_LINE_SMOOTH); // Disable antialiasing } } void beginDraw(){ glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glLoadIdentity(); glTranslatef(0.0,0.0,0.0); glBegin(GL_LINES); } void endDraw(){ glEnd(); //SwapBuffers(ghDC); } void doPaint(HWND){ //dirty SwapBuffers(ghDC); } void drawString(String s, double x, double y){ } void drawStringCentered(String s, double x, double y){ } void floodFill(double x, double y){ } }; //--------------------------------------------------------------------------- #endif