Пример #1
0
void Quad_ResizeSide4(Point2 corners[4], int side, const Vector2& move, bool symetric)
{
	Point2 ends1 = Segment2(corners[0],corners[3]).midpoint(0.5);
	Point2 ends2 = Segment2(corners[1],corners[2]).midpoint(0.5);

	Point2 side1 = Segment2(corners[0],corners[1]).midpoint(0.5);
	Point2 side2 = Segment2(corners[2],corners[3]).midpoint(0.5);

	Vector2	dir;
	switch(side) {
	case 0: dir = Vector2(side2,side1);	break;
	case 1: dir = Vector2(ends1,ends2);	break;
	case 2: dir = Vector2(side1,side2);	break;
	case 3: dir = Vector2(ends2,ends1);	break;
	}
	dir.normalize();
	Vector2	real_move = dir.projection(move);

	corners[side] += real_move;
	corners[(side+1)%4] += real_move;

	if (symetric)
	{
		corners[(side+2)%4] -= real_move;
		corners[(side+3)%4] -= real_move;
	}

}
Пример #2
0
void	Quad_4to2(const Point2 corners[4], Point2 ends[2], double& width_mtr)
{
	ends[0] = Segment2(corners[0],corners[3]).midpoint(0.5);
	ends[1] = Segment2(corners[1],corners[2]).midpoint(0.5);

	Point2 side1 = Segment2(corners[0],corners[1]).midpoint(0.5);
	Point2 side2 = Segment2(corners[2],corners[3]).midpoint(0.5);

	width_mtr = sqrt(VectorLLToMeters(Segment2(side1,side2).midpoint(),Vector2(side1,side2)).squared_length());
}
Пример #3
0
void	Quad_4to1(const Point2 corners[4], Point2& ctr, double& heading, double& len_mtr, double& width_mtr)
{
	Point2 ends1 = Segment2(corners[0],corners[3]).midpoint(0.5);
	Point2 ends2 = Segment2(corners[1],corners[2]).midpoint(0.5);

	Point2 side1 = Segment2(corners[0],corners[1]).midpoint(0.5);
	Point2 side2 = Segment2(corners[2],corners[3]).midpoint(0.5);

	ctr.x_ = (corners[0].x()  + corners[1].x()  + corners[2].x() + corners[3].x()) * 0.25;
	ctr.y_ = (corners[0].y()  + corners[1].y()  + corners[2].y() + corners[3].y()) * 0.25;

	heading = VectorDegs2NorthHeading(ctr,ends1,Vector2(ends1,ends2));
	width_mtr = sqrt(VectorLLToMeters(ctr,Vector2(side1, side2)).squared_length());
	len_mtr = sqrt(VectorLLToMeters(ctr,Vector2(ends1, ends2)).squared_length());

}
Пример #4
0
bool ConvexHull2::checkEdgeIntersection(const Segment2 &seg) const
{
	if ( vertices.size() < 2 )
	{
		return false;
	}
	else if ( vertices.size() == 2 )
	{
		return Segment2( vertices.front(), vertices.back() ).intersects( seg );
	}
	else
	{
		int iPrev = vertices.size() - 1;
		for (int i = 0; i < vertices.size(); i++)
		{
			Segment2 edge( vertices[iPrev], vertices[i] );

			if ( edge.intersects( seg ) )
			{
				return true;
			}

			iPrev = i;
		}

		return false;
	}
}
Пример #5
0
double ConvexHull2::sqrDistanceTo(const Point2 &point) const
{
	if ( contains( point ) )
	{
		return 0.0;
	}
	else
	{
		double sqrDist = Segment2( vertices.back(), vertices.front() ).sqrDistanceTo( point );

		for (int i = 1; i < vertices.size(); i++)
		{
			double d2 = Segment2( vertices[i-1], vertices[i] ).sqrDistanceTo( point );
			sqrDist = std::min( sqrDist, d2 );
		}

		return sqrDist;
	}
}
// Find Coarse Edges
list<CVertex> CMeshCreation::InitPolygon(list<CVertex> pList)
{
	mEdgeLength = 0.05f;
	CVertex st = *pList.begin();
	dt.insert(Point2(st.mPos[0], st.mPos[1]));

	CVertex ed;

	m2DMesh.mVertexList.push_back(st);

	for (list<CVertex>::iterator it = pList.begin(); it != pList.end(); it++)
	{
		ed = *it;
		float len = (ed.mPos[0] - st.mPos[0])*(ed.mPos[0] - st.mPos[0]) + (ed.mPos[1] - st.mPos[1])*(ed.mPos[1] - st.mPos[1]);

		if (len > mEdgeLength*mEdgeLength)
		{
			m2DMesh.mVertexList.push_back(CVertex(ed));
			m2DMesh.mEdgeList.push_back(CEdge(m2DMesh.mVertexList.size() - 1, m2DMesh.mVertexList.size()));

			Point2 p0(st.mPos[0], st.mPos[1]);
			Point2 p1(ed.mPos[0], ed.mPos[1]);

			dt.insert(p1);
			vSegments.push_back(Segment2(p0, p1));

			st = ed;
		}
	}

	// st + ed
	st = *pList.begin();
	ed = m2DMesh.mVertexList.back();
	m2DMesh.mVertexList.push_back(st);
	
	Point2 p0(st.mPos[0], st.mPos[1]);
	Point2 p1(ed.mPos[0], ed.mPos[1]);
	//dt.insert(p0);
	vSegments.push_back(Segment2(p1, p0));

	return m2DMesh.mVertexList;
}
void customVisualization(Zone2* pZone, const std::string& name)
{
	// Create a Visualizer2 object with the output filename "example5.ps"
	Visualizer2 vis(name);
#if 1
	// Extract the triangles of the zone
	std::vector<Triangle2*> vTriangles;
	pZone->getTriangles(vTriangles);
	std::cout << "The zone consists of " << vTriangles.size() << " triangles" << std::endl;
	// Draw the zone triangles in red
	for (std::vector<Triangle2*>::iterator it(vTriangles.begin()); it != vTriangles.end(); ++it)
	{
		vis.addObject(**it, Color(1, 0, 0, 0.01, true)); // true means it is the fill color
	}
	// Draw the borders of the zone triangles
	for (std::vector<Triangle2*>::iterator it(vTriangles.begin()); it != vTriangles.end(); ++it)
	{
		vis.addObject(**it, Color(0, 0, 0, 0.01, false));
	}
#endif
#if 0
	// Retrieve the triangles of the triangulation. Draw them in black
	std::vector<Triangle2*> vAllDelaunayTriangles;
	dt.getTrianglePointers(vAllDelaunayTriangles);
	for (std::vector<Triangle2*>::iterator it = vAllDelaunayTriangles.begin(); it != vAllDelaunayTriangles.end();
		++it)
	{
		Triangle2* t(*it);
		Point2* p0 = t->getCorner(0);
		Point2* p1 = t->getCorner(1);
		Point2* p2 = t->getCorner(2);
		vis.addObject(Segment2(*p0, *p1), Color(1, 0, 0, 0.01));
		vis.addObject(Segment2(*p1, *p2), Color(1, 0, 0, 0.01));
		vis.addObject(Segment2(*p2, *p0), Color(1, 0, 0, 0.01));
	}
#endif
	// Finally, write the postscript file to disk!
	vis.writeFile();
}
Пример #8
0
bool ConvexHull2::intersects(const BBox2 &box) const
{
	int vertI = vertices.size() - 1;
	for (int vertJ = 0; vertJ < vertices.size(); vertJ++)
	{
		if ( box.contains( vertices[vertI] )   ||   box.intersects( Segment2( vertices[vertI], vertices[vertJ] ) ) )
		{
			return true;
		}

		vertI = vertJ;
	}


	return false;
}
Пример #9
0
bool	build_convex_polygon(
				Pmwx::Ccb_halfedge_circulator									ccb,
				vector<pair<Pmwx::Halfedge_handle, Pmwx::Halfedge_handle> >&	sides,
				const CoordTranslator2&											trans,
				Polygon2&														metric_bounds,
				double															max_err_mtrs,
				double															min_side_len)
{
	double	e_sq = max_err_mtrs*max_err_mtrs;
	sides.clear();
	metric_bounds.clear();
	
	Pmwx::Ccb_halfedge_circulator circ(ccb);
//	Bbox2				bounds;
//	
//	do {
//		bounds += cgal2ben(circ->source()->point());
//	} while (++circ != ccb);

	Pmwx::Ccb_halfedge_circulator start,next;
	start = ccb;
	do {
		--start;
		if(!sides_can_merge(start,ccb))
			break;
		if(!within_err_metric(start,ccb,trans,e_sq))
			break;		
	} while(start != ccb);
	++start;
	
	// now we can go around.

	circ = start;
	//int ne = count_circulator(start);
	//printf("Poly has %d sides.\n", ne);
	do {
	 
		Pmwx::Ccb_halfedge_circulator stop(circ);
		do {
			++stop;
		} while(sides_can_merge(circ,stop) && within_err_metric(circ,stop,trans,e_sq) && stop != start);
	
		--stop;
		//printf("Pushing side of %d, %d\n", circulator_distance_to(start, circ),circulator_distance_to(start,stop));
		sides.push_back(pair<Pmwx::Halfedge_handle,Pmwx::Halfedge_handle>(circ, stop));
		++stop;
		circ = stop;
	
	} while(circ != start);
	
	if(sides.size() < 3)	
	{
		//debug_mesh_point(bounds.centroid(),1,1,1);
		return false;
	}
	
	int i, j, k;
	
	vector<Segment2>	msides;
	for(i = 0; i < sides.size(); ++i)
	{
		j = (i + 1) % sides.size();		
		DebugAssert(sides[i].second->target() == sides[j].first->source());
		msides.push_back(Segment2(
						trans.Forward(cgal2ben(sides[i].first->source()->point())),
						trans.Forward(cgal2ben(sides[i].second->target()->point()))));						
	}	
	vector<Segment2>	debug(msides);
	for(i = 0; i < sides.size(); ++i)
	{
		j = (i + 1) % sides.size();		
		Vector2	v1(msides[i].p1,msides[i].p2);
		Vector2	v2(msides[j].p1,msides[j].p2);
		v1.normalize();
		v2.normalize();
		if(v1.dot(v2) > 0.9998 ||
			!v1.left_turn(v2))
		{
			//debug_mesh_point(trans.Reverse(msides[i].p2),1,0,0);
			return false;
		}
		double w = width_for_he(sides[i].first);
		if(w)
		{
			v1 = v1.perpendicular_ccw();
			v1 *= w;
			msides[i].p1 += v1;
			msides[i].p2 += v1;
		}
	}
	for(j = 0; j < sides.size(); ++j)
	{
		i = (j + sides.size() - 1) % sides.size();
		Line2 li(msides[i]), lj(msides[j]);
		Point2	p;
		if(!li.intersect(lj,p))
		{
			Assert(!"Failure to intersect.\n");
			return false;
		}
		metric_bounds.push_back(p);
	}
	
	for(i = 0; i < metric_bounds.size(); ++i)
	{
		j = (i + 1) % metric_bounds.size();
		k = (i + 2) % metric_bounds.size();
		if(metric_bounds.side(i).squared_length() < (min_side_len*min_side_len))
		{
			//debug_mesh_line(trans.Reverse(metric_bounds.side(i).p1),trans.Reverse(metric_bounds.side(i).p2),1,1,0,1,1,0);
			return false;
		}
		if(!left_turn(metric_bounds[i],metric_bounds[j],metric_bounds[k]))
		{
			//debug_mesh_point(trans.Reverse(metric_bounds[j]),1,1,0);
			return false;
		}
		if(Vector2(msides[i].p1,msides[i].p2).dot(Vector2(metric_bounds[i],metric_bounds[j])) < 0.0)
		{
			//debug_mesh_line(trans.Reverse(msides[i].p1),trans.Reverse(msides[i].p2),1,0,0,1,0,0);
			return false;
		}
	}
	DebugAssert(metric_bounds.size() == msides.size());
	DebugAssert(msides.size() == sides.size());
		
	return true;
}
Пример #10
0
// This routine tries to find the 'grain' of a block by a weighting of dot 
// products relative to all sides.  A few notes:
// 1. It tries to first run wiht a handy-cap to take only street-level roads.
//    It then tries again if the filter is too harsh.
// 2. It does an intentionally bad job when there are a huge number of sides
//    since such high-count polygons aren't sane AGB candidates.
// 3. It ALWAYS returns some answer, no matter how goofy the polygon.
void find_major_axis(vector<block_pt>&	pts,
				Segment2 *			out_segment,
				Vector2 *			out_major,
				Vector2 *			out_minor,
				double *			bounds)
{
	double best_v = -1.0;
	Vector2	temp_a, temp_b;
	double bounds_temp[4];
	
	if(out_major == NULL) out_major = &temp_a;
	if(out_minor == NULL) out_minor = &temp_b;
	if(bounds == NULL) bounds = bounds_temp;

	bool elev_ok = false;
		
	for(int tries = 0; tries < 2; ++tries)
	{	
		
		for(int i = 0; i < pts.size(); ++i)
		if(elev_ok || ground_road_access_for_he(pts[i].orig))
		{
			int j = (i + 1) % pts.size();
			Vector2	v_a(pts[i].loc,pts[j].loc);
			v_a.normalize();
			Vector2 v_b(v_a.perpendicular_ccw());
			
			double total = 0.0;
			int max_k = min(pts.size(),PTS_LIM);
			for(int k = 0; k < max_k; ++k)
			{
				int l = (k + 1) % pts.size();
				Vector2	s(pts[k].loc,pts[l].loc);
				
				total += max(fabs(v_a.dot(s)),fabs(v_b.dot(s)));			
			}
			
			if(total >= best_v)
			{
				best_v = total;
				if(out_segment) *out_segment = Segment2(pts[i].loc,pts[j].loc);
				*out_major = v_a;
				*out_minor = v_b;			
			}	
		}
		
		if(best_v != -1)
			break;
		elev_ok = true;
	}
	
	
	{
		int longest = -1;
		double corr_len = -1;
		for(int i = 0; i < pts.size(); ++i)
		if(/*elev_ok ||*/ ground_road_access_for_he(pts[i].orig))
		{
			int j = (i + 1) % pts.size();
			Vector2 this_side(pts[i].loc,pts[j].loc);
			double len = this_side.normalize();
			double my_corr = fltmax2(fabs(this_side.dot(*out_major)), fabs(this_side.dot(*out_minor)));
			if(my_corr > 0.996194698091746)
			{
				my_corr *= len;
				if(my_corr > corr_len)
				{
					longest = i;
					corr_len = my_corr;
				}			
			}
		}
		if(longest >= 0)
		{
			int i = longest;
			int j = (longest + 1) % pts.size();
			Vector2	v_a(pts[i].loc,pts[j].loc);
			v_a.normalize();
			Vector2 v_b(v_a.perpendicular_ccw());

			if(out_segment) *out_segment = Segment2(pts[i].loc,pts[j].loc);
			*out_major = v_a;
			*out_minor = v_b;			
		}
	}
	
	
	
	
	bounds[2] = bounds[0] = out_major->dot(Vector2(pts[0].loc));
	bounds[3] = bounds[1] = out_minor->dot(Vector2(pts[0].loc));
	for(int n = 1; n < pts.size(); ++n)
	{
		double ca = out_major->dot(Vector2(pts[n].loc));
		double cb = out_minor->dot(Vector2(pts[n].loc));
		bounds[0] = min(bounds[0],ca);
		bounds[1] = min(bounds[1],cb);
		bounds[2] = max(bounds[2],ca);
		bounds[3] = max(bounds[3],cb);
	}
	
//	if(fabs(bounds[3] - bounds[1]) > fabs(bounds[2] - bounds[0]))
//	{
//		*out_major = out_major->perpendicular_ccw();
//		*out_minor = out_minor->perpendicular_ccw();
//
//		bounds[2] = bounds[0] = out_major->dot(Vector2(pts[0].loc));
//		bounds[3] = bounds[1] = out_minor->dot(Vector2(pts[0].loc));
//		for(int n = 1; n < pts.size(); ++n)
//		{
//			double ca = out_major->dot(Vector2(pts[n].loc));
//			double cb = out_minor->dot(Vector2(pts[n].loc));
//			bounds[0] = min(bounds[0],ca);
//			bounds[1] = min(bounds[1],cb);
//			bounds[2] = max(bounds[2],ca);
//			bounds[3] = max(bounds[3],cb);
//		}
//
//	}
}
vector<vector<Point> > Resampling(vector<Point>* input_contour, vector<Point>* refer_point)
{
    int i,j; int size = (*input_contour).size(); Point temp;
    
    Point Head = (*refer_point)[0]; Point Left_foot = (*refer_point)[1]; Point Right_foot = (*refer_point)[2];
    int Head_num=0,Right_foot_num=0,Left_foot_num=0;
    
    vector<Point> Head_start_contour(size);
    
    int Seg1_length = 0;            int Seg2_length = 0;           int Seg3_length = 0;
    int Seg1_sample_point=15;   int Seg2_sample_point=10;  int Seg3_sample_point=15;
    
    for(i=0;i<size;i++)
    {
        temp = (*input_contour)[i];
        if(temp==Head){Head_num = i;}
        else if(temp==Right_foot){Right_foot_num=i;}
        else if(temp==Left_foot){Left_foot_num=i;}
        else{;};
    }
    
    if(Head_num > Right_foot_num)
    {
        Seg2_length = Right_foot_num - Left_foot_num;
        Seg3_length = Head_num - Right_foot_num;
        for(j=Head_num;j<size;j++){Head_start_contour[j-Head_num] = (*input_contour)[j];}
        for(j=0;j<Head_num;j++){Head_start_contour[j+(size-Head_num)] = (*input_contour)[j];}
        Right_foot_num = Right_foot_num + (size - Head_num);
        Left_foot_num = Left_foot_num + (size - Head_num);
        Head_num = 0;
        Seg1_length = Left_foot_num;
    }
    else if(Head_num < Right_foot_num)
    {
        Seg1_length = Left_foot_num - Head_num;
        Seg2_length = Right_foot_num - Left_foot_num;
        for(j=Head_num;j<size;j++){Head_start_contour[j-Head_num] = (*input_contour)[j];}
        for(j=0;j<Head_num;j++){Head_start_contour[j+(size-Head_num)] = (*input_contour)[j];}
        Right_foot_num = Right_foot_num - Head_num;
        Left_foot_num = Left_foot_num - Head_num;
        Head_num = 0;
        Seg3_length = size - Right_foot_num;
    }
    
    double Seg1_inter_length = ((double)Seg1_length/(double)Seg1_sample_point);
    double Seg2_inter_length = ((double)Seg2_length/(double)Seg2_sample_point);
    double Seg3_inter_length = ((double)Seg3_length/(double)Seg3_sample_point);
    
    int Whole_point_num = Seg1_sample_point + Seg2_sample_point + Seg3_sample_point;
    int index1=0,index2=0,index3=0;
    
    vector<Point> Segment1(Seg1_sample_point); vector<Point> Segment2(Seg2_sample_point); vector<Point> Segment3(Seg3_sample_point);
    vector<vector<Point> > result;
    
    int k;
    
    for(k=0;k<Seg1_sample_point;k++)
    {
        index1 = (int)k*Seg1_inter_length;
        Segment1[k] = Head_start_contour[index1];
        temp = Segment1[k];
        //cout << index1 << ":" << temp << endl;
    }
    for(k=0;k<Seg2_sample_point;k++)
    {
        index2 = Seg1_length + (int)k*Seg2_inter_length;
        Segment2[k] = Head_start_contour[index2];
        temp = Segment2[k];
        //cout << index2 << ":" << temp << endl;
    }
    for(k=0;k<Seg3_sample_point;k++)
    {
        index3 = (Seg1_length+Seg2_length) + (int)k*Seg3_inter_length;
        Segment3[k] = Head_start_contour[index3];
        temp = Segment3[k];
        //cout << index3 << ":" << temp << endl;
    }
    
    result.push_back(Segment1);    result.push_back(Segment2);    result.push_back(Segment3);
    
    return result;
}
Пример #12
0
        {}

        Point2<T> const& operator[](int i) const {
            return reinterpret_cast<Point2<T> const*>(this)[i];
        }

        Point2<T>& operator[](int i) {
            return reinterpret_cast<Point2<T>*>(this)[i];
        }


    };

    template <typename T>
    Segment2<T> const Segment2<T>::NOTHING = Segment2(
        Point2<T>(+inf(), +inf()),  
        Point2<T>(-inf(), -inf())
    );

    template <typename T>
    Segment2<T> const Segment2<T>::EVERYTHING = Segment2(
        Point2<T>(-inf(), -inf()),
        Point2<T>(+inf(), +inf())
    );

    typedef Segment2<int> Segment2i;
    typedef Segment2<float> Segment2f;

    template <typename T>
    Point2<T> center(Segment2<T> const& segment) {
        return Point2<T>(
            (segment.min.x + segment.max.x) / 2.0f,
Пример #13
0
// Determine the 'side' of @point
Side ConvexHull2::side(const Point2 &point) const
{
	if ( vertices.size() == 0 )
	{
		return SIDE_NEGATIVE;
	}
	else if ( vertices.size() == 1 )
	{
		return point == vertices.front()  ?  SIDE_ON  :  SIDE_NEGATIVE;
	}
	else if ( vertices.size() == 2 )
	{
		return Segment2( vertices[0], vertices[1] ).on( point )  ?  SIDE_ON  :  SIDE_NEGATIVE;
	}
	else
	{
		int rightCrossings = 0;
		int leftCrossings = 0;

		int iPrev = vertices.size() - 1;
		for (int i = 0; i < vertices.size(); i++)
		{
			//@point is inside if it lies on a vertex
			if ( point == vertices[i] )
			{
				return SIDE_ON;
			}

			//check if the edge at position @iPrev straddles the horizontal axis
			//that passes through @p
			bool rightStraddle = ( vertices[i].y > point.y )  !=  ( vertices[iPrev].y > point.y );
			bool leftStraddle = ( vertices[i].y < point.y )  !=  ( vertices[iPrev].y < point.y );

			if ( rightStraddle || leftStraddle )
			{
				double areax2 = Point2::areaOfTrianglex2( vertices[iPrev], vertices[i], point );

				bool edgePointsUp = vertices[i].y > vertices[iPrev].y;

				bool pOnLeft = edgePointsUp  ?  areax2 > 0.0  :  areax2 < 0.0;
				bool pOnRight = edgePointsUp  ?  areax2 < 0.0  :  areax2 > 0.0;

				if ( rightStraddle  &&  pOnLeft )
				{
					//right straddle AND @p on left of the edge; intersection between
					//the edge and the horizontal axis passing through @p lies
					//to the right of @p
					rightCrossings++;
				}
				if ( leftStraddle  &&  pOnRight )
				{
					//left straddle AND @p on right of the edge; intesection between
					//the edge and the horizontal axis passing through @o lies
					//to the left of @p
					leftCrossings++;
				}
			}

			iPrev = i;
		}


		//@p is on an edge if left and right cross counts do not have the
		//same parity
		if ( ( rightCrossings % 2 )  !=  ( leftCrossings %2 ) )
		{
			//@p is inside the boundary
			return SIDE_ON;
		}

		//@p is inside if the number of crossings is odd
		if ( ( rightCrossings % 2 )  ==  1 )
		{
			return SIDE_POSITIVE;
		}
		else
		{
			return SIDE_NEGATIVE;
		}
	}
}
Пример #14
0
void ConvexHull2::addPoint(const Point2 &p)
{
	if ( vertices.size() < 2 )
	{
		if ( !vertices.contains( p ) )
		{
			vertices.push_back( p );
		}
	}
	else if ( vertices.size() == 2 )
	{
		Segment2 seg( vertices[0], vertices[1] );
		if ( seg.onLeft( p ) )
		{
			vertices.push_back( p );
		}
		else if ( seg.onRight( p ) )
		{
			vertices.insert( 1, p );
		}
	}
	else
	{
		// Find the first edge for which @p is on the positive side of the line defined by the edge
		int firstOnOrLeft = -1;
		for (int vertI = 0; vertI < vertices.size(); vertI++)
		{
			int vertJ = nextIndex( vertI, vertices.size() );
			Segment2 seg( vertices[vertI], vertices[vertJ] );
			if ( seg.onOrLeft( p ) )
			{
				firstOnOrLeft = vertI;
				break;
			}
		}

		gs_assert( firstOnOrLeft != -1, "ConvexHull2::addPoint(): could not find first segment which has @p to the left\n" );


		// Starting with at the edge after the first edge for which @p is on the positive side (found in previous step),
		// find the first edge for which @p is on the negative side
		int vertI = nextIndex( firstOnOrLeft, vertices.size() );
		int firstOnRight = -1;
		for (int i = 0; i < vertices.size(); i++)
		{
			int vertJ = nextIndex( vertI, vertices.size() );
			Segment2 seg( vertices[vertI], vertices[vertJ] );
			if ( seg.onRight( p ) )
			{
				firstOnRight = vertI;
			}
			vertI = vertJ;
		}


		// If @firstOnRight is -1, then @p is on the left of all edges, meaning that it is inside the hull; do nothing in this case
		if ( firstOnRight != -1 )
		{
			// Starting the the first edge for which @p is on the negative side (found in previous step), look at the next
			// edge, and see if @p is on the negative side. If so, remove the vertex shared by both edges.
			int vertI = firstOnRight;
			int vertJ = nextIndex( vertI, vertices.size() );
			int vertK = nextIndex( vertJ, vertices.size() );
			Segment2 segB( vertices[vertJ], vertices[vertK] );

			while ( segB.onRight( p ) )
			{
				vertices.remove( vertJ );

				if ( vertJ < vertI )
				{
					vertI--;
				}
				vertJ = nextIndex( vertI, vertices.size() );
				vertK = nextIndex( vertJ, vertices.size() );
				segB = Segment2( vertices[vertJ], vertices[vertK] );
			}

			// Insert @p after @vertI
			vertices.insert( vertI + 1, p );
		}
	}
}