#!/usr/bin/perl -w
use strict;
my ($appname,$version)=qw[Meander 0.1];

# Done:
# fix zooming (?)
# avoid scientific notation for coords
# select multiple overlapping points
# discontinuous motifs
# closing display window brokenness


# TODO: 
# load/save dialogs
# editable angles/lengths
# documentation
# better avoidance of runaway calculations
# bounding box calculation (for clipping when zoomed)
# anti-aliased display
# printing/image export
# email client


# buttons:
# 1: select, delayed drag
# 2: insta-drag, no select
# 3: safe select/deselect, no drag

use constant TRUE=>1;
use constant FALSE=>!TRUE;

use constant pi=>4*atan2(1,1);

my %mfvars; # Makefile Vars
my $typemap; # dirty hack, see below

BEGIN {
    # ExtUtils::Depends knows how to find the typemaps and headers for the
    # Gtk2 module and the ones on which it depends (Glib, basically).
    # use it to get the INC, LIBS, and TYPEMAPS information we need to give
    # to Inline::C so we can use gtk2-perl functions and such.
    use ExtUtils::Depends;
    my $dep = ExtUtils::Depends->new ('Meander', 'Glib', 'Gtk2');
    %mfvars = $dep->get_makefile_vars;

    # Inline::C's manpage claims that it takes the same value for TYPEMAPS as
    # MakeMaker, but this is an evil lie --- it takes only a filename string,
    # whereas MakeMaker takes a string or an array of strings.  We'll have to
    # consolidate the many typemap files from the depends object into one to
    # appease the fickle Inline.  This bites.
    my %typemap_info;
    use strict;
    foreach my $f (@{$mfvars{TYPEMAPS}}) {
	open IN, $f or die "can't open $f: $!\n";
	my $cur = '';
	while (<IN>) {
	    if ($cur eq '') {
		next unless /^TYPEMAP/;
		$cur = 'TYPEMAP';
	    } elsif (/^(INPUT|OUTPUT)/) {
		$cur = $1;
	    } else {
		push @{$typemap_info{$cur}}, $_;
	    }
	}
	close IN;
    }
    use File::Temp ':mktemp';
    $typemap = mktemp ("/tmp/".$0."XXXXXX");
    open OUT, ">$typemap" or die "can't write temp typemap file: $!";
    print OUT "# generated by $0 from ".join(" ", @{$mfvars{TYPEMAPS}})."\n\n";
    foreach my $section (qw(TYPEMAP INPUT OUTPUT)) {
	print OUT "$section\n";
	foreach (@{$typemap_info{$section}}) {
	    print OUT $_;
	}
    }
    close OUT;
}
#END {
#    unlink $typemap;
#}

use Inline (C => Config =>
	CCFLAGS => '-O3',
	INC => $mfvars{INC},
	LIBS => $mfvars{LIBS},
	TYPEMAPS => $typemap, #$mfvars{TYPEMAPS}, # $%@^ Inline, see above
);
use Inline C => <<'EOF';

#include <gtk2perl.h>

#define POLL_INTERVAL 500000

#define THRESH 0.5

#define W (da->allocation.width)
#define H (da->allocation.height)

GtkWidget *da;
double *motif;
int mlen;

int busy = 0, aborted = 0;
int count;
int next_poll;
int lines;
unsigned int connected;

#define BUFSIZE 1024
GdkPoint buf[BUFSIZE+1], *bufptr;

static void draw_points (void) {
    (lines?gdk_draw_lines:gdk_draw_points)
	(da->window,
	 da->style->fg_gc[GTK_WIDGET_STATE (da)],
	 buf, bufptr-buf);

    if (lines)
	*buf=*--bufptr;

    count+=bufptr-buf;

    if (count > next_poll)
    {
	next_poll = count + POLL_INTERVAL;
	while (gtk_events_pending()) gtk_main_iteration();
    }

    bufptr=buf;
    if (lines)
	bufptr++;
}

static void flush (void) {
    if (!lines)
	return;
    
    if (bufptr!=buf) {
	draw_points();
	bufptr--;
    }
}
	
static void meander (double x, double y, double dx, double dy, int iter) {
    int i;
    double *ptr = motif;

    if (aborted)
	return;

    /* only valid when all lengths<1 */
    if ((x-dx*2<0 && x+dx*2<0 && x-dy*2<0 && x+dy*2<0) ||
	(y-dy*2<0 && y+dy*2<0 && y-dx*2<0 && y+dx*2<0) ||
	(x-dx*2>W && x+dx*2>W && x-dy*2>W && x+dy*2>W) ||
	(y-dy*2>H && y+dy*2>H && y-dx*2>H && y+dx*2>H)) {
	flush();
	return;
    }

    for (i=0; i<mlen; i++) {
	double xx=*ptr++;
	double yy=*ptr++;
	
	double xn = dx*xx+dy*yy;
	double yn = dy*xx-dx*yy;

	int c = (connected*2 >> i);
	
	if (c & 7) {
	    if (!iter || xn*xn+yn*yn<THRESH || !(c & 2)) {
		bufptr->x = x; bufptr->y = y;
		if (++bufptr == buf+BUFSIZE)
		draw_points();
	    } else {
		meander (x, y, xn, yn, iter-1);
	    }
	}

	if (!(c & 2))
	    flush();


	x += xn;
	y += yn;
    }
}

/* unfortunately, adding the "void" prototype confuses Inline::C */
int abort_draw ()
{
    if (!busy)
	return FALSE;

    aborted = TRUE;

    return TRUE;
}

int draw_meander (SV *points_ref, GtkWidget *area, int xstart, int ystart, int dx, int dy, int maxiter, int l, int c) {
    AV *points;
    int len;
    double *ptr;
    int i;

    if (busy)
	return -1;

    busy = TRUE;
    
    if (!SvROK(points_ref))
	croak ("oops");

    points = (AV*)SvRV (points_ref);

    len = av_len (points)+1;
    ptr = motif = malloc (len * sizeof (double));

    if (!motif)
	croak ("out of memory");

    if (len & 1)
	croak ("oops");

    mlen = len/2;

    for (i=0; i<len; i++) {
	SV **val = av_fetch (points, i, 0);
#if 0
	if (!SvNOK (*val))
	    croak ("oops");
#endif
	*ptr++=(double)SvNV(*val);
    }

    da = area;

    bufptr = buf;
    count = 0;
    lines = l;
    connected = c;

    next_poll = POLL_INTERVAL;

    if (maxiter--)
	meander (xstart, ystart, dx, dy, maxiter);
    else {
	bufptr->x = xstart;
	bufptr->y = ystart;
	bufptr++;
    }
	
    free (motif);

    bufptr->x = xstart+dx;
    bufptr->y = ystart+dy;
    bufptr++;
    
    draw_points();

    busy = FALSE;
    aborted = FALSE;

    if (aborted)
	return -2;

    return count;
}

EOF

use Gnome2;
use Gnome2::Canvas;
use Gtk2 '-init';
use Gtk2::GladeXML;

Gnome2::Program->init ($appname, $version);

#my $glade=Gtk2::GladeXML->new("meander.glade");
my $glade=Gtk2::GladeXML->new_from_buffer(<<"EOF");
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">

<glade-interface>
<requires lib="gnome"/>
<requires lib="canvas"/>

<widget class="GtkWindow" id="dispwin">
  <property name="visible">True</property>
  <property name="title" translatable="yes">Display</property>
  <property name="type">GTK_WINDOW_TOPLEVEL</property>
  <property name="window_position">GTK_WIN_POS_NONE</property>
  <property name="modal">False</property>
  <property name="default_width">600</property>
  <property name="default_height">500</property>
  <property name="resizable">True</property>
  <property name="destroy_with_parent">False</property>
  <signal name="delete_event" handler="hide_window" last_modification_time="Sat, 21 May 2005 18:59:27 GMT"/>

  <child>
    <widget class="GtkDrawingArea" id="drawingarea">
      <property name="visible">True</property>
      <property name="events">GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
      <property name="extension_events">GDK_EXTENSION_EVENTS_ALL</property>
      <signal name="expose_event" handler="on_expose_event" last_modification_time="Thu, 26 Feb 2004 22:23:14 GMT"/>
      <signal name="button_press_event" handler="on_drawingarea_click" last_modification_time="Sun, 29 Feb 2004 17:23:18 GMT"/>
    </widget>
  </child>
</widget>

<widget class="GtkWindow" id="editwin">
  <property name="visible">True</property>
  <property name="title" translatable="yes">$appname</property>
  <property name="type">GTK_WINDOW_TOPLEVEL</property>
  <property name="window_position">GTK_WIN_POS_NONE</property>
  <property name="modal">False</property>
  <property name="default_height">430</property>
  <property name="resizable">True</property>
  <property name="destroy_with_parent">False</property>
  <signal name="destroy" handler="gtk_main_quit" last_modification_time="Thu, 26 Feb 2004 01:22:51 GMT"/>

  <child>
    <widget class="GtkVBox" id="vbox1">
      <property name="visible">True</property>
      <property name="homogeneous">False</property>
      <property name="spacing">0</property>

      <child>
	<widget class="GtkMenuBar" id="menubar1">
	  <property name="visible">True</property>

	  <child>
	    <widget class="GtkMenuItem" id="file1">
	      <property name="visible">True</property>
	      <property name="stock_item">GNOMEUIINFO_MENU_FILE_TREE</property>

	      <child>
		<widget class="GtkMenu" id="file1_menu">

		  <child>
		    <widget class="GtkImageMenuItem" id="new1">
		      <property name="visible">True</property>
		      <property name="stock_item">GNOMEUIINFO_MENU_NEW_ITEM</property>
		      <property name="label" translatable="yes">_New</property>
		      <property name="use_underline">True</property>
		      <signal name="activate" handler="on_new1_activate" last_modification_time="Thu, 26 Feb 2004 00:45:09 GMT"/>
		    </widget>
		  </child>

		  <child>
		    <widget class="GtkImageMenuItem" id="open1">
		      <property name="visible">True</property>
		      <property name="stock_item">GNOMEUIINFO_MENU_OPEN_ITEM</property>
		      <signal name="activate" handler="on_open1_activate" last_modification_time="Thu, 26 Feb 2004 00:45:09 GMT"/>
		    </widget>
		  </child>

		  <child>
		    <widget class="GtkImageMenuItem" id="save1">
		      <property name="visible">True</property>
		      <property name="stock_item">GNOMEUIINFO_MENU_SAVE_ITEM</property>
		      <signal name="activate" handler="on_save1_activate" last_modification_time="Thu, 26 Feb 2004 00:45:09 GMT"/>
		    </widget>
		  </child>

		  <child>
		    <widget class="GtkImageMenuItem" id="save_as1">
		      <property name="visible">True</property>
		      <property name="stock_item">GNOMEUIINFO_MENU_SAVE_AS_ITEM</property>
		      <signal name="activate" handler="on_save_as1_activate" last_modification_time="Thu, 26 Feb 2004 00:45:09 GMT"/>
		    </widget>
		  </child>

		  <child>
		    <widget class="GtkMenuItem" id="separator3">
		      <property name="visible">True</property>
		    </widget>
		  </child>

		  <child>
		    <widget class="GtkImageMenuItem" id="quit1">
		      <property name="visible">True</property>
		      <property name="stock_item">GNOMEUIINFO_MENU_EXIT_ITEM</property>
		      <signal name="activate" handler="gtk_main_quit" last_modification_time="Mon, 01 Mar 2004 03:02:14 GMT"/>
		    </widget>
		  </child>
		</widget>
	      </child>
	    </widget>
	  </child>

	  <child>
	    <widget class="GtkMenuItem" id="edit1">
	      <property name="visible">True</property>
	      <property name="stock_item">GNOMEUIINFO_MENU_EDIT_TREE</property>

	      <child>
		<widget class="GtkMenu" id="edit1_menu">

		  <child>
		    <widget class="GtkMenuItem" id="point_attachment1">
		      <property name="visible">True</property>
		      <property name="label" translatable="yes">_Point attachment</property>
		      <property name="use_underline">True</property>

		      <child>
			<widget class="GtkMenu" id="point_attachment1_menu">

			  <child>
			    <widget class="GtkRadioMenuItem" id="next_line1">
			      <property name="visible">True</property>
			      <property name="label" translatable="yes">_Next line</property>
			      <property name="use_underline">True</property>
			      <property name="active">True</property>
			      <signal name="activate" handler="on_next_line1_activate" last_modification_time="Wed, 11 May 2005 15:35:28 GMT"/>
			      <accelerator key="N" modifiers="0" signal="activate"/>
			    </widget>
			  </child>

			  <child>
			    <widget class="GtkRadioMenuItem" id="previous_line1">
			      <property name="visible">True</property>
			      <property name="label" translatable="yes">_Previous line</property>
			      <property name="use_underline">True</property>
			      <property name="active">False</property>
			      <property name="group">next_line1</property>
			      <signal name="activate" handler="on_previous_line1_activate" last_modification_time="Wed, 11 May 2005 15:35:28 GMT"/>
			      <accelerator key="P" modifiers="0" signal="activate"/>
			    </widget>
			  </child>
			</widget>
		      </child>
		    </widget>
		  </child>

		  <child>
		    <widget class="GtkMenuItem" id="fix1">
		      <property name="visible">True</property>
		      <property name="label" translatable="yes">Fix</property>
		      <property name="use_underline">True</property>

		      <child>
			<widget class="GtkMenu" id="fix1_menu">

			  <child>
			    <widget class="GtkRadioMenuItem" id="none1">
			      <property name="visible">True</property>
			      <property name="label" translatable="yes">None</property>
			      <property name="use_underline">True</property>
			      <property name="active">True</property>
			      <signal name="activate" handler="on_none1_activate" last_modification_time="Thu, 12 May 2005 15:08:27 GMT"/>
			    </widget>
			  </child>

			  <child>
			    <widget class="GtkRadioMenuItem" id="x_position1">
			      <property name="visible">True</property>
			      <property name="label" translatable="yes">X position</property>
			      <property name="use_underline">True</property>
			      <property name="active">False</property>
			      <property name="group">none1</property>
			      <signal name="activate" handler="on_x_position1_activate" last_modification_time="Thu, 12 May 2005 15:04:02 GMT"/>
			    </widget>
			  </child>

			  <child>
			    <widget class="GtkRadioMenuItem" id="y_position1">
			      <property name="visible">True</property>
			      <property name="label" translatable="yes">Y position</property>
			      <property name="use_underline">True</property>
			      <property name="active">False</property>
			      <property name="group">none1</property>
			      <signal name="activate" handler="on_y_position1_activate" last_modification_time="Thu, 12 May 2005 15:04:02 GMT"/>
			    </widget>
			  </child>

			  <child>
			    <widget class="GtkRadioMenuItem" id="angle1">
			      <property name="visible">True</property>
			      <property name="label" translatable="yes">Angle</property>
			      <property name="use_underline">True</property>
			      <property name="active">False</property>
			      <property name="group">none1</property>
			      <signal name="activate" handler="on_angle1_activate" last_modification_time="Thu, 12 May 2005 15:04:02 GMT"/>
			    </widget>
			  </child>

			  <child>
			    <widget class="GtkRadioMenuItem" id="length1">
			      <property name="visible">True</property>
			      <property name="label" translatable="yes">Length</property>
			      <property name="use_underline">True</property>
			      <property name="active">False</property>
			      <property name="group">none1</property>
			      <signal name="activate" handler="on_length1_activate" last_modification_time="Thu, 12 May 2005 15:04:02 GMT"/>
			    </widget>
			  </child>
			</widget>
		      </child>
		    </widget>
		  </child>

		  <child>
		    <widget class="GtkMenuItem" id="flip_motif1">
		      <property name="visible">True</property>
		      <property name="label" translatable="yes">_Flip motif</property>
		      <property name="use_underline">True</property>
		      <signal name="activate" handler="on_flip_motif1_activate" last_modification_time="Wed, 11 May 2005 15:35:28 GMT"/>
		    </widget>
		  </child>
		</widget>
	      </child>
	    </widget>
	  </child>

	  <child>
	    <widget class="GtkMenuItem" id="view1">
	      <property name="visible">True</property>
	      <property name="stock_item">GNOMEUIINFO_MENU_VIEW_TREE</property>

	      <child>
		<widget class="GtkMenu" id="view1_menu">

		  <child>
		    <widget class="GtkMenuItem" id="show_display_window">
		      <property name="visible">True</property>
		      <property name="label" translatable="yes">_Show display window</property>
		      <property name="use_underline">True</property>
		      <signal name="activate" handler="on_show_display_window_activate" last_modification_time="Thu, 26 Feb 2004 02:49:31 GMT"/>
		    </widget>
		  </child>
		</widget>
	      </child>
	    </widget>
	  </child>

	  <child>
	    <widget class="GtkMenuItem" id="presets">
	      <property name="visible">True</property>
	      <property name="label" translatable="yes">_Presets</property>
	      <property name="use_underline">True</property>
	    </widget>
	  </child>

	  <child>
	    <widget class="GtkMenuItem" id="help1">
	      <property name="visible">True</property>
	      <property name="stock_item">GNOMEUIINFO_MENU_HELP_TREE</property>

	      <child>
		<widget class="GtkMenu" id="help1_menu">

		  <child>
		    <widget class="GtkImageMenuItem" id="about1">
		      <property name="visible">True</property>
		      <property name="stock_item">GNOMEUIINFO_MENU_ABOUT_ITEM</property>
		      <signal name="activate" handler="on_about1_activate" last_modification_time="Thu, 26 Feb 2004 00:45:09 GMT"/>
		    </widget>
		  </child>
		</widget>
	      </child>
	    </widget>
	  </child>
	</widget>
	<packing>
	  <property name="padding">0</property>
	  <property name="expand">False</property>
	  <property name="fill">False</property>
	</packing>
      </child>

      <child>
	<widget class="GtkFrame" id="frame2">
	  <property name="visible">True</property>
	  <property name="label_xalign">0</property>
	  <property name="label_yalign">0.5</property>
	  <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>

	  <child>
	    <widget class="GtkHBox" id="hbox1">
	      <property name="border_width">2</property>
	      <property name="visible">True</property>
	      <property name="homogeneous">False</property>
	      <property name="spacing">2</property>

	      <child>
		<widget class="GtkLabel" id="">
		  <property name="visible">True</property>
		  <property name="label" translatable="yes">Number of _sides</property>
		  <property name="use_underline">True</property>
		  <property name="use_markup">False</property>
		  <property name="justify">GTK_JUSTIFY_LEFT</property>
		  <property name="wrap">False</property>
		  <property name="selectable">False</property>
		  <property name="xalign">0.5</property>
		  <property name="yalign">0.5</property>
		  <property name="xpad">0</property>
		  <property name="ypad">0</property>
		  <property name="mnemonic_widget">sides</property>
		</widget>
		<packing>
		  <property name="padding">0</property>
		  <property name="expand">False</property>
		  <property name="fill">False</property>
		</packing>
	      </child>

	      <child>
		<widget class="GtkCombo" id="combo1">
		  <property name="visible">True</property>
		  <property name="value_in_list">False</property>
		  <property name="allow_empty">True</property>
		  <property name="case_sensitive">False</property>
		  <property name="enable_arrow_keys">True</property>
		  <property name="enable_arrows_always">False</property>

		  <child internal-child="entry">
		    <widget class="GtkEntry" id="sides">
		      <property name="visible">True</property>
		      <property name="can_focus">True</property>
		      <property name="editable">True</property>
		      <property name="visibility">True</property>
		      <property name="max_length">0</property>
		      <property name="text" translatable="yes"></property>
		      <property name="has_frame">True</property>
		      <property name="invisible_char" translatable="yes">*</property>
		      <property name="activates_default">False</property>
		      <signal name="changed" handler="on_sides_changed" last_modification_time="Fri, 27 Feb 2004 00:56:18 GMT"/>
		    </widget>
		  </child>

		  <child internal-child="list">
		    <widget class="GtkList" id="combo-list1">
		      <property name="visible">True</property>
		      <property name="selection_mode">GTK_SELECTION_BROWSE</property>

		      <child>
			<widget class="GtkListItem" id="listitem204">
			  <property name="visible">True</property>
			  <property name="can_focus">True</property>
			  <property name="label" translatable="yes">1 (line)</property>
			</widget>
		      </child>

		      <child>
			<widget class="GtkListItem" id="listitem205">
			  <property name="visible">True</property>
			  <property name="can_focus">True</property>
			  <property name="label" translatable="yes">2 (double-sided line)</property>
			</widget>
		      </child>

		      <child>
			<widget class="GtkListItem" id="listitem206">
			  <property name="visible">True</property>
			  <property name="can_focus">True</property>
			  <property name="label" translatable="yes">3 (triangle)</property>
			</widget>
		      </child>

		      <child>
			<widget class="GtkListItem" id="listitem207">
			  <property name="visible">True</property>
			  <property name="can_focus">True</property>
			  <property name="label" translatable="yes">4 (square)</property>
			</widget>
		      </child>

		      <child>
			<widget class="GtkListItem" id="listitem208">
			  <property name="visible">True</property>
			  <property name="can_focus">True</property>
			  <property name="label" translatable="yes">5 (pentagon)</property>
			</widget>
		      </child>

		      <child>
			<widget class="GtkListItem" id="listitem209">
			  <property name="visible">True</property>
			  <property name="can_focus">True</property>
			  <property name="label" translatable="yes">6 (hexagon)</property>
			</widget>
		      </child>
		    </widget>
		  </child>
		</widget>
		<packing>
		  <property name="padding">0</property>
		  <property name="expand">False</property>
		  <property name="fill">False</property>
		</packing>
	      </child>
	    </widget>
	  </child>

	  <child>
	    <widget class="GtkLabel" id="baseline-label">
	      <property name="visible">True</property>
	      <property name="label" translatable="yes">Baseline</property>
	      <property name="use_underline">False</property>
	      <property name="use_markup">False</property>
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
	      <property name="wrap">False</property>
	      <property name="selectable">False</property>
	      <property name="xalign">0.5</property>
	      <property name="yalign">0.5</property>
	      <property name="xpad">0</property>
	      <property name="ypad">0</property>
	    </widget>
	    <packing>
	      <property name="type">label_item</property>
	    </packing>
	  </child>
	</widget>
	<packing>
	  <property name="padding">2</property>
	  <property name="expand">False</property>
	  <property name="fill">True</property>
	</packing>
      </child>

      <child>
	<widget class="GtkFrame" id="frame3">
	  <property name="visible">True</property>
	  <property name="label_xalign">0</property>
	  <property name="label_yalign">0.5</property>
	  <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>

	  <child>
	    <widget class="GtkVBox" id="vbox2">
	      <property name="visible">True</property>
	      <property name="homogeneous">False</property>
	      <property name="spacing">0</property>

	      <child>
		<widget class="GtkScrolledWindow" id="scrolledwindow2">
		  <property name="visible">True</property>
		  <property name="can_focus">True</property>
		  <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
		  <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
		  <property name="shadow_type">GTK_SHADOW_NONE</property>
		  <property name="window_placement">GTK_CORNER_TOP_LEFT</property>

		  <child>
		    <widget class="GnomeCanvas" id="editcanvas">
		      <property name="height_request">120</property>
		      <property name="visible">True</property>
		      <property name="can_focus">True</property>
		      <property name="anti_aliased">True</property>
		      <property name="scroll_x1">0</property>
		      <property name="scroll_y1">0.6</property>
		      <property name="scroll_x2">1</property>
		      <property name="scroll_y2">-0.6</property>
		      <property name="pixels_per_unit">150</property>
		      <signal name="button_press_event" handler="on_select_point" after="yes" last_modification_time="Thu, 04 Mar 2004 00:56:52 GMT"/>
		      <signal name="button_release_event" handler="on_select_point" after="yes" last_modification_time="Thu, 04 Mar 2004 00:57:05 GMT"/>
		      <signal name="motion_notify_event" handler="on_select_point" after="yes" last_modification_time="Thu, 12 May 2005 14:33:17 GMT"/>
		    </widget>
		  </child>
		</widget>
		<packing>
		  <property name="padding">0</property>
		  <property name="expand">True</property>
		  <property name="fill">True</property>
		</packing>
	      </child>

	      <child>
		<widget class="GtkHBox" id="hbox2">
		  <property name="visible">True</property>
		  <property name="homogeneous">False</property>
		  <property name="spacing">0</property>

		  <child>
		    <widget class="GtkFrame" id="frame4">
		      <property name="visible">True</property>
		      <property name="label_xalign">0</property>
		      <property name="label_yalign">0.5</property>
		      <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>

		      <child>
			<widget class="GtkVBox" id="vbox4">
			  <property name="visible">True</property>
			  <property name="homogeneous">False</property>
			  <property name="spacing">0</property>

			  <child>
			    <widget class="GtkTable" id="table1">
			      <property name="visible">True</property>
			      <property name="n_rows">2</property>
			      <property name="n_columns">4</property>
			      <property name="homogeneous">False</property>
			      <property name="row_spacing">0</property>
			      <property name="column_spacing">2</property>

			      <child>
				<widget class="GtkEntry" id="x">
				  <property name="visible">True</property>
				  <property name="can_focus">True</property>
				  <property name="editable">True</property>
				  <property name="visibility">True</property>
				  <property name="max_length">0</property>
				  <property name="text" translatable="yes"></property>
				  <property name="has_frame">True</property>
				  <property name="invisible_char" translatable="yes">*</property>
				  <property name="activates_default">False</property>
				  <property name="width_chars">5</property>
				  <signal name="changed" handler="on_xy_changed" last_modification_time="Thu, 26 Feb 2004 03:50:57 GMT"/>
				</widget>
				<packing>
				  <property name="left_attach">1</property>
				  <property name="right_attach">2</property>
				  <property name="top_attach">0</property>
				  <property name="bottom_attach">1</property>
				  <property name="y_options"></property>
				</packing>
			      </child>

			      <child>
				<widget class="GtkEntry" id="y">
				  <property name="visible">True</property>
				  <property name="can_focus">True</property>
				  <property name="editable">True</property>
				  <property name="visibility">True</property>
				  <property name="max_length">0</property>
				  <property name="text" translatable="yes"></property>
				  <property name="has_frame">True</property>
				  <property name="invisible_char" translatable="yes">*</property>
				  <property name="activates_default">False</property>
				  <property name="width_chars">5</property>
				  <signal name="changed" handler="on_xy_changed" last_modification_time="Thu, 26 Feb 2004 03:50:42 GMT"/>
				</widget>
				<packing>
				  <property name="left_attach">1</property>
				  <property name="right_attach">2</property>
				  <property name="top_attach">1</property>
				  <property name="bottom_attach">2</property>
				  <property name="y_options"></property>
				</packing>
			      </child>

			      <child>
				<widget class="GtkEntry" id="angle">
				  <property name="visible">True</property>
				  <property name="can_focus">True</property>
				  <property name="editable">True</property>
				  <property name="visibility">True</property>
				  <property name="max_length">0</property>
				  <property name="text" translatable="yes"></property>
				  <property name="has_frame">True</property>
				  <property name="invisible_char" translatable="yes">*</property>
				  <property name="activates_default">False</property>
				  <property name="width_chars">4</property>
				</widget>
				<packing>
				  <property name="left_attach">3</property>
				  <property name="right_attach">4</property>
				  <property name="top_attach">0</property>
				  <property name="bottom_attach">1</property>
				  <property name="y_options"></property>
				</packing>
			      </child>

			      <child>
				<widget class="GtkEntry" id="length">
				  <property name="visible">True</property>
				  <property name="can_focus">True</property>
				  <property name="editable">True</property>
				  <property name="visibility">True</property>
				  <property name="max_length">0</property>
				  <property name="text" translatable="yes"></property>
				  <property name="has_frame">True</property>
				  <property name="invisible_char" translatable="yes">*</property>
				  <property name="activates_default">False</property>
				  <property name="width_chars">4</property>
				</widget>
				<packing>
				  <property name="left_attach">3</property>
				  <property name="right_attach">4</property>
				  <property name="top_attach">1</property>
				  <property name="bottom_attach">2</property>
				  <property name="y_options"></property>
				</packing>
			      </child>

			      <child>
				<widget class="GtkToggleButton" id="lengthbutton">
				  <property name="visible">True</property>
				  <property name="can_focus">True</property>
				  <property name="label" translatable="yes">_Length</property>
				  <property name="use_underline">True</property>
				  <property name="relief">GTK_RELIEF_NORMAL</property>
				  <property name="active">False</property>
				  <property name="inconsistent">False</property>
				  <signal name="toggled" handler="on_fixbutton_activate" last_modification_time="Tue, 17 May 2005 22:17:25 GMT"/>
				</widget>
				<packing>
				  <property name="left_attach">2</property>
				  <property name="right_attach">3</property>
				  <property name="top_attach">1</property>
				  <property name="bottom_attach">2</property>
				  <property name="x_options">shrink|fill</property>
				  <property name="y_options"></property>
				</packing>
			      </child>

			      <child>
				<widget class="GtkToggleButton" id="anglebutton">
				  <property name="visible">True</property>
				  <property name="can_focus">True</property>
				  <property name="label" translatable="yes">_Angle</property>
				  <property name="use_underline">True</property>
				  <property name="relief">GTK_RELIEF_NORMAL</property>
				  <property name="active">False</property>
				  <property name="inconsistent">False</property>
				  <signal name="toggled" handler="on_fixbutton_activate" last_modification_time="Tue, 17 May 2005 22:17:19 GMT"/>
				</widget>
				<packing>
				  <property name="left_attach">2</property>
				  <property name="right_attach">3</property>
				  <property name="top_attach">0</property>
				  <property name="bottom_attach">1</property>
				  <property name="x_options">shrink|fill</property>
				  <property name="y_options"></property>
				</packing>
			      </child>

			      <child>
				<widget class="GtkToggleButton" id="ybutton">
				  <property name="visible">True</property>
				  <property name="can_focus">True</property>
				  <property name="label" translatable="yes">_Y</property>
				  <property name="use_underline">True</property>
				  <property name="relief">GTK_RELIEF_NORMAL</property>
				  <property name="active">False</property>
				  <property name="inconsistent">False</property>
				  <signal name="toggled" handler="on_fixbutton_activate" last_modification_time="Tue, 17 May 2005 22:18:06 GMT"/>
				</widget>
				<packing>
				  <property name="left_attach">0</property>
				  <property name="right_attach">1</property>
				  <property name="top_attach">1</property>
				  <property name="bottom_attach">2</property>
				  <property name="x_options">shrink</property>
				  <property name="y_options"></property>
				</packing>
			      </child>

			      <child>
				<widget class="GtkToggleButton" id="xbutton">
				  <property name="visible">True</property>
				  <property name="can_focus">True</property>
				  <property name="label" translatable="yes">_X</property>
				  <property name="use_underline">True</property>
				  <property name="relief">GTK_RELIEF_NORMAL</property>
				  <property name="active">False</property>
				  <property name="inconsistent">False</property>
				  <signal name="toggled" handler="on_fixbutton_activate" last_modification_time="Tue, 17 May 2005 22:16:46 GMT"/>
				</widget>
				<packing>
				  <property name="left_attach">0</property>
				  <property name="right_attach">1</property>
				  <property name="top_attach">0</property>
				  <property name="bottom_attach">1</property>
				  <property name="x_options">shrink|fill</property>
				  <property name="y_options"></property>
				</packing>
			      </child>
			    </widget>
			    <packing>
			      <property name="padding">0</property>
			      <property name="expand">True</property>
			      <property name="fill">True</property>
			    </packing>
			  </child>

			  <child>
			    <widget class="GtkCheckButton" id="connected">
			      <property name="visible">True</property>
			      <property name="can_focus">True</property>
			      <property name="label" translatable="yes">_Connected</property>
			      <property name="use_underline">True</property>
			      <property name="relief">GTK_RELIEF_NORMAL</property>
			      <property name="active">False</property>
			      <property name="inconsistent">False</property>
			      <property name="draw_indicator">True</property>
			      <signal name="toggled" handler="on_connected_toggled" last_modification_time="Wed, 11 May 2005 17:20:20 GMT"/>
			    </widget>
			    <packing>
			      <property name="padding">0</property>
			      <property name="expand">False</property>
			      <property name="fill">False</property>
			    </packing>
			  </child>

			  <child>
			    <widget class="GtkHBox" id="hbox5">
			      <property name="visible">True</property>
			      <property name="homogeneous">False</property>
			      <property name="spacing">0</property>

			      <child>
				<widget class="GtkLabel" id="label14">
				  <property name="visible">True</property>
				  <property name="label" translatable="yes">Point</property>
				  <property name="use_underline">False</property>
				  <property name="use_markup">False</property>
				  <property name="justify">GTK_JUSTIFY_LEFT</property>
				  <property name="wrap">False</property>
				  <property name="selectable">False</property>
				  <property name="xalign">0.5</property>
				  <property name="yalign">0.5</property>
				  <property name="xpad">0</property>
				  <property name="ypad">0</property>
				</widget>
				<packing>
				  <property name="padding">0</property>
				  <property name="expand">False</property>
				  <property name="fill">False</property>
				</packing>
			      </child>

			      <child>
				<widget class="GtkSpinButton" id="point">
				  <property name="visible">True</property>
				  <property name="can_focus">True</property>
				  <property name="climb_rate">1</property>
				  <property name="digits">0</property>
				  <property name="numeric">True</property>
				  <property name="update_policy">GTK_UPDATE_ALWAYS</property>
				  <property name="snap_to_ticks">False</property>
				  <property name="wrap">False</property>
				  <property name="adjustment">1 1 100 1 10 10</property>
				  <signal name="changed" handler="on_point_changed" last_modification_time="Wed, 11 May 2005 16:21:35 GMT"/>
				</widget>
				<packing>
				  <property name="padding">0</property>
				  <property name="expand">True</property>
				  <property name="fill">True</property>
				</packing>
			      </child>

			      <child>
				<widget class="GtkHButtonBox" id="hbuttonbox1">
				  <property name="visible">True</property>
				  <property name="layout_style">GTK_BUTTONBOX_SPREAD</property>
				  <property name="spacing">0</property>

				  <child>
				    <widget class="GtkButton" id="insert">
				      <property name="visible">True</property>
				      <property name="can_default">True</property>
				      <property name="can_focus">True</property>
				      <property name="label" translatable="yes">_Insert</property>
				      <property name="use_underline">True</property>
				      <property name="relief">GTK_RELIEF_NORMAL</property>
				      <signal name="clicked" handler="on_insert_clicked" last_modification_time="Thu, 26 Feb 2004 03:55:38 GMT"/>
				      <accelerator key="i" modifiers="0" signal="clicked"/>
				    </widget>
				  </child>

				  <child>
				    <widget class="GtkButton" id="delete">
				      <property name="visible">True</property>
				      <property name="can_default">True</property>
				      <property name="can_focus">True</property>
				      <property name="label" translatable="yes">_Delete</property>
				      <property name="use_underline">True</property>
				      <property name="relief">GTK_RELIEF_NORMAL</property>
				      <signal name="clicked" handler="on_delete_clicked" last_modification_time="Thu, 26 Feb 2004 03:55:44 GMT"/>
				      <accelerator key="d" modifiers="0" signal="clicked"/>
				      <accelerator key="Delete" modifiers="0" signal="clicked"/>
				    </widget>
				  </child>
				</widget>
				<packing>
				  <property name="padding">0</property>
				  <property name="expand">True</property>
				  <property name="fill">True</property>
				</packing>
			      </child>
			    </widget>
			    <packing>
			      <property name="padding">0</property>
			      <property name="expand">True</property>
			      <property name="fill">True</property>
			    </packing>
			  </child>
			</widget>
		      </child>

		      <child>
			<widget class="GtkLabel" id="label10">
			  <property name="visible">True</property>
			  <property name="label" translatable="yes">Current point</property>
			  <property name="use_underline">False</property>
			  <property name="use_markup">False</property>
			  <property name="justify">GTK_JUSTIFY_LEFT</property>
			  <property name="wrap">False</property>
			  <property name="selectable">False</property>
			  <property name="xalign">0.5</property>
			  <property name="yalign">0.5</property>
			  <property name="xpad">0</property>
			  <property name="ypad">0</property>
			</widget>
			<packing>
			  <property name="type">label_item</property>
			</packing>
		      </child>
		    </widget>
		    <packing>
		      <property name="padding">2</property>
		      <property name="expand">True</property>
		      <property name="fill">True</property>
		    </packing>
		  </child>
		</widget>
		<packing>
		  <property name="padding">0</property>
		  <property name="expand">False</property>
		  <property name="fill">False</property>
		</packing>
	      </child>
	    </widget>
	  </child>

	  <child>
	    <widget class="GtkLabel" id="motif-label">
	      <property name="visible">True</property>
	      <property name="label" translatable="yes">Motif</property>
	      <property name="use_underline">False</property>
	      <property name="use_markup">False</property>
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
	      <property name="wrap">False</property>
	      <property name="selectable">False</property>
	      <property name="xalign">0.5</property>
	      <property name="yalign">0.5</property>
	      <property name="xpad">0</property>
	      <property name="ypad">0</property>
	    </widget>
	    <packing>
	      <property name="type">label_item</property>
	    </packing>
	  </child>
	</widget>
	<packing>
	  <property name="padding">2</property>
	  <property name="expand">True</property>
	  <property name="fill">True</property>
	</packing>
      </child>

      <child>
	<widget class="GtkHBox" id="hbox3">
	  <property name="visible">True</property>
	  <property name="homogeneous">False</property>
	  <property name="spacing">2</property>

	  <child>
	    <widget class="GtkLabel" id="label9">
	      <property name="visible">True</property>
	      <property name="label" translatable="yes">_Iterations</property>
	      <property name="use_underline">True</property>
	      <property name="use_markup">False</property>
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
	      <property name="wrap">False</property>
	      <property name="selectable">False</property>
	      <property name="xalign">0.5</property>
	      <property name="yalign">0.5</property>
	      <property name="xpad">0</property>
	      <property name="ypad">0</property>
	      <property name="mnemonic_widget">iterations</property>
	    </widget>
	    <packing>
	      <property name="padding">0</property>
	      <property name="expand">False</property>
	      <property name="fill">False</property>
	    </packing>
	  </child>

	  <child>
	    <widget class="GtkSpinButton" id="iterations">
	      <property name="visible">True</property>
	      <property name="can_focus">True</property>
	      <property name="climb_rate">1</property>
	      <property name="digits">0</property>
	      <property name="numeric">False</property>
	      <property name="update_policy">GTK_UPDATE_ALWAYS</property>
	      <property name="snap_to_ticks">False</property>
	      <property name="wrap">False</property>
	      <property name="adjustment">6 0 1000 1 10 10</property>
	      <signal name="changed" handler="on_iterations_changed" last_modification_time="Thu, 26 Feb 2004 01:38:01 GMT"/>
	    </widget>
	    <packing>
	      <property name="padding">0</property>
	      <property name="expand">False</property>
	      <property name="fill">False</property>
	    </packing>
	  </child>

	  <child>
	    <widget class="GtkCheckButton" id="autoiter">
	      <property name="visible">True</property>
	      <property name="can_focus">True</property>
	      <property name="label" translatable="yes">A_uto iterations</property>
	      <property name="use_underline">True</property>
	      <property name="relief">GTK_RELIEF_NORMAL</property>
	      <property name="active">False</property>
	      <property name="inconsistent">False</property>
	      <property name="draw_indicator">True</property>
	      <signal name="toggled" handler="on_autoiter_toggled" last_modification_time="Sun, 29 Feb 2004 02:36:08 GMT"/>
	    </widget>
	    <packing>
	      <property name="padding">0</property>
	      <property name="expand">True</property>
	      <property name="fill">False</property>
	    </packing>
	  </child>
	</widget>
	<packing>
	  <property name="padding">0</property>
	  <property name="expand">False</property>
	  <property name="fill">True</property>
	</packing>
      </child>

      <child>
	<widget class="GtkStatusbar" id="statusbar">
	  <property name="visible">True</property>
	  <property name="has_resize_grip">True</property>
	</widget>
	<packing>
	  <property name="padding">0</property>
	  <property name="expand">False</property>
	  <property name="fill">False</property>
	</packing>
      </child>
    </widget>
  </child>
</widget>

<widget class="GtkFileSelection" id="fileselection">
  <property name="border_width">10</property>
  <property name="title" translatable="yes">Select File</property>
  <property name="type">GTK_WINDOW_TOPLEVEL</property>
  <property name="window_position">GTK_WIN_POS_NONE</property>
  <property name="modal">False</property>
  <property name="resizable">True</property>
  <property name="destroy_with_parent">False</property>
  <property name="show_fileops">False</property>
  <signal name="response" handler="on_fileselection_response" last_modification_time="Mon, 01 Mar 2004 02:50:30 GMT"/>

  <child internal-child="cancel_button">
    <widget class="GtkButton" id="cancel_button2">
      <property name="visible">True</property>
      <property name="can_default">True</property>
      <property name="can_focus">True</property>
      <property name="relief">GTK_RELIEF_NORMAL</property>
    </widget>
  </child>

  <child internal-child="ok_button">
    <widget class="GtkButton" id="ok_button2">
      <property name="visible">True</property>
      <property name="can_default">True</property>
      <property name="can_focus">True</property>
      <property name="relief">GTK_RELIEF_NORMAL</property>
    </widget>
  </child>
</widget>

<widget class="GtkDialog" id="about">
  <property name="title" translatable="yes">About $appname</property>
  <property name="type">GTK_WINDOW_TOPLEVEL</property>
  <property name="window_position">GTK_WIN_POS_NONE</property>
  <property name="modal">False</property>
  <property name="resizable">True</property>
  <property name="destroy_with_parent">False</property>
  <property name="has_separator">False</property>
  <signal name="response" handler="hide_window" last_modification_time="Sat, 21 May 2005 19:00:15 GMT"/>
  <signal name="delete_event" handler="hide_window" last_modification_time="Sat, 21 May 2005 19:00:03 GMT"/>

  <child internal-child="vbox">
    <widget class="GtkVBox" id="dialog-vbox1">
      <property name="visible">True</property>
      <property name="homogeneous">False</property>
      <property name="spacing">0</property>

      <child internal-child="action_area">
	<widget class="GtkHButtonBox" id="dialog-action_area1">
	  <property name="visible">True</property>
	  <property name="layout_style">GTK_BUTTONBOX_END</property>

	  <child>
	    <widget class="GtkButton" id="closebutton1">
	      <property name="visible">True</property>
	      <property name="can_default">True</property>
	      <property name="can_focus">True</property>
	      <property name="has_focus">True</property>
	      <property name="label">gtk-close</property>
	      <property name="use_stock">True</property>
	      <property name="relief">GTK_RELIEF_NORMAL</property>
	      <property name="response_id">-7</property>
	    </widget>
	  </child>
	</widget>
	<packing>
	  <property name="padding">0</property>
	  <property name="expand">False</property>
	  <property name="fill">True</property>
	  <property name="pack_type">GTK_PACK_END</property>
	</packing>
      </child>

      <child>
	<widget class="GtkVBox" id="vbox5">
	  <property name="visible">True</property>
	  <property name="homogeneous">False</property>
	  <property name="spacing">0</property>

	  <child>
	    <widget class="GtkDrawingArea" id="drawingarea2">
	      <property name="visible">True</property>
	    </widget>
	    <packing>
	      <property name="padding">0</property>
	      <property name="expand">False</property>
	      <property name="fill">False</property>
	    </packing>
	  </child>

	  <child>
	    <widget class="GtkHBox" id="hbox4">
	      <property name="visible">True</property>
	      <property name="homogeneous">False</property>
	      <property name="spacing">0</property>

	      <child>
		<widget class="GtkDrawingArea" id="aboutlogo">
		  <property name="width_request">200</property>
		  <property name="height_request">80</property>
		  <property name="visible">True</property>
		  <signal name="expose_event" handler="on_aboutlogo_expose_event" last_modification_time="Mon, 01 Mar 2004 18:02:37 GMT"/>
		</widget>
		<packing>
		  <property name="padding">0</property>
		  <property name="expand">True</property>
		  <property name="fill">False</property>
		</packing>
	      </child>
	    </widget>
	    <packing>
	      <property name="padding">0</property>
	      <property name="expand">False</property>
	      <property name="fill">False</property>
	    </packing>
	  </child>

	  <child>
	    <widget class="GtkLabel" id="">
	      <property name="visible">True</property>
	      <property name="label" translatable="yes">&lt;span size=&quot;xx-large&quot;&gt;&lt;b&gt;$appname $version&lt;/b&gt;&lt;/span&gt;</property>
	      <property name="use_underline">False</property>
	      <property name="use_markup">True</property>
	      <property name="justify">GTK_JUSTIFY_CENTER</property>
	      <property name="wrap">False</property>
	      <property name="selectable">False</property>
	      <property name="xalign">0.5</property>
	      <property name="yalign">0.5</property>
	      <property name="xpad">0</property>
	      <property name="ypad">0</property>
	    </widget>
	    <packing>
	      <property name="padding">5</property>
	      <property name="expand">False</property>
	      <property name="fill">False</property>
	    </packing>
	  </child>

	  <child>
	    <widget class="GtkLabel" id="label12">
	      <property name="visible">True</property>
	      <property name="label" translatable="yes">An interactive fractal plotter.</property>
	      <property name="use_underline">False</property>
	      <property name="use_markup">True</property>
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
	      <property name="wrap">False</property>
	      <property name="selectable">False</property>
	      <property name="xalign">0.5</property>
	      <property name="yalign">0.5</property>
	      <property name="xpad">0</property>
	      <property name="ypad">0</property>
	    </widget>
	    <packing>
	      <property name="padding">3</property>
	      <property name="expand">False</property>
	      <property name="fill">True</property>
	    </packing>
	  </child>

	  <child>
	    <widget class="GtkLabel" id="label13">
	      <property name="visible">True</property>
	      <property name="label" translatable="yes">&lt;small&gt;&amp;#169; 2004-5 Tom Hargreaves &lt;i&gt;&amp;lt;hex\@freezone.co.uk&amp;gt;&lt;/i&gt;.&lt;/small&gt;</property>
	      <property name="use_underline">False</property>
	      <property name="use_markup">True</property>
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
	      <property name="wrap">False</property>
	      <property name="selectable">False</property>
	      <property name="xalign">0.5</property>
	      <property name="yalign">0.5</property>
	      <property name="xpad">0</property>
	      <property name="ypad">0</property>
	    </widget>
	    <packing>
	      <property name="padding">3</property>
	      <property name="expand">False</property>
	      <property name="fill">False</property>
	    </packing>
	  </child>

	  <child>
	    <widget class="GnomeHRef" id="href1">
	      <property name="visible">True</property>
	      <property name="can_focus">True</property>
	      <property name="url">http://sphere.chronosempire.org.uk/~HEx/meander/</property>
	      <property name="label" translatable="yes">http://sphere.chronosempire.org.uk/~HEx/meander/</property>
	    </widget>
	    <packing>
	      <property name="padding">0</property>
	      <property name="expand">False</property>
	      <property name="fill">False</property>
	    </packing>
	  </child>
	</widget>
	<packing>
	  <property name="padding">0</property>
	  <property name="expand">True</property>
	  <property name="fill">True</property>
	</packing>
      </child>
    </widget>
  </child>
</widget>

</glade-interface>

EOF

$glade->signal_autoconnect_from_package("main");


my @points;
my @angles;
my @connected;

my ($dispwin,$iterations,$editwin, $dispcanvas, $editcanvas,
    $drawingarea, $xtext, $ytext, $angtext, $lentext, 
    $xbutton, $ybutton, $anglebutton, $lengthbutton,
    $pointtext, $insertbutton, $deletebutton, $conntoggle, $sidesmenu,
    $statusbar, $presets, $filesel, $about)=

    map $glade->get_widget($_), qw[dispwin iterations editwin
				   dispcanvas editcanvas
				   drawingarea x y angle length
				   xbutton ybutton anglebutton lengthbutton
				   point insert delete connected
				   sides statusbar presets
				   fileselection about
				   ];

my @presets;
my $presetmenu=Gtk2::Menu->new();

$presets->set_submenu($presetmenu);

{
    local $/="\n\n";
    while (<DATA>) {
	push @presets, $_;
	/^# (.*)/ or die $_;
	my $n=$_;
	my $item=Gtk2::MenuItem->new_with_label($1);
	$item->show;
	$presetmenu->add($item);
	$item->signal_connect('activate', sub { load ($n) });
    }
}

my $line_offset=0;
my $displine;
my $editline;
#my @editline;
my $dragging=0;
my $panning=0;
my @panoffset;
my $ignoreentry=0;
my $fix='';

my $iter;
my $base;
my $autoiter;

my @editpoints;
my $selected_point;

my $xorigin=300;
my $yorigin=250;
my $xscale=100;
my $yscale=100;

my $statuscontext=$statusbar->get_context_id('foo');

$drawingarea->add_events(qw[button-press-mask]);

init_points();

update_display();
draw_motif_editor();

Gtk2->main;

sub points_to_angles {
    my @points=@_;
    my @angles;
    my ($x,$y)=@{shift @points};
    my $ang=0;
    for my $point (@points) {
	my ($xx,$yy)=@{$point};
#	if ($xx eq '' || $yy eq '') {
#	    print STDERR Dumper [@_] ;
#	    exit;
#	}
				    
	my ($dx,$dy)=($yy-$y, $xx-$x);
	$ang=atan2($dx,$dy);#-$ang;
	push @angles, [$ang, sqrt($dx*$dx+$dy*$dy)];
	($x,$y)=($xx,$yy);
    }
    return @angles;
}

sub normalize {
    my @angles=@_;
    my ($x,$y)=(0,0);
    my $ang=0;

    for my $a (@angles) {
	my ($angle,$dist)=@{$a};
	$ang+=$angle;
	$x+=$dist*cos($ang);
	$y+=$dist*sin($ang);
    }

    return (sqrt($x*$x+$y*$y),atan2($y,$x));
}

sub angles_to_points {
    my ($scale,$theta,@angles)=@_;
    my @points=([0,0]);

    my ($x,$y)=(0,0);
    my $ang=-$theta;
    
    for my $a (@angles) {
	my ($angle,$dist)=@{$a};
	$ang+=$angle;
	$x+=$dist/$scale*cos($ang);
	$y+=$dist/$scale*sin($ang);
	push @points,[$x,$y];
    }
    return @points;
}

sub gtk_main_quit
{
    abort_draw();
    Gtk2->main_quit;
}

sub gtk_false { FALSE }
sub gtk_true { TRUE }

sub on_iterations_changed {
    my $i=$iterations->get_text;
    if ($i=~/^\d+$/) {
	$iter=$i;
	update_display();
    }
}

sub update_display {
	$drawingarea->window->invalidate_rect($drawingarea->allocation,1);
}
    

sub draw_motif_editor {
    $editline=Gnome2::Canvas::Item->new($editcanvas->root, "Gnome2::Canvas::Shape")
	unless $editline;

    my $path=Gnome2::Canvas::PathDef->new;

    for (map {[$points[$_][0],-$points[$_][1],$_&&$connected[$_-1]]}
    0..$#points) {
#	print join(',',@$_),"\n";
	if ($_->[2]) {
	    $path->lineto($_->[0],$_->[1]);
	} else {
	    $path->moveto($_->[0],$_->[1]);
	}
    }
    
    $editline->set('outline-color'=>'black',
		   'width-pixels'=>1
		   );
    $editline->set_path_def ($path);

    my $size=0.03;
    for my $i (0..$#points) {
	unless ($editpoints[$i]) {
	    $editpoints[$i]=Gnome2::Canvas::Item->new($editcanvas->root,
						      "Gnome2::Canvas::Rect");
	    $editpoints[$i]->signal_connect (event => \&on_select_point, $i);
#	    $editpoints[$i]->signal_connect (event => sub {die "foo: $i"}, $i);
	}
	    
	$editpoints[$i]->set(
			     x1 => $_->[0]-$size,
			     y1 => -$_->[1]-$size,
			     x2 => $_->[0]+$size,
			     y2 => -$_->[1]+$size,
			     fill_color => 'black',
			     ) for $points[$i];
    }
    for my $i (@points..$#editpoints) {
	next unless $editpoints[$i];
	$editpoints[$i]->destroy;
	undef $editpoints[$i];
    }

    do_select($selected_point);
}

sub fix_point {
    my ($x,$y)=@_;
    my ($oldx,$oldy)=@{$points[$selected_point]};
    +{ # DWIM, dammit!
	$xbutton => sub { $x=$oldx },
	$ybutton => sub { $y=$oldy },
	$anglebutton => sub {
	    my ($nx, $ny)=@{$points[$selected_point+$line_offset*2+1]};
	    $x-=$nx;$y-=$ny;
	    my $nlen=sqrt($x*$x+$y*$y);
	    my $ang=atan2($oldy-$ny,$oldx-$nx);
	    $ang+=pi if $x*($oldx-$nx)<0;
	    ($x,$y)=($nx+cos($ang)*$nlen,$ny+sin($ang)*$nlen);
	},
	$lengthbutton => sub { 
	    my ($ang, $len)=@{$angles[$selected_point+$line_offset]};
	    my ($nx, $ny)=@{$points[$selected_point+$line_offset*2+1]};
	    $x-=$nx;$y-=$ny;
	    my $nlen=sqrt($x*$x+$y*$y);
	    if ($nlen) {
		($x,$y)=($nx+$x*$len/$nlen, $ny+$y*$len/$nlen);
	    } else {
		($x,$y)=($oldx,$oldy);
	    }
	},
    }->{$fix}();

    return ($x,$y);
}

sub on_select_point {
    my ($item, $event, $num)=@_;
    undef $num if $item eq $editcanvas;
#    printf "%s: %s\n", $event->type, $num;
#    return 0 unless ref($event) eq 'Gtk2::Gdk::Event::Button';
    if ($event->type eq 'button-press') {
	do_select($num) if $event->button!=2 && defined $num;# || $event->button==3);
	if ($selected_point && $selected_point<$#points) {
	    if ($event->button==2) {
		my ($x, $y)=($event->x,$event->y);
		($x, $y)=$editcanvas->window_to_world($x, $y)
		    if $item eq $editcanvas; # WHY?
		$y=-$y;
		($x,$y)=fix_point($x,$y) if $fix;

		$points[$selected_point]=[$x,$y];
		update_angles();
		points_changed();
	    }
	    if ($event->button!=3) {
		$editpoints[$selected_point]->grab ([qw[pointer-motion-mask button-release-mask]],
						  Gtk2::Gdk::Cursor->new('fleur'), 0);
		$dragging=1;
	    }
	}
    } elsif ($event->type eq 'button-release') {
	$editpoints[$selected_point]->ungrab() if $dragging;
	$dragging=0;
    } elsif ($event->type eq 'motion-notify') {
	if ($dragging) {
	    die unless defined $selected_point;
	    my ($x, $y)=($event->x,-$event->y);
	    1e-15>abs and $_=0 for $x,$y; # $*@%ing rounding errors
	    ($x,$y)=fix_point($x,$y) if $fix;
	    $points[$selected_point]=[$x,$y];
	    update_angles();
	    points_changed();
	}
    }
    return defined $num?TRUE:FALSE;
}

sub do_select {
    $ignoreentry=1;
#    print Dumper $selected_point,@_;
    if (defined $selected_point) {
	$editpoints[$selected_point]->set(
					  fill_color => 'black',
					  );
#	printf "x1=%f\n", $editpoints[$selected_point]->get("x1");
    }
    my $i=$selected_point=shift;
    if (defined $i) {
	$editpoints[$i]->set(
					  fill_color => 'red',
					  );

	$_->[0]->set_text($_->[1])
	    for ([$xtext=>$points[$i][0]],
		 [$ytext=>$points[$i][1]],
		  );

	if ($i+$line_offset>=0 && $i+$line_offset<$#points) {
	    $angtext->set_text($angles[$i+$line_offset][0]*180/pi);
	    $lentext->set_text($angles[$i+$line_offset][1]);
	}
	$pointtext->set_value($i+1) unless $pointtext->get_value eq $i+1;
	$conntoggle->set_active($connected[$i+$line_offset]);
    }

    {
	my $e=$i && $i<$#points;
	for ($xtext,$ytext) {
	    $_->set_editable($e);
	}
    }

    {
	my $e=defined $i && ($i+$line_offset>=0) && ($i+$line_offset<$#points);
	for ($angtext,$lentext) {
	    $_->set_editable($e);
	    $_->set_text('') unless $e;
	}
    }

    $deletebutton->set_sensitive($i && $i<$#points);
    $insertbutton->set_sensitive(defined $i);
    $conntoggle->set_sensitive(defined $i && ($i+$line_offset>=0) && ($i+$line_offset<$#points));

    $ignoreentry=0;
}

sub on_show_display_window_activate {
    $dispwin->show;
}

sub points_changed {
    $pointtext->get('adjustment')->upper(scalar @points);
    draw_motif_editor();
    update_display();
}    

sub update_angles {
    @angles=points_to_angles(@points);
}

sub update_points {
    @points=angles_to_points(@angles);
}

sub on_xy_changed {
    return FALSE unless defined $selected_point;
    return FALSE if $ignoreentry;
    my ($obj)=@_;
    die unless $obj eq $xtext || $obj eq $ytext;
    my $coord=$obj->get_text;
    if ($coord=~/^[-+]?\d*.?\d+$/) {
	$points[$selected_point][($obj eq $xtext)?0:1]=$obj->get_text;
	update_angles();
	points_changed();
    }
    return FALSE;
}

sub on_insert_clicked {
    if (defined $selected_point) {
	my $i=$selected_point;
	$i-- if ($i==$#points);
	my ($x,$y);
	$x=($points[$i][0]+$points[$i+1][0])/2;
	$y=($points[$i][1]+$points[$i+1][1])/2;
	splice @points, $i+1, 0, [$x,$y];
	splice @connected, $i+1, 0, 1;#$connected[$i]&&$connected[$i+1];
	update_angles();
	do_select($i+1);
	points_changed();
    }
    return FALSE;
}

sub on_delete_clicked {
    if (defined $selected_point) {
	my $i=$selected_point;
	if ($i && $i<$#points) {
	    splice @points, $i, 1;
	    splice @connected, $i, 1;
	    update_angles();
	    do_select($i-1);
	    points_changed();
	}
#	$selected_point=undef;
    }
    return FALSE;
}

sub on_point_changed {
    do_select (shift->get_value-1);
    return FALSE;
}

sub on_connected_toggled {
    return FALSE unless defined $selected_point;
    my $new=shift->get_active;
    for ($connected[$selected_point+$line_offset]) {
	$_=$new,points_changed() if defined $_ && $_!=$new;
    }
    return FALSE;
}

sub init_points {
    @points=([0,0],[1,0]);
    @connected=(1);
    $base=1;
    $iter=4;
    update_angles();
    update_params();
    do_select(undef);
    points_changed();
    do_select(0);
}

sub load {
    my $f=shift;
    init_points();
    for (split /\n/, $f) {
	next if /^#/;
	chomp;
	$iter=$1, next if /^iterations\s+(\d+)/;
	$base=$1, next if /^baseline\s+(\d+)/;
	if (s/^points\s+//) {
	    @points=([0,0]);
	    @connected=(!!s/^\s*-//);
	    while (s/^\s*\(([-+]?\d*.?\d+)\s+([-+]?\d*.?\d+\s*)\)//) {
		push @points,[$1,$2];
		push @connected,!!s/^\s*-//;
	    }
	    push @points,[1,0];
	    next if /^\s*$/;
	}
	die "Syntax error: $_";
    }
    update_angles();
    update_params();
#    do_select(undef);
    points_changed();
    do_select(0);
}

sub save {
    sub conn { $connected[shift]?'-':' '}
    print <<"EOF";
# Generated by $appname $version
points @{[conn(0).join "",map { sprintf "(%s %s)%s", @{$points[$_]}, conn($_) } 1..$#points-1]}
baseline $base
iterations $iter
EOF
}
sub on_open1_activate {
    $filesel->show;
}

sub on_fileselection_response {
    my ($obj,$event)=@_;
}

sub update_params {
    $iterations->set_text($iter);
    $sidesmenu->set_text($base);
}

sub on_new1_activate {
    init_points();
}

sub on_save1_activate {
    save();
}

sub on_previous_line1_activate {
    $line_offset=-1;
    do_select($selected_point);
}

sub on_next_line1_activate {
    $line_offset=0;
    do_select($selected_point);
}

{
    my $ignore=0;
    sub on_fixbutton_activate {
	return if $ignore;
	$ignore=1;
	$fix=shift;
	$fix=0 unless $fix->get_active;
	for ($xbutton, $ybutton, $lengthbutton, $anglebutton) {
	    $_->set_active($_==$fix);
	}
	$ignore=0;
    }
}

sub on_sides_changed {
    my $s=$sidesmenu->get_text;
    if ($s=~/^(\d+)/ && $1) {
	$base=$1;
	update_display();
    }
}    

sub on_autoiter_toggled {
    my ($obj)=@_;
    $autoiter=$obj->get_active;
    $iterations->set_sensitive(!$autoiter);
    update_display();
}

sub on_drawingarea_click {
    my ($obj,$event)=@_;
    my $zoom=7/6;
    my $size=$drawingarea->allocation;
    if ($event->button==1) {
	$xscale*=$zoom;
	$yscale*=$zoom;
#	$xorigin+=$size->width*($zoom-1)/2;
#	$yorigin+=$size->height*($zoom-1)/2;
	$xorigin=$xorigin*$zoom-$event->x*($zoom-1);
	$yorigin=$yorigin*$zoom-$event->y*($zoom-1);
	update_display();
    } elsif ($event->button==3) {
	$xscale/=$zoom;
	$yscale/=$zoom;
	$xorigin=$xorigin/$zoom+$event->x*($zoom-1)/$zoom;
	$yorigin=$yorigin/$zoom+$event->y*($zoom-1)/$zoom;
#	$xorigin=($xorigin/$zoom+$event->x*(1/$zoom-1));
#	$yorigin=($yorigin/$zoom+$event->y*(1/$zoom-1));
	update_display();
    } elsif ($event->button==2) {
	$xorigin-=($event->x-$size->width/2);
	$yorigin-=($event->y-$size->height/2);
	update_display();
    }
}

sub on_flip_motif1_activate {
    $_->[1]=-$_->[1] for @points;
    update_angles();
    points_changed();
}

sub on_about1_activate {
    $about->show;
}

sub hide_window {
    shift->hide;
    return TRUE;
}

sub on_aboutlogo_expose_event {
    my ($obj, $event)=@_;

    # this may fail if we're already busy drawing, but it's not important
    draw_meander ([.4, .2, .2, -.4, .4, .2],
			      $obj,
			      25, 40,
			      150, 0,
			      20, 0, -1);
}

sub busy {
    $_ && $_->window->set_cursor(shift() ? Gtk2::Gdk::Cursor->new("watch"):undef) for $dispwin,$editwin;
}

sub set_status {
    $statusbar->pop($statuscontext);
    $statusbar->push($statuscontext, shift);
}

sub on_expose_event {
    my ($obj, $event)=@_;

    my $maxiter=$iter;
    if ($autoiter) {
	# check convergence
	my @a=map {$connected[$_]?$angles[$_]:()} 0..$#connected;
	if (@points>2 && grep { $_->[1]>=0.99 } @a) {
	    $maxiter=log(1e7)/log($#points);
	} else {
	    $maxiter=500;
	}
    }
    
    my $start=$base>2?pi/2+pi/$base:pi;
    my ($x,$y);

    my $count=0;
    my ($starttime)=times;
    my $c=0;
    $c|=($connected[$_]<<$_) for 0..$#points-1;

    busy(TRUE);

    if (abort_draw()) {

# we've been called when we were already in the middle of drawing; the
# snag is that we need to return in order to tell the previous
# operation to abort, and we can't start anything new until that's happened.
# so we have to make sure we get called again soon.

# this doesn't work because we get called with multiple rectangles and we'd
# need to queue them
#	my $rect=$event->area;
#	# we seem to need to clone this for the callback :(
#	$rect=Gtk2::Gdk::Rectangle->new($rect->x, $rect->y, $rect->width, $rect->height);
#	print "rect:", join(",", $rect->x, $rect->y, $rect->width, $rect->height), "\n";
#	Glib::Timeout->add (10,sub { $drawingarea->window->invalidate_rect ($rect, 1) });

# this works, but blanks the entire display, which is irritating
#	Glib::Timeout->add (10,sub { $drawingarea->window->invalidate_rect ($rect, 1) });

# best solution(?): don't bother invalidating, just draw unbuffered in real-time
	Glib::Timeout->add (10,\&on_expose_event,$obj); # ,$event?
	  return 0;
      }

    my $complete=1;
BASELOOP:
    for my $i (0..$base) {
#	$obj->modify_fg ($obj->state, Gtk2::Gdk::Color->new (65535*($i &1),32767*($i &2), 16383*($i &4)));
	my $xx=cos(2*pi*$i/$base+$start);
	my $yy=sin(2*pi*$i/$base+$start);
	$start=0 if $base==1; #ewwww
	if ($i) {
	    my $dx=$xx-$x;
	    my $dy=$yy-$y;

	    my @a=(0,0);
	    my $ret=draw_meander ([map {
		my @b=($_->[0]-$a[0], $_->[1]-$a[1]); @a=@$_; @b;
	    } @points[1..$#points]],
				  $obj,
				  $xorigin+$x*$xscale, $yorigin+$y*$yscale,
				  $dx*$xscale, $dy*$yscale,
				  $maxiter, !$autoiter,
				  $c
				  );
	    $complete=0, last BASELOOP if $ret<0;

	    $count+=$ret;
	}
	($x,$y)=($xx,$yy);
    }

    busy(FALSE);

    if ($complete) {
	set_status(sprintf "%d points, %.2fs elapsed", $count, times-$starttime);
    } else {
	set_status(sprintf "%d points...", $count);
    }

    return FALSE;
}

__DATA__
# Lévy curve
points -(0.5 0.5)-
baseline 1
iterations 15

# Lévy square
points -(0.5 0.5)-
baseline 2
iterations 15

# Lévy square with warts
points -(0 0.5)-(0.5 0.5)-(0.5 0.33333333333)-(0.5 0.5)-(1 0.5)-
baseline 2
iterations 6

# Lévy tapestry
points -(0.5 -0.5)-
baseline 4
iterations 14

# Lévy hexagon
points -(0.25 0.43301270189221932338)-(0.75 0.43301270189221932338)-
baseline 2
iterations 8

# Koch snowflake
points -(0.3333333333333 0)-(0.5 0.28867513459481)-(0.66666666667 0)-
baseline 3
iterations 6

# Koch anti-snowflake
points -(0.3333333333333 0)-(0.5 -0.28867513459481)-(0.666666666667 0)-
baseline 3
iterations 6

# Minkowski sausage
points -(0.25 0)-(0.25 0.25)-(0.5 0.25)-(0.5 0)-(0.5 -0.25)-(0.75 -0.25)-(0.75 0)-
baseline 4
iterations 4

# Cross (Mandelbrot quintet)
points -(0.4 0.2)-(0.6 -0.2)-
baseline 4
iterations 5

# Square pattern
points -(0 0.5)-(0.5 0.5)-(0.5 0.0)-
baseline 4
iterations 6

# Square pattern 2
points -(0 0.5)-(0.5 0.5)-(0.5 0.0)-(0.57333333 -0.26)-
#(0.5 -0.24)
baseline 4
iterations 6

# Bushy square
points -(0.25 0.25)-(0.65849365 0.09150635)-(0.75 -0.25)-
baseline 4
iterations 5

# Dragon curve
points -(-0.5 0.5) (0.5 -0.5)-
baseline 1
iterations 16

# Twindragon
points -(0.5 0.5) (0.5 -0.5)-
baseline 1
iterations 16

# Terdragon
points -(0.5 -0.28867513459481)-(0.5 0.28867513459481)-
baseline 1
iterations 8

# Tergriffin
points -(0.5 0.28867513459481288224) (0.5 -0.28867513459481288224)-(1 0) (0.75 -0.14433756729740644111)-(0.25 0.14433756729740644111) 
baseline 1
iterations 9

# Quarter cross
points -(0.2 0.4)-(0.6 0.2)-(0.4 -0.2)-(0.8 -0.4)-
baseline 1
iterations 7

# Sierpinski gasket
points -(0.5 0)-(0.25 0.4330127018922)-(0.75 0.4330127018922)-(0.5 0)-
baseline 1
iterations 8

# Seirpinski gasket II
points -(0.5 0)-(0.25 -0.4330127018922) (0.5 0)-
baseline 1
iterations 7

# Sierpinski carpet
points -(0.3333333333 0) (0.6666666667 0)-(1 0) (1 0.3333333333)-(1 0.6666666667)-(1 1) (0.6666666667 1)-(0.3333333333 1)-(0 1) (0 0.6666666667)-(0 0.3333333333) (0 0) (0.3333333333 0)-(0.6666666667 0) 
baseline 1
iterations 5

# 0-isotrianguloid (Pseudo-Sierpinski triangle)
points -(0.5 0) (0.5 0.8660254037844)-(0.25 0.433012701892219) (0.75 0.1443375672974)-(0.5 0.57735026919) (0.5 -0)-
baseline 1
iterations 8

# 60-isotrianguloid
points -(0.25 -0.43301270189221932338) (0.75 -0.43301270189221932338)-(1 0) (0.75 0)-(0.25 0) (0.75 .43301270189221932338)-(0.25 .43301270189221932338) 
baseline 1
iterations 8

# 60-0-isotrianguloid
points -(0.25 -0.43301270189221932338) (0.75 -0.43301270189221932338)-(1 0) (0.25 0)-(0.75 0) (0.75 .43301270189221932338)-(0.25 .43301270189221932338) 
baseline 1
iterations 8

# Filled Koch snowflake
points -(0.3333333333333333 0) (0.1666666666666666667 0.2886751345948)-(0.5 0.2886751345948)-(0.833333333333333 0.2886751345948) (0.166666666666667 -0.2886751345948)-(0.5 -0.2886751345948)-(0.833333333333333 -0.2886751345948) (0.5 -0.2886751345948)-(0.5 0.2886751345948) (0.666666666666667 0)-
baseline 1
iterations 5

# 0-cyclohexamer
points -(0.3333333333333333 0) (0.1666666666666666667 0.2886751345948)-(0.5 0.2886751345948)-(0.833333333333333 0.2886751345948) (0.166666666666667 -0.2886751345948)-(0.5 -0.2886751345948)-(0.833333333333333 -0.2886751345948) (0.666666666666667 0)-
baseline 1
iterations 5

# Sierpinski snowflake
points -(0.3333333333333333 0)-(0.666666666666667 0)-(1 0) (0.1666666666666666667 0.2886751345948)-(0.5 0.2886751345948)-(0.833333333333333 0.2886751345948) (0.166666666666667 -0.2886751345948)-(0.5 -0.2886751345948)-(0.833333333333333 -0.2886751345948) 
baseline 1
iterations 5

# Frozen teardrop
points -(0.3333333333333333 0) (0.25 0.43301270189221932338)-(0.41666666666666666 0.14433756729740644111) (0.75 0.43301270189221932338)-(0.58333333333333333 0.14433756729740644111) (0.25 -0.43301270189221932338)-(0.416666666666666667 -0.14433756729740644111) (0.75 -0.43301270189221932338)-(0.583333333333333333333 -0.14433756729740644111) (1 0)-(1.33333333333333333 0) (1 0)-(0.6666666666666666667 0)-
baseline 1
iterations 6

# Fudgeflake
points -(0.5 0.2886751345948) (0.5 0.86602540378)-(0.5 0.2886751345948) (1 0)-(.5 0.2886751345948) 
baseline 1
iterations 8

# Star metatrapezium
points -(0.25 0)-(0.75 0)-(1 0) (0.25 0)-(0.375 .21650635094610966169)-(0.625 0.21650635094610966169)-(0.75 0)-(0.625 0.21650635094610966169)-(0.75 0.43301270189221932338)-(0.625 0.21650635094610966169)-(0.375 0.21650635094610966169)-(0.25 0.43301270189221932338)-(0.375 0.21650635094610966169)-(0.25 0) 
baseline 2
iterations 6

# Curved square
points -(0.5 0.05)-(0.5 -0.452493781056)-(0.5 0.05)-
baseline 4
iterations 8

# Torn square 
points -(0.4709 0)-(0.5 -0.47)-(0.5291 0)-
baseline 4
iterations 8

# Ice square
points -(0.5 0)-(0.5 -0.333333333333)-(0.5 0)-
baseline 4
iterations 8

# Ice triangle
points -(0.5 0)-(0.375 -0.2165063509461)-(0.5 0)-(0.625 -0.2165063509461)-(0.5 0)-
baseline 3
iterations 6

# Pentagram
points -(0.38196601125 0)-(0.5 -0.363271264)-(0.61803398875 0)-
baseline 5
iterations 6

# Sphinx
points -(0.25 0.43301270189)-(0.5 0.86602540378)-(0.75 0.43301270189)-
baseline 3
iterations 8

# Zigzag square
points -(0.25 0.25)-(0.125 0)-(0.875 0)-(0.75 -0.25)-
baseline 4
iterations 15

# Octagonal
points -(0.146447 0.353553)-(0.5 0.5)-(0.853553 0.353553)-(1 0)-(0.853553 -0.353553)-(0.5 -0.5)-(0.146447 -0.353553)-(0 0)
baseline 1
iterations 6

# Logarithmic spiral
points -(0.04 0.15333333)-
baseline 1
iterations 200

# Double spiral
points -(0.01 0.04)-(0.99 -0.04)-
baseline 1
iterations 800

# Alien brains
points -(0.47333333 0.32)-(1.01333333 0.00666667)-(0.55333333 -0.14)-
baseline 1
iterations 9

# Loopy
points -(0.52 -0.23333333)-(0.5 -0.45)-(0.48 -0.23333333)-
baseline 5
iterations 8

# Whirl
points -(0.3 0.2)-(0.5 0)-(0.22 0.22)-
baseline 3
iterations 12

# Monstrous
points -(0.4 0)-(0.5 0.08)-(1.16 -0.28)-
baseline 4
iterations 15

# Starry
points -(0.393333 0.36)-(0.58 -0.353333)-
baseline 1
iterations 10

# Swirly
points -(0.25 0)-(0.25 0.25)-(0.75 -0.25)-(0.75 0)-
baseline 5
iterations 8

# Swirly 2
points -(0.2 0.25)-(0.8 -0.25)-
baseline 4
iterations 30

# Swirly 3
points -(0.133333 0.166667)-(0.900000 -0.266667)-
baseline 6
iterations 40

# [Hexagonal]
points -(0.25 0.43301270189221932338)-(0.75 0.43301270189221932338)-(1 0)-(0.75 -0.43301270189221932338)-(0.25 -0.43301270189221932338)-(0 0)
baseline 1
iterations 5

# [Worm]
points -(0 0.25)-(0 0.50)-(0.25 0.5)-(0.5 0.5)-(0.5 0.25)-(0.5 0)-(0.5 -0.25)-(0.5 -0.5)-(0.75 -0.5)-(1 -0.5)-(1 -0.25)-
baseline 1
iterations 6

# Sheaf
points -(0.25 0)-(0.5 0)-(0.5 0.25)-(0.75 0.25)-(0.5 0.25)-(0.5 0)-(0.75 0)-
baseline 2
iterations 5

# Sheaf 2
points -(0.25 0.0)-(0.5 0.0)-(0.5 0.25)-(0.75 0.25)-(0.5 0.25)-(0.5 0.0)-(0.5 -0.25)-(0.25 -0.25)-(0.5 -0.25)-(0.5 0.0)-(0.75 0.0)-
baseline 4
iterations 5

# Anti-Koch 6 [dendritish]
points -(0.25 0)-(0.5 -.43301270189221932338)-(0.75 0)-
baseline 6
iterations 8

# Anti-Koch 3
# Generated by Meander 0.0.H
points -(0.25 0)-(0.5 -.43301270189221932338)-(0.75 0)-
baseline 3
iterations 8

# Anti-Koch II 3
points -(0.25 0)-(0.375 -0.21650635094611)-(0.5 0)-(0.625 -0.21650635094611)-(0.75 0)-
baseline 3
iterations 8

# Anti-Koch III 3
points -(0.25 0)-(0.375 -0.21650635094611)-(0.5 0)-(0.625 -0.21650635094611)-(0.5 -0.43301270189221932338)-(0.375 -0.21650635094611)-(0.625 -0.21650635094611)-(0.75 0)-
baseline 3
iterations 5

# Anti-Koch III 6
points -(0.25 0)-(0.375 -0.21650635094611)-(0.5 0)-(0.625 -0.21650635094611)-(0.5 -0.43301270189221932338)-(0.375 -0.21650635094611)-(0.625 -0.21650635094611)-(0.75 0)-
baseline 6
iterations 5

# Koch-unknown-angle
points -(0.33333333333333 0)-(0.5 0.55277079839256664151)-(0.6666666666666667 0)-
baseline 2
iterations 10

# Tartan
points -(0.4 0)-(0.5 -0.1)-(0.5 -0.5)-(0.5 -0.1)-(0.6 0)-
baseline 4
iterations 8

# Clouds
points -(0.19333333 -0.19333333)-(0.5 -0.06)-(1.18 0.16666667)-
baseline 2
iterations 8

# Exotic
points -(0.28 -0.11333333)-(0.5 0.4)-(0.72 -0.11333333)-
baseline 3
iterations 9

# Spiky sea-horse
points -(0.30666667 0.2)-(0.5 -0)-(1.29333333 -0.16666667)-
baseline 2
iterations 20

# Blotchy
points -(0.50666667 0.38)-(0.55333333 -0.27333333)-
baseline 1
iterations 10

# Twisty
points -(0.1666666667 0)-(0.1666666667 0.3333333333)-(0.5 0.3333333333)-(0.500000 0.3333333333)-(0.5 0.1666666667)-(0.3333333333 0.1666666667)-(0.3333333333 0)-(0.6666666667 0)-(0.6666666667 -0.1666666667)-(0.5 -0.1666666667)-(0.5 -0.3333333333)-(0.8333333333 -0.3333333333)-(0.8333333333 0)-

# Peano triangle
points -(0.5 0)-(0.25 0.4330127018922)-(0.5 0)-
baseline 3
iterations 8

# Peano hexagon
points -(0.5 0)-(0.25 -0.4330127018922)-(0.75 0.4330127018922)-(0.5 0)-
baseline 1
iterations 8

# Peano square
points -(0.3333333333 0)-(0.3333333333 0.3333333333)-(0.6666666667 0.3333333333)-(0.6666666667 0)-(0.3333333333 0)-(0.3333333333 -0.3333333333)-(0.6666666667 -0.3333333333)-(0.6666666667 0)-
baseline 1
iterations 6

# Box fractal
points -(0.3333333333 0)-(0.3333333333 -0.3333333333)-(0.6666666667 -0.3333333333)-(0.6666666667 0)-
baseline 4
iterations 6

# Wheeled square
points -(0.3333333333 0)-(0.3333333333 0)-(0.3333333333 -0.3333333333)-(0.3333333333 0)-(0.6666666667 0)-(0.6666666667 -0.3333333333)-(0.6666666667 0)-
baseline 4
iterations 5

# Not fudgeflake
points -(0.33333333333333 0)-(0.5 0.2886751345948)-(0.8333333333333 0.2886751345948)-
baseline 3
iterations 6

# Anti-Koch-alike
points -(0.25 0)-(0.5 -0.2886751345948)-(0.75 0)-
baseline 3
iterations 6

# Another anti-Koch-alike
points -(0.2 0)-(0.5 -0.218223035)-(0.8 0)-
baseline 3
iterations 6

# Unnamed
points -(0.250000 0.000000)- (0.500000 -0.288670)- (0.250000 0.000000) (0.750000 0.000000)- (0.500000 -0.288670)- (0.750000 0.000000)-
baseline 4
iterations 2

# Unnamed 2
points -(0.250000 0.000000)- (0.500000 -0.288670)- (0.250000 0.000000)- (0.500000 0.288670)- (0.750000 0.000000)- (0.500000 -0.288670) (0.750000 0.000000)-
baseline 6
iterations 6

# Fractured
points -(-0.42 -0.366666666666667) (1.23333333333333 0.786666666666667)-
baseline 1
iterations 20

# Pentagonal spiral
points -(0.126666666666667 -0.713333333333333) (1.46 0.406666666666667)-
baseline 1
iterations 35

# Shaded tri-spiral
points -(0 0.486666666666667) (1.33277993982348 0.828554135887042)-
baseline 1
iterations 36

# Splodge
points -(0.5 0.2886751345948)-(0.5 -0.2886751345948)-(0.5 0.2886751345948)-
baseline 6
iterations 8

# Splodge 2
points -(0.5 0.2886751345948)-(0.5 -0.2886751345948) (0.5 0.2886751345948)-
baseline 3
iterations 10

# Fig 5.15
points -(0 0.7) (0.3 0)-
baseline 1
iterations 12

# Fig 5.16
points -(0.6 0.6) (0.47 0)-
baseline 1
iterations 16

# Fig 5.17
points -(0 0.707106781186548) (0.5 0.5)-
baseline 1
iterations 16

# Generated by Meander 0.0.H
points  (-0.2071 0.7071)-(0.146446 0.7071)-(0.5 0.7071)-(0.853553 0.7071) (0.1464 .353)-(0.5 .353)-(0.853553 .353)-(1.2071067 .353) (0.5 1.06)-(0.8535 1.06) (0.1464 0)-(0.5 0) 
baseline 1
iterations 2

# Stingray
points -(0.5 0)-(1 0) (0.25 -0.25)-(0.25 0.25)-(0.25 -0.25) 
baseline 1
iterations 8
