]> git.sesse.net Git - sproing/commitdiff
Initial checkin for move to Git (no prior version history available). master
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Tue, 22 Jan 2013 16:18:14 +0000 (17:18 +0100)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Tue, 22 Jan 2013 16:18:14 +0000 (17:18 +0100)
LICENSE [new file with mode: 0644]
glwindow.cpp [new file with mode: 0644]
glwindow.h [new file with mode: 0644]
main.cpp [new file with mode: 0644]
spec.txt [new file with mode: 0644]

diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..6f1f2cd
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,2 @@
+Copyright 2007 Steinar H. Gunderson.
+Licensed under the GPL, v2.
diff --git a/glwindow.cpp b/glwindow.cpp
new file mode 100644 (file)
index 0000000..eec09ca
--- /dev/null
@@ -0,0 +1,436 @@
+/*
+ * The whole interface between GLWindow and the configuration stuff is rather
+ * icky, and _should_ be rewritten. It appears to work somehow, though ;-)
+ */
+
+#include <stdio.h>
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+#ifdef __linux__
+#include <unistd.h>
+#include <GL/glx.h>
+#include <X11/extensions/xf86vmode.h>
+#include <X11/keysym.h>
+#endif
+
+#include <GL/gl.h>
+#include <GL/glu.h>
+
+#include "glwindow.h"
+
+#ifdef WIN32
+LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
+
+/* this is ugly, but so is Win32 ;-) */
+GLWindow *win;
+#endif
+
+#define DEMOLIB_XASPECT 4.0
+#define DEMOLIB_YASPECT 3.0
+
+void GLWindow::resize(int x, int y, int w, int h)
+{
+       /* Prevent division by zero */
+       if (h == 0) {
+               h = 1;
+       }
+
+       float aspect = (float)w / (float)h;
+       if (aspect > DEMOLIB_XASPECT / DEMOLIB_YASPECT) {
+               int new_w = (int)((float)h * DEMOLIB_XASPECT / DEMOLIB_YASPECT);
+               x += (w - new_w) / 2;
+               w = new_w;
+       } else if (aspect < DEMOLIB_XASPECT / DEMOLIB_YASPECT) {
+               int new_h = (int)((float)w * DEMOLIB_YASPECT / DEMOLIB_XASPECT);
+               y += (h - new_h) / 2;
+               h = new_h;
+       }
+       glViewport(x, y, w, h);
+
+       glMatrixMode(GL_PROJECTION);
+       glLoadIdentity();
+        gluPerspective(53.0f, (GLfloat)w / (GLfloat)h, 1.0f, 500.0f);
+       glMatrixMode(GL_MODELVIEW);
+       glLoadIdentity();
+
+#ifdef __linux__
+//     XClearWindow(this->dpy, this->win);
+#endif
+}
+
+GLWindow::GLWindow(const char *title, int width, int height, int bpp, bool fullscreen, int zbuffer, int visual_id)
+{
+#ifdef WIN32
+       GLuint PixelFormat;
+       WNDCLASS wc;
+       DWORD dwExStyle;
+       DWORD dwStyle;
+       DEVMODE dmScreenSettings;
+
+       if (visual_id != -1) {
+               EnumDisplaySettings(NULL, visual_id, &dmScreenSettings);
+               width = dmScreenSettings.dmPelsWidth;
+               height = dmScreenSettings.dmPelsHeight;
+               bpp = dmScreenSettings.dmBitsPerPel;
+       } else {
+               memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
+               dmScreenSettings.dmSize = sizeof(dmScreenSettings);
+               dmScreenSettings.dmPelsWidth = width;
+               dmScreenSettings.dmPelsHeight = height;
+               dmScreenSettings.dmBitsPerPel = bpp;
+               dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
+       }
+       win = this;
+       
+       RECT WindowRect;
+       WindowRect.left = (long)0;
+       WindowRect.right = (long)width;
+       WindowRect.top = (long)0;
+       WindowRect.bottom = (long)height;
+       
+#endif /* WIN32 */     
+#ifdef __linux__
+       XVisualInfo *vi;
+       int dpyWidth = 0, dpyHeight = 0;
+       int i;
+       XF86VidModeModeInfo **modes;
+       int modeNum;
+       int bestMode;
+       Atom wmDelete;
+       Window winDummy;
+       unsigned int borderDummy;
+
+       static int attrList[] = {
+               GLX_RGBA, 
+               GLX_RED_SIZE, 1,
+               GLX_GREEN_SIZE, 1,
+               GLX_BLUE_SIZE, 1,
+               GLX_DOUBLEBUFFER,
+               GLX_DEPTH_SIZE, zbuffer,
+               GLX_STENCIL_SIZE, 4,
+               None
+       };
+#endif /* __linux__ */
+
+       this->x = 0;
+       this->y = 0;
+       this->width = width;
+       this->height = height;
+       this->bpp = bpp;
+       this->fullscreen = fullscreen;
+       this->zbuffer = zbuffer;
+       this->done = 0;
+       
+#ifdef WIN32
+       this->hInstance = GetModuleHandle(NULL);
+       wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
+       wc.lpfnWndProc = WndProc;
+       wc.cbClsExtra = 0;
+       wc.cbWndExtra = 0;
+       wc.hInstance = this->hInstance;
+       wc.hIcon = NULL;
+       wc.hCursor = NULL;
+       wc.hbrBackground = NULL;
+       wc.lpszMenuName = NULL;
+       wc.lpszClassName = "Excess-OGL";
+
+       if( !RegisterClass(&wc) ) throw "Couldn't register Window Class";
+
+#endif /* WIN32 */
+#ifdef __linux__
+       /* set best mode to current */
+       bestMode = 0;
+
+       /* get a connection */
+       this->dpy = XOpenDisplay(0);
+       this->screen = DefaultScreen(this->dpy);
+
+       if (fullscreen) {
+               XF86VidModeGetAllModeLines(this->dpy, this->screen, &modeNum, &modes);
+
+               /* save desktop-resolution before switching modes */
+               this->deskMode = *modes[0];
+
+               /* look for mode with requested resolution */
+               for (i = 0; i < modeNum; i++) {
+                       if ((modes[i]->hdisplay == width) && (modes[i]->vdisplay == height)) {
+                               bestMode = i;
+                       }
+               }
+
+               /* if we don't have it, bomb out */
+               if (bestMode == 0 && (modes[0]->hdisplay != width || modes[0]->vdisplay != height)) {
+                       throw "Couldn't set requested screen mode.";
+               }
+       }
+
+       if (visual_id != -1) {
+               XVisualInfo tmplate;
+               int nret;
+               
+               tmplate.visualid = visual_id;
+               vi = XGetVisualInfo(this->dpy, VisualIDMask, &tmplate, &nret);
+               if (vi == NULL) {
+                       throw "Couldn't get selected visual!";
+               }
+       } else {
+               /* get an appropriate visual */
+               vi = glXChooseVisual(this->dpy, this->screen, attrList);
+               if (vi == NULL) {
+                       throw "Couldn't get double-buffered visual";
+               }
+       }
+
+       /* create a GLX context */
+       this->ctx = glXCreateContext(this->dpy, vi, NULL, GL_TRUE);
+
+       /* create a color map (umm, needed?) */
+       Colormap cmap = XCreateColormap(this->dpy, RootWindow(this->dpy, vi->screen),
+               vi->visual, AllocNone);
+       this->attr.colormap = cmap;
+
+       /* make a blank cursor */
+       {
+               static char data[1] = {0};
+               Cursor cursor;
+               Pixmap blank;
+               XColor dummy;
+
+               blank = XCreateBitmapFromData(this->dpy, RootWindow(this->dpy, vi->screen), data, 1, 1);
+               if (blank == None)
+                       throw "Out of memory!";
+               cursor = XCreatePixmapCursor(this->dpy, blank, blank, &dummy, &dummy, 0, 0);
+               XFreePixmap(this->dpy, blank);
+               this->attr.cursor = cursor;
+       }
+               
+       this->attr.border_pixel = 0;
+#endif /* __linux__ */
+
+       /* change screen mode */        
+       if (fullscreen) {
+#ifdef WIN32
+               if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
+                       throw "Couldn't set requested screen mode.";
+               }
+#endif /* WIN32 */
+#ifdef __linux__
+               XF86VidModeSwitchToMode(this->dpy, this->screen, modes[bestMode]);
+               XF86VidModeSetViewPort(this->dpy, this->screen, 0, 0);
+               dpyWidth = modes[bestMode]->hdisplay;
+               dpyHeight = modes[bestMode]->vdisplay;
+               XFree(modes);
+#endif /* __linux__ */
+       }
+
+       /* create the window */
+#ifdef WIN32
+       if (fullscreen) {
+               dwExStyle = WS_EX_APPWINDOW;
+               dwStyle = WS_POPUP;
+               ShowCursor(FALSE);
+       } else {
+               dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
+               dwStyle = WS_OVERLAPPEDWINDOW;
+       }
+
+       AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);
+
+       if (!(hWnd = CreateWindowEx(dwExStyle,
+                                   "Excess-OGL",
+                                   title,
+                                   dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
+                                   0, 0,
+                                   WindowRect.right - WindowRect.left,
+                                   WindowRect.bottom - WindowRect.top,
+                                   NULL,
+                                   NULL,
+                                   this->hInstance,
+                                   NULL))) {
+               delete this;
+               throw "Could not change screenmode";
+       }
+#endif
+#ifdef __linux__
+       this->attr.background_pixel = 0;
+
+       if (fullscreen) {
+               /* create a fullscreen window */
+               this->attr.override_redirect = True;
+               this->attr.event_mask = KeyPressMask | ButtonPressMask |
+                                       StructureNotifyMask;
+
+               this->win = XCreateWindow(this->dpy, RootWindow(this->dpy, vi->screen),
+                       0, 0, dpyWidth, dpyHeight, 0, vi->depth, InputOutput, vi->visual,
+                       CWColormap | CWCursor | CWEventMask | CWOverrideRedirect,
+                       &this->attr);
+               XWarpPointer(this->dpy, None, this->win, 0, 0, 0, 0, 0, 0);
+               XMapRaised(this->dpy, this->win);
+               XGrabKeyboard(this->dpy, this->win, True, GrabModeAsync,
+                       GrabModeAsync, CurrentTime);
+               XGrabPointer(this->dpy, this->win, True, ButtonPressMask,
+                       GrabModeAsync, GrabModeAsync, this->win, None, CurrentTime);
+       } else {
+               /* create a window in window mode*/
+               this->attr.event_mask = KeyPressMask | ButtonPressMask |
+                       StructureNotifyMask;
+               this->win = XCreateWindow(this->dpy, RootWindow(this->dpy, vi->screen),
+                       0, 0, width, height, 0, vi->depth, InputOutput, vi->visual,
+                       CWColormap | CWBorderPixel | CWEventMask, &this->attr);
+
+               /* only set window title and handle wm_delete_events if in windowed mode */
+               wmDelete = XInternAtom(this->dpy, "WM_DELETE_WINDOW", True);
+               XSetWMProtocols(this->dpy, this->win, &wmDelete, 1);
+               XSetStandardProperties(this->dpy, this->win, title,
+                       title, None, NULL, 0, NULL);
+               XMapRaised(this->dpy, this->win);
+       }
+#endif /* __linux__ */
+
+#ifdef WIN32
+       static PIXELFORMATDESCRIPTOR pfd = {
+               sizeof(PIXELFORMATDESCRIPTOR),
+               1,
+               PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
+               PFD_TYPE_RGBA,
+               bpp,
+               0, 0, 0, 0, 0, 0,
+               0,
+               0,
+               0,
+               0, 0, 0, 0,
+               zbuffer,
+               8,
+               0,
+               PFD_MAIN_PLANE,
+               0,
+               0, 0, 0
+       };
+       
+       hDC = GetDC(hWnd);
+       PixelFormat = ChoosePixelFormat(hDC, &pfd);
+       if (PixelFormat == 0) {
+               throw "Could not find a usable pixelformat";
+       }
+       SetPixelFormat(hDC, PixelFormat, &pfd);
+       hRC = wglCreateContext(hDC);
+       wglMakeCurrent(hDC, hRC);
+       ShowWindow(hWnd, SW_SHOW);
+       SetForegroundWindow(hWnd);
+       SetFocus(hWnd);
+
+       SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
+#endif /* WIN32        */
+#ifdef __linux__
+       /* connect the glx-context to the window */
+       glXMakeCurrent(this->dpy, this->win, this->ctx);
+       XClearWindow(this->dpy, this->win);
+       XGetGeometry(this->dpy, this->win, &winDummy, &this->x, &this->y,
+               &this->width, &this->height, &borderDummy, &this->bpp);
+       if (!glXIsDirect(this->dpy, this->ctx)) {
+               throw "No direct rendering (hardware acceleration) available!";
+       }
+
+       nice(-7);
+#endif /* __linux__ */
+
+       this->resize(0, 0, this->width, this->height);
+}
+
+GLWindow::~GLWindow()
+{
+#ifdef __linux__
+       if (this->ctx) {
+               if (!glXMakeCurrent(this->dpy, None, NULL)) {
+                       throw "Could not release drawing context.";
+               }
+               glXDestroyContext(this->dpy, this->ctx);
+               this->ctx = NULL;
+       }
+#endif
+
+       if (fullscreen) {
+#ifdef __linux__
+               XF86VidModeSwitchToMode(this->dpy, this->screen, &this->deskMode);
+               XF86VidModeSetViewPort(this->dpy, this->screen, 0, 0);
+#endif
+#ifdef WIN32
+               ChangeDisplaySettings(NULL,0);
+               ShowCursor(TRUE);
+#endif
+       }
+
+#ifdef __linux__
+       XCloseDisplay(this->dpy);
+#endif
+
+#ifdef WIN32
+       if (hRC) {
+               wglMakeCurrent(NULL, NULL);
+               wglDeleteContext(hRC);
+               hRC = NULL;
+       }
+       
+       if (hDC != NULL && ReleaseDC(hWnd, hDC)) hDC = NULL;
+       if (hWnd != NULL && DestroyWindow(hWnd)) hWnd = NULL;
+       UnregisterClass("Excess-OGL", hInstance);
+#endif
+}
+
+void GLWindow::flip()
+{
+#ifdef WIN32
+       MSG msg;
+       if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+               if (msg.message == WM_QUIT) {
+                       this->done = TRUE;
+               } else {
+                       TranslateMessage(&msg);
+                       DispatchMessage(&msg);
+               }
+       }
+       SwapBuffers(this->hDC);
+#endif
+#ifdef __linux__
+       glXSwapBuffers(this->dpy, this->win);
+#endif
+}
+
+bool GLWindow::is_done()
+{
+       return this->done;
+}
+
+#ifdef WIN32
+LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+       switch (uMsg) {
+       case WM_SYSCOMMAND:
+               switch (wParam) {
+               case SC_SCREENSAVE:
+               case SC_MONITORPOWER:
+                       return 0;
+               }
+               break;
+
+       case WM_CLOSE:
+               PostQuitMessage(0);
+               return 0;
+
+       case WM_KEYUP:
+               if (wParam == VK_ESCAPE)
+                       PostQuitMessage(0);
+               return 0;
+
+       case WM_SIZE:
+               win->resize(0, 0, LOWORD(lParam), HIWORD(lParam));
+               return 0;
+       }
+
+       return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+#endif //WIN32
diff --git a/glwindow.h b/glwindow.h
new file mode 100644 (file)
index 0000000..5a3162d
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef _GLWINDOW_H
+#define _GLWINDOW_H
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+#ifdef __linux__
+#include <GL/glx.h>
+#include <X11/extensions/xf86vmode.h>
+#include <X11/keysym.h>
+#endif
+
+#include <GL/gl.h>
+#include <GL/glu.h>
+
+class GLWindow {
+public:
+       GLWindow(const char *title, int width, int height, int bpp, bool fullscreen, int zbuffer, int visual_id);
+       ~GLWindow();
+       void resize(int x, int y, int w, int h);
+       void flip();
+       bool is_done();
+
+       friend class DemoHandler;
+       friend class DirectSoundAudioDriver;
+       
+protected:
+#ifdef WIN32
+       HDC hDC;
+       HGLRC hRC;
+       HWND hWnd;
+       HINSTANCE hInstance;
+#endif
+#ifdef __linux__
+       Display *dpy;
+       int screen;
+       Window win;
+       GLXContext ctx;
+       XSetWindowAttributes attr;
+       Bool fs;
+       XF86VidModeModeInfo deskMode;
+#endif                                             
+       
+       char *title;
+       bool fullscreen;
+       int x, y;
+       unsigned int width, height;
+       unsigned int bpp;
+       int zbuffer;
+       bool done;
+       void initGL();
+};
+
+#endif
diff --git a/main.cpp b/main.cpp
new file mode 100644 (file)
index 0000000..3e197d0
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,267 @@
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <vector>
+#include <algorithm>
+#include "glwindow.h"
+
+struct point {
+       double x, y;
+       double px, py;
+       double ax, ay;
+};
+struct measurement {
+       int p1, p2;
+       double d;
+       double stress;
+       double alpha;
+};
+struct angle {
+       int p1, p2, p3;
+       double rad;
+       double stress;
+       double alpha;
+};
+std::vector<point> points;
+std::vector<measurement> measurements;
+std::vector<angle> angles;
+
+void reload()
+{
+       FILE *file = fopen("spec.txt", "r");
+       unsigned num_points;
+       fscanf(file, "%u", &num_points);
+
+       if (num_points < points.size()) {
+               points.erase(points.begin() + num_points, points.end());
+       } else if (num_points > points.size()) {
+               for (int i = points.size(); i < num_points; ++i) {
+                       point p;
+                       if (i == 0) {
+                               p.x = p.y = 0.0;
+                       } else {
+                               p.x = rand() / (RAND_MAX + 1.0);
+                               p.y = rand() / (RAND_MAX + 1.0);
+                       }
+                       if (i == 1) {
+                               p.y = 0.0;
+                       }
+                       p.px = p.x;
+                       p.py = p.y;
+                       points.push_back(p);
+               }
+       }
+
+       measurements.erase(measurements.begin(), measurements.end());
+       angles.erase(angles.begin(), angles.end());
+
+       for ( ;; ) {
+               char buf[256];
+               if (fscanf(file, "%s", &buf) != 1)
+                       break;
+
+               if (buf[0] == 'd') {
+                       measurement l;
+                       if (fscanf(file, "%u %u %lf %lf", &l.p1, &l.p2, &l.d, &l.alpha) != 4)
+                               break;
+                       measurements.push_back(l);
+               }
+               if (buf[0] == 'a') {
+                       angle a;
+                       if (fscanf(file, "%u %u %u %lf %lf", &a.p1, &a.p2, &a.p3, &a.rad, &a.alpha) != 5)
+                               break;
+                       a.rad *= M_PI / 180.0;
+                       angles.push_back(a);
+               }
+       }
+
+       fclose(file);
+}
+
+void recalc()
+{
+       double dt = 1.0/60.0;
+       double k = 0.01;
+       double k_angle = 0.02;
+       double f = 0.02 * dt;
+
+       for (unsigned i = 0; i < points.size(); ++i) {
+               points[i].ax = points[i].ay = 0.0;
+       }
+
+       for (unsigned i = 0; i < measurements.size(); ++i) {
+               double dx = points[measurements[i].p2].x - points[measurements[i].p1].x;        
+               double dy = points[measurements[i].p2].y - points[measurements[i].p1].y;        
+               double d = sqrt(dx*dx + dy*dy);
+               measurements[i].stress = fabs(d - measurements[i].d);
+
+               points[measurements[i].p1].ax += (d - measurements[i].d) * dx/d * k;    
+               points[measurements[i].p1].ay += (d - measurements[i].d) * dy/d * k;    
+               points[measurements[i].p2].ax -= (d - measurements[i].d) * dx/d * k;    
+               points[measurements[i].p2].ay -= (d - measurements[i].d) * dy/d * k;
+       }
+       
+       for (unsigned i = 0; i < angles.size(); ++i) {
+               double cx = points[angles[i].p1].x - points[angles[i].p2].x;
+               double cy = points[angles[i].p1].y - points[angles[i].p2].y;
+               double c = sqrt(cx*cx + cy*cy);
+               
+               double bx = points[angles[i].p3].x - points[angles[i].p2].x;
+               double by = points[angles[i].p3].y - points[angles[i].p2].y;
+               double b = sqrt(bx*bx + by*by);
+
+               double alpha = acos((bx*cx + by*cy) / (b*c));
+               angles[i].stress = fabs(alpha - angles[i].rad);
+
+               double tp3x = -by / b;
+               double tp3y = bx / b;
+               
+               double tp1x = cy / c;
+               double tp1y = -cx / c;
+
+               points[angles[i].p1].ax -= (alpha - angles[i].rad) * tp1x * k_angle;    
+               points[angles[i].p1].ay -= (alpha - angles[i].rad) * tp1y * k_angle;    
+               points[angles[i].p3].ax -= (alpha - angles[i].rad) * tp3x * k_angle;    
+               points[angles[i].p3].ay -= (alpha - angles[i].rad) * tp3y * k_angle;
+       }
+
+       // constraints
+       if (points.size() >= 1) {
+               points[0].ax = points[0].ay = 0.0;
+       }
+       if (points.size() >= 2) {
+               points[1].ay = 0.0;
+       }
+
+       // Verlet integration
+       for (unsigned i = 0; i < points.size(); ++i) {
+               double nx = (2.0 - f) * points[i].x - (1.0 - f) * points[i].px + points[i].ax * dt * dt; 
+               double ny = (2.0 - f) * points[i].y - (1.0 - f) * points[i].py + points[i].ay * dt * dt; 
+               points[i].px = points[i].x;
+               points[i].py = points[i].y;
+               points[i].x = nx;
+               points[i].y = ny;
+       }
+       
+       if (points.size() >= 2) {
+               if (points[1].x < 0.0) {
+                       points[1].x = -points[1].x;
+                       points[1].px = -points[1].px;
+                       points[1].ax = -points[1].ax;
+               }
+       }
+}
+
+void redraw(GLWindow &win)
+{
+       glClear(GL_COLOR_BUFFER_BIT);
+
+       glEnable(GL_BLEND);
+       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       //glEnable(GL_LINE_SMOOTH);
+       //glLineWidth(5.0);
+
+       glMatrixMode(GL_PROJECTION);
+       glLoadIdentity();
+       glOrtho(-1.0, 1.0, -1.0, 1.0, 0.0, 1.0);
+
+       glMatrixMode(GL_MODELVIEW);
+       glLoadIdentity();
+       glScalef(0.1, 0.1, 0.1);
+
+       glPointSize(5.0);
+       glColor4f(1.0, 1.0, 1.0, 1.0);
+
+       glBegin(GL_POINTS);
+       for (int i = 0; i < points.size(); ++i) {
+               glVertex2f(points[i].x, points[i].y);
+       }
+       glEnd();
+
+       glBegin(GL_LINES);
+       for (int i = 0; i < measurements.size(); ++i) {
+               double sfac = std::min(5.0 * (fabs(measurements[i].stress) / measurements[i].d), 1.0);
+               double r, g, b;
+               if (sfac < 0.5) {
+                       r = 2.0 * sfac;
+                       g = 1.0;
+                       b = 0.0;
+               } else {
+                       r = 1.0;
+                       g = 1.0 - 2.0 * (sfac - 0.5);
+                       b = 0.0;
+               }
+
+               glColor4f(r, g, b, measurements[i].alpha);
+               glVertex2f(points[measurements[i].p1].x, points[measurements[i].p1].y);
+               glVertex2f(points[measurements[i].p2].x, points[measurements[i].p2].y);
+       }
+       glEnd();
+
+       win.flip();
+}
+
+void randomize(double Z)
+{
+       for (int i = 2; i < points.size(); ++i) {
+               points[i].x += Z * (rand() / (RAND_MAX + 1.0) - .5);
+               points[i].y += Z * (rand() / (RAND_MAX + 1.0) - .5);
+       }
+}
+
+void destress()
+{
+       double max_stress = 0.0;
+       int p1, p2;
+
+       for (int i = 0; i < measurements.size(); ++i) {
+               if (measurements[i].p1 <= 1 ||
+                   measurements[i].p2 <= 1)
+                       continue;
+               
+               if (measurements[i].stress > max_stress) {
+                       max_stress = measurements[i].stress;
+                       p1 = measurements[i].p1;
+                       p2 = measurements[i].p2;
+               }
+       }
+       
+       std::swap(points[p1], points[p2]);
+}
+
+int main(void)
+{      
+       GLWindow glw("foo", 640, 480, 32, false, 16, -1);
+       int one = 1;
+
+       srand((time_t)time(NULL));
+
+       ioctl(0, FIONBIO, &one);
+       reload();
+
+       for ( ;; ) {
+               char ch;
+               while (read(0, &ch, 1) == 1) {
+                       if (ch == 'r') {
+                               reload();
+                       }
+                       if (ch == 'z') {
+                               randomize(0.005);
+                       }
+                       if (ch == 'Z') {
+                               randomize(0.1);
+                       }
+                       if (ch == 's') {
+                               destress();
+                       }
+               }
+               recalc();
+               recalc();
+               recalc();
+               recalc();
+               recalc();
+               redraw(glw);
+       }
+}
diff --git a/spec.txt b/spec.txt
new file mode 100644 (file)
index 0000000..e6e7483
--- /dev/null
+++ b/spec.txt
@@ -0,0 +1,19 @@
+8
+d 0 1 2.1 1.0
+d 1 2 4.4 1.0
+d 2 3 3.1 1.0
+d 3 4 4.5 1.0
+d 4 5 2.2 1.0
+d 5 6 10.0 1.0
+d 6 7 2.2 1.0
+d 7 0 10.0 1.0
+d 0 2 5.5 0.5
+d 2 7 5.5 0.5
+d 3 5 5.5 0.5
+d 3 6 5.0 0.5
+d 3 7 5.7 0.5
+d 1 4 6.0 0.1
+d 0 5 10.0 0.1
+d 0 4 8.0 0.1
+d 1 5 8.0 0.1
+d 0 6 11.0 0.1