Spiro

Mechanism for drawing smooth contours with constant curvature at the spline joins.

History

Created by Raph Levien, ex Ghostscript maintainer and font creator. He works now on fuchsia and xi.

FAQ

What is it?

Clothoid to bezier conversions

How cool is it?

Rather.

Competitors?

None

When to use it?

When you have a set of point and want to know it's bezier curve.

Is it dead?

No, it seems to be used in fontforge.

Where is it?

http://libspiro.sourceforge.net/

How to use it

The documentation is fragmeted.

Download

file: 1_download.sh

#!/bin/bash

mkdir -p tmp
cd ./tmp
wget -O libspiro.tar.bz2 -q https://sourceforge.net/projects/libspiro/files/libspiro/20071029/libspiro_src-20071029.tar.bz2/download
tar -xaf libspiro.tar.bz2
cd libspiro-20071029/
./configure --enable-static
make

Init

The great thing is that that you can use name groups directly. Here is one examples, albeit a little modified.

file: main.c

#include <spiroentrypoints.h>
#include <bezctx.h>
#include <zmisc.h>

#include <stdio.h>

typedef struct _bezctx_XXX {
    /* This is a superclass of bezctx, and this is the entry for the base */
    bezctx base;

    double x, y;
    int is_open;
} bezctx_XXX;

static void bezctx_moveto_XXX(bezctx *z, double x, double y, int is_open) {
    /* This routine starts a new contour */
    bezctx_XXX *bc = (bezctx_XXX *)z;

    if (!bc->is_open) {
        fprintf(stdout, "z\n");
    }
    fprintf(stdout, "%g %g m\n", x, y);
    bc->is_open = is_open;
    bc->x = x;
    bc->y = y;
}

void bezctx_lineto_XXX(bezctx *z, double x, double y) {
    /* This routine creates a linear spline from the previous point specified to this one */
    bezctx_XXX *bc = (bezctx_XXX *)z;

    fprintf(stdout, "%g %g l\n", x, y);
    bc->x = x;
    bc->y = y;
}

void bezctx_quadto_XXX(bezctx *z, double xm, double ym, double x3, double y3) {
    /* This could create a quadratic spline, except PostScript only is prepared to deal with */
    /*  cubics, so convert the quadratic into the equivalent cubic */
    bezctx_XXX *bc = (bezctx_XXX *)z;
    double x0, y0;
    double x1, y1;
    double x2, y2;

    x0 = bc->x;
    y0 = bc->y;
    x1 = xm + (1./3) * (x0 - xm);
    y1 = ym + (1./3) * (y0 - ym);
    x2 = xm + (1./3) * (x3 - xm);
    y2 = ym + (1./3) * (y3 - ym);
    fprintf(stdout, "%g %g %g %g %g %g c\n", x1, y1, x2, y2, x3, y3);
    bc->x = x3;
    bc->y = y3;
}

void bezctx_curveto_XXX(bezctx *z, double x1, double y1, double x2, double y2, double x3, double y3) {
    /* And this creates a cubic */
    bezctx_XXX *bc = (bezctx_XXX *)z;

    fprintf(stdout, "%g %g %g %g %g %g c\n", x1, y1, x2, y2, x3, y3);
    bc->x = x3;
    bc->y = y3;
}

static bezctx_XXX * new_bezctx_XXX() {
     bezctx_XXX *result = znew(bezctx_XXX, 1);

    result->base.moveto = bezctx_moveto_XXX;
    result->base.lineto = bezctx_lineto_XXX;
    result->base.quadto = bezctx_quadto_XXX;
    result->base.curveto = bezctx_curveto_XXX;
    result->base.mark_knot = NULL;
    result->is_open = 1;

    return result;
}

static void delete_bezctx_XXX(bezctx_XXX *bc) {
    if (!bc->is_open) {
        fprintf(stdout, "z\n");
    }
    zfree(bc);
}

int main(const int argc, const char **argv){
    spiro_cp c_points[4], o_points[4];
    bezctx_XXX *bc;

    fprintf(stdout, "\nopen:\n");
    c_points[0].x = -100; c_points[0].y =    0; c_points[0].ty = SPIRO_OPEN_CONTOUR;
    c_points[1].x =    0; c_points[1].y =  100; c_points[1].ty = SPIRO_G4;
    c_points[2].x =  100; c_points[2].y =    0; c_points[2].ty = SPIRO_G4;
    c_points[3].x =    0; c_points[3].y = -100; c_points[3].ty = SPIRO_END_OPEN_CONTOUR;
    bc = new_bezctx_XXX();
    TaggedSpiroCPsToBezier(c_points, (bezctx*)&bc->base);
    delete_bezctx_XXX(bc);

    fprintf(stdout, "\nclosed:\n");
    o_points[0].x = -100; o_points[0].y =    0; o_points[0].ty = SPIRO_OPEN_CONTOUR;
    o_points[1].x =    0; o_points[1].y =  100; o_points[1].ty = SPIRO_G4;
    o_points[2].x =  100; o_points[2].y =    0; o_points[2].ty = SPIRO_G4;
    o_points[3].x =    0; o_points[3].y = -100; o_points[3].ty = SPIRO_END_OPEN_CONTOUR;
    bc = new_bezctx_XXX();
    TaggedSpiroCPsToBezier(o_points, (bezctx*)&bc->base);
    delete_bezctx_XXX(bc);

    return 0;
}

The freetype version is a little better because this returns a boolena if the bezier curve had a good fit.

file: 2_compile_and_run.sh

#!/bin/bash

clang -o ./tmp/main main.c -I./tmp/libspiro-20071029/ ./tmp/libspiro-20071029/.libs/libspiro.a -lm
./tmp/main

This gives

open:
100 0 m
-100 26.1799 -89.2227 52.1987 -70.7107 70.7107 c
-52.1987 89.2227 -26.1799 100 0 100 c
26.1799 100 52.1987 89.2227 70.7107 70.7107 c
89.2227 52.1987 100 26.1799 100 0 c
100 -26.1799 89.2227 -52.1987 70.7107 -70.7107 c
52.1987 -89.2227 26.1799 -100 0 -100 c

closed:
-100 0 m
-100 26.1799 -89.2227 52.1987 -70.7107 70.7107 c
-52.1987 89.2227 -26.1799 100 0 100 c
26.1799 100 52.1987 89.2227 70.7107 70.7107 c
89.2227 52.1987 100 26.1799 100 0 c
100 -26.1799 89.2227 -52.1987 70.7107 -70.7107 c
52.1987 -89.2227 26.1799 -100 0 -100 c

Further reading:

We only tipped the top of the iceberg here. There is much more:

https://github.com/fontforge/libspiro