[xiph-commits] r9631 - in trunk: . planarity

xiphmont at svn.xiph.org xiphmont at svn.xiph.org
Wed Jul 27 21:00:52 PDT 2005


Author: xiphmont
Date: 2005-07-27 20:59:23 -0700 (Wed, 27 Jul 2005)
New Revision: 9631

Added:
   trunk/planarity/
   trunk/planarity/Makefile
   trunk/planarity/arrange.c
   trunk/planarity/arrange.h
   trunk/planarity/box.c
   trunk/planarity/box.h
   trunk/planarity/buttonbar.c
   trunk/planarity/buttons.c
   trunk/planarity/buttons.h
   trunk/planarity/gPlanarity
   trunk/planarity/gPlanarity.gz
   trunk/planarity/gameboard.c
   trunk/planarity/gameboard.h
   trunk/planarity/gamestate.c
   trunk/planarity/gamestate.h
   trunk/planarity/generate.c
   trunk/planarity/generate.h
   trunk/planarity/graph.c
   trunk/planarity/graph.h
   trunk/planarity/touch-version
   trunk/planarity/version.h
Log:
First Import

Added: trunk/planarity/Makefile
===================================================================
--- trunk/planarity/Makefile	2005-07-26 23:19:44 UTC (rev 9630)
+++ trunk/planarity/Makefile	2005-07-28 03:59:23 UTC (rev 9631)
@@ -0,0 +1,85 @@
+# Fuck Automake
+# Fuck the horse it rode in on
+# and Fuck its little dog Libtool too
+
+TARGET  = gPlanarity
+CC      = gcc 
+LD      = gcc
+INSTALL = install
+STRIP   = strip
+PREFIX  = /usr/local
+BINDIR  = $(PREFIX)/bin
+ETCDIR  = /etc/$(TARGET)
+MANDIR  = $(PREFIX)/man
+
+# the very long, LIBS entry is to build an executable that will
+# work for on most modern linux distros without any of the
+# bleeding-edge GTK 2.7/2.8 libs installed.
+
+SRC  = graph.c arrange.c gameboard.c generate.c gamestate.c\
+	buttons.c buttonbar.c box.c
+OBJ  = graph.o arrange.o gameboard.o generate.o gamestate.o\
+	buttons.o buttonbar.o box.o
+GCF  = `pkg-config --static --cflags "gtk+-2.0 >= 2.7.2"`
+
+# uncomment below for a normal dynamic build
+
+LDF  = `pkg-config --static --libs "gtk+-2.0 >= 2.7.2"`
+
+# uncomment below for a build with static Gtk 2.7; this generally
+# requires that all of the Gtk dependencies were built with static
+# dependencies also pointing to the local install
+
+SLDF  = `pkg-config --static --libs-only-L "gtk+-2.0 >= 2.7.2"`
+GTK   = /usr/local/lib
+EXPAT= /usr/lib/libexpat.a
+XINERAMA = /usr/X11R6/lib/libXinerama.a
+PIXMAN = /usr/lib/libpixman.a
+SLIBS = $(GTK)/libgtk-x11-2.0.a \
+	$(GTK)/libgdk-x11-2.0.a $(GTK)/libgdk_pixbuf-2.0.a \
+	$(GTK)/libpangocairo-1.0.a $(GTK)/libpangoft2-1.0.a \
+	$(GTK)/libpangox-1.0.a $(GTK)/libpangoxft-1.0.a \
+	$(GTK)/libpango-1.0.a $(GTK)/libcairo.a \
+	$(GTK)/libatk-1.0.a $(GTK)/libgmodule-2.0.a \
+	$(GTK)/libgobject-2.0.a $(GTK)/libglib-2.0.a \
+	-lpthread $(EXPAT) -lfreetype -lz -lm -lc \
+	-lXext -lXrandr $(XINERAMA) -lXcursor -lX11 $(PIXMAN) -lXft
+
+all:    
+	$(MAKE) target CFLAGS="-O2 -ffast-math $(GCF) $(ADD_DEF)"
+	$(STRIP) $(TARGET)
+
+static:    
+	$(MAKE) static-target CFLAGS="-O2 -ffast-math $(GCF) $(ADD_DEF)"
+	$(STRIP) $(TARGET)
+
+debug:
+	$(MAKE) target CFLAGS="-g -Wall -W -Wno-unused-parameter -D__NO_MATH_INLINES $(GCF) $(ADD_DEF)"
+
+profile:
+	$(MAKE) target CFLAGS="-pg -g -O2 -ffast-math $(GCF) $(ADD_DEF)" LIBS="$(LIBS) -lgprof-helper "
+
+clean:
+	rm -f $(OBJ) *.d *.d.* gmon.out $(TARGET)
+
+distclean: clean
+	rm -f *~
+
+%.d: %.c
+	$(CC) -M $(CFLAGS) $< > $@.$$$$; sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; rm -f $@.$$$$
+
+ifeq ($(MAKECMDGOALS),target)
+include $(SRC:.c=.d)
+endif
+
+target:  $(OBJ) 
+	./touch-version
+	$(LD) $(OBJ) $(CFLAGS) -o $(TARGET) $(LIBS) $(LDF) 
+
+static-target: $(OBJ)
+	./touch-version
+	$(LD) $(OBJ) $(CFLAGS) -o $(TARGET) $(SLIBS) $(SLDF) 
+
+install: target
+	$(INSTALL) -d -m 0755 $(BINDIR)
+	$(INSTALL) -m 0755 $(TARGET) $(BINDIR)

Added: trunk/planarity/arrange.c
===================================================================
--- trunk/planarity/arrange.c	2005-07-26 23:19:44 UTC (rev 9630)
+++ trunk/planarity/arrange.c	2005-07-28 03:59:23 UTC (rev 9631)
@@ -0,0 +1,36 @@
+#include <math.h>
+
+#include "graph.h"
+#include "arrange.h"
+
+void arrange_verticies_circle(int n){
+  vertex *v = get_verticies();
+  int bw=get_board_width();
+  int bh=get_board_height();
+  int radius=min(bw,bh)*.45;
+  int i;
+  for(i=0;i<n;i++){
+    int x = rint( radius * cos( i*M_PI*2./n));
+    int y = rint( radius * sin( i*M_PI*2./n));
+    move_vertex(v,x+(bw>>1),y+(bh>>1));
+    v=v->next;
+  }
+}
+
+void arrange_verticies_mesh(int width, int height){
+  vertex *v = get_verticies();
+  int bw=get_board_width();
+  int bh=get_board_height();
+  int spacing=min((float)bw/width,(float)bh/height)*.9;
+  int x,y;
+
+  int xpad=(bw - (width-1)*spacing)/2;
+  int ypad=(bh - (height-1)*spacing)/2;
+
+  for(y=0;y<height;y++){
+    for(x=0;x<width;x++){
+      move_vertex(v,x*spacing+xpad,y*spacing+ypad);
+      v=v->next;
+    }
+  }
+}

Added: trunk/planarity/arrange.h
===================================================================
--- trunk/planarity/arrange.h	2005-07-26 23:19:44 UTC (rev 9630)
+++ trunk/planarity/arrange.h	2005-07-28 03:59:23 UTC (rev 9631)
@@ -0,0 +1,2 @@
+extern void arrange_verticies_circle(int n);
+extern void arrange_verticies_mesh(int width, int height);

Added: trunk/planarity/box.c
===================================================================
--- trunk/planarity/box.c	2005-07-26 23:19:44 UTC (rev 9630)
+++ trunk/planarity/box.c	2005-07-28 03:59:23 UTC (rev 9631)
@@ -0,0 +1,165 @@
+#include <math.h>
+#include <gtk/gtk.h>
+#include "graph.h"
+#include "box.h"
+
+void topbox (cairo_t *c, double w, double h){
+  
+  double x0 = B_BORDER+B_RADIUS;
+  double y0 = B_BORDER;
+
+  double x1 = B_BORDER;
+  double y1 = B_BORDER+B_RADIUS;
+
+  double x2 = B_BORDER;
+  double y2 = h-B_BORDER-B_RADIUS;
+
+  double x3 = B_BORDER+B_RADIUS;
+  double y3 = h-B_BORDER;
+
+  double x4 = w/2 - B_HUMP - B_RADIUS;
+  double y4 = h-B_BORDER;
+
+  double x5 = x4+B_RADIUS;
+  double y5 = y4;
+
+
+  double x7 = w/2 + B_HUMP + B_RADIUS;
+  double y7 = h-B_BORDER;
+
+  double x8 = w - B_BORDER -B_RADIUS;
+  double y8 = h-B_BORDER;
+
+  double x9 = w - B_BORDER;
+  double y9 = h-B_BORDER-B_RADIUS;
+
+  double x10 = w - B_BORDER;
+  double y10 = B_BORDER+B_RADIUS;
+
+  double x11 = w - B_BORDER-B_RADIUS;
+  double y11 = B_BORDER;
+
+  cairo_set_line_width(c,B_LINE);
+
+  cairo_move_to  (c, x0, y0);
+  cairo_curve_to (c, x1,y0, x1,y0, x1,y1);
+  cairo_line_to (c, x2,y2);
+  cairo_curve_to (c, x2,y3, x2,y3, x3,y3);
+  cairo_line_to (c, x4,y4);
+
+  {
+    double hh = get_board_height()*.5+50;
+
+    double radius = sqrt( (w*.5-x5) * (w*.5-x5) + (hh-y5) * (hh-y5));
+    double siderad = atan( (x5-w*.5)/(hh-y5));
+
+    double xx = sin(siderad+.05)*radius+w*.5;
+    double yy = -cos(siderad+.05)*radius+hh;
+    double x = sin(siderad+.1)*radius+w*.5;
+    double y = -cos(siderad+.1)*radius+hh;
+
+    cairo_curve_to (c, x4+B_RADIUS,y4, xx,yy, x,y);
+    cairo_arc(c,w*.5,hh,radius,-M_PI*.5+siderad+.1,-M_PI*.5-siderad-.1);
+
+    xx = -sin(siderad+.05)*radius+w*.5;
+    yy = -cos(siderad+.05)*radius+hh;
+    xx = -sin(siderad+.1)*radius+w*.5;
+    yy = -cos(siderad+.1)*radius+hh;
+
+    cairo_curve_to (c, xx,yy, x7-B_RADIUS,y7, x7,y7);
+
+  }
+
+  cairo_line_to  (c, x8,y8);
+  cairo_curve_to (c, x9,y8, x9,y8, x9,y9);
+  cairo_line_to  (c, x10,y10);
+  cairo_curve_to (c, x10,y11, x10,y11, x11,y11);
+  cairo_close_path (c);
+
+  cairo_set_source_rgba (c, B_COLOR);
+  cairo_fill_preserve (c);
+  cairo_set_source_rgba (c, B_LINE_COLOR);
+  cairo_stroke (c);
+
+}
+
+void bottombox (cairo_t *c, double w, double h){
+  
+  double x0 = B_BORDER+B_RADIUS;
+  double y0 = h-B_BORDER;
+
+  double x1 = B_BORDER;
+  double y1 = h-B_BORDER-B_RADIUS;
+
+  double x2 = B_BORDER;
+  double y2 = B_BORDER+B_RADIUS;
+
+  double x3 = B_BORDER+B_RADIUS;
+  double y3 = B_BORDER;
+
+  double x4 = w/2 - B_HUMP - B_RADIUS;
+  double y4 = B_BORDER;
+
+  double x5 = x4+B_RADIUS;
+  double y5 = y4;
+
+
+  double x7 = w/2 + B_HUMP + B_RADIUS;
+  double y7 = B_BORDER;
+
+  double x8 = w - B_BORDER -B_RADIUS;
+  double y8 = B_BORDER;
+
+  double x9 = w - B_BORDER;
+  double y9 = B_BORDER+B_RADIUS;
+
+  double x10 = w - B_BORDER;
+  double y10 = h- B_BORDER -B_RADIUS;
+
+  double x11 = w - B_BORDER-B_RADIUS;
+  double y11 = h-B_BORDER;
+
+  cairo_set_line_width(c,B_LINE);
+
+  cairo_move_to  (c, x0, y0);
+  cairo_curve_to (c, x1,y0, x1,y0, x1,y1);
+  cairo_line_to (c, x2,y2);
+  cairo_curve_to (c, x2,y3, x2,y3, x3,y3);
+  cairo_line_to (c, x4,y4);
+
+  {
+    double hh = get_board_height()*.5+50;
+
+    double radius = sqrt( (w*.5-x5) * (w*.5-x5) + (y5+hh-h) * (y5+hh-h));
+    double siderad = atan( (x5-w*.5)/(y5+hh-h));
+
+    double xx = sin(siderad+.05)*radius+w*.5;
+    double yy = cos(siderad+.05)*radius-hh+h;
+    double x = sin(siderad+.1)*radius+w*.5;
+    double y = cos(siderad+.1)*radius-hh+h;
+
+    cairo_curve_to (c, x4+B_RADIUS,y4, xx,yy, x,y);
+    cairo_arc_negative(c,w*.5,h-hh,radius,M_PI*.5-siderad-.1,M_PI*.5+siderad+.1);
+
+    xx = -sin(siderad+.05)*radius+w*.5;
+    yy = cos(siderad+.05)*radius-hh+h;
+    xx = -sin(siderad+.1)*radius+w*.5;
+    yy = cos(siderad+.1)*radius-hh+h;
+
+    cairo_curve_to (c, xx,yy, x7-B_RADIUS,y7, x7,y7);
+
+  }
+
+  cairo_line_to  (c, x8,y8);
+  cairo_curve_to (c, x9,y8, x9,y8, x9,y9);
+  cairo_line_to  (c, x10,y10);
+  cairo_curve_to (c, x10,y11, x10,y11, x11,y11);
+  cairo_close_path (c);
+
+  cairo_set_source_rgba (c, B_COLOR);
+  cairo_fill_preserve (c);
+  cairo_set_source_rgba (c, B_LINE_COLOR);
+  cairo_stroke (c);
+
+}
+

Added: trunk/planarity/box.h
===================================================================
--- trunk/planarity/box.h	2005-07-26 23:19:44 UTC (rev 9630)
+++ trunk/planarity/box.h	2005-07-28 03:59:23 UTC (rev 9631)
@@ -0,0 +1,14 @@
+#define B_LINE 1
+#define B_BORDER 6.5
+#define B_RADIUS 20
+#define B_HUMP 130
+#define B_COLOR         .1,.1,.7,.1
+#define B_LINE_COLOR     0, 0,.7,.3
+#define TEXT_COLOR      .0,.0,.7,.6
+
+
+#define SCOREHEIGHT 50
+
+
+extern void topbox (cairo_t *c, double w, double h);
+extern void bottombox (cairo_t *c, double w, double h);

Added: trunk/planarity/buttonbar.c
===================================================================
--- trunk/planarity/buttonbar.c	2005-07-26 23:19:44 UTC (rev 9630)
+++ trunk/planarity/buttonbar.c	2005-07-28 03:59:23 UTC (rev 9631)
@@ -0,0 +1,771 @@
+#include <math.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include "graph.h"
+#include "gameboard.h"
+#include "gamestate.h"
+#include "buttons.h"
+#include "box.h"
+
+
+/************************ the lower button bar *********************/
+
+#define NUMBUTTONS 10
+
+typedef struct {
+  int target_x;
+  int target_x_inactive;
+  int target_x_active;
+
+  int x;
+  int x_inactive;
+  int x_active;
+  int y;
+
+  cairo_surface_t *idle;
+  cairo_surface_t *lit;
+
+  char *rollovertext;
+  cairo_text_extents_t ex;
+
+  int alphad;
+  double alpha;
+
+  int rollover;
+  int press;
+
+  void (*callback)();
+} buttonstate;
+
+static buttonstate states[NUMBUTTONS];
+
+static int width=0;
+static int height=0;
+static int deployed=0;
+static int sweeper=0;
+static gint timer = 0;
+static int checkbutton_deployed=0;
+static int allclear=1; // actually just a shirt-circuit
+static buttonstate *grabbed=0;
+static void (*undeploy_callback)(Gameboard *g);
+
+/*********************** static housekeeping *****************/
+
+/* initialize the rather weird little animation engine */
+static void buttons_initial_placement(int w, int h){
+  
+  int count=BUTTON_BORDER;
+  int i;
+
+  width=w;
+  height=h;
+  
+  for(i=0;i<BUTTON_LEFT;i++){
+    buttonstate *b=states+i;
+    b->x=0;
+    b->target_x=count;
+    b->x_active=b->target_x_active=count;
+    b->x_inactive=b->target_x_inactive=count-BUTTON_EXPOSE;
+    b->y = h - BUTTON_Y_FROM_BOTTOM;
+    count += BUTTON_SPACING;
+  }
+  count -= BUTTON_SPACING;
+
+  // assumes we will always have at least as many buttons left as right
+  sweeper=count;
+  
+  count=width-BUTTON_BORDER;
+  for(i=0;i<BUTTON_RIGHT;i++){
+    buttonstate *b=states+NUMBUTTONS-i-1;
+    b->x=width;
+    b->target_x=count;
+    b->x_active=b->target_x_active=count;
+    b->x_inactive=b->target_x_inactive=count+BUTTON_EXPOSE;
+    b->y = h - BUTTON_Y_FROM_BOTTOM;
+    if(i>0) // special-case the checkbutton
+      count -= BUTTON_SPACING;
+  }
+  
+  // special-case the checkbutton
+  states[9].target_x_inactive=states[9].target_x_active+BUTTON_SPACING;
+  states[9].x_inactive=states[9].target_x_inactive;
+  states[9].target_x=states[9].target_x_inactive;
+
+}
+
+/* cache buttons as small surfaces */
+static cairo_surface_t *cache_button(Gameboard *g,
+				     void (*draw)(cairo_t *c, 
+						  double x, 
+						  double y),
+				     double pR,double pG,double pB,double pA,
+				     double fR,double fG,double fB,double fA){
+  cairo_surface_t *ret = 
+    cairo_surface_create_similar (cairo_get_target (g->wc),
+				  CAIRO_CONTENT_COLOR_ALPHA,
+				  BUTTON_RADIUS*2+1,
+				  BUTTON_RADIUS*2+1);
+  
+  cairo_t *c = cairo_create(ret);
+
+  cairo_save(c);
+  cairo_set_operator(c,CAIRO_OPERATOR_CLEAR);
+  cairo_set_source_rgba (c, 1,1,1,1);
+  cairo_paint(c);
+  cairo_restore(c);
+     
+  cairo_set_source_rgba(c,fR,fG,fB,fA);
+  cairo_set_line_width(c,BUTTON_LINE_WIDTH);
+  draw(c,BUTTON_RADIUS+.5,BUTTON_RADIUS+.5);
+
+  cairo_set_source_rgba(c,pR,pG,pB,pA);
+  cairo_stroke(c);
+  
+  cairo_destroy(c);
+  return ret;
+}
+
+/* determine the x/y/w/h box around a button */
+static GdkRectangle button_box(buttonstate *b){
+  GdkRectangle r;
+
+  int x = b->x - BUTTON_RADIUS-1;
+  int y = b->y - BUTTON_RADIUS-1;
+  r.x=x;
+  r.y=y;
+  r.width = BUTTON_RADIUS*2+2;
+  r.height= BUTTON_RADIUS*2+2;
+
+  return r;
+}
+
+/* determine the x/y/w/h box around the rollover text */
+static GdkRectangle rollover_box(buttonstate *b){
+  GdkRectangle r;
+
+  int x = b->x - b->ex.width/2 + b->ex.x_bearing -2;
+  int y = b->y - BUTTON_RADIUS - BUTTON_TEXT_BORDER + b->ex.y_bearing -2;
+
+  if(x<BUTTON_TEXT_BORDER)x=BUTTON_TEXT_BORDER;
+  if(y<BUTTON_TEXT_BORDER)y=BUTTON_TEXT_BORDER;
+  if(x+b->ex.width >= width - BUTTON_TEXT_BORDER)
+    x = width - BUTTON_TEXT_BORDER - b->ex.width;
+  if(y+b->ex.height >= height - BUTTON_TEXT_BORDER)
+    y = height - BUTTON_TEXT_BORDER - b->ex.height;
+
+  r.x=x;
+  r.y=y;
+  r.width=b->ex.width+5;
+  r.height=b->ex.height+5;
+
+  return r;
+}
+
+/* draw the actual rollover */
+static void stroke_rollover(Gameboard *g, buttonstate *b, cairo_t *c){
+  cairo_matrix_t m;
+
+  cairo_select_font_face (c, "Arial",
+			  CAIRO_FONT_SLANT_NORMAL,
+			  CAIRO_FONT_WEIGHT_BOLD);
+
+  cairo_matrix_init_scale (&m, BUTTON_TEXT_SIZE);
+  cairo_set_font_matrix (c,&m);
+  
+  {
+    GdkRectangle r=rollover_box(b);
+
+    cairo_move_to (c, r.x - b->ex.x_bearing +2, r.y -b->ex.y_bearing+2 );
+
+    cairo_set_line_width(c,3);
+    cairo_set_source_rgba(c,1,1,1,.9);
+    cairo_text_path (c, b->rollovertext);  
+    cairo_stroke(c);
+
+    cairo_set_source_rgba(c,BUTTON_TEXT_COLOR);
+    cairo_move_to (c, r.x - b->ex.x_bearing +2, r.y -b->ex.y_bearing+2 );
+    
+
+    cairo_show_text (c, b->rollovertext);  
+
+  }
+}
+
+/* cache the text extents of a rollover */
+static void rollover_extents(Gameboard *g, buttonstate *b){
+  
+  cairo_matrix_t m;
+
+  cairo_t *c = cairo_create(g->foreground);
+  cairo_select_font_face (c, "Arial",
+			  CAIRO_FONT_SLANT_NORMAL,
+			  CAIRO_FONT_WEIGHT_BOLD);
+  
+  cairo_matrix_init_scale (&m, BUTTON_TEXT_SIZE);
+  cairo_set_font_matrix (c,&m);
+  
+  cairo_text_extents (c, b->rollovertext, &b->ex);
+
+
+  cairo_destroy(c);
+}
+
+/* request an expose for a button region*/
+static void invalidate_button(Gameboard *g,buttonstate *b){
+  GdkRectangle r;
+
+  r.x=b->x-BUTTON_RADIUS-1;
+  r.y=b->y-BUTTON_RADIUS-1;
+  r.width=BUTTON_RADIUS*2+2;
+  r.height=BUTTON_RADIUS*2+2;
+
+  gdk_window_invalidate_rect(g->w.window,&r,FALSE);
+}
+
+/* request an expose for a rollover region*/
+static void invalidate_rollover(Gameboard *g,buttonstate *b){
+  GdkRectangle r = rollover_box(b);
+  invalidate_button(g,b);
+  gdk_window_invalidate_rect(g->w.window,&r,FALSE);
+}
+
+/* like invalidation, but just expands a rectangular region */
+static void expand_rectangle_button(buttonstate *b,GdkRectangle *r){
+  int x=b->x-BUTTON_RADIUS-1;
+  int y=b->y-BUTTON_RADIUS-1;
+  int x2=x+BUTTON_RADIUS*2+2;
+  int y2=y+BUTTON_RADIUS*2+2;
+
+  int rx2=r->x+r->width;
+  int ry2=r->y+r->height;
+
+  if(r->width==0 || r->height==0){
+    r->x=x;
+    r->y=y;
+    r->width=x2-x;
+    r->height=y2-y;
+  }
+
+  if(x<r->x)
+    r->x=x;
+  if(y<r->y)
+    r->y=y;
+  if(rx2<x2)
+    rx2=x2;
+  r->width=rx2-r->x;
+  if(ry2<y2)
+    ry2=y2;
+  r->height=ry2-r->y;
+}
+
+/* like invalidation, but just expands a rectangular region */
+static void expand_rectangle_rollover(buttonstate *b,GdkRectangle *r){
+  GdkRectangle rr = rollover_box(b);
+  int x=rr.x;
+  int y=rr.y;
+  int x2=x+rr.width;
+  int y2=y+rr.height;
+
+  int rx2=r->x+r->width;
+  int ry2=r->y+r->height;
+
+  if(r->width==0 || r->height==0){
+    r->x=x;
+    r->y=y;
+    r->width=x2-x;
+    r->height=y2-y;
+  }
+
+  if(x<r->x)
+    r->x=x;
+  if(y<r->y)
+    r->y=y;
+  if(rx2<x2)
+    rx2=x2;
+  r->width=rx2-r->x;
+  if(ry2<y2)
+    ry2=y2;
+  r->height=ry2-r->y;
+}
+
+/* do the animation math for one button for one frame */
+static int animate_one(buttonstate *b, GdkRectangle *r){
+  int ret=0;
+  int w2=width/2;
+
+  if(b->target_x>w2 && width-sweeper>b->target_x)
+    b->target_x=width-sweeper;
+  if(b->target_x<w2 && sweeper<b->target_x)
+    b->target_x=sweeper;
+
+  if(b->target_x_active != b->x_active){
+    ret=1;
+    if(b->target_x_active > b->x_active){
+      b->x_active+=BUTTON_DELTA;
+      if(b->x_active>b->target_x_active)
+	b->x_active=b->target_x_active;
+    }else{
+      b->x_active-=BUTTON_DELTA;
+      if(b->x_active<b->target_x_active)
+	b->x_active=b->target_x_active;
+    }
+  }
+
+  if(b->target_x_inactive != b->x_inactive){
+    ret=1;
+    if(b->target_x_inactive > b->x_inactive){
+      b->x_inactive+=BUTTON_DELTA;
+      if(b->x_inactive>b->target_x_inactive)
+	b->x_inactive=b->target_x_inactive;
+    }else{
+      b->x_inactive-=BUTTON_DELTA;
+      if(b->x_inactive<b->target_x_inactive)
+	b->x_inactive=b->target_x_inactive;
+    }
+  }
+
+  if(b->target_x != b->x){
+    ret=1;
+    expand_rectangle_button(b,r);
+    if(b->rollover)
+      expand_rectangle_rollover(b,r);
+
+    if(b->target_x > b->x){
+      b->x+=BUTTON_DELTA;
+      if(b->x>b->target_x)b->x=b->target_x;
+    }else{
+      b->x-=BUTTON_DELTA;
+      if(b->x<b->target_x)b->x=b->target_x;
+    }
+    expand_rectangle_button(b,r);
+    if(b->rollover)
+      expand_rectangle_rollover(b,r);
+  }
+
+  if(b->alphad != b->x_active - b->x){
+    double alpha=0;
+    if(b->x_inactive>b->x_active){
+      if(b->x<=b->x_active)
+	alpha=1.;
+      else if (b->x>=b->x_inactive)
+	alpha=0.;
+      else
+	alpha = (double)(b->x_inactive-b->x)/(b->x_inactive-b->x_active);
+    }else if (b->x_inactive<b->x_active){
+      if(b->x>=b->x_active)
+	alpha=1.;
+      else if (b->x<=b->x_inactive)
+	alpha=0.;
+      else
+	alpha = (double)(b->x_inactive-b->x)/(b->x_inactive-b->x_active);
+    }
+    if(alpha != b->alpha){
+      ret=1;
+      expand_rectangle_button(b,r);
+      b->alpha=alpha;
+    }
+    b->alphad = b->x_active - b->x;
+  }
+  return ret;
+}
+
+/* perform a single frame of animation for all buttons/rollovers */
+static gboolean animate_buttons(gpointer ptr){
+  Gameboard *g=(Gameboard *)ptr;
+  GdkRectangle expose;
+  int ret=0,i;
+
+  if(!g->first_expose)return 1;
+  
+  memset(&expose,0,sizeof(expose));
+  for(i=0;i<BUTTON_LEFT;i++)
+    ret|=animate_one(states+i,&expose);
+  
+  /* avoid the normal expose event mechanism
+     during the button animations.  This direct method is much faster as
+     it won't accidentally combine exposes over long stretches of
+     alpha-blended surfaces. */
+  run_immediate_expose(g,
+		       expose.x,
+  	       expose.y,
+  	       expose.width,
+  	       expose.height);
+  
+
+
+  memset(&expose,0,sizeof(expose));
+  for(i=BUTTON_LEFT;i<NUMBUTTONS;i++)
+    ret|=animate_one(states+i,&expose);
+    
+  run_immediate_expose(g,
+		       expose.x,
+		       expose.y,
+		       expose.width,
+		       expose.height);
+
+  if(!ret && timer!=0){
+    g_source_remove(timer);
+    timer=0;
+  }
+  return ret;
+}
+
+/* helper that wraps animate-buttons for removing the buttons at the
+   end of a level */
+static gboolean deanimate_buttons(gpointer ptr){
+  Gameboard *g=(Gameboard *)ptr;
+  int ret=0;
+  sweeper-=BUTTON_DELTA;
+  if(sweeper< -(BUTTON_RADIUS +1)){
+    sweeper= -(BUTTON_RADIUS +1);
+  }else
+    ret=1;
+
+  ret|=animate_buttons(ptr);
+
+  if(!ret)
+    // undeploy finished... call the undeploy callback
+    undeploy_callback(g);
+
+  return ret;
+}
+
+/* the normal expose method for all buttons; called from the master
+   widget's expose */
+void expose_buttons(Gameboard *g,cairo_t *c, int x,int y,int w,int h){
+  int i;
+  
+  for(i=0;i<NUMBUTTONS;i++){
+
+    buttonstate *b=states+i;
+    GdkRectangle r = rollover_box(b);
+    GdkRectangle br = button_box(b);
+
+    if(x < br.x+br.width &&
+       y < br.y+br.height &&
+       x+w > br.x &&
+       y+h > br.y) {
+
+      cairo_set_source_surface(c,
+			       (b->rollover || b->press ? b->lit : b->idle),
+			       br.x,
+			       br.y);
+      
+      if(b->press)
+	cairo_paint_with_alpha(c,b->alpha);
+      cairo_paint_with_alpha(c,b->alpha);
+
+    }
+
+    if(b->rollover || b->press)
+      if(x < r.x+r.width &&
+	 y < r.y+r.height &&
+	 x+w > r.x &&
+	 y+h > r.y)
+	
+	stroke_rollover(g,b,c);
+
+  }
+}
+
+/* match x/y to a button if any */
+static buttonstate *find_button(int x,int y){
+  int i;
+  for(i=0;i<NUMBUTTONS;i++){
+    buttonstate *b=states+i;
+    if( (b->x-x)*(b->x-x) + (b->y-y)*(b->y-y) <= BUTTON_RADIUS*BUTTON_RADIUS+4)
+      if(b->x != b->x_inactive)
+	return b;
+  }
+  return 0;
+}
+
+/* clear all buttons to unpressed/unlit */
+static void button_clear_state(Gameboard *g){
+  int i;
+  if(!allclear){
+    for(i=0;i<NUMBUTTONS;i++){
+      buttonstate *bb=states+i;
+      if(bb->rollover)
+	invalidate_rollover(g,bb);
+      if(bb->press)
+	invalidate_button(g,bb);
+      
+      bb->rollover=0;
+      bb->press=0;
+    }
+  }
+  allclear=1;
+  grabbed=0;
+}
+
+/* set a button to pressed or lit */
+static void button_set_state(Gameboard *g, buttonstate *b, int rollover, int press){
+  int i;
+  
+  if(b->rollover == rollover && b->press == press)return;
+
+  for(i=0;i<NUMBUTTONS;i++){
+    buttonstate *bb=states+i;
+    if(bb!=b){
+      if(bb->rollover)
+	invalidate_rollover(g,bb);
+      if(bb->press)
+	invalidate_button(g,bb);
+
+      bb->rollover=0;
+      bb->press=0;
+    }else{
+      if(bb->rollover != rollover)
+	invalidate_rollover(g,bb);
+      if(bb->press != press)
+	invalidate_button(g,bb);
+      
+      bb->rollover=rollover;
+      bb->press=press;
+    }
+  }
+  allclear=0;
+}
+
+/******************** toplevel abstraction calls *********************/
+
+/* initialize the persistent caches; called once when gameboard is
+   first created */
+void init_buttons(Gameboard *g){
+  int i;
+  memset(states,0,sizeof(states));
+
+  states[0].idle = cache_button(g, path_button_exit, 
+				BUTTON_QUIT_IDLE_PATH, 
+				BUTTON_QUIT_IDLE_FILL);
+  states[0].lit  = cache_button(g, path_button_exit, 
+				BUTTON_QUIT_LIT_PATH, 
+				BUTTON_QUIT_LIT_FILL);
+
+  states[1].idle = cache_button(g, path_button_back, 
+				BUTTON_IDLE_PATH,
+				BUTTON_IDLE_FILL);
+  states[1].lit  = cache_button(g, path_button_back, 
+				BUTTON_LIT_PATH,
+				BUTTON_LIT_FILL);
+  states[2].idle = cache_button(g, path_button_reset, 
+				BUTTON_IDLE_PATH,
+				BUTTON_IDLE_FILL);
+  states[2].lit  = cache_button(g, path_button_reset, 
+				BUTTON_LIT_PATH,
+				BUTTON_LIT_FILL);
+  states[3].idle = cache_button(g, path_button_pause, 
+				BUTTON_IDLE_PATH,
+				BUTTON_IDLE_FILL);
+  states[3].lit  = cache_button(g, path_button_pause, 
+				BUTTON_LIT_PATH,
+				BUTTON_LIT_FILL);
+  states[4].idle = cache_button(g, path_button_help, 
+				BUTTON_IDLE_PATH,
+				BUTTON_IDLE_FILL);
+  states[4].lit  = cache_button(g, path_button_help, 
+				BUTTON_LIT_PATH,
+				BUTTON_LIT_FILL);
+  states[5].idle = cache_button(g, path_button_expand, 
+				BUTTON_IDLE_PATH,
+				BUTTON_IDLE_FILL);
+  states[5].lit  = cache_button(g, path_button_expand, 
+				BUTTON_LIT_PATH,
+				BUTTON_LIT_FILL);
+  states[6].idle = cache_button(g, path_button_shrink, 
+				BUTTON_IDLE_PATH,
+				BUTTON_IDLE_FILL);
+  states[6].lit  = cache_button(g, path_button_shrink, 
+				BUTTON_LIT_PATH,
+				BUTTON_LIT_FILL);
+  states[7].idle = cache_button(g, path_button_lines, 
+				BUTTON_IDLE_PATH,
+				BUTTON_IDLE_FILL);
+  states[7].lit  = cache_button(g, path_button_lines, 
+				BUTTON_LIT_PATH,
+				BUTTON_LIT_FILL);
+  states[8].idle = cache_button(g, path_button_int, 
+				BUTTON_IDLE_PATH,
+				BUTTON_IDLE_FILL);
+  states[8].lit  = cache_button(g, path_button_int, 
+				BUTTON_LIT_PATH,
+				BUTTON_LIT_FILL);
+  states[9].idle = cache_button(g, path_button_check, 
+				BUTTON_CHECK_IDLE_PATH,
+				BUTTON_CHECK_IDLE_FILL);
+  states[9].lit  = cache_button(g, path_button_check, 
+				BUTTON_CHECK_LIT_PATH,
+				BUTTON_CHECK_LIT_FILL);
+
+  states[0].rollovertext="exit gPlanarity";
+  states[1].rollovertext="back to menu";
+  states[2].rollovertext="reset board";
+  states[3].rollovertext="pause";
+  states[4].rollovertext="help / about";
+  states[5].rollovertext="expand";
+  states[6].rollovertext="shrink";
+  states[7].rollovertext="hide/show lines";
+  states[8].rollovertext="mark intersections";
+  states[9].rollovertext="click when finished!";
+
+  states[0].callback = quit;
+  states[2].callback = reset_board;
+  states[5].callback = expand;
+  states[6].callback = shrink;
+  states[7].callback = hide_show_lines;
+  states[8].callback = mark_intersections;
+  states[9].callback = finish_board;
+
+  for(i=0;i<NUMBUTTONS;i++)
+    rollover_extents(g,states+i);
+}
+
+/* effects animated 'rollout' of buttons when level begins */
+void deploy_buttons(Gameboard *g){
+  if(!deployed ){
+    buttons_initial_placement(get_board_width(),get_board_height());
+    timer = g_timeout_add(BUTTON_ANIM_INTERVAL, animate_buttons, (gpointer)g);
+    deployed=1;
+    checkbutton_deployed=0;
+  }
+
+}
+
+/* effects animated rollout of 'check' button */
+void deploy_check(Gameboard *g){
+  if(deployed && !checkbutton_deployed){
+    int i;
+    for(i=BUTTON_LEFT;i<NUMBUTTONS-1;i++){
+      states[i].target_x-=BUTTON_SPACING;
+      states[i].target_x_active-=BUTTON_SPACING;
+      states[i].target_x_inactive-=BUTTON_SPACING;
+    }
+    states[9].target_x=states[9].target_x_active;
+    if(timer!=0)
+      g_source_remove(timer);
+    timer = g_timeout_add(BUTTON_ANIM_INTERVAL, animate_buttons, (gpointer)g);
+    checkbutton_deployed=1;
+  }
+}
+
+/* effects animated rollback of 'check' button */
+void undeploy_check(Gameboard *g){
+  if(checkbutton_deployed){
+    int i;
+    for(i=BUTTON_LEFT;i<NUMBUTTONS-1;i++){
+      states[i].target_x+=BUTTON_SPACING;
+      states[i].target_x_active+=BUTTON_SPACING;
+      states[i].target_x_inactive+=BUTTON_SPACING;
+    }
+    states[9].target_x=states[9].target_x_inactive;
+    if(timer!=0)
+      g_source_remove(timer);
+    timer = g_timeout_add(BUTTON_ANIM_INTERVAL, animate_buttons, (gpointer)g);
+    checkbutton_deployed=0;
+  }
+}
+
+/* effects animated rollback of buttons at end of level */
+void undeploy_buttons(Gameboard *g, void (*callback)(Gameboard *g)){
+  if(timer!=0)
+    g_source_remove(timer);
+
+  button_clear_state(g);
+  grabbed=0;
+  deployed=0;
+  undeploy_callback=callback;
+  checkbutton_deployed=0;
+
+  g_timeout_add(BUTTON_ANIM_INTERVAL, deanimate_buttons, (gpointer)g);
+
+
+}
+
+/* resize the button bar; called from master resize in gameboard */
+void resize_buttons(int w,int h){
+  int i;
+  int dx=w-width;
+  int dy=h-height;
+
+  for(i=BUTTON_LEFT;i<NUMBUTTONS;i++){
+    states[i].x+=dx;
+    states[i].target_x+=dx;
+    states[i].x_active+=dx;
+    states[i].target_x_active+=dx;
+    states[i].x_inactive+=dx;
+    states[i].target_x_inactive+=dx;
+    states[i].y+=dy;
+  }
+  width=w;
+}
+
+
+/* handles mouse motion events; called out of gameboard's master
+   motion handler; returns nonzero if it used focus */
+int button_motion_event(Gameboard *g, GdkEventMotion *event, int focusable){
+  buttonstate *b = find_button((int)event->x,(int)event->y);
+
+  if(deployed){
+    if(grabbed){
+      if(grabbed==b)
+	button_set_state(g,grabbed,1,1);
+      else
+	button_set_state(g,grabbed,0,0);
+      return 1;
+    }
+    
+    if(focusable && !grabbed){
+      
+      if(b){
+	button_set_state(g,b,1,0);
+	return 1;
+      }
+    }
+    
+    button_clear_state(g);
+  }
+  return 0;
+}
+
+/* handles mouse button press events; called out of gameboard's master
+   mouse handler.  returns nonzero if grabbing focus */
+int button_button_press(Gameboard *g, GdkEventButton *event, int focusable){
+
+  if(deployed){
+    if(focusable && deployed){
+      
+      buttonstate *b = find_button((int)event->x,(int)event->y);
+      if(b){
+	button_set_state(g,b,1,1);
+      grabbed = b;
+      return 1;
+      }
+    }
+    
+    button_clear_state(g);
+  }
+  return 0;  
+}
+
+/* handles mouse button press events; called out of gameboard's master
+   mouse handler. returns nonzero if grabbing focus */
+int button_button_release(Gameboard *g, GdkEventButton *event, int focusable){
+  if(focusable && deployed){
+    
+    buttonstate *b = find_button((int)event->x,(int)event->y);
+    if(b && grabbed==b){
+      button_set_state(g,b,1,0);
+      
+      if(b->callback)
+	b->callback(g);
+      
+      
+    }
+  }
+  
+  grabbed=0;
+  return 0;  
+}

Added: trunk/planarity/buttons.c
===================================================================
--- trunk/planarity/buttons.c	2005-07-26 23:19:44 UTC (rev 9630)
+++ trunk/planarity/buttons.c	2005-07-28 03:59:23 UTC (rev 9631)
@@ -0,0 +1,254 @@
+#include <math.h>
+#include <gtk/gtk.h>
+
+#include "graph.h"
+#include "gameboard.h"
+#include "buttons.h"
+
+
+/************************* simple round icon drawing *********************/
+
+void path_button_help(cairo_t *c, double x, double y){
+  
+  cairo_save(c);
+  cairo_translate(c,x,y);
+  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
+  cairo_set_line_width(c,1);
+  cairo_arc(c,0,0,14,0,2*M_PI);
+
+  cairo_move_to(c,-9,-2);
+  cairo_curve_to(c,-8,-7, -4.5,-12, 0,-12);
+  cairo_curve_to(c, 7,-12,  9,-6, 9,-4);
+  cairo_curve_to(c, 9,0 ,6,2, 5,2);
+
+  cairo_curve_to(c, 4,2, 2.5,3, 2.5,6);
+  cairo_line_to(c,-2.5,6);
+
+  cairo_curve_to(c,-2.5,3, -3,1, 0,-1);
+  cairo_curve_to(c, 11,-4, -3,-12, -4,-2);
+  cairo_close_path(c);
+
+  cairo_move_to(c,-2.5,8);
+  cairo_line_to(c,2.5,8);
+  cairo_line_to(c,2.5,12);
+  cairo_line_to(c,-2.5,12);
+  cairo_close_path(c);
+  cairo_fill_preserve(c);
+
+  cairo_restore(c);
+
+}
+
+void path_button_pause(cairo_t *c, double x, double y){
+  
+  cairo_save(c);
+  cairo_translate(c,x,y);
+  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
+  cairo_set_line_width(c,1);
+  cairo_arc(c,0,0,14,0,2*M_PI);
+
+  cairo_rectangle(c,-7,-9,5,18);
+  cairo_rectangle(c,2,-9,5,18);
+  cairo_fill_preserve(c);
+  cairo_restore(c);
+
+}
+
+void path_button_exit(cairo_t *c, double x, double y){
+  
+  cairo_save(c);
+  cairo_translate(c,x,y);
+  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
+  cairo_set_line_width(c,1);
+  cairo_arc(c,0,0,14,0,2*M_PI);
+
+  cairo_move_to(c,-6,-9.5);
+  cairo_line_to(c,0,-3.5);
+  cairo_line_to(c,6,-9.5);
+  cairo_line_to(c,9.5,-6);
+  cairo_line_to(c,3.5,0);
+  cairo_line_to(c,9.5,6);
+  cairo_line_to(c,6,9.5);
+  cairo_line_to(c,0,3.5);
+  cairo_line_to(c,-6,9.5);
+  cairo_line_to(c,-9.5,6);
+  cairo_line_to(c,-3.5,0);
+  cairo_line_to(c,-9.5,-6);
+  cairo_close_path(c);
+
+  cairo_fill_preserve(c);
+  cairo_restore(c);
+
+}
+
+void path_button_back(cairo_t *c, double x, double y){
+  
+  cairo_save(c);
+  cairo_translate(c,x,y);
+  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
+  cairo_set_line_width(c,1);
+
+  cairo_arc(c,0,0,14,0,2*M_PI);
+
+  cairo_move_to(c,0,-11);
+  cairo_line_to(c,-9,0);
+  cairo_line_to(c,-3,0);
+  cairo_line_to(c,-3,10);
+  cairo_line_to(c,3,10);
+  cairo_line_to(c,3,0);
+  cairo_line_to(c,9,0);
+  cairo_close_path(c);
+
+  cairo_fill_preserve(c);
+  cairo_restore(c);
+
+}
+
+void path_button_reset(cairo_t *c, double x, double y){
+  
+  cairo_save(c);
+  cairo_translate(c,x,y);
+  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
+  cairo_set_line_width(c,1);
+
+  cairo_arc(c,0,0,14,0,2*M_PI);
+
+  cairo_move_to(c,-11,-5);
+  cairo_line_to(c,-12,1);
+  cairo_line_to(c,-6,1);
+  cairo_line_to(c,0, 11);
+  cairo_line_to(c,6,1);
+  cairo_line_to(c,12,1);
+  cairo_line_to(c,11,-5);
+  cairo_line_to(c,4,-5);
+  cairo_line_to(c,0,2);
+  cairo_line_to(c,-4,-5);
+  cairo_close_path(c);
+
+  cairo_fill_preserve(c);
+  cairo_restore(c);
+
+}
+
+void path_button_expand(cairo_t *c, double x, double y){
+  
+  cairo_save(c);
+  cairo_translate(c,x,y);
+  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
+  cairo_set_line_width(c,1);
+
+  cairo_arc(c,0,0,14,0,2*M_PI);
+
+  cairo_move_to(c,-8.5,-3);
+  cairo_line_to(c,-2.5,-3);
+  cairo_line_to(c,-3,-8.5);
+  cairo_line_to(c,3, -8.5);
+  cairo_line_to(c,3,-3);
+  cairo_line_to(c,8.5,-3);
+  cairo_line_to(c,8.5,3);
+  cairo_line_to(c,3,3);
+  cairo_line_to(c,3,8.5);
+  cairo_line_to(c,-3,8.5);
+  cairo_line_to(c,-3,3);
+  cairo_line_to(c,-8.5,3);
+  cairo_close_path(c);
+
+  cairo_fill_preserve(c);
+  cairo_restore(c);
+
+}
+
+void path_button_shrink(cairo_t *c, double x, double y){
+  
+  cairo_save(c);
+  cairo_translate(c,x,y);
+  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
+  cairo_set_line_width(c,1);
+
+  cairo_arc(c,0,0,14,0,2*M_PI);
+
+  cairo_move_to(c,-10,-3);
+  cairo_line_to(c,10,-3);
+  cairo_line_to(c,10,3);
+  cairo_line_to(c,-10,3);
+  cairo_close_path(c);
+
+  cairo_fill_preserve(c);
+  cairo_restore(c);
+
+}
+
+void path_button_lines(cairo_t *c, double x, double y){
+  
+  cairo_save(c);
+  cairo_translate(c,x,y);
+  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
+  cairo_set_line_width(c,1);
+
+  cairo_arc(c,0,0,14,0,2*M_PI);
+
+  cairo_move_to(c,6,-4);
+  cairo_arc(c,0,-4,6,0,2*M_PI);
+
+  cairo_move_to(c,0,2);
+  cairo_line_to(c,0,10);
+  cairo_close_path(c);
+
+  cairo_move_to(c,2.68328,5.36656-4);
+  cairo_rel_line_to(c,4,8);
+  cairo_close_path(c);
+
+  cairo_move_to(c,-2.68328,5.36656-4);
+  cairo_rel_line_to(c,-4,8);
+  cairo_close_path(c);
+
+  cairo_fill_preserve(c);
+  cairo_restore(c);
+
+}
+
+void path_button_int(cairo_t *c, double x, double y){
+  
+  cairo_save(c);
+  cairo_translate(c,x,y);
+  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
+  cairo_set_line_width(c,1);
+
+  cairo_arc(c,0,0,14,0,2*M_PI);
+
+  cairo_move_to(c,8,0);
+  cairo_line_to(c,0,8);
+  cairo_line_to(c,-8,0);
+  cairo_line_to(c,0,-8);
+
+  cairo_close_path(c);
+ 
+  cairo_fill_preserve(c);
+  cairo_restore(c);
+
+}
+
+void path_button_check(cairo_t *c, double x, double y){
+  
+  cairo_save(c);
+  cairo_translate(c,x,y);
+  cairo_set_fill_rule(c,CAIRO_FILL_RULE_EVEN_ODD);
+  cairo_set_line_width(c,1);
+
+  cairo_arc(c,0,0,14,0,2*M_PI);
+
+  cairo_move_to(c,8,-8);
+  cairo_curve_to(c, 7,-7, 11,-7.3, 10,-6);
+  cairo_line_to(c,0,9);
+  cairo_curve_to(c, -1,9.1, -2,11, -3,10);
+  cairo_line_to(c,-11,4);
+  cairo_curve_to(c, -12,3, -10,.5, -9,1);
+  cairo_line_to(c,-3,3);
+
+  cairo_close_path(c);
+ 
+  cairo_fill_preserve(c);
+  cairo_restore(c);
+
+}
+

Added: trunk/planarity/buttons.h
===================================================================
--- trunk/planarity/buttons.h	2005-07-26 23:19:44 UTC (rev 9630)
+++ trunk/planarity/buttons.h	2005-07-28 03:59:23 UTC (rev 9631)
@@ -0,0 +1,59 @@
+extern void go_buttons(Gameboard *g);
+extern void init_buttons(Gameboard *g);
+extern void expose_buttons(Gameboard *g,cairo_t *c,int x, int y, int w, int h);
+extern void deploy_buttons(Gameboard *g);
+extern void undeploy_buttons(Gameboard *g, void (*callback)());
+extern void resize_buttons(int w,int h);
+extern void deploy_check(Gameboard *g);
+extern void undeploy_check(Gameboard *g);
+
+extern int button_motion_event(Gameboard *g, GdkEventMotion *event, int focusable);
+extern int button_button_press(Gameboard *g, GdkEventButton *event, int focusable);
+extern int button_button_release(Gameboard *g, GdkEventButton *event, int focusable);
+
+
+
+extern void path_button_help(cairo_t *c, double x, double y);
+extern void path_button_back(cairo_t *c, double x, double y);
+extern void path_button_reset(cairo_t *c, double x, double y);
+extern void path_button_pause(cairo_t *c, double x, double y);
+extern void path_button_exit(cairo_t *c, double x, double y);
+extern void path_button_expand(cairo_t *c, double x, double y);
+extern void path_button_shrink(cairo_t *c, double x, double y);
+extern void path_button_lines(cairo_t *c, double x, double y);
+extern void path_button_int(cairo_t *c, double x, double y);
+extern void path_button_check(cairo_t *c, double x, double y);
+ 
+
+#define BUTTON_QUIT_IDLE_FILL   .7,.1,.1,.3
+#define BUTTON_QUIT_IDLE_PATH   .7,.1,.1,.6
+
+#define BUTTON_QUIT_LIT_FILL    .7,.1,.1,.5
+#define BUTTON_QUIT_LIT_PATH    .7,.1,.1,.6
+
+#define BUTTON_IDLE_FILL        .1,.1,.7,.3
+#define BUTTON_IDLE_PATH        .1,.1,.7,.6
+
+#define BUTTON_LIT_FILL         .1,.1,.7,.6
+#define BUTTON_LIT_PATH         .1,.1,.7,.6
+
+#define BUTTON_CHECK_IDLE_FILL  .1,.5,.1,.3
+#define BUTTON_CHECK_IDLE_PATH  .1,.5,.1,.6
+
+#define BUTTON_CHECK_LIT_FILL   .1,.5,.1,.6
+#define BUTTON_CHECK_LIT_PATH   .1,.5,.1,.6
+
+#define BUTTON_RADIUS 14
+#define BUTTON_Y_FROM_BOTTOM 25
+#define BUTTON_LINE_WIDTH 1
+#define BUTTON_TEXT_BORDER 15
+#define BUTTON_TEXT_COLOR       .1,.1,.7,.8
+#define BUTTON_TEXT_SIZE  15.,18.
+
+#define BUTTON_ANIM_INTERVAL 15
+#define BUTTON_LEFT 5
+#define BUTTON_RIGHT 5
+#define BUTTON_BORDER 35
+#define BUTTON_SPACING 35
+#define BUTTON_EXPOSE  50
+#define BUTTON_DELTA 3

Added: trunk/planarity/gPlanarity
===================================================================
(Binary files differ)


Property changes on: trunk/planarity/gPlanarity
___________________________________________________________________
Name: svn:executable
   + 
Name: svn:mime-type
   + application/octet-stream

Added: trunk/planarity/gPlanarity.gz
===================================================================
(Binary files differ)


Property changes on: trunk/planarity/gPlanarity.gz
___________________________________________________________________
Name: svn:executable
   + 
Name: svn:mime-type
   + application/octet-stream

Added: trunk/planarity/gameboard.c
===================================================================
--- trunk/planarity/gameboard.c	2005-07-26 23:19:44 UTC (rev 9630)
+++ trunk/planarity/gameboard.c	2005-07-28 03:59:23 UTC (rev 9631)
@@ -0,0 +1,1060 @@
+#include <gtk/gtk.h>
+#include <gtk/gtkmain.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <stdlib.h>
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "graph.h"
+#include "gameboard.h"
+#include "gamestate.h"
+#include "buttons.h"
+#include "box.h"
+
+static GtkWidgetClass *parent_class = NULL;
+
+/* vertex area and draw operations ***********************************/
+
+// invalidate the box around a single vertex
+static void invalidate_region_vertex_off(GtkWidget *widget, 
+					 vertex *v, int dx, int dy){
+  if(v){
+    GdkRectangle r;
+    r.x = v->x - V_RADIUS - V_LINE + dx;
+    r.y = v->y - V_RADIUS - V_LINE + dy;
+    r.width =  (V_RADIUS + V_LINE)*2;
+    r.height = (V_RADIUS + V_LINE)*2;
+    
+    gdk_window_invalidate_rect (widget->window, &r, FALSE);
+  }
+}
+
+void invalidate_region_vertex(Gameboard *g, vertex *v){
+  invalidate_region_vertex_off(&g->w,v,0,0);
+}
+
+// invalidate the box around a single line
+static void invalidate_region_edges(GtkWidget *widget, vertex *v){
+  GdkRectangle r;
+  
+  if(v){
+    edge_list *el=v->edges;
+    while (el){
+      edge *e=el->edge;
+      r.x = min(e->A->x,e->B->x) - E_LINE;
+      r.y = min(e->A->y,e->B->y) - E_LINE;
+      r.width  = labs(e->B->x - e->A->x) + 1 + E_LINE*2;
+      r.height = labs(e->B->y - e->A->y) + 1 + E_LINE*2;
+      gdk_window_invalidate_rect (widget->window, &r, FALSE);
+      el=el->next;
+    }
+  }
+}
+
+// invalidate the vertex and attached verticies
+static void invalidate_region_attached(GtkWidget *widget, vertex *v){
+  if(v){
+    edge_list *el=v->edges;
+    while (el){
+      edge *e=el->edge;
+      if(e->A != v)invalidate_region_vertex(GAMEBOARD(widget),e->A);
+      if(e->B != v)invalidate_region_vertex(GAMEBOARD(widget),e->B);
+      el=el->next;
+    }
+    invalidate_region_vertex(GAMEBOARD(widget),v);
+  }
+}
+
+// invalidate the selection box plus enough area to catch any verticies
+static void invalidate_region_selection(GtkWidget *widget){
+  Gameboard *g = GAMEBOARD (widget);
+  GdkRectangle r;
+  r.x = g->selectionx - (V_RADIUS + V_LINE)*2;
+  r.y = g->selectiony - (V_RADIUS + V_LINE)*2;
+  r.width =  g->selectionw + (V_RADIUS + V_LINE)*4;
+  r.height = g->selectionh + (V_RADIUS + V_LINE)*4;
+  
+  gdk_window_invalidate_rect (widget->window, &r, FALSE);
+}
+
+// invalidate the selection box plus enough area to catch any verticies
+static void invalidate_region_verticies_selection(GtkWidget *widget){
+  Gameboard *g = GAMEBOARD (widget);
+  vertex *v=get_verticies();
+  while(v){
+    if(v->selected)
+      invalidate_region_vertex_off(widget,v,g->dragx,g->dragy);
+    v=v->next;
+  }
+}
+
+static cairo_surface_t *cache_vertex(Gameboard *g){
+  cairo_surface_t *ret=
+    cairo_surface_create_similar (cairo_get_target (g->wc),
+				  CAIRO_CONTENT_COLOR_ALPHA,
+				  (V_RADIUS+V_LINE)*2,
+				  (V_RADIUS+V_LINE)*2);
+  cairo_t *c = cairo_create(ret);
+  
+  cairo_set_line_width(c,V_LINE);
+  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS,0,2*M_PI);
+  cairo_set_source_rgb(c,V_FILL_IDLE_COLOR);
+  cairo_fill_preserve(c);
+  cairo_set_source_rgb(c,V_LINE_COLOR);
+  cairo_stroke(c);
+
+  cairo_destroy(c);
+  return ret;
+}
+
+static cairo_surface_t *cache_vertex_sel(Gameboard *g){
+  cairo_surface_t *ret=
+    cairo_surface_create_similar (cairo_get_target (g->wc),
+				  CAIRO_CONTENT_COLOR_ALPHA,
+				  (V_RADIUS+V_LINE)*2,
+				  (V_RADIUS+V_LINE)*2);
+  cairo_t *c = cairo_create(ret);
+  
+  cairo_set_line_width(c,V_LINE);
+  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS,0,2*M_PI);
+  cairo_set_source_rgb(c,V_FILL_LIT_COLOR);
+  cairo_fill_preserve(c);
+  cairo_set_source_rgb(c,V_LINE_COLOR);
+  cairo_stroke(c);
+  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS*.5,0,2*M_PI);
+  cairo_set_source_rgb(c,V_FILL_IDLE_COLOR);
+  cairo_fill(c);
+
+  cairo_destroy(c);
+  return ret;
+}
+
+static cairo_surface_t *cache_vertex_grabbed(Gameboard *g){
+  cairo_surface_t *ret=
+    cairo_surface_create_similar (cairo_get_target (g->wc),
+				  CAIRO_CONTENT_COLOR_ALPHA,
+				  (V_RADIUS+V_LINE)*2,
+				  (V_RADIUS+V_LINE)*2);
+  cairo_t *c = cairo_create(ret);
+  
+  cairo_set_line_width(c,V_LINE);
+  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS,0,2*M_PI);
+  cairo_set_source_rgb(c,V_FILL_LIT_COLOR);
+  cairo_fill_preserve(c);
+  cairo_set_source_rgb(c,V_LINE_COLOR);
+  cairo_stroke(c);
+  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS*.5,0,2*M_PI);
+  cairo_set_source_rgb(c,V_FILL_ADJ_COLOR);
+  cairo_fill(c);
+
+  cairo_destroy(c);
+  return ret;
+}
+
+static cairo_surface_t *cache_vertex_lit(Gameboard *g){
+  cairo_surface_t *ret=
+    cairo_surface_create_similar (cairo_get_target (g->wc),
+				  CAIRO_CONTENT_COLOR_ALPHA,
+				  (V_RADIUS+V_LINE)*2,
+				  (V_RADIUS+V_LINE)*2);
+  cairo_t *c = cairo_create(ret);
+  
+  cairo_set_line_width(c,V_LINE);
+  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS,0,2*M_PI);
+  cairo_set_source_rgb(c,V_FILL_LIT_COLOR);
+  cairo_fill_preserve(c);
+  cairo_set_source_rgb(c,V_LINE_COLOR);
+  cairo_stroke(c);
+
+  cairo_destroy(c);
+  return ret;
+}
+
+static cairo_surface_t *cache_vertex_attached(Gameboard *g){
+  cairo_surface_t *ret=
+    cairo_surface_create_similar (cairo_get_target (g->wc),
+				  CAIRO_CONTENT_COLOR_ALPHA,
+				  (V_RADIUS+V_LINE)*2,
+				  (V_RADIUS+V_LINE)*2);
+  cairo_t *c = cairo_create(ret);
+  
+  cairo_set_line_width(c,V_LINE);
+  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS,0,2*M_PI);
+  cairo_set_source_rgb(c,V_FILL_ADJ_COLOR);
+  cairo_fill_preserve(c);
+  cairo_set_source_rgb(c,V_LINE_COLOR);
+  cairo_stroke(c);
+
+  cairo_destroy(c);
+  return ret;
+}
+
+static cairo_surface_t *cache_vertex_ghost(Gameboard *g){
+  cairo_surface_t *ret=
+    cairo_surface_create_similar (cairo_get_target (g->wc),
+				  CAIRO_CONTENT_COLOR_ALPHA,
+				  (V_RADIUS+V_LINE)*2,
+				  (V_RADIUS+V_LINE)*2);
+  cairo_t *c = cairo_create(ret);
+  
+  cairo_set_line_width(c,V_LINE);
+  cairo_arc(c,V_RADIUS+V_LINE,V_RADIUS+V_LINE,V_RADIUS,0,2*M_PI);
+  cairo_set_source_rgba(c,V_LINE_COLOR,.2);
+  cairo_fill_preserve(c);
+  cairo_set_source_rgba(c,V_LINE_COLOR,.4);
+  cairo_stroke(c);
+
+  cairo_destroy(c);
+  return ret;
+}
+
+
+static void draw_vertex(cairo_t *c,vertex *v,cairo_surface_t *s){
+  cairo_set_source_surface(c,
+			   s,
+			   v->x-V_LINE-V_RADIUS,
+			   v->y-V_LINE-V_RADIUS);
+  cairo_paint(c);
+}      
+
+static void setup_background_edge(cairo_t *c){
+  cairo_set_line_width(c,E_LINE);
+  cairo_set_source_rgba(c,E_LINE_B_COLOR);
+}
+
+static void setup_foreground_edge(cairo_t *c){
+  cairo_set_line_width(c,E_LINE);
+  cairo_set_source_rgba(c,E_LINE_F_COLOR);
+}
+
+static void draw_edge(cairo_t *c,edge *e){
+  cairo_move_to(c,e->A->x,e->A->y);
+  cairo_line_to(c,e->B->x,e->B->y);
+}
+
+static void finish_edge(cairo_t *c){
+  cairo_stroke(c);
+}
+
+static void draw_selection_rectangle(Gameboard *g,cairo_t *c){
+  cairo_set_source_rgba(c,SELECTBOX_COLOR);
+  cairo_rectangle(c,g->selectionx,
+		  g->selectiony,
+		  g->selectionw,
+		  g->selectionh);
+  cairo_fill(c);
+}
+
+static void midground_draw(Gameboard *g,cairo_t *c,int x,int y,int w,int h){
+  /* Edges attached to the grabbed vertex are drawn here; they're the
+     inactive edges. */
+  if(g->grabbed_vertex && !g->hide_lines){
+    edge_list *el=g->grabbed_vertex->edges;
+    setup_foreground_edge(c);
+    while(el){
+      edge *e=el->edge;
+      /* no need to check rectangle; if they're to be drawn, they'll
+	 always be in the rect */
+      draw_edge(c,e);
+      el=el->next;
+    }
+    finish_edge(c);
+  }
+  
+  /* verticies are drawn in the foreground */
+  {
+    vertex *v = get_verticies();
+    int clipx = x-V_RADIUS;
+    int clipw = x+w+V_RADIUS;
+    int clipy = y-V_RADIUS;
+    int cliph = y+h+V_RADIUS;
+    
+    while(v){
+
+      /* is the vertex in the expose rectangle? */
+      if(v->x>=clipx && v->x<=clipw &&
+	 v->y>=clipy && v->y<=cliph){
+	
+	if(v == g->grabbed_vertex && !g->group_drag) {
+	  draw_vertex(c,v,g->vertex_grabbed);      
+	} else if( v->selected ){
+	  draw_vertex(c,v,g->vertex_sel);
+	} else if ( v == g->lit_vertex){
+	  draw_vertex(c,v,g->vertex_lit);
+	} else if (v->attached_to_grabbed && !g->group_drag){
+	  draw_vertex(c,v,g->vertex_attached);
+	}else
+	  draw_vertex(c,v,g->vertex);
+      }
+      
+      v=v->next;
+    }
+  }
+}
+
+static void background_draw(Gameboard *g,cairo_t *c){
+  int width=get_board_width();
+  int height=get_board_height();
+  edge *e=get_edges();
+
+  cairo_set_source_rgb(c,1,1,1);
+  cairo_paint(c);
+    
+  if(!g->hide_lines){
+    setup_background_edge(c);
+    while(e){
+      if(e->active){
+	draw_edge(c,e);
+      }
+      e=e->next;
+    }
+    finish_edge(c);
+  }
+
+  // if there's a a group drag in progress, midground is drawn here
+  if(g->group_drag)
+    midground_draw(g,c,0,0,width,height);
+
+}
+
+static void background_addv(Gameboard *g,cairo_t *c, vertex *v){
+  edge_list *el=v->edges;
+
+  if(!g->hide_lines){
+    setup_background_edge(c);
+    while(el){
+      edge *e=el->edge;
+      
+      if(e->active)
+	draw_edge(c,e);
+      
+      el=el->next;
+    }
+    finish_edge(c);
+  }
+}
+
+static void foreground_draw(Gameboard *g,cairo_t *c,
+			    int x,int y,int width,int height){
+  /* if a group drag is in progress, draw the group ghosted in the foreground */
+
+  if(g->group_drag){ 
+    vertex *v = get_verticies();
+    while(v){
+      
+      if( v->selected ){
+	vertex tv;
+	tv.x=v->x+g->dragx;
+	tv.y=v->y+g->dragy;
+	draw_vertex(c,&tv,g->vertex_ghost);
+      }
+
+      v=v->next;
+    }
+  }else
+    midground_draw(g,c,x,y,width,height);    
+
+  if(g->selection_grab)
+    draw_selection_rectangle(g,c);
+}
+
+static void update_background(GtkWidget *widget){
+  Gameboard *g = GAMEBOARD (widget);
+  GdkRectangle r;
+  cairo_t *c = cairo_create(g->background);
+
+  g->delayed_background=0;
+
+  // render the far background plane
+  background_draw(g,c);
+  cairo_destroy(c);
+  
+  r.x=0;
+  r.y=0;
+  r.width=widget->allocation.width;
+  r.height=widget->allocation.height;
+
+  gdk_window_invalidate_rect (widget->window, &r, FALSE);
+    
+}
+
+void update_full(Gameboard *g){
+  update_background(&g->w);
+}
+
+// also updates score
+static void update_background_addv(GtkWidget *widget, vertex *v){
+  Gameboard *g = GAMEBOARD (widget);
+  cairo_t *c = cairo_create(g->background);
+
+  g->delayed_background=0;
+
+  // render the far background plane
+  background_addv(g,c,v);
+  cairo_destroy(c);
+
+  invalidate_region_attached(widget,v);    
+}
+
+static void update_background_delayed(GtkWidget *widget){
+  Gameboard *g = GAMEBOARD (widget);
+  g->delayed_background=1;
+}
+
+static void check_lit(GtkWidget *widget,int x, int y){
+  Gameboard *g = GAMEBOARD (widget);
+  vertex *v = find_vertex(x,y);
+  if(v!=g->lit_vertex){
+    invalidate_region_vertex(g,v);
+    invalidate_region_vertex(g,g->lit_vertex);
+    g->lit_vertex = v;
+  }
+}
+
+static void score_update(Gameboard *g){
+  /* Score, level, intersections */
+  char level_string[160];
+  char score_string[160];
+  char int_string[160];
+  char obj_string[160];
+  cairo_text_extents_t extentsL;
+  cairo_text_extents_t extentsS;
+  cairo_text_extents_t extentsO;
+  cairo_text_extents_t extentsI;
+  cairo_matrix_t m;
+
+  cairo_t *c = cairo_create(g->forescore);
+
+  if(get_num_intersections() <= get_objective()){
+    deploy_check(g);
+  }else{
+    undeploy_check(g);
+  }
+
+
+  // clear the pane
+  cairo_save(c);
+  cairo_set_operator(c,CAIRO_OPERATOR_CLEAR);
+  cairo_set_source_rgba (c, 1,1,1,1);
+  cairo_paint(c);
+  cairo_restore(c);
+
+  topbox(c,get_board_width(),SCOREHEIGHT);
+
+  cairo_select_font_face (c, "Arial",
+			  CAIRO_FONT_SLANT_NORMAL,
+			  CAIRO_FONT_WEIGHT_BOLD);
+
+  cairo_matrix_init_scale (&m, 12.,15.);
+  cairo_set_font_matrix (c,&m);
+  cairo_set_source_rgba (c, TEXT_COLOR);
+
+  snprintf(level_string,160,"Level %d: \"%s\"",get_level()+1,get_level_string());
+  snprintf(score_string,160,"Score: %d",get_score());
+  snprintf(int_string,160,"Intersections: %d",get_num_intersections());
+  snprintf(obj_string,160,"Objective: %s",get_objective_string());
+
+  cairo_text_extents (c, level_string, &extentsL);
+  cairo_text_extents (c, obj_string, &extentsO);
+  cairo_text_extents (c, int_string, &extentsI);
+  cairo_text_extents (c, score_string, &extentsS);
+
+  /*
+  text_h = extentsL.height;
+  text_h = max(text_h,extentsO.height);
+  text_h = max(text_h,extentsI.height);
+  text_h = max(text_h,extentsS.height);
+  */
+
+  int ty1 = 23;
+  int ty2 = 38;
+
+  cairo_move_to (c, 15, ty1);
+  cairo_show_text (c, int_string);  
+  cairo_move_to (c, 15, ty2);
+  cairo_show_text (c, score_string);  
+
+  cairo_move_to (c, get_board_width()-extentsL.width-15, ty1);
+  cairo_show_text (c, level_string);  
+  cairo_move_to (c, get_board_width()-extentsO.width-15, ty2);
+  cairo_show_text (c, obj_string);  
+
+  cairo_destroy(c);
+
+  // slightly lazy
+  {
+    GdkRectangle r;
+    r.x=0;
+    r.y=0;
+    r.width=get_board_width();
+    r.height = SCOREHEIGHT;
+    gdk_window_invalidate_rect (g->w.window, &r, FALSE);
+  }
+}
+
+static gint mouse_motion(GtkWidget        *widget,
+			 GdkEventMotion   *event){
+  Gameboard *g = GAMEBOARD (widget);
+
+  if(g->button_grabbed){
+    g->button_grabbed = 
+      button_motion_event(g,event,
+			  (!g->lit_vertex && !g->grabbed_vertex && !g->selection_grab));
+  }
+
+  if(!g->button_grabbed){
+    if(g->grabbed_vertex){
+      int x = (int)event->x;
+      int y = (int)event->y;
+      g->dragx = x-g->grabx;
+      g->dragy = y-g->graby;
+      
+      invalidate_region_vertex(g,g->grabbed_vertex);
+      invalidate_region_edges(widget,g->grabbed_vertex);
+      move_vertex(g->grabbed_vertex,x+g->graboffx,y+g->graboffy);
+      invalidate_region_vertex(g,g->grabbed_vertex);
+      invalidate_region_edges(widget,g->grabbed_vertex);
+    }else if (g->selection_grab){
+      invalidate_region_selection(widget);
+      
+      g->selectionx = min(g->grabx, event->x);
+      g->selectionw = labs(g->grabx - event->x);
+      g->selectiony = min(g->graby, event->y);
+      g->selectionh = labs(g->graby - event->y);
+      select_verticies(g->selectionx,g->selectiony,
+		       g->selectionx+g->selectionw-1,
+		       g->selectiony+g->selectionh-1);
+      
+      invalidate_region_selection(widget);
+    }else if(g->group_drag){
+      invalidate_region_verticies_selection(widget);
+      g->dragx = event->x - g->grabx;
+      g->dragy = event->y - g->graby;
+      
+      // if this puts any of the dragged offscreen adjust the drag back
+      // onscreen.  It will avoid confusing/concerning users
+      {
+	vertex *v=get_verticies();
+	int w=get_board_width();
+	int h=get_board_height();
+	
+	while(v){
+	  if(v->selected){
+	    if(v->x + g->dragx >= w)
+	      g->dragx=w - v->x -1;
+	    if(v->x + g->dragx < 0 )
+	      g->dragx= -v->x;
+	    if(v->y + g->dragy >= h)
+	      g->dragy=h - v->y -1;
+	    if(v->y + g->dragy < 0 )
+	    g->dragy= -v->y;
+	  }
+	  v=v->next;
+	}
+      }
+      
+      invalidate_region_verticies_selection(widget);
+    }else{
+      check_lit(widget, (int)event->x,(int)event->y);
+      
+      button_motion_event(g,event,
+			  (!g->lit_vertex && !g->grabbed_vertex && !g->selection_grab));
+
+    }
+  }
+  return TRUE;
+}
+
+static gboolean button_press (GtkWidget        *widget,
+			      GdkEventButton   *event){
+  Gameboard *g = GAMEBOARD (widget);
+
+  vertex *v = find_vertex((int)event->x,(int)event->y);
+  g->button_grabbed=0;
+
+  if(!g->group_drag && event->state&GDK_SHIFT_MASK){
+    if(v){
+      if(v->selected){
+	v->selected=0;
+	g->selection_active--;
+      }else{
+	v->selected=1;
+	g->selection_active++;
+      }
+      invalidate_region_vertex(g,g->lit_vertex);
+    }else{
+      // addending group drag
+      g->selection_grab=1;
+      g->grabx = (int)event->x;
+      g->graby = (int)event->y;
+      g->selectionx = g->grabx;
+      g->selectionw = 0;
+      g->selectiony = g->graby;
+      g->selectionh = 0;
+    }
+  }else{
+
+    if(g->selection_active){
+      vertex *v = find_vertex((int)event->x,(int)event->y);
+      if(v && v->selected){
+	// group drag
+	g->group_drag=1;
+	g->grabx = (int)event->x;
+	g->graby = (int)event->y;
+	g->dragx = 0;
+	g->dragy = 0;
+	// put the verticies into the background for faster update
+	update_background(widget); 
+      }else{
+
+	if(button_button_press(g,event,1)){
+	  g->button_grabbed=1;
+	}else{
+	  deselect_verticies();
+	  update_background(widget); 
+	  g->selection_active=0;
+	  g->group_drag=0;
+	  check_lit(widget,event->x,event->y);
+	  button_press(widget,event);
+	}
+      }
+
+    }else if(g->lit_vertex){
+      // vertex grab
+      g->grabbed_vertex = g->lit_vertex;
+      g->grabx = (int)event->x;
+      g->graby = (int)event->y;
+      g->graboffx = g->grabbed_vertex->x-g->grabx;
+      g->graboffy = g->grabbed_vertex->y-g->graby;
+      
+      grab_vertex(g->grabbed_vertex);
+      invalidate_region_attached(widget,g->grabbed_vertex);
+      invalidate_region_edges(widget,g->grabbed_vertex);
+      
+      update_background_delayed(widget);
+    }else{
+      
+      if(button_button_press(g,event,1)){
+	g->button_grabbed=1;
+      }else{
+	// selection grab;
+	g->selection_grab=1;
+	g->grabx = (int)event->x;
+	g->graby = (int)event->y;
+	g->selectionx = g->grabx;
+	g->selectionw = 0;
+	g->selectiony = g->graby;
+	g->selectionh = 0;
+      }
+    }
+  }
+
+  if(!g->button_grabbed)
+    hide_intersections(g);
+  
+  return TRUE;
+}
+
+static gboolean button_release (GtkWidget        *widget,
+				GdkEventButton   *event){
+  Gameboard *g = GAMEBOARD (widget);
+
+  button_button_release(g,event,1);
+
+  if(g->grabbed_vertex){
+    ungrab_vertex(g->grabbed_vertex);
+    update_background_addv(widget,g->grabbed_vertex);
+    score_update(g);
+    g->grabbed_vertex = 0;
+  }
+
+  if(g->selection_grab){
+    invalidate_region_selection(widget);
+    g->selection_grab=0;
+    // are there selected verticies? Avoid accidentally misgrabbing one.
+    if(num_selected_verticies()<=1){
+      g->selection_active=0;
+      deselect_verticies(); // could have grabbed just one
+    }else{
+      commit_volatile_selection();
+      g->selection_active=num_selected_verticies();
+    }
+  }
+
+  if(g->group_drag){
+    move_selected_verticies(g->dragx,g->dragy);
+    update_background(widget); // cheating
+    g->group_drag=0;
+  }
+
+  mouse_motion(widget,(GdkEventMotion *)event); // the cast is safe in
+						// this case
+
+  return TRUE;
+}
+
+static void size_request (GtkWidget *widget,GtkRequisition *requisition){
+
+  requisition->width = get_board_width();
+  requisition->height = get_board_height();
+
+}
+
+static void gameboard_init (Gameboard *g){
+
+  // instance initialization
+
+
+}
+
+static void gameboard_destroy (GtkObject *object){
+  if (GTK_OBJECT_CLASS (parent_class)->destroy)
+    (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void draw_intersection(cairo_t *c,double x, double y){
+  cairo_move_to(c,x-INTERSECTION_RADIUS,y);
+  cairo_rel_line_to(c,INTERSECTION_RADIUS,-INTERSECTION_RADIUS);
+  cairo_rel_line_to(c,INTERSECTION_RADIUS,INTERSECTION_RADIUS);
+  cairo_rel_line_to(c,-INTERSECTION_RADIUS,INTERSECTION_RADIUS);
+  cairo_close_path(c);
+}
+static void expose_intersections(Gameboard *g,cairo_t *c,
+				 int x,int y,int w,int h){
+
+  if(g->show_intersections){
+    cairo_set_source_rgba (c, INTERSECTION_COLOR);
+    cairo_set_line_width(c, INTERSECTION_LINE_WIDTH);
+
+    double xx=x-(INTERSECTION_LINE_WIDTH*.5 + INTERSECTION_RADIUS);
+    double x2=w+x+(INTERSECTION_LINE_WIDTH + INTERSECTION_RADIUS*2);
+    double yy=y-(INTERSECTION_LINE_WIDTH*.5 + INTERSECTION_RADIUS);
+    double y2=h+y+(INTERSECTION_LINE_WIDTH + INTERSECTION_RADIUS*2);
+
+    // walk the intersection list of all edges; draw if paired is a higher pointer
+    edge *e=get_edges();
+    while(e){
+      intersection *i = e->i.next;
+      while(i){
+	if(i->paired > i){
+	  double ix=i->x, iy=i->y;
+    
+	  if(ix >= xx && ix <= x2 && iy >= yy && iy <= y2)
+	    draw_intersection(c,ix,iy);
+	}
+	i=i->next;
+      }
+      e=e->next;
+    }
+    cairo_stroke(c);
+  }
+}
+
+void run_immediate_expose(Gameboard *g,
+			  int x, int y, int w, int h){
+
+  if (w==0 || h==0) return;
+
+  //fprintf(stderr,"(%d) %d/%d %d/%d\n",g->first_expose,x,w,y,h);
+
+  cairo_t *c = cairo_create(g->foreground);
+
+  // copy background to foreground draw buffer
+  cairo_set_source_surface(c,g->background,0,0);
+  cairo_rectangle(c,x,y,w,h);
+  cairo_fill(c);
+  foreground_draw(g,c,x,y,w,h);
+
+  // copy in any of the score or button surfaces?
+  if(y<SCOREHEIGHT){
+    cairo_set_source_surface(c,g->forescore,0,0);
+    cairo_rectangle(c, x,y,w,
+		    min(SCOREHEIGHT-y,h));
+    cairo_fill(c);
+  }
+  if(y+h>g->w.allocation.height-SCOREHEIGHT){
+     cairo_set_source_surface(c,g->forebutton,0,g->w.allocation.height-SCOREHEIGHT);
+     cairo_rectangle(c, x,y,w,h);
+     cairo_fill(c);
+  }
+  expose_buttons(g,c,x,y,w,h);
+
+  expose_intersections(g,c,x,y,w,h);
+
+  cairo_destroy(c);
+  
+  // blit to window
+  cairo_set_source_surface(g->wc,g->foreground,0,0);
+  cairo_rectangle(g->wc,x,y,w,h);
+  cairo_fill(g->wc);
+
+  if(g->delayed_background)update_background(&g->w);
+  g->first_expose=1;
+  //gdk_window_process_all_updates(); 
+}
+
+static gint gameboard_expose (GtkWidget      *widget,
+			      GdkEventExpose *event){
+  
+  Gameboard *g = GAMEBOARD (widget);
+  run_immediate_expose(g,event->area.x,event->area.y,
+		       event->area.width,event->area.height);
+  return FALSE;
+}
+
+static void paint_bottom_box (Gameboard *g){
+  cairo_t *c = cairo_create(g->forebutton);
+  
+  cairo_save(c);
+  cairo_set_operator(c,CAIRO_OPERATOR_CLEAR);
+  cairo_set_source_rgba (c, 1,1,1,1);
+  cairo_paint(c);
+  cairo_restore(c);
+
+  bottombox(c,g->w.allocation.width,SCOREHEIGHT);
+  cairo_destroy(c);
+}
+
+static void gameboard_realize (GtkWidget *widget){
+  Gameboard *g = GAMEBOARD (widget);
+  GdkWindowAttr attributes;
+  gint      attributes_mask;
+
+  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+  attributes.x = widget->allocation.x;
+  attributes.y = widget->allocation.y;
+  attributes.width = widget->allocation.width;
+  attributes.height = widget->allocation.height;
+  attributes.wclass = GDK_INPUT_OUTPUT;
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.event_mask = 
+    gtk_widget_get_events (widget) | 
+    GDK_EXPOSURE_MASK|
+    GDK_POINTER_MOTION_MASK|
+    GDK_BUTTON_PRESS_MASK  |
+    GDK_BUTTON_RELEASE_MASK|
+    GDK_KEY_PRESS_MASK |
+    GDK_KEY_RELEASE_MASK |
+    GDK_STRUCTURE_MASK;
+  attributes.visual = gtk_widget_get_visual (widget);
+  attributes.colormap = gtk_widget_get_colormap (widget);
+  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+  widget->window = gdk_window_new (widget->parent->window,
+				   &attributes, attributes_mask);
+  gtk_style_attach (widget->style, widget->window);
+  gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+  gdk_window_set_user_data (widget->window, widget);
+  gtk_widget_set_double_buffered (widget, FALSE);
+
+  g->wc = gdk_cairo_create(widget->window);
+
+  g->forescore = 
+    cairo_surface_create_similar (cairo_get_target (g->wc),
+				  CAIRO_CONTENT_COLOR_ALPHA,
+				  widget->allocation.width,
+				  SCOREHEIGHT);
+
+  g->forebutton = 
+    cairo_surface_create_similar (cairo_get_target (g->wc),
+				  CAIRO_CONTENT_COLOR_ALPHA,
+				  widget->allocation.width,
+				  SCOREHEIGHT);
+
+  g->background = 
+    cairo_surface_create_similar (cairo_get_target (g->wc),
+				  CAIRO_CONTENT_COLOR,
+				  widget->allocation.width,
+				  widget->allocation.height);
+  g->foreground = 
+    cairo_surface_create_similar (cairo_get_target (g->wc),
+				  CAIRO_CONTENT_COLOR,
+				  widget->allocation.width,
+				  widget->allocation.height);  
+
+  // cache the vertex images that are drawn in quantity; the blit with
+  // alpha is much faster than the render
+
+  g->vertex = cache_vertex(g);
+  g->vertex_lit = cache_vertex_lit(g);
+  g->vertex_attached = cache_vertex_attached(g);
+  g->vertex_grabbed = cache_vertex_grabbed(g);
+  g->vertex_sel = cache_vertex_sel(g);
+  g->vertex_ghost = cache_vertex_ghost(g);
+
+  update_background(widget); 
+  score_update(g);
+  paint_bottom_box(g);
+  init_buttons(g);
+}
+
+static void gameboard_size_allocate (GtkWidget     *widget,
+				     GtkAllocation *allocation){
+  Gameboard *g = GAMEBOARD (widget);
+  widget->allocation = *allocation;
+
+  if (GTK_WIDGET_REALIZED (widget)){
+    int oldw=get_board_width();
+    int oldh=get_board_height();
+
+    gdk_window_move_resize (widget->window, allocation->x, allocation->y, 
+			    allocation->width, allocation->height);
+    
+ 
+     if (g->forescore)
+      cairo_surface_destroy(g->forescore);
+    if (g->forebutton)
+      cairo_surface_destroy(g->forebutton);
+    if (g->background)
+      cairo_surface_destroy(g->background);
+    if (g->foreground)
+      cairo_surface_destroy(g->foreground);
+    
+    g->background = 
+      cairo_surface_create_similar (cairo_get_target (g->wc),
+				    CAIRO_CONTENT_COLOR, // don't need alpha
+				    widget->allocation.width,
+				    widget->allocation.height);
+    g->forescore = 
+      cairo_surface_create_similar (cairo_get_target (g->wc),
+				    CAIRO_CONTENT_COLOR_ALPHA,
+				    widget->allocation.width,
+				    SCOREHEIGHT);
+    g->forebutton = 
+      cairo_surface_create_similar (cairo_get_target (g->wc),
+				    CAIRO_CONTENT_COLOR_ALPHA,
+				    widget->allocation.width,
+				    SCOREHEIGHT);
+
+    g->foreground = 
+      cairo_surface_create_similar (cairo_get_target (g->wc),
+				    CAIRO_CONTENT_COLOR, // don't need alpha
+				    widget->allocation.width,
+				    widget->allocation.height);  
+
+    // recenter all the verticies; doesn't require recomputation
+    {
+      vertex *v=get_verticies();
+      int xd=(widget->allocation.width-oldw)*.5;
+      int yd=(widget->allocation.height-oldh)*.5;
+
+      while(v){
+	v->x+=xd;
+	v->y+=yd;
+	v=v->next;
+      }
+    }
+
+    // also verifies all verticies are onscreen
+    resize_board(allocation->width,allocation->height);
+
+    update_background(widget);
+    paint_bottom_box(g);
+    score_update(g);
+    resize_buttons(widget->allocation.width,widget->allocation.height);
+  }
+}
+
+static void gameboard_class_init (GameboardClass * class) {
+
+  GtkObjectClass *object_class;
+  GtkWidgetClass *widget_class;
+
+  object_class = (GtkObjectClass *) class;
+  widget_class = (GtkWidgetClass *) class;
+
+  parent_class = gtk_type_class (GTK_TYPE_WIDGET);
+
+  object_class->destroy = gameboard_destroy;
+
+  widget_class->realize = gameboard_realize;
+  widget_class->expose_event = gameboard_expose;
+  widget_class->size_request = size_request;
+  widget_class->size_allocate = gameboard_size_allocate;
+  widget_class->button_press_event = button_press;
+  widget_class->button_release_event = button_release;
+  widget_class->motion_notify_event = mouse_motion;
+
+}
+
+GType gameboard_get_type (void){
+
+  static GType gameboard_type = 0;
+
+  if (!gameboard_type)
+    {
+      static const GTypeInfo gameboard_info = {
+        sizeof (GameboardClass),
+        NULL,
+        NULL,
+        (GClassInitFunc) gameboard_class_init,
+        NULL,
+        NULL,
+        sizeof (Gameboard),
+        0,
+        (GInstanceInitFunc) gameboard_init,
+	0
+      };
+
+      gameboard_type = g_type_register_static (GTK_TYPE_WIDGET, "Gameboard",
+                                               &gameboard_info, 0);
+    }
+  
+  return gameboard_type;
+}
+
+Gameboard *gameboard_new (void) {
+  GtkWidget *g = GTK_WIDGET (g_object_new (GAMEBOARD_TYPE, NULL));
+  Gameboard *gb = GAMEBOARD (g);
+  return gb;
+}
+
+void gameboard_reset (Gameboard *g) {
+  g->lit_vertex=0;
+  g->grabbed_vertex=0;
+  g->delayed_background=0;
+
+  g->group_drag=0;
+  g->button_grabbed=0;
+  g->selection_grab=0;
+  g->selection_active=0;
+
+  update_background(&g->w);
+  score_update(g);
+}
+
+void hide_lines(Gameboard *g){
+  g->hide_lines=1;
+  update_background(&g->w);
+}
+
+void show_lines(Gameboard *g){
+  g->hide_lines=0;
+  update_background(&g->w);
+}
+
+int get_hide_lines(Gameboard *g){
+  return g->hide_lines;
+}
+
+void show_intersections(Gameboard *g){
+  if(!g->show_intersections){
+    g->show_intersections=1;
+    update_background(&g->w);
+  }else{
+    hide_intersections(g);
+  }
+}
+
+void hide_intersections(Gameboard *g){
+  if(g->show_intersections){
+    g->show_intersections=0;
+    update_background(&g->w);
+  }
+}
+
+int selected(Gameboard *g){
+  return g->selection_active;
+}

Added: trunk/planarity/gameboard.h
===================================================================
--- trunk/planarity/gameboard.h	2005-07-26 23:19:44 UTC (rev 9630)
+++ trunk/planarity/gameboard.h	2005-07-28 03:59:23 UTC (rev 9631)
@@ -0,0 +1,96 @@
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtkcontainer.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtkdrawingarea.h>
+
+
+#define V_RADIUS 8
+#define V_LINE 2
+#define V_LINE_COLOR         0, 0, 0
+#define V_FILL_IDLE_COLOR   .2,.2, 1
+#define V_FILL_LIT_COLOR     1, 1, 1
+#define V_FILL_ADJ_COLOR     1,.2,.2
+
+#define E_LINE 1.5
+#define E_LINE_F_COLOR       0, 0, 0, 1
+#define E_LINE_B_COLOR      .5,.5,.5, 1
+#define SELECTBOX_COLOR     .2,.8,.8,.3
+
+#define INTERSECTION_COLOR  1,.1,.1,.8
+#define INTERSECTION_RADIUS 6
+#define INTERSECTION_LINE_WIDTH 2
+
+G_BEGIN_DECLS
+
+#define GAMEBOARD_TYPE            (gameboard_get_type ())
+#define GAMEBOARD(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAMEBOARD_TYPE, Gameboard))
+#define GAMEBOARD_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GAMEBOARD_TYPE, GameboardClass))
+#define IS_GAMEBOARD(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAMEBOARD_TYPE))
+#define IS_GAMEBOARD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAMEBOARD_TYPE))
+
+typedef struct _Gameboard       Gameboard;
+typedef struct _GameboardClass  GameboardClass;
+
+struct _Gameboard{
+  GtkWidget w;
+
+  cairo_t         *wc;
+  cairo_surface_t *vertex;
+  cairo_surface_t *vertex_lit;
+  cairo_surface_t *vertex_grabbed;
+  cairo_surface_t *vertex_attached;
+  cairo_surface_t *vertex_sel;
+  cairo_surface_t *vertex_ghost;
+  cairo_surface_t *forescore;
+  cairo_surface_t *forebutton;
+  cairo_surface_t *background;
+  cairo_surface_t *foreground;
+  int delayed_background;
+
+  vertex *grabbed_vertex;
+  vertex *lit_vertex;
+  int group_drag;
+  int button_grabbed;
+  
+  int grabx;
+  int graby;
+  int dragx;
+  int dragy;
+  int graboffx;
+  int graboffy;
+  
+  int selection_grab;
+  int selection_active;
+  int selectionx;
+  int selectiony;
+  int selectionw;
+  int selectionh;
+
+  int hide_lines;
+
+  int first_expose;
+  int show_intersections;
+};
+
+struct _GameboardClass{
+  GtkWidgetClass parent_class;
+  void (* gameboard) (Gameboard *m);
+};
+
+GType          gameboard_get_type        (void);
+Gameboard*     gameboard_new             (void);
+
+G_END_DECLS
+
+extern void main_board(int argc, char *argv[]);
+extern void run_immediate_expose(Gameboard *g,int x, int y, int w, int h);
+extern void gameboard_reset(Gameboard *g);
+extern void hide_lines(Gameboard *g);
+extern void show_lines(Gameboard *g);
+extern int get_hide_lines(Gameboard *g);
+extern void show_intersections(Gameboard *g);
+extern void hide_intersections(Gameboard *g);
+extern void invalidate_region_vertex(Gameboard *g, vertex *v);
+extern int selected(Gameboard *g);
+extern void update_full(Gameboard *g);

Added: trunk/planarity/gamestate.c
===================================================================
--- trunk/planarity/gamestate.c	2005-07-26 23:19:44 UTC (rev 9630)
+++ trunk/planarity/gamestate.c	2005-07-28 03:59:23 UTC (rev 9631)
@@ -0,0 +1,233 @@
+#include <math.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include "graph.h"
+#include "gameboard.h"
+#include "generate.h"
+#include "gamestate.h"
+#include "buttons.h"
+
+Gameboard *gameboard;
+
+static int width=800;
+static int height=640;
+
+static int level=0;
+static int score=0;
+
+static int initial_intersections;
+static int objective=0;
+static int objective_lessthan=0;
+
+static gboolean key_press(GtkWidget *w,GdkEventKey *event,gpointer in){
+
+  if(event->keyval == GDK_q && event->state&GDK_CONTROL_MASK) 
+    gtk_main_quit();
+
+  return FALSE;
+}
+
+void resize_board(int x, int y){
+  width=x;
+  height=y;
+  check_verticies();
+}
+
+int get_board_width(){
+  return width;
+}
+
+int get_board_height(){
+  return height;
+}
+
+void setup_board(){
+  generate_mesh_1(level);
+  impress_location();
+  initial_intersections = get_num_intersections();
+  gameboard_reset(gameboard);
+
+  //gdk_flush();
+  deploy_buttons(gameboard);
+}
+
+#define RESET_DELTA 2;
+
+void reset_board(){
+  int flag=1;
+  int hide_state = get_hide_lines(gameboard);
+  hide_lines(gameboard);
+
+  vertex *v=get_verticies();
+  while(v){
+    deactivate_vertex(v);
+    v=v->next;
+  }
+
+  while(flag){
+    flag=0;
+
+    v=get_verticies();
+    while(v){
+      if(v->x != v->orig_x || v->y != v->orig_y){
+	flag=1;
+      
+	invalidate_region_vertex(gameboard,v);
+	if(v->x<v->orig_x){
+	  v->x+=RESET_DELTA;
+	  if(v->x>v->orig_x)v->x=v->orig_x;
+	}else{
+	  v->x-=RESET_DELTA;
+	  if(v->x<v->orig_x)v->x=v->orig_x;
+	}
+	if(v->y<v->orig_y){
+	  v->y+=RESET_DELTA;
+	  if(v->y>v->orig_y)v->y=v->orig_y;
+	}else{
+	  v->y-=RESET_DELTA;
+	  if(v->y<v->orig_y)v->y=v->orig_y;
+	}
+	invalidate_region_vertex(gameboard,v);
+      }
+      v=v->next;
+    }
+    gdk_window_process_all_updates();
+    gdk_flush();
+    
+  }
+
+  v=get_verticies();
+  while(v){
+    activate_vertex(v);
+    v=v->next;
+  }
+
+  if(!hide_state)show_lines(gameboard);
+  if(get_num_intersections() <= get_objective()){
+    deploy_check(gameboard);
+  }else{
+    undeploy_check(gameboard);
+  }
+  update_full(gameboard);
+  // reset timer
+}
+
+void scale(double scale){
+  int x=get_board_width()/2;
+  int y=get_board_height()/2;
+  int sel = selected(gameboard);
+  // if selected, expand from center of selected mass
+  if(sel){
+    vertex *v=get_verticies();
+    double xac=0;
+    double yac=0;
+    while(v){
+      if(v->selected){
+	xac+=v->x;
+	yac+=v->y;
+      }
+      v=v->next;
+    }
+    x = xac / selected(gameboard);
+    y = yac / selected(gameboard);
+  }
+
+  vertex *v=get_verticies();
+  while(v){
+  
+    if(!sel || v->selected){
+      int nx = rint((v->x - x)*scale+x);
+      int ny = rint((v->y - y)*scale+y);
+      
+      if(nx<0)nx=0;
+      if(nx>=width)nx=width-1;
+      if(ny<0)ny=0;
+      if(ny>=height)ny=height-1;
+
+      deactivate_vertex(v);
+      v->x = nx;
+      v->y = ny;
+    }
+    v=v->next;
+  }
+  
+  v=get_verticies();
+  while(v){
+    activate_vertex(v);
+    v=v->next;
+  }
+
+  update_full(gameboard);
+}
+
+void expand(){
+  scale(1.2);
+}
+
+void shrink(){
+  scale(.8);
+}
+
+void hide_show_lines(){
+  if(get_hide_lines(gameboard))
+    show_lines(gameboard);
+  else
+    hide_lines(gameboard);
+}
+
+void mark_intersections(){
+  show_intersections(gameboard);
+}
+
+void finish_board(){
+  if(get_num_intersections()<=initial_intersections){
+    score+=initial_intersections;
+    level++;
+    undeploy_buttons(gameboard,setup_board);
+  }
+}
+
+void quit(){
+  undeploy_buttons(gameboard,gtk_main_quit);
+}
+
+int get_score(){
+  return score + initial_intersections-get_num_intersections();
+}
+
+int get_objective(){
+  return objective;
+}
+
+char *get_objective_string(){
+  return "zero intersections";
+}
+
+int get_level(){
+  return level;
+}
+
+char *get_level_string(){
+  return "\"original level\"";
+}
+
+int main(int argc, char *argv[]){
+  GtkWidget *window;
+  gtk_init (&argc, &argv);
+  
+  window   = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  g_signal_connect (G_OBJECT (window), "delete-event",
+                    G_CALLBACK (gtk_main_quit), NULL);
+  g_signal_connect (G_OBJECT (window), "key-press-event",
+                    G_CALLBACK (key_press), window);
+  
+  gameboard = gameboard_new ();
+
+  gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET(gameboard));
+  gtk_widget_show_all (window);
+
+  setup_board(gameboard);
+
+  gtk_main ();
+  return 0;
+}

Added: trunk/planarity/gamestate.h
===================================================================
--- trunk/planarity/gamestate.h	2005-07-26 23:19:44 UTC (rev 9630)
+++ trunk/planarity/gamestate.h	2005-07-28 03:59:23 UTC (rev 9631)
@@ -0,0 +1,17 @@
+
+extern void resize_board(int x, int y);
+extern int get_board_width();
+extern int get_board_height();
+extern void setup_board();
+extern void finish_board();
+extern void hide_show_lines();
+extern void mark_intersections();
+extern void reset_board();
+extern void expand();
+extern void shrink();
+extern int get_score();
+extern int get_objective();
+extern char *get_objective_string();
+extern int get_level();
+extern char *get_level_string();
+extern void quit();

Added: trunk/planarity/generate.c
===================================================================
--- trunk/planarity/generate.c	2005-07-26 23:19:44 UTC (rev 9630)
+++ trunk/planarity/generate.c	2005-07-28 03:59:23 UTC (rev 9631)
@@ -0,0 +1,227 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "graph.h"
+#include "generate.h"
+#include "arrange.h"
+
+typedef struct {
+  vertex **v;
+  int width;
+  int height;
+} mesh;
+
+typedef struct {
+  int vnum[8];
+  vertex *center;
+  mesh   *m;
+} neighbors_grid;
+
+typedef struct {
+  int vnum[8];
+  int num;
+} neighbors_list;
+
+static void populate_neighbors(int vnum, mesh *m, 
+			       neighbors_grid *ng){
+  int width = m->width;
+  int y = vnum/width;
+  int x = vnum - (y*width);
+  int i;
+
+  for(i=0;i<8;i++)ng->vnum[i]=-1;
+
+
+  ng->center = m->v[vnum];
+  ng->m = m;
+
+  if(y-1 >= 0){
+    if(x-1 >= 0)        ng->vnum[0]= (y-1)*width+(x-1);
+                        ng->vnum[1]= (y-1)*width+x;
+    if(x+1 <  m->width) ng->vnum[2]= (y-1)*width+(x+1);
+  }
+
+  if(x-1   >= 0)        ng->vnum[3]= y*width+(x-1);
+  if(x+1   <  m->width) ng->vnum[4]= y*width+(x+1);
+
+  if(y+1   < m->height){
+    if(x-1 >= 0)        ng->vnum[5]= (y+1)*width+(x-1);
+                        ng->vnum[6]= (y+1)*width+x;
+    if(x+1 <  m->width) ng->vnum[7]= (y+1)*width+(x+1);
+  }
+
+}
+
+// eliminate from neighbor structs the verticies that already have at
+// least one edge
+static void filter_spanned_neighbors(neighbors_grid *ng,
+				     neighbors_list *nl){
+  int i;
+  int count=0;
+  for(i=0;i<8;i++)
+    if(ng->vnum[i]==-1 || ng->m->v[ng->vnum[i]]->edges){
+      ng->vnum[i]=-1;
+    }else{
+      nl->vnum[count++]=ng->vnum[i];
+    }
+  nl->num=count;
+
+}
+
+// eliminate from neighbor struct any verticies to which we can't make
+// an edge without crossing another edge.  Only 0,2,5,7 possible.
+static void filter_intersections(neighbors_grid *ng){
+  int i;
+  for(i=0;i<8;i++){
+    switch(i){
+    case 0: 
+      if(ng->vnum[1] != -1 && 
+	 ng->vnum[3] != -1 &&
+	 exists_edge(ng->m->v[ng->vnum[1]],
+		     ng->m->v[ng->vnum[3]]))
+	ng->vnum[i]=-1;
+      break;
+      
+    case 2: 
+      if(ng->vnum[1] != -1 && 
+	 ng->vnum[4] != -1 &&
+	 exists_edge(ng->m->v[ng->vnum[1]],
+		     ng->m->v[ng->vnum[4]]))
+	ng->vnum[i]=-1;
+      break;
+      
+    case 5: 
+      if(ng->vnum[3] != -1 && 
+	 ng->vnum[6] != -1 &&
+	 exists_edge(ng->m->v[ng->vnum[3]],
+		     ng->m->v[ng->vnum[6]]))
+	ng->vnum[i]=-1;
+      break;
+      
+    case 7: 
+      if(ng->vnum[4] != -1 && 
+	 ng->vnum[6] != -1 &&
+	 exists_edge(ng->m->v[ng->vnum[4]],
+		     ng->m->v[ng->vnum[6]]))
+	ng->vnum[i]=-1;
+      break;
+    } 
+  }
+}
+
+// eliminate verticies we've already connected to
+static void filter_edges(neighbors_grid *ng,
+			 neighbors_list *nl){
+
+  vertex *v=ng->center;
+  int count=0,i;
+  for(i=0;i<8;i++){
+    if(ng->vnum[i]!=-1){
+      if(!exists_edge(v,ng->m->v[ng->vnum[i]]))
+	nl->vnum[count++]=ng->vnum[i];
+      else
+	ng->vnum[i]=-1;
+    }
+  }
+  nl->num=count;
+}
+
+static void random_populate(int current, mesh *m, int min_connect, float prob){
+  int num_edges=0,i;
+  neighbors_grid ng;
+  neighbors_list nl;
+  populate_neighbors(current, m, &ng);
+  filter_intersections(&ng);
+  filter_edges(&ng,&nl);
+
+  {
+    edge_list *el=m->v[current]->edges;
+    while(el){
+      num_edges++;
+      el=el->next;
+    }
+  }
+
+  while(num_edges<min_connect && nl.num){
+    int choice = random() % nl.num;
+    add_edge(m->v[current], m->v[nl.vnum[choice]]);
+    num_edges++;
+    filter_intersections(&ng);
+    filter_edges(&ng,&nl);
+  }
+  
+  for(i=0;i<nl.num;i++)
+    if(random()<RAND_MAX*prob){
+      num_edges++;
+      add_edge(m->v[current], m->v[nl.vnum[i]]);
+    }
+}
+
+static void span_depth_first(int current, mesh *m){
+  neighbors_grid ng;
+  neighbors_list nl;
+
+  while(1){
+    populate_neighbors(current, m, &ng);
+    // don't reverse the order of the next two
+    filter_intersections(&ng);
+    filter_spanned_neighbors(&ng,&nl);
+    if(nl.num == 0) break;
+    
+    {
+      int choice = random() % nl.num;
+      add_edge(m->v[current], m->v[nl.vnum[choice]]);
+      
+      span_depth_first(nl.vnum[choice], m);
+    }
+  }
+}
+
+void generate_mesh_1(int order){
+  int flag=0;
+  mesh m;
+  m.width=3;
+  m.height=2;
+  vertex *vlist;
+
+  {
+    while(order--){
+      if(flag){
+	flag=0;
+	m.width+=2;
+      }else{
+	flag=1;
+	m.height+=1;
+      }
+    }
+  }
+
+  vlist=new_board(m.width * m.height);
+
+  /* a flat vector is easier to address while building the mesh */
+  {
+    int i;
+    vertex *v=vlist;
+    m.v=alloca(m.width*m.height * sizeof(*m.v));
+    for(i=0;i<m.width*m.height;i++){
+      m.v[i]=v;
+      v=v->next;
+    }
+  }
+
+  /* first walk a random spanning tree */
+  span_depth_first(0, &m);
+  
+  /* now iterate the whole mesh adding random edges */
+  {
+    int i;
+    for(i=0;i<m.width*m.height;i++)
+      random_populate(i, &m, 2, .25);
+  }
+
+  randomize_verticies(vlist);
+  arrange_verticies_circle(m.width*m.height);
+
+  //arrange_verticies_mesh(m.width,m.height);
+}
+

Added: trunk/planarity/generate.h
===================================================================
--- trunk/planarity/generate.h	2005-07-26 23:19:44 UTC (rev 9630)
+++ trunk/planarity/generate.h	2005-07-28 03:59:23 UTC (rev 9631)
@@ -0,0 +1 @@
+extern void generate_mesh_1(int order);

Added: trunk/planarity/graph.c
===================================================================
--- trunk/planarity/graph.c	2005-07-26 23:19:44 UTC (rev 9630)
+++ trunk/planarity/graph.c	2005-07-28 03:59:23 UTC (rev 9631)
@@ -0,0 +1,704 @@
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "graph.h"
+#include "gameboard.h"
+#define CHUNK 64
+
+/* mesh/board state */
+static vertex *verticies=0;
+static vertex *vertex_pool=0;
+static edge *edges=0;
+static edge *edge_pool=0;
+static edge_list *edge_list_pool=0;
+static intersection *intersection_pool=0;
+
+static int active_intersections=0;
+static int reported_intersections=0;
+
+static int num_edges=0;
+static int num_edges_active=0;
+
+
+/************************ edge list maint operations ********************/
+static void add_edge_to_list(vertex *v, edge *e){
+  edge_list *ret;
+  
+  if(edge_list_pool==0){
+    int i;
+    edge_list_pool = calloc(CHUNK,sizeof(*edge_list_pool));
+    for(i=0;i<CHUNK-1;i++) /* last addition's next points to nothing */
+      edge_list_pool[i].next=edge_list_pool+i+1;
+  }
+
+  ret=edge_list_pool;
+  edge_list_pool=ret->next;
+
+  ret->edge=e;
+  ret->next=v->edges;
+  v->edges=ret;
+}
+
+static void release_edge_list(vertex *v){
+  edge_list *el=v->edges;
+
+  if(el){
+    edge_list *end=el;
+    while(end->next)end=end->next;
+  
+    end->next = edge_list_pool;
+    edge_list_pool = el;
+    
+    v->edges=0;
+  }
+}
+
+/************************ intersection maint operations ********************/
+
+static intersection *new_intersection(){
+  intersection *ret;
+  
+  if(intersection_pool==0){
+    int i;
+    intersection_pool = calloc(CHUNK,sizeof(*intersection_pool));
+    for(i=0;i<CHUNK-1;i++) /* last addition's next points to nothing */
+      intersection_pool[i].next=intersection_pool+i+1;
+  }
+
+  ret=intersection_pool;
+  intersection_pool=ret->next;
+  return ret;
+}
+
+static void add_paired_intersection(edge *a, edge *b, double x, double y){
+  intersection *A=new_intersection();
+  intersection *B=new_intersection();
+
+  A->paired=B;
+  B->paired=A;
+
+  A->next=a->i.next;
+  if(A->next)
+    A->next->prev=A;
+  A->prev=&(a->i);
+  a->i.next=A;
+
+  B->next=b->i.next;
+  if(B->next)
+    B->next->prev=B;
+  B->prev=&(b->i);
+  b->i.next=B;
+
+  A->x=B->x=x;
+  A->y=B->y=y;
+}
+
+static void release_intersection(intersection *i){
+  memset(i,0,sizeof(*i));
+  i->next=intersection_pool;
+  intersection_pool=i;
+}
+
+static void release_intersection_list(edge *e){
+  intersection *i=e->i.next;
+  
+  while(i!=0){
+    intersection *next=i->next;
+    release_intersection(i);
+    i=next;
+  }
+  e->i.next=0;
+}
+
+static int release_paired_intersection_list(edge *e){
+  intersection *i=e->i.next;
+  int count=0;
+
+  while(i){
+    intersection *next=i->next;
+    intersection *j=i->paired;
+
+    j->prev->next=j->next;
+    if(j->next)
+      j->next->prev=j->prev;
+
+    release_intersection(i);
+    release_intersection(j);
+    i=next;
+    count++;
+  }
+  e->i.next=0;
+  return count;
+}
+
+/************************ edge maint operations ******************************/
+/* also adds to the edge list */
+edge *add_edge(vertex *A, vertex *B){
+  edge *ret;
+  
+  if(edge_pool==0){
+    int i;
+    edge_pool = calloc(CHUNK,sizeof(*edge_pool));
+    for(i=0;i<CHUNK-1;i++) /* last addition's next points to nothing */
+      edge_pool[i].next=edge_pool+i+1;
+  }
+
+  ret=edge_pool;
+  edge_pool=ret->next;
+
+  ret->A=A;
+  ret->B=B;
+  ret->active=0;
+  ret->foreground=0;
+  ret->i.next=0;
+  ret->next=edges;
+  edges=ret;
+
+  add_edge_to_list(A,ret);
+  add_edge_to_list(B,ret);
+
+  num_edges++;
+
+  return ret;
+}
+
+static void release_edges(){
+  edge *e = edges;
+
+  while(e){
+    edge *next=e->next;
+    release_intersection_list(e);
+    e->next=edge_pool;
+    edge_pool=e;
+    e=next;
+  }
+  edges=0;
+  num_edges=0;
+  num_edges_active=0;
+}
+
+static int intersects(vertex *L1, vertex *L2, vertex *M1, vertex *M2, double *xo, double *yo){
+  /* y = ax + b */
+  float La=0;
+  float Lb=0;
+  float Ma=0;
+  float Mb=0;
+
+  // but first, edges that share a vertex don't intersect by definition
+  if(L1==M1) return 0;
+  if(L1==M2) return 0;
+  if(L2==M1) return 0;
+  if(L2==M2) return 0;
+
+  if(L1->x != L2->x){
+    La = (float)(L2->y - L1->y) / (L2->x - L1->x);
+    Lb = (L1->y - L1->x * La);
+  }
+  
+  if(M1->x != M2->x){
+    Ma = (float)(M2->y - M1->y) / (M2->x - M1->x);
+    Mb = (M1->y - M1->x * Ma);
+  }
+  
+  if(L1->x == L2->x){
+    // L is vertical line
+
+    if(M1->x == M2->x){
+      // M is also a vertical line
+
+      if(L1->x == M1->x){
+	// L and M vertical on same x, overlap?
+	if(M1->y > L1->y && 
+	   M1->y > L2->y &&
+	   M2->y > L1->y &&
+	   M2->y > L2->y) return 0;
+	if(M1->y < L1->y && 
+	   M1->y < L2->y &&
+	   M2->y < L1->y &&
+	   M2->y < L2->y) return 0;
+
+	{
+	  double y1=max( min(M1->y,M2->y), min(L1->y, L2->y));
+	  double y2=min( max(M1->y,M2->y), max(L1->y, L2->y));
+
+	  *xo = M1->x;
+	  *yo = (y1+y2)*.5;
+
+	}
+
+      }else
+	// L and M vertical, different x
+	return 0;
+      
+    }else{
+      // L vertical, M not vertical
+      float y = Ma*L1->x + Mb;
+
+      if(y < L1->y && y < L2->y) return 0;
+      if(y > L1->y && y > L2->y) return 0;
+      if(y < M1->y && y < M2->y) return 0;
+      if(y > M1->y && y > M2->y) return 0;
+
+      *xo = L1->x;
+      *yo=y;
+
+    }
+  }else{
+
+    if(M1->x == M2->x){
+      // M vertical, L not vertical
+      float y = La*M1->x + Lb;
+
+      if(y < L1->y && y < L2->y) return 0;
+      if(y > L1->y && y > L2->y) return 0;
+      if(y < M1->y && y < M2->y) return 0;
+      if(y > M1->y && y > M2->y) return 0;
+
+      *xo = M1->x;
+      *yo=y;
+
+    }else{
+
+      // L and M both have non-infinite slope
+      if(La == Ma){
+	//L and M have the same slope
+	if(Mb != Lb) return 0; 
+	
+	// two segments on same line...
+	if(M1->x > L1->x && 
+	   M1->x > L2->x &&
+	   M2->x > L1->x &&
+	   M2->x > L2->x) return 0;
+	if(M1->x < L1->x && 
+	   M1->x < L2->x &&
+	   M2->x < L1->x &&
+	   M2->x < L2->x) return 0;
+	
+	{
+	  double x1=max( min(M1->x,M2->x), min(L1->x, L2->x));
+	  double x2=min( max(M1->x,M2->x), max(L1->x, L2->x));
+	  double y1=max( min(M1->y,M2->y), min(L1->y, L2->y));
+	  double y2=min( max(M1->y,M2->y), max(L1->y, L2->y));
+	  
+	  *xo = (x1+x2)*.5;
+	  *yo = (y1+y2)*.5;
+	  
+	}
+	
+	
+      }else{
+	// finally typical case: L and M have different non-infinite slopes
+	float x = (Mb-Lb) / (La - Ma);
+	
+	if(x < L1->x && x < L2->x) return 0;
+	if(x > L1->x && x > L2->x) return 0;
+	if(x < M1->x && x < M2->x) return 0;
+	if(x > M1->x && x > M2->x) return 0;
+	
+	*xo = x;
+	*yo = La*x + Lb;
+      }
+    }
+  }
+
+  return 1;
+}
+
+static void activate_edge(edge *e){
+  /* computes all intersections */
+  if(!e->active && e->A->active && e->B->active){
+    edge *test=edges;
+    while(test){
+      if(test != e && test->active){
+	double x,y;
+	if(intersects(e->A,e->B,test->A,test->B,&x,&y)){
+	  add_paired_intersection(e,test,x,y);
+	  active_intersections++;
+
+	}
+      }
+      test=test->next;
+    }
+    e->active=1;
+    num_edges_active++;
+    if(num_edges_active == num_edges)
+      reported_intersections = active_intersections;
+  }
+}
+
+static void deactivate_edge(edge *e){
+  /* releases all associated intersections */
+  if(e->active){
+    active_intersections -= 
+      release_paired_intersection_list(e);
+    num_edges_active--;
+    e->active=0;
+  }
+}
+
+int exists_edge(vertex *a, vertex *b){
+  edge_list *el=a->edges;
+  while(el){
+    if(el->edge->A == b) return 1;
+    if(el->edge->B == b) return 1;
+    el=el->next;
+  }
+  return 0;
+}
+
+/*********************** vertex maint operations *************************/
+static vertex *get_vertex(){
+  vertex *ret;
+  
+  if(vertex_pool==0){
+    int i;
+    vertex_pool = calloc(CHUNK,sizeof(*vertex_pool));
+    for(i=0;i<CHUNK-1;i++) /* last addition's next points to nothing */
+      vertex_pool[i].next=vertex_pool+i+1;
+  }
+
+  ret=vertex_pool;
+  vertex_pool=ret->next;
+
+  ret->x=0;
+  ret->y=0;
+  ret->active=0;
+  ret->selected=0;
+  ret->selected_volatile=0;
+  ret->grabbed=0;
+  ret->attached_to_grabbed=0;
+  ret->edges=0;
+  ret->next=0;
+  return ret;
+}
+
+static void release_vertex(vertex *v){
+  release_edge_list(v);
+  v->next=vertex_pool;
+  vertex_pool=v;
+}
+
+static void set_num_verticies(int num){
+  /* do it the simple way; release all, link anew */
+  vertex *v=verticies;
+
+  while(v){
+    vertex *next=v->next;
+    release_vertex(v);
+    v=next;
+  }
+  verticies=0;
+  release_edges();
+
+  while(num--){
+    v=get_vertex();
+    v->next=verticies;
+    verticies=v;
+  }
+}
+
+void activate_vertex(vertex *v){
+  edge_list *el=v->edges;
+  v->active=1;
+  while(el){
+    activate_edge(el->edge);
+    el=el->next;
+  }
+}
+
+void deactivate_vertex(vertex *v){
+  edge_list *el=v->edges;
+  while(el){
+    edge_list *next=el->next;
+    deactivate_edge(el->edge);
+    el=next;
+  }
+  v->active=0;
+}
+
+void grab_vertex(vertex *v){
+  edge_list *el=v->edges;
+  deactivate_vertex(v);
+  while(el){
+    edge_list *next=el->next;
+    edge *e=el->edge;
+    vertex *other=(e->A==v?e->B:e->A);
+    other->attached_to_grabbed=1;
+    el=next;
+  }
+  v->grabbed=1;
+}
+
+void ungrab_vertex(vertex *v){
+  edge_list *el=v->edges;
+  activate_vertex(v);
+  while(el){
+    edge_list *next=el->next;
+    edge *e=el->edge;
+    vertex *other=(e->A==v?e->B:e->A);
+    other->attached_to_grabbed=0;
+    el=next;
+  }
+  v->grabbed=0;
+}
+
+vertex *find_vertex(int x, int y){
+  vertex *v = verticies;
+  vertex *match = 0;
+
+  while(v){
+    vertex *next=v->next;
+    int xd=x-v->x;
+    int yd=y-v->y;
+    if(xd*xd + yd*yd <= V_RADIUS_SQ) match=v;
+    v=next;
+  }
+  
+  return match;
+}
+
+static void check_vertex_helper(vertex *v, int reactivate){
+  int flag=0;
+
+  if(v->x>=get_board_width()){
+    v->x=get_board_width()-1;
+    flag=1;
+  }
+  if(v->x<0){
+    v->x=0;
+    flag=1;
+  }
+  if(v->y>=get_board_height()){
+    v->y=get_board_height()-1;
+    flag=0;
+  }
+  if(v->y<0){
+    v->y=0;
+    flag=1;
+  }
+  if(flag){
+    if(v->edges){
+      deactivate_vertex(v);
+      if(reactivate)activate_vertex(v);
+    }
+  }
+}
+
+static void check_vertex(vertex *v){
+  check_vertex_helper(v,1);
+}
+
+void check_verticies(){
+  vertex *v=verticies;
+  while(v){
+    vertex *next=v->next;
+    check_vertex_helper(v,0);
+    v=next;
+  }
+
+  v=verticies;
+  while(v){
+    vertex *next=v->next;
+    activate_vertex(v);
+    v=next;
+  }
+}
+
+void move_vertex(vertex *v, int x, int y){
+  if(!v->grabbed) deactivate_vertex(v);
+  v->x=x;
+  v->y=y;
+  check_vertex_helper(v,0);
+  if(!v->grabbed) activate_vertex(v);
+}
+
+// tenative selection; must be confirmed if next call should not clear
+void select_verticies(int x1, int y1, int x2, int y2){
+  vertex *v = verticies;
+
+  if(x1>x2){
+    int temp=x1;
+    x1=x2;
+    x2=temp;
+  }
+
+  if(y1>y2){
+    int temp=y1;
+    y1=y2;
+    y2=temp;
+  }
+  x1-=V_RADIUS;
+  x2+=V_RADIUS;
+  y1-=V_RADIUS;
+  y2+=V_RADIUS;
+
+  while(v){
+    vertex *next=v->next;
+    if(v->selected_volatile)v->selected=0;
+
+    if(!v->selected){
+      if(v->x>=x1 && v->x<=x2 && v->y>=y1 && v->y<=y2){
+	v->selected=1;
+	v->selected_volatile=1;
+      }
+    }
+
+    v=next;
+  }
+}
+
+int num_selected_verticies(){
+  vertex *v = verticies;
+  int count=0;
+  while(v){
+    if(v->selected)count++;
+    v=v->next;
+  }
+  return count;
+}
+
+void deselect_verticies(){
+  vertex *v = verticies;
+  
+  while(v){
+    v->selected=0;
+    v->selected_volatile=0;
+    v=v->next;
+  }
+
+}
+
+void commit_volatile_selection(){
+  vertex *v = verticies;
+  
+  while(v){
+    v->selected_volatile=0;
+    v=v->next;
+  }
+}
+
+void move_selected_verticies(int dx, int dy){
+  vertex *v = verticies;
+  /* deactivate selected verticies */
+  while(v){
+    vertex *next=v->next;
+    if(v->selected)
+      deactivate_vertex(v);
+    v=next;
+  }
+
+  /* move selected verticies and reactivate */
+  v=verticies;
+  while(v){
+    vertex *next=v->next;
+    if(v->selected){
+      v->x+=dx;
+      v->y+=dy;
+      check_vertex(v);
+      activate_vertex(v);
+    }
+    v=next;
+  }
+
+}
+
+void scale_verticies(float amount){
+  vertex *v=verticies;
+  int x=get_board_width()/2;
+  int y=get_board_height()/2;
+
+  while(v){
+    vertex *next=v->next;
+    deactivate_vertex(v);
+    v->x=rint((v->x-x)*amount)+x;
+    v->y=rint((v->y-y)*amount)+y;
+    v=next;
+  }
+
+  v=verticies;
+  while(v){
+    vertex *next=v->next;
+    check_vertex(v);
+    activate_vertex(v);
+    v=next;
+  }
+}
+
+static vertex *split_vertex_list(vertex *v){
+  vertex *half=v;
+  vertex *prevhalf=v;
+
+  while(v){
+    v=v->next;
+    if(v)v=v->next;
+    prevhalf=half;
+    half=half->next;
+  }
+
+  prevhalf->next=0;
+  return half;
+}
+
+static vertex *randomize_helper(vertex *v){
+  vertex *w=split_vertex_list(v);
+  if(w){
+    vertex *a = randomize_helper(v);
+    vertex *b = randomize_helper(w);
+    v=0;
+    w=0;
+
+    while(a && b){
+      if(random()&1){
+	// pull off head of a
+	if(w)
+	  w=w->next=a;
+	else
+	  v=w=a;
+	a=a->next;
+      }else{
+	// pull off head of b
+	if(w)
+	  w=w->next=b;
+	else
+	  v=w=b;
+	b=b->next;
+      }
+    }
+    if(a)
+      w->next=a;
+    if(b)
+      w->next=b;
+  }
+  return v;
+}
+
+void randomize_verticies(){
+  verticies=randomize_helper(verticies);
+}
+
+int get_num_intersections(){
+  return reported_intersections;
+}
+
+vertex *get_verticies(){
+  return verticies;
+}
+
+edge *get_edges(){
+  return edges;
+}
+
+vertex *new_board(int num_v){
+  set_num_verticies(num_v);
+  return verticies;
+}
+
+void impress_location(){
+  vertex *v=verticies;
+  while(v){
+    v->orig_x=v->x;
+    v->orig_y=v->y;
+    v=v->next;
+  }
+}

Added: trunk/planarity/graph.h
===================================================================
--- trunk/planarity/graph.h	2005-07-26 23:19:44 UTC (rev 9630)
+++ trunk/planarity/graph.h	2005-07-28 03:59:23 UTC (rev 9631)
@@ -0,0 +1,71 @@
+/* graph abstraction */
+
+#define V_RADIUS_SQ (V_RADIUS*V_RADIUS)
+#define min(a,b) ((a)<(b)?(a):(b))
+#define max(a,b) ((a)>(b)?(a):(b))
+
+
+typedef struct vertex {
+  int x;
+  int y;
+  int orig_x;
+  int orig_y;
+
+  int active;
+  int selected_volatile;
+  int selected;
+  int grabbed;
+  int attached_to_grabbed;
+  struct edge_list *edges;
+  struct vertex *next;
+} vertex;
+
+typedef struct intersection {
+  struct intersection *prev;
+  struct intersection *next;
+  struct intersection *paired;
+  double x;
+  double y;
+} intersection;
+
+typedef struct edge{
+  vertex *A;
+  vertex *B;
+
+  int active;
+  int foreground;
+
+  intersection i; // correct, not a pointer
+  struct edge *next;
+} edge;
+
+typedef struct edge_list{
+  edge *edge;
+  struct edge_list *next;
+} edge_list;
+
+extern void resize_board(int x, int y);
+extern vertex *new_board(int num_v);
+extern vertex *find_vertex(int x, int y);
+extern void move_vertex(vertex *v, int x, int y);
+extern void grab_vertex(vertex *v);
+extern void ungrab_vertex(vertex *v);
+extern void activate_vertex(vertex *v);
+extern void deactivate_vertex(vertex *v);
+extern void select_verticies(int x1, int y1, int x2, int y2);
+extern void deselect_verticies();
+extern void move_selected_verticies(int dx, int dy);
+extern void scale_verticies(float amount);
+extern void randomize_verticies();
+extern edge *add_edge(vertex *A, vertex *B);
+extern int exists_edge(vertex *a, vertex *b);
+extern int get_board_width();
+extern int get_board_height();
+extern vertex *get_verticies();
+extern edge *get_edges();
+extern int num_selected_verticies();
+extern int get_num_intersections();
+extern int get_max_intersections();
+extern void check_verticies();
+extern void impress_location();
+extern void commit_volatile_selection();

Added: trunk/planarity/touch-version
===================================================================
--- trunk/planarity/touch-version	2005-07-26 23:19:44 UTC (rev 9630)
+++ trunk/planarity/touch-version	2005-07-28 03:59:23 UTC (rev 9631)
@@ -0,0 +1,21 @@
+#!/usr/bin/perl
+
+if(open F,"version.h"){
+	$line=<F>;
+	close F;
+
+	if(open F,">version.h"){
+	
+	    print F "$line";
+	    chomp($line=`date`);
+	    print F "/* DO NOT EDIT: Automated versioning hack [$line] */\n";
+	    close F;
+	    0;
+	}else{
+	    print "touch-version: Failed to write new version.h\n";
+	    1;
+	}
+}else{
+	print "touch-version: Failed to open version.h\n";
+	1;
+}


Property changes on: trunk/planarity/touch-version
___________________________________________________________________
Name: svn:executable
   + 

Added: trunk/planarity/version.h
===================================================================
--- trunk/planarity/version.h	2005-07-26 23:19:44 UTC (rev 9630)
+++ trunk/planarity/version.h	2005-07-28 03:59:23 UTC (rev 9631)
@@ -0,0 +1,2 @@
+/* DO NOT EDIT: Automated versioning hack [Thu Jul 21 05:46:15 EDT 2005] */
+/* DO NOT EDIT: Automated versioning hack [Wed Jul 27 23:28:33 EDT 2005] */



More information about the commits mailing list