/*
 * p2d_attrib.c - Determine various polygon attributes
 *
 * Written 2012, 2015 by Werner Almesberger
 * Copyright 2012, 2015 Werner Almesberger
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 */


#include <stdbool.h>
#include <stdio.h>
#include <math.h>
#include <assert.h>

#include "poly2d.h"


/*
 * Angle in counter-clockwise direction to turn at point B when coming from A
 * in order to face towards C.
 */

static double angle_3(const struct v2d *a, const struct v2d *b,
    const struct v2d *c)
{
	double ax, ay, bx, by;
	double cs, aa, bb;
	double cross, angle;

	ax = b->x - a->x;
	ay = b->y - a->y;
	bx = c->x - b->x;
	by = c->y - b->y;

	cross = ax * by - ay * bx;
	if (!cross)
		return 0;

	aa = hypot(ax, ay);
	bb = hypot(bx, by);

	cs = (ax * bx + ay * by) / aa / bb;
	if (cs <= -1)
		angle = 180;
	else if (cs >= 1)
		angle = 0;
	else
		angle = acos(cs) / M_PI * 180.0;
	return cross >= 0 ? angle : -angle;
}

/*
 * If we predominantly turn to the right, then the path must be clockwise.
 */

bool p2d_is_cw(const struct p2d *p)
{
	const struct v2d *v;
	double a = 0;

	assert(p2d_vertices(p) >= 3);
	assert(p2d_is_closed(p));
	assert(p2d_no_intersect(p));

	v = p->v;
	do {
		a += angle_3(v, v->next, v->next->next);
		v = v->next;
	}
	while (v != p->v);
	assert(!isnan(a));	/* require -std=c99 or -std=gnu99 */
	return a < 0;
}


bool p2d_is_closed(const struct p2d *p)
{
	return p->v == p->last || p->last->next;
}


/*
 * Known bug: if the polygon intersects on a vertex, the intersection may
 * go unnoticed.
 */

bool p2d_no_intersect(const struct p2d *p)
{
	const struct v2d *v, *u;

	v = p->v;
	while (v) {
		u = v->next;
		if (!u || u == p->v)
			return 1;
		u = u->next;
		if (!u || u == p->v)
			return 1;
		while (u && u->next && u->next != v) {
			if (v2d_intersect(v, v->next, u, u->next,
			    NULL, NULL) > 0)
				return 0;
			u = u->next;
			if (u == p->v)
				break;
		}
		v = v->next;
		if (v == p->v)
			break;
	}
	return 1;
}


unsigned p2d_vertices(const struct p2d *p)
{
	const struct v2d *v;
	unsigned n = 0;

	v = p->v;
	while (v) {
		n++;
		v = v->next;
		if (v == p->v)
			break;
	}
	return n;
}