/** * @retval true the two entities can be trimmed to each other; * i.e. they are in a graphic or in the same polyline. */ bool RS_Information::isTrimmable(RS_Entity* e1, RS_Entity* e2) { if (e1!=NULL && e2!=NULL) { if (e1->getParent()!=NULL && e2->getParent()!=NULL) { if (e1->getParent()->rtti()==RS2::EntityPolyline && e2->getParent()->rtti()==RS2::EntityPolyline && e1->getParent()==e2->getParent()) { // in the same polyline RS_EntityContainer* pl = e1->getParent(); int idx1 = pl->findEntity(e1); int idx2 = pl->findEntity(e2); RS_DEBUG->print("RS_Information::isTrimmable: " "idx1: %d, idx2: %d", idx1, idx2); if (abs(idx1-idx2)==1 || abs(idx1-idx2)==pl->count()-1) { // directly following entities return true; } else { // not directly following entities return false; } } else if ((e1->getParent()->rtti()==RS2::EntityContainer || e1->getParent()->rtti()==RS2::EntityGraphic || e1->getParent()->rtti()==RS2::EntityBlock) && (e2->getParent()->rtti()==RS2::EntityContainer || e2->getParent()->rtti()==RS2::EntityGraphic || e2->getParent()->rtti()==RS2::EntityBlock)) { // normal entities: return true; } } else { // independent entities with the same parent: return (e1->getParent()==e2->getParent()); } } return false; }
RS_VectorSolutions RS_Information::createQuadrilateral(const RS_EntityContainer& container) { RS_VectorSolutions ret; if(container.count()!=4) return ret; RS_EntityContainer c(container); std::vector<RS_Line*> lines; for(auto e: c){ if(e->rtti()!=RS2::EntityLine) return ret; lines.push_back(static_cast<RS_Line*>(e)); } if(lines.size()!=4) return ret; //find intersections std::vector<RS_Vector> vertices; for(auto it=lines.begin()+1; it != lines.end(); ++it){ for(auto jt=lines.begin(); jt != it; ++jt){ RS_VectorSolutions const& sol=RS_Information::getIntersectionLineLine(*it, *jt); if(sol.size()){ vertices.push_back(sol.at(0)); } } } // std::cout<<"vertices.size()="<<vertices.size()<<std::endl; switch (vertices.size()){ default: return ret; case 4: break; case 5: case 6: for(RS_Line* pl: lines){ const double a0=pl->getDirection1(); std::vector<std::vector<RS_Vector>::iterator> left; std::vector<std::vector<RS_Vector>::iterator> right; for(auto it=vertices.begin(); it != vertices.end(); ++it){ RS_Vector const& dir=*it - pl->getNearestPointOnEntity(*it, false); if(dir.squared()<RS_TOLERANCE15) continue; // std::cout<<"angle="<<remainder(dir.angle() - a0, 2.*M_PI)<<std::endl; if(remainder(dir.angle() - a0, 2.*M_PI) > 0.) left.push_back(it); else right.push_back(it); if(left.size()==2 && right.size()==1){ vertices.erase(right[0]); break; } else if(left.size()==1 && right.size()==2){ vertices.erase(left[0]); break; } } if(vertices.size()==4) break; } break; } //order vertices RS_Vector center{0., 0.}; for(const RS_Vector& vp: vertices) center += vp; center *= 0.25; std::sort(vertices.begin(), vertices.end(), [¢er](const RS_Vector& a, const RS_Vector&b)->bool{ return center.angleTo(a)<center.angleTo(b); } ); for(const RS_Vector& vp: vertices){ ret.push_back(vp); // std::cout<<"vp="<<vp<<std::endl; } return ret; }
/** * Rearranges the atomic entities in this container in a way that connected * entities are stored in the right order and direction. * Non-recoursive. Only affects atomic entities in this container. * * @retval true all contours were closed * @retval false at least one contour is not closed * to do: find closed contour by flood-fill */ bool RS_EntityContainer::optimizeContours() { // std::cout<<"RS_EntityContainer::optimizeContours: begin"<<std::endl; // DEBUG_HEADER // std::cout<<"loop with count()="<<count()<<std::endl; RS_DEBUG->print("RS_EntityContainer::optimizeContours"); RS_EntityContainer tmp; tmp.setAutoUpdateBorders(false); bool closed=true; /** accept all full circles **/ QList<RS_Entity*> enList; for(auto e1: entities){ if (!e1->isEdge() || e1->isContainer() ) { enList<<e1; continue; } //detect circles and whole ellipses switch(e1->rtti()){ case RS2::EntityEllipse: if(static_cast<RS_Ellipse*>(e1)->isEllipticArc()) continue; case RS2::EntityCircle: //directly detect circles, bug#3443277 tmp.addEntity(e1->clone()); enList<<e1; default: continue; } } // std::cout<<"RS_EntityContainer::optimizeContours: 1"<<std::endl; /** remove unsupported entities */ for(RS_Entity* it: enList) removeEntity(it); /** check and form a closed contour **/ // std::cout<<"RS_EntityContainer::optimizeContours: 2"<<std::endl; /** the first entity **/ RS_Entity* current(nullptr); if(count()>0) { current=entityAt(0)->clone(); tmp.addEntity(current); removeEntity(entityAt(0)); }else { if(tmp.count()==0) return false; } // std::cout<<"RS_EntityContainer::optimizeContours: 3"<<std::endl; RS_Vector vpStart; RS_Vector vpEnd; if(current){ vpStart=current->getStartpoint(); vpEnd=current->getEndpoint(); } RS_Entity* next(nullptr); // std::cout<<"RS_EntityContainer::optimizeContours: 4"<<std::endl; /** connect entities **/ const QString errMsg=QObject::tr("Hatch failed due to a gap=%1 between (%2, %3) and (%4, %5)"); while(count()>0){ double dist(0.); RS_Vector&& vpTmp=getNearestEndpoint(vpEnd,&dist,&next); if(dist>1e-8) { if(vpEnd.squaredTo(vpStart)<1e-8){ RS_Entity* e2=entityAt(0); tmp.addEntity(e2->clone()); vpStart=e2->getStartpoint(); vpEnd=e2->getEndpoint(); removeEntity(e2); continue; } QG_DIALOGFACTORY->commandMessage(errMsg.arg(dist).arg(vpTmp.x).arg(vpTmp.y).arg(vpEnd.x).arg(vpEnd.y)); closed=false; } if(next && closed){ //workaround if next is nullptr next->setProcessed(true); RS_Entity* eTmp = next->clone(); if(vpEnd.squaredTo(eTmp->getStartpoint())>vpEnd.squaredTo(eTmp->getEndpoint())) eTmp->revertDirection(); vpEnd=eTmp->getEndpoint(); tmp.addEntity(eTmp); removeEntity(next); } else { //workaround if next is nullptr // std::cout<<"RS_EntityContainer::optimizeContours: next is nullptr" <<std::endl; closed=false; //workaround if next is nullptr break; //workaround if next is nullptr } //workaround if next is nullptr } // DEBUG_HEADER if(vpEnd.valid && vpEnd.squaredTo(vpStart)>1e-8) { if(closed) QG_DIALOGFACTORY->commandMessage(errMsg.arg(vpEnd.distanceTo(vpStart)) .arg(vpStart.x).arg(vpStart.y).arg(vpEnd.x).arg(vpEnd.y)); closed=false; } // std::cout<<"RS_EntityContainer::optimizeContours: 5"<<std::endl; // add new sorted entities: for(auto en: tmp){ en->setProcessed(false); addEntity(en->clone()); } // std::cout<<"RS_EntityContainer::optimizeContours: 6"<<std::endl; RS_DEBUG->print("RS_EntityContainer::optimizeContours: OK"); // std::cout<<"RS_EntityContainer::optimizeContours: end: count()="<<count()<<std::endl; // std::cout<<"RS_EntityContainer::optimizeContours: closed="<<closed<<std::endl; return closed; }