mirror of
https://github.com/buserror/mii_emu.git
synced 2025-02-17 21:30:33 +00:00
820 lines
19 KiB
C
820 lines
19 KiB
C
/*
|
|
* c2_geometry.c
|
|
*
|
|
* C2DGeometry Implementation
|
|
* Created: Monday, March 30, 1998 11:51:47
|
|
* Converted back to C99 May 2013
|
|
*
|
|
* Copyright (C) 1998-2023 Michel Pollet <buserror@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
#include <stdio.h>
|
|
#include "c2_geometry.h"
|
|
#include "c2_geometry_poly.h"
|
|
|
|
const char *
|
|
c2_rect_as_str(
|
|
const c2_rect_p r )
|
|
{
|
|
static char ret[8][32];
|
|
static int reti = 0;
|
|
char *d = ret[reti];
|
|
reti = (reti+1) & 7;
|
|
if (r)
|
|
sprintf(d, "[%d,%d,%d,%d]",r->v[0],r->v[1],r->v[2],r->v[3]);
|
|
else strcpy(d, "[NULL]");
|
|
return d;
|
|
}
|
|
|
|
uint8_t
|
|
c2_rect_get_next_edge(
|
|
uint8_t inEdge,
|
|
int inCW )
|
|
{
|
|
uint8_t ret = inCW ? (inEdge << 1) & 0xf : (inEdge >> 1) & 0xf;
|
|
return ret ? ret : inCW ? out_Left : out_Bottom;
|
|
}
|
|
|
|
uint8_t
|
|
c2_rect_is_on_edge(
|
|
const c2_rect_p r,
|
|
const c2_pt_p p )
|
|
{
|
|
if (p->v[X] == r->tl.v[X])
|
|
return out_Left;
|
|
if (p->v[Y] == r->tl.v[Y])
|
|
return out_Top;
|
|
if (p->v[X] == r->br.v[X])
|
|
return out_Right;
|
|
if (p->v[Y] == r->br.v[Y])
|
|
return out_Bottom;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
c2_rect_get_edge(
|
|
const c2_rect_p r,
|
|
uint8_t inEdge,
|
|
c2_segment_p outEdge )
|
|
{
|
|
switch (inEdge) {
|
|
case out_Left:
|
|
c2_segment_set(outEdge, r->tl.v[X], r->tl.v[Y], r->tl.v[X], r->br.v[Y]);
|
|
break;
|
|
case out_Top:
|
|
c2_segment_set(outEdge, r->tl.v[X], r->tl.v[Y], r->br.v[X], r->tl.v[Y]);
|
|
break;
|
|
case out_Right:
|
|
c2_segment_set(outEdge, r->br.v[X], r->tl.v[Y], r->br.v[X], r->br.v[Y]);
|
|
break;
|
|
case out_Bottom:
|
|
c2_segment_set(outEdge, r->tl.v[X], r->br.v[Y], r->br.v[X], r->br.v[Y]);
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
c2_rect_get_corner(
|
|
const c2_rect_p r,
|
|
uint8_t inCorner,
|
|
c2_pt_p outCorner,
|
|
int inCW )
|
|
{
|
|
uint8_t corner = inCW ? inCorner : c2_rect_get_next_edge(inCorner, 0);
|
|
switch (corner) {
|
|
case corner_TopLeft:
|
|
*outCorner = r->tl;
|
|
break;
|
|
case corner_TopRight:
|
|
outCorner->v[X] = r->br.v[X];
|
|
outCorner->v[Y] = r->tl.v[Y];
|
|
break;
|
|
case corner_BottomRight:
|
|
*outCorner = r->br;
|
|
break;
|
|
case corner_BottomLeft:
|
|
outCorner->v[X] = r->tl.v[X];
|
|
outCorner->v[Y] = r->br.v[Y];
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
c2_rect_clip_segment(
|
|
const c2_rect_p r,
|
|
c2_segment_p s,
|
|
c2_segment_p o,
|
|
char * outEdges )
|
|
{
|
|
int accept = 0, done = 0;
|
|
uint8_t outcode0 = c2_rect_get_out_code(r, &s->a);
|
|
uint8_t outcode1 = c2_rect_get_out_code(r, &s->b);
|
|
*o = *s;
|
|
do {
|
|
if (!(outcode0 | outcode1)) {
|
|
accept = 1; done = 1;
|
|
continue;
|
|
} else if ((outcode0 & outcode1) != 0) {
|
|
if (outEdges) {
|
|
outEdges[0] = outcode0;
|
|
outEdges[1] = outcode1; // return offending borders
|
|
}
|
|
done = 1; // don't accept
|
|
continue;
|
|
}
|
|
|
|
// at least one end is outside, pick it
|
|
c2_pt_t *p;
|
|
uint8_t *outcode;
|
|
uint8_t dummy;
|
|
uint8_t *edge = &dummy;
|
|
if (outcode0) {
|
|
outcode = &outcode0;
|
|
p = &o->a;
|
|
if (outEdges) edge = &edge[0];
|
|
} else {
|
|
outcode = &outcode1;
|
|
p = &o->b;
|
|
if (outEdges) edge = &edge[1];
|
|
}
|
|
if (*outcode & out_Top) {
|
|
p->v[X] = s->a.v[X] +
|
|
(s->b.v[X] - s->a.v[X]) *
|
|
(double)(r->tl.v[Y] - s->a.v[Y]) / (s->b.v[Y] - s->a.v[Y]);
|
|
p->v[Y] = r->tl.v[Y];
|
|
*edge = out_Top;
|
|
*outcode &= ~out_Top;
|
|
} else if (*outcode & out_Bottom) {
|
|
p->v[X] = s->a.v[X] + (s->b.v[X] - s->a.v[X]) *
|
|
(double)(r->br.v[Y] - s->a.v[Y]) / (s->b.v[Y] - s->a.v[Y]);
|
|
p->v[Y] = r->br.v[Y];
|
|
*edge = out_Bottom;
|
|
*outcode &= ~out_Bottom;
|
|
}
|
|
if (*outcode & out_Left) {
|
|
p->v[Y] = s->a.v[Y] + (s->b.v[Y] - s->a.v[Y]) *
|
|
(double)(r->tl.v[X] - s->a.v[X]) / (s->b.v[X] - s->a.v[X]);
|
|
p->v[X] = r->tl.v[X];
|
|
*edge = out_Left;
|
|
*outcode &= ~out_Left;
|
|
} else if (*outcode & out_Right) {
|
|
p->v[Y] = s->a.v[Y] + (s->b.v[Y] - s->a.v[Y]) *
|
|
(double)(r->br.v[X] - s->a.v[X]) / (s->b.v[X] - s->a.v[X]);
|
|
p->v[X] = r->br.v[X];
|
|
*edge = out_Right;
|
|
*outcode &= ~out_Right;
|
|
}
|
|
*outcode = c2_rect_get_out_code(r, p);
|
|
*outcode = 0;
|
|
} while (!done);
|
|
|
|
return accept;
|
|
}
|
|
|
|
// C2DRectangle::Intersect(
|
|
int
|
|
c2_rect_clip_rect(
|
|
const c2_rect_p r,
|
|
const c2_rect_p s,
|
|
c2_rect_p o )
|
|
{
|
|
uint8_t outcode0 = c2_rect_get_out_code(r, &s->tl);
|
|
uint8_t outcode1 = c2_rect_get_out_code(r, &s->br);
|
|
|
|
// if both corners are on the same 'side' as the
|
|
// other corner, there can't be any intersection anyway
|
|
if (outcode0 & outcode1) {
|
|
o->br = o->tl;// make sure result rect is empty
|
|
return 0;
|
|
}
|
|
// ^^ this is equivalent to testing for all corners:
|
|
// for (int c = out_Left; c <= out_Bottom; c <<= 1)
|
|
// if ((outcode0 & c) && (outcode1 & c))
|
|
// return 0;
|
|
|
|
/* here we /know/ the rectangle intersects, or contains the
|
|
* other, so just detect where the corners are in relation to
|
|
* the rectangle, and copy the 'clipped' coordinates over
|
|
*/
|
|
*o = *s;
|
|
if (outcode0 & out_Left)
|
|
o->tl.x = r->tl.x;
|
|
if (outcode0 & out_Top)
|
|
o->tl.y = r->tl.y;
|
|
if (outcode1 & out_Right)
|
|
o->br.x = r->br.x;
|
|
if (outcode1 & out_Bottom)
|
|
o->br.y = r->br.y;
|
|
// only return 1 is the result is a true
|
|
// rectangle. adjacent rectangles would
|
|
// return a 'valid' intersection with with
|
|
// width or height being 0
|
|
return c2_rect_width(o) && c2_rect_height(o);
|
|
}
|
|
|
|
//! return true if all corners of 'r2' are inside 'r1'
|
|
int
|
|
c2_rect_contains_rect(
|
|
const c2_rect_p r1,
|
|
const c2_rect_p r2 )
|
|
{
|
|
for (int c = corner_TopLeft; c <= corner_BottomLeft; c <<= 1) {
|
|
c2_pt_t p = {};
|
|
c2_rect_get_corner(r2, c, &p, 1);
|
|
if (!c2_rect_contains_pt(r1, &p))
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
// Return true if rectangle 'r' intersects with rectangle 's'
|
|
int
|
|
c2_rect_intersect_rect(
|
|
const c2_rect_p s,
|
|
const c2_rect_p r )
|
|
{
|
|
// if any of the corners are inside, we are intersecting anyway
|
|
for (int c = corner_TopLeft; c <= corner_BottomLeft; c <<= 1) {
|
|
c2_pt_t p = {};
|
|
c2_rect_get_corner(r, c, &p, 1);
|
|
if (c2_rect_contains_pt(s, &p))
|
|
return 1;
|
|
}
|
|
// if any of the edges of 'r' intersects 's', we intersect
|
|
for (int e = out_Left; e <= out_Bottom; e <<= 1) {
|
|
c2_segment_t seg, clip;
|
|
c2_rect_get_edge(r, e, &seg);
|
|
if (c2_rect_clip_segment(s, &seg, &clip, NULL))
|
|
return 1;
|
|
}
|
|
// if 'r' *contains* 's', we intersect
|
|
return c2_rect_contains_rect(r, s);
|
|
}
|
|
|
|
uint8_t
|
|
c2_rect_get_out_code(
|
|
const c2_rect_p r,
|
|
const c2_pt_p p )
|
|
{
|
|
return ((p->v[X] < r->tl.v[X] ? out_Left : (p->v[X] > r->br.v[X]) ? out_Right : 0) |
|
|
(p->v[Y] < r->tl.v[Y] ? out_Top : (p->v[Y] > r->br.v[Y]) ? out_Bottom : 0));
|
|
}
|
|
|
|
void
|
|
c2_rect_clip_pt(
|
|
const c2_rect_p r,
|
|
c2_pt_p p )
|
|
{
|
|
uint8_t o = c2_rect_get_out_code(r, p);
|
|
if (o & out_Left)
|
|
p->v[X] = r->tl.v[X];
|
|
else
|
|
p->v[X] = r->br.v[X];
|
|
if (o & out_Top)
|
|
p->v[Y] = r->tl.v[Y];
|
|
else
|
|
p->v[Y] = r->br.v[Y];
|
|
}
|
|
|
|
|
|
void
|
|
c2_polyline_clear(
|
|
c2_polyline_p pl)
|
|
{
|
|
const c2_rect_t z = C2_RECT_ZERO;
|
|
pl->bounds = z;
|
|
c2_pt_array_clear(&pl->pt);
|
|
}
|
|
|
|
//c2_polyline_t::GetSegment(
|
|
int
|
|
c2_polyline_get_segment(
|
|
c2_polyline_p pl,
|
|
long ind,
|
|
c2_segment_p o )
|
|
{
|
|
if (ind > pl->pt.count)
|
|
return 0;
|
|
o->a = pl->pt.e[ind];
|
|
o->b = pl->pt.e[ind == pl->pt.count ? 1 : ind];
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
c2_polyline_offset(
|
|
c2_polyline_p pl,
|
|
c2_coord_t inX,
|
|
c2_coord_t inY )
|
|
{
|
|
for (unsigned int i = 0; i < pl->pt.count; i++) {
|
|
pl->pt.e[i].x += inX;
|
|
pl->pt.e[i].y += inY;
|
|
}
|
|
c2_rect_offset(&pl->bounds, inX, inY);
|
|
}
|
|
|
|
void
|
|
c2_polyline_scale(
|
|
c2_polyline_p pl,
|
|
double inFactor,
|
|
c2_rect_p inSkip /* = NULL */ )
|
|
{
|
|
long pMax = pl->pt.count;
|
|
c2_pt_t * c = pl->pt.e;
|
|
long count = 0;
|
|
long total = pMax;
|
|
int testrect = inSkip != NULL;
|
|
|
|
c2_rect_scale(&pl->bounds, inFactor);
|
|
|
|
if (c2_rect_height(&pl->bounds) > 3 && c2_rect_width(&pl->bounds) > 3 && total >= 8) {
|
|
c2_pt_t delta = C2_PT_ZERO;
|
|
c2_pt_t o = C2_PT_ZERO;
|
|
short skip = 11;
|
|
c2_pt_t *d = c;
|
|
c2_pt_t orig;
|
|
|
|
for (int i = 0; i < total; i++, c++) {
|
|
orig = *c;
|
|
c2_pt_scale(c, inFactor);
|
|
delta.v[X] += c->v[X] - o.v[X];
|
|
delta.v[Y] += c->v[Y] - o.v[Y];
|
|
int add = 0;
|
|
|
|
if (testrect)
|
|
add = orig.v[X] == inSkip->tl.v[X] || orig.v[X] == inSkip->br.v[X] ||
|
|
orig.v[Y] == inSkip->tl.v[Y] || orig.v[X] == inSkip->br.v[Y];
|
|
|
|
if (!add)
|
|
add = skip > 10 || delta.v[X] > 3 || delta.v[X] < -3 ||
|
|
delta.v[Y] > 3 || delta.v[Y] < -3;
|
|
if (add) {
|
|
*d++ = *c;
|
|
count++;
|
|
delta.v[X] = delta.v[Y] = 0;
|
|
skip = 0;
|
|
} else
|
|
skip++;
|
|
o = *c;
|
|
}
|
|
if (count < 2) {
|
|
c = pl->pt.e;
|
|
c[0] = pl->bounds.tl;
|
|
c[1] = pl->bounds.br;
|
|
count = 2;
|
|
}
|
|
if (count & 1) {
|
|
*d++ = o;
|
|
count++;
|
|
}
|
|
} else {
|
|
for (int i = 1; i <= total; i++, c++)
|
|
c2_pt_scale(c, inFactor);
|
|
}
|
|
|
|
pl->pt.count = count;
|
|
// TODO: trim?
|
|
}
|
|
|
|
void
|
|
c2_polyline_add_pt(
|
|
c2_polyline_p pl,
|
|
c2_pt_p p )
|
|
{
|
|
if (!pl->pt.count) {
|
|
pl->bounds.tl = *p;
|
|
pl->bounds.br = *p;
|
|
} else {
|
|
pl->bounds.tl.v[X] = PMIN(pl->bounds.tl.v[X], p->v[X]);
|
|
pl->bounds.tl.v[Y] = PMIN(pl->bounds.tl.v[Y], p->v[Y]);
|
|
pl->bounds.br.v[X] = PMAX(pl->bounds.br.v[X], p->v[X]);
|
|
pl->bounds.br.v[Y] = PMAX(pl->bounds.br.v[Y], p->v[Y]);
|
|
}
|
|
c2_pt_array_add(&pl->pt, *p);
|
|
}
|
|
|
|
#if 0
|
|
void
|
|
c2_polyline_t::Frame()
|
|
{
|
|
Lock();
|
|
SInt32 pMax = GetCount();
|
|
c2_pt_t *c = (c2_pt_t*)GetItemPtr(1);
|
|
MoveTo(Pixel_32k(c->v[X]), Pixel_32k(c->v[Y]));
|
|
for (int i = 1; i <= GetCount(); i++, c++) {
|
|
if (i == 1)
|
|
i = 1;
|
|
LineTo(Pixel_32k(c->v[X]), Pixel_32k(c->v[Y]));
|
|
}
|
|
Unlock();
|
|
}
|
|
#endif
|
|
|
|
void
|
|
c2_polyline_array_break(
|
|
c2_polyline_array_p pa)
|
|
{
|
|
pa->current = NULL;
|
|
}
|
|
|
|
void
|
|
c2_polyline_array_add_pt(
|
|
c2_polyline_array_p pa,
|
|
c2_pt_p p )
|
|
{
|
|
if (!pa->current) {
|
|
pa->current = malloc(sizeof(c2_polyline_t));
|
|
memset(pa->current, 0, sizeof(*pa->current));
|
|
c2_polyline_array_add(pa, pa->current);
|
|
}
|
|
c2_polyline_add_pt(pa->current, p);
|
|
}
|
|
|
|
int
|
|
c2_polyline_array_clip(
|
|
c2_polyline_array_p pa,
|
|
c2_rect_p clip,
|
|
c2_polyline_array_p outPoly )
|
|
{
|
|
for (long poly = 0; poly < pa->count; poly++) {
|
|
c2_polyline_t *p = pa->e[poly];
|
|
if (!p)
|
|
break;
|
|
|
|
long pMax = p->pt.count;
|
|
c2_pt_t * last = p->pt.e;
|
|
int lastIn = c2_rect_contains_pt(clip, last);
|
|
c2_pt_t * current = last+1;
|
|
int currentIn = c2_rect_contains_pt(clip, current);
|
|
|
|
for (long pIndex = 2; pIndex <= pMax; pIndex++) {
|
|
if (lastIn && currentIn) { // both points are IN
|
|
//outPoly.AddPoint(*last);
|
|
c2_polyline_array_add_pt(outPoly, current);
|
|
} else if (lastIn && !currentIn) { // line goes OUT
|
|
c2_segment_t src = { .a = *last, .b = *current };
|
|
c2_segment_t dst;
|
|
c2_rect_clip_segment(clip, &src, &dst, NULL);
|
|
c2_polyline_array_add_pt(outPoly, &dst.b);
|
|
c2_polyline_array_break(outPoly);
|
|
} else if (!lastIn && currentIn) { // line goes IN
|
|
c2_segment_t src = { .a = *last, .b = *current };
|
|
c2_segment_t dst;
|
|
c2_rect_clip_segment(clip, &src, &dst, NULL);
|
|
c2_polyline_array_break(outPoly);
|
|
c2_polyline_array_add_pt(outPoly, &dst.a);
|
|
c2_polyline_array_add_pt(outPoly, &dst.b);
|
|
} else { // line outside
|
|
c2_segment_t src = { .a = *last, .b = *current };
|
|
c2_segment_t dst;
|
|
if (c2_rect_clip_segment(clip, &src, &dst, NULL)) { // both point are out, but crosses the rectangle
|
|
c2_polyline_array_break(outPoly);
|
|
c2_polyline_array_add_pt(outPoly, &dst.a);
|
|
c2_polyline_array_add_pt(outPoly, &dst.b);
|
|
}
|
|
}
|
|
last++;
|
|
current++;
|
|
lastIn = c2_rect_contains_pt(clip, last);
|
|
currentIn = c2_rect_contains_pt(clip, current);
|
|
}
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void
|
|
c2_polyline_array_scale(
|
|
c2_polyline_array_p pa,
|
|
float inFactor,
|
|
c2_rect_p inSkip /* = NULL */ )
|
|
{
|
|
for (unsigned int ind = 0; ind < pa->count; ind++) {
|
|
c2_polyline_scale(pa->e[ind], inFactor, inSkip);
|
|
}
|
|
}
|
|
|
|
void
|
|
c2_polyline_array_offset(
|
|
c2_polyline_array_p pa,
|
|
c2_coord_t inX,
|
|
c2_coord_t inY )
|
|
{
|
|
for (unsigned int ind = 0; ind < pa->count; ind++) {
|
|
c2_polyline_offset(pa->e[ind], inX, inY);
|
|
}
|
|
}
|
|
|
|
void
|
|
c2_scanline_array_proper_alloc(
|
|
c2_scanline_array_p a,
|
|
c2_coord_t height)
|
|
{
|
|
c2_scanline_array_realloc(a, height);
|
|
memset(a->e, 0, height * sizeof(a->e[0]));
|
|
}
|
|
|
|
void
|
|
c2_scanline_array_proper_clear(
|
|
c2_scanline_array_p a)
|
|
{
|
|
for (unsigned int i = 0; i < a->count; i++)
|
|
c2_coord_array_free(&a->e[i]);
|
|
}
|
|
|
|
void
|
|
c2_scanline_array_add_coord(
|
|
c2_scanline_array_p a,
|
|
int inY,
|
|
c2_coord_t inX )
|
|
{
|
|
if (inY < 0 || inY >= (int)a->count) return;
|
|
|
|
c2_coord_array_p l = &a->e[inY];
|
|
|
|
if (l->count == 0 || inX >= l->e[l->count-1]) {
|
|
c2_coord_array_add(l, inX);
|
|
return;
|
|
}
|
|
|
|
int num = l->count;
|
|
int ind = 0;
|
|
c2_coord_t * cur = l->e;
|
|
|
|
while (*cur < inX && num--) {
|
|
cur++;
|
|
ind++;
|
|
}
|
|
c2_coord_array_insert(l, ind, &inX, 1);
|
|
}
|
|
|
|
int
|
|
c2_polygon_isempty(
|
|
c2_polygon_p pl)
|
|
{
|
|
return !pl->pt.count || c2_rect_isempty(&pl->bounds);
|
|
}
|
|
|
|
c2_coord_t
|
|
c2_polygon_get_heigth(
|
|
c2_polygon_p pl)
|
|
{
|
|
return c2_rect_height(&pl->bounds);
|
|
}
|
|
|
|
void
|
|
c2_polygon_clip(
|
|
c2_polygon_p pl,
|
|
c2_rect_p clip,
|
|
c2_polygon_p outPoly )
|
|
{
|
|
int sect = c2_rect_intersect_rect(&pl->bounds, clip);
|
|
int contains = c2_rect_contains_rect(&pl->bounds, clip);
|
|
if (!sect && !contains)
|
|
return; // don't even bother
|
|
|
|
long pMax = pl->pt.count;
|
|
c2_pt_t * last = pl->pt.e;
|
|
int lastIn = c2_rect_contains_pt(clip, last);
|
|
c2_pt_t * current = last+1;
|
|
int currentIn = c2_rect_contains_pt(clip, current);
|
|
//char outPart = clip.GetOutCode(*last);
|
|
uint8_t edgesStack[40];
|
|
short edgesClock[40];
|
|
long edgesCount = 0;
|
|
|
|
char edgesIndexes[13] = // Bit valued to quadrant index
|
|
{ 0, 1, 3, 2, 5, 0, 4, 0, 7, 8, 0, 0, 6 };
|
|
|
|
// if we start outside, remember our quadrant
|
|
if (!lastIn)
|
|
edgesStack[edgesCount++] = edgesIndexes[c2_rect_get_out_code(clip, last)];
|
|
if (lastIn)
|
|
c2_polyline_add_pt(outPoly, last);
|
|
for (long pIndex = 2; pIndex <= pMax; pIndex++) {
|
|
if (lastIn && currentIn) { // both points are IN
|
|
c2_polyline_add_pt(outPoly, current);
|
|
} else if (lastIn && !currentIn) { // line goes OUT
|
|
c2_segment_t src = { .a = *last, .b = *current };
|
|
c2_segment_t dst;
|
|
c2_rect_clip_segment(clip, &src, &dst, NULL);
|
|
c2_polyline_add_pt(outPoly, &dst.b);
|
|
edgesStack[edgesCount++] = edgesIndexes[c2_rect_is_on_edge(clip, &dst.b)];
|
|
} else if (!lastIn && currentIn) { // line goes IN
|
|
// flush corner stack
|
|
if (edgesCount > 1) {
|
|
for (int c = 0; c < edgesCount; c++) {
|
|
c2_pt_t p;
|
|
switch (edgesStack[c]) {
|
|
case 2: // topleft
|
|
p = clip->tl;
|
|
c2_polyline_add_pt(outPoly, &p);
|
|
break;
|
|
case 4: // topright
|
|
p.v[X] = clip->br.v[X]; p.v[Y] = clip->tl.v[Y];
|
|
c2_polyline_add_pt(outPoly, &p);
|
|
break;
|
|
case 6: // botright
|
|
p = clip->br;
|
|
c2_polyline_add_pt(outPoly, &p);
|
|
break;
|
|
case 8: // botleft
|
|
p.v[X] = clip->tl.v[X]; p.v[Y] = clip->br.v[Y];
|
|
c2_polyline_add_pt(outPoly, &p);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
edgesCount = 0;
|
|
c2_segment_t src = { .a = *last, .b = *current };
|
|
c2_segment_t dst;
|
|
c2_rect_clip_segment(clip, &src, &dst, NULL);
|
|
c2_polyline_add_pt(outPoly, &dst.a);
|
|
c2_polyline_add_pt(outPoly, &dst.b);
|
|
} else { // line outside
|
|
c2_segment_t src = { .a = *last, .b = *current };
|
|
c2_segment_t dst;
|
|
if (c2_rect_clip_segment(clip, &src, &dst, NULL)) { // both point are out, but crosses the rectangle
|
|
// flush corner stack
|
|
if (edgesCount > 1) {
|
|
for (int c = 0; c < edgesCount; c++) {
|
|
c2_pt_t p;
|
|
switch (edgesStack[c]) {
|
|
case 2: // topleft
|
|
p = clip->tl;
|
|
c2_polyline_add_pt(outPoly, &p);
|
|
break;
|
|
case 4: // topright
|
|
p.v[X] = clip->br.v[X]; p.v[Y] = clip->tl.v[Y];
|
|
c2_polyline_add_pt(outPoly, &p);
|
|
break;
|
|
case 6: // botright
|
|
p = clip->br;
|
|
c2_polyline_add_pt(outPoly, &p);
|
|
break;
|
|
case 8: // botleft
|
|
p.v[X] = clip->tl.v[X]; p.v[Y] = clip->br.v[Y];
|
|
c2_polyline_add_pt(outPoly, &p);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
edgesCount = 0;
|
|
c2_polyline_add_pt(outPoly, &dst.a);
|
|
c2_polyline_add_pt(outPoly, &dst.b);
|
|
edgesStack[edgesCount++] = edgesIndexes[c2_rect_is_on_edge(clip, &dst.b)];
|
|
} else { // setup edge stack properly
|
|
unsigned char clockTable[9] =
|
|
{ 0, 0x0e, 0x1c, 0x38, 0x70, 0xe0, 0xc1, 0x83, 0x07 };
|
|
uint8_t o = edgesIndexes[c2_rect_get_out_code(clip, current)];
|
|
if (o != edgesStack[edgesCount - 1]) {
|
|
short clock = clockTable[edgesStack[edgesCount - 1]] & (1 << (o-1)) ? 1 : -1;
|
|
|
|
do {
|
|
if (edgesCount == 1)
|
|
edgesClock[edgesCount -1] = clock;
|
|
|
|
if (clock == edgesClock[edgesCount -1]) {
|
|
int c = edgesStack[edgesCount - 1];
|
|
do {
|
|
c += clock;
|
|
if (c == 0) c = 8;
|
|
else if (c == 9) c = 1;
|
|
edgesClock[edgesCount] = clock;
|
|
edgesStack[edgesCount++] = c;
|
|
#ifdef TRACELIB
|
|
{
|
|
static long ecount = 0;
|
|
if (edgesCount > ecount) {
|
|
ecount = edgesCount;
|
|
printf("Max edgecount = %d\n", ecount);
|
|
}
|
|
}
|
|
if (edgesCount == 40) {
|
|
DebugStr("\p HELP!");
|
|
}
|
|
#endif
|
|
} while (c != o);
|
|
} else { // change direction, remove points
|
|
while (edgesCount > 1 && edgesStack[edgesCount - 1] != o)
|
|
edgesCount--;
|
|
}
|
|
} while (o != edgesStack[edgesCount -1]);
|
|
}
|
|
}
|
|
}
|
|
last++;
|
|
current++;
|
|
lastIn = c2_rect_contains_pt(clip, last);
|
|
currentIn = c2_rect_contains_pt(clip, current);
|
|
}
|
|
// flush corner stack
|
|
if (edgesCount > 1) {
|
|
for (int c = 0; c < edgesCount; c++) {
|
|
c2_pt_t p;
|
|
switch (edgesStack[c]) {
|
|
case 2: // topleft
|
|
p = clip->tl;
|
|
c2_polyline_add_pt(outPoly, &p);
|
|
break;
|
|
case 4: // topright
|
|
p.v[X] = clip->br.v[X]; p.v[Y] = clip->tl.v[Y];
|
|
c2_polyline_add_pt(outPoly, &p);
|
|
break;
|
|
case 6: // botright
|
|
p = clip->br;
|
|
c2_polyline_add_pt(outPoly, &p);
|
|
break;
|
|
case 8: // botleft
|
|
p.v[X] = clip->tl.v[X]; p.v[Y] = clip->br.v[Y];
|
|
c2_polyline_add_pt(outPoly, &p);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
edgesCount = 0;
|
|
}
|
|
|
|
|
|
#define exchange(t, p1, p2) { t e=p1;p1=p2;p2=e; }
|
|
|
|
void
|
|
c2_polygon_scanline(
|
|
c2_polygon_p pl,
|
|
c2_scanline_array_p ioList,
|
|
c2_coord_t ymin )
|
|
{
|
|
c2_coord_t ysize = ioList->height;//TODO check if we meant the polygon height?
|
|
if (ysize <= 0 || c2_rect_width(&pl->bounds) <= 0)
|
|
return;
|
|
|
|
c2_pt_t *first = pl->pt.e;
|
|
c2_pt_t *sp1 = first;
|
|
c2_pt_t *sp2 = sp1+1;
|
|
long numPoints = pl->pt.count;
|
|
//
|
|
// Compute list of intersections between the vertex and the horizontal
|
|
// lines. The lists are automaticaly sorted
|
|
//
|
|
do {
|
|
if (numPoints == 1) sp2 = first;
|
|
c2_pt_t *p1 = sp1;
|
|
c2_pt_t *p2 = sp2;
|
|
if (p2->v[Y] < p1->v[Y])
|
|
exchange(c2_pt_t *, p1, p2);
|
|
c2_coord_t y1 = p1->v[Y];
|
|
c2_coord_t y2 = p2->v[Y];
|
|
c2_coord_t idy = y2 - y1;
|
|
if ((y1 < ymin && y2 < ymin) ||
|
|
((y1-ymin > ioList->height) && (y2-ymin > ioList->height)))
|
|
continue;
|
|
if (idy) {
|
|
double x, dx;
|
|
x = p1->v[X];
|
|
dx = (p2->v[X] - p1->v[X]) / (double)idy;
|
|
do {
|
|
c2_coord_t ix = x;
|
|
c2_scanline_array_add_coord(ioList, y1-ymin, x - ix > 0.5 ? ix+1 : ix);
|
|
x += dx;
|
|
y1++;
|
|
} while (y1 < y2);
|
|
} else {
|
|
c2_scanline_array_add_coord(ioList, y1-ymin, p1->v[X]);
|
|
c2_scanline_array_add_coord(ioList, y1-ymin, p2->v[X]);
|
|
}
|
|
sp1++;
|
|
sp2++;
|
|
} while(numPoints-- > 1);
|
|
}
|
|
|
|
#if 0
|
|
void
|
|
c2_polygon_t::Paint()
|
|
{
|
|
c2_scanline_list_t scan(Height()+1);
|
|
AddIntersections(scan, bounds.tl.v[Y]);
|
|
|
|
for (int i = 0; i < Height(); i++) {
|
|
SInt32 num = scan.Get(i).GetCount();
|
|
if (num > 1) {
|
|
c2_coord_t * cur = scan.Get(i);
|
|
for (SInt32 ind = 1; ind <= num - 1; ind += 2, cur += 2) {
|
|
if (cur[0] < cur[1]) {
|
|
MoveTo(cur[0], bounds.tl.v[Y]+i);
|
|
LineTo(cur[1], bounds.tl.v[Y]+i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|