Initial checkin of Pika from heckimp
This commit is contained in:
51
plug-ins/selection-to-path/README
Normal file
51
plug-ins/selection-to-path/README
Normal file
@ -0,0 +1,51 @@
|
||||
|
||||
|
||||
Andy Thomas (alt@gimp.org) 9th July 1999
|
||||
|
||||
|
||||
This plug-in will take a selection and convert it into a path.
|
||||
For the purpose of the plug-in the selection boundary is defined
|
||||
in a similar manner to that worked out for the "marching ants" markers
|
||||
of the selection. I think this gives the best user feel/feedback since
|
||||
the created path "follows" the "marching ants".
|
||||
|
||||
I cannot claim responsibility for the underlying algorithms. These
|
||||
were taken directly from the GNU font utilities (the "limn" program
|
||||
in particular) written by Karl Berry and Kathryn Hargreaves.
|
||||
|
||||
Their email addresses quoted in the README are:-
|
||||
|
||||
Karl Berry karl@cs.umb.edu
|
||||
Kathryn Hargreaves letters@cs.umb.edu
|
||||
|
||||
Please see fontutils-0.6 package for more details. I have included the
|
||||
README from the limn part of the package.
|
||||
|
||||
I thank Karl & Kathryn for producing such a well written set of utilities.
|
||||
|
||||
I have just added a pika 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.
|
||||
WARNING:- 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 pika-developer mailing list. Thank.
|
56
plug-ins/selection-to-path/README.limn
Normal file
56
plug-ins/selection-to-path/README.limn
Normal file
@ -0,0 +1,56 @@
|
||||
This program converts bitmap fonts to a homegrown outline format, bezier
|
||||
(BZR). The program `bzrto' converts that format to something usable for
|
||||
typesetting.
|
||||
|
||||
We used two main sources in writing the program:
|
||||
|
||||
@mastersthesis{Schneider:PIC-88,
|
||||
author = "Philip J. Schneider",
|
||||
title = "Phoenix: An Interactive Curve Design System Based on the
|
||||
Automatic Fitting of Hand-Sketched Curves",
|
||||
school = inst-u-wash,
|
||||
year = 1988,
|
||||
}
|
||||
|
||||
@article{Plass:CG-17-229,
|
||||
author = "Michael Plass and Maureen Stone",
|
||||
title = "Curve-fitting with Piecewise Parametric Cubics",
|
||||
journal = j-comp-graphics,
|
||||
year = 1983,
|
||||
volume = 17,
|
||||
number = 3,
|
||||
month = jul,
|
||||
pages = "229-239",
|
||||
}
|
||||
|
||||
We had access to the code for Phoenix, thanks to Philip, but none of our
|
||||
code is based on his (mostly because his task was allow interactive
|
||||
sketching, and ours to fit bitmap characters, and the two require
|
||||
different data structures). The general outline of the fitting
|
||||
algorithm does come from Phoenix.
|
||||
|
||||
We also found this article helpful:
|
||||
|
||||
@Inproceedings{Gonczarowski:RIDT91-1,
|
||||
author = "Jakob Gonczarowski",
|
||||
title = "A Fast Approach to Auto-tracing (with Parametric
|
||||
Cubics)",
|
||||
pages = "1--15",
|
||||
crossref = "Morris:RIDT91",
|
||||
acknowledgement = ack-kb,
|
||||
}
|
||||
|
||||
@String{proc-RIDT91 = "Raster Imaging and Digital Typography II"}
|
||||
|
||||
@Proceedings{Morris:RIDT91,
|
||||
title = proc-RIDT91,
|
||||
booktitle = proc-RIDT91,
|
||||
year = "1991",
|
||||
editor = "Robert A. Morris and Jacques Andr{\'e}",
|
||||
publisher = pub-CUP,
|
||||
address = pub-CUP:adr,
|
||||
acknowledgement = ack-kb,
|
||||
}
|
||||
|
||||
(These BibTeX entries are from the type.bib and ep.bib files on
|
||||
math.utah.edu:pub/tex/bib.)
|
112
plug-ins/selection-to-path/bitmap.h
Normal file
112
plug-ins/selection-to-path/bitmap.h
Normal file
@ -0,0 +1,112 @@
|
||||
/* bitmap.h: definition for a bitmap type. No packing is done by
|
||||
* default; each pixel is represented by an entire byte. Among other
|
||||
* things, this means the type can be used for both grayscale and binary
|
||||
* images.
|
||||
*
|
||||
* Copyright (C) 1992 Free Software Foundation, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3, 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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 getting 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 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 */
|
||||
|
63
plug-ins/selection-to-path/bounding-box.h
Normal file
63
plug-ins/selection-to-path/bounding-box.h
Normal file
@ -0,0 +1,63 @@
|
||||
/* bounding-box.h: operations on both real- and integer-valued bounding boxes.
|
||||
*
|
||||
* Copyright (C) 1992 Free Software Foundation, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3, 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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 asymmetrically. */
|
||||
#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 bounding_box_type dimensions_to_bb (dimensions_type);
|
||||
extern 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 */
|
184
plug-ins/selection-to-path/curve.c
Normal file
184
plug-ins/selection-to-path/curve.c
Normal file
@ -0,0 +1,184 @@
|
||||
/* 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 3, 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "global.h"
|
||||
#include "curve.h"
|
||||
|
||||
|
||||
/* Return an entirely empty curve. */
|
||||
|
||||
curve_type
|
||||
new_curve (void)
|
||||
{
|
||||
curve_type curve = g_new (struct curve, 1);
|
||||
|
||||
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 = g_new (point_type, 1);
|
||||
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 = g_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 (void)
|
||||
{
|
||||
curve_list_type curve_list;
|
||||
|
||||
curve_list.length = 0;
|
||||
curve_list.data = NULL;
|
||||
curve_list.clockwise = FALSE;
|
||||
|
||||
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 = g_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 (void)
|
||||
{
|
||||
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 = g_realloc (l->data,( CURVE_LIST_ARRAY_LENGTH (*l))*sizeof(curve_list_type));
|
||||
LAST_CURVE_LIST_ARRAY_ELT (*l) = curve_list;
|
||||
}
|
157
plug-ins/selection-to-path/curve.h
Normal file
157
plug-ins/selection-to-path/curve.h
Normal file
@ -0,0 +1,157 @@
|
||||
/* curve.h: data structures for the conversion from pixels to splines.
|
||||
*
|
||||
* Copyright (C) 1992 Free Software Foundation, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3, 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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 */
|
268
plug-ins/selection-to-path/edge.c
Normal file
268
plug-ins/selection-to-path/edge.c
Normal file
@ -0,0 +1,268 @@
|
||||
/* 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 3, 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "selection-to-path.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. */
|
||||
|
||||
static boolean
|
||||
is_outline_edge (edge_type edge,
|
||||
unsigned row, unsigned col)
|
||||
{
|
||||
/* If this pixel isn't black, it's not part of the outline. */
|
||||
if (sel_pixel_is_white(row, col))
|
||||
return false;
|
||||
|
||||
switch (edge)
|
||||
{
|
||||
case left:
|
||||
return col == 0 || sel_pixel_is_white(row, col - 1);
|
||||
|
||||
case top:
|
||||
return row == 0 || sel_pixel_is_white(row - 1, col);
|
||||
|
||||
case right:
|
||||
return (col == sel_get_width() - 1)
|
||||
|| sel_pixel_is_white(row, col + 1);
|
||||
|
||||
case bottom:
|
||||
return (row == sel_get_height() - 1)
|
||||
|| sel_pixel_is_white(row + 1, col);
|
||||
|
||||
case no_edge:
|
||||
default:
|
||||
printf ("is_outline_edge: Bad edge value(%d)", edge);
|
||||
}
|
||||
|
||||
return 0; /* NOTREACHED */
|
||||
}
|
||||
|
||||
/* If EDGE is not already marked, we mark it; otherwise, it's a fatal error.
|
||||
The position ROW and COL should be inside the bitmap MARKED. EDGE can
|
||||
be `no_edge'; we just return false. */
|
||||
|
||||
void
|
||||
mark_edge (edge_type edge, unsigned row, unsigned col, bitmap_type *marked)
|
||||
{
|
||||
/* printf("row = %d, col = %d \n",row,col); */
|
||||
assert (!is_marked_edge (edge, row, col, *marked));
|
||||
|
||||
if (edge != no_edge)
|
||||
BITMAP_PIXEL (*marked, row, col) |= 1 << edge;
|
||||
}
|
||||
|
||||
|
||||
/* Test if the edge EDGE at ROW/COL in MARKED is marked. */
|
||||
|
||||
static boolean
|
||||
is_marked_edge (edge_type edge, unsigned row, unsigned col, bitmap_type marked)
|
||||
{
|
||||
return
|
||||
edge == no_edge ? false : BITMAP_PIXEL (marked, row, col) & (1 << edge);
|
||||
}
|
||||
|
||||
|
||||
/* Return the edge which is counterclockwise-adjacent to EDGE. This
|
||||
code makes use of the ``numericness'' of C enumeration constants;
|
||||
sorry about that. */
|
||||
|
||||
#define NUM_EDGES no_edge
|
||||
|
||||
static edge_type
|
||||
next_edge (edge_type edge)
|
||||
{
|
||||
return edge == no_edge ? edge : (edge + 1) % NUM_EDGES;
|
||||
}
|
59
plug-ins/selection-to-path/edge.h
Normal file
59
plug-ins/selection-to-path/edge.h
Normal file
@ -0,0 +1,59 @@
|
||||
/* edge.h: declarations for edge traversing.
|
||||
*
|
||||
* Copyright (C) 1992 Free Software Foundation, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3, 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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 */
|
1967
plug-ins/selection-to-path/fit.c
Normal file
1967
plug-ins/selection-to-path/fit.c
Normal file
File diff suppressed because it is too large
Load Diff
54
plug-ins/selection-to-path/fit.h
Normal file
54
plug-ins/selection-to-path/fit.h
Normal file
@ -0,0 +1,54 @@
|
||||
/* fit.h: convert the pixel representation to splines.
|
||||
*
|
||||
* Copyright (C) 1992 Free Software Foundation, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3, 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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 */
|
213
plug-ins/selection-to-path/global.h
Normal file
213
plug-ins/selection-to-path/global.h
Normal file
@ -0,0 +1,213 @@
|
||||
/* 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 3, 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLOBAL_H
|
||||
#define GLOBAL_H
|
||||
|
||||
#include <stdlib.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(x) \
|
||||
START_FATAL (); fprintf (stderr, "%s", x); 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(x) \
|
||||
START_WARNING (); fprintf (stderr, "%s", x); 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 SIGN(x) ((x) > 0 ? 1 : (x) < 0 ? -1 : 0)
|
||||
#define SROUND(x) ((int) ((int) (x) + .5 * SIGN (x)))
|
||||
|
||||
#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. */
|
||||
|
||||
#if 0 /* These aren't actually defined anywhere */
|
||||
/* 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);
|
||||
|
||||
#endif
|
||||
|
||||
/* 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 boolean epsilon_equal (real v1, real v2);
|
||||
|
||||
/* Arc cosine, in degrees. */
|
||||
extern real my_acosd (real);
|
||||
|
||||
/* Return the Euclidean distance between the two points. */
|
||||
extern real distance (real_coordinate_type, real_coordinate_type);
|
||||
extern real int_distance (coordinate_type, coordinate_type);
|
||||
|
||||
/* Slope between two points (delta y per unit x). */
|
||||
extern real slope (real_coordinate_type, real_coordinate_type);
|
||||
|
||||
/* Make a real coordinate from an integer one, and vice versa. */
|
||||
extern real_coordinate_type int_to_real_coord (coordinate_type);
|
||||
extern coordinate_type real_to_int_coord (real_coordinate_type);
|
||||
|
||||
/* Test if two integer points are adjacent. */
|
||||
extern 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 possibilities: 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 */
|
177
plug-ins/selection-to-path/math.c
Normal file
177
plug-ins/selection-to-path/math.c
Normal file
@ -0,0 +1,177 @@
|
||||
/* 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 3, 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "libpika/pika.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. */
|
||||
|
||||
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. */
|
||||
|
||||
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. */
|
||||
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. */
|
||||
|
||||
real
|
||||
my_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 / G_PI;
|
||||
}
|
||||
|
||||
|
||||
/* The slope of the line defined by COORD1 and COORD2. */
|
||||
|
||||
real
|
||||
slope (real_coordinate_type coord1, real_coordinate_type coord2)
|
||||
{
|
||||
g_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. */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
coordinate_type
|
||||
real_to_int_coord (real_coordinate_type real_coord)
|
||||
{
|
||||
coordinate_type int_coord;
|
||||
|
||||
int_coord.x = SROUND (real_coord.x);
|
||||
int_coord.y = SROUND (real_coord.y);
|
||||
|
||||
return int_coord;
|
||||
}
|
||||
|
||||
|
||||
/* See if two points (described by their row and column) are adjacent. */
|
||||
|
||||
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 = g_new (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;
|
||||
}
|
39
plug-ins/selection-to-path/meson.build
Normal file
39
plug-ins/selection-to-path/meson.build
Normal file
@ -0,0 +1,39 @@
|
||||
plugin_name = 'selection-to-path'
|
||||
|
||||
plugin_sources = [
|
||||
'curve.c',
|
||||
'edge.c',
|
||||
'fit.c',
|
||||
'math.c',
|
||||
'pxl-outline.c',
|
||||
'selection-to-path-dialog.c',
|
||||
'selection-to-path.c',
|
||||
'spline.c',
|
||||
'vector.c',
|
||||
]
|
||||
|
||||
if platform_windows
|
||||
plugin_sources += windows.compile_resources(
|
||||
pika_plugins_rc,
|
||||
args: [
|
||||
'--define', 'ORIGINALFILENAME_STR="@0@"'.format(plugin_name+'.exe'),
|
||||
'--define', 'INTERNALNAME_STR="@0@"' .format(plugin_name),
|
||||
'--define', 'TOP_SRCDIR="@0@"' .format(meson.project_source_root()),
|
||||
],
|
||||
include_directories: [
|
||||
rootInclude, appInclude,
|
||||
],
|
||||
)
|
||||
endif
|
||||
|
||||
plugin_selection_to_path_deps = [
|
||||
libpikaui_dep,
|
||||
math,
|
||||
]
|
||||
|
||||
selection_to_path = executable(plugin_name,
|
||||
plugin_sources,
|
||||
dependencies: plugin_selection_to_path_deps,
|
||||
install: true,
|
||||
install_dir: pikaplugindir / 'plug-ins' / plugin_name,
|
||||
)
|
254
plug-ins/selection-to-path/pxl-outline.c
Normal file
254
plug-ins/selection-to-path/pxl-outline.c
Normal file
@ -0,0 +1,254 @@
|
||||
/* 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 3, 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "global.h"
|
||||
#include "selection-to-path.h"
|
||||
#include "bitmap.h"
|
||||
#include "edge.h"
|
||||
#include "pxl-outline.h"
|
||||
|
||||
#include "libpika/stdplugins-intl.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) = g_new0 (one_byte, size); /* g_new returns NULL if size == 0 */
|
||||
|
||||
/* 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 (void)
|
||||
{
|
||||
pixel_outline_list_type outline_list;
|
||||
unsigned row, col;
|
||||
gint height;
|
||||
gint width;
|
||||
bitmap_type marked = local_new_bitmap (sel_get_width(),sel_get_height());
|
||||
|
||||
/* printf("width = %d, height = %d\n",BITMAP_WIDTH(marked),BITMAP_HEIGHT(marked)); */
|
||||
|
||||
pika_progress_init (_("Selection to Path"));
|
||||
|
||||
O_LIST_LENGTH (outline_list) = 0;
|
||||
outline_list.data = NULL;
|
||||
|
||||
height = sel_get_height ();
|
||||
width = sel_get_width ();
|
||||
|
||||
for (row = 0; row < height; row++)
|
||||
{
|
||||
for (col = 0; col < 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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if ((row & 0xf) == 0)
|
||||
pika_progress_update (((gdouble)row) / height);
|
||||
}
|
||||
|
||||
pika_progress_update (1.0);
|
||||
|
||||
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 *)g_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. */
|
||||
|
||||
|
||||
static pixel_outline_type
|
||||
new_pixel_outline (void)
|
||||
{
|
||||
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 *)g_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. */
|
||||
|
||||
static void
|
||||
append_coordinate (pixel_outline_type *o, int x, int y, edge_type edge)
|
||||
{
|
||||
coordinate_type c;
|
||||
|
||||
c.x = x;
|
||||
c.y = y;
|
||||
|
||||
switch (edge)
|
||||
{
|
||||
case top:
|
||||
c.y++;
|
||||
break;
|
||||
|
||||
case right:
|
||||
c.x++;
|
||||
c.y++;
|
||||
break;
|
||||
|
||||
case bottom:
|
||||
c.x++;
|
||||
break;
|
||||
|
||||
case left:
|
||||
break;
|
||||
|
||||
default:
|
||||
g_printerr ("append_coordinate: Bad edge (%d)", edge);
|
||||
}
|
||||
|
||||
append_outline_pixel (o, c);
|
||||
}
|
68
plug-ins/selection-to-path/pxl-outline.h
Normal file
68
plug-ins/selection-to-path/pxl-outline.h
Normal file
@ -0,0 +1,68 @@
|
||||
/* 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 3, 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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 the next 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 (void);
|
||||
|
||||
/* Free the memory in the list. */
|
||||
extern void free_pixel_outline_list (pixel_outline_list_type *);
|
||||
|
||||
#endif /* not PXL_OUTLINE_H */
|
387
plug-ins/selection-to-path/selection-to-path-dialog.c
Normal file
387
plug-ins/selection-to-path/selection-to-path-dialog.c
Normal file
@ -0,0 +1,387 @@
|
||||
/*
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This is a plug-in for PIKA.
|
||||
*
|
||||
* 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Change log:-
|
||||
* 0.1 First version.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#include "selection-to-path.h"
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
static GSList * adjust_widgets = NULL;
|
||||
|
||||
void scale_entry_update_double (PikaLabelSpin *entry,
|
||||
gdouble *value);
|
||||
|
||||
/* Reset to recommended defaults */
|
||||
void
|
||||
reset_adv_dialog (void)
|
||||
{
|
||||
GSList *list;
|
||||
GObject *widget;
|
||||
gdouble *value;
|
||||
|
||||
for (list = adjust_widgets; list; list = g_slist_next (list))
|
||||
{
|
||||
widget = G_OBJECT (list->data);
|
||||
value = (gdouble *) g_object_get_data (G_OBJECT (widget),
|
||||
"default_value");
|
||||
|
||||
if (GTK_IS_ADJUSTMENT (widget))
|
||||
{
|
||||
gtk_adjustment_set_value (GTK_ADJUSTMENT (widget),
|
||||
*value);
|
||||
}
|
||||
else if (GTK_IS_TOGGLE_BUTTON (widget))
|
||||
{
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget),
|
||||
(gboolean)(*value));
|
||||
}
|
||||
else if (PIKA_IS_SCALE_ENTRY (widget))
|
||||
{
|
||||
pika_label_spin_set_value (PIKA_LABEL_SPIN (widget),
|
||||
*value);
|
||||
}
|
||||
else
|
||||
g_warning ("Internal widget list error");
|
||||
}
|
||||
}
|
||||
|
||||
static gpointer
|
||||
def_val (gdouble default_value)
|
||||
{
|
||||
gdouble *value = g_new0 (gdouble, 1);
|
||||
*value = default_value;
|
||||
return (value);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
dialog_create_selection_area (SELVALS *sels)
|
||||
{
|
||||
GtkWidget *scrolled_win;
|
||||
GtkWidget *grid;
|
||||
GtkWidget *check;
|
||||
GtkWidget *scale;
|
||||
gint row;
|
||||
|
||||
scrolled_win = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_widget_set_size_request (scrolled_win, -1, 400);
|
||||
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win),
|
||||
GTK_SHADOW_NONE);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
|
||||
GTK_POLICY_NEVER,
|
||||
GTK_POLICY_ALWAYS);
|
||||
gtk_scrolled_window_set_overlay_scrolling (GTK_SCROLLED_WINDOW (scrolled_win),
|
||||
FALSE);
|
||||
|
||||
grid = gtk_grid_new ();
|
||||
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
|
||||
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
|
||||
gtk_container_add (GTK_CONTAINER (scrolled_win), grid);
|
||||
gtk_widget_show (grid);
|
||||
row = 0;
|
||||
|
||||
scale = pika_scale_entry_new (_("Align Threshold:"), sels->align_threshold, 0.2, 2.0, 2);
|
||||
pika_help_set_help_data (scale,
|
||||
_("If two endpoints are closer than this, "
|
||||
"they are made to be equal."), NULL);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (scale_entry_update_double),
|
||||
&sels->align_threshold);
|
||||
adjust_widgets = g_slist_append (adjust_widgets, scale);
|
||||
g_object_set_data (G_OBJECT (scale), "default_value", def_val (0.5));
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
scale = pika_scale_entry_new (_("Corner Always Threshold:"), sels->corner_always_threshold, 30, 180, 2);
|
||||
pika_help_set_help_data (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);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (scale_entry_update_double),
|
||||
&sels->corner_always_threshold);
|
||||
adjust_widgets = g_slist_append (adjust_widgets, scale);
|
||||
g_object_set_data (G_OBJECT (scale), "default_value", def_val (60.0));
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
scale = pika_scale_entry_new (_("Corner Surround:"), sels->corner_surround, 3, 8, 0);
|
||||
pika_help_set_help_data (scale,
|
||||
_("Number of points to consider when determining if a "
|
||||
"point is a corner or not."), NULL);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (scale_entry_update_double),
|
||||
&sels->corner_surround);
|
||||
adjust_widgets = g_slist_append (adjust_widgets, scale);
|
||||
g_object_set_data (G_OBJECT (scale), "default_value", def_val (4.0));
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
scale = pika_scale_entry_new (_("Corner Threshold:"), sels->corner_threshold, 0, 180, 2);
|
||||
pika_help_set_help_data (scale,
|
||||
_("If a point, its predecessors, and its successors "
|
||||
"define an angle smaller than this, it's a corner."),
|
||||
NULL);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (scale_entry_update_double),
|
||||
&sels->corner_threshold);
|
||||
adjust_widgets = g_slist_append (adjust_widgets, scale);
|
||||
g_object_set_data (G_OBJECT (scale), "default_value", def_val (100.0));
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
scale = pika_scale_entry_new (_("Error Threshold:"), sels->error_threshold, 0.2, 10, 2);
|
||||
pika_help_set_help_data (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);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (scale_entry_update_double),
|
||||
&sels->error_threshold);
|
||||
adjust_widgets = g_slist_append (adjust_widgets, scale);
|
||||
g_object_set_data (G_OBJECT (scale), "default_value", def_val (0.40));
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
scale = pika_scale_entry_new (_("Filter Alternative Surround:"), sels->filter_alternative_surround, 1, 10, 0);
|
||||
pika_help_set_help_data (scale,
|
||||
_("A second number of adjacent points to consider "
|
||||
"when filtering."), NULL);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (scale_entry_update_double),
|
||||
&sels->filter_alternative_surround);
|
||||
adjust_widgets = g_slist_append (adjust_widgets, scale);
|
||||
g_object_set_data (G_OBJECT (scale), "default_value", def_val (1.0));
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
scale = pika_scale_entry_new (_("Filter Epsilon:"), sels->filter_epsilon, 5, 40, 2);
|
||||
pika_help_set_help_data (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);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (scale_entry_update_double),
|
||||
&sels->filter_epsilon);
|
||||
adjust_widgets = g_slist_append (adjust_widgets, scale);
|
||||
g_object_set_data (G_OBJECT (scale), "default_value", def_val (10.0));
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
scale = pika_scale_entry_new (_("Filter Iteration Count:"), sels->filter_iteration_count, 4, 70, 0);
|
||||
pika_help_set_help_data (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);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (scale_entry_update_double),
|
||||
&sels->filter_iteration_count);
|
||||
adjust_widgets = g_slist_append (adjust_widgets, scale);
|
||||
g_object_set_data (G_OBJECT (scale), "default_value", def_val (4.0));
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
scale = pika_scale_entry_new (_("Filter Percent:"), sels->filter_percent, 0, 1, 2);
|
||||
pika_help_set_help_data (scale,
|
||||
_("To produce the new point, use the old point plus "
|
||||
"this times the neighbors."), NULL);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (scale_entry_update_double),
|
||||
&sels->filter_percent);
|
||||
adjust_widgets = g_slist_append (adjust_widgets, scale);
|
||||
g_object_set_data (G_OBJECT (scale), "default_value", def_val (0.33));
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
scale = pika_scale_entry_new (_("Filter Secondary Surround:"), sels->filter_secondary_surround, 3, 10, 0);
|
||||
pika_help_set_help_data (scale,
|
||||
_("Number of adjacent points to consider if "
|
||||
"'filter_surround' points defines a straight line."),
|
||||
NULL);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (scale_entry_update_double),
|
||||
&sels->filter_secondary_surround);
|
||||
adjust_widgets = g_slist_append (adjust_widgets, scale);
|
||||
g_object_set_data (G_OBJECT (scale), "default_value", def_val (3.0));
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
scale = pika_scale_entry_new (_("Filter Surround:"), sels->filter_surround, 2, 10, 0);
|
||||
pika_help_set_help_data (scale,
|
||||
_("Number of adjacent points to consider when filtering."),
|
||||
NULL);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (scale_entry_update_double),
|
||||
&sels->filter_surround);
|
||||
adjust_widgets = g_slist_append (adjust_widgets, scale);
|
||||
g_object_set_data (G_OBJECT (scale), "default_value", def_val (2.0));
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
check = gtk_check_button_new_with_label (_("Keep Knees"));
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), sels->keep_knees);
|
||||
gtk_grid_attach (GTK_GRID (grid), check, 1, row, 2, 1);
|
||||
pika_help_set_help_data (GTK_WIDGET (check),
|
||||
_("Says whether or not to remove 'knee' "
|
||||
"points after finding the outline."), NULL);
|
||||
g_signal_connect (check, "toggled",
|
||||
G_CALLBACK (pika_toggle_button_update),
|
||||
&sels->keep_knees);
|
||||
gtk_widget_show (check);
|
||||
adjust_widgets = g_slist_append (adjust_widgets, check);
|
||||
g_object_set_data (G_OBJECT (check), "default_value", def_val ((gdouble)FALSE));
|
||||
row++;
|
||||
|
||||
scale = pika_scale_entry_new (_("Line Reversion Threshold:"), sels->line_reversion_threshold, 0.01, 0.2, 3);
|
||||
pika_help_set_help_data (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);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (scale_entry_update_double),
|
||||
&sels->line_reversion_threshold);
|
||||
adjust_widgets = g_slist_append (adjust_widgets, scale);
|
||||
g_object_set_data (G_OBJECT (scale), "default_value", def_val (0.01));
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
scale = pika_scale_entry_new (_("Line Threshold:"), sels->line_threshold, 0.2, 4, 2);
|
||||
pika_help_set_help_data (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);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (scale_entry_update_double),
|
||||
&sels->line_threshold);
|
||||
adjust_widgets = g_slist_append (adjust_widgets, scale);
|
||||
g_object_set_data (G_OBJECT (scale), "default_value", def_val (0.5));
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
scale = pika_scale_entry_new (_("Reparametrize Improvement:"), sels->reparameterize_improvement, 0, 1, 2);
|
||||
pika_help_set_help_data (scale,
|
||||
_("If reparameterization doesn't improve the fit by this "
|
||||
"much percent, stop doing it. ""Amount of error at which "
|
||||
"it is pointless to reparameterize."), NULL);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (scale_entry_update_double),
|
||||
&sels->reparameterize_improvement);
|
||||
adjust_widgets = g_slist_append (adjust_widgets, scale);
|
||||
g_object_set_data (G_OBJECT (scale), "default_value", def_val (0.01));
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
scale = pika_scale_entry_new (_("Reparametrize Threshold:"), sels->reparameterize_threshold, 1, 50, 2);
|
||||
pika_help_set_help_data (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);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (scale_entry_update_double),
|
||||
&sels->reparameterize_threshold);
|
||||
adjust_widgets = g_slist_append (adjust_widgets, scale);
|
||||
g_object_set_data (G_OBJECT (scale), "default_value", def_val (1.0));
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
scale = pika_scale_entry_new (_("Subdivide Search:"), sels->subdivide_search, 0.05, 1, 2);
|
||||
pika_help_set_help_data (scale,
|
||||
_("Percentage of the curve away from the worst point "
|
||||
"to look for a better place to subdivide."), NULL);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (scale_entry_update_double),
|
||||
&sels->subdivide_search);
|
||||
adjust_widgets = g_slist_append (adjust_widgets, scale);
|
||||
g_object_set_data (G_OBJECT (scale), "default_value", def_val (0.1));
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
scale = pika_scale_entry_new (_("Subdivide Surround:"), sels->subdivide_surround, 2, 10, 0);
|
||||
pika_help_set_help_data (scale,
|
||||
_("Number of points to consider when deciding whether "
|
||||
"a given point is a better place to subdivide."),
|
||||
NULL);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (scale_entry_update_double),
|
||||
&sels->subdivide_surround);
|
||||
adjust_widgets = g_slist_append (adjust_widgets, scale);
|
||||
g_object_set_data (G_OBJECT (scale), "default_value", def_val (4.0));
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
scale = pika_scale_entry_new (_("Subdivide Threshold:"), sels->subdivide_threshold, 0.01, 1, 2);
|
||||
pika_help_set_help_data (scale,
|
||||
_("How many pixels a point can diverge from a straight "
|
||||
"line and still be considered a better place to "
|
||||
"subdivide."), NULL);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (scale_entry_update_double),
|
||||
&sels->subdivide_threshold);
|
||||
adjust_widgets = g_slist_append (adjust_widgets, scale);
|
||||
g_object_set_data (G_OBJECT (scale), "default_value", def_val (0.03));
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
scale = pika_scale_entry_new (_("Tangent Surround:"), sels->tangent_surround, 2, 10, 0);
|
||||
pika_help_set_help_data (scale,
|
||||
_("Number of points to look at on either side of a "
|
||||
"point when computing the approximation to the "
|
||||
"tangent at that point."), NULL);
|
||||
g_signal_connect (scale, "value-changed",
|
||||
G_CALLBACK (scale_entry_update_double),
|
||||
&sels->tangent_surround);
|
||||
adjust_widgets = g_slist_append (adjust_widgets, scale);
|
||||
g_object_set_data (G_OBJECT (scale), "default_value", def_val (3.0));
|
||||
gtk_grid_attach (GTK_GRID (grid), scale, 0, row++, 3, 1);
|
||||
gtk_widget_show (scale);
|
||||
|
||||
return scrolled_win;
|
||||
}
|
||||
|
||||
void
|
||||
scale_entry_update_double (PikaLabelSpin *entry,
|
||||
gdouble *value)
|
||||
{
|
||||
*value = pika_label_spin_get_value (entry);
|
||||
}
|
640
plug-ins/selection-to-path/selection-to-path.c
Normal file
640
plug-ins/selection-to-path/selection-to-path.c
Normal file
@ -0,0 +1,640 @@
|
||||
/*
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This is a plug-in for PIKA.
|
||||
*
|
||||
* 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Change log:-
|
||||
* 0.1 First version.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "libpika/pika.h"
|
||||
#include "libpika/pikaui.h"
|
||||
|
||||
#include "libpikamath/pikamath.h"
|
||||
|
||||
#include "global.h"
|
||||
#include "types.h"
|
||||
#include "pxl-outline.h"
|
||||
#include "fit.h"
|
||||
#include "spline.h"
|
||||
#include "selection-to-path.h"
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
|
||||
#define PLUG_IN_PROC "plug-in-sel2path"
|
||||
#define PLUG_IN_BINARY "selection-to-path"
|
||||
#define PLUG_IN_ROLE "pika-selection-to-path"
|
||||
|
||||
#define RESPONSE_RESET 1
|
||||
#define MID_POINT 127
|
||||
|
||||
|
||||
typedef struct _Sel2path Sel2path;
|
||||
typedef struct _Sel2pathClass Sel2pathClass;
|
||||
|
||||
struct _Sel2path
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _Sel2pathClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define SEL2PATH_TYPE (sel2path_get_type ())
|
||||
#define SEL2PATH (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEL2PATH_TYPE, Sel2path))
|
||||
|
||||
GType sel2path_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * sel2path_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * sel2path_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * sel2path_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
static gint sel2path_dialog (SELVALS *sels);
|
||||
static void sel2path_response (GtkWidget *widget,
|
||||
gint response_id,
|
||||
gpointer data);
|
||||
static void dialog_print_selVals (SELVALS *sels);
|
||||
static gboolean sel2path (PikaImage *image);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Sel2path, sel2path, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (SEL2PATH_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
static gint sel_x1, sel_y1, sel_x2, sel_y2;
|
||||
static gint has_sel, sel_width, sel_height;
|
||||
static SELVALS selVals;
|
||||
static GeglSampler *sel_sampler;
|
||||
static gboolean retVal = TRUE; /* Toggle if cancel button clicked */
|
||||
|
||||
|
||||
static void
|
||||
sel2path_class_init (Sel2pathClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = sel2path_query_procedures;
|
||||
plug_in_class->create_procedure = sel2path_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
sel2path_init (Sel2path *sel2path)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
sel2path_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
sel2path_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, PLUG_IN_PROC))
|
||||
{
|
||||
procedure = pika_image_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
sel2path_run, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "*");
|
||||
pika_procedure_set_sensitivity_mask (procedure,
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLE |
|
||||
PIKA_PROCEDURE_SENSITIVE_DRAWABLES |
|
||||
PIKA_PROCEDURE_SENSITIVE_NO_DRAWABLES);
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
"Converts a selection to a path",
|
||||
"Converts a selection to a path",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Andy Thomas",
|
||||
"Andy Thomas",
|
||||
"1999");
|
||||
|
||||
PIKA_PROC_ARG_DOUBLE (procedure, "align-threshold",
|
||||
"Align threshold",
|
||||
"Align threshold",
|
||||
0.2, 2.0, 0.5,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_DOUBLE (procedure, "corner-always-threshold",
|
||||
"Corner always threshold",
|
||||
"Corner always threshold",
|
||||
30, 180, 60.0,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "corner-surround",
|
||||
"Corner surround",
|
||||
"Corner surround",
|
||||
3, 8, 4,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_DOUBLE (procedure, "corner-threshold",
|
||||
"Corner threshold",
|
||||
"Corner threshold",
|
||||
0, 180, 100.0,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_DOUBLE (procedure, "error-threshold",
|
||||
"Error threshold",
|
||||
"Error threshold",
|
||||
0.2, 10, 0.4,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "filter-alternative-surround",
|
||||
"Filter alternative surround",
|
||||
"Filter alternative surround",
|
||||
1, 10, 1,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_DOUBLE (procedure, "filter-epsilon",
|
||||
"Filter epsilon",
|
||||
"Filter epsilon",
|
||||
5, 40, 10.0,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "filter-iteration-count",
|
||||
"Filter iteration count",
|
||||
"Filter iteration count",
|
||||
4, 70, 4,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_DOUBLE (procedure, "filter-percent",
|
||||
"Filter percent",
|
||||
"Filter percent",
|
||||
0, 1, 0.33,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "filter-secondary-surround",
|
||||
"Filter secondary surround",
|
||||
"Filter secondary surround",
|
||||
3, 10, 3,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "filter-surround",
|
||||
"Filter surround",
|
||||
"Filter surround",
|
||||
2, 10, 2,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "keep-knees",
|
||||
"Keep knees",
|
||||
"Keep knees",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_DOUBLE (procedure, "line-reversion-threshold",
|
||||
"Line reversion threshold",
|
||||
"Line reversion threshold",
|
||||
0.01, 0.2, 0.01,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_DOUBLE (procedure, "line-threshold",
|
||||
"Line threshold",
|
||||
"Line threshold",
|
||||
0.2, 4, 0.5,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_DOUBLE (procedure, "reparametrize-improvement",
|
||||
"Reparametrize improvement",
|
||||
"Reparametrize improvement",
|
||||
0, 1, 0.01,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_DOUBLE (procedure, "reparametrize-threshold",
|
||||
"Reparametrize threshold",
|
||||
"Reparametrize threshold",
|
||||
1, 50, 1.0,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_DOUBLE (procedure, "subdivide-search",
|
||||
"Subdivide search",
|
||||
"Subdivide search",
|
||||
0.05, 1, 0.1,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "subdivide-surround",
|
||||
"Subdivide surround",
|
||||
"Subdivide surround",
|
||||
2, 10, 4,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_DOUBLE (procedure, "subdivide-threshold",
|
||||
"Subdivide threshold",
|
||||
"Subdivide threshold",
|
||||
0.01, 1, 0.03,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "tangent-surround",
|
||||
"Tangent surround",
|
||||
"Tangent surround",
|
||||
2, 10, 3,
|
||||
G_PARAM_READWRITE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
sel2path_run (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
if (pika_selection_is_empty (image))
|
||||
{
|
||||
g_message (_("No selection to convert"));
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_SUCCESS,
|
||||
NULL);
|
||||
}
|
||||
|
||||
fit_set_default_params (&selVals);
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
if (pika_get_data_size (PLUG_IN_PROC) > 0)
|
||||
pika_get_data (PLUG_IN_PROC, &selVals);
|
||||
|
||||
if (! sel2path_dialog (&selVals))
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
|
||||
/* Get the current settings */
|
||||
fit_set_params (&selVals);
|
||||
break;
|
||||
|
||||
case PIKA_RUN_NONINTERACTIVE:
|
||||
selVals.align_threshold = PIKA_VALUES_GET_DOUBLE (args, 0);
|
||||
selVals.corner_always_threshold = PIKA_VALUES_GET_DOUBLE (args, 1);
|
||||
selVals.corner_surround = PIKA_VALUES_GET_INT (args, 2);
|
||||
selVals.corner_threshold = PIKA_VALUES_GET_DOUBLE (args, 3);
|
||||
selVals.error_threshold = PIKA_VALUES_GET_DOUBLE (args, 4);
|
||||
selVals.filter_alternative_surround = PIKA_VALUES_GET_INT (args, 5);
|
||||
selVals.filter_epsilon = PIKA_VALUES_GET_DOUBLE (args, 6);
|
||||
selVals.filter_iteration_count = PIKA_VALUES_GET_INT (args, 7);
|
||||
selVals.filter_percent = PIKA_VALUES_GET_DOUBLE (args, 8);
|
||||
selVals.filter_secondary_surround = PIKA_VALUES_GET_INT (args, 9);
|
||||
selVals.filter_surround = PIKA_VALUES_GET_INT (args, 10);
|
||||
selVals.keep_knees = PIKA_VALUES_GET_BOOLEAN (args, 11);
|
||||
selVals.line_reversion_threshold = PIKA_VALUES_GET_DOUBLE (args, 12);
|
||||
selVals.line_threshold = PIKA_VALUES_GET_DOUBLE (args, 13);
|
||||
selVals.reparameterize_improvement = PIKA_VALUES_GET_DOUBLE (args, 14);
|
||||
selVals.reparameterize_threshold = PIKA_VALUES_GET_DOUBLE (args, 15);
|
||||
selVals.subdivide_search = PIKA_VALUES_GET_DOUBLE (args, 16);
|
||||
selVals.subdivide_surround = PIKA_VALUES_GET_INT (args, 17);
|
||||
selVals.subdivide_threshold = PIKA_VALUES_GET_DOUBLE (args, 18);
|
||||
selVals.tangent_surround = PIKA_VALUES_GET_INT (args, 19);
|
||||
|
||||
fit_set_params (&selVals);
|
||||
break;
|
||||
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
if (pika_get_data_size (PLUG_IN_PROC) > 0)
|
||||
{
|
||||
pika_get_data (PLUG_IN_PROC, &selVals);
|
||||
|
||||
/* Set up the last values */
|
||||
fit_set_params (&selVals);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (! sel2path (image))
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
NULL);
|
||||
|
||||
dialog_print_selVals (&selVals);
|
||||
|
||||
if (run_mode == PIKA_RUN_INTERACTIVE)
|
||||
pika_set_data (PLUG_IN_PROC, &selVals, sizeof(SELVALS));
|
||||
|
||||
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
|
||||
}
|
||||
|
||||
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 *table;
|
||||
|
||||
retVal = FALSE;
|
||||
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
dlg = pika_dialog_new (_("Selection to Path Advanced Settings"),
|
||||
PLUG_IN_ROLE,
|
||||
NULL, 0,
|
||||
pika_standard_help_func, PLUG_IN_PROC,
|
||||
|
||||
_("_Reset"), RESPONSE_RESET,
|
||||
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
||||
_("_OK"), GTK_RESPONSE_OK,
|
||||
|
||||
NULL);
|
||||
|
||||
pika_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
|
||||
RESPONSE_RESET,
|
||||
GTK_RESPONSE_OK,
|
||||
GTK_RESPONSE_CANCEL,
|
||||
-1);
|
||||
|
||||
pika_window_set_transient (GTK_WINDOW (dlg));
|
||||
|
||||
g_signal_connect (dlg, "response",
|
||||
G_CALLBACK (sel2path_response),
|
||||
NULL);
|
||||
g_signal_connect (dlg, "destroy",
|
||||
G_CALLBACK (gtk_main_quit),
|
||||
NULL);
|
||||
|
||||
table = dialog_create_selection_area (sels);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (table), 12);
|
||||
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
|
||||
table, TRUE, TRUE, 0);
|
||||
gtk_widget_show (table);
|
||||
|
||||
gtk_widget_show (dlg);
|
||||
|
||||
gtk_main ();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static void
|
||||
sel2path_response (GtkWidget *widget,
|
||||
gint response_id,
|
||||
gpointer data)
|
||||
{
|
||||
switch (response_id)
|
||||
{
|
||||
case RESPONSE_RESET:
|
||||
reset_adv_dialog ();
|
||||
fit_set_params (&selVals);
|
||||
break;
|
||||
|
||||
case GTK_RESPONSE_OK:
|
||||
retVal = TRUE;
|
||||
|
||||
default:
|
||||
gtk_widget_destroy (widget);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
gegl_sampler_get (sel_sampler,
|
||||
col + sel_x1, row + sel_y1, NULL, &ret, GEGL_ABYSS_NONE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sel_pixel_is_white (gint row,
|
||||
gint col)
|
||||
{
|
||||
if (sel_pixel_value (row, col) < MID_POINT)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gint
|
||||
sel_get_width (void)
|
||||
{
|
||||
return sel_width;
|
||||
}
|
||||
|
||||
gint
|
||||
sel_get_height (void)
|
||||
{
|
||||
return sel_height;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sel_valid_pixel (gint row,
|
||||
gint col)
|
||||
{
|
||||
return (0 <= (row) && (row) < sel_get_height ()
|
||||
&& 0 <= (col) && (col) < sel_get_width ());
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
do_points (spline_list_array_type in_splines,
|
||||
PikaImage *image)
|
||||
{
|
||||
PikaVectors *vectors;
|
||||
gint32 stroke;
|
||||
gint i, j;
|
||||
gboolean have_points = FALSE;
|
||||
spline_list_type spline_list;
|
||||
|
||||
/* check if there really is something to do... */
|
||||
for (i = 0; i < SPLINE_LIST_ARRAY_LENGTH (in_splines); i++)
|
||||
{
|
||||
spline_list = SPLINE_LIST_ARRAY_ELT (in_splines, i);
|
||||
/* Ignore single points that are on their own */
|
||||
if (SPLINE_LIST_LENGTH (spline_list) < 2)
|
||||
continue;
|
||||
have_points = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (! have_points)
|
||||
return;
|
||||
|
||||
vectors = pika_vectors_new (image, _("Selection"));
|
||||
|
||||
for (j = 0; j < SPLINE_LIST_ARRAY_LENGTH (in_splines); j++)
|
||||
{
|
||||
spline_type seg;
|
||||
|
||||
spline_list = SPLINE_LIST_ARRAY_ELT (in_splines, j);
|
||||
|
||||
/* Ignore single points that are on their own */
|
||||
if (SPLINE_LIST_LENGTH (spline_list) < 2)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* we're constructing the path backwards
|
||||
* to have the result of least surprise for "Text along Path".
|
||||
*/
|
||||
seg = SPLINE_LIST_ELT (spline_list, SPLINE_LIST_LENGTH (spline_list) - 1);
|
||||
stroke = pika_vectors_bezier_stroke_new_moveto (vectors,
|
||||
END_POINT (seg).x,
|
||||
END_POINT (seg).y);
|
||||
|
||||
for (i = SPLINE_LIST_LENGTH (spline_list); i > 0; i--)
|
||||
{
|
||||
seg = SPLINE_LIST_ELT (spline_list, i-1);
|
||||
|
||||
if (SPLINE_DEGREE (seg) == LINEAR)
|
||||
pika_vectors_bezier_stroke_lineto (vectors, stroke,
|
||||
START_POINT (seg).x,
|
||||
START_POINT (seg).y);
|
||||
else if (SPLINE_DEGREE (seg) == CUBIC)
|
||||
pika_vectors_bezier_stroke_cubicto (vectors, stroke,
|
||||
CONTROL2 (seg).x,
|
||||
CONTROL2 (seg).y,
|
||||
CONTROL1 (seg).x,
|
||||
CONTROL1 (seg).y,
|
||||
START_POINT (seg).x,
|
||||
START_POINT (seg).y);
|
||||
else
|
||||
g_warning ("print_spline: strange degree (%d)",
|
||||
SPLINE_DEGREE (seg));
|
||||
}
|
||||
|
||||
pika_vectors_stroke_close (vectors, stroke);
|
||||
|
||||
/* transform to PIKAs coordinate system, taking the selections
|
||||
* bounding box into account */
|
||||
pika_vectors_stroke_scale (vectors, stroke, 1.0, -1.0);
|
||||
pika_vectors_stroke_translate (vectors, stroke,
|
||||
sel_x1, sel_y1 + sel_height + 1);
|
||||
}
|
||||
|
||||
pika_image_insert_vectors (image, vectors, NULL, -1);
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
sel2path (PikaImage *image)
|
||||
{
|
||||
PikaSelection *selection;
|
||||
GeglBuffer *sel_buffer;
|
||||
pixel_outline_list_type olt;
|
||||
spline_list_array_type splines;
|
||||
|
||||
pika_selection_bounds (image, &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 = pika_image_get_selection (image);
|
||||
|
||||
if (! selection)
|
||||
return FALSE;
|
||||
|
||||
sel_buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (selection));
|
||||
sel_sampler = gegl_buffer_sampler_new (sel_buffer,
|
||||
babl_format ("Y u8"),
|
||||
GEGL_SAMPLER_NEAREST);
|
||||
|
||||
olt = find_outline_pixels ();
|
||||
|
||||
splines = fitted_splines (olt);
|
||||
|
||||
do_points (splines, image);
|
||||
|
||||
g_object_unref (sel_sampler);
|
||||
g_object_unref (sel_buffer);
|
||||
|
||||
pika_displays_flush ();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
safe_free (address *item)
|
||||
{
|
||||
g_free (*item);
|
||||
*item = NULL;
|
||||
}
|
37
plug-ins/selection-to-path/selection-to-path.h
Normal file
37
plug-ins/selection-to-path/selection-to-path.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* This is a plug-in for PIKA.
|
||||
*
|
||||
* 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "gtk/gtk.h"
|
||||
#include "libpika/pika.h"
|
||||
|
||||
guchar sel_pixel_value (gint, gint);
|
||||
gint sel_pixel_is_white (gint, gint);
|
||||
gint sel_get_width (void);
|
||||
gint sel_get_height (void);
|
||||
gboolean sel_valid_pixel (gint, gint);
|
||||
void reset_adv_dialog (void);
|
||||
|
||||
|
||||
GtkWidget * dialog_create_selection_area(SELVALS *);
|
||||
|
233
plug-ins/selection-to-path/spline.c
Normal file
233
plug-ins/selection-to-path/spline.c
Normal file
@ -0,0 +1,233 @@
|
||||
/* 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 3, 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <glib.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 (void)
|
||||
{
|
||||
real_coordinate_type coord = { -100.0, -100.0 };
|
||||
spline_type spline;
|
||||
|
||||
START_POINT (spline)
|
||||
= CONTROL1 (spline)
|
||||
= CONTROL2 (spline)
|
||||
= END_POINT (spline)
|
||||
= coord;
|
||||
SPLINE_DEGREE (spline) = -1;
|
||||
SPLINE_LINEARITY (spline) = 0;
|
||||
|
||||
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++)
|
||||
{
|
||||
#if defined (__GNUC__)
|
||||
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);
|
||||
#else
|
||||
/* HB: the above is really nice, but is there any other compiler
|
||||
* supporting this ??
|
||||
*/
|
||||
real_coordinate_type t1;
|
||||
real_coordinate_type t2;
|
||||
t1.x = V[j - 1].v[i].x * one_minus_t;
|
||||
t1.y = V[j - 1].v[i].y * one_minus_t;
|
||||
t2.x = V[j - 1].v[i + 1].x * t;
|
||||
t2.y = V[j - 1].v[i + 1].y * t;
|
||||
V[j].v[i].x = t1.x + t2.x;
|
||||
V[j].v[i].y = t1.y + t2.y;
|
||||
#endif
|
||||
}
|
||||
|
||||
return V[degree].v[0];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Return a new, empty, spline list. */
|
||||
|
||||
spline_list_type *
|
||||
new_spline_list (void)
|
||||
{
|
||||
spline_list_type *answer = g_new (spline_list_type, 1);
|
||||
|
||||
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 = g_new (spline_list_type, 1);
|
||||
|
||||
SPLINE_LIST_DATA (*answer) = g_new (spline_type, 1);
|
||||
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) = g_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) = g_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 (void)
|
||||
{
|
||||
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) = g_realloc(SPLINE_LIST_ARRAY_DATA (*l),(SPLINE_LIST_ARRAY_LENGTH (*l))*sizeof(spline_list_type));
|
||||
LAST_SPLINE_LIST_ARRAY_ELT (*l) = s;
|
||||
}
|
124
plug-ins/selection-to-path/spline.h
Normal file
124
plug-ins/selection-to-path/spline.h
Normal file
@ -0,0 +1,124 @@
|
||||
/* spline.h: manipulate the spline representation.
|
||||
*
|
||||
* Copyright (C) 1992 Free Software Foundation, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3, 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SPLINE_H
|
||||
#define SPLINE_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "bounding-box.h"
|
||||
#include "types.h"
|
||||
|
||||
|
||||
/* Third degree is the highest we deal with. */
|
||||
typedef enum
|
||||
{
|
||||
LINEAR = 1, QUADRATIC = 2, CUBIC = 3
|
||||
} polynomial_degree;
|
||||
|
||||
|
||||
/* A Bezier spline can be represented as four points in the real plane:
|
||||
a starting point, ending point, and two control points. The
|
||||
curve always lies in the convex hull defined by the four points. It
|
||||
is also convenient to save the divergence of the spline from the
|
||||
straight line defined by the endpoints. */
|
||||
typedef struct
|
||||
{
|
||||
real_coordinate_type v[4]; /* The control points. */
|
||||
polynomial_degree degree;
|
||||
real linearity;
|
||||
} spline_type;
|
||||
|
||||
#define START_POINT(spl) ((spl).v[0])
|
||||
#define CONTROL1(spl) ((spl).v[1])
|
||||
#define CONTROL2(spl) ((spl).v[2])
|
||||
#define END_POINT(spl) ((spl).v[3])
|
||||
#define SPLINE_DEGREE(spl) ((spl).degree)
|
||||
#define SPLINE_LINEARITY(spl) ((spl).linearity)
|
||||
|
||||
|
||||
/* Return a spline structure. */
|
||||
extern spline_type new_spline (void);
|
||||
|
||||
/* Print a spline on the given file. */
|
||||
extern void print_spline (FILE *, spline_type);
|
||||
|
||||
/* Evaluate SPLINE at the given T value. */
|
||||
extern real_coordinate_type evaluate_spline (spline_type spline, real t);
|
||||
|
||||
/* Each outline in a character is typically represented by many
|
||||
splines. So, here is a list structure for that: */
|
||||
typedef struct
|
||||
{
|
||||
spline_type *data;
|
||||
unsigned length;
|
||||
} spline_list_type;
|
||||
|
||||
/* An empty list will have length zero (and null data). */
|
||||
#define SPLINE_LIST_LENGTH(s_l) ((s_l).length)
|
||||
|
||||
/* The address of the beginning of the array of data. */
|
||||
#define SPLINE_LIST_DATA(s_l) ((s_l).data)
|
||||
|
||||
/* The element INDEX in S_L. */
|
||||
#define SPLINE_LIST_ELT(s_l, index) (SPLINE_LIST_DATA (s_l)[index])
|
||||
|
||||
/* The last element in S_L. */
|
||||
#define LAST_SPLINE_LIST_ELT(s_l) \
|
||||
(SPLINE_LIST_DATA (s_l)[SPLINE_LIST_LENGTH (s_l) - 1])
|
||||
|
||||
/* The previous and next elements to INDEX in S_L. */
|
||||
#define NEXT_SPLINE_LIST_ELT(s_l, index) \
|
||||
SPLINE_LIST_ELT (s_l, ((index) + 1) % SPLINE_LIST_LENGTH (s_l))
|
||||
#define PREV_SPLINE_LIST_ELT(s_l, index) \
|
||||
SPLINE_LIST_ELT (s_l, index == 0 \
|
||||
? SPLINE_LIST_LENGTH (s_l) - 1 \
|
||||
: index - 1)
|
||||
|
||||
/* Construct and destroy new `spline_list_type' objects. */
|
||||
extern spline_list_type *new_spline_list (void);
|
||||
extern spline_list_type *init_spline_list (spline_type);
|
||||
extern void free_spline_list (spline_list_type *);
|
||||
|
||||
/* Append the spline S to the list S_LIST. */
|
||||
extern void append_spline (spline_list_type *s_list, spline_type s);
|
||||
|
||||
/* Append the elements in list S2 to S1, changing S1. */
|
||||
extern void concat_spline_lists (spline_list_type *s1, spline_list_type s2);
|
||||
|
||||
|
||||
/* Each character is in general made up of many outlines. So here is one
|
||||
more list structure. */
|
||||
typedef struct
|
||||
{
|
||||
spline_list_type *data;
|
||||
unsigned length;
|
||||
} spline_list_array_type;
|
||||
|
||||
/* Turns out we can use the same definitions for lists of lists as for
|
||||
just lists. But we define the usual names, just in case. */
|
||||
#define SPLINE_LIST_ARRAY_LENGTH SPLINE_LIST_LENGTH
|
||||
#define SPLINE_LIST_ARRAY_DATA SPLINE_LIST_DATA
|
||||
#define SPLINE_LIST_ARRAY_ELT SPLINE_LIST_ELT
|
||||
#define LAST_SPLINE_LIST_ARRAY_ELT LAST_SPLINE_LIST_ELT
|
||||
|
||||
/* The usual routines. */
|
||||
extern spline_list_array_type new_spline_list_array (void);
|
||||
extern void free_spline_list_array (spline_list_array_type *);
|
||||
extern void append_spline_list (spline_list_array_type *, spline_list_type);
|
||||
|
||||
#endif /* not SPLINE_H */
|
147
plug-ins/selection-to-path/types.h
Normal file
147
plug-ins/selection-to-path/types.h
Normal file
@ -0,0 +1,147 @@
|
||||
/* types.h: general types.
|
||||
*
|
||||
* Copyright (C) 1992 Free Software Foundation, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3, 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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 */
|
249
plug-ins/selection-to-path/vector.c
Normal file
249
plug-ins/selection-to-path/vector.c
Normal file
@ -0,0 +1,249 @@
|
||||
/* 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 3, 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "global.h"
|
||||
#include "config.h"
|
||||
|
||||
#include "vector.h"
|
||||
|
||||
|
||||
/* Given the point COORD, return the corresponding vector. */
|
||||
|
||||
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. */
|
||||
|
||||
real_coordinate_type
|
||||
vector_to_point (const vector_type v)
|
||||
{
|
||||
real_coordinate_type coord;
|
||||
|
||||
coord.x = v.dx;
|
||||
coord.y = v.dy;
|
||||
|
||||
return coord;
|
||||
}
|
||||
|
||||
|
||||
real
|
||||
magnitude (const vector_type v)
|
||||
{
|
||||
return hypot (v.dx, v.dy);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
real
|
||||
Vdot (const vector_type v1, const vector_type v2)
|
||||
{
|
||||
return v1.dx * v2.dx + v1.dy * v2.dy;
|
||||
}
|
||||
|
||||
|
||||
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. */
|
||||
|
||||
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 my_acosd (Vdot (v2, v1));
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
coordinate_type
|
||||
Vadd_int_point (const coordinate_type c, const vector_type v)
|
||||
{
|
||||
coordinate_type a;
|
||||
|
||||
a.x = SROUND ((real) c.x + v.dx);
|
||||
a.y = SROUND ((real) c.y + v.dy);
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
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. */
|
||||
|
||||
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. */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
boolean
|
||||
IPequal (const coordinate_type c1, const coordinate_type c2)
|
||||
{
|
||||
return c1.x == c2.x && c1.y == c2.y;
|
||||
}
|
95
plug-ins/selection-to-path/vector.h
Normal file
95
plug-ins/selection-to-path/vector.h
Normal file
@ -0,0 +1,95 @@
|
||||
/* vector.h: operations on vectors and points.
|
||||
*
|
||||
* Copyright (C) 1992 Free Software Foundation, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3, 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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 vector_type make_vector (const real_coordinate_type);
|
||||
|
||||
/* And a vector as a point, i.e., a displacement from the origin. */
|
||||
extern 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 real magnitude (const vector_type);
|
||||
extern vector_type normalize (const vector_type);
|
||||
|
||||
extern vector_type Vadd (const vector_type, const vector_type);
|
||||
extern real Vdot (const vector_type, const vector_type);
|
||||
extern vector_type Vmult_scalar (const vector_type, const real);
|
||||
extern 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 real_coordinate_type Vadd_point
|
||||
(const real_coordinate_type, const vector_type);
|
||||
|
||||
#define Psubtract_vector Vsubtract_point
|
||||
extern real_coordinate_type Vsubtract_point
|
||||
(const real_coordinate_type, const vector_type);
|
||||
|
||||
/* This returns the rounded sum. */
|
||||
#define IPadd_vector Vadd_int_point
|
||||
extern coordinate_type Vadd_int_point
|
||||
(const coordinate_type, const vector_type);
|
||||
|
||||
/* Take the absolute value of both components. */
|
||||
extern 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 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 vector_type IPsubtract
|
||||
(const coordinate_type, const coordinate_type);
|
||||
extern coordinate_type IPsubtractP
|
||||
(const coordinate_type, const coordinate_type);
|
||||
extern coordinate_type IPadd
|
||||
(const coordinate_type, const coordinate_type);
|
||||
extern coordinate_type IPmult_scalar (const coordinate_type, const int);
|
||||
extern real_coordinate_type IPmult_real
|
||||
(const coordinate_type, const real);
|
||||
extern boolean IPequal (const coordinate_type, const coordinate_type);
|
||||
|
||||
#endif /* not VECTOR_H */
|
Reference in New Issue
Block a user