1# Spiro ![](spiral32.png) [![Coverity Scan Build Status](https://scan.coverity.com/projects/794/badge.svg?flat=1)](https://scan.coverity.com/projects/794) 2 3## Introduction 4 5![](spiro-a.png) 6 7Spiro is the creation of [Raph Levien](http://www.levien.com/). It simplifies the drawing of beautiful curves. 8 9Using bézier splines an artist can easily draw curves with the same slope on either side of an on-curve point. Spiros, on the other hand, are based on clothoid splines which make it easy to maintain constant curvature as well as constant slope. Such curves will simply look nicer. 10 11Raph Levien's spiro splines only use on-curve points and so are easier to use and more intuitive to the artist. 12 13This library will take an array of spiro control points and convert them into a series of bézier splines which can then be used in the myriad of ways the world has come to use béziers. 14 15## Installation 16 17Installing from Git master requires 2 preparatory steps: 18 19First, you need to create the ./configure script if you do not have it yet 20```sh 21autoreconf -i (or use 'autoreconf --install --force' for more modern setups) 22automake --foreign -Wall 23``` 24 25Second, you then use the usual steps to compile the library. 26Various operating systems and setups will need ./configure options set. 27The INSTALLATION file has detailed info for `configure' options. 28Example install steps for Linux, FreeBSD, Win32/64 are shown below: 29 30Installing on Linux 31```sh 32./configure 33make 34make check 35sudo make install 36``` 37 38Installing on FreeBSD 39```sh 40./configure --prefix=$(pwd)/BUILD 41make clean 42make 43make install 44``` 45 46Installing on Windows 32-bit 47```sh 48./configure --host=i686-w64-mingw32 --prefix=$(pwd)/build-w32 49make clean 50make 51make install 52``` 53 54Installing on Windows 64-bit 55```sh 56./configure --host=x86_64-w64-mingw32 --prefix=$(pwd)/build-w64 57make clean 58make 59make install 60``` 61 62NOTE: Some Distros and Operating Systems may require you to run 'ldconfig' 63to recognize LibSpiro if you are not rebooting your computer first before 64installing another program that depends on LibSpiro. To do this, you may 65need to run 'ldconfig' in 'su -' mode after you have done 'make install': 66```sh 67 $ su - 68 # ldconfig 69 # exit 70 $ 71``` 72 73## Usage 74 75### In FontForge 76 77FontForge will autodetect libspiro when it is installed in the usual way. 78 79An exception to this is with the Mac bundled version (where `FontForge.app` is copied to `/Applications`). To install your compiled version into the bundle, run ```sh ./configure --prefix=/Applications/FontForge.app/Contents/Resources/opt/local/ ``` 80 81#### Crash Reporting 82 83Mac OS X: A helping script, `./fontforge.sh` is provided to run FontForge inside a debugger to get useful information on solving crashes. An example issue is at https://github.com/fontforge/libspiro/issues/4 84 85# Developing 86 87### Two methods of using libspiro in your programs 88 89- [C](#programming-with-libspiro-in-c) 90- [Java](#programming-with-libspiro-in-java) 91 92### Programming with libspiro in C 93 94- Basic Types 95 - [spiro control point](#the-spiro-control-point) 96 - [ncq control value](#the-ncq-control-value) 97 - [bézier context](#the-bezier-context) 98- [Header file](#calling-into-libspiro) 99- Entry points 100 - int [SpiroCPsToBezier2](#spirocpstobezier2)(spiro_cp *,int n,int ncq,int is_closed,bezctx *) 101 - int [TaggedSpiroCPsToBezier2](#taggedspirocpstobezier2)(spiro_cp *,int ncq,bezctx *) 102 103#### Basic Types 104 105#### The spiro control point 106 107```c 108typedef struct { 109 double x; 110 double y; 111 char ty; 112} spiro_cp; 113 114 /* Possible values of the "ty" field. */ 115#define SPIRO_CORNER 'v' 116#define SPIRO_G4 'o' 117#define SPIRO_G2 'c' 118#define SPIRO_LEFT '[' 119#define SPIRO_RIGHT ']' 120#define SPIRO_ANCHOR 'a' 121#define SPIRO_HANDLE 'h' 122 123 /* For a closed contour add an extra cp with a ty set to */ 124#define SPIRO_END 'z' 125 /* For an open contour the first cp must have a ty set to*/ 126#define SPIRO_OPEN_CONTOUR '{' 127 /* For an open contour the last cp must have a ty set to */ 128#define SPIRO_END_OPEN_CONTOUR '}' 129``` 130 131A spiro control point contains a location and a point type. There are six basic types of spiro control points: 132 133- A corner point – where the slopes and curvatures of the incoming and outgoing splines are unconstrained 134- A G4 curve point – Continuous up to the fourth derivative 135- A G2 curve point – Continuous up to the second derivative. 136- A left constraint point – Used to connect a curve to a straight line 137- A right constraint point – Used to connect a straight line to a curve. 138 If you have a contour which is drawn clockwise, and you have a straight segment at the top, then the left point of that straight segment should be a left constraint, and the right point should be a right constraint. 139- An anchor - Is a knot point with a fixed angle (followed by the handle cp which creates the angle). 140 The anchor behaves as both left and right constraint points at one single point. 141 142The left constraint, right constraint, anchor and handle points are easiest explained using 143examples from path5 and path6 which are tested in tests/call-test5.c and tests/call-test6.c 144 145![](path5.png) ![](path6.png) 146```c 147path5[] path6[] 148{ 0, 0,'{'}, { 0, 0,'{'}, 149{100, 100,'c'}, {100, 100,'c'}, 150{200, 200,'['}, {200, 200,'a'}, 151{300, 200,']'}, {300, 200,'h'}, 152{400, 150,'c'}, {300, 150,'c'}, 153{300, 100,'['}, {200, 100,'a'}, 154{200, 100,']'}, {150, 100,'h'}, 155{150, 50,'c'}, {150, 50,'c'}, 156{100, 0,'['}, {100, 0,'a'}, 157{ 0,-100,']'}, { 0,-100,'h'}, 158{-50,-200,'c'}, { 50,-100,'c'}, 159{-80,-250,'}'}, { 20,-150,'}'}, 160``` 161 162 163## The ncq control value 164 165There is a need to pass additional information to libspiro, and therefore the 'ncq' value was added. 166'ncq' can be thought of as toggle switches telling libspiro how to work with the source spiro control points. 167Below is the current toggle switch definitions, and default 'ncq' value is zero. 168 169```c 170/* int ncq flags and values */ 171#define SPIRO_INCLUDE_LAST_KNOT 0x0100 172#define SPIRO_RETRO_VER1 0x0400 173#define SPIRO_REVERSE_SRC 0x0800 174#define SPIRO_ARC_CUB_QUAD_CLR 0x7FFF 175#define SPIRO_ARC_CUB_QUAD_MASK 0x7000 176#define SPIRO_CUBIC_TO_BEZIER 0x0000 177#define SPIRO_CUBIC_MIN_MAYBE 0x1000 178#define SPIRO_ARC_MAYBE 0x2000 179#define SPIRO_ARC_MIN_MAYBE 0x3000 180#define SPIRO_QUAD0_TO_BEZIER 0x4000 181``` 182 183The definitions for ncq (above) are: 184 185SPIRO_INCLUDE_LAST_KNOT: 186Existing libspiro behaviour is to add a knot point to match each spiro point, but does not include the last knot. 187This option includes the last knot with the existing output results while the spiro is still open. Closed spiros should refer to the first knot point since the last and first knot are the same. 188 189SPIRO_RETRO_VER1: 190This newer version of libspiro has modified the way path calculations are made. 191The reason for this was seen as an advantage, because it allows a user to scale and move spiro paths, which is a common expectation in graphics, and there are other added advantages, such as making the path as part of templates, and more. 192An effort was made to keep results as close to original as possible, but this was not possible due to scaling factors in the calculations. 193As the main user for libspiro is FontForge, users such as font designers may see the least change since scaling targets x={0..1000}, y={0..1000}, while other users in graphics may see changes since they can be using scales much larger than 1000. 194The good news here is 'SPIRO_RETRO_VER1' allows the user to toggle libspiro to use the older calculation method if the user needs backwards compatibility, otherwise, leaving this off allows spiros to use the new calculation method which allows scaling and moving spiro paths. 195Older programs that use the older libspiro interfaces will see no-change since they use the older calculation method to maintain backwards compatibility. 196 197SPIRO_REVERSE_SRC: 198There may be a need to reverse the spiro path direction. 199This option edits the source spiro path, and reverses the information, then proceeds to continue doing libspiro calculations with the reversed path. 200When libspiro is done calculating bezier output, you will also have a reversed (input) spiro path, therefore save the new spiro path if you need it. 201This simplifies this process for the calling program to a simple option 'SPIRO_REVERSE_SRC', and the results are up to date as per ths version of libspiro. 202NOTE - libspiro calculations are a one-way calculation, so you are not likely to see the same results in the reverse spiro path direction, but if you need this option, it is available here. 203 204SPIRO_CUBIC_TO_BEZIER: 205LibSpiro default action is to create cubic bezier curves. 206 207SPIRO_CUBIC_MIN_MAYBE: 208Cubic arcs can potentially be made with greater bends and less points. 209 210SPIRO_ARC_MAYBE and SPIRO_ARC_MIN_MAYBE: 211Instead of the default cubic output, this exposes the midpoint, which might be useful to someone. 212 213SPIRO_QUAD0_TO_BEZIER: 214Rough approximation of quadratic to bezier curves. Knot points will have smooth connection but midpoints may be visually okay or not. 215 216 217#### The bezier context 218 219```c 220struct _bezctx { 221 /* Called by spiro to start a contour */ 222 void (*moveto)(bezctx *bc, double x, double y, int is_open); 223 224 /* Called by spiro to move from the last point to the next one on a straight line */ 225 void (*lineto)(bezctx *bc, double x, double y); 226 227 /* Called by spiro to move from the last point to the next along a quadratic bézier spline */ 228 /* (x1,y1) is the quadratic bézier control point and (x2,y2) will be the new end point */ 229 void (*quadto)(bezctx *bc, double x1, double y1, double x2, double y2); 230 231 /* Called by spiro to move from the last point to the next along a cubic bézier spline */ 232 /* (x1,y1) and (x2,y2) are the two off-curve control point and (x3,y3) will be the new end point */ 233 void (*curveto)(bezctx *bc, double x1, double y1, double x2, double y2, 234 double x3, double y3); 235 236 /* Called by spiro to notify calling function this is a knot point */ 237 void (*mark_knot)(bezctx *bc, int knot_idx); 238}; 239``` 240 241You must create a super-class of this abstract type that handles the creation of your particular representation of bézier splines. As an [example I provide the one used by Raph to generate PostScript output](bezctx.md) (cubic béziers). Spiro will convert a set of spiro_cps into a set of bézier curves. As it does so it will call the appropriate routine in your bézier context with this information – this should allow you to create your own internal representation of those curves. 242 243#### Calling into libspiro 244 245Your program needs this Libspiro header file: 246 247```c 248#include <spiroentrypoints.h> 249``` 250 251You must define a bézier context that is appropriate for your internal splines (See [Raph's PostScript example](bezctx.md)). 252 253#### SpiroCPsToBezier2 254 255You must create an array of spiro control points: 256 257```c 258 spiro_cp points[4]; 259 260 /* This defines something very like a circle, centered at the origin with radius 100 */ 261 262 points[0].x = -100; points[0].y = 0; points[0].ty = SPIRO_G4; 263 points[1].x = 0; points[1].y = 100; points[1].ty = SPIRO_G4; 264 points[2].x = 100; points[2].y = 0; points[2].ty = SPIRO_G4; 265 points[3].x = 0; points[3].y = -100; points[3].ty = SPIRO_G4; 266``` 267 268![](closedspiro.png) 269 270Then call `SpiroCPsToBezier2`, a routine which takes 5 arguments and returns bc and an integer pass/fail flag. 271 2721. An array of input spiros 2732. The number of elements in the spiros array (this example has 4) 2743. Additional ncq control variable (default==0) 2754. Whether this describes a closed (True=1) or open (False=0) contour 2765. A bézier results output context 2776. An integer success flag. 1 = completed task and have valid bézier results, or 0 = unable to complete task, bézier results are invalid. 278 279 ```c 280 bc = new_bezctx_ps(); 281 success = SpiroCPsToBezier2(points,4,ncq,True,bc) 282 bezctx_ps_close(bc); 283 ``` 284 285#### TaggedSpiroCPsToBezier2 286 287Or call `TaggedSpiroCPsToBezier2`. This routine requires that the array of spiro control points be tagged according to Raph's internal conventions. A closed curve will have an extra control point attached to the end of it with a type of `SPIRO_END`; 288 289```c 290 spiro_cp points[5]; 291 292 points[0].x = -100; points[0].y = 0; points[0].ty = SPIRO_G4; 293 points[1].x = 0; points[1].y = 100; points[1].ty = SPIRO_G4; 294 points[2].x = 100; points[2].y = 0; points[2].ty = SPIRO_G4; 295 points[3].x = 0; points[3].y = -100; points[3].ty = SPIRO_G4; 296 points[4].x = 0; points[4].y = 0; points[4].ty = SPIRO_END; 297``` 298 299(The x,y location of this last SPIRO_END point is irrelevant). 300 301An open curve will have the type of the first control point set to `SPIRO_OPEN_CONTOUR` and the last to `SPIRO_END_OPEN_CONTOUR`. 302 303```c 304 spiro_cp points[4]; 305 306 points[0].x = -100; points[0].y = 0; points[0].ty = SPIRO_OPEN_CONTOUR; 307 points[1].x = 0; points[1].y = 100; points[1].ty = SPIRO_G4; 308 points[2].x = 100; points[2].y = 0; points[2].ty = SPIRO_G4; 309 points[3].x = 0; points[3].y = -100; points[3].ty = SPIRO_END_OPEN_CONTOUR; 310``` 311 312![](openspiro.png) 313 314(In an open contour the point types of the first and last control points are going to be ignored). 315 316In this case there is no need to provide a point count nor an open/closed contour flag. That information can be obtained from the control points themselves. So `TaggedSpiroCPsToBezier2` only takes 3 arguments and returns bc and an integer pass/fail flag. 317 3181. An array of input spiros 3192. A bézier results output context 3203. Additional ncq control variable (default==0) 3214. An integer success flag. 1 = completed task and have valid bézier results, or 0 = unable to complete task, bézier results are invalid. 322 323 ```c 324 bc = new_bezctx_ps(); 325 success = TaggedSpiroCPsToBezier2(points,ncq,bc) 326 bezctx_ps_close(bc); 327 ``` 328 329### Programming with libspiro in Java 330 331**CAVEAT:** I'm not proficient in Java. 332 333### Classes 334 335- `SpiroPointType` – this is an enumerated type which defines the same pointtypes used by the C interface: `CORNER`, `G4`, `G2`, `LEFT`, `RIGHT`, `END`, `OPEN`, `OPEN_END` 336 337- `SpiroCP` 338 ```java 339 public class SpiroCP { 340 public double x,y; 341 SpiroPointType type; 342 public SpiroCP(double xx, double yy, SpiroPointType ty); 343 public String toString(); 344 } 345 ``` 346 347- `SpiroBezierContext` – a Java interface used in conversion of an array of SpiroCPs to a Bézier contour. 348 349 ```java 350 public interface SpiroBezierContext { 351 void MoveTo(double x, double y, boolean isOpen); 352 void LineTo(double x, double y); 353 void QuadTo(double x1, double y1, double x2, double y2); 354 void CubicTo(double x1, double y1, double x2, double y2, double x3, double y3); 355 void MarkKnot(int knotIdx); 356 } 357 ``` 358 359- `Spiro` – a class with only static members: 360 361 ```java 362 public class Spiro { 363 // takes an array of SpiroCPs and converts to a Bézier 364 static public void 365 SpiroCPsToBezier(SpiroCP [] spiros,int n,boolean isclosed, 366 SpiroBezierContext bc); 367 // takes an array of SpiroCPs (the array contains its own terminator and indication of whether the contour is open or closed) 368 static public void 369 TaggedSpiroCPsToBezier(SpiroCP [] spiros,SpiroBezierContext bc); 370 371 // Two routines for reading and writing one of Raph's plate files 372 static public void 373 SavePlateFile(Writer output,SpiroCP [][] spirocontours) 374 throws IOException; 375 static public SpiroCP [][] 376 ReadPlateFile(BufferedReader input) throws IOException; 377 } 378 ``` 379