Blame bezctx.md

rpm-build 8267b0
# The Bézier context
rpm-build 8267b0
rpm-build 8267b0
```c
rpm-build 8267b0
    struct _bezctx {
rpm-build 8267b0
    void (*moveto)(bezctx *bc, double x, double y, int is_open);
rpm-build 8267b0
    void (*lineto)(bezctx *bc, double x, double y);
rpm-build 8267b0
    void (*quadto)(bezctx *bc, double x1, double y1, double x2, double y2);
rpm-build 8267b0
    void (*curveto)(bezctx *bc, double x1, double y1, double x2, double y2,
rpm-build 8267b0
                    double x3, double y3);
rpm-build 8267b0
    void (*mark_knot)(bezctx *bc, int knot_idx);
rpm-build 8267b0
    };
rpm-build 8267b0
```
rpm-build 8267b0
rpm-build 8267b0
You must create a super-class of this abstract type that handles the creation of your particular representation of bézier splines.
rpm-build 8267b0
rpm-build 8267b0
I provide two examples here, one is the type Raph created for doing output to encapsulated postscript files, and the other is the type fontforge uses internally for converting from spiros into its native spline format.
rpm-build 8267b0
rpm-build 8267b0
## PostScript
rpm-build 8267b0
rpm-build 8267b0
First a header file. This declares a routine which will create a PostScript bezier context – this routine takes a file argument, splines will be written to the file as we gather more information.
rpm-build 8267b0
rpm-build 8267b0
```c
rpm-build 8267b0
/* bezctx_ps.h */
rpm-build 8267b0
const char *ps_prolog;		/* A string to be written to the file before conversion */
rpm-build 8267b0
const char *ps_postlog;		/* A string to be written to the file after conversion */
rpm-build 8267b0
rpm-build 8267b0
bezctx *new_bezctx_ps(FILE *f);
rpm-build 8267b0
rpm-build 8267b0
void
rpm-build 8267b0
bezctx_ps_close(bezctx *bc);
rpm-build 8267b0
```
rpm-build 8267b0
rpm-build 8267b0
Then the file itself.
rpm-build 8267b0
rpm-build 8267b0
```c
rpm-build 8267b0
/* bezctx_ps.c */
rpm-build 8267b0
/*
rpm-build 8267b0
ppedit - A pattern plate editor for Spiro splines.
rpm-build 8267b0
Copyright (C) 2007 Raph Levien
rpm-build 8267b0
rpm-build 8267b0
This program is free software; you can redistribute it and/or
rpm-build 8267b0
modify it under the terms of the GNU General Public License
rpm-build 8267b0
as published by the Free Software Foundation; either version 3
rpm-build 8267b0
of the License, or (at your option) any later version.
rpm-build 8267b0
rpm-build 8267b0
This program is distributed in the hope that it will be useful,
rpm-build 8267b0
but WITHOUT ANY WARRANTY; without even the implied warranty of
rpm-build 8267b0
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
rpm-build 8267b0
GNU General Public License for more details.
rpm-build 8267b0
rpm-build 8267b0
You should have received a copy of the GNU General Public License
rpm-build 8267b0
along with this program; if not, write to the Free Software
rpm-build 8267b0
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
rpm-build 8267b0
02110-1301, USA.
rpm-build 8267b0
rpm-build 8267b0
*/
rpm-build 8267b0
#include <stdio.h>
rpm-build 8267b0
rpm-build 8267b0
#include "zmisc.h"
rpm-build 8267b0
#include "bezctx.h"
rpm-build 8267b0
#include "bezctx_ps.h"
rpm-build 8267b0
rpm-build 8267b0
typedef struct {
rpm-build 8267b0
    bezctx base;	/* This is a superclass of bezctx, and this is the entry for the base */
rpm-build 8267b0
    int is_open;
rpm-build 8267b0
    double x, y;
rpm-build 8267b0
    FILE *f;
rpm-build 8267b0
} bezctx_ps;
rpm-build 8267b0
rpm-build 8267b0
const char *ps_prolog = "%!PS\n"
rpm-build 8267b0
"/m { moveto } bind def\n"
rpm-build 8267b0
"/l { lineto } bind def\n"
rpm-build 8267b0
"/c { curveto } bind def\n"
rpm-build 8267b0
"/z { closepath } bind def\n"
rpm-build 8267b0
"1 -1 scale\n"
rpm-build 8267b0
"0 -792 translate\n";
rpm-build 8267b0
rpm-build 8267b0
const char *ps_postlog = "stroke\n"
rpm-build 8267b0
"showpage\n";
rpm-build 8267b0
rpm-build 8267b0
/* This routine starts a new contour */
rpm-build 8267b0
static void
rpm-build 8267b0
bezctx_ps_moveto(bezctx *z, double x, double y, int is_open) {
rpm-build 8267b0
    bezctx_ps *bc = (bezctx_ps *)z;
rpm-build 8267b0
rpm-build 8267b0
    if (!bc->is_open) fprintf(bc->f, "z\n");
rpm-build 8267b0
    fprintf(bc->f, "%g %g m\n", x, y);
rpm-build 8267b0
    bc->is_open = is_open;
rpm-build 8267b0
    bc->x = x;
rpm-build 8267b0
    bc->y = y;
rpm-build 8267b0
}
rpm-build 8267b0
rpm-build 8267b0
/* This routine creates a linear spline from the previous point specified to this one */
rpm-build 8267b0
void
rpm-build 8267b0
bezctx_ps_lineto(bezctx *z, double x, double y) {
rpm-build 8267b0
    bezctx_ps *bc = (bezctx_ps *)z;
rpm-build 8267b0
rpm-build 8267b0
    fprintf(bc->f, "%g %g l\n", x, y);
rpm-build 8267b0
    bc->x = x;
rpm-build 8267b0
    bc->y = y;
rpm-build 8267b0
}
rpm-build 8267b0
rpm-build 8267b0
/* This could create a quadratic spline, except PostScript only is prepared to deal with */
rpm-build 8267b0
/*  cubics, so convert the quadratic into the equivalent cubic */
rpm-build 8267b0
void
rpm-build 8267b0
bezctx_ps_quadto(bezctx *z, double xm, double ym, double x3, double y3)
rpm-build 8267b0
{
rpm-build 8267b0
    bezctx_ps *bc = (bezctx_ps *)z;
rpm-build 8267b0
    double x0, y0;
rpm-build 8267b0
    double x1, y1;
rpm-build 8267b0
    double x2, y2;
rpm-build 8267b0
rpm-build 8267b0
    x0 = bc->x;
rpm-build 8267b0
    y0 = bc->y;
rpm-build 8267b0
    x1 = xm + (1./3) * (x0 - xm);
rpm-build 8267b0
    y1 = ym + (1./3) * (y0 - ym);
rpm-build 8267b0
    x2 = xm + (1./3) * (x3 - xm);
rpm-build 8267b0
    y2 = ym + (1./3) * (y3 - ym);
rpm-build 8267b0
    fprintf(bc->f, "%g %g %g %g %g %g c\n", x1, y1, x2, y2, x3, y3);
rpm-build 8267b0
    bc->x = x3;
rpm-build 8267b0
    bc->y = y3;
rpm-build 8267b0
}
rpm-build 8267b0
rpm-build 8267b0
/* And this creates a cubic */
rpm-build 8267b0
void
rpm-build 8267b0
bezctx_ps_curveto(bezctx *z, double x1, double y1, double x2, double y2,
rpm-build 8267b0
		  double x3, double y3)
rpm-build 8267b0
{
rpm-build 8267b0
    bezctx_ps *bc = (bezctx_ps *)z;
rpm-build 8267b0
rpm-build 8267b0
    fprintf(bc->f, "%g %g %g %g %g %g c\n", x1, y1, x2, y2, x3, y3);
rpm-build 8267b0
    bc->x = x3;
rpm-build 8267b0
    bc->y = y3;
rpm-build 8267b0
}
rpm-build 8267b0
rpm-build 8267b0
/* Allocates and initializes a new PostScript bezier context */
rpm-build 8267b0
bezctx *
rpm-build 8267b0
new_bezctx_ps(FILE *f) {
rpm-build 8267b0
    bezctx_ps *result = znew(bezctx_ps, 1);
rpm-build 8267b0
rpm-build 8267b0
    result->base.moveto = bezctx_ps_moveto;
rpm-build 8267b0
    result->base.lineto = bezctx_ps_lineto;
rpm-build 8267b0
    result->base.quadto = bezctx_ps_quadto;
rpm-build 8267b0
    result->base.curveto = bezctx_ps_curveto;
rpm-build 8267b0
    result->base.mark_knot = NULL;
rpm-build 8267b0
    result->is_open = 1;
rpm-build 8267b0
    result->f = f;
rpm-build 8267b0
    return &result->base;
rpm-build 8267b0
}
rpm-build 8267b0
rpm-build 8267b0
/* Finishes an old PostScript bezier context */
rpm-build 8267b0
void
rpm-build 8267b0
bezctx_ps_close(bezctx *z)
rpm-build 8267b0
{
rpm-build 8267b0
    bezctx_ps *bc = (bezctx_ps *)z;
rpm-build 8267b0
rpm-build 8267b0
    if (!bc->is_open) fprintf(bc->f, "z\n");
rpm-build 8267b0
    zfree(bc);
rpm-build 8267b0
}
rpm-build 8267b0
```
rpm-build 8267b0
rpm-build 8267b0
## FontForge
rpm-build 8267b0
rpm-build 8267b0
First the header file. This declares a routine which will create a fontforge bezier context, and another which will finish off (and free) the context. This last routine returns a collection of splines, a splineset, fontforge's internal bezier contour type.
rpm-build 8267b0
rpm-build 8267b0
```c
rpm-build 8267b0
/* bezctx_ff.h */
rpm-build 8267b0
#include "spiroentrypoints.h"
rpm-build 8267b0
#include "bezctx.h"
rpm-build 8267b0
rpm-build 8267b0
bezctx *new_bezctx_ff(void);
rpm-build 8267b0
rpm-build 8267b0
struct splineset;
rpm-build 8267b0
rpm-build 8267b0
struct splineset *bezctx_ff_close(bezctx *bc);
rpm-build 8267b0
```
rpm-build 8267b0
rpm-build 8267b0
Then the file itself.
rpm-build 8267b0
rpm-build 8267b0
```c
rpm-build 8267b0
/* bezctx_ff.c */
rpm-build 8267b0
#include "bezctx_ff.h"
rpm-build 8267b0
#include "pfaeditui.h"
rpm-build 8267b0
#ifdef HAVE_IEEEFP_H
rpm-build 8267b0
# include <ieeefp.h>		/* Solaris defines isnan in ieeefp rather than math.h */
rpm-build 8267b0
#else
rpm-build 8267b0
# include <math.h>
rpm-build 8267b0
#endif
rpm-build 8267b0
rpm-build 8267b0
typedef struct {
rpm-build 8267b0
    bezctx base;			/* This is a superclass of bezctx, and this is the entry for the base */
rpm-build 8267b0
    int is_open;
rpm-build 8267b0
    int gotnans;			/* Sometimes spiro fails to converge and we get NaNs. Complain the first */
rpm-build 8267b0
					/* time this happens, but not thereafter */
rpm-build 8267b0
    SplineSet *ss;			/* The fontforge contour which we build up as we go along */
rpm-build 8267b0
} bezctx_ff;
rpm-build 8267b0
rpm-build 8267b0
static void
rpm-build 8267b0
nancheck(bezctx_ff *bc) {
rpm-build 8267b0
rpm-build 8267b0
    if ( !bc->gotnans ) {		/* Called when we get passed a NaN. Complain the first time that happens */
rpm-build 8267b0
	LogError("Spiros did not converge" );
rpm-build 8267b0
	bc->gotnans = true;
rpm-build 8267b0
    }
rpm-build 8267b0
}
rpm-build 8267b0
rpm-build 8267b0
/* This routine starts a new contour */
rpm-build 8267b0
/* So we allocate a new SplineSet, and then add the first point to it */
rpm-build 8267b0
static void
rpm-build 8267b0
bezctx_ff_moveto(bezctx *z, double x, double y, int is_open) {
rpm-build 8267b0
    bezctx_ff *bc = (bezctx_ff *)z;
rpm-build 8267b0
rpm-build 8267b0
    if ( !finite(x) || !finite(y)) {	/* Protection against NaNs */
rpm-build 8267b0
	nancheck(bc);
rpm-build 8267b0
	x = y = 0;
rpm-build 8267b0
    }
rpm-build 8267b0
    if (!bc->is_open) {
rpm-build 8267b0
	SplineSet *ss = chunkalloc(sizeof(SplineSet));
rpm-build 8267b0
	ss->next = bc->ss;
rpm-build 8267b0
	bc->ss = ss;
rpm-build 8267b0
    }
rpm-build 8267b0
    bc->ss->first = bc->ss->last = SplinePointCreate(x,y);
rpm-build 8267b0
    bc->is_open = is_open;
rpm-build 8267b0
}
rpm-build 8267b0
rpm-build 8267b0
/* This routine creates a linear spline from the previous point specified to this one */
rpm-build 8267b0
static void
rpm-build 8267b0
bezctx_ff_lineto(bezctx *z, double x, double y) {
rpm-build 8267b0
    bezctx_ff *bc = (bezctx_ff *)z;
rpm-build 8267b0
    SplinePoint *sp;
rpm-build 8267b0
rpm-build 8267b0
    if ( !finite(x) || !finite(y)) {
rpm-build 8267b0
	nancheck(bc);
rpm-build 8267b0
	x = y = 0;
rpm-build 8267b0
    }
rpm-build 8267b0
    sp = SplinePointCreate(x,y);
rpm-build 8267b0
    SplineMake3(bc->ss->last,sp);
rpm-build 8267b0
    bc->ss->last = sp;
rpm-build 8267b0
}
rpm-build 8267b0
rpm-build 8267b0
/* This could create a quadratic spline, except fontforge only is prepared to deal with */
rpm-build 8267b0
/*  cubics, so convert the quadratic into the equivalent cubic */
rpm-build 8267b0
static void
rpm-build 8267b0
bezctx_ff_quadto(bezctx *z, double xm, double ym, double x3, double y3)
rpm-build 8267b0
{
rpm-build 8267b0
    bezctx_ff *bc = (bezctx_ff *)z;
rpm-build 8267b0
    double x0, y0;
rpm-build 8267b0
    double x1, y1;
rpm-build 8267b0
    double x2, y2;
rpm-build 8267b0
    SplinePoint *sp;
rpm-build 8267b0
rpm-build 8267b0
    if ( !finite(xm) || !finite(ym) || !finite(x3) || !finite(y3)) {
rpm-build 8267b0
	nancheck(bc);
rpm-build 8267b0
	xm = ym = x3 = y3 = 0;
rpm-build 8267b0
    }
rpm-build 8267b0
    sp = SplinePointCreate(x3,y3);
rpm-build 8267b0
    x0 = bc->ss->last->me.x;
rpm-build 8267b0
    y0 = bc->ss->last->me.y;
rpm-build 8267b0
    x1 = xm + (1./3) * (x0 - xm);
rpm-build 8267b0
    y1 = ym + (1./3) * (y0 - ym);
rpm-build 8267b0
    x2 = xm + (1./3) * (x3 - xm);
rpm-build 8267b0
    y2 = ym + (1./3) * (y3 - ym);
rpm-build 8267b0
    bc->ss->last->nextcp.x = x1;
rpm-build 8267b0
    bc->ss->last->nextcp.y = y1;
rpm-build 8267b0
    bc->ss->last->nonextcp = false;
rpm-build 8267b0
    sp->prevcp.x = x2;
rpm-build 8267b0
    sp->prevcp.y = y2;
rpm-build 8267b0
    sp->noprevcp = false;
rpm-build 8267b0
    SplineMake3(bc->ss->last,sp);
rpm-build 8267b0
    bc->ss->last = sp;
rpm-build 8267b0
}
rpm-build 8267b0
rpm-build 8267b0
/* And this creates a cubic */
rpm-build 8267b0
static void
rpm-build 8267b0
bezctx_ff_curveto(bezctx *z, double x1, double y1, double x2, double y2,
rpm-build 8267b0
		  double x3, double y3)
rpm-build 8267b0
{
rpm-build 8267b0
    bezctx_ff *bc = (bezctx_ff *)z;
rpm-build 8267b0
    SplinePoint *sp;
rpm-build 8267b0
rpm-build 8267b0
    if ( !finite(x1) || !finite(y1) || !finite(x2) || !finite(y2) || !finite(x3) || !finite(y3)) {
rpm-build 8267b0
	nancheck(bc);
rpm-build 8267b0
	x1 = y1 = x2 = y2 = x3 = y3 = 0;
rpm-build 8267b0
    }
rpm-build 8267b0
    sp = SplinePointCreate(x3,y3);
rpm-build 8267b0
    bc->ss->last->nextcp.x = x1;
rpm-build 8267b0
    bc->ss->last->nextcp.y = y1;
rpm-build 8267b0
    bc->ss->last->nonextcp = false;
rpm-build 8267b0
    sp->prevcp.x = x2;
rpm-build 8267b0
    sp->prevcp.y = y2;
rpm-build 8267b0
    sp->noprevcp = false;
rpm-build 8267b0
    SplineMake3(bc->ss->last,sp);
rpm-build 8267b0
    bc->ss->last = sp;
rpm-build 8267b0
}
rpm-build 8267b0
rpm-build 8267b0
/* Allocates and initializes a new fontforge bezier context */
rpm-build 8267b0
bezctx *
rpm-build 8267b0
new_bezctx_ff(void) {
rpm-build 8267b0
    bezctx_ff *result = chunkalloc(sizeof(bezctx_ff));
rpm-build 8267b0
rpm-build 8267b0
    result->base.moveto = bezctx_ff_moveto;
rpm-build 8267b0
    result->base.lineto = bezctx_ff_lineto;
rpm-build 8267b0
    result->base.quadto = bezctx_ff_quadto;
rpm-build 8267b0
    result->base.curveto = bezctx_ff_curveto;
rpm-build 8267b0
    result->base.mark_knot = NULL;
rpm-build 8267b0
    result->is_open = 0;
rpm-build 8267b0
    result->gotnans = 0;
rpm-build 8267b0
    result->ss = NULL;
rpm-build 8267b0
    return &result->base;
rpm-build 8267b0
}
rpm-build 8267b0
rpm-build 8267b0
/* Finishes an old fontforge bezier context, and returns the contour which was created */
rpm-build 8267b0
struct splinepointlist *
rpm-build 8267b0
bezctx_ff_close(bezctx *z)
rpm-build 8267b0
{
rpm-build 8267b0
    bezctx_ff *bc = (bezctx_ff *)z;
rpm-build 8267b0
    SplineSet *ss = bc->ss;
rpm-build 8267b0
rpm-build 8267b0
    if (!bc->is_open && ss!=NULL ) {
rpm-build 8267b0
	if ( ss->first!=ss->last &&
rpm-build 8267b0
		RealNear(ss->first->me.x,ss->last->me.x) &&
rpm-build 8267b0
		RealNear(ss->first->me.y,ss->last->me.y)) {
rpm-build 8267b0
	    ss->first->prevcp = ss->last->prevcp;
rpm-build 8267b0
	    ss->first->noprevcp = ss->last->noprevcp;
rpm-build 8267b0
	    ss->first->prev = ss->last->prev;
rpm-build 8267b0
	    ss->first->prev->to = ss->first;
rpm-build 8267b0
	    SplinePointFree(ss->last);
rpm-build 8267b0
	    ss->last = ss->first;
rpm-build 8267b0
	} else {
rpm-build 8267b0
	    SplineMake3(ss->last,ss->first);
rpm-build 8267b0
	    ss->last = ss->first;
rpm-build 8267b0
	}
rpm-build 8267b0
    }
rpm-build 8267b0
    chunkfree(bc,sizeof(bezctx_ff));
rpm-build 8267b0
return( ss );
rpm-build 8267b0
}
rpm-build 8267b0
```