Added core clipping tools for ellipses

This commit is contained in:
Stanislau Tsitsianok 2020-06-16 20:20:29 +03:00
parent 305b61ed1c
commit 70aea26781
No known key found for this signature in database
GPG Key ID: 349CB26B2ED6E0C0

View File

@ -1120,6 +1120,279 @@ int8_t ellipse_next(ellipse_state* s, int32_t* ret_x0, int32_t* ret_y, int32_t*
return 0;
}
typedef enum {
CT_AND,
CT_OR,
CT_CLIP
} clip_type;
typedef struct clip_node {
clip_type type;
double a, b, c;
struct clip_node* l;
struct clip_node* r;
} clip_node;
typedef struct event_list {
int32_t x;
int8_t type;
struct event_list* next;
} event_list;
void clip_tree_transpose(clip_node* root) {
if (root != NULL) {
if (root->type == CT_CLIP) {
double t = root->a;
root->a = root->b;
root->b = t;
}
clip_tree_transpose(root->l);
clip_tree_transpose(root->r);
}
}
void clip_tree_free(clip_node* root) {
if (root != NULL) {
clip_tree_free(root->l);
clip_tree_free(root->r);
free(root);
}
}
// Outputs a sequence of open-close events (types -1 and 1) for non-intersecting
// segments sorted by X coordinate.
// Merging nodes (AND, OR) may also accept sequences for intersecting segments,
// i.e. something like correct bracket sequences.
event_list* clip_tree_do_clip(clip_node* root, int32_t x0, int32_t y, int32_t x1) {
if (root == NULL) {
event_list* start = malloc(sizeof(event_list));
event_list* end = malloc(sizeof(event_list));
start->x = x0;
start->type = 1;
start->next = end;
end->x = x1;
end->type = -1;
end->next = NULL;
return start;
}
if (root->type == CT_CLIP) {
double eps = 1e-9;
double A = root->a;
double B = root->b;
double C = root->c;
if (fabs(A) < eps) {
if (B * y + C < -eps) {
x0 = 1;
x1 = 0;
}
} else {
// X of intersection
double ix = - (B * y + C) / A;
if (A * x0 + B * y + C < eps) {
x0 = round(fmax(x0, ix));
}
if (A * x1 + B * y + C < eps) {
x1 = round(fmin(x1, ix));
}
}
if (x0 <= x1) {
event_list* start = malloc(sizeof(event_list));
event_list* end = malloc(sizeof(event_list));
start->x = x0;
start->type = 1;
start->next = end;
end->x = x1;
end->type = -1;
end->next = NULL;
return start;
} else {
return NULL;
}
}
if (root->type == CT_OR || root->type == CT_AND) {
event_list* l1 = clip_tree_do_clip(root->l, x0, y, x1);
event_list* l2 = clip_tree_do_clip(root->r, x0, y, x1);
event_list* ret = NULL;
event_list* tail = NULL;
int32_t k1 = 0;
int32_t k2 = 0;
while (l1 != NULL || l2 != NULL) {
event_list* t;
if (l2 == NULL || (l1 != NULL && (l1->x < l2->x || (l1->x == l2->x && l1->type > l2->type)))) {
t = l1;
k1 += t->type;
assert(k1 >= 0);
l1 = l1->next;
} else {
t = l2;
k2 += t->type;
assert(k2 >= 0);
l2 = l2->next;
}
t->next = NULL;
if ((root->type == CT_OR && (
(t->type == 1 && (tail == NULL || tail->type == -1)) ||
(t->type == -1 && k1 == 0 && k2 == 0)
)) ||
(root->type == CT_AND && (
(t->type == 1 && (tail == NULL || tail->type == -1) && k1 > 0 && k2 > 0) ||
(t->type == -1 && tail != NULL && tail->type == 1 && (k1 == 0 || k2 == 0))
))) {
if (tail == NULL) {
ret = t;
} else {
tail->next = t;
}
tail = t;
} else {
free(t);
}
}
return ret;
}
return NULL;
}
typedef struct {
ellipse_state st;
clip_node* root;
event_list* head;
int32_t y;
} clip_ellipse_state;
void arc_init(clip_ellipse_state* s, int32_t a, int32_t b, int32_t w, int32_t al, int32_t ar) {
if (a < b) {
// transpose the coordinate system
arc_init(s, b, a, w, 90 - ar, 90 - al);
ellipse_init(&s->st, a, b, w);
clip_tree_transpose(s->root);
} else {
ellipse_init(&s->st, a, b, w);
s->head = NULL;
// normalize angles: 0 <= al < 360, al <= ar <= al + 360
if (ar - al >= 360) {
al = 0;
ar = 360;
} else {
al = (al < 0 ? 360 - (-al % 360) : al) % 360;
ar = al + (ar < al ? 360 - ((al - ar) % 360) : ar - al) % 360;
}
if (ar == al + 360) {
s->root = NULL;
} else {
clip_node* lc = malloc(sizeof(clip_node));
clip_node* rc = malloc(sizeof(clip_node));
lc->l = lc->r = rc->l = rc->r = NULL;
lc->type = rc->type = CT_CLIP;
lc->a = -a * sin(al * M_PI / 180.0);
lc->b = b * cos(al * M_PI / 180.0);
lc->c = (a * a - b * b) * sin(al * M_PI / 90.0) / 2.0;
rc->a = a * sin(ar * M_PI / 180.0);
rc->b = -b * cos(ar * M_PI / 180.0);
rc->c = (b * b - a * a) * sin(ar * M_PI / 90.0) / 2.0;
if (al % 180 == 0 || ar % 180 == 0 || al == ar) {
s->root = malloc(sizeof(clip_node));
s->root->l = lc;
s->root->r = rc;
s->root->type = ar - al < 180 ? CT_AND : CT_OR;
if (al == ar) {
lc = malloc(sizeof(clip_node));
lc->l = lc->r = NULL;
lc->type = CT_CLIP;
lc->a = al == 0 ? 1 : al == 180 ? -1 : 0;
lc->b = al % 180 ? (al < 180 ? 1 : -1) : 0;
lc->c = 0;
rc = s->root;
s->root = malloc(sizeof(clip_node));
s->root->l = lc;
s->root->r = rc;
s->root->type = CT_AND;
}
} else if ((al / 180 + ar / 180) % 2 == 1) {
s->root = malloc(sizeof(clip_node));
s->root->l = malloc(sizeof(clip_node));
s->root->l->l = malloc(sizeof(clip_node));
s->root->l->r = lc;
s->root->r = malloc(sizeof(clip_node));
s->root->r->l = malloc(sizeof(clip_node));
s->root->r->r = rc;
s->root->type = CT_OR;
s->root->l->type = CT_AND;
s->root->r->type = CT_AND;
s->root->l->l->type = CT_CLIP;
s->root->r->l->type = CT_CLIP;
s->root->l->l->l = s->root->l->l->r = NULL;
s->root->r->l->l = s->root->r->l->r = NULL;
s->root->l->l->a = s->root->l->l->c = 0;
s->root->r->l->a = s->root->r->l->c = 0;
s->root->l->l->b = (al / 180) % 2 == 0 ? 1 : -1;
s->root->r->l->b = (ar / 180) % 2 == 0 ? 1 : -1;
} else {
s->root = malloc(sizeof(clip_node));
s->root->l = malloc(sizeof(clip_node));
s->root->r = malloc(sizeof(clip_node));
s->root->type = s->root->l->type = ar - al < 180 ? CT_AND : CT_OR;
s->root->l->l = lc;
s->root->l->r = rc;
s->root->r->type = CT_CLIP;
s->root->r->l = s->root->r->r = NULL;
s->root->r->a = s->root->r->c = 0;
s->root->r->b = ar < 180 || ar > 540 ? 1 : -1;
}
}
}
}
void chord_init(clip_ellipse_state* s, int32_t a, int32_t b, int32_t w, int32_t al, int32_t ar) {
ellipse_init(&s->st, a, b, w);
s->head = NULL;
// line equation for chord
double xl = a * cos(al * M_PI / 180.0), xr = a * cos(ar * M_PI / 180.0);
double yl = b * sin(al * M_PI / 180.0), yr = b * sin(ar * M_PI / 180.0);
s->root = malloc(sizeof(clip_node));
s->root->l = s->root->r = NULL;
s->root->type = CT_CLIP;
s->root->a = yr - yl;
s->root->b = xl - xr;
s->root->c = -(s->root->a * xl + s->root->b * yl);
}
void clip_ellipse_free(clip_ellipse_state* s) {
clip_tree_free(s->root);
while (s->head != NULL) {
event_list* t = s->head;
s->head = s->head->next;
free(t);
}
}
int8_t clip_ellipse_next(clip_ellipse_state* s, int32_t* ret_x0, int32_t* ret_y, int32_t* ret_x1) {
int32_t x0, y, x1;
while (s->head == NULL && ellipse_next(&s->st, &x0, &y, &x1) >= 0) {
s->head = clip_tree_do_clip(s->root, x0, y, x1);
s->y = y;
}
if (s->head != NULL) {
*ret_y = s->y;
event_list* t = s->head;
s->head = s->head->next;
*ret_x0 = t->x;
free(t);
t = s->head;
assert(t != NULL);
s->head = s->head->next;
*ret_x1 = t->x;
free(t);
return 0;
}
return -1;
}
static int
ellipseNew(Imaging im, int x0, int y0, int x1, int y1,
const void* ink_, int fill,