예제 #1
0
	DLL_PUBLIC double CDECL area(ClipperLib::IntPoint* path, size_t count) {
		ClipperLib::Path v = ClipperLib::Path();
		for(size_t i = 0; i < count; i++) {
			v.emplace(v.end(), path[i].X, path[i].Y);
		}

		return ClipperLib::Area(v);
	}
예제 #2
0
void
Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input, ClipperLib::Path &output)
{
    output.clear();
    for (Slic3r::Points::const_iterator pit = input.points.begin(); pit != input.points.end(); ++pit) {
        output.push_back(ClipperLib::IntPoint( (*pit).x, (*pit).y ));
    }
}
예제 #3
0
	//==============================================================
	// Static functions
	//==============================================================
	DLL_PUBLIC bool CDECL orientation(ClipperLib::IntPoint* path, size_t count) {
		ClipperLib::Path v = ClipperLib::Path();
		for(size_t i = 0; i < count; i++) {
			v.emplace(v.end(), path[i].X, path[i].Y);
		}

		return ClipperLib::Orientation(v);
	}
예제 #4
0
void
ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, T &output)
{
    output.points.clear();
    for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit) {
        output.points.push_back(Slic3r::Point( (*pit).X, (*pit).Y ));
    }
}
예제 #5
0
static ClipperLib::Path toClipperPath(const GeometryCoordinates& ring) {
    ClipperLib::Path result;
    result.reserve(ring.size());
    for (const auto& p : ring) {
        result.emplace_back(p.x, p.y);
    }
    return result;
}
예제 #6
0
Vector<Vector2> expand(const Vector<Vector2> &points, const Rect2i &rect, float epsilon = 2.0) {
	int size = points.size();
	ERR_FAIL_COND_V(size < 2, Vector<Vector2>());

	ClipperLib::Path subj;
	ClipperLib::PolyTree solution;
	ClipperLib::PolyTree out;

	for (int i = 0; i < points.size(); i++) {

		subj << ClipperLib::IntPoint(points[i].x * PRECISION, points[i].y * PRECISION);
	}
	ClipperLib::ClipperOffset co;
	co.AddPath(subj, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
	co.Execute(solution, epsilon * PRECISION);

	ClipperLib::PolyNode *p = solution.GetFirst();

	ERR_FAIL_COND_V(!p, points);

	while (p->IsHole()) {
		p = p->GetNext();
	}

	//turn the result into simply polygon (AKA, fix overlap)

	//clamp into the specified rect
	ClipperLib::Clipper cl;
	cl.StrictlySimple(true);
	cl.AddPath(p->Contour, ClipperLib::ptSubject, true);
	//create the clipping rect
	ClipperLib::Path clamp;
	clamp.push_back(ClipperLib::IntPoint(0, 0));
	clamp.push_back(ClipperLib::IntPoint(rect.size.width * PRECISION, 0));
	clamp.push_back(ClipperLib::IntPoint(rect.size.width * PRECISION, rect.size.height * PRECISION));
	clamp.push_back(ClipperLib::IntPoint(0, rect.size.height * PRECISION));
	cl.AddPath(clamp, ClipperLib::ptClip, true);
	cl.Execute(ClipperLib::ctIntersection, out);

	Vector<Vector2> outPoints;
	ClipperLib::PolyNode *p2 = out.GetFirst();
	while (p2->IsHole()) {
		p2 = p2->GetNext();
	}

	int lasti = p2->Contour.size() - 1;
	Vector2 prev = Vector2(p2->Contour[lasti].X / PRECISION, p2->Contour[lasti].Y / PRECISION);
	for (unsigned int i = 0; i < p2->Contour.size(); i++) {

		Vector2 cur = Vector2(p2->Contour[i].X / PRECISION, p2->Contour[i].Y / PRECISION);
		if (cur.distance_to(prev) > 0.5) {
			outPoints.push_back(cur);
			prev = cur;
		}
	}
	return outPoints;
}
예제 #7
0
void
ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, T* output)
{
    PROFILE_FUNC();
    output->points.clear();
    output->points.reserve(input.size());
    for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit)
        output->points.push_back(Slic3r::Point( (*pit).X, (*pit).Y ));
}
예제 #8
0
std::vector<Vec2> AutoPolygon::expand(const std::vector<Vec2>& points, const cocos2d::Rect &rect, const float& epsilon)
{
    auto size = points.size();
    // if there are less than 3 points, then we have nothing
    if(size<3)
    {
        log("AUTOPOLYGON: cannot expand points for %s with less than 3 points, e: %f", _filename.c_str(), epsilon);
        return std::vector<Vec2>();
    }
    ClipperLib::Path subj;
    ClipperLib::PolyTree solution;
    ClipperLib::PolyTree out;
    for(std::vector<Vec2>::const_iterator it = points.begin(); it<points.end(); it++)
    {
        subj << ClipperLib::IntPoint(it-> x* PRECISION, it->y * PRECISION);
    }
    ClipperLib::ClipperOffset co;
    co.AddPath(subj, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
    co.Execute(solution, epsilon * PRECISION);
    
    ClipperLib::PolyNode* p = solution.GetFirst();
    if(!p)
    {
        log("AUTOPOLYGON: Clipper failed to expand the points");
        return points;
    }
    while(p->IsHole()){
        p = p->GetNext();
    }

    //turn the result into simply polygon (AKA, fix overlap)
    
    //clamp into the specified rect
    ClipperLib::Clipper cl;
    cl.StrictlySimple(true);
    cl.AddPath(p->Contour, ClipperLib::ptSubject, true);
    //create the clipping rect
    ClipperLib::Path clamp;
    clamp.push_back(ClipperLib::IntPoint(0, 0));
    clamp.push_back(ClipperLib::IntPoint(rect.size.width/_scaleFactor * PRECISION, 0));
    clamp.push_back(ClipperLib::IntPoint(rect.size.width/_scaleFactor * PRECISION, rect.size.height/_scaleFactor * PRECISION));
    clamp.push_back(ClipperLib::IntPoint(0, rect.size.height/_scaleFactor * PRECISION));
    cl.AddPath(clamp, ClipperLib::ptClip, true);
    cl.Execute(ClipperLib::ctIntersection, out);
    
    std::vector<Vec2> outPoints;
    ClipperLib::PolyNode* p2 = out.GetFirst();
    while(p2->IsHole()){
        p2 = p2->GetNext();
    }
    auto end = p2->Contour.end();
    for(std::vector<ClipperLib::IntPoint>::const_iterator pt = p2->Contour.begin(); pt < end; pt++)
    {
        outPoints.push_back(Vec2(pt->X/PRECISION, pt->Y/PRECISION));
    }
    return outPoints;
}
예제 #9
0
ClipperLib::Path polygon::path(base_int denom) const
{
	ClipperLib::Path ret;
	for(auto v : this->vertexes)
	{
		ret.push_back(ClipperLib::IntPoint((ClipperLib::cInt)(v.x.numerator() * (denom / v.x.denominator())), (ClipperLib::cInt)(v.y.numerator() * (denom / v.y.denominator()))));
	}
	return ret;
}
예제 #10
0
ClipperLib::Path
Slic3rMultiPoint_to_ClipperPath_reversed(const Slic3r::MultiPoint &input)
{
    ClipperLib::Path output;
    output.reserve(input.points.size());
    for (Slic3r::Points::const_reverse_iterator pit = input.points.rbegin(); pit != input.points.rend(); ++pit)
        output.emplace_back((*pit)(0), (*pit)(1));
    return output;
}
예제 #11
0
void unscaleClipperPolygon(ClipperLib::Path &polygon)
{
    PROFILE_FUNC();
    for (ClipperLib::Path::iterator pit = polygon.begin(); pit != polygon.end(); ++pit) {
        pit->X += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA;
        pit->Y += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA;
        pit->X >>= CLIPPER_OFFSET_POWER_OF_2;
        pit->Y >>= CLIPPER_OFFSET_POWER_OF_2;
    }
}
예제 #12
0
std::string
SVG::get_path_d(const ClipperLib::Path &path, double scale, bool closed) const
{
    std::ostringstream d;
    d << "M ";
    for (ClipperLib::Path::const_iterator p = path.begin(); p != path.end(); ++p) {
        d << COORD(scale * p->X - origin.x) << " ";
        d << COORD(scale * p->Y - origin.y) << " ";
    }
    if (closed) d << "z";
    return d.str();
}
예제 #13
0
ClipperLib::Path Clipper::toClipper(const ofPolyline& polyline,
                                    ClipperLib::cInt scale)
{
    ClipperLib::Path path;

    for (auto& vertex: polyline.getVertices())
    {
        path.push_back(toClipper(vertex, scale));
    }

    return path;
}
void Grasp_Calculator::path_to_double_polygon(DPolygon2D &double_polygon, ClipperLib::Path int_polygon)
{
    ClipperLib::cInt factor = 100000;
    double_polygon.clear();
    for (std::vector<IntPoint>::iterator ip = int_polygon.begin(); ip != int_polygon.end(); ++ip)
    {
        DoublePoint2D d2p;
        d2p.x = ((double)ip->X) / factor;
        d2p.y = ((double)ip->Y) / factor;
        double_polygon.push_back(d2p);
    }
}
예제 #15
0
	DLL_PUBLIC void CDECL add_offset_path(ClipperLib::ClipperOffset *ptr, ClipperLib::IntPoint* path, size_t count,
																				ClipperLib::JoinType joinType, ClipperLib::EndType endType) {
		ClipperLib::Path v = ClipperLib::Path();
		for(size_t i = 0; i < count; i++) {
			v.emplace(v.end(), path[i].X, path[i].Y);
		}

		try {
			ptr->AddPath(v, joinType, endType);
		} catch(ClipperLib::clipperException e) {
			printf(e.what());
		}
	}
예제 #16
0
	DLL_PUBLIC bool CDECL add_path(ClipperLib::Clipper *ptr, ClipperLib::IntPoint* path, size_t count, ClipperLib::PolyType polyType, bool closed) {
		ClipperLib::Path v = ClipperLib::Path();
		for(size_t i = 0; i < count; i++) {
			v.emplace(v.end(), path[i].X, path[i].Y);
		}

		bool result = false;

		try {
			result = ptr->AddPath(v, polyType, closed);
		} catch(ClipperLib::clipperException e) {
			printf(e.what());
		}

		return result;
	}
예제 #17
0
파일: World.cpp 프로젝트: zivl/Crush-Around
// Set the objects (defined by contour points) to be models in the world and scene.
void World::setObjectsToBeModeled(const std::vector<std::vector<cv::Point>> contours) {

    int contourSize = (int)contours.size();
    for(int i = 0; i < contourSize; i++ )
    {
        std::vector<cv::Point> currentShape = contours[i];
        int numOfPoints = (int)currentShape.size();

        b2Vec2 * vertices = new b2Vec2[numOfPoints];
        ClipperLib::Paths* polygons = new ClipperLib::Paths();
        ClipperLib::Path polygon;

        for (int j = 0; j < numOfPoints; j++)
        {
            vertices[j].x = currentShape[j].x / PTM_RATIO;
            vertices[j].y = currentShape[j].y / PTM_RATIO;

            //cv::line(m_scene, currentShape[j], currentShape[(j + 1) % numOfPoints], cv::Scalar(0,0,255));
            //std::cout << "[" << vertices[j].x << "," <<vertices[j].y << "]" << std::endl;

            polygon.push_back(ClipperLib::IntPoint(currentShape[j].x, currentShape[j].y));
        }

        b2BodyDef objectBodyDef;
        objectBodyDef.type = b2_staticBody;

        b2Body *objectBody = m_world->CreateBody(&objectBodyDef);
        objectBody->SetUserData(polygons);

        polygons->push_back(polygon);

        b2EdgeShape objectEdgeShape;
        b2FixtureDef objectShapeDef;
        objectShapeDef.shape = &objectEdgeShape;

        for (int j = 0; j < numOfPoints - 1; j++)
        {
            objectEdgeShape.Set(vertices[j], vertices[j+1]);
            objectBody->CreateFixture(&objectShapeDef);
        }

        objectEdgeShape.Set(vertices[numOfPoints - 1], vertices[0]);
        objectBody->CreateFixture(&objectShapeDef);
        m_objectBodies.push_back(objectBody);
        delete[] vertices;
    }
}
예제 #18
0
	ClipperLib::Path pathToClipperPath(const panda::types::Path& path)
	{
		ClipperLib::Path out;
		auto maxVal = std::numeric_limits<ClipperLib::cInt>::max();
		ClipperLib::IntPoint prevPt(maxVal, maxVal);
		for (const auto& pt : path.points)
		{
			auto newPt = convert(pt);
			if (newPt == prevPt)
				continue;
			out.push_back(newPt);
			prevPt = newPt;
		}

		if (path.points.size() > 1 && path.points.back() == path.points.front())
			out.pop_back();
		return out;
	}
예제 #19
0
void BooleanTool::rebuildCoordinate(
        ClipperLib::Path::size_type index,
        const ClipperLib::Path& polygon,
        const PolyMap& polymap,
        PathObject* object,
        bool start_new_part)
{
	MapCoord coord(0.001 * polygon.at(index).X, 0.001 * polygon.at(index).Y);
	if (polymap.contains(polygon.at(index)))
	{
		PathCoordInfo info = polymap.value(polygon.at(index));
		MapCoord& original = info.first->path->getCoordinate(info.second->index);
		
		if (original.isDashPoint())
			coord.setDashPoint(true);
	}
	object->addCoordinate(coord, start_new_part);
}
예제 #20
0
ClipperLib::Path ElementGeometryClipper::createPathFromBoundingBox()
{
    double xMin = quadKeyBbox_.minPoint.longitude, yMin = quadKeyBbox_.minPoint.latitude,
            xMax = quadKeyBbox_.maxPoint.longitude, yMax = quadKeyBbox_.maxPoint.latitude;
    ClipperLib::Path rect;
    rect.push_back(ClipperLib::IntPoint(static_cast<ClipperLib::cInt>(xMin*Scale), 
                                        static_cast<ClipperLib::cInt>(yMin*Scale)));

    rect.push_back(ClipperLib::IntPoint(static_cast<ClipperLib::cInt>(xMax*Scale), 
                                        static_cast<ClipperLib::cInt>(yMin*Scale)));

    rect.push_back(ClipperLib::IntPoint(static_cast<ClipperLib::cInt>(xMax*Scale), 
                                        static_cast<ClipperLib::cInt>(yMax*Scale)));

    rect.push_back(ClipperLib::IntPoint(static_cast<ClipperLib::cInt>(xMin*Scale),
                                        static_cast<ClipperLib::cInt>(yMax*Scale)));
    return std::move(rect);
}
예제 #21
0
	panda::types::Path clipperPathToPath(const ClipperLib::Path& path)
	{
		panda::types::Path out;
		if (path.empty())
			return out;
		for (const auto& pt : path)
			out.points.push_back(convert(pt));
		if (out.points.front() != out.points.back())
			out.points.push_back(out.points.front());
		return out;
	}
예제 #22
0
static GeometryCoordinates fromClipperPath(const ClipperLib::Path& path) {
    GeometryCoordinates result;
    result.reserve(path.size() + 1);
    
    result.reserve(path.size());
    for (const auto& p : path) {
        using Coordinate = GeometryCoordinates::coordinate_type;
        assert(p.x >= std::numeric_limits<Coordinate>::min());
        assert(p.x <= std::numeric_limits<Coordinate>::max());
        assert(p.y >= std::numeric_limits<Coordinate>::min());
        assert(p.y <= std::numeric_limits<Coordinate>::max());
        result.emplace_back(Coordinate(p.x), Coordinate(p.y));
    }
    
    // Clipper does not repeat initial point, but our geometry model requires it.
    if (!result.empty()) {
        result.push_back(result.front());
    }
    
    return result;
}
예제 #23
0
void Polygon2d_TestModule()
{
    // "This structure contains a sequence of IntPoint vertices defining a
    // single contour"
    ClipperLib::Path aPath;

    SEGMENTS aSegments;

    aPath.resize( 4 );

    aPath[0] = ClipperLib::IntPoint( -2, -2 );
    aPath[1] = ClipperLib::IntPoint(  2, -2 );
    aPath[2] = ClipperLib::IntPoint(  2,  2 );
    aPath[3] = ClipperLib::IntPoint( -2,  2 );

    // It must be an outter polygon
    wxASSERT( ClipperLib::Orientation( aPath ) );

    polygon_Convert( aPath, aSegments, 1.0f );

    wxASSERT( aPath.size() == aSegments.size() );

    wxASSERT( aSegments[0].m_Start == SFVEC2F( -2.0f,  2.0f ) );
    wxASSERT( aSegments[1].m_Start == SFVEC2F(  2.0f,  2.0f ) );
    wxASSERT( aSegments[2].m_Start == SFVEC2F(  2.0f, -2.0f ) );
    wxASSERT( aSegments[3].m_Start == SFVEC2F( -2.0f, -2.0f ) );

    wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F(  0.0f,  0.0f ) ) );
    wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( -1.9f, -1.9f ) ) );
    wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( -1.9f,  1.9f ) ) );
    wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F(  1.9f,  1.9f ) ) );
    wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F(  1.9f, -1.9f ) ) );

    wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( -2.1f, -2.0f ) ) == false );
    wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( -2.1f,  2.0f ) ) == false );
    wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F(  2.1f,  2.0f ) ) == false );
    wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F(  2.1f, -2.0f ) ) == false );
}
예제 #24
0
static void polygon_Convert( const ClipperLib::Path &aPath,
                             SEGMENTS &aOutSegment,
                             float aBiuTo3DunitsScale )
{
    aOutSegment.resize( aPath.size() );

    for( unsigned i = 0; i < aPath.size(); i++ )
    {
        aOutSegment[i].m_Start = SFVEC2F( (float) aPath[i].X * aBiuTo3DunitsScale,
                                          (float)-aPath[i].Y * aBiuTo3DunitsScale );
    }

    unsigned int i;
    unsigned int j = aOutSegment.size () - 1;

    for( i = 0; i < aOutSegment.size (); j = i++ )
    {
        // Calculate constants for each segment
        aOutSegment[i].m_inv_JY_minus_IY = 1.0f / ( aOutSegment[j].m_Start.y -
                                                    aOutSegment[i].m_Start.y );
        aOutSegment[i].m_JX_minus_IX = (aOutSegment[j].m_Start.x - aOutSegment[i].m_Start.x);
    }
}
예제 #25
0
    void buildMansardShape(const utymap::meshing::Polygon& polygon, ClipperLib::Path& offsetShape, std::size_t index)
    {
        if (!ClipperLib::Orientation(offsetShape))
            std::reverse(offsetShape.begin(), offsetShape.end());

        // build top
        utymap::meshing::Polygon topShape(offsetShape.size(), 0);
        std::vector<utymap::meshing::Vector2> topShapeVertices;
        topShapeVertices.reserve(offsetShape.size());
        for (const auto& p : offsetShape) {
            topShapeVertices.push_back(utymap::meshing::Vector2(p.X / Scale, p.Y/ Scale));
        }
        topShape.addContour(topShapeVertices);

        auto topOptions = utymap::meshing::MeshBuilder::Options(0, 0, colorNoiseFreq_, height_, gradient_, minHeight_);
        builderContext_.meshBuilder.addPolygon(meshContext_.mesh, topShape, topOptions);

        // build sides
        auto sideOptions = utymap::meshing::MeshBuilder::Options( 0, 0, colorNoiseFreq_, 0, gradient_, 0);
        double topHeight = minHeight_ + height_;
        auto size = polygon.points.size();
        for (std::size_t i = 0; i < size; i += 2) {
            auto topIndex = i;
            auto bottomIndex = (index + i) % size;
            auto nextTopIndex = (i + 2)  % size;
            auto nextBottomIndex = (index + i + 2) % size;

            auto v0 = utymap::meshing::Vector3(polygon.points[bottomIndex], minHeight_, polygon.points[bottomIndex + 1]);
            auto v1 = utymap::meshing::Vector3(polygon.points[nextBottomIndex], minHeight_, polygon.points[nextBottomIndex + 1]);
            auto v2 = utymap::meshing::Vector3(topShape.points[nextTopIndex], topHeight, topShape.points[nextTopIndex + 1]);
            auto v3 = utymap::meshing::Vector3(topShape.points[topIndex], topHeight, topShape.points[topIndex + 1]);

            builderContext_.meshBuilder.addTriangle(meshContext_.mesh, v2, v0, v3, sideOptions, false);
            builderContext_.meshBuilder.addTriangle(meshContext_.mesh, v0, v2, v1, sideOptions, false);
        }
    }
geo::Polygon<geo::Ring<Vector>> Environment::subtract(geo::Polygon<geo::Ring<Vector>> const& poly, geo::Ring<Vector> const& ring) {
	
	ClipperLib::Path subj;
  	ClipperLib::Paths solution;
  	
  	ClipperLib::Clipper c;
	
  	for (Vector const& v : poly.ering)
  		subj.push_back(ClipperLib::IntPoint((int)v.x, (int)v.y));
  	c.AddPath(subj, ClipperLib::ptSubject, true);

  	for (Ring const& ring : poly.irings) {
		subj.clear();
		for (Vector const& v : ring)
  			subj.push_back(ClipperLib::IntPoint((int)v.x, (int)v.y));
  	   	std::reverse(subj.begin(), subj.end());
  		c.AddPath(subj, ClipperLib::ptSubject, true);
  	}
	
	subj.clear();
  	for (Vector const& v : ring)
  		subj.push_back(ClipperLib::IntPoint((int)v.x, (int)v.y));
  	c.AddPath(subj, ClipperLib::ptClip, true);

    c.Execute(ClipperLib::ctDifference, solution);
    geo::Polygon<geo::Ring<Vector>> ans;
    for (ClipperLib::IntPoint const& pt : solution[0]) {
    	ans.ering.push_back({pt.X, pt.Y});
    }
    for (int i = 1; i < solution.size(); ++i) {
    	ClipperLib::Path const& path = solution[i];
    	geo::Ring<Vector> ring;
    	for (ClipperLib::IntPoint const& pt : path)
    		ring.push_back({pt.X, pt.Y});
    	ans.irings.push_back(ring);
    }
    geo::correct(ans);
	return ans;

}
예제 #27
0
 inline void setCoordinates(T& t, const ClipperLib::Path& path) {
     t.coordinates.reserve(path.size());
     for (const auto& c : path) {
         t.coordinates.push_back(GeoCoordinate(c.Y / Scale, c.X / Scale));
     }
 }
예제 #28
0
void BooleanTool::rebuildTwoIndexSegment(
        ClipperLib::Path::size_type start_index,
        ClipperLib::Path::size_type end_index,
        bool sequence_increasing,
        const ClipperLib::Path& polygon,
        const PolyMap& polymap,
        PathObject* object)
{
	Q_UNUSED(sequence_increasing); // only used in Q_ASSERT.
	
	PathCoordInfo start_info = polymap.value(polygon.at(start_index));
	PathCoordInfo end_info = polymap.value(polygon.at(end_index));
	PathObject* original = end_info.first->path;
	
	bool coords_increasing;
	bool is_curve;
	int coord_index;
	if (start_info.second->index == end_info.second->index)
	{
		coord_index = end_info.second->index;
		bool found = checkSegmentMatch(original, coord_index, polygon, start_index, end_index, coords_increasing, is_curve);
		if (!found)
		{
			object->getCoordinate(object->getCoordinateCount() - 1).setCurveStart(false);
			rebuildCoordinate(end_index, polygon, polymap, object);
			return;
		}
		Q_ASSERT(coords_increasing == sequence_increasing);
	}
	else
	{
		coord_index = end_info.second->index;
		bool found = checkSegmentMatch(original, coord_index, polygon, start_index, end_index, coords_increasing, is_curve);
		if (!found)
		{
			coord_index = start_info.second->index;
			found = checkSegmentMatch(original, coord_index, polygon, start_index, end_index, coords_increasing, is_curve);
			if (!found)
			{
				object->getCoordinate(object->getCoordinateCount() - 1).setCurveStart(false);
				rebuildCoordinate(end_index, polygon, polymap, object);
				return;
			}
		}
	}
	
	if (!is_curve)
		object->getCoordinate(object->getCoordinateCount() - 1).setCurveStart(false);
	
	if (coords_increasing)
	{
		object->addCoordinate(resetCoordinate(original->getCoordinate(coord_index + 1)));
		if (is_curve)
		{
			object->addCoordinate(original->getCoordinate(coord_index + 2));
			object->addCoordinate(resetCoordinate(original->getCoordinate(coord_index + 3)));
		}
	}
	else
	{
		if (is_curve)
		{
			object->addCoordinate(resetCoordinate(original->getCoordinate(coord_index + 2)));
			object->addCoordinate(original->getCoordinate(coord_index + 1));
		}
		object->addCoordinate(resetCoordinate(original->getCoordinate(coord_index + 0)));
	}
}
예제 #29
0
void BooleanTool::rebuildSegment(
        ClipperLib::Path::size_type start_index,
        ClipperLib::Path::size_type end_index,
        bool sequence_increasing,
        const ClipperLib::Path& polygon,
        const PolyMap& polymap,
        PathObject* object)
{
	auto num_points = polygon.size();
	
	object->getCoordinate(object->getCoordinateCount() - 1).setCurveStart(true);
	
	if ((start_index + 1) % num_points == end_index)
	{
		// This could happen for a straight line or a very flat curve - take coords directly from original
		rebuildTwoIndexSegment(start_index, end_index, sequence_increasing, polygon, polymap, object);
		return;
	}

	// Get polygon point coordinates
	const auto& start_point       = polygon.at(start_index);
	const auto& second_point      = polygon.at((start_index + 1) % num_points);
	const auto& second_last_point = polygon.at((end_index - 1) % num_points);
	const auto& end_point         = polygon.at(end_index);
	
	// Try to find the middle coordinates in the same part
	bool found = false;
	PathCoordInfo second_info{ nullptr, nullptr };
	PathCoordInfo second_last_info{ nullptr, nullptr };
	for (auto second_it = polymap.find(second_point); second_it != polymap.end(); ++second_it)
	{
		for (auto second_last_it = polymap.find(second_last_point);
		     second_last_it != polymap.end() && second_last_it.key() == second_last_point;
		     ++second_last_it)
		{
			if (second_it->first == second_last_it->first &&
			    second_it->second->index == second_last_it->second->index)
			{
				// Same part
				found = true;
				second_info = *second_it;
				second_last_info = *second_last_it;
				break;
			}
		}
		if (found)
			break;
	}
	
	if (!found)
	{
		// Need unambiguous path part information to find the original object with high probability
		qDebug() << "BooleanTool::rebuildSegment: cannot identify original object!";
		rebuildSegmentFromPathOnly(start_point, second_point, second_last_point, end_point, object);
		return;
	}
	
	const PathPart* original_path = second_info.first;
	
	// Try to find the outer coordinates in the same part
	PathCoordInfo start_info{ nullptr, nullptr };
	for (auto start_it = polymap.find(start_point);
	     start_it != polymap.end() && start_it.key() == start_point;
	     ++start_it)
	{
		if (start_it->first == original_path)
		{
			start_info = *start_it;
			break;
		}
	}
	Q_ASSERT(!start_info.first || start_info.first == second_info.first);
	
	PathCoordInfo end_info{ nullptr, nullptr };
	for (auto end_it = polymap.find(end_point);
	     end_it != polymap.end() && end_it.key() == end_point;
	     ++end_it)
	{
		if (end_it->first == original_path)
		{
			end_info = *end_it;
			break;
		}
	}
	Q_ASSERT(!end_info.first || end_info.first == second_info.first);
	
	const PathObject* original = original_path->path;
	auto edge_start = second_info.second->index;
	if (edge_start == second_info.first->last_index)
		edge_start = second_info.first->first_index;
	
	// Find out start tangent
	auto start_param = 0.0;
	MapCoord start_coord = MapCoord(0.001 * start_point.X, 0.001 * start_point.Y);
	MapCoord start_tangent;
	MapCoord end_tangent;
	MapCoord end_coord;
	
	double start_error_sq, end_error_sq;
	// Maximum difference in mm from reconstructed start and end coords to the
	// intersection points returned by Clipper
	const double error_bound = 0.4;
	
	if (sequence_increasing)
	{
		if ( second_info.second->param == 0.0 ||
		     ( start_info.first &&
		       start_info.second->param == 0.0 &&
		       ( start_info.second->index == edge_start ||
		         (start_info.second->index == start_info.first->last_index && start_info.first->first_index == edge_start) ) ) )
		{
			// Take coordinates directly
			start_tangent = original->getCoordinate(edge_start + 1);
			end_tangent = original->getCoordinate(edge_start + 2);
			
			start_error_sq = start_coord.distanceSquaredTo(original->getCoordinate(edge_start + 0));
			if (start_error_sq > error_bound)
				qDebug() << "BooleanTool::rebuildSegment: start error too high in increasing direct case: " << sqrt(start_error_sq);
		}
		else
		{
			// Approximate coords
			const PathCoord* prev_coord = second_info.second - 1;
			
			auto dx = second_point.X - start_point.X;
			auto dy = second_point.Y - start_point.Y;
			auto point_dist = 0.001 * sqrt(dx*dx + dy*dy);
			
			auto delta_start_param = (second_info.second->param - prev_coord->param) * point_dist / qMax(1e-7f, (second_info.second->clen - prev_coord->clen));
			start_param = qBound(0.0, second_info.second->param - delta_start_param, 1.0);
			
			MapCoordF unused, o2, o3, o4;
			PathCoord::splitBezierCurve(MapCoordF(original->getCoordinate(edge_start + 0)), MapCoordF(original->getCoordinate(edge_start + 1)),
			                            MapCoordF(original->getCoordinate(edge_start + 2)), MapCoordF(original->getCoordinate(edge_start + 3)),
			                            start_param,
			                            unused, unused, o2, o3, o4);
			start_tangent = MapCoord(o3);
			end_tangent = MapCoord(o4);
			
			start_error_sq = start_coord.distanceSquaredTo(MapCoord(o2));
			if (start_error_sq > error_bound)
				qDebug() << "BooleanTool::rebuildSegment: start error too high in increasing general case: " << sqrt(start_error_sq);
		}
		
		// Find better end point approximation and its tangent
		if ( second_last_info.second->param == 0.0 ||
		     (end_info.first &&
		      end_info.second->param == 0.0 &&
		      ( end_info.second->index == edge_start+3 ||
		        (end_info.second->index == end_info.first->first_index && end_info.first->last_index == edge_start+3) ) ) )
		{
			// Take coordinates directly
			end_coord = original->getCoordinate(edge_start + 3);
			
			auto test_x = end_point.X - end_coord.nativeX();
			auto test_y = end_point.Y - end_coord.nativeY();
			end_error_sq = 0.001 * sqrt(test_x*test_x + test_y*test_y);
			if (end_error_sq > error_bound)
				qDebug() << "BooleanTool::rebuildSegment: end error too high in increasing direct case: " << sqrt(end_error_sq);
		}
		else
		{
			// Approximate coords
			const PathCoord* next_coord = second_last_info.second + 1;
			auto next_coord_param = next_coord->param;
			if (next_coord_param == 0.0)
				next_coord_param = 1.0;
			
			auto dx = end_point.X - second_last_point.X;
			auto dy = end_point.Y - second_last_point.Y;
			auto point_dist = 0.001 * sqrt(dx*dx + dy*dy);
			
			auto delta_end_param = (next_coord_param - second_last_info.second->param) * point_dist / qMax(1e-7f, (next_coord->clen - second_last_info.second->clen));
			auto end_param = (second_last_info.second->param + delta_end_param - start_param) / (1.0 - start_param);
			
			MapCoordF o0, o1, o2, unused;
			PathCoord::splitBezierCurve(MapCoordF(start_coord), MapCoordF(start_tangent),
			                            MapCoordF(end_tangent), MapCoordF(original->getCoordinate(edge_start + 3)),
			                            end_param,
			                            o0, o1, o2, unused, unused);
			start_tangent = MapCoord(o0);
			end_tangent = MapCoord(o1);
			end_coord = MapCoord(o2);
			
			auto test_x = end_point.X - end_coord.nativeX();
			auto test_y = end_point.Y - end_coord.nativeY();
			end_error_sq = 0.001 * sqrt(test_x*test_x + test_y*test_y);
			if (end_error_sq > error_bound)
				qDebug() << "BooleanTool::rebuildSegment: end error too high in increasing general case: " << sqrt(end_error_sq);
		}
	}
	else // if (!sequence_increasing)
	{
		if ( second_info.second->param == 0.0 ||
		     ( start_info.first &&
		       start_info.second->param == 0.0 &&
		       ( start_info.second->index == edge_start+3 ||
			     (start_info.second->index == start_info.first->first_index && start_info.first->last_index == edge_start+3) ) ) )
		{
			// Take coordinates directly
			start_tangent = original->getCoordinate(edge_start + 2);
			end_tangent = original->getCoordinate(edge_start + 1);
			
			start_error_sq = start_coord.distanceSquaredTo(original->getCoordinate(edge_start + 3));
			if (start_error_sq > error_bound)
				qDebug() << "BooleanTool::rebuildSegment: start error too high in decreasing direct case: " << sqrt(start_error_sq);
		}
		else
		{
			// Approximate coords
			const PathCoord* next_coord = second_info.second + 1;
			auto next_coord_param = next_coord->param;
			if (next_coord_param == 0.0)
				next_coord_param = 1.0;
			
			auto dx = second_point.X - start_point.X;
			auto dy = second_point.Y - start_point.Y;
			auto point_dist = 0.001 * sqrt(dx*dx + dy*dy);
			
			auto delta_start_param = (next_coord_param - second_info.second->param) * point_dist / qMax(1e-7f, (next_coord->clen - second_info.second->clen));
			start_param = qBound(0.0, 1.0 - second_info.second->param + delta_start_param, 1.0);
			
			MapCoordF unused, o2, o3, o4;
			PathCoord::splitBezierCurve(MapCoordF(original->getCoordinate(edge_start + 3)), MapCoordF(original->getCoordinate(edge_start + 2)),
			                            MapCoordF(original->getCoordinate(edge_start + 1)), MapCoordF(original->getCoordinate(edge_start + 0)),
			                            start_param,
			                            unused, unused, o2, o3, o4);
			start_tangent = MapCoord(o3);
			end_tangent = MapCoord(o4);
			
			start_error_sq = start_coord.distanceSquaredTo(MapCoord(o2));
			if (start_error_sq > error_bound)
				qDebug() << "BooleanTool::rebuildSegment: start error too high in decreasing general case: " << sqrt(start_error_sq);
		}
		
		// Find better end point approximation and its tangent
		if ( second_last_info.second->param == 0.0 ||
		     ( end_info.first &&
		       end_info.second->param == 0.0 &&
		       ( end_info.second->index == edge_start ||
		         (end_info.second->index == end_info.first->last_index && end_info.first->first_index == edge_start) ) ) )
		{
			// Take coordinates directly
			end_coord = original->getCoordinate(edge_start + 0);
			
			auto test_x = end_point.X - end_coord.nativeX();
			auto test_y = end_point.Y - end_coord.nativeY();
			end_error_sq = 0.001 * sqrt(test_x*test_x + test_y*test_y);
			if (end_error_sq > error_bound)
				qDebug() << "BooleanTool::rebuildSegment: end error too high in decreasing direct case: " << sqrt(end_error_sq);
		}
		else
		{
			// Approximate coords
			const PathCoord* prev_coord = second_last_info.second - 1;
			
			auto dx = end_point.X - second_last_point.X;
			auto dy = end_point.Y - second_last_point.Y;
			auto point_dist = 0.001 * sqrt(dx*dx + dy*dy);
			
			auto delta_end_param = (second_last_info.second->param - prev_coord->param) * point_dist / qMax(1e-7f, (second_last_info.second->clen - prev_coord->clen));
			auto end_param = (1.0 - second_last_info.second->param + delta_end_param) / (1 - start_param);
			
			MapCoordF o0, o1, o2, unused;
			PathCoord::splitBezierCurve(MapCoordF(start_coord), MapCoordF(start_tangent),
			                            MapCoordF(end_tangent), MapCoordF(original->getCoordinate(edge_start + 0)),
			                            end_param,
			                            o0, o1, o2, unused, unused);
			start_tangent = MapCoord(o0);
			end_tangent = MapCoord(o1);
			end_coord = MapCoord(o2);
			
			auto test_x = end_point.X - end_coord.nativeX();
			auto test_y = end_point.Y - end_coord.nativeY();
			end_error_sq = 0.001 * sqrt(test_x*test_x + test_y*test_y);
			if (end_error_sq > error_bound)
				qDebug() << "BooleanTool::rebuildSegment: end error too high in decreasing general case: " << sqrt(end_error_sq);
		}
	}
	
	if (start_error_sq <= error_bound && end_error_sq <= error_bound)
	{
		// Rebuild bezier curve using information from original curve
		object->addCoordinate(start_tangent);
		object->addCoordinate(end_tangent);
		object->addCoordinate(resetCoordinate(end_coord));
	}
	else
	{
		// Rebuild bezier curve approximately using tangents derived from result polygon
		rebuildSegmentFromPathOnly(start_point, second_point, second_last_point, end_point, object);
	}
}
예제 #30
0
void BooleanTool::polygonToPathPart(const ClipperLib::Path& polygon, const PolyMap& polymap, PathObject* object)
{
	auto num_points = polygon.size();
	if (num_points < 3)
		return;
	
	// Index of first used point in polygon
	auto part_start_index = 0u;
	auto cur_info = PathCoordInfo{ nullptr, nullptr };
	
	// Check if we can find either an unknown intersection point
	// or a path coord with parameter 0.
	// This gives a starting point to search for curves to rebuild
	// (because we cannot start in the middle of a curve)
	for (; part_start_index < num_points; ++part_start_index)
	{
		auto current_point = polygon.at(part_start_index);
		if (!polymap.contains(current_point))
			break;
		
		if (polymap.value(current_point).second->param == 0.0)
		{
			cur_info = polymap.value(current_point);
			break;
		}
	}
	
	if (part_start_index == num_points)
	{
		// Did not find a valid starting point. Return the part as a polygon.
		for (auto i = 0u; i < num_points; ++i)
			object->addCoordinate(MapCoord(0.001 * polygon.at(i).X, 0.001 * polygon.at(i).Y), (i == 0));
		object->parts().back().setClosed(true, true);
		return;
	}
	
	// Add the first point to the object
	rebuildCoordinate(part_start_index, polygon, polymap, object, true);
	
	
	// Index of first segment point in polygon
	auto segment_start_index = part_start_index;
	bool have_sequence = false;
	bool sequence_increasing = false;
	bool stop_before = false;
	
	// Advance along the boundary and rebuild the curve for every sequence
	// of path coord pointers with the same path and index.
	auto i = part_start_index;
	do
	{
		++i;
		if (i >= num_points)
			i = 0;
		
		PathCoordInfo new_info{ nullptr, nullptr };
		auto new_point = polygon.at(i);
		if (polymap.contains(new_point))
			new_info = polymap.value(new_point);
		
		if (cur_info.first && cur_info.first == new_info.first)
		{
			// Same original part
			auto cur_coord_index = cur_info.second->index;
			MapCoord& cur_coord = cur_info.first->path->getCoordinate(cur_coord_index);
			
			auto new_coord_index = new_info.second->index;
			MapCoord& new_coord = new_info.first->path->getCoordinate(new_coord_index);
			
			auto cur_coord_index_adjusted = cur_coord_index;
			if (cur_coord_index_adjusted == new_info.first->first_index)
				cur_coord_index_adjusted = new_info.first->last_index;
			
			auto new_coord_index_adjusted = new_coord_index;
			if (new_coord_index_adjusted == new_info.first->first_index)
				new_coord_index_adjusted = new_info.first->last_index;
			
			if (cur_coord_index == new_coord_index)
			{
				// Somewhere on a curve
				bool param_increasing = new_info.second->param > cur_info.second->param;
				if (!have_sequence)
				{
					have_sequence = true;
					sequence_increasing = param_increasing;
				}
				else if (have_sequence && sequence_increasing != param_increasing)
				{
					stop_before = true;
				}
			}
			else if (new_info.second->param == 0.0 &&
			         ( (cur_coord.isCurveStart() && new_coord_index_adjusted == cur_coord_index + 3) ||
					   (!cur_coord.isCurveStart() && new_coord_index_adjusted == cur_coord_index + 1) ) )
			{
				// Original curve is from cur_coord_index to new_coord_index_adjusted.
				if (!have_sequence)
				{
					have_sequence = true;
					sequence_increasing = true;
				}
				else
				{
					stop_before = !sequence_increasing;
				}
			}
			else if (cur_info.second->param == 0.0 &&
			         ( (new_coord.isCurveStart() && new_coord_index + 3 == cur_coord_index_adjusted) ||
					   (!new_coord.isCurveStart() && new_coord_index + 1 == cur_coord_index_adjusted) ) )
			{
				// Original curve is from new_coord_index to cur_coord_index_adjusted.
				if (!have_sequence)
				{
					have_sequence = true;
					sequence_increasing = false;
				}
				else
				{
					stop_before = sequence_increasing;
				}
			}
			else if ((segment_start_index + 1) % num_points != i)
			{
				// Not immediately after segment_start_index
				stop_before = true;
			}
		}
		
		if (i == part_start_index ||
		    stop_before ||
		    (new_info.second && new_info.second->param == 0.0) ||
			(cur_info.first && (cur_info.first != new_info.first || cur_info.second->index != new_info.second->index) && i != (segment_start_index + 1) % num_points) ||
			!new_info.first)
		{
			if (stop_before)
			{
				if (i == 0)
					i = num_points - 1;
				else
					--i;
			}
			
			if (have_sequence)
				// A sequence of at least two points belonging to the same curve
				rebuildSegment(segment_start_index, i, sequence_increasing, polygon, polymap, object);
			else
				// A single straight edge
				rebuildCoordinate(i, polygon, polymap, object);
			
			if (stop_before)
			{
				++i;
				if (i >= num_points)
					i = 0;
				rebuildCoordinate(i, polygon, polymap, object);
				stop_before = false;
			}
			
			segment_start_index = i;
			have_sequence = false;
		}
		
		cur_info = new_info;
	}
	while  (i != part_start_index);
	
	object->parts().back().connectEnds();
}