spiro 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,37 @@
1
+ #ifndef _SPIRO_H
2
+ #define _SPIRO_H
3
+
4
+ typedef struct {
5
+ /* User passes an array of SpiroCP in this format for Spiro to solve */
6
+ double x; /* Spiro CodePoint Xloc */
7
+ double y; /* Spiro CodePoint Yloc */
8
+ char ty; /* Spiro CodePoint Type */
9
+ } spiro_cp;
10
+
11
+ struct spiro_seg_s {
12
+ /* run_spiro() uses array of information given in the structure above and */
13
+ /* creates an array in this structure format to use by spiro_to_bpath for */
14
+ /* building bezier curves */
15
+ double x; /* SpiroCP segment_chord startX */
16
+ double y; /* SpiroCP segment_chord startY */
17
+ char ty; /* Spiro CodePoint Type */
18
+ double bend_th; /* bend theta between this vector and next vector */
19
+ double ks[4];
20
+ double seg_ch; /* segment_chord distance from xy to next SpiroCP */
21
+ double seg_th; /* segment_theta angle for this SpiroCP */
22
+ double l;
23
+ };
24
+
25
+ typedef struct spiro_seg_s spiro_seg;
26
+
27
+ spiro_seg *
28
+ run_spiro(const spiro_cp *src, int n);
29
+
30
+ void
31
+ free_spiro(spiro_seg *s);
32
+
33
+ void
34
+ spiro_to_bpath(const spiro_seg *s, int n, bezctx *bc);
35
+
36
+ double get_knot_th(const spiro_seg *s, int i);
37
+ #endif
@@ -0,0 +1,68 @@
1
+ /*
2
+ ppedit - A pattern plate editor for Spiro splines.
3
+ Copyright (C) 2007 Raph Levien
4
+
5
+ This program is free software; you can redistribute it and/or
6
+ modify it under the terms of the GNU General Public License
7
+ as published by the Free Software Foundation; either version 3
8
+ of the License, or (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program; if not, write to the Free Software
17
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18
+ 02110-1301, USA.
19
+
20
+ */
21
+ //#include "spiro-config.h"
22
+ #ifdef VERBOSE
23
+ #include <stdio.h>
24
+ #endif
25
+
26
+ #include "bezctx.h"
27
+
28
+ void bezctx_moveto(bezctx *bc, double x, double y, int is_open)
29
+ {
30
+ #ifdef VERBOSE
31
+ printf("moveto(%g,%g)_%d\n",x,y,is_open);
32
+ #endif
33
+ bc->moveto(bc, x, y, is_open);
34
+ }
35
+
36
+ void bezctx_lineto(bezctx *bc, double x, double y)
37
+ {
38
+ #ifdef VERBOSE
39
+ printf("lineto(%g,%g)\n",x,y);
40
+ #endif
41
+ bc->lineto(bc, x, y);
42
+ }
43
+
44
+ void bezctx_quadto(bezctx *bc, double x1, double y1, double x2, double y2)
45
+ {
46
+ #ifdef VERBOSE
47
+ printf("quadto(%g,%g, %g,%g)\n",x1,y1,x2,y2);
48
+ #endif
49
+ bc->quadto(bc, x1, y1, x2, y2);
50
+ }
51
+
52
+ void bezctx_curveto(bezctx *bc, double x1, double y1, double x2, double y2,
53
+ double x3, double y3)
54
+ {
55
+ #ifdef VERBOSE
56
+ printf("curveto(%g,%g, %g,%g, %g,%g)\n",x1,y1,x2,y2,x3,y3);
57
+ #endif
58
+ bc->curveto(bc, x1, y1, x2, y2, x3, y3);
59
+ }
60
+
61
+ void bezctx_mark_knot(bezctx *bc, int knot_idx)
62
+ {
63
+ #ifdef VERBOSE
64
+ printf("mark_knot()_%d\n",knot_idx);
65
+ #endif
66
+ if (bc->mark_knot)
67
+ bc->mark_knot(bc, knot_idx);
68
+ }
@@ -0,0 +1,23 @@
1
+ #ifndef _BEZCTX_H
2
+ #define _BEZCTX_H
3
+ #include "bezctx_intf.h"
4
+
5
+ struct _bezctx {
6
+ /* Called by spiro to start a contour */
7
+ void (*moveto)(bezctx *bc, double x, double y, int is_open);
8
+
9
+ /* Called by spiro to move from the last point to the next one on a straight line */
10
+ void (*lineto)(bezctx *bc, double x, double y);
11
+
12
+ /* Called by spiro to move from the last point to the next along a quadratic bezier spline */
13
+ /* (x1,y1) is the quadratic bezier control point and (x2,y2) will be the new end point */
14
+ void (*quadto)(bezctx *bc, double x1, double y1, double x2, double y2);
15
+
16
+ /* Called by spiro to move from the last point to the next along a cubic bezier spline */
17
+ /* (x1,y1) and (x2,y2) are the two off-curve control point and (x3,y3) will be the new end point */
18
+ void (*curveto)(bezctx *bc, double x1, double y1, double x2, double y2,
19
+ double x3, double y3);
20
+
21
+ void (*mark_knot)(bezctx *bc, int knot_idx);
22
+ };
23
+ #endif
@@ -0,0 +1,23 @@
1
+ #ifndef _BEZCTX_INTF_H
2
+ #define _BEZCTX_INTF_H
3
+ typedef struct _bezctx bezctx;
4
+
5
+ bezctx *
6
+ new_bezctx(void);
7
+
8
+ void
9
+ bezctx_moveto(bezctx *bc, double x, double y, int is_open);
10
+
11
+ void
12
+ bezctx_lineto(bezctx *bc, double x, double y);
13
+
14
+ void
15
+ bezctx_quadto(bezctx *bc, double x1, double y1, double x2, double y2);
16
+
17
+ void
18
+ bezctx_curveto(bezctx *bc, double x1, double y1, double x2, double y2,
19
+ double x3, double y3);
20
+
21
+ void
22
+ bezctx_mark_knot(bezctx *bc, int knot_idx);
23
+ #endif
@@ -0,0 +1,91 @@
1
+ #include <stdlib.h>
2
+ #include "bezctx_rb.h"
3
+
4
+ Splines splines;
5
+
6
+ void initSplines(Splines *a, size_t initialSize) {
7
+ a->array = (node *)malloc(initialSize * sizeof(node));
8
+ a->used = 0;
9
+ a->size = initialSize;
10
+ }
11
+
12
+ void appendSplines(Splines *a, node element) {
13
+ if (a->used == a->size) {
14
+ a->size *= 2;
15
+ a->array = (node *)realloc(a->array, a->size * sizeof(node));
16
+ }
17
+ a->array[a->used++] = element;
18
+ }
19
+
20
+ void freeSplines(Splines *a) {
21
+ free(a->array);
22
+ a->array = NULL;
23
+ a->used = a->size = 0;
24
+ }
25
+
26
+ typedef struct {
27
+ bezctx base; // This is a superclass of bezctx, entry for the base
28
+ int is_open;
29
+ } bezctx_rb;
30
+
31
+ /* This routine starts a new contour */
32
+ static void bezctx_rb_moveto(bezctx *z, double x, double y, int is_open) {
33
+ bezctx_rb *bc = (bezctx_rb *)z;
34
+
35
+ bc->is_open = is_open;
36
+
37
+ node n = { x, y, SPLINE_CORNER };
38
+ appendSplines(&splines, n);
39
+ }
40
+
41
+ /* This routine creates a linear spline from the previous point specified to this one */
42
+ void bezctx_rb_lineto(bezctx *z, double x, double y) {
43
+ bezctx_rb *bc = (bezctx_rb *)z;
44
+
45
+ node n = { x, y, SPLINE_CORNER };
46
+ appendSplines(&splines, n);
47
+ }
48
+
49
+ /* This creates a qubic curve */
50
+ void bezctx_rb_quadto(bezctx *z, double xm, double ym, double x3, double y3) {
51
+ bezctx_rb *bc = (bezctx_rb *)z;
52
+
53
+ node n1 = { xm, ym, SPLINE_QUADRATIC };
54
+ appendSplines(&splines, n1);
55
+
56
+ node n2 = { x3, y3, SPLINE_CORNER };
57
+ appendSplines(&splines, n2);
58
+ }
59
+
60
+ /* And this creates a cubic */
61
+ void bezctx_rb_curveto(bezctx *z, double x1, double y1, double x2, double y2,
62
+ double x3, double y3) {
63
+ bezctx_rb *bc = (bezctx_rb *)z;
64
+
65
+ node n1 = { x1, y1, SPLINE_CUBIC };
66
+ appendSplines(&splines, n1);
67
+ node n2 = { x2, y2, SPLINE_CUBIC };
68
+ appendSplines(&splines, n2);
69
+ node n3 = { x3, y3, SPLINE_CORNER };
70
+ appendSplines(&splines, n3);
71
+ }
72
+
73
+ /* Allocates and initializes a new bezier context */
74
+ bezctx * new_bezctx_rb(void) {
75
+ bezctx_rb *result = (bezctx_rb *)malloc(sizeof(bezctx_rb));
76
+ initSplines(&splines, 20);
77
+
78
+ result->base.moveto = bezctx_rb_moveto;
79
+ result->base.lineto = bezctx_rb_lineto;
80
+ result->base.quadto = bezctx_rb_quadto;
81
+ result->base.curveto = bezctx_rb_curveto;
82
+ result->base.mark_knot = NULL;
83
+ result->is_open = 0;
84
+ return &result->base;
85
+ }
86
+
87
+ /* Finishes an old bezier context */
88
+ void bezctx_rb_close(bezctx *z) {
89
+ bezctx_rb *bc = (bezctx_rb *)z;
90
+ free(bc);
91
+ }
@@ -0,0 +1,32 @@
1
+ #include "spiroentrypoints.h"
2
+ #include "bezctx.h"
3
+
4
+ // Possible values of the "ty" field.
5
+ #define SPLINE_CORNER 1
6
+ #define SPLINE_CUBIC 3
7
+ #define SPLINE_QUADRATIC 4
8
+
9
+ typedef struct {
10
+ double x;
11
+ double y;
12
+ int ty;
13
+ } node;
14
+
15
+ typedef struct Splines {
16
+ node *array;
17
+ size_t used;
18
+ size_t size;
19
+ } Splines;
20
+
21
+ extern Splines splines;
22
+
23
+
24
+ bezctx *new_bezctx_rb(void);
25
+
26
+ //struct splineset;
27
+ //struct Splines;
28
+
29
+ void freeSplines(Splines *a);
30
+
31
+ //struct splineset *bezctx_rb_close(bezctx *bc);
32
+ void bezctx_rb_close(bezctx *z);
@@ -0,0 +1,6 @@
1
+ require 'mkmf'
2
+
3
+ #CONFIG['LDSHARED'] = "$(CXX) -shared"
4
+
5
+ #dir_config('spiro')
6
+ create_makefile('spiro/spiro')
@@ -0,0 +1,121 @@
1
+ #include <ruby.h>
2
+ #include "spiroentrypoints.h" // call spiro through here
3
+ #include "bezctx_rb.h" // bezctx structure
4
+ #include "_spiro.h"
5
+
6
+ static ID id_corner;
7
+ static ID id_g4;
8
+ static ID id_g2;
9
+ static ID id_left;
10
+ static ID id_right;
11
+
12
+ static inline char sym_to_spiro_type(VALUE sym) {
13
+ ID inp = rb_to_id(sym);
14
+
15
+ if (inp == id_corner) {
16
+ return SPIRO_CORNER;
17
+ } else if (inp == id_g2) {
18
+ return SPIRO_G2;
19
+ } else if (inp == id_g4) {
20
+ return SPIRO_G4;
21
+ } else if (inp == id_left) {
22
+ return SPIRO_LEFT;
23
+ } else if (inp == id_right) {
24
+ return SPIRO_RIGHT;
25
+ }
26
+
27
+ rb_raise(rb_eArgError, "%s", "Expected :g2, :g4, :node, :left or :right");
28
+ }
29
+
30
+ static inline VALUE spline_type_to_sym(int ty) {
31
+ if (ty == SPLINE_CORNER) {
32
+ return ID2SYM(rb_intern("node"));
33
+ } else if (ty == SPLINE_CUBIC) {
34
+ return ID2SYM(rb_intern("cubic"));
35
+ } else if (ty == SPLINE_QUADRATIC) {
36
+ return ID2SYM(rb_intern("quadratic"));
37
+ }
38
+ }
39
+
40
+ static inline int bool_to_int(VALUE b) {
41
+ switch (TYPE(b)) {
42
+ case T_TRUE:
43
+ return 1;
44
+ break;
45
+ case T_FALSE:
46
+ return 0;
47
+ break;
48
+ default:
49
+ rb_raise(rb_eTypeError, "not a boolean");
50
+ break;
51
+ }
52
+ }
53
+
54
+ static VALUE spiros_to_splines(VALUE mod, VALUE spirosValue, VALUE closedValue) {
55
+ Check_Type(spirosValue, T_ARRAY);
56
+
57
+ // Take Ruby array of points and turn into Spiro C struct array
58
+ spiro_cp spiros[RARRAY_LEN(spirosValue)];
59
+ for (int i = 0; i != RARRAY_LEN(spirosValue); i++) {
60
+ VALUE nodeValue = rb_ary_entry(spirosValue, i);
61
+ // Validate node is an array and length 3 (x, y, type)
62
+ Check_Type(nodeValue, T_ARRAY);
63
+ if (RARRAY_LEN(nodeValue) != 3) rb_raise(rb_eArgError, "%s", "Invalid node");
64
+
65
+ // Validate individual node elements
66
+ VALUE xValue = rb_ary_entry(nodeValue, 0);
67
+ if (TYPE(xValue) != T_FIXNUM && TYPE(xValue) != T_FLOAT) {
68
+ rb_raise(rb_eTypeError, "x coord must be float or fixnum");
69
+ }
70
+ VALUE yValue = rb_ary_entry(nodeValue, 1);
71
+ if (TYPE(yValue) != T_FIXNUM && TYPE(yValue) != T_FLOAT) {
72
+ rb_raise(rb_eTypeError, "y coord must be float or fixnum");
73
+ }
74
+ VALUE typeValue = rb_ary_entry(nodeValue, 2);
75
+ Check_Type(typeValue, T_SYMBOL);
76
+
77
+ spiros[i].x = (long)(NUM2DBL(xValue));
78
+ spiros[i].y = (long)(NUM2DBL(yValue));
79
+ spiros[i].ty = sym_to_spiro_type(typeValue);
80
+ }
81
+
82
+ int closed = bool_to_int(closedValue);
83
+
84
+ // bezctx_rb is custom, stores the result of running Spiro
85
+ bezctx *bc = new_bezctx_rb();
86
+ // Run Spiro on the Spiros array and populate custom struct with splines array
87
+ int success = SpiroCPsToBezier0(spiros, RARRAY_LEN(spirosValue), closed, bc);
88
+
89
+ if (success) {
90
+ // Build Ruby array from Spiro array
91
+ VALUE splinesValue = rb_ary_new();
92
+ // Free memory from the custom struct
93
+ bezctx_rb_close(bc);
94
+ for (int i = 0; i < splines.used; i++) {
95
+ VALUE x = rb_float_new(splines.array[i].x);
96
+ VALUE y = rb_float_new(splines.array[i].y);
97
+ VALUE ty = spline_type_to_sym(splines.array[i].ty);
98
+ VALUE nodeValue = rb_ary_new3(3, x, y, ty);
99
+ rb_ary_push(splinesValue, nodeValue);
100
+ }
101
+ freeSplines(&splines);
102
+
103
+ return splinesValue;
104
+ }
105
+ else {
106
+ bezctx_rb_close(bc);
107
+ freeSplines(&splines);
108
+ return Qnil;
109
+ }
110
+ }
111
+
112
+ void Init_spiro(void) {
113
+ id_corner = rb_intern("node");
114
+ id_g4 = rb_intern("g4");
115
+ id_g2 = rb_intern("g2");
116
+ id_left = rb_intern("left");
117
+ id_right = rb_intern("right");
118
+
119
+ VALUE mSpiro = rb_define_module("Spiro");
120
+ rb_define_singleton_method(mSpiro, "spiros_to_splines", spiros_to_splines, 2);
121
+ }
@@ -0,0 +1,83 @@
1
+ /*
2
+ libspiro - conversion between spiro control points and bezier's
3
+ Copyright (C) 2007 Raph Levien
4
+
5
+ This program is free software; you can redistribute it and/or
6
+ modify it under the terms of the GNU General Public License
7
+ as published by the Free Software Foundation; either version 3
8
+ of the License, or (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program; if not, write to the Free Software
17
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18
+ 02110-1301, USA.
19
+
20
+ */
21
+ /* Interface routines to Raph's spiro package. */
22
+
23
+ #include "spiroentrypoints.h"
24
+
25
+ /* These two functions are kept for backwards compatibility */
26
+ void SpiroCPsToBezier(spiro_cp *spiros,int n,int isclosed,bezctx *bc) {
27
+ SpiroCPsToBezier0(spiros,n,isclosed,bc);
28
+ }
29
+ void TaggedSpiroCPsToBezier(spiro_cp *spiros,bezctx *bc) {
30
+ TaggedSpiroCPsToBezier0(spiros,bc);
31
+ }
32
+
33
+ int
34
+ SpiroCPsToBezier0(spiro_cp *spiros,int n,int isclosed,bezctx *bc)
35
+ {
36
+ spiro_seg *s;
37
+
38
+ if ( n<=0 )
39
+ return 0;
40
+ if ( isclosed )
41
+ s = run_spiro(spiros,n);
42
+ else {
43
+ char oldty_start = spiros[0].ty;
44
+ char oldty_end = spiros[n-1].ty;
45
+ spiros[0].ty = '{';
46
+ spiros[n-1].ty = '}';
47
+ s = run_spiro(spiros,n);
48
+ spiros[n-1].ty = oldty_end;
49
+ spiros[0].ty = oldty_start;
50
+ }
51
+ if (s) {
52
+ spiro_to_bpath(s,n,bc);
53
+ free_spiro(s);
54
+ return 1; /* success */
55
+ }
56
+ return 0 ; /* spiro did not converge or encountered non-finite values */
57
+ }
58
+
59
+ int
60
+ TaggedSpiroCPsToBezier0(spiro_cp *spiros,bezctx *bc)
61
+ {
62
+ spiro_seg *s;
63
+ int n;
64
+
65
+ for ( n=0; spiros[n].ty!='z' && spiros[n].ty!='}'; ++n );
66
+ if ( spiros[n].ty == '}' ) ++n;
67
+
68
+ if ( n<=0 ) return 0; /* invalid input */
69
+ s = run_spiro(spiros,n);
70
+ if (s) {
71
+ spiro_to_bpath(s,n,bc);
72
+ free_spiro(s);
73
+ return 1; /* success */
74
+ }
75
+ return 0 ; /* spiro did not converge or encountered non-finite values */
76
+ }
77
+
78
+ void SpiroCPsToBezier1(spiro_cp *spiros,int n,int isclosed,bezctx *bc,int *done) {
79
+ *done = SpiroCPsToBezier0(spiros,n,isclosed,bc);
80
+ }
81
+ void TaggedSpiroCPsToBezier1(spiro_cp *spiros,bezctx *bc,int *done) {
82
+ *done = TaggedSpiroCPsToBezier0(spiros,bc);
83
+ }