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

#ifndef G2D_SLINE_H
#define G2D_SLINE_H
#include <opc89.h>
#include <gengeo2d/prim.h>
#include <gengeo2d/vect.h>
#include <gengeo2d/box.h>
#include <gengeo2d/cline.h>


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

/* Construct an sline from two endpoints and stroke details */
G2D_INLINE g2d_sline_t g2d_sline(g2d_vect_t p1, g2d_vect_t p2, g2d_coord_t width, g2d_cap_t cap);

/* Calculate the bounding box of an sline */
G2D_INLINE g2d_box_t g2d_sline_bbox(const g2d_sline_t *line);

/* Return if various object types have intersection with an sline */
G2D_INLINE int g2d_isc_sline_circle(const g2d_sline_t *sline, g2d_vect_t center, g2d_calc_t r);
G2D_INLINE int g2d_isc_sline_sline(const g2d_sline_t *line1, const g2d_sline_t *line2);

/* Return the side clines of an sline; they are the clines offset from the
   centerline by +- stroke width / 2 */
G2D_INLINE void g2d_sline_sides(const g2d_sline_t *sline, g2d_cline_t *side_pos, g2d_cline_t *side_neg);


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


G2D_INLINE g2d_sline_t g2d_sline(g2d_vect_t p1, g2d_vect_t p2, g2d_coord_t width, g2d_cap_t cap)
{
	g2d_sline_t l;
	l.c.p1 = p1; l.c.p2 = p2;
	l.s.width = width; l.s.cap = cap;
	return l;
}

G2D_INLINE g2d_box_t g2d_sline_bbox(const g2d_sline_t *line)
{
	g2d_box_t b;
	g2d_calc_t hw = g2d_calc_t_div_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(line->s.width ) ,g2d_calc_t_convfrom_double(2.0));
	g2d_cvect_t dv, nv, p1, p2, a;

	p1.x = g2d_calc_t_convfrom_g2d_coord_t(line->c.p1.x); p1.y = g2d_calc_t_convfrom_g2d_coord_t(line->c.p1.y);
	p2.x = g2d_calc_t_convfrom_g2d_coord_t(line->c.p2.x); p2.y = g2d_calc_t_convfrom_g2d_coord_t(line->c.p2.y);

	switch(line->s.cap) {
		case G2D_CAP_ROUND:
			b.p1 = g2d_vect(g2d_round_coord_down(g2d_calc_t_sub_g2d_calc_t(p1.x  ,hw)), g2d_round_coord_down(g2d_calc_t_sub_g2d_calc_t(p1.y  ,hw)));
			b.p2 = g2d_vect(g2d_round_coord_up(g2d_calc_t_sub_g2d_calc_t(p1.x  ,hw)), g2d_round_coord_up(g2d_calc_t_sub_g2d_calc_t(p1.y  ,hw)));
			g2d__box_bump_pt(&b, g2d_cvect(g2d_calc_t_add_g2d_calc_t(p1.x  ,hw), g2d_calc_t_add_g2d_calc_t(p1.y  ,hw)));
			g2d__box_bump_pt(&b, g2d_cvect(g2d_calc_t_sub_g2d_calc_t(p2.x  ,hw), g2d_calc_t_sub_g2d_calc_t(p2.y  ,hw)));
			g2d__box_bump_pt(&b, g2d_cvect(g2d_calc_t_add_g2d_calc_t(p2.x  ,hw), g2d_calc_t_add_g2d_calc_t(p2.y  ,hw)));
			break;
		case G2D_CAP_SQUARE:
			dv = g2d_cvect_t_sub_g2d_cvect_t(p2  ,p1);
			dv = g2d__vect_normalize(dv);
			nv = g2d__vect_perp(dv);
			a = g2d_cvect_t_add_g2d_cvect_t(g2d_cvect_t_sub_g2d_cvect_t(p1  ,g2d_calc_t_MUL_g2d_cvect_t(dv  ,hw ) ),g2d_calc_t_MUL_g2d_cvect_t(nv  ,hw));
			b.p1.x = g2d_round_coord_down(a.x); b.p1.y = g2d_round_coord_down(a.y);
			b.p2.x = g2d_round_coord_up(a.x); b.p2.y = g2d_round_coord_up(a.y);
			g2d__box_bump_pt(&b, g2d_cvect_t_sub_g2d_cvect_t(g2d_cvect_t_sub_g2d_cvect_t(p1  ,g2d_calc_t_MUL_g2d_cvect_t(dv  ,hw ) ),g2d_calc_t_MUL_g2d_cvect_t(nv  ,hw)));
			g2d__box_bump_pt(&b, g2d_cvect_t_add_g2d_cvect_t(g2d_cvect_t_add_g2d_cvect_t(p2  ,g2d_calc_t_MUL_g2d_cvect_t(dv  ,hw ) ),g2d_calc_t_MUL_g2d_cvect_t(nv  ,hw)));
			g2d__box_bump_pt(&b, g2d_cvect_t_sub_g2d_cvect_t(g2d_cvect_t_add_g2d_cvect_t(p2  ,g2d_calc_t_MUL_g2d_cvect_t(dv  ,hw ) ),g2d_calc_t_MUL_g2d_cvect_t(nv  ,hw)));
			break;
	}
	return b;
}

G2D_INLINE int g2d_isc_sline_circle(const g2d_sline_t *sline, g2d_vect_t center, g2d_calc_t r)
{
	g2d_cline_t cline = sline->c;
	g2d_calc_t len, sn, cs, x_saved, half_thick = g2d_calc_t_div_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(sline->s.width),g2d_calc_t_convfrom_double(2.0)), tmp;

	/* local coord system's origin should be cline's first end point */
	cline.p2 = g2d_vect_t_sub_g2d_vect_t(cline.p2  ,cline.p1);
	center = g2d_vect_t_sub_g2d_vect_t(center  ,cline.p1);

	if ((g2d_coord_t_eq_g2d_coord_t(center.x  ,g2d_coord_t_convfrom_int(0))) && (g2d_coord_t_eq_g2d_coord_t(center.y  ,g2d_coord_t_convfrom_int(0))))
		return 1;

	/* rotate circle center around the new origin; local coord system is cline now */
	len = g2d__distance(g2d_cvect(g2d_calc_t_convfrom_double(0.0),g2d_calc_t_convfrom_double(0.0)), g2d_cvect_t_convfrom_g2d_vect_t(cline.p2));

	x_saved = g2d_calc_t_convfrom_g2d_coord_t(center.x);

	if (g2d_calc_t_neq_g2d_calc_t(len  ,g2d_calc_t_convfrom_double(0.0))) { /* rotate onloy if line has a length */
		sn = g2d_calc_t_div_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(cline.p2.y ) ,len); /* sin(theta) */
		cs = g2d_calc_t_div_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(cline.p2.x ) ,len); /* cos(theta) */
		center.x = g2d_coord_t_convfrom_g2d_calc_t(g2d_calc_t_add_g2d_calc_t(g2d_calc_t_mul_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(center.x ) ,cs ) ,g2d_calc_t_mul_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(center.y ) ,sn)));
		center.y = g2d_coord_t_convfrom_g2d_calc_t(g2d_calc_t_sub_g2d_calc_t(g2d_calc_t_mul_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(center.y ) ,cs ) ,g2d_calc_t_mul_g2d_calc_t(x_saved  ,sn)));
	}

	/* mirror to upper half, if needed */
	if (g2d_coord_t_lt_g2d_coord_t(center.y  ,g2d_coord_t_convfrom_int(0)))
		center.y = g2d_coord_t_neg(center.y);

	switch(sline->s.cap) {
		case G2D_CAP_ROUND:
			if (g2d_coord_t_lte_g2d_coord_t(center.x  ,g2d_coord_t_convfrom_int(0))) { /* center.x left to p1 */
				tmp = (g2d_calc_t_add_g2d_calc_t(r  ,half_thick));
				return g2d_calc_t_gt_g2d_calc_t(g2d_calc_t_mul_g2d_calc_t(tmp,tmp ) ,g2d__distance2(g2d_cvect(g2d_calc_t_convfrom_double(0.0), g2d_calc_t_convfrom_double(0.0)), g2d_cvect_t_convfrom_g2d_vect_t(center)));
			}
			else if (g2d_calc_t_gte_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(center.x ) ,len)) { /* center.x right to p2 */
				tmp = g2d_calc_t_add_g2d_calc_t(r  ,half_thick);
				return g2d_calc_t_gt_g2d_calc_t(g2d_calc_t_mul_g2d_calc_t(tmp,tmp ) ,g2d__distance2(g2d_cvect(len, g2d_calc_t_convfrom_double(0.0)), g2d_cvect_t_convfrom_g2d_vect_t(center)));
			}
			/* center.x between p1 and p2 */
			return g2d_calc_t_lt_g2d_calc_t((g2d_calc_t_sub_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(center.y ) ,half_thick))  ,r);

		case G2D_CAP_SQUARE:
			len += sline->s.width;
			center.x += half_thick;

			if (g2d_calc_t_lte_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(center.x ) ,g2d_calc_t_convfrom_double(0.0))) { /* center.x left to p1 */
				if (g2d_calc_t_lte_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(center.y ) ,half_thick))
					return g2d_calc_t_lt_g2d_calc_t((g2d_calc_t_convfrom_g2d_coord_t(g2d_coord_t_neg(center.x)))  ,r);
				else
					return g2d_calc_t_gt_g2d_calc_t(g2d_calc_t_mul_g2d_calc_t(r,r ) ,g2d__distance2(g2d_cvect(g2d_calc_t_convfrom_double(0.0), half_thick), g2d_cvect_t_convfrom_g2d_vect_t(center)));
			}
			else if (g2d_calc_t_gte_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(center.x ) ,len)) { /* center.x right to p2 */
				if (g2d_calc_t_lte_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(center.y ) ,half_thick))
					return g2d_calc_t_lt_g2d_calc_t((g2d_calc_t_sub_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(center.x ) ,len))  ,r);
				else
					return g2d_calc_t_gt_g2d_calc_t(g2d_calc_t_mul_g2d_calc_t(r,r ) ,g2d__distance2(g2d_cvect(len, half_thick), g2d_cvect_t_convfrom_g2d_vect_t(center)));
			}
			/* center.x between p1 and p2 */
			return g2d_calc_t_lt_g2d_calc_t((g2d_calc_t_sub_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(center.y ) ,half_thick))  ,r);
	}

	return 0;
}

/* Check if the idxth cap of linecap touches line; idx is 0 or 1 */
G2D_INLINE int g2d_isc_sline_sline_cap(const g2d_sline_t *line, const g2d_sline_t *linecap, int idx)
{
	/* no square-line isc yet */
	 assert ( linecap->s.cap == G2D_CAP_ROUND ) ;
	if (idx == 0)
		return g2d_isc_sline_circle(line, linecap->c.p1, g2d_calc_t_div_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(linecap->s.width),g2d_calc_t_convfrom_double(2.0)));
	return g2d_isc_sline_circle(line, linecap->c.p2, g2d_calc_t_div_g2d_calc_t(g2d_calc_t_convfrom_g2d_coord_t(linecap->s.width),g2d_calc_t_convfrom_double(2.0)));
}

G2D_INLINE int g2d_isc_sline_sline(const g2d_sline_t *line1, const g2d_sline_t *line2)
{
	/* cheapest, most common: centerline crossing; this is when the body of the
	   lines cross */
	if (g2d_isc_cline_cline(&line1->c, &line2->c)) return 1;

	/* else end caps can still touch the other line */
	if (g2d_isc_sline_sline_cap(line1, line2, 0)) return 1;
	if (g2d_isc_sline_sline_cap(line1, line2, 1)) return 1;
	if (g2d_isc_sline_sline_cap(line2, line1, 0)) return 1;
	if (g2d_isc_sline_sline_cap(line2, line1, 1)) return 1;

	return 0;
}


G2D_INLINE void g2d_sline_sides(const g2d_sline_t *sline, g2d_cline_t *side_pos, g2d_cline_t *side_neg)
{
	g2d_cvect_t n = g2d__vect_normal((g2d_cvect_t_convfrom_g2d_vect_t(g2d_vect_t_sub_g2d_vect_t(sline->c.p2  ,sline->c.p1))));
	g2d_cvect_t o = g2d_calc_t_MUL_g2d_cvect_t(g2d_calc_t_MUL_g2d_cvect_t(n  ,g2d_calc_t_convfrom_g2d_coord_t(sline->s.width ) ),g2d_calc_t_convfrom_double(0.5));
	if (side_pos != NULL) {
		side_pos->p1 = g2d_vect_t_convfrom_g2d_cvect_t(g2d_cvect_t_ADD_g2d_vect_t(sline->c.p1  ,o));
		side_pos->p2 = g2d_vect_t_convfrom_g2d_cvect_t(g2d_cvect_t_ADD_g2d_vect_t(sline->c.p2  ,o));
	}
	if (side_neg != NULL) {
		side_neg->p1 = g2d_vect_t_convfrom_g2d_cvect_t(g2d_vect_t_sub_g2d_cvect_t(sline->c.p1  ,o));
		side_neg->p2 = g2d_vect_t_convfrom_g2d_cvect_t(g2d_vect_t_sub_g2d_cvect_t(sline->c.p2  ,o));
	}
}

#endif /* G2D_SLINE_H */
