mirror of https://github.com/GNOME/gimp.git
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:
parent
b13dd4fd69
commit
8beb7c582c
15
ChangeLog
15
ChangeLog
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/* XPM */
|
||||
static char * topath_xpm[] = {
|
||||
"18 18 5 1",
|
||||
" c None",
|
||||
". c #000000",
|
||||
"+ c #BCBABC",
|
||||
"@ c #DEDADE",
|
||||
"# c #FFFFFF",
|
||||
" .. ..",
|
||||
" ............... ",
|
||||
" .. ... ..",
|
||||
" +@@#. ",
|
||||
" #.@@@@@.# ",
|
||||
" .+@@@@@@@++ ",
|
||||
" #@@@@@@@@@. ",
|
||||
" .@@@@@@@@@@@# ",
|
||||
" +@@@@@@@@@@@+ ",
|
||||
" #@@@@@@@@@@@. ",
|
||||
" .@@@@@@@@@@@+ ",
|
||||
" +@@@@@@@@@@@# ",
|
||||
" .@@@@@@@@@+ ",
|
||||
" ++@@@@@@@.# ",
|
||||
" #.@@@@@#+ ",
|
||||
" .+#.+ ",
|
||||
" . ... . ",
|
||||
" ................."};
|
|
@ -31,6 +31,7 @@ SUBDIRS = \
|
|||
print \
|
||||
rcm \
|
||||
sgi \
|
||||
sel2path \
|
||||
sinus \
|
||||
struc \
|
||||
unsharp \
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
Makefile.in
|
||||
Makefile
|
||||
.deps
|
||||
_libs
|
||||
.libs
|
||||
sel2path
|
|
@ -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
|
|
@ -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.
|
|
@ -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.)
|
|
@ -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 */
|
||||
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
File diff suppressed because it is too large
Load Diff
|
@ -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 */
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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 *);
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
Loading…
Reference in New Issue