/*
  *** DO NOT EDIT ***
  This file has been generated by opc89, and will be overwritten.
  Please edit the source of this file instead.
*/

#ifndef G2D_CARC_H
#define G2D_CARC_H
#include <opc89.h>
#include <gengeo2d/prim.h>
#include <gengeo2d/common.h>
#include <gengeo2d/vect.h>
#include <gengeo2d/box.h>


/****** API ******/

/* Construct a carc center, radius and angles */
G2D_INLINE g2d_carc_t g2d_carc(g2d_vect_t center, g2d_coord_t radius, g2d_angle_t start, g2d_angle_t end);

/* Calculate the bounding box of a carc */
G2D_INLINE g2d_box_t g2d_carc_bbox(const g2d_carc_t *arc);

/* Return the coordinates of a point on the arc offset 0..1 from the start angle */
G2D_INLINE g2d_cvect_t g2d__carc_offs(const g2d_carc_t *arc, g2d_offs_t offs);
G2D_INLINE g2d_vect_t g2d_carc_offs(const g2d_carc_t *arc, g2d_offs_t offs);

/* Return a tangential unit vector at offs, pointing in the direction of
   the delta angle */
G2D_INLINE g2d_cvect_t g2d__carc_offs_tang1(const g2d_carc_t *arc, g2d_offs_t offs);

/* Convert a point into an offset or an offset into a point */
G2D_INLINE g2d_offs_t g2d__offs_carc_pt(const g2d_carc_t *arc, g2d_cvect_t pt); /* doesn't check radius */
G2D_INLINE g2d_cvect_t g2d__carc_offs(const g2d_carc_t *carc, g2d_offs_t offs);
G2D_INLINE g2d_offs_t g2d_offs_carc_pt(const g2d_carc_t *arc, g2d_vect_t pt); /* doesn't check radius */
G2D_INLINE g2d_vect_t g2d_carc_offs(const g2d_carc_t *carc, g2d_offs_t offs);

/* Convert a point into an angle, within the arc's angle range if possible;
   the rel variant returns delta angle from start */
G2D_INLINE g2d_angle_t g2d_ang_carc_pt(const g2d_carc_t *arc, g2d_vect_t pt);
G2D_INLINE g2d_angle_t g2d__ang_carc_pt(const g2d_carc_t *arc, g2d_cvect_t pt);
G2D_INLINE g2d_angle_t g2d_ang_carc_pt_rel(const g2d_carc_t *arc, g2d_vect_t pt);
G2D_INLINE g2d_angle_t g2d__ang_carc_pt_rel(const g2d_carc_t *arc, g2d_cvect_t pt);

/* Generic intersection calculations */
G2D_INLINE int g2d__iscp_carc_box(const g2d_carc_t *arc, const g2d_box_t *box, g2d_cvect_t ip[8], g2d_offs_t offs[8]);
G2D_INLINE int g2d_iscp_carc_box(const g2d_carc_t *arc, const g2d_box_t *box, g2d_vect_t ip[8], g2d_offs_t offs[8]);

/* Special case (optimized) intersections with axis aligned centerlines */
G2D_INLINE int g2d__iscp_carc_hcline(const g2d_carc_t *arc, g2d_coord_t y, g2d_coord_t x1, g2d_coord_t x2, g2d_cvect_t ip[2], g2d_offs_t offs[2]);
G2D_INLINE int g2d__iscp_carc_vcline(const g2d_carc_t *arc, g2d_coord_t x, g2d_coord_t y1, g2d_coord_t y2, g2d_cvect_t ip[2], g2d_offs_t offs[2]);

/* carc-circle intersection (not optimized) */
G2D_INLINE int g2d__iscp_carc_circle(const g2d_carc_t *arc1, g2d_vect_t center, g2d_calc_t r, g2d_cvect_t ip[2],  g2d_offs_t offs[2]);
G2D_INLINE int g2d_iscp_carc_circle(const g2d_carc_t *arc1, g2d_vect_t center, g2d_calc_t r,  g2d_vect_t ip[2],  g2d_offs_t offs[2]);


/****** IMPLEMENTATION ******/


G2D_INLINE g2d_carc_t g2d_carc(g2d_vect_t center, g2d_coord_t radius, g2d_angle_t start, g2d_angle_t delta)
{
	g2d_carc_t a;
	a.c = center; a.r = radius;
	a.start = start; a.delta = delta;
	return a;
}


G2D_INLINE g2d_box_t g2d_carc_bbox(const g2d_carc_t *arc)
{
	double ca1, ca2, sa1, sa2; /* legit double: store result of sin() and cos() */
	g2d_calc_t minx = g2d_calc_t_convfrom_double(0.0), maxx = g2d_calc_t_convfrom_double(0.0), miny = g2d_calc_t_convfrom_double(0.0), maxy = g2d_calc_t_convfrom_double(0.0);
	g2d_angle_t start, delta, end, pi2 = g2d_angle_t_convfrom_double(G2D_PI * 2.0);
	g2d_box_t bb;

	delta = arc->delta;
	if (g2d_angle_t_gt_g2d_angle_t(delta  ,pi2)) delta = pi2;
	else if (g2d_angle_t_lt_g2d_angle_t(delta  ,g2d_angle_t_neg(pi2))) delta = g2d_angle_t_neg(pi2);

	/* cheap handling of the full circle case (no rounding required as everyhting is in g2d_coord_t) */
	if ((g2d_angle_t_eq_g2d_angle_t(delta  ,pi2)) || (g2d_angle_t_eq_g2d_angle_t(delta  ,g2d_angle_t_neg(pi2)))) {
		bb.p1.x = g2d_coord_t_sub_g2d_coord_t(arc->c.x  ,arc->r);
		bb.p2.x = g2d_coord_t_add_g2d_coord_t(arc->c.x  ,arc->r);
		bb.p1.y = g2d_coord_t_sub_g2d_coord_t(arc->c.y  ,arc->r);
		bb.p2.y = g2d_coord_t_add_g2d_coord_t(arc->c.y  ,arc->r);
		return bb;
	}

	/* normalize angles so that end > start and start is between 0 and 360 */
	if (g2d_angle_t_lt_g2d_angle_t(delta  ,g2d_angle_t_convfrom_double(0.0))) {
		start = g2d_angle_t_add_g2d_angle_t(arc->start  ,delta);
		end = arc->start;
		delta = g2d_angle_t_neg(delta);
	}
	else {
		start = arc->start;
		end = g2d_angle_t_add_g2d_angle_t(arc->start  ,delta);
	}
	start = g2d_normalize_angle(start);
	if (g2d_angle_t_lt_g2d_angle_t(start  ,g2d_angle_t_convfrom_double(0.0)))
		start += (G2D_PI)*2;
	end = g2d_angle_t_add_g2d_angle_t(start  ,delta);

	/* check if the bounding box is determined by an intermediate point of the arc */
	if ((  g2d_is_between_angles ( (g2d_angle_t)0.0 , start , end ) )
		)maxx = g2d_calc_t_convfrom_double(1.0);
	if ((  g2d_is_between_angles ( (g2d_angle_t)G2D_PI , start , end ) )
		)minx = g2d_calc_t_convfrom_double(-1.0);

	if ((  g2d_is_between_angles ( (g2d_angle_t)(0.5*G2D_PI) , start , end ) )
		)miny = g2d_calc_t_convfrom_double(1.0);
	if ((  g2d_is_between_angles ( (g2d_angle_t)(1.5*G2D_PI) , start , end ) )
		)maxy = g2d_calc_t_convfrom_double(-1.0);

	/* if not, the bbox touches the endpoint */
	if ((g2d_calc_t_eq_g2d_calc_t(minx  ,g2d_calc_t_convfrom_double(0.0))) || (g2d_calc_t_eq_g2d_calc_t(maxx  ,g2d_calc_t_convfrom_double(0.0)))) {
		ca1 = double_convfrom_g2d_calc_t(g2d_cos(start));
		ca2 = double_convfrom_g2d_calc_t(g2d_cos(end));
		if (g2d_calc_t_eq_g2d_calc_t(minx  ,g2d_calc_t_convfrom_double(0.0))) minx =(  G2D_MIN ( ca1 , ca2 ) );
		if (g2d_calc_t_eq_g2d_calc_t(maxx  ,g2d_calc_t_convfrom_double(0.0))) maxx =(  G2D_MAX ( ca1 , ca2 ) );
	}
	if ((g2d_calc_t_eq_g2d_calc_t(miny  ,g2d_calc_t_convfrom_double(0.0))) || (g2d_calc_t_eq_g2d_calc_t(maxy  ,g2d_calc_t_convfrom_double(0.0)))) {
		sa1 = double_convfrom_g2d_calc_t(g2d_sin(start));
		sa2 = double_convfrom_g2d_calc_t(g2d_sin(end));
		if (g2d_calc_t_eq_g2d_calc_t(miny  ,g2d_calc_t_convfrom_double(0.0))) miny =(  G2D_MAX ( sa1 , sa2 ) );
		if (g2d_calc_t_eq_g2d_calc_t(maxy  ,g2d_calc_t_convfrom_double(0.0))) maxy =(  G2D_MIN ( sa1 , sa2 ) );
	}

	/* Finally, calculate bounds */
	bb.p1.x = g2d_round_coord_down(g2d_calc_t_add_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(arc->c.x ) ,g2d_calc_t_mul_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(arc->r ) ,minx)));
	bb.p2.x = g2d_round_coord_up(g2d_calc_t_add_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(arc->c.x ) ,g2d_calc_t_mul_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(arc->r ) ,maxx)));
	bb.p2.y = g2d_round_coord_up(g2d_calc_t_add_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(arc->c.y ) ,g2d_calc_t_mul_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(arc->r ) ,miny)));
	bb.p1.y = g2d_round_coord_down(g2d_calc_t_add_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(arc->c.y ) ,g2d_calc_t_mul_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(arc->r ) ,maxy)));

	return bb;
}

G2D_INLINE g2d_cvect_t g2d__carc_offs(const g2d_carc_t *arc, g2d_offs_t offs)
{
	g2d_angle_t ang = (g2d_angle_t_add_g2d_angle_t(arc->start  ,g2d_angle_t_MUL_g2d_offs_t(offs  ,arc->delta)));
	g2d_calc_t acx = g2d_calc_t_convfrom_g2d_coord_t(arc->c.x), acy = g2d_calc_t_convfrom_g2d_coord_t(arc->c.y), r = g2d_calc_t_convfrom_g2d_coord_t(arc->r);
	return g2d_cvect(g2d_calc_t_add_g2d_calc_t(acx  ,g2d_calc_t_mul_g2d_calc_t(g2d_cos(ang)  ,r)), g2d_calc_t_add_g2d_calc_t(acy  ,g2d_calc_t_mul_g2d_calc_t(g2d_sin(ang)  ,r)));
}

G2D_INLINE g2d_vect_t g2d_carc_offs(const g2d_carc_t *arc, g2d_offs_t offs)
{
	return g2d_vect_t_convfrom_g2d_cvect_t(g2d__carc_offs(arc, offs));
}


G2D_INLINE g2d_cvect_t g2d__carc_offs_tang1(const g2d_carc_t *arc, g2d_offs_t offs)
{
	g2d_angle_t ang = (g2d_angle_t_add_g2d_angle_t(arc->start  ,g2d_angle_t_MUL_g2d_offs_t(offs  ,arc->delta)));
	g2d_calc_t acx = g2d_calc_t_convfrom_g2d_coord_t(arc->c.x), acy = g2d_calc_t_convfrom_g2d_coord_t(arc->c.y), r = g2d_calc_t_convfrom_g2d_coord_t(arc->r);
	g2d_cvect_t ac, t, e = g2d_cvect(g2d_calc_t_add_g2d_calc_t(acx  ,g2d_calc_t_mul_g2d_calc_t(g2d_cos(ang)  ,r)), g2d_calc_t_add_g2d_calc_t(acy  ,g2d_calc_t_mul_g2d_calc_t(g2d_sin(ang)  ,r)));
	ac = g2d_cvect_t_convfrom_g2d_vect_t(arc->c);
	t = g2d_cvect_t_sub_g2d_cvect_t(e  ,ac);
	return (g2d_cvect_t_neg(g2d__vect_normal(t)));
}

G2D_INLINE g2d_vect_t g2d_carc_offs_tang1(const g2d_carc_t *arc, g2d_offs_t offs)
{
	return g2d_vect_t_convfrom_g2d_cvect_t(g2d__carc_offs_tang1(arc, offs));
}

G2D_INLINE void g2d_carc_start_end_norm(const g2d_carc_t *arc, g2d_angle_t *start_out, g2d_angle_t *end_out)
{
	g2d_angle_t start, delta;
	start = g2d_normalize_angle(arc->start);
	if (g2d_angle_t_lt_g2d_angle_t(start  ,g2d_angle_t_convfrom_double(0.0)))
		start += (2*G2D_PI);
	if ((g2d_angle_t_lte_g2d_angle_t(arc->delta  ,g2d_angle_t_neg((g2d_angle_t_convfrom_double(2*G2D_PI))))) || (g2d_angle_t_gte_g2d_angle_t(arc->delta  ,(g2d_angle_t_convfrom_double(2*G2D_PI)))))
		delta = (g2d_angle_t_convfrom_double(2*G2D_PI));
	else
		delta = arc->delta;
	if (g2d_angle_t_gte_g2d_angle_t(delta  ,g2d_angle_t_convfrom_double(0.0))) {
		*start_out = start;
		*end_out = g2d_angle_t_add_g2d_angle_t(start  ,delta);
	}
	else {
		*start_out = g2d_angle_t_add_g2d_angle_t(start  ,delta);
		*end_out = start;
	}
	if (g2d_angle_t_lt_g2d_angle_t((*end_out)  ,(*start_out))) {
		double tmp = double_convfrom_g2d_angle_t(*end_out);
		*end_out = *start_out;
		*start_out = g2d_angle_t_convfrom_double(tmp);
	}
}

G2D_INLINE int g2d__carc_ison_pt(const g2d_carc_t *arc, g2d_cvect_t pt)
{
	g2d_calc_t dst2, r2 = g2d_calc_t_convfrom_g2d_coord_t(arc->r);
	g2d_angle_t start, end, pa;
	g2d_cvect_t v, arc_cent = g2d_cvect_t_convfrom_g2d_vect_t(arc->c);

	r2 = g2d_calc_t_mul_g2d_calc_t(r2  ,r2);
	dst2 = g2d_calc_t_sub_g2d_calc_t(g2d__distance2(arc_cent, pt)  ,r2);

	/* quit fast if not on the right radius */
	if ((g2d_calc_t_lt_g2d_calc_t(dst2  ,g2d_calc_t_neg(G2D_COORD_TOL2))) || (g2d_calc_t_gt_g2d_calc_t(dst2  ,G2D_COORD_TOL2)))
		return 0;


	g2d_carc_start_end_norm(arc, &start, &end);
	v = g2d_cvect_t_sub_g2d_cvect_t(pt  ,arc_cent);
	pa = g2d_atan2(v.y, v.x);

	{
		int a;
		a =(  g2d_is_between ( pa , start , end )  );
		return a;
	}
}

G2D_INLINE int g2d_carc_ison_pt(const g2d_carc_t *arc, g2d_vect_t pt)
{
	return g2d__carc_ison_pt(arc, g2d_cvect_t_convfrom_g2d_vect_t(pt));
}


G2D_INLINE int g2d__iscp_carc_hcline(const g2d_carc_t *arc, g2d_coord_t y, g2d_coord_t x1, g2d_coord_t x2, g2d_cvect_t ip[2], g2d_offs_t offs[2])
{
	g2d_calc_t dx, dy, x, r = g2d_calc_t_convfrom_g2d_coord_t(arc->r);
	g2d_angle_t ang, ang2, start, end, a_pi;
	int n, iscs = 0;

	a_pi = g2d_angle_t_convfrom_double(G2D_PI);

	if (g2d_coord_t_lte_g2d_coord_t(arc->r  ,g2d_coord_t_convfrom_int(0))) { /* degenerate case: arc is a point */
		if (g2d_coord_t_neq_g2d_coord_t(arc->c.y  ,y)) return 0;
		if (!((  g2d_is_between ( arc->c.x , x1 , x2 ) )) )return 0;
		if (ip != NULL) ip[0] = g2d_cvect_t_convfrom_g2d_vect_t(arc->c);
		if (offs != 0) offs = 0;
		return 1;
	}

	/* check if the line is within range on y */
	dy = g2d_calc_t_convfrom_g2d_coord_t(g2d_coord_t_sub_g2d_coord_t(y  ,arc->c.y));
	if ((g2d_calc_t_gt_g2d_calc_t(dy  ,r)) || (g2d_calc_t_lt_g2d_calc_t(dy  ,g2d_calc_t_neg(r)))) return 0;

	/* calculate the angle under the line would hit the arc */
	g2d_carc_start_end_norm(arc, &start, &end);
	ang = g2d_asin(g2d_calc_t_div_g2d_calc_t(dy,r));

	for(n = 0; n < 2; n++,ang = g2d_angle_t_sub_g2d_angle_t(a_pi  ,ang)) {
		int good;
		
		ang2 = ang;
		if (g2d_angle_t_lt_g2d_angle_t(ang2  ,g2d_angle_t_convfrom_double(0.0)))
			ang2 += a_pi * 2.0;

		good =(  g2d_is_between_angles ( ang2 , start , end )  )||(  g2d_is_between_angles ( ang , start , end ) );
		if (!good) continue;

		/* calculate the x coord of the anticipated intersection and check if it is on the line */
		if (iscs == 0) dx = g2d_sqrt(g2d_calc_t_sub_g2d_calc_t(g2d_calc_t_mul_g2d_calc_t(r  ,r ) ,g2d_calc_t_mul_g2d_calc_t(dy  ,dy)));
		x = g2d_calc_t_convfrom_g2d_coord_t(arc->c.x);
		if (n == 0)
			x = g2d_calc_t_add_g2d_calc_t(x  ,dx);
		else
			x = g2d_calc_t_sub_g2d_calc_t(x  ,dx);
		good =(  g2d_is_between ( x , (g2d_calc_t)x1 , (g2d_calc_t)x2 ) );
		if (!good) continue;

		/* got an intersection */
		if (ip != NULL) ip[iscs] = g2d_cvect(x, g2d_calc_t_convfrom_g2d_coord_t(y));
		if (offs != NULL) {
			if (g2d_angle_t_lt_g2d_angle_t(ang2  ,start)) ang2 += (G2D_PI)*2.0;
			offs[iscs] = g2d_offs_t_convfrom_g2d_angle_t(g2d_angle_t_div_g2d_angle_t((g2d_angle_t_sub_g2d_angle_t(ang2  ,start))  ,(g2d_angle_t_sub_g2d_angle_t(end  ,start))));
		}
		iscs++;
	}

	return iscs;
}

G2D_INLINE int g2d__iscp_carc_vcline(const g2d_carc_t *arc, g2d_coord_t x, g2d_coord_t y1, g2d_coord_t y2, g2d_cvect_t ip[2], g2d_offs_t offs[2])
{
	g2d_calc_t dx, dy, y, r = g2d_calc_t_convfrom_g2d_coord_t(arc->r);
	g2d_angle_t ang, ang2, start, end;
	int n, iscs = 0;

	if (g2d_calc_t_lte_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(arc->r ) ,g2d_calc_t_convfrom_double(0.0))) { /* degenerate case: arc is a point */
		if (g2d_coord_t_neq_g2d_coord_t(arc->c.x  ,x)) return 0;
		if (!((  g2d_is_between ( arc->c.y , y1 , y2 ) )) )return 0;
		if (ip != NULL) ip[0] = g2d_cvect_t_convfrom_g2d_vect_t(arc->c);
		if (offs != 0) offs = 0;
		return 1;
	}

	/* check if the line is within range on y */
	dx = g2d_calc_t_convfrom_g2d_coord_t(g2d_coord_t_sub_g2d_coord_t(x  ,arc->c.x));
	if ((g2d_calc_t_gt_g2d_calc_t(dx  ,r)) || (g2d_calc_t_lt_g2d_calc_t(dx  ,g2d_calc_t_neg(r)))) return 0;

	/* calculate the angle under the line would hit the arc */
	g2d_carc_start_end_norm(arc, &start, &end);
	ang = g2d_acos(g2d_calc_t_div_g2d_calc_t(dx,r));

	for(n = 0; n < 2; n++,ang =  g2d_angle_t_neg(ang)) {
		int good;

		ang2 = ang;
		if (g2d_angle_t_lt_g2d_angle_t(ang2  ,g2d_angle_t_convfrom_double(0.0)))
			ang2 += (G2D_PI) * 2.0;

		good =(  g2d_is_between_angles ( ang2 , start , end )  )||(  g2d_is_between_angles ( ang , start , end ) );
		if (!good) continue;

		/* calculate the x coord of the anticipated intersection and check if it is on the line */
		if (iscs == 0) dy = g2d_sqrt(g2d_calc_t_sub_g2d_calc_t(g2d_calc_t_mul_g2d_calc_t(r  ,r ) ,g2d_calc_t_mul_g2d_calc_t(dx  ,dx)));
		y = g2d_calc_t_convfrom_g2d_coord_t(arc->c.y);
		if (n == 0)
			y = g2d_calc_t_add_g2d_calc_t(y  ,dy);
		else
			y = g2d_calc_t_sub_g2d_calc_t(y  ,dy);
		good =(  g2d_is_between ( y , (g2d_calc_t)y1 , (g2d_calc_t)y2 ) );
		if (!good) continue;

		/* got an intersection */
		if (ip != NULL) ip[iscs] = g2d_cvect(g2d_calc_t_convfrom_g2d_coord_t(x), y);
		if (offs != NULL) {
			if (g2d_angle_t_lt_g2d_angle_t(ang2  ,start)) ang2 += (G2D_PI)*2.0;
			offs[iscs] = g2d_offs_t_convfrom_g2d_angle_t(g2d_angle_t_div_g2d_angle_t((g2d_angle_t_sub_g2d_angle_t(ang2  ,start))  ,(g2d_angle_t_sub_g2d_angle_t(end  ,start))));
		}
		iscs++;
	}

	return iscs;

}

G2D_INLINE int g2d__iscp_carc_box(const g2d_carc_t *arc, const g2d_box_t *box, g2d_cvect_t ip[8], g2d_offs_t offs[8])
{
	int cnt = 0;
	cnt += g2d__iscp_carc_hcline(arc, box->p1.y, box->p1.x, box->p2.x, ip+cnt, offs+cnt);
	cnt += g2d__iscp_carc_hcline(arc, box->p2.y, box->p1.x, box->p2.x, ip+cnt, offs+cnt);
	cnt += g2d__iscp_carc_vcline(arc, box->p1.x, box->p1.y, box->p2.y, ip+cnt, offs+cnt);
	cnt += g2d__iscp_carc_vcline(arc, box->p2.x, box->p1.y, box->p2.y, ip+cnt, offs+cnt);
	return cnt;
}

G2D_INLINE int g2d_iscp_carc_box(const g2d_carc_t *arc, const g2d_box_t *box, g2d_vect_t ip[8], g2d_offs_t offs[8])
{
	g2d_cvect_t cip[8];
	int n, pts = g2d__iscp_carc_box(arc, box, cip, offs);
	for(n = 0; n < pts; n++)
		ip[n] = g2d_vect_t_convfrom_g2d_cvect_t(cip[n]);
	return pts;
}

G2D_INLINE g2d_angle_t g2d__ang_carc_pt(const g2d_carc_t *arc, g2d_cvect_t pt)
{
	g2d_angle_t start, end, ang = g2d_atan2(g2d_calc_t_sub_g2d_calc_t(pt.y  ,g2d_calc_t_convfrom_g2d_coord_t(arc->c.y)), g2d_calc_t_sub_g2d_calc_t(pt.x  ,g2d_calc_t_convfrom_g2d_coord_t(arc->c.x)));
	g2d_carc_start_end_norm(arc, &start, &end);

	/* make sure start..end are positive */
	if (g2d_angle_t_lt_g2d_angle_t(start  ,g2d_angle_t_convfrom_double(0.0))) {
		start += ( G2D_PI) * 2.0;
		end += ( G2D_PI) * 2.0;
	}
	if (g2d_angle_t_lt_g2d_angle_t(ang  ,start)) ang += ( G2D_PI) * 2.0;
	return ang;
}

G2D_INLINE g2d_angle_t g2d__ang_carc_pt_rel(const g2d_carc_t *arc, g2d_cvect_t pt)
{
	g2d_angle_t start, end, ang = g2d_atan2(g2d_calc_t_sub_g2d_calc_t(pt.y  ,g2d_calc_t_convfrom_g2d_coord_t(arc->c.y)), g2d_calc_t_sub_g2d_calc_t(pt.x  ,g2d_calc_t_convfrom_g2d_coord_t(arc->c.x)));
	g2d_carc_start_end_norm(arc, &start, &end);

	/* make sure start..end are positive */
	if (g2d_angle_t_lt_g2d_angle_t(start  ,g2d_angle_t_convfrom_double(0.0))) {
		start += ( G2D_PI) * 2.0;
		end += ( G2D_PI) * 2.0;
	}
	if (g2d_angle_t_lt_g2d_angle_t(ang  ,start)) ang += ( G2D_PI) * 2.0;
	return (g2d_angle_t_sub_g2d_angle_t(ang  ,start));
}

G2D_INLINE g2d_angle_t g2d_ang_carc_pt(const g2d_carc_t *arc, g2d_vect_t pt)
{
	return g2d__ang_carc_pt(arc, g2d_cvect_t_convfrom_g2d_vect_t(pt));
}


G2D_INLINE g2d_angle_t g2d_ang_carc_pt_rel(const g2d_carc_t *arc, g2d_vect_t pt)
{
	return g2d__ang_carc_pt_rel(arc, g2d_cvect_t_convfrom_g2d_vect_t(pt));
}

G2D_INLINE g2d_offs_t g2d__offs_carc_pt(const g2d_carc_t *arc, g2d_cvect_t pt)
{
	g2d_angle_t delta = (arc->delta > 0) ? arc->delta : g2d_angle_t_neg(arc->delta); /* pt is always positive because ends were normalized */
	return g2d_offs_t_convfrom_g2d_angle_t(g2d_angle_t_div_g2d_angle_t(g2d__ang_carc_pt_rel(arc, pt)  ,delta));
}

G2D_INLINE g2d_offs_t g2d_offs_carc_pt(const g2d_carc_t *arc, g2d_vect_t pt)
{
	g2d_angle_t delta = (arc->delta > 0) ? arc->delta : g2d_angle_t_neg(arc->delta); /* pt is always positive because ends were normalized */
	return g2d_offs_t_convfrom_g2d_angle_t(g2d_angle_t_div_g2d_angle_t(g2d_ang_carc_pt_rel(arc, pt)  ,delta));
}

G2D_INLINE int g2d__iscp_carc_carc_addisp(g2d_offs_t off1, g2d_cvect_t isp,  g2d_cvect_t iip[2],  g2d_offs_t ioffs[2], int pts)
{
	if (pts >= 2) return pts;

	if ((pts > 0) && (g2d_offs_t_eq_g2d_offs_t(ioffs[0]  ,off1)))
		return pts; /* don't add redundant points */

	if ((pts > 0) && (g2d_cvect_t_eq_g2d_cvect_t(iip[0]  ,isp))) return pts;
	if ((pts > 0) && (g2d_cvect_t_eq_g2d_cvect_t(iip[1]  ,isp))) return pts;

	iip[pts] = isp;
	ioffs[pts] = off1;
	return pts+1;
}

G2D_INLINE int g2d__iscp_carc_carc(const g2d_carc_t *arc1, const g2d_carc_t *arc2,  g2d_cvect_t ip[2],  g2d_offs_t offs[2])
{
	g2d_cvect_t isp0, isp, nD, vD = g2d_cvect_t_convfrom_g2d_vect_t(g2d_vect_t_sub_g2d_vect_t(arc2->c  ,arc1->c)), arc1_c = g2d_cvect_t_convfrom_g2d_vect_t(arc1->c);
	g2d_calc_t r1q, r2q, d1, y, D, dist, ytmp, arc1r = g2d_calc_t_convfrom_g2d_coord_t(arc1->r), arc2r = g2d_calc_t_convfrom_g2d_coord_t(arc2->r);
	g2d_offs_t off1;
	g2d_angle_t start1, end1, ang2, start2, end2;
	int pts = 0;

	/* special case: may be overlapping arcs */
	if (g2d_vect_t_eq_g2d_vect_t(arc1->c  ,arc2->c)) {
		g2d_cvect_t iip[2] = {0};   /* need to track all intersection points internally because endpoints may match */
		g2d_offs_t ioffs[2] = {0};
		int full1, full2;
		g2d_angle_t pi2, mpi2;

		if (g2d_coord_t_neq_g2d_coord_t(arc1->r  ,arc2->r))
			return 0;

		/* can't use  g2d_carc_start_end_norm() since it would reorder
		   start and end and that would mess with which end we return as iscp */
		start1 = arc1->start;
		start2 = arc2->start;
		g2d_normalize_angle(start1);
		g2d_normalize_angle(start2);
		end1 = g2d_angle_t_add_g2d_angle_t(start1  ,arc1->delta);
		end2 = g2d_angle_t_add_g2d_angle_t(start2  ,arc2->delta);

		pi2 = g2d_angle_t_convfrom_double((2.0) * G2D_PI);
		mpi2 = g2d_angle_t_convfrom_double((-2.0) * G2D_PI);
		full1 = (g2d_angle_t_gte_g2d_angle_t(arc1->delta  ,pi2)) || (g2d_angle_t_lte_g2d_angle_t(arc1->delta  ,mpi2));
		full2 = (g2d_angle_t_gte_g2d_angle_t(arc2->delta  ,pi2)) || (g2d_angle_t_lte_g2d_angle_t(arc2->delta  ,mpi2));

		if (full1 && full2) /* both arcs are full circles - can't handle correctly */
			return -2;

		if (full1) { /* arc2 is within full circle arc1 */
			iip[0] = g2d__carc_offs(arc2, g2d_offs_t_convfrom_double(0.0));
			ioffs[0] = g2d__offs_carc_pt(arc1, iip[0]);
			iip[1] = g2d__carc_offs(arc2, g2d_offs_t_convfrom_double(1.0));
			ioffs[1] = g2d__offs_carc_pt(arc1, iip[1]);
			pts = 2;
			goto olap_out;
		}

		if (full2) { /* arc1 is within full circle arc2 */
			iip[0] = g2d__carc_offs(arc1, g2d_offs_t_convfrom_double(0.0));
			ioffs[0] = g2d_offs_t_convfrom_double(0.0);
			iip[1] = g2d__carc_offs(arc1, g2d_offs_t_convfrom_double(1.0));
			ioffs[1] = g2d_offs_t_convfrom_double(1.0);
			pts = 2;
			goto olap_out;
		}

		/* check arc2's endpoint within arc1 */
		if ((  g2d_is_between_angles ( start2 , start1 , end1 ) ) ){
			isp = g2d__carc_offs(arc2, g2d_offs_t_convfrom_double(0.0));
			off1 = g2d__offs_carc_pt(arc1, isp);
			pts = g2d__iscp_carc_carc_addisp(off1, isp, iip,  ioffs, pts);
		}

		if ((  g2d_is_between_angles ( end2 , start1 , end1 ) ) ){
			isp = g2d__carc_offs(arc2, g2d_offs_t_convfrom_double(1.0));
			off1 = g2d__offs_carc_pt(arc1, isp);
			pts = g2d__iscp_carc_carc_addisp(off1, isp, iip,  ioffs, pts);
		}

		/* check if arc1 endpoints are within arc2 */
		if ((pts < 2) &&(  g2d_is_between_angles ( start1 , start2 , end2 ) ) ){
			isp = g2d__carc_offs(arc1, g2d_offs_t_convfrom_double(0.0));
			off1 = g2d_offs_t_convfrom_double(0.0);
			pts = g2d__iscp_carc_carc_addisp(off1, isp, iip,  ioffs, pts);
		}

		if ((pts < 2) &&(  g2d_is_between_angles ( end1 , start2 , end2 ) ) ){
			isp = g2d__carc_offs(arc1, g2d_offs_t_convfrom_double(1.0));
			off1 = g2d_offs_t_convfrom_double(1.0);
			pts = g2d__iscp_carc_carc_addisp(off1, isp, iip,  ioffs, pts);
		}


		/* produce output */
		olap_out:;
		if (ip != NULL) {
			ip[0] = iip[0];
			ip[1] = iip[1];
		}
		if (offs != NULL) {
			offs[0] = ioffs[0];
			offs[1] = ioffs[1];
		}

		return pts;
	}

	D = g2d_calc_t_convfrom_g2d_cvect_t(vD); /* length of vD */
	dist = g2d_calc_t_convfrom_g2d_coord_t(g2d_coord_t_add_g2d_coord_t(arc1->r  ,arc2->r));

	/* the arcs are too far away */
	if (g2d_calc_t_gt_g2d_calc_t(D  ,dist))
		return 0;

	g2d_carc_start_end_norm(arc2, &start2, &end2);


	vD.x = g2d_calc_t_div_g2d_calc_t(vD.x  ,D);
	vD.y = g2d_calc_t_div_g2d_calc_t(vD.y  ,D);
	nD.x = g2d_calc_t_neg(vD.y);
	nD.y = vD.x;

	r1q = g2d_calc_t_mul_g2d_calc_t(arc1r  ,arc1r);
	r2q = g2d_calc_t_mul_g2d_calc_t(arc2r  ,arc2r);
	d1 = g2d_calc_t_add_g2d_calc_t(g2d_calc_t_div_g2d_calc_t((g2d_calc_t_sub_g2d_calc_t(r1q  ,r2q))  ,(g2d_calc_t_mul_g2d_calc_t(g2d_calc_t_convfrom_double(2.0),D)) ) ,g2d_calc_t_div_g2d_calc_t(D,g2d_calc_t_convfrom_double(2.0)));

	isp0 = g2d_cvect_t_add_g2d_cvect_t(arc1_c  ,g2d_calc_t_MUL_g2d_cvect_t(vD  ,d1));

	/* exactly 1 intersection because they are just touching; this must be isp0 */
	if (g2d_calc_t_eq_g2d_calc_t(D  ,dist)) {
		ang2 = g2d__ang_carc_pt(arc2, isp0);
		if ((g2d_angle_t_lt_g2d_angle_t(ang2  ,start2)) || (g2d_angle_t_gt_g2d_angle_t(ang2  ,end2)))
			return 0;
		off1 = g2d__offs_carc_pt(arc1, isp0);
		if ((g2d_offs_t_lt_g2d_offs_t(off1  ,g2d_offs_t_convfrom_double(0.0))) || (g2d_offs_t_gt_g2d_offs_t(off1  ,g2d_offs_t_convfrom_double(1.0))))
			return 0;
		if (ip != NULL)
			ip[0] = isp0;
		if (offs != NULL)
			offs[0] = off1;
		return 1;
	}

	ytmp = g2d_calc_t_sub_g2d_calc_t(r1q  ,(g2d_calc_t_mul_g2d_calc_t(d1,d1)));
	if (g2d_calc_t_lte_g2d_calc_t(ytmp  ,g2d_calc_t_convfrom_double(0.0)))
		return 0; /* one arc is within the other, with smaller radius - no contact */

	y = g2d_sqrt(ytmp);

	isp = g2d_cvect_t_add_g2d_cvect_t(isp0  ,g2d_calc_t_MUL_g2d_cvect_t(nD  ,y));
	ang2 = g2d__ang_carc_pt(arc2, isp);
	if ((g2d_angle_t_lt_g2d_angle_t(ang2  ,start2)) || (g2d_angle_t_gt_g2d_angle_t(ang2  ,end2)))
		goto skip;
	off1 = g2d__offs_carc_pt(arc1, isp);
	if ((g2d_offs_t_lt_g2d_offs_t(off1  ,g2d_offs_t_convfrom_double(0.0))) || (g2d_offs_t_gt_g2d_offs_t(off1  ,g2d_offs_t_convfrom_double(1.0))))
		goto skip;

	if (ip != NULL)
		ip[pts] = isp;
	if (offs != NULL)
		offs[pts] = off1;
	pts++;

	skip:;
	isp = g2d_cvect_t_sub_g2d_cvect_t(isp0  ,g2d_calc_t_MUL_g2d_cvect_t(nD  ,y));
	ang2 = g2d__ang_carc_pt(arc2, isp);
	if ((g2d_angle_t_lt_g2d_angle_t(ang2  ,start2)) || (g2d_angle_t_gt_g2d_angle_t(ang2  ,end2)))
		return pts;
	off1 = g2d__offs_carc_pt(arc1, isp);
	if ((g2d_offs_t_lt_g2d_offs_t(off1  ,g2d_offs_t_convfrom_double(0.0))) || (g2d_offs_t_gt_g2d_offs_t(off1  ,g2d_offs_t_convfrom_double(1.0))))
		return pts;

	if (ip != NULL)
		ip[pts] = isp;
	if (offs != NULL)
		offs[pts] = off1;
	pts++;

	return pts;
}

G2D_INLINE int g2d_iscp_carc_carc(const g2d_carc_t *arc1, const g2d_carc_t *arc2,  g2d_vect_t ip[2],  g2d_offs_t offs[2])
{
	g2d_cvect_t cip[2];
	int pts = g2d__iscp_carc_carc(arc1, arc2,  cip, offs);
	if (ip != NULL) {
		if (pts > 0) ip[0] = g2d_vect_t_convfrom_g2d_cvect_t(cip[0]);
		if (pts > 1) ip[1] = g2d_vect_t_convfrom_g2d_cvect_t(cip[1]);
	}
	return pts;
}

G2D_INLINE int g2d__iscp_carc_circle(const g2d_carc_t *arc1, g2d_vect_t center, g2d_calc_t r, g2d_cvect_t ip[2],  g2d_offs_t offs[2])
{
	g2d_carc_t arc2;

	arc2.c = center;
	arc2.r = g2d_coord_t_convfrom_g2d_calc_t(r);
	arc2.start = g2d_angle_t_convfrom_double(0.0);
	arc2.delta = g2d_angle_t_convfrom_double(2.0 * G2D_PI);
	return g2d__iscp_carc_carc(arc1, &arc2,  ip,  offs);
}


G2D_INLINE int g2d_iscp_carc_circle(const g2d_carc_t *arc1, g2d_vect_t center, g2d_calc_t r,  g2d_vect_t ip[2],  g2d_offs_t offs[2])
{
	g2d_cvect_t cip[2];
	int pts = g2d__iscp_carc_circle(arc1, center, r,  cip, offs);
	if (ip != NULL) {
		if (pts > 0) ip[0] = g2d_vect_t_convfrom_g2d_cvect_t(cip[0]);
		if (pts > 1) ip[1] = g2d_vect_t_convfrom_g2d_cvect_t(cip[1]);
	}
	return pts;
}
#endif /* G2D_CARC_H */
