• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kakwa / ficlip / 6316780576

26 Sep 2023 06:21PM UTC coverage: 86.936%. Remained the same
6316780576

push

github

kakwa
slipt the code into dedicated files

header to follow

694 of 694 new or added lines in 3 files covered. (100.0%)

925 of 1064 relevant lines covered (86.94%)

254.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.81
/src/path.c
1
/* This file is part of the ficlip clipping library
2
 *
3
 * ficlip is licensed under MIT.
4
 *
5
 * Copyright 2017, Pierre-Francois Carpentier
6
 */
7

8
#include <stdlib.h>
9
#include <stdio.h>
10
#include <string.h>
11
#include <stdbool.h>
12
#include <math.h>
13
#include "ficlip.h"
14
#include "ficlip-private.h"
15

16
void fi_free_path(FI_PATH *path) {
80✔
17
    FI_PATH *tmp = NULL;
80✔
18
    FI_META *meta = NULL;
80✔
19
    if (path != NULL) {
80✔
20
        meta = path->meta;
76✔
21
    }
22
    while (path != NULL) {
5,364✔
23
        tmp = path;
5,284✔
24
        path = path->next;
5,284✔
25
        if (tmp->section.points != NULL)
5,284✔
26
            free(tmp->section.points);
5,242✔
27
        free(tmp);
5,284✔
28
    }
29
    if (meta != NULL)
80✔
30
        free(meta);
46✔
31
}
80✔
32

33
double fi_angle_vect(FI_POINT_D a, FI_POINT_D b) {
24✔
34
    double ret;
35
    double sign = 1;
24✔
36
    ret = acos((a.x * b.x + a.y * b.y) / (sqrt(pow(a.x, 2) + pow(a.y, 2)) *
24✔
37
                                          sqrt(pow(b.x, 2) + pow(b.y, 2))));
24✔
38
    if ((a.x * b.y - a.y * b.x) < 0)
24✔
39
        sign = -1;
10✔
40
    return sign * ret;
24✔
41
}
42

43
// converts endpoints definition of arc to center defition for elliptic arc
44
FI_PARAM_ARC fi_arc_endpoint_to_center(FI_POINT_D s, FI_POINT_D e, FI_POINT_D r,
12✔
45
                                       double phi, FI_SEG_FLAG flag) {
46
    // https://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
47
    int fa = 0;
12✔
48
    phi *= D2R;
12✔
49
    if (flag & FI_LARGE_ARC)
12✔
50
        fa = 1;
4✔
51
    int fs = 0;
12✔
52
    if (flag & FI_SWEEP)
12✔
53
        fs = 1;
8✔
54

55
    FI_PARAM_ARC ret = {0};
12✔
56

57
    FI_POINT_D p1;
58
    p1.x = cos(phi) * (s.x - e.x) / 2 + sin(phi) * (s.y - e.y) / 2;
12✔
59
    p1.y = -1.0 * sin(phi) * (s.x - e.x) / 2 + cos(phi) * (s.y - e.y) / 2;
12✔
60
    double delta = pow(p1.x, 2) / pow(r.x, 2) + pow(p1.y, 2) / pow(r.y, 2);
12✔
61
    if (delta > 1) {
12✔
62
        r.x = sqrt(delta) * r.x;
4✔
63
        r.y = sqrt(delta) * r.y;
4✔
64
    }
65

66
    double coef =
67
        sqrt((pow(r.x, 2) * pow(r.y, 2) - pow(r.x, 2) * pow(p1.y, 2) -
12✔
68
              pow(r.y, 2) * pow(p1.x, 2)) /
12✔
69
             (pow(r.x, 2) * pow(p1.y, 2) + pow(r.y, 2) * pow(p1.x, 2)));
12✔
70
    FI_POINT_D cp;
71
    double sign = 1;
12✔
72
    if (fa == fs)
12✔
73
        sign = -1;
4✔
74
    cp.x = sign * coef * r.x * p1.y / r.y;
12✔
75
    cp.y = sign * coef * r.y * p1.x / r.x * -1;
12✔
76

77
    ret.center.x = cos(phi) * cp.x - sin(phi) * cp.y + (s.x + e.x) / 2;
12✔
78
    ret.center.y = sin(phi) * cp.x + cos(phi) * cp.y + (s.y + e.y) / 2;
12✔
79

80
    FI_POINT_D t1;
81
    FI_POINT_D t2;
82

83
    t1.x = 1;
12✔
84
    t1.y = 0;
12✔
85
    t2.x = (p1.x - cp.x) / r.x;
12✔
86
    t2.y = (p1.y - cp.y) / r.y;
12✔
87
    ret.angle_s = fi_angle_vect(t1, t2);
12✔
88

89
    t1.x = (p1.x - cp.x) / r.x;
12✔
90
    t1.y = (p1.y - cp.y) / r.y;
12✔
91
    t2.x = (-p1.x - cp.x) / r.x;
12✔
92
    t2.y = (-p1.y - cp.y) / r.y;
12✔
93
    ret.angle_d = fi_angle_vect(t1, t2);
12✔
94
    if (fs == 0) {
12✔
95
        if (ret.angle_d > 0) {
4✔
96
            ret.angle_d -= 2 * M_PI;
2✔
97
        }
98
    } else {
99
        if (ret.angle_d < 0) {
8✔
100
            ret.angle_d += 2 * M_PI;
2✔
101
        }
102
    }
103

104
    ret.angle_s *= R2D;
12✔
105
    ret.angle_d *= R2D;
12✔
106
    ret.phi = phi * R2D;
12✔
107
    ret.radius.x = r.x;
12✔
108
    ret.radius.y = r.y;
12✔
109
    return ret;
12✔
110
}
111

112
// converts one arc to segments
113
void fi_arc_to_lines(FI_POINT_D ref, FI_POINT_D *in, FI_SEG_FLAG flag,
12✔
114
                     FI_PATH **out) {
115
    FI_POINT_D s = ref;
12✔
116
    FI_POINT_D r = in[0];
12✔
117
    FI_POINT_D e = in[2];
12✔
118
    double phi = in[1].x;
12✔
119
    if (r.x == 0 || r.y == 0) {
12✔
120
        fi_append_new_seg(out, FI_SEG_LINE);
×
121
        (*out)->section.points[0].x = e.x;
×
122
        (*out)->section.points[0].y = e.y;
×
123
        return;
×
124
    }
125
    FI_PARAM_ARC param = fi_arc_endpoint_to_center(s, e, r, phi, flag);
12✔
126
    double angle = 0;
12✔
127
    double cos_phi = cos(param.phi * D2R);
12✔
128
    double sin_phi = sin(param.phi * D2R);
12✔
129
    for (int i = 0; i <= ARC_RES; i++) {
1,224✔
130
        fi_append_new_seg(out, FI_SEG_LINE);
1,212✔
131
        angle = param.angle_s * D2R +
1,212✔
132
                (double)i / (double)ARC_RES * param.angle_d * D2R;
1,212✔
133
        FI_POINT_D t1;
134
        t1.x = cos(angle) * param.radius.x;
1,212✔
135
        t1.y = sin(angle) * param.radius.y;
1,212✔
136
        (*out)->meta->last->section.points[0].x =
1,212✔
137
            t1.x * cos_phi - t1.y * sin_phi + param.center.x;
1,212✔
138
        (*out)->meta->last->section.points[0].y =
1,212✔
139
            t1.x * sin_phi + t1.y * cos_phi + param.center.y;
1,212✔
140
    }
141
    return;
12✔
142
}
143

144
// converts one cubic bezier curve to segments
145
void fi_cub_bezier_to_lines(FI_POINT_D ref, FI_POINT_D *in, FI_PATH **out) {
16✔
146
    FI_POINT_D P0 = ref;
16✔
147
    FI_POINT_D P1 = in[0];
16✔
148
    FI_POINT_D P2 = in[1];
16✔
149
    FI_POINT_D P3 = in[2];
16✔
150

151
    for (int i = 0; i <= BEZIER_RES; i++) {
1,632✔
152
        fi_append_new_seg(out, FI_SEG_LINE);
1,616✔
153
        double t = (double)i / BEZIER_RES;
1,616✔
154
        (*out)->meta->last->section.points[0].x =
1,616✔
155
            CUB_BEZIER_POINT(P0, P1, P2, P3, x, t);
1,616✔
156
        (*out)->meta->last->section.points[0].y =
1,616✔
157
            CUB_BEZIER_POINT(P0, P1, P2, P3, y, t);
1,616✔
158
    }
159
    // fi_draw_path(*out, stdout);
160
    return;
16✔
161
}
162

163
void fi_qua_bezier_to_lines(FI_POINT_D ref, FI_POINT_D *in, FI_PATH **out) {
2✔
164
    FI_POINT_D P0 = ref;
2✔
165
    FI_POINT_D P1 = in[0];
2✔
166
    FI_POINT_D P2 = in[1];
2✔
167

168
    for (int i = 0; i <= BEZIER_RES; i++) {
204✔
169
        fi_append_new_seg(out, FI_SEG_LINE);
202✔
170
        double t = (double)i / BEZIER_RES;
202✔
171
        (*out)->meta->last->section.points[0].x =
202✔
172
            QUA_BEZIER_POINT(P0, P1, P2, x, t);
202✔
173
        (*out)->meta->last->section.points[0].y =
202✔
174
            QUA_BEZIER_POINT(P0, P1, P2, y, t);
202✔
175
    }
176
    // fi_draw_path(*out, stdout);
177
    return;
2✔
178
}
179

180
void fi_linearize(FI_PATH **in) {
16✔
181
    FI_PATH *tmp = *in;
16✔
182
    FI_POINT_D last_ref_point;
183
    last_ref_point.x = 0;
16✔
184
    last_ref_point.y = 0;
16✔
185

186
    while (tmp != NULL) {
88✔
187
        FI_SEG_TYPE type = tmp->section.type;
72✔
188
        FI_SEG_FLAG flag = tmp->section.flag;
72✔
189
        FI_POINT_D *pt = tmp->section.points;
72✔
190
        FI_PATH *new_seg = NULL;
72✔
191
        FI_PATH *next = tmp->next;
72✔
192
        bool is_first = false;
72✔
193
        if (tmp->prev == NULL)
72✔
194
            is_first = true;
16✔
195
        switch (type) {
72✔
196
        case FI_SEG_END:
12✔
197
            last_ref_point.x = 0;
12✔
198
            last_ref_point.y = 0;
12✔
199
            break;
12✔
200
        case FI_SEG_MOVE:
14✔
201
            last_ref_point.x = pt[0].x;
14✔
202
            last_ref_point.y = pt[0].y;
14✔
203
            break;
14✔
204
        case FI_SEG_LINE:
16✔
205
            last_ref_point.x = pt[0].x;
16✔
206
            last_ref_point.y = pt[0].y;
16✔
207
            break;
16✔
208
        case FI_SEG_ARC:
12✔
209
            fi_arc_to_lines(last_ref_point, pt, flag, &new_seg);
12✔
210
            last_ref_point.x = pt[1].x;
12✔
211
            last_ref_point.y = pt[1].y;
12✔
212
            fi_replace_path(&tmp, new_seg);
12✔
213
            break;
12✔
214
        case FI_SEG_QUA_BEZIER:
2✔
215
            fi_qua_bezier_to_lines(last_ref_point, pt, &new_seg);
2✔
216
            last_ref_point.x = pt[1].x;
2✔
217
            last_ref_point.y = pt[1].y;
2✔
218
            fi_replace_path(&tmp, new_seg);
2✔
219
            break;
2✔
220
        case FI_SEG_CUB_BEZIER:
16✔
221
            fi_cub_bezier_to_lines(last_ref_point, pt, &new_seg);
16✔
222
            last_ref_point.x = pt[2].x;
16✔
223
            last_ref_point.y = pt[2].y;
16✔
224
            fi_replace_path(&tmp, new_seg);
16✔
225
            break;
16✔
226
        }
227
        if (is_first)
72✔
228
            *in = tmp;
16✔
229
        tmp = next;
72✔
230
    }
231
}
16✔
232

233
void fi_replace_path(FI_PATH **old, FI_PATH *new) {
30✔
234
    FI_META *tmp_meta = new->meta;
30✔
235
    FI_PATH *old_tmp = *old;
30✔
236

237
    // old is the first point
238
    if (old_tmp->prev == NULL) {
30✔
239
        old_tmp->meta->first = new;
2✔
240
        *old = new;
2✔
241
    } else {
242
        old_tmp->prev->next = new;
28✔
243
        new->prev = old_tmp->prev;
28✔
244
    }
245
    // old is the last point
246
    if (old_tmp->next == NULL) {
30✔
247
        old_tmp->meta->last = new->meta->last;
2✔
248
    } else {
249
        old_tmp->next->prev = new->meta->last;
28✔
250
        new->meta->last->next = old_tmp->next;
28✔
251
    }
252

253
    // replace the metaaries of the inserted segment
254
    // by those of the host segment
255
    FI_PATH *tmp = new;
30✔
256
    while (tmp != tmp_meta->last) {
3,030✔
257
        tmp->meta = old_tmp->meta;
3,000✔
258
        tmp = tmp->next;
3,000✔
259
    }
260
    tmp->meta = old_tmp->meta;
30✔
261

262
    // isolate the old point
263
    old_tmp->next = NULL;
30✔
264
    old_tmp->meta = NULL;
30✔
265

266
    // free the old point
267
    fi_free_path(old_tmp);
30✔
268

269
    new->meta->n_total += tmp_meta->n_total - 1;
30✔
270
    new->meta->n_end += tmp_meta->n_end - 1;
30✔
271
    new->meta->n_move += tmp_meta->n_move - 1;
30✔
272
    new->meta->n_line += tmp_meta->n_line - 1;
30✔
273
    new->meta->n_arc += tmp_meta->n_arc - 1;
30✔
274
    new->meta->n_qbez += tmp_meta->n_qbez - 1;
30✔
275
    new->meta->n_cbez += tmp_meta->n_cbez - 1;
30✔
276
    // free the new path meta (it's using the old one now)
277
    free(tmp_meta);
30✔
278

279
    return;
30✔
280
}
281

282
void fi_copy_path(FI_PATH *in, FI_PATH **out) {
2✔
283
    FI_PATH *tmp = in;
2✔
284
    FI_PATH *out_current = NULL;
2✔
285
    while (tmp != NULL) {
16✔
286
        FI_SEG_TYPE type = tmp->section.type;
14✔
287
        FI_SEG_FLAG flag = tmp->section.flag;
14✔
288
        FI_POINT_D *pt = tmp->section.points;
14✔
289
        fi_append_new_seg(&out_current, type);
14✔
290
        switch (type) {
14✔
291
        case FI_SEG_END:
2✔
292
            break;
2✔
293
        case FI_SEG_MOVE:
2✔
294
            out_current->meta->last->section.points[0] = pt[0];
2✔
295
            break;
2✔
296
        case FI_SEG_LINE:
4✔
297
            out_current->meta->last->section.points[0] = pt[0];
4✔
298
            break;
4✔
299
        case FI_SEG_ARC:
2✔
300
            out_current->meta->last->section.points[0] = pt[0];
2✔
301
            out_current->meta->last->section.points[1] = pt[1];
2✔
302
            out_current->meta->last->section.points[2] = pt[2];
2✔
303
            out_current->meta->last->section.flag = flag;
2✔
304
            break;
2✔
305
        case FI_SEG_QUA_BEZIER:
2✔
306
            out_current->meta->last->section.points[0] = pt[0];
2✔
307
            out_current->meta->last->section.points[1] = pt[1];
2✔
308
            break;
2✔
309
        case FI_SEG_CUB_BEZIER:
2✔
310
            out_current->meta->last->section.points[0] = pt[0];
2✔
311
            out_current->meta->last->section.points[1] = pt[1];
2✔
312
            out_current->meta->last->section.points[2] = pt[2];
2✔
313
            break;
2✔
314
        }
315
        tmp = tmp->next;
14✔
316
    }
317
    (*out) = out_current;
2✔
318
}
2✔
319

320
void fi_offset_path(FI_PATH *in, FI_POINT_D pt) {
2✔
321
    FI_PATH *tmp = in;
2✔
322
    while (tmp != NULL) {
20✔
323
        FI_SEG_TYPE type = tmp->section.type;
18✔
324
        switch (type) {
18✔
325
        case FI_SEG_END:
2✔
326
            break;
2✔
327
        case FI_SEG_MOVE:
2✔
328
            tmp->section.points[0].x += pt.x;
2✔
329
            tmp->section.points[0].y += pt.y;
2✔
330
            break;
2✔
331
        case FI_SEG_LINE:
2✔
332
            tmp->section.points[0].x += pt.x;
2✔
333
            tmp->section.points[0].y += pt.y;
2✔
334
            break;
2✔
335
        case FI_SEG_ARC:
2✔
336
            tmp->section.points[2].x += pt.x;
2✔
337
            tmp->section.points[2].y += pt.y;
2✔
338
            break;
2✔
339
        case FI_SEG_QUA_BEZIER:
2✔
340
            tmp->section.points[0].x += pt.x;
2✔
341
            tmp->section.points[0].y += pt.y;
2✔
342
            tmp->section.points[1].x += pt.x;
2✔
343
            tmp->section.points[1].y += pt.y;
2✔
344
            break;
2✔
345
        case FI_SEG_CUB_BEZIER:
8✔
346
            tmp->section.points[0].x += pt.x;
8✔
347
            tmp->section.points[0].y += pt.y;
8✔
348
            tmp->section.points[1].x += pt.x;
8✔
349
            tmp->section.points[1].y += pt.y;
8✔
350
            tmp->section.points[2].x += pt.x;
8✔
351
            tmp->section.points[2].y += pt.y;
8✔
352
            break;
8✔
353
        }
354
        tmp = tmp->next;
18✔
355
    }
356
}
2✔
357

358
int fi_append_new_seg(FI_PATH **path, FI_SEG_TYPE type) {
5,286✔
359
    FI_PATH *new_path = calloc(1, sizeof(FI_PATH));
5,286✔
360
    FI_POINT_D *new_seg;
361
    int n_point = 0;
5,286✔
362
    if (*path == NULL || (*path)->meta == NULL) {
5,286✔
363
        *path = new_path;
76✔
364
        (*path)->meta = calloc(sizeof(FI_META), 1);
76✔
365
        new_path->meta->last = new_path;
76✔
366
        new_path->meta->first = new_path;
76✔
367
        new_path->meta->n_max = DEFAULT_MAX_PATH_LENGTH;
76✔
368
    } else {
369
        if ((*path)->meta->n_total >= (*path)->meta->n_max) {
5,210✔
370
            free(new_path);
2✔
371
            return ERR_PATH_TOO_LONG;
2✔
372
        }
373
        (*path)->meta->last->next = new_path;
5,208✔
374
        new_path->prev = (*path)->meta->last;
5,208✔
375
        (*path)->meta->last = new_path;
5,208✔
376
        new_path->meta = (*path)->meta;
5,208✔
377
    }
378
    new_path->meta->n_total += 1;
5,284✔
379
    switch (type) {
5,284✔
380
    case FI_SEG_END:
42✔
381
        new_seg = NULL;
42✔
382
        new_path->meta->n_end += 1;
42✔
383
        n_point = 0;
42✔
384
        break;
42✔
385
    case FI_SEG_MOVE:
46✔
386
        new_seg = calloc(1, sizeof(FI_POINT_D));
46✔
387
        new_path->meta->n_move += 1;
46✔
388
        n_point = 1;
46✔
389
        break;
46✔
390
    case FI_SEG_LINE:
3,118✔
391
        new_seg = calloc(1, sizeof(FI_POINT_D));
3,118✔
392
        new_path->meta->n_line += 1;
3,118✔
393
        n_point = 1;
3,118✔
394
        break;
3,118✔
395
    case FI_SEG_ARC:
26✔
396
        new_seg = calloc(3, sizeof(FI_POINT_D));
26✔
397
        new_path->meta->n_arc += 1;
26✔
398
        n_point = 3;
26✔
399
        break;
26✔
400
    case FI_SEG_QUA_BEZIER:
14✔
401
        new_seg = calloc(2, sizeof(FI_POINT_D));
14✔
402
        new_path->meta->n_qbez += 1;
14✔
403
        n_point = 2;
14✔
404
        break;
14✔
405
    case FI_SEG_CUB_BEZIER:
2,038✔
406
        new_seg = calloc(3, sizeof(FI_POINT_D));
2,038✔
407
        new_path->meta->n_cbez += 1;
2,038✔
408
        n_point = 3;
2,038✔
409
        break;
2,038✔
410
    default:
×
411
        new_seg = NULL;
×
412
        break;
×
413
    }
414
    new_path->section.points = new_seg;
5,284✔
415
    new_path->section.type = type;
5,284✔
416
    new_path->section.n_point = n_point;
5,284✔
417
    return 0;
5,284✔
418
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc