1 /* spline.c: spline and spline list (represented as arrays) manipulation.
2  *
3  * Copyright (C) 1992 Free Software Foundation, Inc.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3, or (at your option)
8  * 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, see <https://www.gnu.org/licenses/>.
17  */
18 
19 #include "config.h"
20 
21 #include <assert.h>
22 
23 #include <glib.h>
24 
25 #include "global.h"
26 #include "bounding-box.h"
27 #include "spline.h"
28 #include "vector.h"
29 
30 /* Return a new spline structure, initialized with (recognizable)
31    garbage.  */
32 
33 spline_type
new_spline(void)34 new_spline (void)
35 {
36   real_coordinate_type coord = { -100.0, -100.0 };
37   spline_type spline;
38 
39   START_POINT (spline)
40     = CONTROL1 (spline)
41     = CONTROL2 (spline)
42     = END_POINT (spline)
43     = coord;
44   SPLINE_DEGREE (spline) = -1;
45   SPLINE_LINEARITY (spline) = 0;
46 
47   return spline;
48 }
49 
50 
51 /* Print a spline in human-readable form.  */
52 
53 void
print_spline(FILE * f,spline_type s)54 print_spline (FILE *f, spline_type s)
55 {
56   if (SPLINE_DEGREE (s) == LINEAR)
57     fprintf (f, "(%.3f,%.3f)--(%.3f,%.3f).\n",
58                 START_POINT (s).x, START_POINT (s).y,
59                 END_POINT (s).x, END_POINT (s).y);
60 
61   else if (SPLINE_DEGREE (s) == CUBIC)
62     fprintf (f, "(%.3f,%.3f)..ctrls(%.3f,%.3f)&(%.3f,%.3f)..(%.3f,%.3f).\n",
63                 START_POINT (s).x, START_POINT (s).y,
64                 CONTROL1 (s).x, CONTROL1 (s).y,
65                 CONTROL2 (s).x, CONTROL2 (s).y,
66                 END_POINT (s).x, END_POINT (s).y);
67 
68   else
69     {
70 /*       FATAL1 ("print_spline: strange degree (%d)", SPLINE_DEGREE (s)); */
71     }
72 }
73 
74 /* Evaluate the spline S at a given T value.  This is an implementation
75    of de Casteljau's algorithm.  See Schneider's thesis (reference in
76    ../limn/README), p.37.  The variable names are taken from there.  */
77 
78 real_coordinate_type
evaluate_spline(spline_type s,real t)79 evaluate_spline (spline_type s, real t)
80 {
81   spline_type V[4];    /* We need degree+1 splines, but assert degree <= 3.  */
82   unsigned i, j;
83   real one_minus_t = 1.0 - t;
84   polynomial_degree degree = SPLINE_DEGREE (s);
85 
86   for (i = 0; i <= degree; i++)
87     V[0].v[i] = s.v[i];
88 
89   for (j = 1; j <= degree; j++)
90     for (i = 0; i <= degree - j; i++)
91       {
92 #if defined (__GNUC__)
93         real_coordinate_type t1 = Pmult_scalar (V[j - 1].v[i], one_minus_t);
94         real_coordinate_type t2 = Pmult_scalar (V[j - 1].v[i + 1], t);
95         V[j].v[i] = Padd (t1, t2);
96 #else
97 	/* HB: the above is really nice, but is there any other compiler
98 	 * supporting this ??
99 	 */
100         real_coordinate_type t1;
101         real_coordinate_type t2;
102         t1.x = V[j - 1].v[i].x * one_minus_t;
103         t1.y = V[j - 1].v[i].y * one_minus_t;
104         t2.x = V[j - 1].v[i + 1].x * t;
105         t2.y = V[j - 1].v[i + 1].y * t;
106         V[j].v[i].x = t1.x + t2.x;
107         V[j].v[i].y = t1.y + t2.y;
108 #endif
109       }
110 
111   return V[degree].v[0];
112 }
113 
114 
115 
116 /* Return a new, empty, spline list.  */
117 
118 spline_list_type *
new_spline_list(void)119 new_spline_list (void)
120 {
121   spline_list_type *answer = g_new (spline_list_type, 1);
122 
123   SPLINE_LIST_DATA (*answer) = NULL;
124   SPLINE_LIST_LENGTH (*answer) = 0;
125 
126   return answer;
127 }
128 
129 
130 /* Return a new spline list with SPLINE as the first element.  */
131 
132 spline_list_type *
init_spline_list(spline_type spline)133 init_spline_list (spline_type spline)
134 {
135   spline_list_type *answer = g_new (spline_list_type, 1);
136 
137   SPLINE_LIST_DATA (*answer) = g_new (spline_type, 1);
138   SPLINE_LIST_ELT (*answer, 0) = spline;
139   SPLINE_LIST_LENGTH (*answer) = 1;
140 
141   return answer;
142 }
143 
144 
145 /* Free the storage in a spline list.  We don't have to free the
146    elements, since they are arrays in automatic storage.  And we don't
147    want to free the list if it was empty.  */
148 
149 void
free_spline_list(spline_list_type * spline_list)150 free_spline_list (spline_list_type *spline_list)
151 {
152   if (SPLINE_LIST_DATA (*spline_list) != NULL)
153     safe_free ((address *) &(SPLINE_LIST_DATA (*spline_list)));
154 }
155 
156 
157 /* Append the spline S to the list SPLINE_LIST.  */
158 
159 void
append_spline(spline_list_type * l,spline_type s)160 append_spline (spline_list_type *l, spline_type s)
161 {
162   assert (l != NULL);
163 
164   SPLINE_LIST_LENGTH (*l)++;
165   SPLINE_LIST_DATA (*l) = g_realloc (SPLINE_LIST_DATA (*l),
166                                SPLINE_LIST_LENGTH (*l) * sizeof (spline_type));
167   LAST_SPLINE_LIST_ELT (*l) = s;
168 }
169 
170 
171 /* Tack the elements in the list S2 onto the end of S1.
172    S2 is not changed.  */
173 
174 void
concat_spline_lists(spline_list_type * s1,spline_list_type s2)175 concat_spline_lists (spline_list_type *s1, spline_list_type s2)
176 {
177   unsigned this_spline;
178   unsigned new_length;
179 
180   assert (s1 != NULL);
181 
182   new_length = SPLINE_LIST_LENGTH (*s1) + SPLINE_LIST_LENGTH (s2);
183 
184   SPLINE_LIST_DATA (*s1) = g_realloc(SPLINE_LIST_DATA (*s1),new_length * sizeof(spline_type));
185 
186   for (this_spline = 0; this_spline < SPLINE_LIST_LENGTH (s2); this_spline++)
187     SPLINE_LIST_ELT (*s1, SPLINE_LIST_LENGTH (*s1)++)
188       = SPLINE_LIST_ELT (s2, this_spline);
189 }
190 
191 
192 /* Return a new, empty, spline list array.  */
193 
194 spline_list_array_type
new_spline_list_array(void)195 new_spline_list_array (void)
196 {
197   spline_list_array_type answer;
198 
199   SPLINE_LIST_ARRAY_DATA (answer) = NULL;
200   SPLINE_LIST_ARRAY_LENGTH (answer) = 0;
201 
202   return answer;
203 }
204 
205 
206 /* Free the storage in a spline list array.  We don't
207    want to free the list if it is empty.  */
208 
209 void
free_spline_list_array(spline_list_array_type * spline_list_array)210 free_spline_list_array (spline_list_array_type *spline_list_array)
211 {
212   unsigned this_list;
213 
214   for (this_list = 0;
215        this_list < SPLINE_LIST_ARRAY_LENGTH (*spline_list_array);
216        this_list++)
217     free_spline_list (&SPLINE_LIST_ARRAY_ELT (*spline_list_array, this_list));
218 
219   if (SPLINE_LIST_ARRAY_DATA (*spline_list_array) != NULL)
220     safe_free ((address *) &(SPLINE_LIST_ARRAY_DATA (*spline_list_array)));
221 }
222 
223 
224 /* Append the spline S to the list SPLINE_LIST_ARRAY.  */
225 
226 void
append_spline_list(spline_list_array_type * l,spline_list_type s)227 append_spline_list (spline_list_array_type *l, spline_list_type s)
228 {
229   SPLINE_LIST_ARRAY_LENGTH (*l)++;
230 
231   SPLINE_LIST_ARRAY_DATA (*l) = g_realloc(SPLINE_LIST_ARRAY_DATA (*l),(SPLINE_LIST_ARRAY_LENGTH (*l))*sizeof(spline_list_type));
232   LAST_SPLINE_LIST_ARRAY_ELT (*l) = s;
233 }
234