Files
newspark110/actions/rs_actionpolylineequidistant.cpp
Chenwenxuan edac2715f0 init
2024-03-06 14:54:30 +08:00

381 lines
14 KiB
C++

/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
**
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file gpl-2.0.txt included in the
** packaging of this file.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**
** This copyright notice MUST APPEAR in all copies of the script!
**
**********************************************************************/
#include<cmath>
#include <QAction>
#include <QMouseEvent>
#include "rs_actionpolylineequidistant.h"
#include "rs_dialogfactory.h"
#include "rs_graphicview.h"
#include "rs_polyline.h"
#include "rs_information.h"
#include "rs_arc.h"
#include "rs_line.h"
#include "rs_debug.h"
RS_ActionPolylineEquidistant::RS_ActionPolylineEquidistant(RS_EntityContainer& container,
RS_GraphicView& graphicView)
:RS_PreviewActionInterface("Create Equidistant Polylines",
container, graphicView)
, targetPoint(new RS_Vector{})
,dist(1.)
,number(1)
{
actionType=RS2::ActionPolylineEquidistant;
}
RS_ActionPolylineEquidistant::~RS_ActionPolylineEquidistant()=default;
void RS_ActionPolylineEquidistant::init(int status) {
RS_PreviewActionInterface::init(status);
originalEntity = nullptr;
*targetPoint = {};
bRightSide = false;
}
/**
* Helper function for makeContour
* Modify newEntity to parellel of orgEntity at distance dist
* If dist is positive the offset is in left else in right
* Bot newEntity and orgEntity are the same type of entity
* if not return nullptr pointer
*
* @retval RS_Entity* of parellel entity
*
* @author Rallaz
*/
RS_Entity* RS_ActionPolylineEquidistant::calculateOffset(RS_Entity* newEntity,RS_Entity* orgEntity, double dist) {
if (orgEntity->rtti()==RS2::EntityArc && newEntity->rtti()==RS2::EntityArc) {
RS_Arc* arc = (RS_Arc*)newEntity;
double r0 = ((RS_Arc*)orgEntity)->getRadius();
double r;
if ( ((RS_Arc*)orgEntity)->isReversed())
r = r0 + dist;
else
r = r0 - dist;
if(r < 0)
return nullptr;
arc->setData(((RS_Arc*)orgEntity)->getData());
arc->setRadius(r);
arc->calculateBorders();
return newEntity;
} else if (orgEntity->rtti()==RS2::EntityLine && newEntity->rtti()==RS2::EntityLine) {
RS_Line* line0 = (RS_Line*)orgEntity;
RS_Line* line1 = (RS_Line*)newEntity;
RS_Vector v0 = line0->getStartpoint();
RS_Vector v1(v0.x,v0.y+dist);
RS_Vector v2(v0.x+line0->getLength(),v0.y+dist);
line1->setStartpoint(v1);
line1->setEndpoint(v2);
line1->rotate(v0, line0->getAngle1());
return newEntity;
}
return nullptr;
}
/**
* Helper function for makeContour
* Calculate the intersection point of first and last entities
* The first vertex is not added and the last is returned instead of added
*
* @retval RS_Vector nearest to startpoint of last and endpoint of first or RS_Vector(false) if not
*
* @author Rallaz
*/
RS_Vector RS_ActionPolylineEquidistant::calculateIntersection(RS_Entity* first,RS_Entity* last) {
RS_VectorSolutions vsol;
RS_Vector v(false);
vsol = RS_Information::getIntersection(first, last, false);
if (vsol.getNumber()==0) {
//Parallel entities
return RS_Vector(false);
} else if (vsol.getNumber()>1 &&
vsol.get(0).distanceTo(last->getStartpoint()) > vsol.get(1).distanceTo(last->getStartpoint())) {
return vsol.get(1);
}
return vsol.get(0);
}
bool RS_ActionPolylineEquidistant::makeContour() {
if (!container) {
RS_DEBUG->print(RS_Debug::D_WARNING,
"RS_ActionPolylineEquidistant::makeContour: no valid container");
return false;
}
RS_Polyline* originalPolyline = (RS_Polyline*)originalEntity;
//create a list of entities to offset without length = 0
QList<RS_Entity*> entities;
for(auto en: *originalPolyline){
if (en->getLength() > 1.0e-12)
entities.append(en);
}
if (entities.isEmpty()) {
return false;
}
if (document) {
document->startUndoCycle();
}
double neg = 1.0;
if(bRightSide)
neg = -1.0;
// Create new helper entities
RS_Vector const origin{0.,0.};
RS_Line line1{origin, origin};//current line
RS_Line lineFirst{origin, origin};//previous line
RS_Arc arc1(nullptr, RS_ArcData(origin, 0,0,0,false));//current arc
RS_Arc arcFirst(nullptr, RS_ArcData(origin, 0,0,0,false));//previous arc
for (int num=1; num<=number || (number==0 && num<=1); num++) {
RS_Polyline* newPolyline = new RS_Polyline(container);
newPolyline->setLayerToActive();
bool first = true;
bool closed = originalPolyline->isClosed();
double bulge = 0.0;
RS_Entity* en;
RS_Entity* prevEntity = entities.last();
RS_Entity* currEntity=nullptr;
for (int i = 0; i < entities.size(); ++i) {
en = entities.at(i);
RS_Vector v{false};
if (en->rtti()==RS2::EntityArc) {
currEntity = &arc1;
calculateOffset(currEntity, en, dist*num*neg);
bulge = arc1.getBulge();
} else {
currEntity = &line1;
bulge = 0.0;
calculateOffset(currEntity, en, dist*num*neg);
}
if (first) {
if (closed){
if (prevEntity->rtti()==RS2::EntityArc) {
prevEntity = calculateOffset(&arcFirst, prevEntity, dist*num*neg);
} else {
prevEntity = calculateOffset(&lineFirst, prevEntity, dist*num*neg);
}
v = calculateIntersection(prevEntity, currEntity);
}
if (!v.valid) {
v = currEntity->getStartpoint();
closed = false;
} else if (currEntity->rtti()==RS2::EntityArc) {
//update bulge
arc1.setAngle1(arc1.getCenter().angleTo(v));
arc1.calculateBorders();
bulge = arc1.getBulge();
}
first = false;
if (!prevEntity) break; //prevent crash if not exist offset for prevEntity
}else{
v = calculateIntersection(prevEntity, currEntity);
if (!v.valid) {
v= prevEntity->getEndpoint();
double dess = currEntity->getStartpoint().distanceTo(prevEntity->getEndpoint());
if (dess > 1.0e-12) {
newPolyline->addVertex(v, bulge);
prevEntity = nullptr;
break;
}
}
double startAngle = prevEntity->getStartpoint().angleTo(prevEntity->getEndpoint());
if (prevEntity->rtti()==RS2::EntityArc) {
arcFirst.setAngle2(arcFirst.getCenter().angleTo(v));
arcFirst.calculateBorders();
newPolyline->setNextBulge(arcFirst.getBulge());
}
//check if the entity are reverted
if (fabs(remainder(prevEntity->getStartpoint().angleTo(prevEntity->getEndpoint())- startAngle, 2.*M_PI)) > 0.785){
prevEntity = newPolyline->lastEntity();
RS_Vector v0 = calculateIntersection(prevEntity, currEntity);
if (prevEntity->rtti()==RS2::EntityArc) {
((RS_Arc*)prevEntity)->setAngle2(arcFirst.getCenter().angleTo(v0));
((RS_Arc*)prevEntity)->calculateBorders();
newPolyline->setNextBulge( ((RS_Arc*)prevEntity)->getBulge() );
} else {
((RS_Line*)prevEntity)->setEndpoint(v0);
newPolyline->setNextBulge( 0.0 );
}
newPolyline->setEndpoint(v0);
}
if (currEntity->rtti()==RS2::EntityArc) {
arc1.setAngle1(arc1.getCenter().angleTo(v));
arc1.calculateBorders();
bulge = arc1.getBulge();
} else
bulge = 0.0;
}
if (prevEntity) {
newPolyline->addVertex(v, bulge, false);
if (currEntity->rtti()==RS2::EntityArc){
arcFirst.setData(arc1.getData());
arcFirst.calculateBorders();
prevEntity = &arcFirst;
} else {
lineFirst.setStartpoint(line1.getStartpoint());
lineFirst.setEndpoint(line1.getEndpoint());
prevEntity = &lineFirst;
}
}
}
//properly terminated, check closed
if (prevEntity && currEntity) {
if (closed){
if (currEntity->rtti()==RS2::EntityArc) {
arc1.setAngle2(arc1.getCenter().angleTo(newPolyline->getStartpoint()));
arc1.calculateBorders();
newPolyline->setNextBulge(arc1.getBulge());
bulge = arc1.getBulge();
}
newPolyline->setClosed(true, bulge);
} else {
newPolyline->addVertex(currEntity->getEndpoint(), bulge);
}
}
if (!newPolyline->isEmpty()) {
container->addEntity(newPolyline);
if (document) document->addUndoable(newPolyline);
}
}
if (document) document->endUndoCycle();
if (graphicView) {
graphicView->redraw();
}
return true;
}
void RS_ActionPolylineEquidistant::trigger() {
RS_DEBUG->print("RS_ActionPolylineEquidistant::trigger()");
if (originalEntity && targetPoint->valid ) {
originalEntity->setHighlighted(false);
graphicView->drawEntity(originalEntity);
makeContour();
originalEntity = nullptr;
*targetPoint = {};
bRightSide = false;
setStatus(ChooseEntity);
RS_DIALOGFACTORY->updateSelectionWidget(container->countSelected(),container->totalSelectedLength());
}
////////////////////////////////////////2006/06/15
graphicView->redraw();
////////////////////////////////////////
}
void RS_ActionPolylineEquidistant::mouseReleaseEvent(QMouseEvent* e) {
if (e->button()==Qt::LeftButton) {
switch (getStatus()) {
case ChooseEntity:
originalEntity = catchEntity(e);
if (!originalEntity) {
RS_DIALOGFACTORY->commandMessage(tr("No Entity found."));
} else if (originalEntity->rtti()!=RS2::EntityPolyline) {
RS_DIALOGFACTORY->commandMessage(
tr("Entity must be a polyline."));
} else {
*targetPoint = snapFree(e);
originalEntity->setHighlighted(true);
graphicView->drawEntity(originalEntity);
double d = graphicView->toGraphDX(snapRange)*0.9;
RS_Entity* Segment = ((RS_Polyline*)originalEntity)->getNearestEntity( *targetPoint, &d, RS2::ResolveNone);
if (Segment->rtti() == RS2::EntityLine) {
double ang = ((RS_Line*)Segment)->getAngle1();
double ang1 = ((RS_Line*)Segment)->getStartpoint().angleTo(*targetPoint);
if( ang > ang1 || ang + M_PI < ang1 )
bRightSide = true;
} else {
RS_Vector cen = ((RS_Arc*)Segment)->getCenter();
if (cen.distanceTo(*targetPoint) > ((RS_Arc*)Segment)->getRadius() && ((RS_Arc*)Segment)->getBulge() > 0 )
bRightSide = true;
}
////////////////////////////////////////2006/06/15
graphicView->redraw();
////////////////////////////////////////
trigger();
}
break;
default:
break;
}
} else if (e->button()==Qt::RightButton) {
deleteSnapper();
if (originalEntity) {
originalEntity->setHighlighted(false);
graphicView->drawEntity(originalEntity);
////////////////////////////////////////2006/06/15
graphicView->redraw();
////////////////////////////////////////
}
init(getStatus()-1);
}
}
void RS_ActionPolylineEquidistant::showOptions() {
RS_ActionInterface::showOptions();
RS_DIALOGFACTORY->requestOptions(this, true);
}
void RS_ActionPolylineEquidistant::hideOptions() {
RS_ActionInterface::hideOptions();
RS_DIALOGFACTORY->requestOptions(this, false);
}
void RS_ActionPolylineEquidistant::updateMouseCursor() {
graphicView->setMouseCursor(RS2::SelectCursor);
}
void RS_ActionPolylineEquidistant::updateMouseButtonHints() {
switch (getStatus()) {
case ChooseEntity:
RS_DIALOGFACTORY->updateMouseWidget(tr("Choose the original polyline"),
tr("Cancel"));
break;
default:
RS_DIALOGFACTORY->updateMouseWidget();
break;
}
}
// EOF