Ejemplo n.º 1
0
/*
** Test left/right side.
*/
static void test_lw_segment_side(void)
{
	int rv = 0;
	POINT2D p1, p2, q;

	/* Vertical line at x=0 */
	p1.x = 0.0;
	p1.y = 0.0;
	p2.x = 0.0;
	p2.y = 1.0;

	/* On the left */
	q.x = -2.0;
	q.y = 1.5;
	rv = lw_segment_side(&p1, &p2, &q);
	//printf("left %g\n",rv);
	CU_ASSERT(rv < 0);

	/* On the right */
	q.x = 2.0;
	rv = lw_segment_side(&p1, &p2, &q);
	//printf("right %g\n",rv);
	CU_ASSERT(rv > 0);

	/* On the line */
	q.x = 0.0;
	rv = lw_segment_side(&p1, &p2, &q);
	//printf("on line %g\n",rv);
	CU_ASSERT_EQUAL(rv, 0);

}
Ejemplo n.º 2
0
/**
* Returns LW_TRUE if b is on the arc formed by a1/a2/a3, but not within
* that portion already described by a1/a2/a3
*/
static int pt_continues_arc(const POINT4D *a1, const POINT4D *a2, const POINT4D *a3, const POINT4D *b)
{
	POINT4D center;
	POINT4D *centerptr=&center;
	double radius = lwcircle_center(a1, a2, a3, &center);
	double b_distance, diff;

	/* Co-linear a1/a2/a3 */
	if ( radius < 0.0 )
		return LW_FALSE;

	b_distance = distance2d_pt_pt((POINT2D*)b, (POINT2D*)centerptr);
	diff = fabs(radius - b_distance);
	LWDEBUGF(4, "circle_radius=%g, b_distance=%g, diff=%g, percentage=%g", radius, b_distance, diff, diff/radius);
	
	/* Is the point b on the circle? */
	if ( diff < EPSILON_SQLMM ) 
	{
		int a2_side = signum(lw_segment_side((POINT2D*)a1, (POINT2D*)a3, (POINT2D*)a2));
		int b_side = signum(lw_segment_side((POINT2D*)a1, (POINT2D*)a3, (POINT2D*)b));
		
		/* Is the point b on the same side of a1/a3 as the mid-point a2 is? */
		/* If not, it's in the unbounded part of the circle, so it continues the arc, return true. */
		if ( b_side != a2_side )
			return LW_TRUE;
	}
	return LW_FALSE;
}
Ejemplo n.º 3
0
/**
* Returns the length of a circular arc segment
*/
double
lw_arc_length(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3)
{
	POINT2D C;
	double radius_A, circumference_A;
	int a2_side, clockwise;
	double a1, a3;
	double angle;
	
	if ( lw_arc_is_pt(A1, A2, A3) )
		return 0.0;
	
	radius_A = lw_arc_center(A1, A2, A3, &C);

	/* Co-linear! Return linear distance! */
	if ( radius_A < 0 ) 
	{
        double dx = A1->x - A3->x;
        double dy = A1->y - A3->y;
		return sqrt(dx*dx + dy*dy);
	}
	
	/* Closed circle! Return the circumference! */
	circumference_A = M_PI * 2 * radius_A;
	if ( p2d_same(A1, A3) )
		return circumference_A;
	
	/* Determine the orientation of the arc */
	a2_side = lw_segment_side(A1, A3, A2);

	/* The side of the A1/A3 line that A2 falls on dictates the sweep  
	   direction from A1 to A3. */
	if ( a2_side == -1 ) 
		clockwise = LW_TRUE;
	else 
		clockwise = LW_FALSE;
		
	/* Angles of each point that defines the arc section */
	a1 = atan2(A1->y - C.y, A1->x - C.x);
	a3 = atan2(A3->y - C.y, A3->x - C.x);

	/* What's the sweep from A1 to A3? */
	if ( clockwise )
	{
		if ( a1 > a3 )
			angle = a1 - a3;
		else
			angle = 2*M_PI + a1 - a3; 
	}
	else
	{
		if ( a3 > a1 )
			angle = a3 - a1;
		else
			angle = 2*M_PI + a3 - a1; 			
	}

	/* Length as proportion of circumference */
	return circumference_A * (angle / (2*M_PI));
}
Ejemplo n.º 4
0
/**
* Returns LW_TRUE if b is on the arc formed by a1/a2/a3, but not within
* that portion already described by a1/a2/a3
*/
static int pt_continues_arc(const POINT4D *a1, const POINT4D *a2, const POINT4D *a3, const POINT4D *b)
{
	POINT2D center;
	POINT2D *t1 = (POINT2D*)a1;
	POINT2D *t2 = (POINT2D*)a2;
	POINT2D *t3 = (POINT2D*)a3;
	POINT2D *tb = (POINT2D*)b;
	double radius = lw_arc_center(t1, t2, t3, &center);
	double b_distance, diff;

	/* Co-linear a1/a2/a3 */
	if ( radius < 0.0 )
		return LW_FALSE;

	b_distance = distance2d_pt_pt(tb, &center);
	diff = fabs(radius - b_distance);
	LWDEBUGF(4, "circle_radius=%g, b_distance=%g, diff=%g, percentage=%g", radius, b_distance, diff, diff/radius);

	/* Is the point b on the circle? */
	if ( diff < EPSILON_SQLMM )
	{
		int a2_side = lw_segment_side(t1, t3, t2);
		int b_side  = lw_segment_side(t1, t3, tb);
		double angle1 = lw_arc_angle(t1, t2, t3);
		double angle2 = lw_arc_angle(t2, t3, tb);

		/* Is the angle similar to the previous one ? */
		diff = fabs(angle1 - angle2);
		LWDEBUGF(4, " angle1: %g, angle2: %g, diff:%g", angle1, angle2, diff);
		if ( diff > EPSILON_SQLMM )
		{
			return LW_FALSE;
		}

		/* Is the point b on the same side of a1/a3 as the mid-point a2 is? */
		/* If not, it's in the unbounded part of the circle, so it continues the arc, return true. */
		if ( b_side != a2_side )
			return LW_TRUE;
	}
	return LW_FALSE;
}
Ejemplo n.º 5
0
int lw_arc_side(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, const POINT2D *Q)
{
	POINT2D C;
	double radius_A;
	double side_Q, side_A2;
	double d;
	
	side_Q = lw_segment_side(A1, A3, Q);
	radius_A = lw_arc_center(A1, A2, A3, &C);
	side_A2 = lw_segment_side(A1, A3, A2);
	
	/* Linear case */
	if ( radius_A < 0 )
		return side_Q;
		
	d = distance2d_pt_pt(Q, &C);
	
	/* Q is on the arc boundary */
	if ( d == radius_A && side_Q == side_A2 )
	{	
		return 0;
	}
	
	/* Q on A1-A3 line, so its on opposite side to A2 */
	if ( side_Q == 0 )
	{
		return -1 * side_A2;
	}
	
	/* 
	* Q is inside the arc boundary, so it's not on the side we 
	* might think from examining only the end points
	*/
	if ( d < radius_A && side_Q == side_A2 )
	{
		side_Q *= -1;
	}
	
	return side_Q;
}
Ejemplo n.º 6
0
int lw_arc_calculate_gbox_cartesian_2d(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, GBOX *gbox)
{
	POINT2D xmin, ymin, xmax, ymax;
	POINT2D C;
	int A2_side;
	double radius_A;

	LWDEBUG(2, "lw_arc_calculate_gbox_cartesian_2d called.");

	radius_A = lw_arc_center(A1, A2, A3, &C);

	/* Negative radius signals straight line, p1/p2/p3 are colinear */
	if (radius_A < 0.0)
	{
        gbox->xmin = FP_MIN(A1->x, A3->x);
        gbox->ymin = FP_MIN(A1->y, A3->y);
        gbox->xmax = FP_MAX(A1->x, A3->x);
        gbox->ymax = FP_MAX(A1->y, A3->y);
	    return LW_SUCCESS;
	}

	/* Matched start/end points imply circle */
	if ( A1->x == A3->x && A1->y == A3->y )
	{
		gbox->xmin = C.x - radius_A;
		gbox->ymin = C.y - radius_A;
		gbox->xmax = C.x + radius_A;
		gbox->ymax = C.y + radius_A;
		return LW_SUCCESS;
	}

	/* First approximation, bounds of start/end points */
    gbox->xmin = FP_MIN(A1->x, A3->x);
    gbox->ymin = FP_MIN(A1->y, A3->y);
    gbox->xmax = FP_MAX(A1->x, A3->x);
    gbox->ymax = FP_MAX(A1->y, A3->y);

	/* Create points for the possible extrema */
	xmin.x = C.x - radius_A;
	xmin.y = C.y;
	ymin.x = C.x;
	ymin.y = C.y - radius_A;
	xmax.x = C.x + radius_A;
	xmax.y = C.y;
	ymax.x = C.x;
	ymax.y = C.y + radius_A;

	/* Divide the circle into two parts, one on each side of a line
	   joining p1 and p3. The circle extrema on the same side of that line
	   as p2 is on, are also the extrema of the bbox. */

	A2_side = lw_segment_side(A1, A3, A2);

	if ( A2_side == lw_segment_side(A1, A3, &xmin) )
		gbox->xmin = xmin.x;

	if ( A2_side == lw_segment_side(A1, A3, &ymin) )
		gbox->ymin = ymin.y;

	if ( A2_side == lw_segment_side(A1, A3, &xmax) )
		gbox->xmax = xmax.x;

	if ( A2_side == lw_segment_side(A1, A3, &ymax) )
		gbox->ymax = ymax.y;

	return LW_SUCCESS;
}
Ejemplo n.º 7
0
static POINTARRAY *
lwcircle_segmentize(POINT4D *p1, POINT4D *p2, POINT4D *p3, uint32_t perQuad)
{
	POINT4D center;
	POINT4D pt;
	int p2_side = 0;
	int clockwise = LW_TRUE;
	double radius; /* Arc radius */
	double increment; /* Angle per segment */
	double a1, a2, a3, angle;
	POINTARRAY *pa;
	int result;
	int is_circle = LW_FALSE;

	LWDEBUG(2, "lwcircle_calculate_gbox called.");

	radius = lwcircle_center(p1, p2, p3, &center);
	p2_side = signum(lw_segment_side((POINT2D*)p1, (POINT2D*)p3, (POINT2D*)p2));

	/* Matched start/end points imply circle */
	if ( p1->x == p3->x && p1->y == p3->y )
		is_circle = LW_TRUE;
	
	/* Negative radius signals straight line, p1/p2/p3 are colinear */
	if ( radius < 0.0 || p2_side == 0 )
	    return NULL;
		
	/* The side of the p1/p3 line that p2 falls on dictates the sweep  
	   direction from p1 to p3. */
	if ( p2_side == -1 )
		clockwise = LW_TRUE;
	else
		clockwise = LW_FALSE;
		
	increment = fabs(M_PI_2 / perQuad);
	
	/* Angles of each point that defines the arc section */
	a1 = atan2(p1->y - center.y, p1->x - center.x);
	a2 = atan2(p2->y - center.y, p2->x - center.x);
	a3 = atan2(p3->y - center.y, p3->x - center.x);

	/* p2 on left side => clockwise sweep */
	if ( clockwise )
	{
		increment *= -1;
		/* Adjust a3 down so we can decrement from a1 to a3 cleanly */
		if ( a3 > a1 )
			a3 -= 2.0 * M_PI;
		if ( a2 > a1 )
			a2 -= 2.0 * M_PI;
	}
	/* p2 on right side => counter-clockwise sweep */
	else
	{
		/* Adjust a3 up so we can increment from a1 to a3 cleanly */
		if ( a3 < a1 )
			a3 += 2.0 * M_PI;
		if ( a2 < a1 )
			a2 += 2.0 * M_PI;
	}
	
	/* Override angles for circle case */
	if( is_circle )
	{
		a3 = a1 + 2.0 * M_PI;
		a2 = a1 + M_PI;
		increment = fabs(increment);
		clockwise = LW_FALSE;
	}
	
	/* Initialize point array */
	pa = ptarray_construct_empty(1, 1, 32);

	/* Sweep from a1 to a3 */
	for ( angle = a1; clockwise ? angle > a3 : angle < a3; angle += increment ) 
	{
		pt.x = center.x + radius * cos(angle);
		pt.y = center.y + radius * sin(angle);
		pt.z = interpolate_arc(angle, a1, a2, a3, p1->z, p2->z, p3->z);
		pt.m = interpolate_arc(angle, a1, a2, a3, p1->m, p2->m, p3->m);
		result = ptarray_append_point(pa, &pt, LW_FALSE);
	}	
	return pa;
}
Ejemplo n.º 8
0
/*
static int lwcircle_calculate_gbox_cartesian(const POINT4D *p1, const POINT4D *p2, const POINT4D *p3, GBOX *gbox)
*/
BOX3D *
lwcircle_compute_box3d(POINT4D *p1, POINT4D *p2, POINT4D *p3)
{
	POINT2D xmin, ymin, xmax, ymax;
	POINT4D *center = NULL;
	int p2_side = 0;
	double radius = 0.0;

	LWDEBUG(2, "lwcircle_compute_box3d called.");

	radius = lwcircle_center(p1, p2, p3, &center);
	BOX3D *box = lwalloc(sizeof(BOX3D));
	
	/* Negative radius signals straight line, p1/p2/p3 are colinear */
	if (radius < 0.0)
	{
		if ( center ) lwfree(center);
        box->xmin = FP_MIN(p1->x, p3->x);
        box->ymin = FP_MIN(p1->y, p3->y);
        box->zmin = FP_MIN(p1->z, p3->z);
        box->xmax = FP_MAX(p1->x, p3->x);
        box->ymax = FP_MAX(p1->y, p3->y);
        box->zmax = FP_MAX(p1->z, p3->z);
	    return box;
	}
	
	/* Matched start/end points imply circle */
	if ( p1->x == p3->x && p1->y == p3->y )
	{
		box->xmin = center->x - radius;
		box->ymin = center->y - radius;
		box->zmin = FP_MIN(p1->z,p2->z);
		box->xmax = center->x + radius;
		box->ymax = center->y + radius;
		box->zmax = FP_MAX(p1->z,p2->z);
		lwfree(center);
		return box;
	}

	/* First approximation, bounds of start/end points */
    box->xmin = FP_MIN(p1->x, p3->x);
    box->ymin = FP_MIN(p1->y, p3->y);
    box->zmin = FP_MIN(p1->z, p3->z);
    box->xmax = FP_MAX(p1->x, p3->x);
    box->ymax = FP_MAX(p1->y, p3->y);
    box->zmax = FP_MAX(p1->z, p3->z);

	/* Create points for the possible extrema */
	xmin.x = center->x - radius;
	xmin.y = center->y;
	ymin.x = center->x;
	ymin.y = center->y - radius;
	xmax.x = center->x + radius;
	xmax.y = center->y;
	ymax.x = center->x;
	ymax.y = center->y + radius;
	
	/* Divide the circle into two parts, one on each side of a line
	   joining p1 and p3. The circle extrema on the same side of that line
	   as p2 is on, are also the extrema of the bbox. */
	
	p2_side = signum(lw_segment_side((POINT2D*)p1, (POINT2D*)p3, (POINT2D*)p2));

	if ( p2_side == signum(lw_segment_side((POINT2D*)p1, (POINT2D*)p3, &xmin)) )
		box->xmin = xmin.x;

	if ( p2_side == signum(lw_segment_side((POINT2D*)p1, (POINT2D*)p3, &ymin)) )
		box->ymin = ymin.y;

	if ( p2_side == signum(lw_segment_side((POINT2D*)p1, (POINT2D*)p3, &xmax)) )
		box->xmax = xmax.x;

	if ( p2_side == signum(lw_segment_side((POINT2D*)p1, (POINT2D*)p3, &ymax)) )
		box->ymax = ymax.y;

	lwfree(center);
	return box;
}
Ejemplo n.º 9
0
int 
ptarray_contains_point_partial(const POINTARRAY *pa, const POINT2D *pt, int check_closed, int *winding_number)
{
	int wn = 0;
	int i;
	double side;
	const POINT2D *seg1;
	const POINT2D *seg2;
	double ymin, ymax;

	seg1 = getPoint2d_cp(pa, 0);
	seg2 = getPoint2d_cp(pa, pa->npoints-1);
	if ( check_closed && ! p2d_same(seg1, seg2) )
		lwerror("ptarray_contains_point called on unclosed ring");
	
	for ( i=1; i < pa->npoints; i++ )
	{
		seg2 = getPoint2d_cp(pa, i);
		
		/* Zero length segments are ignored. */
		if ( seg1->x == seg2->x && seg1->y == seg2->y )
		{
			seg1 = seg2;
			continue;
		}
			
		ymin = FP_MIN(seg1->y, seg2->y);
		ymax = FP_MAX(seg1->y, seg2->y);
		
		/* Only test segments in our vertical range */
		if ( pt->y > ymax || pt->y < ymin ) 
		{
			seg1 = seg2;
			continue;
		}

		side = lw_segment_side(seg1, seg2, pt);

		/* 
		* A point on the boundary of a ring is not contained. 
		* WAS: if (fabs(side) < 1e-12), see #852 
		*/
		if ( (side == 0) && lw_pt_in_seg(pt, seg1, seg2) )
		{
			return LW_BOUNDARY;
		}

		/*
		* If the point is to the left of the line, and it's rising,
		* then the line is to the right of the point and
		* circling counter-clockwise, so incremement.
		*/
		if ( (side < 0) && (seg1->y <= pt->y) && (pt->y < seg2->y) )
		{
			wn++;
		}
		
		/*
		* If the point is to the right of the line, and it's falling,
		* then the line is to the right of the point and circling
		* clockwise, so decrement.
		*/
		else if ( (side > 0) && (seg2->y <= pt->y) && (pt->y < seg1->y) )
		{
			wn--;
		}
		
		seg1 = seg2;
	}

	/* Sent out the winding number for calls that are building on this as a primitive */
	if ( winding_number )
		*winding_number = wn;

	/* Outside */
	if (wn == 0)
	{
		return LW_OUTSIDE;
	}
	
	/* Inside */
	return LW_INSIDE;
}
Ejemplo n.º 10
0
/**
** @brief returns the kind of #CG_SEGMENT_INTERSECTION_TYPE  behavior of lineseg 1 (constructed from p1 and p2) and lineseg 2 (constructed from q1 and q2)
**	@param p1 start point of first straight linesegment
**	@param p2 end point of first straight linesegment
**	@param q1 start point of second line segment
**	@param q2 end point of second line segment
**	@return a #CG_SEGMENT_INTERSECTION_TYPE
** 	Returns one of
**		SEG_ERROR = -1,
**		SEG_NO_INTERSECTION = 0,
**		SEG_COLINEAR = 1,
**		SEG_CROSS_LEFT = 2,
**		SEG_CROSS_RIGHT = 3,
*/
int lw_segment_intersects(const POINT2D *p1, const POINT2D *p2, const POINT2D *q1, const POINT2D *q2)
{

    double pq1, pq2, qp1, qp2;

    /* No envelope interaction => we are done. */
    if (!lw_segment_envelope_intersects(p1, p2, q1, p2))
    {
        return SEG_NO_INTERSECTION;
    }

    /* Are the start and end points of q on the same side of p? */
    pq1=lw_segment_side(p1,p2,q1);
    pq2=lw_segment_side(p1,p2,q2);
    if ((pq1>0 && pq2>0) || (pq1<0 && pq2<0))
    {
        return SEG_NO_INTERSECTION;
    }

    /* Are the start and end points of p on the same side of q? */
    qp1=lw_segment_side(q1,q2,p1);
    qp2=lw_segment_side(q1,q2,p2);
    if ( (qp1 > 0.0 && qp2 > 0.0) || (qp1 < 0.0 && qp2 < 0.0) )
    {
        return SEG_NO_INTERSECTION;
    }

    /* Nobody is on one side or another? Must be colinear. */
    if ( pq1 == 0.0 && pq2 == 0.0 && qp1 == 0.0 && qp2 == 0.0 )
    {
        return SEG_COLINEAR;
    }

    /*
    ** When one end-point touches, the sidedness is determined by the
    ** location of the other end-point. Only touches by the first point
    ** will be considered "real" to avoid double counting.
    */
    LWDEBUGF(4, "pq1=%.15g pq2=%.15g", pq1, pq2);
    LWDEBUGF(4, "qp1=%.15g qp2=%.15g", qp1, qp2);

    /* Second point of p or q touches, it's not a crossing. */
    if ( pq2 == 0.0 || qp2 == 0.0 )
    {
        return SEG_NO_INTERSECTION;
    }

    /* First point of p touches, it's a "crossing". */
    if ( pq1 == 0.0 )
    {
        if ( FP_GT(pq2,0.0) )
            return SEG_CROSS_RIGHT;
        else
            return SEG_CROSS_LEFT;
    }

    /* First point of q touches, it's a crossing. */
    if ( qp1 == 0.0 )
    {
        if ( FP_LT(pq1,pq2) )
            return SEG_CROSS_RIGHT;
        else
            return SEG_CROSS_LEFT;
    }

    /* The segments cross, what direction is the crossing? */
    if ( FP_LT(pq1,pq2) )
        return SEG_CROSS_RIGHT;
    else
        return SEG_CROSS_LEFT;

    /* This should never happen! */
    return SEG_ERROR;
}
Ejemplo n.º 11
0
static int lwcircle_calculate_gbox_cartesian(const POINT4D *p1, const POINT4D *p2, const POINT4D *p3, GBOX *gbox)
{
	POINT2D xmin, ymin, xmax, ymax;
	POINT4D center;
	int p2_side;
	double radius;

	LWDEBUG(2, "lwcircle_calculate_gbox called.");

	radius = lwcircle_center(p1, p2, p3, &center);
	
	/* Negative radius signals straight line, p1/p2/p3 are colinear */
	if (radius < 0.0)
	{
        gbox->xmin = FP_MIN(p1->x, p3->x);
        gbox->ymin = FP_MIN(p1->y, p3->y);
        gbox->zmin = FP_MIN(p1->z, p3->z);
        gbox->xmax = FP_MAX(p1->x, p3->x);
        gbox->ymax = FP_MAX(p1->y, p3->y);
        gbox->zmax = FP_MAX(p1->z, p3->z);
	    return LW_SUCCESS;
	}
	
	/* Matched start/end points imply circle */
	if ( p1->x == p3->x && p1->y == p3->y )
	{
		gbox->xmin = center.x - radius;
		gbox->ymin = center.y - radius;
		gbox->zmin = FP_MIN(p1->z,p2->z);
		gbox->mmin = FP_MIN(p1->m,p2->m);
		gbox->xmax = center.x + radius;
		gbox->ymax = center.y + radius;
		gbox->zmax = FP_MAX(p1->z,p2->z);
		gbox->mmax = FP_MAX(p1->m,p2->m);
		return LW_SUCCESS;
	}

	/* First approximation, bounds of start/end points */
    gbox->xmin = FP_MIN(p1->x, p3->x);
    gbox->ymin = FP_MIN(p1->y, p3->y);
    gbox->zmin = FP_MIN(p1->z, p3->z);
    gbox->mmin = FP_MIN(p1->m, p3->m);
    gbox->xmax = FP_MAX(p1->x, p3->x);
    gbox->ymax = FP_MAX(p1->y, p3->y);
    gbox->zmax = FP_MAX(p1->z, p3->z);
    gbox->mmax = FP_MAX(p1->m, p3->m);

	/* Create points for the possible extrema */
	xmin.x = center.x - radius;
	xmin.y = center.y;
	ymin.x = center.x;
	ymin.y = center.y - radius;
	xmax.x = center.x + radius;
	xmax.y = center.y;
	ymax.x = center.x;
	ymax.y = center.y + radius;
	
	
	/* Divide the circle into two parts, one on each side of a line
	   joining p1 and p3. The circle extrema on the same side of that line
	   as p2 is on, are also the extrema of the bbox. */
	
	p2_side = signum(lw_segment_side((POINT2D*)p1, (POINT2D*)p3, (POINT2D*)p2));

	if ( p2_side == signum(lw_segment_side((POINT2D*)p1, (POINT2D*)p3, &xmin)) )
		gbox->xmin = xmin.x;

	if ( p2_side == signum(lw_segment_side((POINT2D*)p1, (POINT2D*)p3, &ymin)) )
		gbox->ymin = ymin.y;

	if ( p2_side == signum(lw_segment_side((POINT2D*)p1, (POINT2D*)p3, &xmax)) )
		gbox->xmax = xmax.x;

	if ( p2_side == signum(lw_segment_side((POINT2D*)p1, (POINT2D*)p3, &ymax)) )
		gbox->ymax = ymax.y;

	return LW_SUCCESS;
}
Ejemplo n.º 12
0
/**
* Returns true if P is on the same side of the plane partition 
* defined by A1/A3 as A2 is. Only makes sense if P has already been
* determined to be on the circle defined by A1/A2/A3.
*/
int
lw_pt_in_arc(const POINT2D *P, const POINT2D *A1, const POINT2D *A2, const POINT2D *A3)
{
	return lw_segment_side(A1, A3, A2) == lw_segment_side(A1, A3, P);
}
Ejemplo n.º 13
0
LWGEOM*
pta_unstroke(const POINTARRAY *points, int type, int srid)
{
	int i = 0, j, k;
	POINT4D a1, a2, a3, b;
	POINT4D first, center;
	char *edges_in_arcs;
	int found_arc = LW_FALSE;
	int current_arc = 1;
	int num_edges;
	int edge_type; /* non-zero if edge is part of an arc */
	int start, end;
	LWCOLLECTION *outcol;
	/* Minimum number of edges, per quadrant, required to define an arc */
	const unsigned int min_quad_edges = 2;

	/* Die on null input */
	if ( ! points )
		lwerror("pta_unstroke called with null pointarray");

	/* Null on empty input? */
	if ( points->npoints == 0 )
		return NULL;

	/* We can't desegmentize anything shorter than four points */
	if ( points->npoints < 4 )
	{
		/* Return a linestring here*/
		lwerror("pta_unstroke needs implementation for npoints < 4");
	}

	/* Allocate our result array of vertices that are part of arcs */
	num_edges = points->npoints - 1;
	edges_in_arcs = lwalloc(num_edges + 1);
	memset(edges_in_arcs, 0, num_edges + 1);

	/* We make a candidate arc of the first two edges, */
	/* And then see if the next edge follows it */
	while( i < num_edges-2 )
	{
		unsigned int arc_edges;
		double num_quadrants;
		double angle;

		found_arc = LW_FALSE;
		/* Make candidate arc */
		getPoint4d_p(points, i  , &a1);
		getPoint4d_p(points, i+1, &a2);
		getPoint4d_p(points, i+2, &a3);
		memcpy(&first, &a1, sizeof(POINT4D));

		for( j = i+3; j < num_edges+1; j++ )
		{
			LWDEBUGF(4, "i=%d, j=%d", i, j);
			getPoint4d_p(points, j, &b);
			/* Does this point fall on our candidate arc? */
			if ( pt_continues_arc(&a1, &a2, &a3, &b) )
			{
				/* Yes. Mark this edge and the two preceding it as arc components */
				LWDEBUGF(4, "pt_continues_arc #%d", current_arc);
				found_arc = LW_TRUE;
				for ( k = j-1; k > j-4; k-- )
					edges_in_arcs[k] = current_arc;
			}
			else
			{
				/* No. So we're done with this candidate arc */
				LWDEBUG(4, "pt_continues_arc = false");
				current_arc++;
				break;
			}

			memcpy(&a1, &a2, sizeof(POINT4D));
			memcpy(&a2, &a3, sizeof(POINT4D));
			memcpy(&a3,  &b, sizeof(POINT4D));
		}
		/* Jump past all the edges that were added to the arc */
		if ( found_arc )
		{
			/* Check if an arc was composed by enough edges to be
			 * really considered an arc
			 * See http://trac.osgeo.org/postgis/ticket/2420
			 */
			arc_edges = j - 1 - i;
			LWDEBUGF(4, "arc defined by %d edges found", arc_edges);
			if ( first.x == b.x && first.y == b.y ) {
				LWDEBUG(4, "arc is a circle");
				num_quadrants = 4;
			}
			else {
				lw_arc_center((POINT2D*)&first, (POINT2D*)&b, (POINT2D*)&a1, (POINT2D*)&center);
				angle = lw_arc_angle((POINT2D*)&first, (POINT2D*)&center, (POINT2D*)&b);
        int p2_side = lw_segment_side((POINT2D*)&first, (POINT2D*)&a1, (POINT2D*)&b);
        if ( p2_side >= 0 ) angle = -angle;

				if ( angle < 0 ) angle = 2 * M_PI + angle;
				num_quadrants = ( 4 * angle ) / ( 2 * M_PI );
				LWDEBUGF(4, "arc angle (%g %g, %g %g, %g %g) is %g (side is %d), quandrants:%g", first.x, first.y, center.x, center.y, b.x, b.y, angle, p2_side, num_quadrants);
			}
			/* a1 is first point, b is last point */
			if ( arc_edges < min_quad_edges * num_quadrants ) {
				LWDEBUGF(4, "Not enough edges for a %g quadrants arc, %g needed", num_quadrants, min_quad_edges * num_quadrants);
				for ( k = j-1; k >= i; k-- )
					edges_in_arcs[k] = 0;
			}

			i = j-1;
		}
		else
		{
			/* Mark this edge as a linear edge */
			edges_in_arcs[i] = 0;
			i = i+1;
		}
	}

#if POSTGIS_DEBUG_LEVEL > 3
	{
		char *edgestr = lwalloc(num_edges+1);
		for ( i = 0; i < num_edges; i++ )
		{
			if ( edges_in_arcs[i] )
				edgestr[i] = 48 + edges_in_arcs[i];
			else
				edgestr[i] = '.';
		}
		edgestr[num_edges] = 0;
		LWDEBUGF(3, "edge pattern %s", edgestr);
		lwfree(edgestr);
	}
#endif

	start = 0;
	edge_type = edges_in_arcs[0];
	outcol = lwcollection_construct_empty(COMPOUNDTYPE, srid, ptarray_has_z(points), ptarray_has_m(points));
	for( i = 1; i < num_edges; i++ )
	{
		if( edge_type != edges_in_arcs[i] )
		{
			end = i - 1;
			lwcollection_add_lwgeom(outcol, geom_from_pa(points, srid, edge_type, start, end));
			start = i;
			edge_type = edges_in_arcs[i];
		}
	}
	lwfree(edges_in_arcs); /* not needed anymore */

	/* Roll out last item */
	end = num_edges - 1;
	lwcollection_add_lwgeom(outcol, geom_from_pa(points, srid, edge_type, start, end));

	/* Strip down to singleton if only one entry */
	if ( outcol->ngeoms == 1 )
	{
		LWGEOM *outgeom = outcol->geoms[0];
		outcol->ngeoms = 0; lwcollection_free(outcol);
		return outgeom;
	}
	return lwcollection_as_lwgeom(outcol);
}
Ejemplo n.º 14
0
/**
 * Segmentize an arc
 *
 * Does not add the final vertex
 *
 * @param to POINTARRAY to append segmentized vertices to
 * @param p1 first point defining the arc
 * @param p2 second point defining the arc
 * @param p3 third point defining the arc
 * @param tol tolerance, semantic driven by tolerance_type
 * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE
 * @param flags LW_LINEARIZE_FLAGS
 *
 * @return number of points appended (0 if collinear),
 *         or -1 on error (lwerror would be called).
 *
 */
static int
lwarc_linearize(POINTARRAY *to,
                 const POINT4D *p1, const POINT4D *p2, const POINT4D *p3,
                 double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type,
                 int flags)
{
	POINT2D center;
	POINT2D *t1 = (POINT2D*)p1;
	POINT2D *t2 = (POINT2D*)p2;
	POINT2D *t3 = (POINT2D*)p3;
	POINT4D pt;
	int p2_side = 0;
	int clockwise = LW_TRUE;
	double radius; /* Arc radius */
	double increment; /* Angle per segment */
	double angle_shift = 0;
	double a1, a2, a3, angle;
	POINTARRAY *pa = to;
	int is_circle = LW_FALSE;
	int points_added = 0;
	int reverse = 0;

	LWDEBUG(2, "lwarc_linearize called.");

	p2_side = lw_segment_side(t1, t3, t2);

	/* Force counterclockwise scan if SYMMETRIC operation is requsested */
	if ( p2_side == -1 && flags & LW_LINEARIZE_FLAG_SYMMETRIC )
	{
		/* swap p1-p3 */
		t1 = (POINT2D*)p3;
		t3 = (POINT2D*)p1;
		p1 = (POINT4D*)t1;
		p3 = (POINT4D*)t3;
		p2_side = 1;
		reverse = 1;
	}

	radius = lw_arc_center(t1, t2, t3, &center);
	LWDEBUGF(2, " center is POINT(%.15g %.15g) - radius:%g", center.x, center.y, radius);

	/* Matched start/end points imply circle */
	if ( p1->x == p3->x && p1->y == p3->y )
		is_circle = LW_TRUE;

	/* Negative radius signals straight line, p1/p2/p3 are colinear */
	if ( (radius < 0.0 || p2_side == 0) && ! is_circle )
	    return 0;

	/* The side of the p1/p3 line that p2 falls on dictates the sweep
	   direction from p1 to p3. */
	if ( p2_side == -1 )
		clockwise = LW_TRUE;
	else
		clockwise = LW_FALSE;

	if ( tolerance_type == LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD )
	{{
		int perQuad = rint(tol);
		// error out if tol != perQuad ? (not-round)
		if ( perQuad != tol )
		{
			lwerror("lwarc_linearize: segments per quadrant must be an integer value, got %.15g", tol, perQuad);
			return -1;
		}
		if ( perQuad < 1 )
		{
			lwerror("lwarc_linearize: segments per quadrant must be at least 1, got %d", perQuad);
			return -1;
		}
		increment = fabs(M_PI_2 / perQuad);
		LWDEBUGF(2, "lwarc_linearize: perQuad:%d, increment:%g (%g degrees)", perQuad, increment, increment*180/M_PI);

	}}
	else if ( tolerance_type == LW_LINEARIZE_TOLERANCE_TYPE_MAX_DEVIATION )
	{{
		double halfAngle;
		if ( tol <= 0 )
		{
			lwerror("lwarc_linearize: max deviation must be bigger than 0, got %.15g", tol);
			return -1;
		}
		halfAngle = acos( -tol / radius + 1 );
		increment = 2 * halfAngle;
		LWDEBUGF(2, "lwarc_linearize: maxDiff:%g, radius:%g, halfAngle:%g, increment:%g (%g degrees)", tol, radius, halfAngle, increment, increment*180/M_PI);
	}}
	else if ( tolerance_type == LW_LINEARIZE_TOLERANCE_TYPE_MAX_ANGLE )
	{
		increment = tol;
		if ( increment <= 0 )
		{
			lwerror("lwarc_linearize: max angle must be bigger than 0, got %.15g", tol);
			return -1;
		}
	}
	else
	{
		lwerror("lwarc_linearize: unsupported tolerance type %d", tolerance_type);
		return LW_FALSE;
	}

	/* Angles of each point that defines the arc section */
	a1 = atan2(p1->y - center.y, p1->x - center.x);
	a2 = atan2(p2->y - center.y, p2->x - center.x);
	a3 = atan2(p3->y - center.y, p3->x - center.x);

	LWDEBUGF(2, "lwarc_linearize A1:%g (%g) A2:%g (%g) A3:%g (%g)",
		a1, a1*180/M_PI, a2, a2*180/M_PI, a3, a3*180/M_PI);

	if ( flags & LW_LINEARIZE_FLAG_SYMMETRIC )
	{{
		/* Calculate total arc angle, in radians */
		double angle = clockwise ? a1 - a3 : a3 - a1;
		if ( angle < 0 ) angle += M_PI * 2;
		LWDEBUGF(2, "lwarc_linearize SYMMETRIC requested - total angle %g deg",
			         angle * 180 / M_PI);
		if ( flags & LW_LINEARIZE_FLAG_RETAIN_ANGLE )
		{{
			/* Number of steps */
			int steps = trunc(angle / increment);
			/* Angle reminder */
			double angle_reminder = angle - ( increment * steps );
			angle_shift = angle_reminder / 2.0;

			LWDEBUGF(2, "lwarc_linearize RETAIN_ANGLE operation requested - "
			         "total angle %g, steps %d, increment %g, reminder %g",
			         angle * 180 / M_PI, steps, increment * 180 / M_PI,
			         angle_reminder * 180 / M_PI);
		}}
		else
		{{
			/* Number of segments in output */
			int segs = ceil(angle / increment);
			/* Tweak increment to be regular for all the arc */
			increment = angle/segs;

			LWDEBUGF(2, "lwarc_linearize SYMMETRIC operation requested - "
							"total angle %g degrees - LINESTRING(%g %g,%g %g,%g %g) - S:%d -   I:%g",
							angle*180/M_PI, p1->x, p1->y, center.x, center.y, p3->x, p3->y,
							segs, increment*180/M_PI);
		}}
	}}

	/* p2 on left side => clockwise sweep */
	if ( clockwise )
	{
		LWDEBUG(2, " Clockwise sweep");
		increment *= -1;
		angle_shift *= -1;
		/* Adjust a3 down so we can decrement from a1 to a3 cleanly */
		if ( a3 > a1 )
			a3 -= 2.0 * M_PI;
		if ( a2 > a1 )
			a2 -= 2.0 * M_PI;
	}
	/* p2 on right side => counter-clockwise sweep */
	else
	{
		LWDEBUG(2, " Counterclockwise sweep");
		/* Adjust a3 up so we can increment from a1 to a3 cleanly */
		if ( a3 < a1 )
			a3 += 2.0 * M_PI;
		if ( a2 < a1 )
			a2 += 2.0 * M_PI;
	}

	/* Override angles for circle case */
	if( is_circle )
	{
		a3 = a1 + 2.0 * M_PI;
		a2 = a1 + M_PI;
		increment = fabs(increment);
		clockwise = LW_FALSE;
	}

	LWDEBUGF(2, "lwarc_linearize angle_shift:%g, increment:%g",
		angle_shift * 180/M_PI, increment * 180/M_PI);

	if ( reverse ) {{
		const int capacity = 8; /* TODO: compute exactly ? */
		pa = ptarray_construct_empty(ptarray_has_z(to), ptarray_has_m(to), capacity);
	}}

	/* Sweep from a1 to a3 */
	if ( ! reverse )
	{
		ptarray_append_point(pa, p1, LW_FALSE);
	}
	++points_added;
	if ( angle_shift ) angle_shift -= increment;
	LWDEBUGF(2, "a1:%g (%g deg), a3:%g (%g deg), inc:%g, shi:%g, cw:%d",
		a1, a1 * 180 / M_PI, a3, a3 * 180 / M_PI, increment, angle_shift, clockwise);
	for ( angle = a1 + increment + angle_shift; clockwise ? angle > a3 : angle < a3; angle += increment )
	{
		LWDEBUGF(2, " SA: %g ( %g deg )", angle, angle*180/M_PI);
		pt.x = center.x + radius * cos(angle);
		pt.y = center.y + radius * sin(angle);
		pt.z = interpolate_arc(angle, a1, a2, a3, p1->z, p2->z, p3->z);
		pt.m = interpolate_arc(angle, a1, a2, a3, p1->m, p2->m, p3->m);
		ptarray_append_point(pa, &pt, LW_FALSE);
		++points_added;
		angle_shift = 0;
	}

	if ( reverse ) {{
		int i;
		ptarray_append_point(to, p3, LW_FALSE);
		for ( i=pa->npoints; i>0; i-- ) {
			getPoint4d_p(pa, i-1, &pt);
			ptarray_append_point(to, &pt, LW_FALSE);
		}
		ptarray_free(pa);
	}}

	return points_added;
}