configure.in plug-ins/Makefile.am plug-ins/sel2path/* <- New directory

Fri Jul  9 22:24:53 BST 1999 Andy Thomas <alt@gimp.org>

	* configure.in
	* plug-ins/Makefile.am
	* plug-ins/sel2path/* <- New directory
	* app/paths_dialog.c
	* pixmap/topath.xpm

	New function implemented by a plugin.
	Will convert a selection into a path. Please
	see the README in sel2path directory for more
	details (especially where the underlying algorithms/code
	were obtained from).
This commit is contained in:
BST 1999 Andy Thomas 1999-07-09 21:52:00 +00:00 committed by Andy Thomas
parent b13dd4fd69
commit 8beb7c582c
30 changed files with 6056 additions and 6 deletions

View File

@ -1,3 +1,18 @@
Fri Jul 9 22:24:53 BST 1999 Andy Thomas <alt@gimp.org>
* configure.in
* plug-ins/Makefile.am
* plug-ins/sel2path/* <- New directory
* app/paths_dialog.c
* pixmap/topath.xpm
New function implemented by a plugin.
Will convert a selection into a path. Please
see the README in sel2path directory for more
details (especially where the underlying algorithms/code
were obtained from).
Fri Jul 9 20:12:12 MEST 1999 Sven Neumann <sven@gimp.org>
* app/app_procs.c: applied a (modified) patch from

View File

@ -31,9 +31,11 @@
#include "drawable.h"
#include "errors.h"
#include "floating_sel.h"
#include "gdisplay.h"
#include "gimage.h"
#include "gimpimage.h"
#include "gimpdrawable.h"
#include "gimage_mask.h"
#include "gdisplay.h"
#include "gimprc.h"
#include "gimpset.h"
#include "general.h"
@ -52,6 +54,7 @@
#include "session.h"
#include "undo.h"
#include "drawable_pvt.h"
#include "libgimp/gimpintl.h"
#include "pixmaps/new.xpm"
@ -63,6 +66,7 @@
#include "pixmaps/penedit.xpm"
#include "pixmaps/penstroke.xpm"
#include "pixmaps/toselection.xpm"
#include "pixmaps/topath.xpm"
#include "pixmaps/path.xbm"
#include "pixmaps/locked.xbm"
@ -143,6 +147,10 @@ static void paths_dialog_new_point_callback (GtkWidget *, gpointer);
static void paths_dialog_add_point_callback (GtkWidget *, gpointer);
static void paths_dialog_delete_point_callback (GtkWidget *, gpointer);
static void paths_dialog_edit_point_callback (GtkWidget *, gpointer);
static void paths_dialog_sel_to_path_callback (GtkWidget *, gpointer);
static void paths_dialog_advanced_to_path_callback (GtkWidget *, gpointer);
static void paths_dialog_null_callback (GtkWidget *, gpointer);
static void path_close(PATHP);
#define NEW_PATH_BUTTON 1
@ -153,11 +161,21 @@ static void path_close(PATHP);
#define COPY_PATH_BUTTON 8
#define PASTE_PATH_BUTTON 9
/* the ops buttons */
static OpsButtonCallback to_path_ext_callbacks[] =
{
paths_dialog_advanced_to_path_callback, /* SHIFT */
paths_dialog_null_callback, /* CTRL */
paths_dialog_null_callback, /* MOD1 */
paths_dialog_null_callback, /* SHIFT + CTRL */
};
static OpsButton paths_ops_buttons[] =
{
{ new_xpm, paths_dialog_new_path_callback, NULL, N_("New Path"), NULL, 0 },
{ duplicate_xpm, paths_dialog_dup_path_callback, NULL, N_("Duplicate Path"), NULL, 0 },
{ toselection_xpm, paths_dialog_path_to_sel_callback, NULL, N_("Path to Selection"), NULL, 0 },
{ topath_xpm, paths_dialog_sel_to_path_callback, to_path_ext_callbacks, N_("Selection to Path"), NULL, 0 },
{ penstroke_xpm, paths_dialog_stroke_path_callback, NULL, N_("Stroke Path"), NULL, 0 },
{ delete_xpm, paths_dialog_delete_path_callback, NULL, N_("Delete Path"), NULL, 0 },
{ NULL, NULL, NULL, NULL, NULL, 0 }
@ -197,11 +215,11 @@ paths_ops_button_set_sensitive (gint but,
break;
case STROKE_PATH_BUTTON:
menus_set_sensitive_locale ("<Paths>", N_("/Stroke Path"), sensitive);
gtk_widget_set_sensitive(paths_ops_buttons[3].widget,sensitive);
gtk_widget_set_sensitive(paths_ops_buttons[4].widget,sensitive);
break;
case DEL_PATH_BUTTON:
menus_set_sensitive_locale ("<Paths>", N_("/Delete Path"), sensitive);
gtk_widget_set_sensitive(paths_ops_buttons[4].widget,sensitive);
gtk_widget_set_sensitive(paths_ops_buttons[5].widget,sensitive);
break;
case COPY_PATH_BUTTON:
menus_set_sensitive_locale ("<Paths>", N_("/Copy Path"), sensitive);
@ -1511,6 +1529,75 @@ paths_dialog_dup_path_callback (GtkWidget * widget, gpointer udata)
paths_dialog->current_path_list->last_selected_row = tmprow;
}
static void paths_dialog_advanced_to_path_callback (GtkWidget *widget,
gpointer udata)
{
ProcRecord *proc_rec;
Argument *args;
GimpImage *gimage;
/* find the sel2path PDB record */
if ((proc_rec = procedural_db_lookup ("plug_in_sel2path_advanced")) == NULL)
{
g_message (_("Selection to path (advanced) procedure lookup failed"));
return;
}
gimage = paths_dialog->gimage;
/* plug-in arguments as if called by <Image>/Filters/... */
args = g_new (Argument, 3);
args[0].arg_type = PDB_INT32;
args[0].value.pdb_int = RUN_INTERACTIVE;
args[1].arg_type = PDB_IMAGE;
args[1].value.pdb_int = (gint32) pdb_image_to_id (gimage);
args[2].arg_type = PDB_DRAWABLE;
args[2].value.pdb_int = (gint32) (gimage_active_drawable (gimage))->ID;
plug_in_run (proc_rec, args, 3, FALSE, TRUE, (gimage_active_drawable (gimage))->ID);
g_free (args);
}
static void paths_dialog_null_callback (GtkWidget *widget,
gpointer udata)
{
/* Maybe some more here later? */
}
void
paths_dialog_sel_to_path_callback (GtkWidget * widget, gpointer udata)
{
ProcRecord *proc_rec;
Argument *args;
GimpImage *gimage;
/* find the sel2path PDB record */
if ((proc_rec = procedural_db_lookup ("plug_in_sel2path")) == NULL)
{
g_message (_("Selection to path procedure lookup failed"));
return;
}
gimage = paths_dialog->gimage;
/* plug-in arguments as if called by <Image>/Filters/... */
args = g_new (Argument, 3);
args[0].arg_type = PDB_INT32;
args[0].value.pdb_int = RUN_INTERACTIVE;
args[1].arg_type = PDB_IMAGE;
args[1].value.pdb_int = (gint32) pdb_image_to_id (gimage);
args[2].arg_type = PDB_DRAWABLE;
args[2].value.pdb_int = (gint32) (gimage_active_drawable (gimage))->ID;
plug_in_run (proc_rec, args, 3, FALSE, TRUE, (gimage_active_drawable (gimage))->ID);
g_free (args);
}
void
paths_dialog_path_to_sel_callback (GtkWidget * widget, gpointer udata)
{

View File

@ -31,9 +31,11 @@
#include "drawable.h"
#include "errors.h"
#include "floating_sel.h"
#include "gdisplay.h"
#include "gimage.h"
#include "gimpimage.h"
#include "gimpdrawable.h"
#include "gimage_mask.h"
#include "gdisplay.h"
#include "gimprc.h"
#include "gimpset.h"
#include "general.h"
@ -52,6 +54,7 @@
#include "session.h"
#include "undo.h"
#include "drawable_pvt.h"
#include "libgimp/gimpintl.h"
#include "pixmaps/new.xpm"
@ -63,6 +66,7 @@
#include "pixmaps/penedit.xpm"
#include "pixmaps/penstroke.xpm"
#include "pixmaps/toselection.xpm"
#include "pixmaps/topath.xpm"
#include "pixmaps/path.xbm"
#include "pixmaps/locked.xbm"
@ -143,6 +147,10 @@ static void paths_dialog_new_point_callback (GtkWidget *, gpointer);
static void paths_dialog_add_point_callback (GtkWidget *, gpointer);
static void paths_dialog_delete_point_callback (GtkWidget *, gpointer);
static void paths_dialog_edit_point_callback (GtkWidget *, gpointer);
static void paths_dialog_sel_to_path_callback (GtkWidget *, gpointer);
static void paths_dialog_advanced_to_path_callback (GtkWidget *, gpointer);
static void paths_dialog_null_callback (GtkWidget *, gpointer);
static void path_close(PATHP);
#define NEW_PATH_BUTTON 1
@ -153,11 +161,21 @@ static void path_close(PATHP);
#define COPY_PATH_BUTTON 8
#define PASTE_PATH_BUTTON 9
/* the ops buttons */
static OpsButtonCallback to_path_ext_callbacks[] =
{
paths_dialog_advanced_to_path_callback, /* SHIFT */
paths_dialog_null_callback, /* CTRL */
paths_dialog_null_callback, /* MOD1 */
paths_dialog_null_callback, /* SHIFT + CTRL */
};
static OpsButton paths_ops_buttons[] =
{
{ new_xpm, paths_dialog_new_path_callback, NULL, N_("New Path"), NULL, 0 },
{ duplicate_xpm, paths_dialog_dup_path_callback, NULL, N_("Duplicate Path"), NULL, 0 },
{ toselection_xpm, paths_dialog_path_to_sel_callback, NULL, N_("Path to Selection"), NULL, 0 },
{ topath_xpm, paths_dialog_sel_to_path_callback, to_path_ext_callbacks, N_("Selection to Path"), NULL, 0 },
{ penstroke_xpm, paths_dialog_stroke_path_callback, NULL, N_("Stroke Path"), NULL, 0 },
{ delete_xpm, paths_dialog_delete_path_callback, NULL, N_("Delete Path"), NULL, 0 },
{ NULL, NULL, NULL, NULL, NULL, 0 }
@ -197,11 +215,11 @@ paths_ops_button_set_sensitive (gint but,
break;
case STROKE_PATH_BUTTON:
menus_set_sensitive_locale ("<Paths>", N_("/Stroke Path"), sensitive);
gtk_widget_set_sensitive(paths_ops_buttons[3].widget,sensitive);
gtk_widget_set_sensitive(paths_ops_buttons[4].widget,sensitive);
break;
case DEL_PATH_BUTTON:
menus_set_sensitive_locale ("<Paths>", N_("/Delete Path"), sensitive);
gtk_widget_set_sensitive(paths_ops_buttons[4].widget,sensitive);
gtk_widget_set_sensitive(paths_ops_buttons[5].widget,sensitive);
break;
case COPY_PATH_BUTTON:
menus_set_sensitive_locale ("<Paths>", N_("/Copy Path"), sensitive);
@ -1511,6 +1529,75 @@ paths_dialog_dup_path_callback (GtkWidget * widget, gpointer udata)
paths_dialog->current_path_list->last_selected_row = tmprow;
}
static void paths_dialog_advanced_to_path_callback (GtkWidget *widget,
gpointer udata)
{
ProcRecord *proc_rec;
Argument *args;
GimpImage *gimage;
/* find the sel2path PDB record */
if ((proc_rec = procedural_db_lookup ("plug_in_sel2path_advanced")) == NULL)
{
g_message (_("Selection to path (advanced) procedure lookup failed"));
return;
}
gimage = paths_dialog->gimage;
/* plug-in arguments as if called by <Image>/Filters/... */
args = g_new (Argument, 3);
args[0].arg_type = PDB_INT32;
args[0].value.pdb_int = RUN_INTERACTIVE;
args[1].arg_type = PDB_IMAGE;
args[1].value.pdb_int = (gint32) pdb_image_to_id (gimage);
args[2].arg_type = PDB_DRAWABLE;
args[2].value.pdb_int = (gint32) (gimage_active_drawable (gimage))->ID;
plug_in_run (proc_rec, args, 3, FALSE, TRUE, (gimage_active_drawable (gimage))->ID);
g_free (args);
}
static void paths_dialog_null_callback (GtkWidget *widget,
gpointer udata)
{
/* Maybe some more here later? */
}
void
paths_dialog_sel_to_path_callback (GtkWidget * widget, gpointer udata)
{
ProcRecord *proc_rec;
Argument *args;
GimpImage *gimage;
/* find the sel2path PDB record */
if ((proc_rec = procedural_db_lookup ("plug_in_sel2path")) == NULL)
{
g_message (_("Selection to path procedure lookup failed"));
return;
}
gimage = paths_dialog->gimage;
/* plug-in arguments as if called by <Image>/Filters/... */
args = g_new (Argument, 3);
args[0].arg_type = PDB_INT32;
args[0].value.pdb_int = RUN_INTERACTIVE;
args[1].arg_type = PDB_IMAGE;
args[1].value.pdb_int = (gint32) pdb_image_to_id (gimage);
args[2].arg_type = PDB_DRAWABLE;
args[2].value.pdb_int = (gint32) (gimage_active_drawable (gimage))->ID;
plug_in_run (proc_rec, args, 3, FALSE, TRUE, (gimage_active_drawable (gimage))->ID);
g_free (args);
}
void
paths_dialog_path_to_sel_callback (GtkWidget * widget, gpointer udata)
{

View File

@ -655,6 +655,7 @@ plug-ins/mosaic/Makefile
plug-ins/pagecurl/Makefile
plug-ins/print/Makefile
plug-ins/rcm/Makefile
plug-ins/sel2path/Makefile
plug-ins/sgi/Makefile
plug-ins/sinus/Makefile
plug-ins/struc/Makefile

26
pixmaps/topath.xpm Normal file
View File

@ -0,0 +1,26 @@
/* XPM */
static char * topath_xpm[] = {
"18 18 5 1",
" c None",
". c #000000",
"+ c #BCBABC",
"@ c #DEDADE",
"# c #FFFFFF",
" .. ..",
" ............... ",
" .. ... ..",
" +@@#. ",
" #.@@@@@.# ",
" .+@@@@@@@++ ",
" #@@@@@@@@@. ",
" .@@@@@@@@@@@# ",
" +@@@@@@@@@@@+ ",
" #@@@@@@@@@@@. ",
" .@@@@@@@@@@@+ ",
" +@@@@@@@@@@@# ",
" .@@@@@@@@@+ ",
" ++@@@@@@@.# ",
" #.@@@@@#+ ",
" .+#.+ ",
" . ... . ",
" ................."};

View File

@ -31,6 +31,7 @@ SUBDIRS = \
print \
rcm \
sgi \
sel2path \
sinus \
struc \
unsharp \

View File

@ -0,0 +1,6 @@
Makefile.in
Makefile
.deps
_libs
.libs
sel2path

View File

@ -0,0 +1,51 @@
## Process this file with automake to produce Makefile.in
libexecdir = $(gimpplugindir)/plug-ins
EXTRA_DIST = README \
README.limn
libexec_PROGRAMS = sel2path
sel2path_SOURCES = \
bitmap.h \
bounding-box.h \
curve.c \
curve.h \
edge.c \
edge.h \
fit.c \
fit.h \
global.h \
math.c \
pxl-outline.c \
pxl-outline.h \
sel2path.c \
sel2path_adv_dialog.c \
sel2path.h \
spline.c \
spline.h \
types.h \
vector.c \
vector.h
INCLUDES = \
-I$(top_srcdir) \
$(GTK_CFLAGS) \
-I$(includedir)
AM_CPPFLAGS = \
-DLOCALEDIR=\""$(localedir)"\"
LDADD = \
$(top_builddir)/libgimp/libgimp.la \
$(top_builddir)/plug-ins/libgck/gck/libgck.la \
$(GTK_LIBS) \
$(INTLLIBS)
.PHONY: files
files:
@files=`ls $(DISTFILES) 2> /dev/null`; for p in $$files; do \
echo $$p; \
done

51
plug-ins/sel2path/README Normal file
View File

@ -0,0 +1,51 @@
Andy Thomas (alt@gimp.org) 9th July 1999
This plug-in will take a selection and convert it into a path.
For the purpose of the plug-in the selection boundary is defined
in a similar manner to that worked out for the "marching ants" markers
of the selection. I think this gives the best user feel/feedback since
the created path "follows" the "marching ants".
I cannot claim responsibility for the underlying algorithms. These
were taken directly from the GNU font utilities (the "limn" program
in particular) written by Karl Berry and Kathryn Hargreaves.
Their email addresses quoted in the README are:-
Karl Berry karl@cs.umb.edu
Kathryn Hargreaves letters@cs.umb.edu
Please see fontutils-0.6 package for more details. I have included the
README from the limn part of the package.
I thank Karl & Kathryn for producing such a well written set of utilites.
I have just added a gimp front-end onto them.
How to use it.
~~~~~~~~~~~~~~
Simply select an area and then select either "<Image>/Selection/To Path"
menu item or the "Selection To Image" button in the paths dialog. The new
path will be created. Currently if the LCP dialog has not been activated
then the path will not be visible... A bug I have just found - simply
bring up the LCP dialog and select the Paths tab to see the newly created
path.
An additional function can be obtained by having the "Shift" modifier pressed
while using the button in the paths dialog. This will pop-up a "power-users"
menu where the parameters to the underlying algorithms can be modified.
WARING:- Some values may cause the plugin to enter extremely long operations.
You have been warned.
Have fun!
Andy.
PS. Please direct any bugs etc found in this plugin to either
myself or the gimp-developer mailing list. Thank.

View File

@ -0,0 +1,56 @@
This program converts bitmap fonts to a homegrown outline format, bezier
(BZR). The program `bzrto' converts that format to something usable for
typesetting.
We used two main sources in writing the program:
@mastersthesis{Schneider:PIC-88,
author = "Philip J. Schneider",
title = "Phoenix: An Interactive Curve Design System Based on the
Automatic Fitting of Hand-Sketched Curves",
school = inst-u-wash,
year = 1988,
}
@article{Plass:CG-17-229,
author = "Michael Plass and Maureen Stone",
title = "Curve-fitting with Piecewise Parametric Cubics",
journal = j-comp-graphics,
year = 1983,
volume = 17,
number = 3,
month = jul,
pages = "229-239",
}
We had access to the code for Phoenix, thanks to Philip, but none of our
code is based on his (mostly because his task was allow interactive
sketching, and ours to fit bitmap characters, and the two require
different data structures). The general outline of the fitting
algorithm does come from Phoenix.
We also found this article helpful:
@Inproceedings{Gonczarowski:RIDT91-1,
author = "Jakob Gonczarowski",
title = "A Fast Approach to Auto-tracing (with Parametric
Cubics)",
pages = "1--15",
crossref = "Morris:RIDT91",
acknowledgement = ack-kb,
}
@String{proc-RIDT91 = "Raster Imaging and Digital Typography II"}
@Proceedings{Morris:RIDT91,
title = proc-RIDT91,
booktitle = proc-RIDT91,
year = "1991",
editor = "Robert A. Morris and Jacques Andr{\'e}",
publisher = pub-CUP,
address = pub-CUP:adr,
acknowledgement = ack-kb,
}
(These BibTeX entries are from the type.bib and ep.bib files on
math.utah.edu:pub/tex/bib.)

112
plug-ins/sel2path/bitmap.h Normal file
View File

@ -0,0 +1,112 @@
/* bitmap.h: definition for a bitmap type. No packing is done by
default; each pixel is represented by an entire byte. Among other
things, this means the type can be used for both grayscale and binary
images.
Copyright (C) 1992 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifndef BITMAP_H
#define BITMAP_H
#include <stdio.h>
#include "bounding-box.h"
#include "types.h"
/* If the bitmap holds 8-bit values, rather than one-bit, the
definition of BLACK here is wrong. So don't use it in that case! */
#define WHITE 0
#define BLACK 1
/* The basic structure and macros to access it. */
typedef struct
{
dimensions_type dimensions;
one_byte *bitmap;
} bitmap_type;
/* The dimensions of the bitmap, in pixels. */
#define BITMAP_DIMENSIONS(b) ((b).dimensions)
/* The pixels, represented as an array of bytes (in contiguous storage).
Each pixel is a single byte, even for binary fonts. */
#define BITMAP_BITS(b) ((b).bitmap)
/* These are convenient abbreviations for geting inside the members. */
#define BITMAP_WIDTH(b) DIMENSIONS_WIDTH (BITMAP_DIMENSIONS (b))
#define BITMAP_HEIGHT(b) DIMENSIONS_HEIGHT (BITMAP_DIMENSIONS (b))
/* This is the address of the first pixel in the row ROW. */
#define BITMAP_ROW(b, row) (BITMAP_BITS (b) + (row) * BITMAP_WIDTH (b))
/* This is the pixel at [ROW,COL]. */
#define BITMAP_PIXEL(b, row, col) \
(*(BITMAP_BITS (b) + (row) * BITMAP_WIDTH (b) + (col)))
#define BITMAP_VALID_PIXEL(b, row, col) \
(0 <= (row) && (row) < BITMAP_HEIGHT (b) \
&& 0 <= (col) && (col) < BITMAP_WIDTH (b))
/* Assume that the pixel at [ROW,COL] itself is black. */
#define BITMAP_INTERIOR_PIXEL(b, row, col) \
(0 != (row) && (row) != BITMAP_HEIGHT (b) - 1 \
&& 0 != (col) && (col) != BITMAP_WIDTH (b) - 1 \
&& BITMAP_PIXEL (b, row - 1, col - 1) == BLACK \
&& BITMAP_PIXEL (b, row - 1, col) == BLACK \
&& BITMAP_PIXEL (b, row - 1, col + 1) == BLACK \
&& BITMAP_PIXEL (b, row, col - 1) == BLACK \
&& BITMAP_PIXEL (b, row, col + 1) == BLACK \
&& BITMAP_PIXEL (b, row + 1, col - 1) == BLACK \
&& BITMAP_PIXEL (b, row + 1, col) == BLACK \
&& BITMAP_PIXEL (b, row + 1, col + 1) == BLACK)
/* Allocate storage for the bits, set them all to white, and return an
initialized structure. */
extern bitmap_type new_bitmap (dimensions_type);
/* Free that storage. */
extern void free_bitmap (bitmap_type *);
/* Make a fresh copy of BITMAP in a new structure, and return it. */
extern bitmap_type copy_bitmap (bitmap_type bitmap);
/* Return the pixels in the bitmap B enclosed by the bounding box BB.
The result is put in newly allocated storage. */
extern bitmap_type extract_subbitmap (bitmap_type b, bounding_box_type bb);
/* Consider the dimensions of a bitmap as a bounding box. The bounding
box returned is in bitmap coordinates, rather than Cartesian, and
refers to pixels, rather than edges. Specifically, this means that
the maximum column is one less than results from `dimensions_to_bb
(BITMAP_DIMENSIONS ())'. */
extern const bounding_box_type bitmap_to_bb (const bitmap_type);
/* Return a vector of zero-based column numbers marking transitions from
black to white or white to black in ROW, which is of length WIDTH.
The end of the vector is marked with an element of length WIDTH + 1.
The first element always marks a white-to-black transition (or it's
0, if the first pixel in ROW is black). */
extern unsigned *bitmap_find_transitions (const one_byte *row, unsigned width);
/* Print part of or all of a bitmap. */
extern void print_bounded_bitmap (FILE *, bitmap_type, bounding_box_type);
extern void print_bitmap (FILE *, bitmap_type);
#endif /* not BITMAP_H */

View File

@ -0,0 +1,63 @@
/* bounding-box.h: operations on both real- and integer-valued bounding boxes.
Copyright (C) 1992 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifndef BOUNDING_BOX_H
#define BOUNDING_BOX_H
#include "types.h"
/* The bounding box's numbers are usually in Cartesian/Metafont
coordinates: (0,0) is towards the lower left. */
typedef struct
{
signed_4_bytes min_row, max_row;
signed_4_bytes min_col, max_col;
} bounding_box_type;
typedef struct
{
real min_row, max_row;
real min_col, max_col;
} real_bounding_box_type;
/* These accessing macros work for both types of bounding boxes, since
the member names are the same. */
#define MIN_ROW(bb) ((bb).min_row)
#define MAX_ROW(bb) ((bb).max_row)
#define MIN_COL(bb) ((bb).min_col)
#define MAX_COL(bb) ((bb).max_col)
/* See the comments at `get_character_bitmap' in gf_input.c for why the
width and height are treated asymetrically. */
#define BB_WIDTH(bb) (MAX_COL (bb) - MIN_COL (bb))
#define BB_HEIGHT(bb) (MAX_ROW (bb) - MIN_ROW (bb) + 1)
/* Convert a dimensions structure to an integer bounding box, and vice
versa. */
extern const bounding_box_type dimensions_to_bb (dimensions_type);
extern const dimensions_type bb_to_dimensions (bounding_box_type);
/* Update the bounding box BB from the point P. */
extern void update_real_bounding_box (real_bounding_box_type *bb,
real_coordinate_type p);
extern void update_bounding_box (bounding_box_type *bb, coordinate_type p);
#endif /* not BOUNDING_BOX_H */

183
plug-ins/sel2path/curve.c Normal file
View File

@ -0,0 +1,183 @@
/* curve.c: operations on the lists of pixels and lists of curves.
Copyright (C) 1992 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <stdio.h>
#include <malloc.h>
#include "config.h"
#include "global.h"
#include "curve.h"
/* Return an entirely empty curve. */
curve_type
new_curve ()
{
curve_type curve = malloc (sizeof (struct curve));
curve->point_list = NULL;
CURVE_LENGTH (curve) = 0;
CURVE_CYCLIC (curve) = false;
CURVE_START_TANGENT (curve) = CURVE_END_TANGENT (curve) = NULL;
PREVIOUS_CURVE (curve) = NEXT_CURVE (curve) = NULL;
return curve;
}
/* Start the returned curve off with COORD as the first point. */
curve_type
init_curve (coordinate_type coord)
{
curve_type curve = new_curve ();
curve->point_list = malloc (sizeof (point_type));
CURVE_LENGTH (curve) = 1;
CURVE_POINT (curve, 0) = int_to_real_coord (coord);
return curve;
}
/* Don't copy the points or tangents, but copy everything else. */
curve_type
copy_most_of_curve (curve_type old_curve)
{
curve_type curve = new_curve ();
CURVE_CYCLIC (curve) = CURVE_CYCLIC (old_curve);
PREVIOUS_CURVE (curve) = PREVIOUS_CURVE (old_curve);
NEXT_CURVE (curve) = NEXT_CURVE (old_curve);
return curve;
}
/* The length of CURVE will be zero if we ended up not being able to fit
it (which in turn implies a problem elsewhere in the program, but at
any rate, we shouldn't try here to free the nonexistent curve). */
void
free_curve (curve_type curve)
{
if (CURVE_LENGTH (curve) > 0)
safe_free ((address *) &(curve->point_list));
}
void
append_pixel (curve_type curve, coordinate_type coord)
{
append_point (curve, int_to_real_coord (coord));
}
void
append_point (curve_type curve, real_coordinate_type coord)
{
CURVE_LENGTH (curve)++;
curve->point_list = realloc(curve->point_list,CURVE_LENGTH (curve) * sizeof(point_type));
LAST_CURVE_POINT (curve) = coord;
/* The t value does not need to be set. */
}
/* Return an initialized but empty curve list. */
curve_list_type
new_curve_list ()
{
curve_list_type curve_list;
curve_list.length = 0;
curve_list.data = NULL;
return curve_list;
}
/* Free a curve list and all the curves it contains. */
void
free_curve_list (curve_list_type *curve_list)
{
unsigned this_curve;
for (this_curve = 0; this_curve < curve_list->length; this_curve++)
free_curve (curve_list->data[this_curve]);
/* If the character was empty, it won't have any curves. */
if (curve_list->data != NULL)
safe_free ((address *) &(curve_list->data));
}
/* Add an element to a curve list. */
void
append_curve (curve_list_type *curve_list, curve_type curve)
{
curve_list->length++;
curve_list->data = realloc(curve_list->data,curve_list->length*sizeof(curve_type));
curve_list->data[curve_list->length - 1] = curve;
}
/* Return an initialized but empty curve list array. */
curve_list_array_type
new_curve_list_array ()
{
curve_list_array_type curve_list_array;
CURVE_LIST_ARRAY_LENGTH (curve_list_array) = 0;
curve_list_array.data = NULL;
return curve_list_array;
}
/* Free a curve list array and all the curve lists it contains. */
void
free_curve_list_array (curve_list_array_type *curve_list_array)
{
unsigned this_list;
for (this_list = 0; this_list < CURVE_LIST_ARRAY_LENGTH (*curve_list_array);
this_list++)
free_curve_list (&CURVE_LIST_ARRAY_ELT (*curve_list_array, this_list));
/* If the character was empty, it won't have any curves. */
if (curve_list_array->data != NULL)
safe_free ((address *) &(curve_list_array->data));
}
/* Add an element to a curve list array. */
void
append_curve_list (curve_list_array_type *l, curve_list_type curve_list)
{
CURVE_LIST_ARRAY_LENGTH (*l)++;
l->data = realloc(l->data,( CURVE_LIST_ARRAY_LENGTH (*l))*sizeof(curve_list_type));
LAST_CURVE_LIST_ARRAY_ELT (*l) = curve_list;
}

157
plug-ins/sel2path/curve.h Normal file
View File

@ -0,0 +1,157 @@
/* curve.h: data structures for the conversion from pixels to splines.
Copyright (C) 1992 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifndef CURVE_H
#define CURVE_H
#include "types.h"
#include "vector.h"
/* We are simultaneously manipulating two different representations of
the same outline: one based on (x,y) positions in the plane, and one
based on parametric splines. (We are trying to match the latter to
the former.) Although the original (x,y)'s are pixel positions,
i.e., integers, after filtering they are reals. */
typedef struct
{
real_coordinate_type coord;
real t;
} point_type;
/* It turns out to be convenient to break the list of all the pixels in
the outline into sublists, divided at ``corners''. Then each of the
sublists is treated independently. Each of these sublists is a `curve'. */
struct curve
{
point_type *point_list;
int length;
boolean cyclic;
vector_type *start_tangent;
vector_type *end_tangent;
struct curve *previous;
struct curve *next;
};
typedef struct curve *curve_type;
/* Get at the coordinates and the t values. */
#define CURVE_POINT(c, n) ((c)->point_list[n].coord)
#define LAST_CURVE_POINT(c) ((c)->point_list[(c)->length-1].coord)
#define CURVE_T(c, n) ((c)->point_list[n].t)
#define LAST_CURVE_T(c) ((c)->point_list[(c)->length-1].t)
/* This is the length of `point_list'. */
#define CURVE_LENGTH(c) ((c)->length)
/* A curve is ``cyclic'' if it didn't have any corners, after all, so
the last point is adjacent to the first. */
#define CURVE_CYCLIC(c) ((c)->cyclic)
/* If the curve is cyclic, the next and previous points should wrap
around; otherwise, if we get to the end, we return CURVE_LENGTH and
-1, respectively. */
#define CURVE_NEXT(c, n) \
((n) + 1 >= CURVE_LENGTH (c) \
? CURVE_CYCLIC (c) ? ((n) + 1) % CURVE_LENGTH (c) : CURVE_LENGTH (c) \
: (n) + 1)
#define CURVE_PREV(c, n) \
((int) (n) - 1 < 0 \
? CURVE_CYCLIC (c) ? CURVE_LENGTH (c) + (int) (n) - 1 : -1 \
: (int) (n) - 1)
/* The tangents at the endpoints are computed using the neighboring curves. */
#define CURVE_START_TANGENT(c) ((c)->start_tangent)
#define CURVE_END_TANGENT(c) ((c)->end_tangent)
#define PREVIOUS_CURVE(c) ((c)->previous)
#define NEXT_CURVE(c) ((c)->next)
/* Return an entirely empty curve. */
extern curve_type new_curve (void);
/* Return a curve with the point P as its first element. */
extern curve_type init_curve (coordinate_type p);
/* Return a curve the same as C, except without any points. */
extern curve_type copy_most_of_curve (curve_type c);
/* Free the memory C uses. */
extern void free_curve (curve_type c);
/* Append the point P to the end of C's list. */
extern void append_pixel (curve_type c, coordinate_type p);
/* Like `append_pixel', for a point in real coordinates. */
extern void append_point (curve_type c, real_coordinate_type p);
/* Write some or all, respectively, of the curve C in human-readable
form to the log file, if logging is enabled. */
extern void log_curve (curve_type c, boolean print_t);
extern void log_entire_curve (curve_type c);
/* Display the curve C online, if displaying is enabled. */
extern void display_curve (curve_type);
/* So, an outline is a list of curves. */
typedef struct
{
curve_type *data;
unsigned length;
boolean clockwise;
} curve_list_type;
/* Number of curves in the list. */
#define CURVE_LIST_LENGTH(c_l) ((c_l).length)
/* Access the individual curves. */
#define CURVE_LIST_ELT(c_l, n) ((c_l).data[n])
#define LAST_CURVE_LIST_ELT(c_l) ((c_l).data[CURVE_LIST_LENGTH (c_l) - 1])
/* Says whether the outline that this curve list represents moves
clockwise or counterclockwise. */
#define CURVE_LIST_CLOCKWISE(c_l) ((c_l).clockwise)
extern curve_list_type new_curve_list (void);
extern void free_curve_list (curve_list_type *);
extern void append_curve (curve_list_type *, curve_type);
/* And a character is a list of outlines. I named this
`curve_list_array_type' because `curve_list_list_type' seemed pretty
monstrous. */
typedef struct
{
curve_list_type *data;
unsigned length;
} curve_list_array_type;
/* Turns out we can use the same definitions for lists of lists as for
just lists. But we define the usual names, just in case. */
#define CURVE_LIST_ARRAY_LENGTH CURVE_LIST_LENGTH
#define CURVE_LIST_ARRAY_ELT CURVE_LIST_ELT
#define LAST_CURVE_LIST_ARRAY_ELT LAST_CURVE_LIST_ELT
extern curve_list_array_type new_curve_list_array (void);
extern void free_curve_list_array (curve_list_array_type *);
extern void append_curve_list (curve_list_array_type *, curve_list_type);
#endif /* not CURVE_H */

266
plug-ins/sel2path/edge.c Normal file
View File

@ -0,0 +1,266 @@
/* edge.c: operations on edges in bitmaps.
Copyright (C) 1992 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <assert.h>
#include "types.h"
#include "sel2path.h"
#include "edge.h"
/* We can move in any of eight directions as we are traversing
the outline. These numbers are not arbitrary; TRY_PIXEL depends on
them. */
typedef enum
{
north = 0, northwest = 1, west = 2, southwest = 3, south = 4,
southeast = 5, east = 6, northeast = 7
} direction_type;
static boolean is_marked_edge (edge_type, unsigned, unsigned, bitmap_type);
static boolean is_outline_edge (edge_type, unsigned, unsigned);
static edge_type next_edge (edge_type);
/* The following macros are used (directly or indirectly) by the
`next_outline_edge' routine. */
/* Given the direction DIR of the pixel to test, decide which edge on
that pixel we are supposed to test. Because we've chosen the mapping
from directions to numbers carefully, we don't have to do much. */
#define FIND_TEST_EDGE(dir) ((dir) / 2)
/* Find how to move in direction DIR on the axis AXIS (either `ROW' or
`COL'). We are in the ``display'' coordinate system, with y
increasing downward and x increasing to the right. Therefore, we are
implementing the following table:
direction row delta col delta
north -1 0
south +1 0
east 0 +1
west 0 +1
with the other four directions (e.g., northwest) being the sum of
their components (e.g., north + west).
The first macro, `COMPUTE_DELTA', handles splitting up the latter
cases, all of which have been assigned odd numbers. */
#define COMPUTE_DELTA(axis, dir) \
((dir) % 2 != 0 \
? COMPUTE_##axis##_DELTA ((dir) - 1) \
+ COMPUTE_##axis##_DELTA (((dir) + 1) % 8) \
: COMPUTE_##axis##_DELTA (dir) \
)
/* Now it is trivial to implement the four cardinal directions. */
#define COMPUTE_ROW_DELTA(dir) \
((dir) == north ? -1 : (dir) == south ? +1 : 0)
#define COMPUTE_COL_DELTA(dir) \
((dir) == west ? -1 : (dir) == east ? +1 : 0)
/* See if the appropriate edge on the pixel from (row,col) in direction
DIR is on the outline. If so, update `row', `col', and `edge', and
break. We also use the variable `character' as the bitmap in which
to look. */
#define TRY_PIXEL(dir) \
{ \
int delta_r = COMPUTE_DELTA (ROW, dir); \
int delta_c = COMPUTE_DELTA (COL, dir); \
int test_row = *row + delta_r; \
int test_col = *col + delta_c; \
edge_type test_edge = FIND_TEST_EDGE (dir); \
\
if (sel_valid_pixel(test_row, test_col) \
&& is_outline_edge (test_edge, test_row, test_col)) \
{ \
*row = test_row; \
*col = test_col; \
*edge = test_edge; \
break; \
} \
}
/* Finally, we are ready to implement the routine that finds the next
edge on the outline. We look first for an adjacent edge that is not
on the current pixel. We want to go around outside outlines
counterclockwise, and inside outlines clockwise (because that is how
both Metafont and Adobe Type 1 format want their curves to be drawn).
The very first outline (an outside one) on each character starts on a
top edge (STARTING_EDGE in edge.h defines this); so, if we're at a
top edge, we want to go only to the left (on the pixel to the west)
or down (on the same pixel), to begin with. Then, when we're on a
left edge, we want to go to the top edge (on the southwest pixel) or
to the left edge (on the south pixel).
All well and good. But if you draw a rasterized circle (or whatever),
eventually we have to come back around to the beginning; at that
point, we'll be on a top edge, and we'll have to go to the right edge
on the northwest pixel. Draw pictures.
The upshot is, if we find an edge on another pixel, we return (in ROW
and COL) the position of the new pixel, and (in EDGE) the kind of
edge it is. If we don't find such an edge, we return (in EDGE) the
next (in a counterclockwise direction) edge on the current pixel. */
void
next_outline_edge (edge_type *edge,
unsigned *row, unsigned *col)
{
unsigned original_row = *row;
unsigned original_col = *col;
switch (*edge)
{
case right:
TRY_PIXEL (north);
TRY_PIXEL (northeast);
break;
case top:
TRY_PIXEL (west);
TRY_PIXEL (northwest);
break;
case left:
TRY_PIXEL (south);
TRY_PIXEL (southwest);
break;
case bottom:
TRY_PIXEL (east);
TRY_PIXEL (southeast);
break;
default:
printf ("next_outline_edge: Bad edge value (%d)", *edge);
}
/* If we didn't find an adjacent edge on another pixel, return the
next edge on the current pixel. */
if (*row == original_row && *col == original_col)
*edge = next_edge (*edge);
}
/* We return the next edge on the pixel at position ROW and COL which is
an unmarked outline edge. By ``next'' we mean either the one sent in
in STARTING_EDGE, if it qualifies, or the next such returned by
`next_edge'. */
edge_type
next_unmarked_outline_edge (unsigned row, unsigned col,
edge_type starting_edge,
bitmap_type marked)
{
edge_type edge = starting_edge;
assert (edge != no_edge);
while (is_marked_edge (edge, row, col, marked)
|| !is_outline_edge (edge, row, col))
{
edge = next_edge (edge);
if (edge == starting_edge)
return no_edge;
}
return edge;
}
/* We check to see if the edge EDGE of the pixel at position ROW and COL
is an outline edge; i.e., that it is a black pixel which shares that
edge with a white pixel. The position ROW and COL should be inside
the bitmap CHARACTER. */
boolean
is_outline_edge (edge_type edge,
unsigned row, unsigned col)
{
/* If this pixel isn't black, it's not part of the outline. */
if (sel_pixel_is_white(row, col))
return false;
switch (edge)
{
case left:
return col == 0 || sel_pixel_is_white(row, col - 1);
case top:
return row == 0 || sel_pixel_is_white(row - 1, col);
case right:
return (col == sel_get_width() - 1)
|| sel_pixel_is_white(row, col + 1);
case bottom:
return (row == sel_get_height() - 1)
|| sel_pixel_is_white(row + 1, col);
case no_edge:
default:
printf ("is_outline_edge: Bad edge value(%d)", edge);
}
return 0; /* NOTREACHED */
}
/* If EDGE is not already marked, we mark it; otherwise, it's a fatal error.
The position ROW and COL should be inside the bitmap MARKED. EDGE can
be `no_edge'; we just return false. */
void
mark_edge (edge_type edge, unsigned row, unsigned col, bitmap_type *marked)
{
/* printf("row = %d, col = %d \n",row,col); */
assert (!is_marked_edge (edge, row, col, *marked));
if (edge != no_edge)
BITMAP_PIXEL (*marked, row, col) |= 1 << edge;
}
/* Test if the edge EDGE at ROW/COL in MARKED is marked. */
static boolean
is_marked_edge (edge_type edge, unsigned row, unsigned col, bitmap_type marked)
{
return
edge == no_edge ? false : BITMAP_PIXEL (marked, row, col) & (1 << edge);
}
/* Return the edge which is counterclockwise-adjacent to EDGE. This
code makes use of the ``numericness'' of C enumeration constants;
sorry about that. */
#define NUM_EDGES no_edge
static edge_type
next_edge (edge_type edge)
{
return edge == no_edge ? edge : (edge + 1) % NUM_EDGES;
}

59
plug-ins/sel2path/edge.h Normal file
View File

@ -0,0 +1,59 @@
/* edge.h: declarations for edge traversing.
Copyright (C) 1992 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifndef EDGE_H
#define EDGE_H
#include "bitmap.h"
/* We consider each pixel to consist of four edges, and we travel along
edges, instead of through pixel centers. This is necessary for those
unfortunate times when a single pixel is on both an inside and an
outside outline.
The numbers chosen here are not arbitrary; the code that figures out
which edge to move to depends on particular values. See the
`TRY_PIXEL' macro in `edge.c'. To emphasize this, I've written in the
numbers we need for each edge value. */
typedef enum
{
top = 1, left = 2, bottom = 3, right = 0, no_edge = 4
} edge_type;
/* This choice is also not arbitrary: starting at the top edge makes the
code find outside outlines before inside ones, which is certainly
what we want. */
#define START_EDGE top
/* Return the next outline edge on B in EDGE, ROW, and COL. */
extern void next_outline_edge (edge_type *edge,
unsigned *row, unsigned *col);
/* Return the next edge after START on the pixel ROW/COL in B that is
unmarked, according to the MARKED array. */
extern edge_type next_unmarked_outline_edge (unsigned row, unsigned col,
edge_type start,
bitmap_type marked);
/* Mark the edge E at the pixel ROW/COL in MARKED. */
extern void mark_edge (edge_type e, unsigned row, unsigned col,
bitmap_type *marked);
#endif /* not EDGE_H */

1928
plug-ins/sel2path/fit.c Normal file

File diff suppressed because it is too large Load Diff

54
plug-ins/sel2path/fit.h Normal file
View File

@ -0,0 +1,54 @@
/* fit.h: convert the pixel representation to splines.
Copyright (C) 1992 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifndef FIT_H
#define FIT_H
#include "pxl-outline.h"
#include "spline.h"
/* See fit.c for descriptions of these variables, all of which can be
set using options. */
extern real align_threshold;
extern real corner_always_threshold;
extern unsigned corner_surround;
extern real corner_threshold;
extern real error_threshold;
extern unsigned filter_alternative_surround;
extern real filter_epsilon;
extern unsigned filter_iteration_count;
extern real filter_percent;
extern unsigned filter_surround;
extern boolean keep_knees;
extern real line_reversion_threshold;
extern real line_threshold;
extern real reparameterize_improvement;
extern real reparameterize_threshold;
extern real subdivide_search;
extern unsigned subdivide_surround;
extern real subdivide_threshold;
extern unsigned tangent_surround;
/* Fit splines and lines to LIST. */
extern spline_list_array_type fitted_splines (pixel_outline_list_type list);
void fit_set_params(SELVALS *);
void fit_set_default_params(SELVALS *);
#endif /* not FIT_H */

208
plug-ins/sel2path/global.h Normal file
View File

@ -0,0 +1,208 @@
/* global.h: extend the standard programming environment a little. This
is included from config.h, which everyone includes.
Copyright (C) 1992 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifndef GLOBAL_H
#define GLOBAL_H
#include "types.h"
/* Define common sorts of messages. */
/* This should be called only after a system call fails. */
#define FATAL_PERROR(s) do { perror (s); exit (errno); } while (0)
#define START_FATAL() do { fputs ("fatal: ", stderr)
#define END_FATAL() fputs (".\n", stderr); exit (1); } while (0)
#define FATAL(s) \
START_FATAL (); fprintf (stderr, "%s", s); END_FATAL ()
#define FATAL1(s, e1) \
START_FATAL (); fprintf (stderr, s, e1); END_FATAL ()
#define FATAL2(s, e1, e2) \
START_FATAL (); fprintf (stderr, s, e1, e2); END_FATAL ()
#define FATAL3(s, e1, e2, e3) \
START_FATAL (); fprintf (stderr, s, e1, e2, e3); END_FATAL ()
#define FATAL4(s, e1, e2, e3, e4) \
START_FATAL (); fprintf (stderr, s, e1, e2, e3, e4); END_FATAL ()
#define START_WARNING() do { fputs ("warning: ", stderr)
#define END_WARNING() fputs (".\n", stderr); fflush (stderr); } while (0)
#define WARNING(s) \
START_WARNING (); fprintf (stderr, "%s", s); END_WARNING ()
#define WARNING1(s, e1) \
START_WARNING (); fprintf (stderr, s, e1); END_WARNING ()
#define WARNING2(s, e1, e2) \
START_WARNING (); fprintf (stderr, s, e1, e2); END_WARNING ()
#define WARNING3(s, e1, e2, e3) \
START_WARNING (); fprintf (stderr, s, e1, e2, e3); END_WARNING ()
#define WARNING4(s, e1, e2, e3, e4) \
START_WARNING (); fprintf (stderr, s, e1, e2, e3, e4); END_WARNING ()
/* Define useful abbreviations. */
/* This is the maximum number of numerals that result when a 64-bit
integer is converted to a string, plus one for a trailing null byte,
plus one for a sign. */
#define MAX_INT_LENGTH 21
/* Printer's points, as defined by TeX (and good typesetters everywhere). */
#define POINTS_PER_INCH 72.27
/* Convert a number V in pixels to printer's points, and vice versa,
assuming a resolution of DPI pixels per inch. */
#define PIXELS_TO_POINTS(v, dpi) (POINTS_PER_INCH * (v) / (dpi))
#define POINTS_TO_REAL_PIXELS(v, dpi) ((v) * (dpi) / POINTS_PER_INCH)
#define POINTS_TO_PIXELS(v, dpi) ((int) (POINTS_TO_REAL_PIXELS (v, dpi) + .5))
/* Some simple numeric operations. It is possible to define these much
more cleanly in GNU C, but we haven't done that (yet). */
#define SQUARE(x) ((x) * (x))
#define CUBE(x) ((x) * (x) * (x))
#define SAME_SIGN(u,v) ((u) >= 0 && (v) >= 0 || (u) < 0 && (v) < 0)
#define ROUND(x) ((int) ((int) (x) + .5 * SIGN (x)))
#define SIGN(x) ((x) > 0 ? 1 : (x) < 0 ? -1 : 0)
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
/* Too bad C doesn't define operators for these. */
#define MAX_EQUALS(var, expr) if ((expr) > (var)) (var) = (expr)
#define MIN_EQUALS(var, expr) if ((expr) < (var)) (var) = (expr)
#define STREQ(s1, s2) (strcmp (s1, s2) == 0)
/* Declarations for commonly-used routines we provide ourselves. The
ones here are only needed by us, so we do not provide them in
unprototyped form. Others are declared both ways in lib.h. */
/* Return the current date and time a la date(1). */
extern string now (void);
/* Check if a string is a valid floating-point or decimal integer.
Returns false if passed NULL. */
extern boolean float_ok (string);
extern boolean integer_ok (string);
/* Like `atoi', but disallow negative numbers. */
extern const unsigned atou (string);
/* The converses of atoi, atou, and atof. These all return dynamically
allocated strings. `dtoa' is so-named because `ftoa' is a library
function on some systems (the IBM RT), and the loader complains that
is defined twice, for reasons I don't understand. */
extern string itoa (int);
extern string utoa (unsigned);
extern string dtoa (double);
/* Like their stdio counterparts, but abort on error, after calling
perror(3) with FILENAME as its argument. */
/* extern FILE *xfopen (string filename, string mode); */
/* extern void xfclose (FILE *, string filename); */
/* extern void xfseek (FILE *, long, int, string filename); */
/* extern four_bytes xftell (FILE *, string filename); */
/* Copies the file FROM to the file TO, then unlinks FROM. */
extern void xrename (string from, string to);
/* Return NAME with any leading path stripped off. This returns a
pointer into NAME. */
/* ALT extern string basename (string name); */
/* If P or *P is null, abort. Otherwise, call free(3) on P,
and then set *P to NULL. */
extern void safe_free (address *p);
/* Math functions. */
/* Says whether V1 and V2 are within REAL_EPSILON of each other.
Fixed-point arithmetic would be better, to guarantee machine
independence, but it's so much more painful to work with. The value
here is smaller than can be represented in either a `fix_word' or a
`scaled_num', so more precision than this will be lost when we
output, anyway. */
#define REAL_EPSILON 0.00001
extern const boolean epsilon_equal (real v1, real v2);
/* Arc cosine, in degrees. */
extern const real acosd (real);
/* Return the Euclidean distance between the two points. */
extern const real distance (real_coordinate_type, real_coordinate_type);
extern const real int_distance (coordinate_type, coordinate_type);
/* Slope between two points (delta y per unit x). */
extern const real slope (real_coordinate_type, real_coordinate_type);
/* Make a real coordinate from an integer one, and vice versa. */
extern const real_coordinate_type int_to_real_coord (coordinate_type);
extern const coordinate_type real_to_int_coord (real_coordinate_type);
/* Test if two integer points are adjacent. */
extern const boolean points_adjacent_p (int row1, int col1, int r2, int c2);
/* Find the largest and smallest elements of an array. */
extern void find_bounds (real values[], unsigned value_count,
/* returned: */ real *the_min, real *the_max);
/* Make all the elements in the array between zero and one. */
extern real *map_to_unit (real * values, unsigned value_count);
/* String functions. */
/* Return (a fresh copy of) SOURCE beginning at START and ending at
LIMIT. (Or NULL if LIMIT < START.) */
extern string substring (string source, const unsigned start,
const unsigned limit);
/* Change all uppercase letters in S to lowercase. */
extern string lowercasify (string s);
/* Character code parsing. */
/* If the string S parses as a character code, this sets *VALID to
`true' and returns the number. If it doesn't, it sets *VALID to
`false' and the return value is garbage.
We allow any of the following possibilies: a single character, as in
`a' or `0'; a decimal number, as in `21'; an octal number, as in `03'
or `0177'; a hexadecimal number, as in `0x3' or `0xff'. */
extern charcode_type parse_charcode (string s, boolean *valid);
/* Like `parse_charcode', but gives a fatal error if the string isn't a
valid character code. */
extern charcode_type xparse_charcode (string s);
/* The environment variable name with which to look up auxiliary files. */
#ifndef LIB_ENVVAR
#define LIB_ENVVAR "FONTUTIL_LIB"
#endif
#endif /* not GLOBAL_H */

175
plug-ins/sel2path/math.c Normal file
View File

@ -0,0 +1,175 @@
/* math.c: define some simple array operations, and other functions.
Copyright (C) 1992 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <math.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "types.h"
#include "global.h"
/* Numerical errors sometimes make a floating point number just slightly
larger or smaller than its true value. When it matters, we need to
compare with some tolerance, REAL_EPSILON, defined in kbase.h. */
const boolean
epsilon_equal (real v1, real v2)
{
return
v1 == v2 /* Usually they'll be exactly equal, anyway. */
|| fabs (v1 - v2) <= REAL_EPSILON;
}
/* Return the Euclidean distance between P1 and P2. */
const real
distance (real_coordinate_type p1, real_coordinate_type p2)
{
return hypot (p1.x - p2.x, p1.y - p2.y);
}
/* Same thing, for integer points. */
const real
int_distance (coordinate_type p1, coordinate_type p2)
{
return hypot ((double) p1.x - p2.x, (double) p1.y - p2.y);
}
/* Return the arc cosine of V, in degrees in the range zero to 180. V
is taken to be in radians. */
const real
acosd (real v)
{
real a;
if (epsilon_equal (v, 1.0))
v = 1.0;
else if (epsilon_equal (v, -1.0))
v = -1.0;
errno = 0;
a = acos (v);
if (errno == ERANGE || errno == EDOM)
FATAL_PERROR ("acosd");
return a * 180.0 / M_PI;
}
/* The slope of the line defined by COORD1 and COORD2. */
const real
slope (real_coordinate_type coord1, real_coordinate_type coord2)
{
assert (coord2.x - coord1.x != 0);
return (coord2.y - coord1.y) / (coord2.x - coord1.x);
}
/* Turn an integer point into a real one, and vice versa. */
const real_coordinate_type
int_to_real_coord (coordinate_type int_coord)
{
real_coordinate_type real_coord;
real_coord.x = int_coord.x;
real_coord.y = int_coord.y;
return real_coord;
}
const coordinate_type
real_to_int_coord (real_coordinate_type real_coord)
{
coordinate_type int_coord;
int_coord.x = ROUND (real_coord.x);
int_coord.y = ROUND (real_coord.y);
return int_coord;
}
/* See if two points (described by their row and column) are adjacent. */
const boolean
points_adjacent_p (int row1, int col1, int row2, int col2)
{
int row_diff = abs (row1 - row2);
int col_diff = abs (col1 - col2);
return
(row_diff == 1 && col_diff == 1)
|| (row_diff == 0 && col_diff == 1)
|| (row_diff == 1 && col_diff == 0);
}
/* Find the largest and smallest elements in an array of reals. */
void
find_bounds (real *values, unsigned value_count, real *min, real *max)
{
unsigned this_value;
/* We must use FLT_MAX and FLT_MIN, instead of the corresponding
values for double, because gcc uses the native atof to parse
floating point constants, and many atof's choke on the extremes. */
*min = FLT_MAX;
*max = FLT_MIN;
for (this_value = 0; this_value < value_count; this_value++)
{
if (values[this_value] < *min)
*min = values[this_value];
if (values[this_value] > *max)
*max = values[this_value];
}
}
/* Map a range of numbers, some positive and some negative, into all
positive, with the greatest being at one and the least at zero.
This allocates new memory. */
real *
map_to_unit (real *values, unsigned value_count)
{
real smallest, largest;
int this_value;
real *mapped_values = malloc (sizeof (real) * value_count);
find_bounds (values, value_count, &smallest, &largest);
largest -= smallest; /* We never care about largest itself. */
for (this_value = 0; this_value < value_count; this_value++)
mapped_values[this_value] = (values[this_value] - smallest) / largest;
return mapped_values;
}

View File

@ -0,0 +1,240 @@
/* pxl-outline.c: find the edges of the bitmap image; we call each such
edge an ``outline''; each outline is made up of one or more pixels;
and each pixel participates via one or more edges.
Copyright (C) 1992 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <stdlib.h>
#include "global.h"
#include "sel2path.h"
#include "bitmap.h"
#include "edge.h"
#include "pxl-outline.h"
static pixel_outline_type find_one_outline (edge_type,
unsigned, unsigned, bitmap_type *);
static void append_pixel_outline (pixel_outline_list_type *,
pixel_outline_type);
static pixel_outline_type new_pixel_outline (void);
static void append_outline_pixel (pixel_outline_type *, coordinate_type);
static void append_coordinate (pixel_outline_type *, int, int, edge_type);
static bitmap_type
local_new_bitmap (unsigned width,unsigned height)
{
bitmap_type answer;
unsigned size = width * height;
BITMAP_HEIGHT(answer) = height;
BITMAP_WIDTH(answer) = width;
BITMAP_BITS (answer) = size > 0 ? calloc (size, 1) : NULL;
/* printf("local_new_bitmap size = %d @[%p]\n",size,BITMAP_BITS (answer)); */
return answer;
}
static void
local_free_bitmap (bitmap_type *b)
{
if (BITMAP_BITS (*b) != NULL)
safe_free ((address *) &BITMAP_BITS (*b));
}
/* A character is made up of a list of one or more outlines. Here, we
go through a character's bitmap top to bottom, left to right, looking
for the next pixel with an unmarked edge also on the character's outline.
Each one of these we find is the starting place for one outline. We
find these outlines and put them in a list to return. */
pixel_outline_list_type
find_outline_pixels ()
{
pixel_outline_list_type outline_list;
unsigned row, col;
bitmap_type marked = local_new_bitmap (sel_get_width(),sel_get_height());
/* printf("width = %d, height = %d\n",BITMAP_WIDTH(marked),BITMAP_HEIGHT(marked)); */
O_LIST_LENGTH (outline_list) = 0;
outline_list.data = NULL;
for (row = 0; row < sel_get_height(); row++)
for (col = 0; col < sel_get_width(); col++)
{
edge_type edge;
if (sel_pixel_is_white(row, col))
continue;
edge = next_unmarked_outline_edge (row, col, START_EDGE,marked);
if (edge != no_edge)
{
pixel_outline_type outline;
boolean clockwise = edge == bottom;
outline = find_one_outline (edge, row, col, &marked);
/* Outside outlines will start at a top edge, and move
counterclockwise, and inside outlines will start at a
bottom edge, and move clockwise. This happens because of
the order in which we look at the edges. */
O_CLOCKWISE (outline) = clockwise;
append_pixel_outline (&outline_list, outline);
}
}
local_free_bitmap (&marked);
return outline_list;
}
/* Here we find one of a character C's outlines. We're passed the
position (ORIGINAL_ROW and ORIGINAL_COL) of a starting pixel and one
of its unmarked edges, ORIGINAL_EDGE. We traverse the adjacent edges
of the outline pixels, appending to the coordinate list. We keep
track of the marked edges in MARKED, so it should be initialized to
zeros when we first get it. */
static pixel_outline_type
find_one_outline (edge_type original_edge,
unsigned original_row, unsigned original_col,
bitmap_type *marked)
{
pixel_outline_type outline = new_pixel_outline ();
unsigned row = original_row, col = original_col;
edge_type edge = original_edge;
do
{
/* Put this edge on to the output list, changing to Cartesian, and
taking account of the side bearings. */
append_coordinate (&outline, col,
sel_get_height() - row, edge);
mark_edge (edge, row, col, marked);
next_outline_edge (&edge, &row, &col);
}
while (row != original_row || col != original_col || edge != original_edge);
return outline;
}
/* Append an outline to an outline list. This is called when we have
completed an entire pixel outline. */
static void
append_pixel_outline (pixel_outline_list_type *outline_list,
pixel_outline_type outline)
{
O_LIST_LENGTH (*outline_list)++;
outline_list->data = (pixel_outline_type *)realloc(outline_list->data,outline_list->length *sizeof(pixel_outline_type));
O_LIST_OUTLINE (*outline_list, O_LIST_LENGTH (*outline_list) - 1) = outline;
}
/* Here is a routine that frees a list of such lists. */
void
free_pixel_outline_list (pixel_outline_list_type *outline_list)
{
unsigned this_outline;
for (this_outline = 0; this_outline < outline_list->length; this_outline++)
{
pixel_outline_type o = outline_list->data[this_outline];
safe_free ((address *) &(o.data));
}
if (outline_list->data != NULL)
safe_free ((address *) &(outline_list->data));
}
/* Return an empty list of pixels. */
pixel_outline_type
new_pixel_outline ()
{
pixel_outline_type pixel_outline;
O_LENGTH (pixel_outline) = 0;
pixel_outline.data = NULL;
return pixel_outline;
}
/* Add the coordinate C to the pixel list O. */
static void
append_outline_pixel (pixel_outline_type *o, coordinate_type c)
{
O_LENGTH (*o)++;
o->data = (coordinate_type *)realloc(o->data, O_LENGTH (*o)*sizeof(coordinate_type));
O_COORDINATE (*o, O_LENGTH (*o) - 1) = c;
}
/* We are given an (X,Y) in Cartesian coordinates, and the edge of the pixel
we're on. We append a corner of that pixel as our coordinate.
If we're on a top edge, we use the upper-left hand corner; right edge
=> upper right; bottom edge => lower right; left edge => lower left. */
void
append_coordinate (pixel_outline_type *o, int x, int y, edge_type edge)
{
coordinate_type c = { x, y};
char * str;
switch (edge)
{
case top:
c.y++;
str = "top";
break;
case right:
c.x++;
c.y++;
str = "right";
break;
case bottom:
c.x++;
str = "bottom";
break;
case left:
str = "left";
break;
default:
printf ("append_coordinate: Bad edge (%d)", edge);
}
append_outline_pixel (o, c);
}

View File

@ -0,0 +1,70 @@
/* pxl-outline.h: find a list of outlines which make up one character.
Copyright (C) 1992 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifndef PXL_OUTLINE_H
#define PXL_OUTLINE_H
#include "types.h"
/* This is a list of contiguous points on the bitmap. */
typedef struct
{
coordinate_type *data;
unsigned length;
boolean clockwise;
} pixel_outline_type;
/* The Nth coordinate in the list. */
#define O_COORDINATE(p_o, n) ((p_o).data[n])
/* The length of the list. */
#define O_LENGTH(p_o) ((p_o).length)
/* Whether the outline moves clockwise or counterclockwise. */
#define O_CLOCKWISE(p_o) ((p_o).clockwise)
/* Since a pixel outline is cyclic, the index of thenext coordinate
after the last is the first, and the previous coordinate before the
first is the last. */
#define O_NEXT(p_o, n) (((n) + 1) % O_LENGTH (p_o))
#define O_PREV(p_o, n) ((n) == 0 \
? O_LENGTH (p_o) - 1 \
: (n) - 1)
/* And the character turns into a list of such lists. */
typedef struct
{
pixel_outline_type *data;
unsigned length;
} pixel_outline_list_type;
/* The Nth list in the list of lists. */
#define O_LIST_OUTLINE(p_o_l, n) ((p_o_l).data[n])
/* The length of the list of lists. */
#define O_LIST_LENGTH(p_o_l) ((p_o_l).length)
/* Find all pixels on the outline in the character C. */
extern pixel_outline_list_type find_outline_pixels ();
/* Free the memory in the list. */
extern void free_pixel_outline_list (pixel_outline_list_type *);
#endif /* not PXL_OUTLINE_H */

View File

@ -0,0 +1,714 @@
/*
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This is a plug-in for the GIMP.
*
* Plugin to convert a selection to a path.
*
* Copyright (C) 1999 Andy Thomas alt@gimp.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
/* Change log:-
* 0.1 First version.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "gtk/gtk.h"
#include "libgimp/gimp.h"
#include "global.h"
#include "types.h"
#include "pxl-outline.h"
#include "fit.h"
#include "spline.h"
#include "sel2path.h"
#include "libgimp/stdplugins-intl.h"
#define MID_POINT 127
/***** Magic numbers *****/
/* Variables set in dialog box */
static void query (void);
static void run (gchar *name,
gint nparams,
GParam *param,
gint *nreturn_vals,
GParam **return_vals);
static gint sel2path_dialog (SELVALS *);
static void sel2path_ok_callback (GtkWidget *,gpointer);
static void sel2path_close_callback (GtkWidget *,gpointer);
static void sel2path_reset_callback (GtkWidget *,gpointer);
static void dialog_print_selVals(SELVALS *);
gboolean do_sel2path (gint32,gint32);
gint gimp_selection_bounds (gint32,gint *,gint *,gint *,gint *,gint *);
gint gimp_selection_is_empty (gint32);
gint gimp_path_set_points (gint32,gchar *,gint,gint,gdouble *);
GPlugInInfo PLUG_IN_INFO =
{
NULL, /* init_proc */
NULL, /* quit_proc */
query, /* query_proc */
run, /* run_proc */
};
static gint sel_x1, sel_y1, sel_x2, sel_y2;
static gint has_sel, sel_width, sel_height;
static SELVALS selVals;
GPixelRgn selection_rgn;
gboolean retVal = TRUE; /* Toggle if cancle button clicked */
MAIN ()
static void
query_2()
{
static GParamDef args[] =
{
{ PARAM_INT32, "run_mode", "Interactive, non-interactive" },
{ PARAM_IMAGE, "image", "Input image (unused)" },
{ PARAM_DRAWABLE, "drawable", "Input drawable" },
{ PARAM_FLOAT, "align_threshold","align_threshold"},
{ PARAM_FLOAT, "corner_always_threshold","corner_always_threshold"},
{ PARAM_INT8, "corner_surround","corner_surround"},
{ PARAM_FLOAT, "corner_threshold","corner_threshold"},
{ PARAM_FLOAT, "error_threshold","error_threshold"},
{ PARAM_INT8, "filter_alternative_surround","filter_alternative_surround"},
{ PARAM_FLOAT, "filter_epsilon","filter_epsilon"},
{ PARAM_INT8, "filter_iteration_count","filter_iteration_count"},
{ PARAM_FLOAT, "filter_percent","filter_percent"},
{ PARAM_INT8, "filter_secondary_surround","filter_secondary_surround"},
{ PARAM_INT8, "filter_surround","filter_surround"},
{ PARAM_INT8, "keep_knees","{1-Yes, 0-No}"},
{ PARAM_FLOAT, "line_reversion_threshold","line_reversion_threshold"},
{ PARAM_FLOAT,"line_threshold","line_threshold"},
{ PARAM_FLOAT,"reparameterize_improvement","reparameterize_improvement"},
{ PARAM_FLOAT,"reparameterize_threshold","reparameterize_threshold"},
{ PARAM_FLOAT,"subdivide_search","subdivide_search"},
{ PARAM_INT8, "subdivide_surround","subdivide_surround"},
{ PARAM_FLOAT,"subdivide_threshold","subdivide_threshold"},
{ PARAM_INT8, "tangent_surround","tangent_surround"},
};
static GParamDef *return_vals = NULL;
static int nargs = sizeof (args) / sizeof (args[0]);
static int nreturn_vals = 0;
gimp_install_procedure ("plug_in_sel2path_advanced",
"Converts a selection to a path (with advanced user menu)",
"Converts a selection to a path (with advanced user menu)",
"Andy Thomas",
"Andy Thomas",
"1999",
NULL,
"RGB*, GRAY*",
PROC_PLUG_IN,
nargs, nreturn_vals,
args, return_vals);
}
static void
query ()
{
static GParamDef args[] =
{
{ PARAM_INT32, "run_mode", "Interactive, non-interactive" },
{ PARAM_IMAGE, "image", "Input image (unused)" },
{ PARAM_DRAWABLE, "drawable", "Input drawable" },
};
static GParamDef *return_vals = NULL;
static int nargs = sizeof (args) / sizeof (args[0]);
static int nreturn_vals = 0;
gimp_install_procedure ("plug_in_sel2path",
"Converts a selection to a path",
"Converts a selection to a path",
"Andy Thomas",
"Andy Thomas",
"1999",
"<Image>/Select/To Path",
"RGB*, GRAY*",
PROC_PLUG_IN,
nargs, nreturn_vals,
args, return_vals);
query_2();
}
static void
run (gchar *name,
gint nparams,
GParam *param,
gint *nreturn_vals,
GParam **return_vals)
{
static GParam values[1];
GDrawable * drawable;
gint32 drawable_ID;
gint32 image_ID;
GRunModeType run_mode;
GStatusType status = STATUS_SUCCESS;
gboolean no_dialog = FALSE;
run_mode = param[0].data.d_int32;
if(!strcmp(name,"plug_in_sel2path"))
no_dialog = TRUE;
*nreturn_vals = 1;
*return_vals = values;
values[0].type = PARAM_STATUS;
values[0].data.d_status = status;
drawable_ID = param[2].data.d_drawable;
drawable = gimp_drawable_get (drawable_ID);
image_ID = gimp_drawable_image_id(drawable_ID);
if(gimp_selection_is_empty(image_ID))
{
g_message("No selection to convert");
gimp_drawable_detach (drawable);
return;
}
fit_set_default_params(&selVals);
if(!no_dialog)
{
switch (run_mode)
{
case RUN_INTERACTIVE:
if(gimp_get_data_size("plug_in_sel2path_advanced") > 0)
{
gimp_get_data("plug_in_sel2path_advanced", &selVals);
}
if (!sel2path_dialog(&selVals))
{
gimp_drawable_detach (drawable);
return;
}
/* Get the current settings */
fit_set_params(&selVals);
break;
case RUN_NONINTERACTIVE:
if (nparams != 23)
status = STATUS_CALLING_ERROR;
if (status == STATUS_SUCCESS) {
selVals.align_threshold = param[3].data.d_float;
selVals.corner_always_threshold = param[4].data.d_float;
selVals.corner_surround = param[5].data.d_int8;
selVals.corner_threshold = param[6].data.d_float;
selVals.error_threshold = param[7].data.d_float;
selVals.filter_alternative_surround = param[8].data.d_int8;
selVals.filter_epsilon = param[9].data.d_float;
selVals.filter_iteration_count = param[10].data.d_int8;
selVals.filter_percent = param[11].data.d_float;
selVals.filter_secondary_surround = param[12].data.d_int8;
selVals.filter_surround = param[13].data.d_int8;
selVals.keep_knees = param[14].data.d_int8;
selVals.line_reversion_threshold = param[15].data.d_float;
selVals.line_threshold = param[16].data.d_float;
selVals.reparameterize_improvement = param[17].data.d_float;
selVals.reparameterize_threshold = param[18].data.d_float;
selVals.subdivide_search = param[19].data.d_float;
selVals.subdivide_surround = param[20].data.d_int8;
selVals.subdivide_threshold = param[21].data.d_float;
selVals.tangent_surround = param[22].data.d_int8;
fit_set_params(&selVals);
}
break;
case RUN_WITH_LAST_VALS:
if(gimp_get_data_size("plug_in_sel2path_advanced") > 0)
{
gimp_get_data("plug_in_sel2path_advanced", &selVals);
/* Set up the last values */
fit_set_params(&selVals);
}
break;
default:
break;
}
}
do_sel2path(drawable_ID,image_ID);
values[0].data.d_status = status;
if(status == STATUS_SUCCESS)
{
dialog_print_selVals(&selVals);
if (run_mode == RUN_INTERACTIVE && !no_dialog)
gimp_set_data("plug_in_sel2path_advanced", &selVals, sizeof(SELVALS));
}
gimp_drawable_detach (drawable);
}
static void
dialog_print_selVals(SELVALS *sels)
{
#if 0
printf("selVals.align_threshold %g\n",selVals.align_threshold);
printf("selVals.corner_always_threshol %g\n",selVals.corner_always_threshold);
printf("selVals.corner_surround %g\n",selVals.corner_surround);
printf("selVals.corner_threshold %g\n",selVals.corner_threshold);
printf("selVals.error_threshold %g\n",selVals.error_threshold);
printf("selVals.filter_alternative_surround %g\n",selVals.filter_alternative_surround);
printf("selVals.filter_epsilon %g\n",selVals.filter_epsilon);
printf("selVals.filter_iteration_count %g\n",selVals.filter_iteration_count);
printf("selVals.filter_percent %g\n",selVals.filter_percent);
printf("selVals.filter_secondary_surround %g\n",selVals.filter_secondary_surround);
printf("selVals.filter_surround %g\n",selVals.filter_surround);
printf("selVals.keep_knees %d\n",selVals.keep_knees);
printf("selVals.line_reversion_threshold %g\n",selVals.line_reversion_threshold);
printf("selVals.line_threshold %g\n",selVals.line_threshold);
printf("selVals.reparameterize_improvement %g\n",selVals.reparameterize_improvement);
printf("selVals.reparameterize_threshold %g\n",selVals.reparameterize_threshold);
printf("selVals.subdivide_search %g\n",selVals.subdivide_search);
printf("selVals.subdivide_surround %g\n",selVals.subdivide_surround);
printf("selVals.subdivide_threshold %g\n",selVals.subdivide_threshold);
printf("selVals.tangent_surround %g\n",selVals.tangent_surround);
#endif /* 0 */
}
/* Build the dialog up. This was the hard part! */
static gint
sel2path_dialog (SELVALS *sels)
{
GtkWidget *dlg;
GtkWidget *button;
GtkWidget *vbox;
GtkWidget *table;
gchar **argv;
gint argc;
argc = 1;
argv = g_new (gchar *, 1);
argv[0] = g_strdup ("sel2path");
gtk_init (&argc, &argv);
gtk_rc_parse (gimp_gtkrc ());
dlg = gtk_dialog_new ();
gtk_window_set_title (GTK_WINDOW (dlg), "Sel2path");
gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE);
gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
(GtkSignalFunc) sel2path_close_callback,
NULL);
vbox = gtk_vbox_new(FALSE, 0);
gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg)->vbox), vbox,
FALSE, FALSE, 0);
gtk_widget_show(vbox);
table = dialog_create_selection_area(sels);
gtk_container_add (GTK_CONTAINER (vbox), table);
/* Action area */
button = gtk_button_new_with_label ("OK");
GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
gtk_signal_connect (GTK_OBJECT (button), "clicked",
(GtkSignalFunc) sel2path_ok_callback,
dlg);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0);
gtk_widget_grab_default (button);
gtk_widget_show (button);
button = gtk_button_new_with_label ("Cancel");
GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
(GtkSignalFunc) gtk_widget_destroy,
GTK_OBJECT (dlg));
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0);
gtk_widget_show (button);
button = gtk_button_new_with_label ("Default Values");
GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
(GtkSignalFunc) sel2path_reset_callback,
GTK_OBJECT (dlg));
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0);
gtk_widget_show (button);
gtk_widget_show (dlg);
gtk_main ();
gdk_flush ();
return retVal;
}
static void
sel2path_close_callback (GtkWidget *widget,
gpointer data)
{
retVal = FALSE;
gtk_main_quit ();
}
static void
sel2path_ok_callback (GtkWidget *widget,
gpointer data)
{
gtk_widget_destroy (GTK_WIDGET (data));
retVal = TRUE;
}
static void
sel2path_reset_callback (GtkWidget *widget,
gpointer data)
{
reset_adv_dialog();
fit_set_params(&selVals);
}
guchar
sel_pixel_value(gint row, gint col)
{
guchar ret;
if(col > sel_width ||
row > sel_height)
{
g_warning("sel_pixel_value [%d,%d] out of bounds",col,row);
return 0;
}
gimp_pixel_rgn_get_pixel(&selection_rgn,&ret,col+sel_x1,row+sel_y1);
return ret;
}
gint
sel_pixel_is_white(gint row, gint col)
{
if(sel_pixel_value(row,col) < MID_POINT)
return 1;
else
return 0;
}
gint
sel_get_width()
{
return sel_width;
}
gint
sel_get_height()
{
return sel_height;
}
gint
sel_valid_pixel(gint row, gint col)
{
return (0 <= (row) && (row) < sel_get_height()
&& 0 <= (col) && (col) < sel_get_width());
}
void
gen_anchor(gdouble *p,double x,double y,int is_newcurve)
{
/* printf("TYPE: %s X: %d Y: %d\n", */
/* (is_newcurve)?"3":"1", */
/* sel_x1+(int)rint(x), */
/* sel_y1 + sel_height - (int)rint(y)+1); */
*p++ = (sel_x1+(int)rint(x));
*p++ = sel_y1 + sel_height - (int)rint(y)+1;
*p++ = (is_newcurve)?3.0:1.0;
}
void
gen_control(gdouble *p,double x,double y)
{
/* printf("TYPE: 2 X: %d Y: %d\n", */
/* sel_x1+(int)rint(x), */
/* sel_y1 + sel_height - (int)rint(y)+1); */
*p++ = sel_x1+(int)rint(x);
*p++ = sel_y1 + sel_height - (int)rint(y)+1;
*p++ = 2.0;
}
void
do_points(spline_list_array_type in_splines,gint32 image_ID)
{
unsigned this_list;
int seg_count = 0;
int point_count = 0;
double last_x,last_y;
gdouble *parray;
gdouble *cur_parray;
gint path_point_count;
for (this_list = 0; this_list < SPLINE_LIST_ARRAY_LENGTH (in_splines);
this_list++)
{
spline_list_type in_list = SPLINE_LIST_ARRAY_ELT (in_splines, this_list);
/* Ignore single points that are on their own */
if(SPLINE_LIST_LENGTH (in_list) < 2)
continue;
point_count += SPLINE_LIST_LENGTH (in_list);
}
/* printf("Name SEL2PATH\n"); */
/* printf("#POINTS: %d\n",point_count*3); */
/* printf("CLOSED: 1\n"); */
/* printf("DRAW: 0\n"); */
/* printf("STATE: 4\n"); */
path_point_count = point_count*9;
cur_parray = (gdouble *)g_new0(gdouble ,point_count*9);
parray = cur_parray;
point_count = 0;
for (this_list = 0; this_list < SPLINE_LIST_ARRAY_LENGTH (in_splines);
this_list++)
{
unsigned this_spline;
spline_list_type in_list = SPLINE_LIST_ARRAY_ELT (in_splines, this_list);
/* if(seg_count > 0 && point_count > 0) */
/* gen_anchor(last_x,last_y,0); */
point_count = 0;
/* Ignore single points that are on their own */
if(SPLINE_LIST_LENGTH (in_list) < 2)
continue;
for (this_spline = 0; this_spline < SPLINE_LIST_LENGTH (in_list);
this_spline++)
{
spline_type s = SPLINE_LIST_ELT (in_list, this_spline);
if (SPLINE_DEGREE (s) == LINEAR)
{
gen_anchor(cur_parray,START_POINT (s).x, START_POINT (s).y,seg_count && !point_count);
cur_parray += 3;
gen_control(cur_parray,START_POINT (s).x, START_POINT (s).y);
cur_parray += 3;
gen_control(cur_parray,END_POINT (s).x, END_POINT (s).y);
cur_parray += 3;
last_x = END_POINT (s).x;
last_y = END_POINT (s).y;
}
else if (SPLINE_DEGREE (s) == CUBIC)
{
gen_anchor(cur_parray,START_POINT (s).x, START_POINT (s).y,seg_count && !point_count);
cur_parray += 3;
gen_control(cur_parray,CONTROL1 (s).x, CONTROL1 (s).y);
cur_parray += 3;
gen_control(cur_parray,CONTROL2 (s).x, CONTROL2 (s).y);
cur_parray += 3;
last_x = END_POINT (s).x;
last_y = END_POINT (s).y;
}
else
g_message ("print_spline: strange degree (%d)", SPLINE_DEGREE (s));
point_count++;
}
seg_count++;
}
gimp_path_set_points (image_ID,
_("selection_to_path"),
1,
path_point_count,
parray);
}
gboolean
do_sel2path(gint32 drawable_ID,gint32 image_ID )
{
gint32 selection_ID;
GDrawable *sel_drawable;
pixel_outline_list_type olt;
spline_list_array_type splines;
gimp_selection_bounds(image_ID,&has_sel,&sel_x1, &sel_y1, &sel_x2, &sel_y2);
sel_width = sel_x2 - sel_x1;
sel_height = sel_y2 - sel_y1;
/* Now get the selection channel */
selection_ID = gimp_image_get_selection(image_ID);
if(selection_ID < 0)
{
g_message("gimp_image_get_selection failed");
return FALSE;
}
sel_drawable = gimp_drawable_get (selection_ID);
if(gimp_drawable_bpp(selection_ID) != 1)
{
g_message("Internal error. Selection bpp > 1");
return FALSE;
}
gimp_pixel_rgn_init(&selection_rgn,sel_drawable,sel_x1,sel_y1,sel_width,sel_height,FALSE,FALSE);
gimp_tile_cache_ntiles(2 * (sel_drawable->width + gimp_tile_width() - 1) / gimp_tile_width());
olt = find_outline_pixels();
splines = fitted_splines (olt);
do_points(splines,image_ID);
return TRUE;
}
gint
gimp_selection_bounds (gint32 image_ID,
gint *has_sel,
gint *x1,
gint *y1,
gint *x2,
gint *y2)
{
GParam *return_vals;
int nreturn_vals;
int result;
return_vals = gimp_run_procedure ("gimp_selection_bounds",
&nreturn_vals,
PARAM_IMAGE, image_ID,
PARAM_END);
result = FALSE;
if (return_vals[0].data.d_status == STATUS_SUCCESS)
{
result = TRUE;
*has_sel = return_vals[1].data.d_int32;
*x1 = return_vals[2].data.d_int32;
*y1 = return_vals[3].data.d_int32;
*x2 = return_vals[4].data.d_int32;
*y2 = return_vals[5].data.d_int32;
}
gimp_destroy_params (return_vals, nreturn_vals);
return result;
}
gint
gimp_path_set_points (gint32 image_ID,
gchar *name,
gint ptype,
gint num_path_points,
gdouble *point_pairs)
{
GParam *return_vals;
int nreturn_vals;
int result;
#if 0
int count;
for(count = 0; count < num_path_points; count++)
{
printf("[%d] %g\n",count,point_pairs[count]);
}
#endif /* 0 */
return_vals = gimp_run_procedure ("gimp_path_set_points",
&nreturn_vals,
PARAM_IMAGE, image_ID,
PARAM_STRING, name,
PARAM_INT32, ptype,
PARAM_INT32, num_path_points,
PARAM_FLOATARRAY, point_pairs,
PARAM_END);
result = FALSE;
if (return_vals[0].data.d_status == STATUS_SUCCESS)
{
result = TRUE;
}
gimp_destroy_params (return_vals, nreturn_vals);
return result;
}
gint
gimp_selection_is_empty (gint32 image_ID)
{
GParam *return_vals;
gint nreturn_vals;
gint result;
return_vals = gimp_run_procedure ("gimp_selection_is_empty",
&nreturn_vals,
PARAM_IMAGE, image_ID,
PARAM_END);
result = FALSE;
if (return_vals[0].data.d_status == STATUS_SUCCESS)
{
result = return_vals[1].data.d_int32;
}
gimp_destroy_params (return_vals, nreturn_vals);
return result;
}
void
safe_free (address *item)
{
if (item == NULL || *item == NULL)
{
fprintf (stderr, "safe_free: Attempt to free a null item.\n");
abort ();
}
free (*item);
*item = NULL;
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This is a plug-in for the GIMP.
*
* Plugin to convert a selection to a path.
*
* Copyright (C) 1999 Andy Thomas alt@gimp.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#include "gtk/gtk.h"
#include "libgimp/gimp.h"
guchar sel_pixel_value (gint, gint);
gint sel_pixel_is_white (gint, gint);
gint sel_get_width (void);
gint sel_get_height (void);
gint sel_valid_pixel (gint, gint);
void reset_adv_dialog (void);
GtkWidget * dialog_create_selection_area(SELVALS *);

View File

@ -0,0 +1,577 @@
/*
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This is a plug-in for the GIMP.
*
* Plugin to convert a selection to a path.
*
* Copyright (C) 1999 Andy Thomas alt@gimp.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
/* Change log:-
* 0.1 First version.
*/
#include <stdio.h>
#include <stdlib.h>
#include "gtk/gtk.h"
#include "libgimp/gimp.h"
#include "types.h"
static GSList * adjust_widgets = NULL;
typedef struct dVal
{
gdouble value;
} dVal;
/* Reset to recommended defaults */
void
reset_adv_dialog()
{
GSList *list = adjust_widgets;
while(list)
{
GtkObject *w = GTK_OBJECT(list->data);
dVal *valptr = (dVal *)gtk_object_get_data(GTK_OBJECT(w),"default_value");
if(GTK_IS_ADJUSTMENT(w))
{
(GTK_ADJUSTMENT(w))->value = valptr->value;
gtk_signal_emit_by_name(GTK_OBJECT(w), "value_changed");
}
else if(GTK_IS_TOGGLE_BUTTON(w))
{
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(w),valptr->value);
}
else
g_warning("Internal widget list error");
list = g_slist_next(list);
}
}
gpointer
def_val(gdouble default_value)
{
dVal *valptr = g_new0(dVal,1);
valptr->value = default_value;
return(valptr);
}
static void
dialog_scale_update(GtkAdjustment * adjustment, gdouble * value)
{
if (*value != adjustment->value) {
*value = adjustment->value;
}
}
static void
on_keep_knees_checkbutton_toggled(GtkToggleButton *togglebutton,
gpointer user_data)
{
int *toggle_val;
toggle_val = (int *) user_data;
if (GTK_TOGGLE_BUTTON (togglebutton)->active)
{
/* Only do for event that sets a toggle button to true */
/* This will break if any more toggles are added? */
*toggle_val = TRUE;
}
else
*toggle_val = FALSE;
}
GtkWidget *
dialog_create_selection_area(SELVALS *sels)
{
GtkWidget *table1;
GtkWidget *align_threshold_scale;
GtkWidget *align_threshold;
GtkWidget *line_reversion_threshold_scale;
GtkWidget *subdivide_threshold_scale;
GtkWidget *tangent_surround_scale;
GtkWidget *subdivide_surround_scale;
GtkWidget *reparameterize_threshold_scale;
GtkWidget *corner_surround_scale;
GtkWidget *corner_threshold_scale;
GtkWidget *filter_percent_scale;
GtkWidget *filter_secondary_surround_scale;
GtkWidget *filter_surround_scale;
GtkWidget *keep_knees_checkbutton;
GtkWidget *subdivide_search_scale;
GtkWidget *reparameterize_improvement_scale;
GtkWidget *line_threshold_scale;
GtkWidget *filter_iteration_count_scale;
GtkWidget *filter_epsilon_scale;
GtkWidget *subdivide_threshold;
GtkWidget *tangent_surround;
GtkWidget *subdivide_surround;
GtkWidget *subdivide_search;
GtkWidget *reparameterize_threshold;
GtkWidget *reparameterize_improvement;
GtkWidget *line_threshold;
GtkWidget *line_reversion_threshold;
GtkWidget *keep_knees;
GtkWidget *filter_surround;
GtkWidget *filter_secondary_surround;
GtkWidget *filter_percent;
GtkWidget *filter_iteration_count;
GtkWidget *filter_epsilon;
GtkWidget *corner_always_threshold_scale;
GtkWidget *error_threshold_scale;
GtkWidget *filter_alternative_surround_scale;
GtkWidget *filter_alternative_surround;
GtkWidget *error_threshold;
GtkWidget *corner_threshold;
GtkWidget *corner_surround;
GtkWidget *corner_always_threshold;
GtkTooltips *tooltips;
GtkObject *size_data;
tooltips = gtk_tooltips_new ();
table1 = gtk_table_new (20, 2, FALSE);
gtk_widget_show (table1);
gtk_table_set_row_spacings (GTK_TABLE (table1), 2);
align_threshold_scale = gtk_hscale_new (GTK_ADJUSTMENT(size_data =gtk_adjustment_new (sels->align_threshold, 0.2, 2, 0.1, 0.1, 0)));
gtk_widget_show (align_threshold_scale);
gtk_table_attach (GTK_TABLE (table1), align_threshold_scale, 1, 2, 0, 1,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_tooltips_set_tip (tooltips, align_threshold_scale, "If two endpoints are closer than this, they are made to be equal.", NULL);
gtk_scale_set_value_pos (GTK_SCALE (align_threshold_scale), GTK_POS_LEFT);
gtk_signal_connect(GTK_OBJECT(size_data), "value_changed",
(GtkSignalFunc) dialog_scale_update,
&sels->align_threshold);
adjust_widgets = g_slist_append(adjust_widgets,size_data);
gtk_object_set_data(GTK_OBJECT(size_data),"default_value",def_val(0.5));
align_threshold = gtk_label_new ("align_threshold: ");
gtk_widget_show (align_threshold);
gtk_table_attach (GTK_TABLE (table1), align_threshold, 0, 1, 0, 1,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
GTK_WIDGET_SET_FLAGS (align_threshold, GTK_CAN_FOCUS);
gtk_tooltips_set_tip (tooltips, align_threshold, "If two endpoints are closer than this, they are made to be equal.\n (-align-threshold)", NULL);
gtk_misc_set_alignment (GTK_MISC (align_threshold), 1, 0.5);
line_reversion_threshold_scale = gtk_hscale_new (GTK_ADJUSTMENT(size_data =gtk_adjustment_new (sels->line_reversion_threshold, 0.01, 0.2, 0.01, 0.01, 0)));
gtk_widget_show (line_reversion_threshold_scale);
gtk_table_attach (GTK_TABLE (table1), line_reversion_threshold_scale, 1, 2, 12, 13,
GTK_EXPAND, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_tooltips_set_tip (tooltips, line_reversion_threshold_scale, "If a spline is closer to a straight line than this, it remains a straight line, even if it would otherwise be changed back to a curve. This is weighted by the square of the curve length, to make shorter curves more likely to be reverted.", NULL);
gtk_scale_set_value_pos (GTK_SCALE (line_reversion_threshold_scale), GTK_POS_LEFT);
gtk_scale_set_digits (GTK_SCALE (line_reversion_threshold_scale), 3);
gtk_signal_connect(GTK_OBJECT(size_data), "value_changed",
(GtkSignalFunc) dialog_scale_update,
&sels->line_reversion_threshold);
adjust_widgets = g_slist_append(adjust_widgets,size_data);
gtk_object_set_data(GTK_OBJECT(size_data),"default_value",def_val(0.01));
subdivide_threshold_scale = gtk_hscale_new (GTK_ADJUSTMENT(size_data =gtk_adjustment_new (sels->subdivide_threshold, 0.01, 1, 0.01, 0.01, 0)));
gtk_widget_show (subdivide_threshold_scale);
gtk_table_attach (GTK_TABLE (table1), subdivide_threshold_scale, 1, 2, 19, 20,
0, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_tooltips_set_tip (tooltips, subdivide_threshold_scale, "How many pixels a point can diverge from a straight line and still be considered a better place to subdivide.", NULL);
gtk_scale_set_value_pos (GTK_SCALE (subdivide_threshold_scale), GTK_POS_LEFT);
gtk_scale_set_digits (GTK_SCALE (subdivide_threshold_scale), 2);
gtk_signal_connect(GTK_OBJECT(size_data), "value_changed",
(GtkSignalFunc) dialog_scale_update,
&sels->subdivide_threshold);
adjust_widgets = g_slist_append(adjust_widgets,size_data);
gtk_object_set_data(GTK_OBJECT(size_data),"default_value",def_val(0.03));
tangent_surround_scale = gtk_hscale_new (GTK_ADJUSTMENT(size_data =gtk_adjustment_new (sels->tangent_surround, 2, 10, 1, 1, 0)));
gtk_widget_show (tangent_surround_scale);
gtk_table_attach (GTK_TABLE (table1), tangent_surround_scale, 1, 2, 18, 19,
0, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_tooltips_set_tip (tooltips, tangent_surround_scale, "Number of points to look at on either side of a point when computing the approximation to the tangent at that point.", NULL);
gtk_scale_set_value_pos (GTK_SCALE (tangent_surround_scale), GTK_POS_LEFT);
gtk_scale_set_digits (GTK_SCALE (tangent_surround_scale), 2);
gtk_signal_connect(GTK_OBJECT(size_data), "value_changed",
(GtkSignalFunc) dialog_scale_update,
&sels->tangent_surround);
adjust_widgets = g_slist_append(adjust_widgets,size_data);
gtk_object_set_data(GTK_OBJECT(size_data),"default_value",def_val(3.0));
subdivide_surround_scale = gtk_hscale_new (GTK_ADJUSTMENT(size_data =gtk_adjustment_new (sels->subdivide_surround, 2, 10, 1, 1, 0)));
gtk_widget_show (subdivide_surround_scale);
gtk_table_attach (GTK_TABLE (table1), subdivide_surround_scale, 1, 2, 17, 18,
0, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_tooltips_set_tip (tooltips, subdivide_surround_scale, "Number of points to consider when deciding whether a given point is a better place to subdivide.", NULL);
gtk_scale_set_value_pos (GTK_SCALE (subdivide_surround_scale), GTK_POS_LEFT);
gtk_scale_set_digits (GTK_SCALE (subdivide_surround_scale), 2);
gtk_signal_connect(GTK_OBJECT(size_data), "value_changed",
(GtkSignalFunc) dialog_scale_update,
&sels->subdivide_surround);
adjust_widgets = g_slist_append(adjust_widgets,size_data);
gtk_object_set_data(GTK_OBJECT(size_data),"default_value",def_val(4.0));
reparameterize_threshold_scale = gtk_hscale_new (GTK_ADJUSTMENT(size_data =gtk_adjustment_new (sels->reparameterize_threshold, 1, 50, 0.5, 0.5, 0)));
gtk_widget_show (reparameterize_threshold_scale);
gtk_table_attach (GTK_TABLE (table1), reparameterize_threshold_scale, 1, 2, 15, 16,
0, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_tooltips_set_tip (tooltips, reparameterize_threshold_scale, "Amount of error at which it is pointless to reparameterize. This happens, for example, when we are trying to fit the outline of the outside of an `O' with a single spline. The initial fit is not good enough for the Newton-Raphson iteration to improve it. It may be that it would be better to detect the cases where we didn't find any corners.", NULL);
gtk_scale_set_value_pos (GTK_SCALE (reparameterize_threshold_scale), GTK_POS_LEFT);
gtk_scale_set_digits (GTK_SCALE (reparameterize_threshold_scale), 2);
gtk_signal_connect(GTK_OBJECT(size_data), "value_changed",
(GtkSignalFunc) dialog_scale_update,
&sels->reparameterize_threshold);
adjust_widgets = g_slist_append(adjust_widgets,size_data);
gtk_object_set_data(GTK_OBJECT(size_data),"default_value",def_val(1.0));
corner_surround_scale = gtk_hscale_new (GTK_ADJUSTMENT(size_data =gtk_adjustment_new (sels->corner_surround, 3, 8, 1, 1, 0)));
gtk_widget_show (corner_surround_scale);
gtk_table_attach (GTK_TABLE (table1), corner_surround_scale, 1, 2, 2, 3,
GTK_EXPAND, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_tooltips_set_tip (tooltips, corner_surround_scale, "Number of points to consider when determining if a point is a corner or not.", NULL);
gtk_scale_set_value_pos (GTK_SCALE (corner_surround_scale), GTK_POS_LEFT);
gtk_signal_connect(GTK_OBJECT(size_data), "value_changed",
(GtkSignalFunc) dialog_scale_update,
&sels->corner_surround);
adjust_widgets = g_slist_append(adjust_widgets,size_data);
gtk_object_set_data(GTK_OBJECT(size_data),"default_value",def_val(4.0));
corner_threshold_scale = gtk_hscale_new (GTK_ADJUSTMENT(size_data =gtk_adjustment_new (sels->corner_threshold, 0, 180, 1, 1, 0)));
gtk_widget_show (corner_threshold_scale);
gtk_table_attach (GTK_TABLE (table1), corner_threshold_scale, 1, 2, 3, 4,
0, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_tooltips_set_tip (tooltips, corner_threshold_scale, "If a point, its predecessors, and its successors define an angle smaller than this, it's a corner. Should be in range 0..180.", NULL);
gtk_scale_set_value_pos (GTK_SCALE (corner_threshold_scale), GTK_POS_LEFT);
gtk_signal_connect(GTK_OBJECT(size_data), "value_changed",
(GtkSignalFunc) dialog_scale_update,
&sels->corner_threshold);
adjust_widgets = g_slist_append(adjust_widgets,size_data);
gtk_object_set_data(GTK_OBJECT(size_data),"default_value",def_val(100.0));
filter_percent_scale = gtk_hscale_new (GTK_ADJUSTMENT(size_data =gtk_adjustment_new (sels->filter_percent, 0, 1, 0.05, 0.01, 0)));
gtk_widget_show (filter_percent_scale);
gtk_table_attach (GTK_TABLE (table1), filter_percent_scale, 1, 2, 8, 9,
GTK_EXPAND, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_tooltips_set_tip (tooltips, filter_percent_scale, "To produce the new point, use the old point plus this times the neighbors.", NULL);
gtk_scale_set_value_pos (GTK_SCALE (filter_percent_scale), GTK_POS_LEFT);
gtk_scale_set_digits (GTK_SCALE (filter_percent_scale), 2);
gtk_signal_connect(GTK_OBJECT(size_data), "value_changed",
(GtkSignalFunc) dialog_scale_update,
&sels->filter_percent);
adjust_widgets = g_slist_append(adjust_widgets,size_data);
gtk_object_set_data(GTK_OBJECT(size_data),"default_value",def_val(0.33));
filter_secondary_surround_scale = gtk_hscale_new (GTK_ADJUSTMENT(size_data =gtk_adjustment_new (sels->filter_secondary_surround, 3, 10, 1, 1, 0)));
gtk_widget_show (filter_secondary_surround_scale);
gtk_table_attach (GTK_TABLE (table1), filter_secondary_surround_scale, 1, 2, 9, 10,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_tooltips_set_tip (tooltips, filter_secondary_surround_scale, "Number of adjacent points to consider if `filter_surround' points defines a straight line.", NULL);
gtk_scale_set_value_pos (GTK_SCALE (filter_secondary_surround_scale), GTK_POS_LEFT);
gtk_scale_set_digits (GTK_SCALE (filter_secondary_surround_scale), 2);
gtk_signal_connect(GTK_OBJECT(size_data), "value_changed",
(GtkSignalFunc) dialog_scale_update,
&sels->filter_secondary_surround);
adjust_widgets = g_slist_append(adjust_widgets,size_data);
gtk_object_set_data(GTK_OBJECT(size_data),"default_value",def_val(3.0));
filter_surround_scale = gtk_hscale_new (GTK_ADJUSTMENT(size_data =gtk_adjustment_new (sels->filter_surround, 2, 10, 1, 1, 0)));
gtk_widget_show (filter_surround_scale);
gtk_table_attach (GTK_TABLE (table1), filter_surround_scale, 1, 2, 10, 11,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_tooltips_set_tip (tooltips, filter_surround_scale, "Number of adjacent points to consider when filtering.", NULL);
gtk_scale_set_value_pos (GTK_SCALE (filter_surround_scale), GTK_POS_LEFT);
gtk_scale_set_digits (GTK_SCALE (filter_surround_scale), 2);
gtk_signal_connect(GTK_OBJECT(size_data), "value_changed",
(GtkSignalFunc) dialog_scale_update,
&sels->filter_surround);
adjust_widgets = g_slist_append(adjust_widgets,size_data);
gtk_object_set_data(GTK_OBJECT(size_data),"default_value",def_val(2.0));
keep_knees_checkbutton = gtk_check_button_new_with_label ("");
gtk_widget_show (keep_knees_checkbutton);
gtk_table_attach (GTK_TABLE (table1), keep_knees_checkbutton, 1, 2, 11, 12,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_tooltips_set_tip (tooltips, keep_knees_checkbutton, "Says whether or not to remove ``knee'' points after finding the outline.", NULL);
gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (keep_knees_checkbutton), sels->keep_knees);
gtk_signal_connect (GTK_OBJECT (keep_knees_checkbutton), "toggled",
GTK_SIGNAL_FUNC (on_keep_knees_checkbutton_toggled),
&sels->keep_knees);
adjust_widgets = g_slist_append(adjust_widgets,keep_knees_checkbutton);
gtk_object_set_data(GTK_OBJECT(keep_knees_checkbutton),"default_value",def_val(FALSE));
subdivide_search_scale = gtk_hscale_new (GTK_ADJUSTMENT(size_data =gtk_adjustment_new(sels->subdivide_search, 0.05, 1, 0.05, 0.01, 0)));
gtk_widget_show (subdivide_search_scale);
gtk_table_attach (GTK_TABLE (table1), subdivide_search_scale, 1, 2, 16, 17,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_tooltips_set_tip (tooltips, subdivide_search_scale, "Percentage of the curve away from the worst point to look for a better place to subdivide.", NULL);
gtk_scale_set_value_pos (GTK_SCALE (subdivide_search_scale), GTK_POS_LEFT);
gtk_scale_set_digits (GTK_SCALE (subdivide_search_scale), 2);
gtk_signal_connect(GTK_OBJECT(size_data), "value_changed",
(GtkSignalFunc) dialog_scale_update,
&sels->subdivide_search);
adjust_widgets = g_slist_append(adjust_widgets,size_data);
gtk_object_set_data(GTK_OBJECT(size_data),"default_value",def_val(0.10));
reparameterize_improvement_scale = gtk_hscale_new (GTK_ADJUSTMENT(size_data =gtk_adjustment_new (sels->reparameterize_improvement, 0, 1, 0.05, 0.01, 0)));
gtk_widget_show (reparameterize_improvement_scale);
gtk_table_attach (GTK_TABLE (table1), reparameterize_improvement_scale, 1, 2, 14, 15,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_tooltips_set_tip (tooltips, reparameterize_improvement_scale, "If reparameterization doesn't improve the fit by this much percent, stop doing it. ", NULL);
gtk_scale_set_value_pos (GTK_SCALE (reparameterize_improvement_scale), GTK_POS_LEFT);
gtk_scale_set_digits (GTK_SCALE (reparameterize_improvement_scale), 2);
gtk_signal_connect(GTK_OBJECT(size_data), "value_changed",
(GtkSignalFunc) dialog_scale_update,
&sels->reparameterize_improvement);
adjust_widgets = g_slist_append(adjust_widgets,size_data);
gtk_object_set_data(GTK_OBJECT(size_data),"default_value",def_val(0.01));
line_threshold_scale = gtk_hscale_new (GTK_ADJUSTMENT(size_data =gtk_adjustment_new (sels->line_threshold, 0.2, 4, 0.1, 0.01, 0)));
gtk_widget_show (line_threshold_scale);
gtk_table_attach (GTK_TABLE (table1), line_threshold_scale, 1, 2, 13, 14,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_tooltips_set_tip (tooltips, line_threshold_scale, "How many pixels (on the average) a spline can diverge from the line determined by its endpoints before it is changed to a straight line.", NULL);
gtk_scale_set_value_pos (GTK_SCALE (line_threshold_scale), GTK_POS_LEFT);
gtk_scale_set_digits (GTK_SCALE (line_threshold_scale), 2);
gtk_signal_connect(GTK_OBJECT(size_data), "value_changed",
(GtkSignalFunc) dialog_scale_update,
&sels->line_threshold);
adjust_widgets = g_slist_append(adjust_widgets,size_data);
gtk_object_set_data(GTK_OBJECT(size_data),"default_value",def_val(0.5));
filter_iteration_count_scale = gtk_hscale_new (GTK_ADJUSTMENT(size_data =gtk_adjustment_new (sels->filter_iteration_count, 4, 70, 1, 0.01, 0)));
gtk_widget_show (filter_iteration_count_scale);
gtk_table_attach (GTK_TABLE (table1), filter_iteration_count_scale, 1, 2, 7, 8,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_tooltips_set_tip (tooltips, filter_iteration_count_scale, "Number of times to smooth original data points. Increasing this number dramatically---to 50 or so---can produce vastly better results. But if any points that ``should'' be corners aren't found, the curve goes to hell around that point.", NULL);
gtk_scale_set_value_pos (GTK_SCALE (filter_iteration_count_scale), GTK_POS_LEFT);
gtk_scale_set_digits (GTK_SCALE (filter_iteration_count_scale), 2);
gtk_signal_connect(GTK_OBJECT(size_data), "value_changed",
(GtkSignalFunc) dialog_scale_update,
&sels->filter_iteration_count);
adjust_widgets = g_slist_append(adjust_widgets,size_data);
gtk_object_set_data(GTK_OBJECT(size_data),"default_value",def_val(4.0));
filter_epsilon_scale = gtk_hscale_new (GTK_ADJUSTMENT(size_data =gtk_adjustment_new (sels->filter_epsilon, 5, 40, 1, 1, 0)));
gtk_widget_show (filter_epsilon_scale);
gtk_table_attach (GTK_TABLE (table1), filter_epsilon_scale, 1, 2, 6, 7,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_tooltips_set_tip (tooltips, filter_epsilon_scale, "If the angles between the vectors produced by filter_surround and filter_alternative_surround points differ by more than this, use the one from filter_alternative_surround.", NULL);
gtk_scale_set_value_pos (GTK_SCALE (filter_epsilon_scale), GTK_POS_LEFT);
gtk_scale_set_digits (GTK_SCALE (filter_epsilon_scale), 2);
gtk_signal_connect(GTK_OBJECT(size_data), "value_changed",
(GtkSignalFunc) dialog_scale_update,
&sels->filter_epsilon);
adjust_widgets = g_slist_append(adjust_widgets,size_data);
gtk_object_set_data(GTK_OBJECT(size_data),"default_value",def_val(10.0));
subdivide_threshold = gtk_label_new ("subdivide_threshold: ");
gtk_widget_show (subdivide_threshold);
gtk_table_attach (GTK_TABLE (table1), subdivide_threshold, 0, 1, 19, 20,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_misc_set_alignment (GTK_MISC (subdivide_threshold), 1, 0.5);
gtk_misc_set_padding (GTK_MISC (subdivide_threshold), 0, 2);
tangent_surround = gtk_label_new ("tangent_surround: ");
gtk_widget_show (tangent_surround);
gtk_table_attach (GTK_TABLE (table1), tangent_surround, 0, 1, 18, 19,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_misc_set_alignment (GTK_MISC (tangent_surround), 1, 0.5);
gtk_misc_set_padding (GTK_MISC (tangent_surround), 0, 2);
subdivide_surround = gtk_label_new ("subdivide_surround: ");
gtk_widget_show (subdivide_surround);
gtk_table_attach (GTK_TABLE (table1), subdivide_surround, 0, 1, 17, 18,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_misc_set_alignment (GTK_MISC (subdivide_surround), 1, 0.5);
gtk_misc_set_padding (GTK_MISC (subdivide_surround), 0, 2);
subdivide_search = gtk_label_new ("subdivide_search: ");
gtk_widget_show (subdivide_search);
gtk_table_attach (GTK_TABLE (table1), subdivide_search, 0, 1, 16, 17,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_misc_set_alignment (GTK_MISC (subdivide_search), 1, 0.5);
gtk_misc_set_padding (GTK_MISC (subdivide_search), 0, 2);
reparameterize_threshold = gtk_label_new ("reparameterize_threshold: ");
gtk_widget_show (reparameterize_threshold);
gtk_table_attach (GTK_TABLE (table1), reparameterize_threshold, 0, 1, 15, 16,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_misc_set_alignment (GTK_MISC (reparameterize_threshold), 1, 0.5);
gtk_misc_set_padding (GTK_MISC (reparameterize_threshold), 0, 2);
reparameterize_improvement = gtk_label_new ("reparameterize_improvement: ");
gtk_widget_show (reparameterize_improvement);
gtk_table_attach (GTK_TABLE (table1), reparameterize_improvement, 0, 1, 14, 15,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_misc_set_alignment (GTK_MISC (reparameterize_improvement), 1, 0.5);
gtk_misc_set_padding (GTK_MISC (reparameterize_improvement), 0, 2);
line_threshold = gtk_label_new ("line_threshold: ");
gtk_widget_show (line_threshold);
gtk_table_attach (GTK_TABLE (table1), line_threshold, 0, 1, 13, 14,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_misc_set_alignment (GTK_MISC (line_threshold), 1, 0.5);
gtk_misc_set_padding (GTK_MISC (line_threshold), 0, 2);
line_reversion_threshold = gtk_label_new ("line_reversion_threshold: ");
gtk_widget_show (line_reversion_threshold);
gtk_table_attach (GTK_TABLE (table1), line_reversion_threshold, 0, 1, 12, 13,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_misc_set_alignment (GTK_MISC (line_reversion_threshold), 1, 0.5);
gtk_misc_set_padding (GTK_MISC (line_reversion_threshold), 0, 2);
keep_knees = gtk_label_new ("keep_knees: ");
gtk_widget_show (keep_knees);
gtk_table_attach (GTK_TABLE (table1), keep_knees, 0, 1, 11, 12,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_misc_set_alignment (GTK_MISC (keep_knees), 1, 0.5);
filter_surround = gtk_label_new ("filter_surround: ");
gtk_widget_show (filter_surround);
gtk_table_attach (GTK_TABLE (table1), filter_surround, 0, 1, 10, 11,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_misc_set_alignment (GTK_MISC (filter_surround), 1, 0.5);
gtk_misc_set_padding (GTK_MISC (filter_surround), 0, 2);
filter_secondary_surround = gtk_label_new ("filter_secondary_surround: ");
gtk_widget_show (filter_secondary_surround);
gtk_table_attach (GTK_TABLE (table1), filter_secondary_surround, 0, 1, 9, 10,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_misc_set_alignment (GTK_MISC (filter_secondary_surround), 1, 0.5);
gtk_misc_set_padding (GTK_MISC (filter_secondary_surround), 0, 2);
filter_percent = gtk_label_new ("filter_percent: ");
gtk_widget_show (filter_percent);
gtk_table_attach (GTK_TABLE (table1), filter_percent, 0, 1, 8, 9,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_misc_set_alignment (GTK_MISC (filter_percent), 1, 0.5);
gtk_misc_set_padding (GTK_MISC (filter_percent), 0, 2);
filter_iteration_count = gtk_label_new ("filter_iteration_count: ");
gtk_widget_show (filter_iteration_count);
gtk_table_attach (GTK_TABLE (table1), filter_iteration_count, 0, 1, 7, 8,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_misc_set_alignment (GTK_MISC (filter_iteration_count), 1, 0.5);
gtk_misc_set_padding (GTK_MISC (filter_iteration_count), 0, 2);
filter_epsilon = gtk_label_new ("filter_epsilon: ");
gtk_widget_show (filter_epsilon);
gtk_table_attach (GTK_TABLE (table1), filter_epsilon, 0, 1, 6, 7,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_tooltips_set_tip (tooltips, filter_epsilon, "If the angles between the vectors produced by filter_surround and\nfilter_alternative_surround points differ by more than this, use\nthe one from filter_alternative_surround.", NULL);
gtk_label_set_justify (GTK_LABEL (filter_epsilon), GTK_JUSTIFY_LEFT);
gtk_misc_set_alignment (GTK_MISC (filter_epsilon), 0.999999, 0.5);
gtk_misc_set_padding (GTK_MISC (filter_epsilon), 0, 2);
corner_always_threshold_scale = gtk_hscale_new (GTK_ADJUSTMENT(size_data =gtk_adjustment_new (sels->corner_always_threshold, 30, 180, 1, 1, 0)));
gtk_widget_show (corner_always_threshold_scale);
gtk_table_attach (GTK_TABLE (table1), corner_always_threshold_scale, 1, 2, 1, 2,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_tooltips_set_tip (tooltips, corner_always_threshold_scale, "If the angle defined by a point and its predecessors and successors is smaller than this, it's a corner, even if it's within `corner_surround' pixels of a point with a smaller angle.", NULL);
gtk_scale_set_value_pos (GTK_SCALE (corner_always_threshold_scale), GTK_POS_LEFT);
gtk_signal_connect(GTK_OBJECT(size_data), "value_changed",
(GtkSignalFunc) dialog_scale_update,
&sels->corner_always_threshold);
adjust_widgets = g_slist_append(adjust_widgets,size_data);
gtk_object_set_data(GTK_OBJECT(size_data),"default_value",def_val(60.0));
error_threshold_scale = gtk_hscale_new (GTK_ADJUSTMENT(size_data =gtk_adjustment_new (sels->error_threshold, 0.2, 10, 0.1, 0.1, 0)));
gtk_widget_show (error_threshold_scale);
gtk_table_attach (GTK_TABLE (table1), error_threshold_scale, 1, 2, 4, 5,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_tooltips_set_tip (tooltips, error_threshold_scale, "Amount of error at which a fitted spline is unacceptable. If any pixel is further away than this from the fitted curve, we try again.", NULL);
gtk_scale_set_value_pos (GTK_SCALE (error_threshold_scale), GTK_POS_LEFT);
gtk_scale_set_digits (GTK_SCALE (error_threshold_scale), 2);
gtk_signal_connect(GTK_OBJECT(size_data), "value_changed",
(GtkSignalFunc) dialog_scale_update,
&sels->error_threshold);
adjust_widgets = g_slist_append(adjust_widgets,size_data);
gtk_object_set_data(GTK_OBJECT(size_data),"default_value",def_val(0.40));
filter_alternative_surround_scale = gtk_hscale_new (GTK_ADJUSTMENT(size_data =gtk_adjustment_new (sels->filter_alternative_surround, 1, 10, 1, 1, 0)));
gtk_widget_show (filter_alternative_surround_scale);
gtk_table_attach (GTK_TABLE (table1), filter_alternative_surround_scale, 1, 2, 5, 6,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_tooltips_set_tip (tooltips, filter_alternative_surround_scale, "A second number of adjacent points to consider when filtering.", NULL);
gtk_scale_set_value_pos (GTK_SCALE (filter_alternative_surround_scale), GTK_POS_LEFT);
gtk_scale_set_digits (GTK_SCALE (filter_alternative_surround_scale), 2);
gtk_signal_connect(GTK_OBJECT(size_data), "value_changed",
(GtkSignalFunc) dialog_scale_update,
&sels->filter_alternative_surround);
adjust_widgets = g_slist_append(adjust_widgets,size_data);
gtk_object_set_data(GTK_OBJECT(size_data),"default_value",def_val(1.0));
filter_alternative_surround = gtk_label_new ("filter_alternative_surround: ");
gtk_widget_show (filter_alternative_surround);
gtk_table_attach (GTK_TABLE (table1), filter_alternative_surround, 0, 1, 5, 6,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_label_set_justify (GTK_LABEL (filter_alternative_surround), GTK_JUSTIFY_LEFT);
gtk_misc_set_alignment (GTK_MISC (filter_alternative_surround), 1, 0.5);
gtk_misc_set_padding (GTK_MISC (filter_alternative_surround), 0, 2);
error_threshold = gtk_label_new ("error_threshold: ");
gtk_widget_show (error_threshold);
gtk_table_attach (GTK_TABLE (table1), error_threshold, 0, 1, 4, 5,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_misc_set_alignment (GTK_MISC (error_threshold), 1, 0.5);
gtk_misc_set_padding (GTK_MISC (error_threshold), 0, 2);
corner_threshold = gtk_label_new ("corner_threshold: ");
gtk_widget_show (corner_threshold);
gtk_table_attach (GTK_TABLE (table1), corner_threshold, 0, 1, 3, 4,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_misc_set_alignment (GTK_MISC (corner_threshold), 1, 0.5);
gtk_misc_set_padding (GTK_MISC (corner_threshold), 0, 2);
corner_surround = gtk_label_new ("corner_surround: ");
gtk_widget_show (corner_surround);
gtk_table_attach (GTK_TABLE (table1), corner_surround, 0, 1, 2, 3,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_misc_set_alignment (GTK_MISC (corner_surround), 1, 0.5);
gtk_misc_set_padding (GTK_MISC (corner_surround), 0, 2);
corner_always_threshold = gtk_label_new ("corner_always_threshold: ");
gtk_widget_show (corner_always_threshold);
gtk_table_attach (GTK_TABLE (table1), corner_always_threshold, 0, 1, 1, 2,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_misc_set_alignment (GTK_MISC (corner_always_threshold), 1, 0.5);
gtk_misc_set_padding (GTK_MISC (corner_always_threshold), 0, 2);
return GTK_WIDGET(table1);
}

214
plug-ins/sel2path/spline.c Normal file
View File

@ -0,0 +1,214 @@
/* spline.c: spline and spline list (represented as arrays) manipulation.
Copyright (C) 1992 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <malloc.h>
#include <assert.h>
#include "global.h"
#include "bounding-box.h"
#include "spline.h"
#include "vector.h"
/* Return a new spline structure, initialized with (recognizable)
garbage. */
spline_type
new_spline ()
{
spline_type spline;
START_POINT (spline)
= CONTROL1 (spline)
= CONTROL2 (spline)
= END_POINT (spline)
= (real_coordinate_type) { -100.0, -100.0 };
SPLINE_DEGREE (spline) = -1;
return spline;
}
/* Print a spline in human-readable form. */
void
print_spline (FILE *f, spline_type s)
{
if (SPLINE_DEGREE (s) == LINEAR)
fprintf (f, "(%.3f,%.3f)--(%.3f,%.3f).\n",
START_POINT (s).x, START_POINT (s).y,
END_POINT (s).x, END_POINT (s).y);
else if (SPLINE_DEGREE (s) == CUBIC)
fprintf (f, "(%.3f,%.3f)..ctrls(%.3f,%.3f)&(%.3f,%.3f)..(%.3f,%.3f).\n",
START_POINT (s).x, START_POINT (s).y,
CONTROL1 (s).x, CONTROL1 (s).y,
CONTROL2 (s).x, CONTROL2 (s).y,
END_POINT (s).x, END_POINT (s).y);
else
{
/* FATAL1 ("print_spline: strange degree (%d)", SPLINE_DEGREE (s)); */
}
}
/* Evaluate the spline S at a given T value. This is an implementation
of de Casteljau's algorithm. See Schneider's thesis (reference in
../limn/README), p.37. The variable names are taken from there. */
real_coordinate_type
evaluate_spline (spline_type s, real t)
{
spline_type V[4]; /* We need degree+1 splines, but assert degree <= 3. */
unsigned i, j;
real one_minus_t = 1.0 - t;
polynomial_degree degree = SPLINE_DEGREE (s);
for (i = 0; i <= degree; i++)
V[0].v[i] = s.v[i];
for (j = 1; j <= degree; j++)
for (i = 0; i <= degree - j; i++)
{
real_coordinate_type t1 = Pmult_scalar (V[j - 1].v[i], one_minus_t);
real_coordinate_type t2 = Pmult_scalar (V[j - 1].v[i + 1], t);
V[j].v[i] = Padd (t1, t2);
}
return V[degree].v[0];
}
/* Return a new, empty, spline list. */
spline_list_type *
new_spline_list ()
{
spline_list_type *answer = malloc (sizeof (spline_list_type));
SPLINE_LIST_DATA (*answer) = NULL;
SPLINE_LIST_LENGTH (*answer) = 0;
return answer;
}
/* Return a new spline list with SPLINE as the first element. */
spline_list_type *
init_spline_list (spline_type spline)
{
spline_list_type *answer = malloc (sizeof (spline_list_type));
SPLINE_LIST_DATA (*answer) = malloc (sizeof (spline_type));
SPLINE_LIST_ELT (*answer, 0) = spline;
SPLINE_LIST_LENGTH (*answer) = 1;
return answer;
}
/* Free the storage in a spline list. We don't have to free the
elements, since they are arrays in automatic storage. And we don't
want to free the list if it was empty. */
void
free_spline_list (spline_list_type *spline_list)
{
if (SPLINE_LIST_DATA (*spline_list) != NULL)
safe_free ((address *) &(SPLINE_LIST_DATA (*spline_list)));
}
/* Append the spline S to the list SPLINE_LIST. */
void
append_spline (spline_list_type *l, spline_type s)
{
assert (l != NULL);
SPLINE_LIST_LENGTH (*l)++;
SPLINE_LIST_DATA (*l) = realloc (SPLINE_LIST_DATA (*l),
SPLINE_LIST_LENGTH (*l) * sizeof (spline_type));
LAST_SPLINE_LIST_ELT (*l) = s;
}
/* Tack the elements in the list S2 onto the end of S1.
S2 is not changed. */
void
concat_spline_lists (spline_list_type *s1, spline_list_type s2)
{
unsigned this_spline;
unsigned new_length;
assert (s1 != NULL);
new_length = SPLINE_LIST_LENGTH (*s1) + SPLINE_LIST_LENGTH (s2);
SPLINE_LIST_DATA (*s1) = realloc(SPLINE_LIST_DATA (*s1),new_length * sizeof(spline_type));
for (this_spline = 0; this_spline < SPLINE_LIST_LENGTH (s2); this_spline++)
SPLINE_LIST_ELT (*s1, SPLINE_LIST_LENGTH (*s1)++)
= SPLINE_LIST_ELT (s2, this_spline);
}
/* Return a new, empty, spline list array. */
spline_list_array_type
new_spline_list_array ()
{
spline_list_array_type answer;
SPLINE_LIST_ARRAY_DATA (answer) = NULL;
SPLINE_LIST_ARRAY_LENGTH (answer) = 0;
return answer;
}
/* Free the storage in a spline list array. We don't
want to free the list if it is empty. */
void
free_spline_list_array (spline_list_array_type *spline_list_array)
{
unsigned this_list;
for (this_list = 0;
this_list < SPLINE_LIST_ARRAY_LENGTH (*spline_list_array);
this_list++)
free_spline_list (&SPLINE_LIST_ARRAY_ELT (*spline_list_array, this_list));
if (SPLINE_LIST_ARRAY_DATA (*spline_list_array) != NULL)
safe_free ((address *) &(SPLINE_LIST_ARRAY_DATA (*spline_list_array)));
}
/* Append the spline S to the list SPLINE_LIST_ARRAY. */
void
append_spline_list (spline_list_array_type *l, spline_list_type s)
{
SPLINE_LIST_ARRAY_LENGTH (*l)++;
SPLINE_LIST_ARRAY_DATA (*l) = realloc(SPLINE_LIST_ARRAY_DATA (*l),(SPLINE_LIST_ARRAY_LENGTH (*l))*sizeof(spline_list_type));
LAST_SPLINE_LIST_ARRAY_ELT (*l) = s;
}

124
plug-ins/sel2path/spline.h Normal file
View File

@ -0,0 +1,124 @@
/* spline.h: manipulate the spline representation.
Copyright (C) 1992 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifndef SPLINE_H
#define SPLINE_H
#include <stdio.h>
#include "bounding-box.h"
#include "types.h"
/* Third degree is the highest we deal with. */
typedef enum
{
LINEAR = 1, QUADRATIC = 2, CUBIC = 3
} polynomial_degree;
/* A Bezier spline can be represented as four points in the real plane:
a starting point, ending point, and two control points. The
curve always lies in the convex hull defined by the four points. It
is also convenient to save the divergence of the spline from the
straight line defined by the endpoints. */
typedef struct
{
real_coordinate_type v[4]; /* The control points. */
polynomial_degree degree;
real linearity;
} spline_type;
#define START_POINT(spl) ((spl).v[0])
#define CONTROL1(spl) ((spl).v[1])
#define CONTROL2(spl) ((spl).v[2])
#define END_POINT(spl) ((spl).v[3])
#define SPLINE_DEGREE(spl) ((spl).degree)
#define SPLINE_LINEARITY(spl) ((spl).linearity)
/* Return a spline structure. */
extern spline_type new_spline (void);
/* Print a spline on the given file. */
extern void print_spline (FILE *, spline_type);
/* Evaluate SPLINE at the given T value. */
extern real_coordinate_type evaluate_spline (spline_type spline, real t);
/* Each outline in a character is typically represented by many
splines. So, here is a list structure for that: */
typedef struct
{
spline_type *data;
unsigned length;
} spline_list_type;
/* An empty list will have length zero (and null data). */
#define SPLINE_LIST_LENGTH(s_l) ((s_l).length)
/* The address of the beginning of the array of data. */
#define SPLINE_LIST_DATA(s_l) ((s_l).data)
/* The element INDEX in S_L. */
#define SPLINE_LIST_ELT(s_l, index) (SPLINE_LIST_DATA (s_l)[index])
/* The last element in S_L. */
#define LAST_SPLINE_LIST_ELT(s_l) \
(SPLINE_LIST_DATA (s_l)[SPLINE_LIST_LENGTH (s_l) - 1])
/* The previous and next elements to INDEX in S_L. */
#define NEXT_SPLINE_LIST_ELT(s_l, index) \
SPLINE_LIST_ELT (s_l, ((index) + 1) % SPLINE_LIST_LENGTH (s_l))
#define PREV_SPLINE_LIST_ELT(s_l, index) \
SPLINE_LIST_ELT (s_l, index == 0 \
? SPLINE_LIST_LENGTH (s_l) - 1 \
: index - 1)
/* Construct and destroy new `spline_list_type' objects. */
extern spline_list_type *new_spline_list (void);
extern spline_list_type *init_spline_list (spline_type);
extern void free_spline_list (spline_list_type *);
/* Append the spline S to the list S_LIST. */
extern void append_spline (spline_list_type *s_list, spline_type s);
/* Append the elements in list S2 to S1, changing S1. */
extern void concat_spline_lists (spline_list_type *s1, spline_list_type s2);
/* Each character is in general made up of many outlines. So here is one
more list structure. */
typedef struct
{
spline_list_type *data;
unsigned length;
} spline_list_array_type;
/* Turns out we can use the same definitions for lists of lists as for
just lists. But we define the usual names, just in case. */
#define SPLINE_LIST_ARRAY_LENGTH SPLINE_LIST_LENGTH
#define SPLINE_LIST_ARRAY_DATA SPLINE_LIST_DATA
#define SPLINE_LIST_ARRAY_ELT SPLINE_LIST_ELT
#define LAST_SPLINE_LIST_ARRAY_ELT LAST_SPLINE_LIST_ELT
/* The usual routines. */
extern spline_list_array_type new_spline_list_array (void);
extern void free_spline_list_array (spline_list_array_type *);
extern void append_spline_list (spline_list_array_type *, spline_list_type);
#endif /* not SPLINE_H */

147
plug-ins/sel2path/types.h Normal file
View File

@ -0,0 +1,147 @@
/* types.h: general types.
Copyright (C) 1992 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifndef TYPES_H
#define TYPES_H
/* Booleans. */
typedef enum { false = 0, true = 1 } boolean;
/* The X11 library defines `FALSE' and `TRUE', and so we only want to
define them if necessary. */
#ifndef FALSE
#define FALSE false
#define TRUE true
#endif /* FALSE */
/* The usual null-terminated string. */
typedef char *string;
/* A generic pointer in ANSI C. */
typedef void *address;
/* We use `real' for our floating-point variables. */
typedef double real;
/* A character code. Perhaps someday we will allow for 16-bit
character codes, but for now we are restricted to 256 characters per
font (like TeX and PostScript). */
typedef unsigned char charcode_type;
/* Used in file formats. */
typedef unsigned char one_byte;
typedef signed char signed_byte;
typedef unsigned short two_bytes;
typedef short signed_2_bytes;
typedef unsigned int four_bytes;
typedef int signed_4_bytes;
typedef int byte_count_type;
/* These are intended to be used for output in file formats where a
``byte'' is defined to be eight bits, regardless of the hardware. */
#define ONE_BYTE_BIG (1 << 8)
#define TWO_BYTES_BIG (1 << 16)
#define THREE_BYTES_BIG (1 << 24)
/* Complex numbers. */
typedef struct
{
real real;
real imag;
} complex;
typedef enum { first_complex_part, second_complex_part} complex_part_type;
typedef enum { polar_rep, rectangular_rep} complex_rep_type;
/* Dimensions of a rectangle. */
typedef struct
{
unsigned height, width;
} dimensions_type;
#define DIMENSIONS_HEIGHT(d) ((d).height)
#define DIMENSIONS_WIDTH(d) ((d).width)
/* Cartesian points. */
typedef struct
{
int x, y;
} coordinate_type;
typedef struct
{
double x, y;
} real_coordinate_type;
#if 0
typedef struct
{
double align_threshold;
double corner_always_threshold;
unsigned int corner_surround;
double corner_threshold;
double error_threshold;
unsigned int filter_alternative_surround;
double filter_epsilon;
unsigned int filter_iteration_count;
double filter_percent;
unsigned int filter_secondary_surround;
unsigned int filter_surround;
boolean keep_knees;
double line_reversion_threshold;
double line_threshold;
double reparameterize_improvement;
double reparameterize_threshold;
double subdivide_search;
unsigned int subdivide_surround;
double subdivide_threshold;
unsigned int tangent_surround;
} SELVALS;
#else
typedef struct
{
double align_threshold;
double corner_always_threshold;
double corner_surround;
double corner_threshold;
double error_threshold;
double filter_alternative_surround;
double filter_epsilon;
double filter_iteration_count;
double filter_percent;
double filter_secondary_surround;
double filter_surround;
boolean keep_knees;
double line_reversion_threshold;
double line_threshold;
double reparameterize_improvement;
double reparameterize_threshold;
double subdivide_search;
double subdivide_surround;
double subdivide_threshold;
double tangent_surround;
} SELVALS;
#endif /* 1 */
#endif /* not TYPES_H */

245
plug-ins/sel2path/vector.c Normal file
View File

@ -0,0 +1,245 @@
/* vector.c: vector/point operations.
Copyright (C) 1992 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <math.h>
#include <assert.h>
#include "global.h"
#include "config.h"
#include "vector.h"
/* Given the point COORD, return the corresponding vector. */
const vector_type
make_vector (const real_coordinate_type c)
{
vector_type v;
v.dx = c.x;
v.dy = c.y;
return v;
}
/* And the converse: given a vector, return the corresponding point. */
const real_coordinate_type
vector_to_point (const vector_type v)
{
real_coordinate_type coord;
coord.x = v.dx;
coord.y = v.dy;
return coord;
}
const real
magnitude (const vector_type v)
{
return hypot (v.dx, v.dy);
}
const vector_type
normalize (const vector_type v)
{
vector_type new_v;
real m = magnitude (v);
assert (m > 0.0);
new_v.dx = v.dx / m;
new_v.dy = v.dy / m;
return new_v;
}
const vector_type
Vadd (const vector_type v1, const vector_type v2)
{
vector_type new_v;
new_v.dx = v1.dx + v2.dx;
new_v.dy = v1.dy + v2.dy;
return new_v;
}
const real
Vdot (const vector_type v1, const vector_type v2)
{
return v1.dx * v2.dx + v1.dy * v2.dy;
}
const vector_type
Vmult_scalar (const vector_type v, const real r)
{
vector_type new_v;
new_v.dx = v.dx * r;
new_v.dy = v.dy * r;
return new_v;
}
/* Given the IN_VECTOR and OUT_VECTOR, return the angle between them in
degrees, in the range zero to 180. */
const real
Vangle (const vector_type in_vector, const vector_type out_vector)
{
vector_type v1 = normalize (in_vector);
vector_type v2 = normalize (out_vector);
return acosd (Vdot (v2, v1));
}
const real_coordinate_type
Vadd_point (const real_coordinate_type c, const vector_type v)
{
real_coordinate_type new_c;
new_c.x = c.x + v.dx;
new_c.y = c.y + v.dy;
return new_c;
}
const real_coordinate_type
Vsubtract_point (const real_coordinate_type c, const vector_type v)
{
real_coordinate_type new_c;
new_c.x = c.x - v.dx;
new_c.y = c.y - v.dy;
return new_c;
}
const coordinate_type
Vadd_int_point (const coordinate_type c, const vector_type v)
{
coordinate_type a;
a.x = ROUND ((real) c.x + v.dx);
a.y = ROUND ((real) c.y + v.dy);
return a;
}
const vector_type
Vabs (const vector_type v)
{
vector_type new_v;
new_v.dx = fabs (v.dx);
new_v.dy = fabs (v.dy);
return new_v;
}
/* Operations on points. */
const vector_type
Psubtract (const real_coordinate_type c1, const real_coordinate_type c2)
{
vector_type v;
v.dx = c1.x - c2.x;
v.dy = c1.y - c2.y;
return v;
}
/* Operations on integer points. */
const vector_type
IPsubtract (const coordinate_type coord1, const coordinate_type coord2)
{
vector_type v;
v.dx = coord1.x - coord2.x;
v.dy = coord1.y - coord2.y;
return v;
}
const coordinate_type
IPsubtractP (const coordinate_type c1, const coordinate_type c2)
{
coordinate_type c;
c.x = c1.x - c2.x;
c.y = c1.y - c2.y;
return c;
}
const coordinate_type
IPadd (const coordinate_type c1, const coordinate_type c2)
{
coordinate_type c;
c.x = c1.x + c2.x;
c.y = c1.y + c2.y;
return c;
}
const coordinate_type
IPmult_scalar (const coordinate_type c, const int i)
{
coordinate_type a;
a.x = c.x * i;
a.y = c.y * i;
return a;
}
const real_coordinate_type
IPmult_real (const coordinate_type c, const real r)
{
real_coordinate_type a;
a.x = c.x * r;
a.y = c.y * r;
return a;
}
const boolean
IPequal (const coordinate_type c1, const coordinate_type c2)
{
return c1.x == c2.x && c1.y == c2.y;
}

View File

@ -0,0 +1,95 @@
/* vector.h: operations on vectors and points.
Copyright (C) 1992 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifndef VECTOR_H
#define VECTOR_H
#include "types.h"
/* Our vectors are represented as displacements along the x and y axes. */
typedef struct
{
real dx, dy;
} vector_type;
/* Consider a point as a vector from the origin. */
extern const vector_type make_vector (const real_coordinate_type);
/* And a vector as a point, i.e., a displacement from the origin. */
extern const real_coordinate_type vector_to_point (const vector_type);
/* Definitions for these common operations can be found in any decent
linear algebra book, and most calculus books. */
extern const real magnitude (const vector_type);
extern const vector_type normalize (const vector_type);
extern const vector_type Vadd (const vector_type, const vector_type);
extern const real Vdot (const vector_type, const vector_type);
extern const vector_type Vmult_scalar (const vector_type, const real);
extern const real Vangle (const vector_type in, const vector_type out);
/* These operations could have been named `P..._vector' just as well as
V..._point, so we may as well allow both names. */
#define Padd_vector Vadd_point
extern const real_coordinate_type Vadd_point
(const real_coordinate_type, const vector_type);
#define Psubtract_vector Vsubtract_point
extern const real_coordinate_type Vsubtract_point
(const real_coordinate_type, const vector_type);
/* This returns the rounded sum. */
#define IPadd_vector Vadd_int_point
extern const coordinate_type Vadd_int_point
(const coordinate_type, const vector_type);
/* Take the absolute value of both components. */
extern const vector_type Vabs (const vector_type);
/* Operations on points with real coordinates. It is not orthogonal,
but more convenient, to have the subtraction operator return a
vector, and the addition operator return a point. */
extern const vector_type Psubtract
(const real_coordinate_type, const real_coordinate_type);
/* These are heavily used in spline fitting, so we define them as macros
instead of functions. */
#define Padd(rc1, rc2) \
((real_coordinate_type) { (rc1).x + (rc2).x, (rc1).y + (rc2).y })
#define Pmult_scalar(rc, r) \
((real_coordinate_type) { (rc).x * (r), (rc).y * (r) })
/* Similarly, for points with integer coordinates; here, a subtraction
operator that does return another point is useful. */
extern const vector_type IPsubtract
(const coordinate_type, const coordinate_type);
extern const coordinate_type IPsubtractP
(const coordinate_type, const coordinate_type);
extern const coordinate_type IPadd
(const coordinate_type, const coordinate_type);
extern const coordinate_type IPmult_scalar (const coordinate_type, const int);
extern const real_coordinate_type IPmult_real
(const coordinate_type, const real);
extern const boolean IPequal (const coordinate_type, const coordinate_type);
#endif /* not VECTOR_H */