| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | #include <QSet> |
| |
|
| | #include "lc_containertraverser.h" |
| | #include "lc_graphicviewport.h" |
| | #include "lc_linemath.h" |
| | #include "lc_splinepoints.h" |
| | #include "lc_undosection.h" |
| | #include "rs_arc.h" |
| | #include "rs_atomicentity.h" |
| | #include "rs_block.h" |
| | #include "rs_circle.h" |
| | #include "rs_clipboard.h" |
| | #include "rs_creation.h" |
| | #include "rs_debug.h" |
| | #include "rs_ellipse.h" |
| | #include "rs_graphic.h" |
| | #include "rs_information.h" |
| | #include "rs_insert.h" |
| | #include "rs_layer.h" |
| | #include "rs_line.h" |
| | #include "rs_math.h" |
| | #include "rs_modification.h" |
| | #include "rs_mtext.h" |
| | #include "rs_polyline.h" |
| | #include "rs_settings.h" |
| | #include "rs_text.h" |
| | #include "rs_units.h" |
| |
|
| | #ifdef EMU_C99 |
| | #include "emu_c99.h" |
| | #endif |
| |
|
| | class LC_SplinePoints; |
| |
|
| | namespace { |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | RS_Vector getPasteScale(const RS_PasteData &data, RS_Graphic *&source, const RS_Graphic &graphic){ |
| |
|
| | |
| | double factor = (RS_TOLERANCE < std::abs(data.factor)) ? data.factor : 1.0; |
| | |
| | if (source == nullptr){ |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::paste: add graphic source from clipboard"); |
| | source = RS_CLIPBOARD->getGraphic(); |
| | |
| | RS2::Unit sourceUnit = source->getUnit(); |
| | RS2::Unit targetUnit = graphic.getUnit(); |
| | factor = RS_Units::convert(factor, sourceUnit, targetUnit); |
| | } |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::paste: pasting scale factor: %g", factor); |
| | |
| | return {factor, factor}; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | RS_Block *addNewBlock(const QString &name, RS_Graphic &graphic){ |
| | RS_BlockData db = RS_BlockData(name, {0.0, 0.0}, false); |
| | auto *b = new RS_Block(&graphic, db); |
| | b->reparent(&graphic); |
| | graphic.addBlock(b); |
| | return b; |
| | } |
| |
|
| | RS_VectorSolutions findIntersection(const RS_Entity &trimEntity, const RS_Entity &limitEntity, double tolerance = 1e-4){ |
| |
|
| | RS_VectorSolutions sol; |
| | if (limitEntity.isAtomic()){ |
| | |
| | return RS_Information::getIntersection(&trimEntity, &limitEntity, false); |
| | } |
| | if (limitEntity.isContainer()){ |
| | auto ec = static_cast<const RS_EntityContainer *>(&limitEntity); |
| |
|
| | for(RS_Entity* e: lc::LC_ContainerTraverser{*ec, RS2::ResolveAll}.entities()) { |
| | RS_VectorSolutions s2 = RS_Information::getIntersection(&trimEntity, |
| | e, false); |
| |
|
| | std::copy_if(s2.begin(), s2.end(), std::back_inserter(sol), [e, tolerance](const RS_Vector &vp){ |
| | return vp.valid && e->isPointOnEntity(vp, tolerance); |
| | }); |
| | } |
| | } |
| | return sol; |
| | } |
| |
|
| | RS_Arc *trimCircle(RS_Circle& circle, const RS_Vector &trimCoord, const RS_VectorSolutions &sol){ |
| | double aStart = 0.; |
| | double aEnd = 2. * M_PI; |
| | switch (sol.size()) { |
| | case 0: |
| | break; |
| | case 1: |
| | aStart = circle.getCenter().angleTo(sol.at(0)); |
| | aEnd = aStart + 2. * M_PI; |
| | break; |
| | default: |
| | case 2: |
| | |
| | const RS_Vector& center0 = circle.getCenter(); |
| | std::vector<double> angles { {center0.angleTo(sol[0]), center0.angleTo(sol[1])}}; |
| | const double a0 = center0.angleTo(trimCoord); |
| | aStart = angles.front(); |
| | aEnd = angles.back(); |
| | if (!RS_Math::isAngleBetween(a0, aStart, aEnd, false)) { |
| | std::swap(aStart, aEnd); |
| | } |
| | break; |
| | } |
| | RS_ArcData arcData(circle.getCenter(), |
| | circle.getRadius(), |
| | aStart, |
| | aEnd, |
| | false); |
| | return new RS_Arc(circle.getParent(), arcData); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | std::string getIdFlagString(RS_Entity *entity){ |
| | if (entity == nullptr) return {}; |
| | return std::to_string(entity->getId()) + "/" + std::to_string(entity->rtti()); |
| | } |
| |
|
| | |
| | RS_AtomicEntity *trimEllipseForRound(RS_AtomicEntity *entity, const RS_Arc &arcFillet){ |
| | if (entity == nullptr) { |
| | return entity; |
| | } |
| | if (entity->rtti() != RS2::EntityEllipse) { |
| | return entity; |
| | } |
| | auto* ellipse = static_cast<RS_Ellipse *>(entity); |
| | if (ellipse->isEllipticArc()) { |
| | return entity; |
| | } |
| | RS_Vector tangent = entity->getNearestPointOnEntity(arcFillet.getCenter(), false); |
| | RS_Line line{nullptr, {arcFillet.getCenter(), tangent}}; |
| | RS_Vector middle = arcFillet.getMiddlePoint(); |
| | RS_Vector opposite = arcFillet.getCenter() + (arcFillet.getCenter() - middle).normalized() * ellipse->getMinorRadius() * 0.01; |
| | RS_Vector trimCoord = ellipse->getNearestPointOnEntity(opposite, false); |
| | RS_VectorSolutions sol = RS_Information::getIntersection(entity, &line, false); |
| | ellipse->prepareTrim(trimCoord, sol); |
| | return entity; |
| | } |
| |
|
| | |
| | RS_AtomicEntity *trimCircleForRound(RS_AtomicEntity *entity, const RS_Arc &arcFillet){ |
| | if (entity == nullptr) { |
| | return entity; |
| | } |
| | if (entity->rtti() == RS2::EntityEllipse) { |
| | return trimEllipseForRound(entity, arcFillet); |
| | } |
| | if (entity->rtti() != RS2::EntityCircle) { |
| | return entity; |
| | } |
| | RS_Line line{nullptr, {arcFillet.getCenter(), entity->getCenter()}}; |
| | RS_Vector middle = arcFillet.getMiddlePoint(); |
| | |
| | |
| | RS_Vector opposite = arcFillet.getCenter() + (arcFillet.getCenter() - middle).normalized() * entity->getRadius() * 0.01; |
| | RS_Vector trimCoord = entity->getNearestPointOnEntity(opposite, true); |
| | RS_VectorSolutions sol = RS_Information::getIntersection(entity, &line, false); |
| | RS_Arc *arc = trimCircle(*static_cast<RS_Circle *>(entity), trimCoord, sol); |
| | delete entity; |
| | return arc; |
| | } |
| |
|
| | inline bool isOneOfPoints(const RS_Vector& candidate, const RS_Vector& point1, const RS_Vector& point2) { |
| | bool result = point1.distanceTo(candidate) < RS_TOLERANCE || point2.distanceTo(candidate) < RS_TOLERANCE; |
| | return result; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | QString getUniqueBlockName(RS_Graphic* graphic, const QString& baseName = QStringLiteral("PASTE")) { |
| | if (!graphic) return baseName; |
| | RS_BlockList* bl = graphic->getBlockList(); |
| | if (!bl) return baseName; |
| | int i = 0; |
| | QString candidate; |
| | do { |
| | candidate = QString("%1_%2").arg(baseName).arg(i++); |
| | } while (bl->find(candidate) != nullptr); |
| | return candidate; |
| | } |
| | } |
| |
|
| |
|
| | RS_PasteData::RS_PasteData(RS_Vector _insertionPoint, |
| | double _factor, |
| | double _angle, |
| | bool _asInsert, |
| | const QString& _blockName): |
| | insertionPoint(_insertionPoint) |
| | ,factor(_factor) |
| | ,angle(_angle) |
| | ,asInsert(_asInsert) |
| | ,blockName(_blockName) |
| | { |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | RS_Modification::RS_Modification(RS_EntityContainer& container, |
| | LC_GraphicViewport* vp, |
| | bool handleUndo) { |
| | this->container = &container; |
| | this->handleUndo = handleUndo; |
| | viewport = vp; |
| | graphic = container.getGraphic(); |
| | document = container.getDocument(); |
| | } |
| |
|
| | |
| | |
| | |
| | void RS_Modification::remove() { |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::remove"); |
| |
|
| | if (container == nullptr) { |
| | RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Modification::remove: no valid container"); |
| | return; |
| | } |
| |
|
| | std::vector<RS_Entity*> selectedEntities; |
| | container->collectSelected(selectedEntities, false); |
| | if (!selectedEntities.empty()) { |
| | remove(selectedEntities); |
| | } |
| | else{ |
| | RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Modification::remove: no valid container is selected"); |
| | } |
| | } |
| |
|
| | void RS_Modification::remove(const std::vector<RS_Entity*> &entitiesList){ |
| | LC_UndoSection undo(document,viewport); |
| |
|
| | for(auto e: entitiesList) { |
| | e->setSelected(false); |
| | e->changeUndoState(); |
| | undo.addUndoable(e); |
| | } |
| |
|
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::remove: OK"); |
| | } |
| |
|
| | |
| | |
| | |
| | void RS_Modification::revertDirection(bool keepSelected) { |
| |
|
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::revertDirection"); |
| |
|
| | if (!container) { |
| | RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Modification::revertDirection: no valid container"); |
| | return; |
| | } |
| |
|
| | std::vector<RS_Entity*> selectedEntities; |
| | container->collectSelected(selectedEntities, false); |
| | if (!selectedEntities.empty()) { |
| | revertDirection(selectedEntities, keepSelected); |
| | } |
| | } |
| |
|
| | void RS_Modification::revertDirection(const std::vector<RS_Entity*> &entitiesList, [[maybe_unused]]bool keepSelected){ |
| | std::vector<RS_Entity*> clonesList; |
| | for(auto e: entitiesList) { |
| | RS_Entity* ec = e->clone(); |
| | ec->revertDirection(); |
| | clonesList.push_back(ec); |
| | } |
| |
|
| | deleteOriginalAndAddNewEntities(clonesList, entitiesList, false, true); |
| | clonesList.clear(); |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::revertDirection: OK"); |
| | } |
| |
|
| | |
| | |
| | |
| | bool RS_Modification::changeAttributes(RS_AttributesData& data, const bool keepSelected){ |
| | return changeAttributes(data, container, keepSelected); |
| | } |
| |
|
| | bool RS_Modification::changeAttributes( |
| | RS_AttributesData& data, |
| | RS_EntityContainer* cont, |
| | const bool keepSelected) { |
| |
|
| | if (cont == nullptr) { |
| | return false; |
| | } |
| | std::vector<RS_Entity *> selectedEntities; |
| | container->collectSelected(selectedEntities, false); |
| | return changeAttributes(data, selectedEntities, cont, keepSelected); |
| | } |
| |
|
| | bool RS_Modification::changeAttributes(RS_AttributesData& data, const std::vector<RS_Entity*> &entitiesList, RS_EntityContainer *cont, bool keepSelected){ |
| | LC_UndoSection undo(document, viewport); |
| | QList<RS_Entity*> clones; |
| | QSet<RS_Block*> blocks; |
| |
|
| | for (auto en: entitiesList) { |
| | if (data.applyBlockDeep && en->rtti() == RS2::EntityInsert) { |
| | RS_Block *bl = dynamic_cast<RS_Insert *>(en)->getBlockForInsert(); |
| | blocks << bl; |
| | } |
| |
|
| | RS_Entity *cl = en->clone(); |
| | RS_Pen pen = cl->getPen(false); |
| | if (data.changeLayer) { |
| | cl->setLayer(data.layer); |
| | } |
| | if (data.changeColor) { |
| | pen.setColor(data.pen.getColor()); |
| | } |
| | if (data.changeLineType) { |
| | pen.setLineType(data.pen.getLineType()); |
| | } |
| | if (data.changeWidth) { |
| | pen.setWidth(data.pen.getWidth()); |
| | } |
| | cl->setPen(pen); |
| |
|
| | en->setSelected(false); |
| | cl->setSelected(keepSelected); |
| |
|
| | clones << cl; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | en->setUndoState(true); |
| | undo.addUndoable(en); |
| |
|
| | } |
| |
|
| | for (const auto& block: std::as_const(blocks)) { |
| | for (auto en: *block) { |
| | if (en != nullptr) |
| | en->setSelected(true); |
| | } |
| | changeAttributes(data, block, keepSelected); |
| | } |
| |
|
| | for (const auto& cl: std::as_const(clones)) { |
| | RS2::EntityType rtti = cl->rtti(); |
| | if (RS2::isDimensionalEntity(rtti)) { |
| | cl->update(); |
| | } |
| |
|
| | cont->addEntity(cl); |
| | |
| | |
| | |
| | undo.addUndoable(cl); |
| | } |
| |
|
| | if (graphic != nullptr) { |
| | graphic->updateInserts(); |
| | } |
| |
|
| | cont->calculateBorders(); |
| | viewport->notifyChanged(); |
| | return true; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | void RS_Modification::copy(const RS_Vector& ref, const bool cut) { |
| |
|
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::copy"); |
| |
|
| | if (!container) { |
| | RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Modification::copy: no valid container"); |
| | return; |
| | } |
| |
|
| | RS_CLIPBOARD->clear(); |
| | if (graphic) { |
| | RS_CLIPBOARD->getGraphic()->setUnit(graphic->getUnit()); |
| | } else { |
| | RS_CLIPBOARD->getGraphic()->setUnit(RS2::None); |
| | } |
| |
|
| | |
| | LC_UndoSection undo( document, viewport, cut && handleUndo); |
| |
|
| | bool selectedEntityFound{false}; |
| | std::vector<RS_Entity *> selected; |
| | collectSelectedEntities(selected); |
| |
|
| | selectedEntityFound = !selected.empty(); |
| | if (selectedEntityFound) { |
| | RS_Vector refPoint; |
| |
|
| | if (ref.valid) { |
| | refPoint = ref; |
| | } else { |
| | RS_BoundData bound = getBoundingRect(selected); |
| | refPoint = bound.getCenter(); |
| | } |
| |
|
| | for (auto e: selected) { |
| | copyEntity(e, refPoint, cut); |
| | } |
| | selected.clear(); |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::copy: OK"); |
| | } |
| | else{ |
| | RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Modification::copy: no valid container is selected"); |
| | } |
| | } |
| |
|
| | void RS_Modification::collectSelectedEntities(std::vector<RS_Entity *> &selected) const{ |
| | for (auto e: *container) { |
| | if (e != nullptr && e->isSelected()) { |
| | selected.push_back(e); |
| | } |
| | } |
| | } |
| |
|
| | RS_BoundData RS_Modification::getBoundingRect(std::vector<RS_Entity *> &selected) { |
| | RS_Vector min = RS_Vector(10e10, 10e10,0); |
| | RS_Vector max = RS_Vector(-10e10, -10e10,0); |
| | for (auto e: selected) { |
| | const RS_Vector &entityMin = e->getMin(); |
| | const RS_Vector &entityMax = e->getMax(); |
| |
|
| | min.x = std::min(min.x, entityMin.x); |
| | min.y = std::min(min.y, entityMin.y); |
| | max.x = std::max(max.x, entityMax.x); |
| | max.y = std::max(max.y, entityMax.y); |
| | } |
| |
|
| | RS_BoundData result(min, max); |
| | return result; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | void RS_Modification::copyEntity(RS_Entity* e, const RS_Vector& ref, const bool cut) { |
| |
|
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::copyEntity"); |
| |
|
| | if (!e || !e->isSelected()) { |
| | RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Modification::copyEntity: no entity is selected"); |
| | return; |
| | } |
| |
|
| | |
| | if (e->rtti() == RS2::EntityInsert) { |
| | dynamic_cast<RS_Insert*>(e)->update(); |
| | } |
| |
|
| | |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::copyEntity: to clipboard: %s", getIdFlagString(e).c_str()); |
| | RS_Entity* c = e->clone(); |
| |
|
| | c->move(-ref); |
| |
|
| | |
| | |
| | |
| | |
| | bool isBlock = c->rtti() == RS2::EntityInsert; |
| | double angle = isBlock ? dynamic_cast<RS_Insert*>(c)->getAngle() : 0.; |
| | |
| | |
| | if (isBlock && std::abs(std::remainder(angle, 2. * M_PI)) > RS_TOLERANCE_ANGLE) |
| | { |
| | auto* insert = dynamic_cast<RS_Insert*>(c); |
| | |
| | insert->setAngle(0.); |
| | } |
| |
|
| | RS_CLIPBOARD->addEntity(c); |
| | copyLayers(e); |
| | copyBlocks(e); |
| |
|
| | |
| | auto originalLayer = e->getLayer(); |
| | |
| | if (originalLayer != nullptr) { |
| | c->setLayer(e->getLayer()->getName()); |
| | } |
| |
|
| |
|
| | if (cut) { |
| | LC_UndoSection undo(document, viewport); |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::copyEntity: cut ID/flag: %s", getIdFlagString(e).c_str()); |
| | e->changeUndoState(); |
| | undo.addUndoable(e); |
| |
|
| | e->setSelected(false); |
| | } else { |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::copyEntity: delete in view ID/flag: %s", getIdFlagString(e).c_str()); |
| | |
| | e->setSelected(false); |
| | } |
| |
|
| | viewport->notifyChanged(); |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::copyEntity: OK"); |
| | } |
| |
|
| | |
| | |
| | |
| | void RS_Modification::copyLayers(RS_Entity* e) { |
| |
|
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::copyLayers"); |
| |
|
| | if (!e) { |
| | RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Modification::copyLayers: no entity is selected"); |
| | return; |
| | } |
| |
|
| | |
| | RS_Layer* l = e->getLayer(); |
| | if (!l) { |
| | RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Modification::copyLayers: no valid layer found"); |
| | return; |
| | } |
| |
|
| | if (!RS_CLIPBOARD->hasLayer(l->getName())) { |
| | RS_CLIPBOARD->addLayer(l->clone()); |
| | } |
| |
|
| | |
| | if (e->rtti()==RS2::EntityInsert) { |
| | |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::copyLayers: copy insert entity ID/flag layers: %s", getIdFlagString(e).c_str()); |
| | RS_Block* b = ((RS_Insert*)e)->getBlockForInsert(); |
| | if (!b) { |
| | RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Modification::copyLayers: could not find block for insert entity"); |
| | return; |
| | } |
| | for(auto e2: *b) { |
| | copyLayers(e2); |
| | } |
| | } else { |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::copyLayers: skip noninsert entity"); |
| | } |
| |
|
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::copyLayers: OK"); |
| | } |
| |
|
| |
|
| |
|
| | |
| | |
| | |
| | void RS_Modification::copyBlocks(RS_Entity* e) { |
| |
|
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::copyBlocks"); |
| |
|
| | if (!e) { |
| | RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Modification::copyBlocks: no entity to process"); |
| | return; |
| | } |
| |
|
| | |
| | if (e->rtti()!=RS2::EntityInsert) { |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::copyBlocks: skip non-insert entity"); |
| | return; |
| | } |
| |
|
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::copyBlocks: get insert entity ID/flag block: %s", getIdFlagString(e).c_str()); |
| | RS_Block* b = ((RS_Insert*)e)->getBlockForInsert(); |
| | if (!b) { |
| | RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Modification::copyBlocks: could not find block for insert entity"); |
| | return; |
| | } |
| | |
| | QString bn = b->getName(); |
| | if (!RS_CLIPBOARD->hasBlock(bn)) { |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::copyBlocks: add block name: %s", bn.toLatin1().data()); |
| | RS_CLIPBOARD->addBlock((RS_Block*)b->clone()); |
| | } |
| | |
| | for(auto e2: *b) { |
| | |
| | if (e2->rtti()==RS2::EntityInsert) { |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::copyBlocks: process insert-into-insert blocks for %s", getIdFlagString(e).c_str()); |
| | copyBlocks(e2); |
| | } |
| | } |
| |
|
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::copyBlocks: OK"); |
| | } |
| |
|
| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | void RS_Modification::paste(const RS_PasteData& data, RS_Graphic* source) { |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::paste:"); |
| |
|
| | if (container == nullptr || container->isLocked() || !container->isVisible()) { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Modification::paste: invalid container"); |
| | return; |
| | } |
| |
|
| | RS_Graphic* src = (source != nullptr) ? source : RS_CLIPBOARD->getGraphic(); |
| | if (src == nullptr) { |
| | RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Modification::paste: no source"); |
| | return; |
| | } |
| |
|
| | |
| | RS_Graphic* srcRef = source; |
| | RS_Vector scaleV = getPasteScale(data, srcRef, *graphic); |
| | src = srcRef; |
| |
|
| | src->calculateBorders(); |
| | RS_Vector center = (src->getMin() + src->getMax()) * 0.5; |
| | RS_Vector offset = data.insertionPoint - center; |
| |
|
| | LC_UndoSection undo(document, viewport, handleUndo); |
| |
|
| | if (data.asInsert) { |
| | |
| | QString bname = data.blockName.isEmpty() ? getUniqueBlockName(graphic) : data.blockName; |
| | RS_Block* block = addNewBlock(bname, *graphic); |
| |
|
| | auto entities = lc::LC_ContainerTraverser{*src, RS2::ResolveAll}.entities(); |
| | for (RS_Entity* e : entities) { |
| | if (e == nullptr || e->isUndone()) continue; |
| | RS_Entity* clone = e->clone(); |
| | |
| | clone->move(-center); |
| | clone->scale(RS_Vector{}, scaleV); |
| | clone->rotate(RS_Vector{}, data.angle); |
| | block->addByBlockEntity(clone); |
| | } |
| |
|
| | |
| | RS_InsertData idata(bname, data.insertionPoint, {1., 1.}, 0.0, 1, 1, {}); |
| | RS_Insert* insert = new RS_Insert(container, idata); |
| | insert->reparent(container); |
| | container->addEntity(insert); |
| |
|
| | |
| | RS_Entity* first = src->firstEntity(RS2::ResolveNone); |
| | if (first) { |
| | insert->setLayer(first->getLayer()); |
| | insert->setPen(first->getPen(true)); |
| | } |
| | insert->setSelected(true); |
| | insert->update(); |
| |
|
| | undo.addUndoable(block); |
| | undo.addUndoable(insert); |
| |
|
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "paste: block '%s'", bname.toLatin1().data()); |
| |
|
| | } else { |
| | |
| | std::vector<RS_Entity*> newEnts; |
| | auto entities = lc::LC_ContainerTraverser{*src, RS2::ResolveAll}.entities(); |
| | for (RS_Entity* e : entities) { |
| | if (e == nullptr || e->isUndone()) continue; |
| | RS_Entity* clone = e->clone(); |
| | |
| | clone->scale(center, scaleV); |
| | clone->rotate(center, data.angle); |
| | clone->move(offset + center); |
| | clone->setSelected(true); |
| | newEnts.push_back(clone); |
| | } |
| | |
| | for (RS_Entity* ne : newEnts) { |
| | ne->reparent(container); |
| | container->addEntity(ne); |
| | undo.addUndoable(ne); |
| | } |
| | } |
| |
|
| | graphic->updateInserts(); |
| | viewport->notifyChanged(); |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "paste: OK ✅"); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | bool RS_Modification::pasteLayers(RS_Graphic* source) { |
| |
|
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::pasteLayers"); |
| |
|
| | if (!source) { |
| | RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Modification::pasteLayers: no valid graphic found"); |
| | return false; |
| | } |
| |
|
| | RS_LayerList* lrs=source->getLayerList(); |
| | for(RS_Layer* l: *lrs) { |
| |
|
| | if(!l) { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Modification::pasteLayers: nullptr layer in source"); |
| | continue; |
| | } |
| |
|
| | |
| | QString ln = l->getName(); |
| | if (!graphic->findLayer(ln)) { |
| | graphic->addLayer(l->clone()); |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::pasteLayers: layer added: %s", ln.toLatin1().data()); |
| | } |
| | } |
| |
|
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::pasteLayers: OK"); |
| | return true; |
| | } |
| |
|
| |
|
| |
|
| | |
| | |
| | |
| | |
| | bool RS_Modification::pasteContainer(RS_Entity* entity, RS_EntityContainer* containerToPaste, QHash<QString, QString>blocksDict, RS_Vector insertionPoint) { |
| |
|
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::pasteInsert"); |
| |
|
| | auto* insert = dynamic_cast<RS_Insert*>(entity); |
| | if (insert == nullptr) { |
| | RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Modification::pasteInsert: no container to process"); |
| | return false; |
| | } |
| |
|
| | |
| | RS_Block* insertBlock = insert->getBlockForInsert(); |
| | if (insertBlock == nullptr) { |
| | RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Modification::pasteInsert: no block to process"); |
| | return false; |
| | } |
| | |
| | QString name_old = insertBlock->getName(); |
| | QString name_new = name_old; |
| | if (name_old != insert->getName()) { |
| | RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Modification::pasteInsert: block and insert names don't coincide"); |
| | return false; |
| | } |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::pasteInsert: processing container: %s", name_old.toLatin1().data()); |
| | |
| | if (graphic->findBlock(name_old)) { |
| | if (insertBlock->getParent() == graphic) { |
| | |
| | pasteEntity(entity, container); |
| | return true; |
| | } else { |
| | name_new = graphic->getBlockList()->newName(name_old); |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::pasteInsert: new block name: %s", name_new.toLatin1().data()); |
| | } |
| | } |
| | blocksDict[name_old] = name_new; |
| | |
| | RS_Block* blockClone = addNewBlock(name_new, *graphic); |
| | |
| | RS_InsertData di = RS_InsertData(name_new, insertionPoint, RS_Vector(1.0, 1.0), 0.0, 1, 1, RS_Vector(0.0,0.0)); |
| | auto* insertClone = new RS_Insert(containerToPaste, di); |
| | insertClone->reparent(containerToPaste); |
| | containerToPaste->addEntity(insertClone); |
| |
|
| | |
| | QString ln = entity->getLayer()->getName(); |
| | RS_Layer* layer = graphic->getLayerList()->find(ln); |
| | if (!layer) { |
| | RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Modification::pasteInsert: unable to select layer to paste in"); |
| | return false; |
| | } |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::pasteInsert: selected layer: %s", layer->getName().toLatin1().data()); |
| | insertClone->setLayer(layer); |
| | insertClone->setPen(entity->getPen(false)); |
| |
|
| | |
| | RS_Vector ip{0.0, 0.0}; |
| | if (containerToPaste->getId() != graphic->getId()) { |
| | ip = blockClone->getBasePoint(); |
| | } |
| |
|
| | |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::pasteInsert: copy content to the subcontainer"); |
| | for(auto* e: *insert) { |
| |
|
| | if(!e) { |
| | RS_DEBUG->print(RS_Debug::D_NOTICE, "RS_Modification::pasteInsert: nullptr entity in block"); |
| | continue; |
| | } |
| |
|
| | if (e->rtti() == RS2::EntityInsert) { |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::pasteInsert: process sub-insert for %s", ((RS_Insert*)e)->getName().toLatin1().data()); |
| | if (!pasteContainer(e, blockClone, blocksDict, ip)) { |
| | RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Modification::pasteInsert: unable to paste entity to sub-insert"); |
| | return false; |
| | } |
| | } else { |
| | if (!pasteEntity(e, blockClone)) { |
| | RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Modification::pasteInsert: unable to paste entity"); |
| | return false; |
| | } |
| | } |
| | } |
| |
|
| | insertClone->update(); |
| | insertClone->setSelected(false); |
| |
|
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::pasteInsert: OK"); |
| | return true; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | bool RS_Modification::pasteEntity(RS_Entity* entity, RS_EntityContainer* containerToPaste) { |
| |
|
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::pasteEntity"); |
| |
|
| | if (!entity) { |
| | RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Modification::pasteEntity: no entity to process"); |
| | return false; |
| | } |
| |
|
| | |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::pasteEntity ID/flag: %s", getIdFlagString(entity).c_str()); |
| | RS_Entity* e = entity->clone(); |
| |
|
| | |
| | QString ln = entity->getLayer()->getName(); |
| | RS_Layer* layer = graphic->getLayerList()->find(ln); |
| | if (!layer) { |
| | RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Modification::pasteInsert: unable to select layer to paste in"); |
| | return false; |
| | } |
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::pasteInsert: selected layer: %s", layer->getName().toLatin1().data()); |
| | e->setLayer(layer); |
| | e->setPen(entity->getPen(false)); |
| |
|
| | |
| | |
| | e->reparent(containerToPaste); |
| | containerToPaste->addEntity(e); |
| | e->setSelected(false); |
| |
|
| | RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Modification::pasteEntity: OK"); |
| | return true; |
| | } |
| |
|
| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | bool RS_Modification::splitPolyline(RS_Polyline& polyline, |
| | RS_Entity& e1, RS_Vector v1, |
| | RS_Entity& e2, RS_Vector v2, |
| | RS_Polyline** polyline1, |
| | RS_Polyline** polyline2) const { |
| |
|
| | if (!container) { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::splitPolyline: no valid container"); |
| | return false; |
| | } |
| |
|
| | RS_Entity* firstEntity = polyline.firstEntity(); |
| | RS_Vector firstPoint(false); |
| | if (firstEntity->rtti()==RS2::EntityLine) { |
| | firstPoint = ((RS_Line*)firstEntity)->getStartpoint(); |
| | } |
| | auto* pl1 = new RS_Polyline(container, |
| | RS_PolylineData(firstPoint, RS_Vector(0.0,0.0), 0)); |
| | auto* pl2 = new RS_Polyline(container); |
| | RS_Polyline* pl = pl1; |
| | RS_Line* line = nullptr; |
| | [[maybe_unused]] RS_Arc* arc = nullptr; |
| |
|
| | if (polyline1) { |
| | *polyline1 = pl1; |
| | } |
| | if (polyline2) { |
| | *polyline2 = pl2; |
| | } |
| |
|
| | for(auto e: polyline){ |
| |
|
| | if (e->rtti()==RS2::EntityLine) { |
| | line = (RS_Line*)e; |
| | arc = nullptr; |
| | } else if (e->rtti()==RS2::EntityArc) { |
| | arc = (RS_Arc*)e; |
| | line = nullptr; |
| | } else { |
| | line = nullptr; |
| | arc = nullptr; |
| | } |
| |
|
| | if (line ) { |
| |
|
| | if (e==&e1 && e==&e2) { |
| | |
| | RS_Vector sp = line->getStartpoint(); |
| | double dist1 = (v1-sp).magnitude(); |
| | double dist2 = (v2-sp).magnitude(); |
| | pl->addVertex(dist1<dist2 ? v1 : v2, 0.0); |
| | pl = pl2; |
| | pl->setStartpoint(dist1<dist2 ? v2 : v1); |
| | pl->addVertex(line->getEndpoint(), 0.0); |
| | } else if (e==&e1 || e==&e2) { |
| | |
| | RS_Vector v = (e==&e1 ? v1 : v2); |
| | if (pl==pl1) { |
| | |
| | pl->addVertex(v, 0.0); |
| | |
| | } else { |
| | |
| | pl = pl2; |
| | pl->setStartpoint(v); |
| | pl->addVertex(line->getEndpoint(), 0.0); |
| | } |
| | } else { |
| | |
| | if (line && pl) { |
| | pl->addVertex(line->getEndpoint(), 0.0); |
| | } |
| | } |
| | } |
| | } |
| |
|
| | container->addEntity(pl1); |
| | container->addEntity(pl2); |
| | |
| | polyline.changeUndoState(); |
| |
|
| | return true; |
| | } |
| |
|
| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | RS_Polyline* RS_Modification::addPolylineNode(RS_Polyline& polyline, |
| | const RS_AtomicEntity& segment, |
| | const RS_Vector& node) { |
| | RS_DEBUG->print("RS_Modification::addPolylineNode"); |
| |
|
| | if (!container) { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::addPolylineNode: no valid container"); |
| | return nullptr; |
| | } |
| |
|
| | if (segment.getParent()!=&polyline) { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::addPolylineNode: segment not part of the polyline"); |
| | return nullptr; |
| | } |
| |
|
| | auto newPolyline = new RS_Polyline(container); |
| | newPolyline->setClosed(polyline.isClosed()); |
| | newPolyline->setSelected(polyline.isSelected()); |
| | newPolyline->setLayer(polyline.getLayer()); |
| | newPolyline->setPen(polyline.getPen(false)); |
| |
|
| | |
| | bool first = true; |
| | RS_Entity* lastEntity = polyline.lastEntity(); |
| | for(auto e: polyline){ |
| | if (e->isAtomic()) { |
| | auto ae = (RS_AtomicEntity*)e; |
| | double bulge = 0.0; |
| | if (ae->rtti()==RS2::EntityArc) { |
| | RS_DEBUG->print("RS_Modification::addPolylineNode: arc segment"); |
| | bulge = ((RS_Arc*)ae)->getBulge(); |
| | } else { |
| | RS_DEBUG->print("RS_Modification::addPolylineNode: line segment"); |
| | bulge = 0.0; |
| | } |
| |
|
| | if (first) { |
| | RS_DEBUG->print("RS_Modification::addPolylineNode: first segment: %f/%f", |
| | ae->getStartpoint().x, ae->getStartpoint().y); |
| |
|
| | newPolyline->setNextBulge(bulge); |
| | newPolyline->addVertex(ae->getStartpoint()); |
| | first = false; |
| | } |
| |
|
| | |
| | if (ae==&segment) { |
| | RS_DEBUG->print("RS_Modification::addPolylineNode: split segment found"); |
| |
|
| | RS_DEBUG->print("RS_Modification::addPolylineNode: node: %f/%f", |
| | node.x, node.y); |
| |
|
| | newPolyline->setNextBulge(0.0); |
| | newPolyline->addVertex(node); |
| |
|
| | RS_DEBUG->print("RS_Modification::addPolylineNode: after node: %f/%f", |
| | ae->getEndpoint().x, ae->getEndpoint().y); |
| |
|
| | if (ae!=lastEntity || !polyline.isClosed()) { |
| | newPolyline->setNextBulge(0.0); |
| | newPolyline->addVertex(ae->getEndpoint()); |
| | } |
| | } else { |
| | RS_DEBUG->print("RS_Modification::addPolylineNode: normal vertex found: %f/%f", |
| | ae->getEndpoint().x, ae->getEndpoint().y); |
| |
|
| | if (ae!=lastEntity || !polyline.isClosed()) { |
| | newPolyline->setNextBulge(bulge); |
| | newPolyline->addVertex(ae->getEndpoint()); |
| | } |
| | } |
| | } else { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::addPolylineNode: Polyline contains non-atomic entities"); |
| | } |
| | } |
| |
|
| | newPolyline->setNextBulge(polyline.getClosingBulge()); |
| | newPolyline->endPolyline(); |
| |
|
| | |
| | container->addEntity(newPolyline); |
| |
|
| | if (handleUndo) { |
| | LC_UndoSection undo( document,viewport); |
| |
|
| | polyline.setUndoState(true); |
| | undo.addUndoable(&polyline); |
| | undo.addUndoable(newPolyline); |
| | } |
| |
|
| | viewport->notifyChanged(); |
| |
|
| | return newPolyline; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | RS_Polyline* RS_Modification::deletePolylineNode(RS_Polyline& polyline, |
| | const RS_Vector& node, bool createOnly) { |
| |
|
| | RS_DEBUG->print("RS_Modification::deletePolylineNode"); |
| |
|
| | if (!container){ |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::addPolylineNode: no valid container"); |
| | return nullptr; |
| | } |
| |
|
| | if (!node.valid){ |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::deletePolylineNode: node not valid"); |
| | return nullptr; |
| | } |
| |
|
| | |
| | if (polyline.count() == 1){ |
| | RS_Entity *e = polyline.firstEntity(); |
| | if (e && e->isAtomic()){ |
| | auto ae = dynamic_cast<RS_AtomicEntity *>(e); |
| | if (node.distanceTo(ae->getStartpoint()) < 1.0e-6 || |
| | node.distanceTo(ae->getEndpoint()) < 1.0e-6){ |
| |
|
| | if (handleUndo){ |
| | LC_UndoSection undo(document,viewport); |
| | polyline.setUndoState(true); |
| | undo.addUndoable(&polyline); |
| | } |
| | } |
| | } |
| | return nullptr; |
| | } |
| |
|
| | auto* newPolyline = new RS_Polyline(container); |
| | newPolyline->setClosed(polyline.isClosed()); |
| | if (!createOnly){ |
| | newPolyline->setSelected(polyline.isSelected()); |
| | newPolyline->setLayer(polyline.getLayer()); |
| | newPolyline->setPen(polyline.getPen(false)); |
| | } |
| |
|
| | |
| | bool first = true; |
| | bool lastDropped = false; |
| | RS_Entity* lastEntity = polyline.lastEntity(); |
| | for (auto e: polyline) { |
| | if (e->isAtomic()){ |
| | auto ae = dynamic_cast<RS_AtomicEntity *>(e); |
| | double bulge = 0.0; |
| | if (ae->rtti() == RS2::EntityArc){ |
| | RS_DEBUG->print("RS_Modification::deletePolylineNode: arc segment"); |
| | bulge = ((RS_Arc *) ae)->getBulge(); |
| | } else { |
| | RS_DEBUG->print("RS_Modification::deletePolylineNode: line segment"); |
| | bulge = 0.0; |
| | } |
| |
|
| | |
| | if (e == lastEntity && polyline.isClosed()){ |
| | continue; |
| | } |
| |
|
| | |
| | if (first && node.distanceTo(ae->getStartpoint()) > 1.0e-6){ |
| | RS_DEBUG->print("RS_Modification::deletePolylineNode: first node: %f/%f", |
| | ae->getStartpoint().x, ae->getStartpoint().y); |
| |
|
| | newPolyline->setNextBulge(bulge); |
| | newPolyline->addVertex(ae->getStartpoint()); |
| | first = false; |
| | } |
| |
|
| | |
| | if (first == false && node.distanceTo(ae->getEndpoint()) > 1.0e-6){ |
| | RS_DEBUG->print("RS_Modification::deletePolylineNode: normal vertex found: %f/%f", |
| | ae->getEndpoint().x, ae->getEndpoint().y); |
| | if (lastDropped){ |
| | |
| | } |
| | newPolyline->setNextBulge(bulge); |
| | newPolyline->addVertex(ae->getEndpoint()); |
| | lastDropped = false; |
| | } |
| |
|
| | |
| | else { |
| | RS_DEBUG->print("RS_Modification::deletePolylineNode: deleting vertex: %f/%f", |
| | ae->getEndpoint().x, ae->getEndpoint().y); |
| | lastDropped = true; |
| | } |
| | } else { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::deletePolylineNode: Polyline contains non-atomic entities"); |
| | } |
| | } |
| |
|
| | RS_DEBUG->print("RS_Modification::deletePolylineNode: ending polyline"); |
| | newPolyline->setNextBulge(polyline.getClosingBulge()); |
| | newPolyline->endPolyline(); |
| |
|
| | |
| | RS_DEBUG->print("RS_Modification::deletePolylineNode: adding new polyline"); |
| |
|
| | container->addEntity(newPolyline); |
| | if (!createOnly){ |
| |
|
| | RS_DEBUG->print("RS_Modification::deletePolylineNode: handling undo"); |
| | if (handleUndo){ |
| | LC_UndoSection undo(document, viewport); |
| |
|
| | polyline.setUndoState(true); |
| | undo.addUndoable(&polyline); |
| | undo.addUndoable(newPolyline); |
| | } |
| | } |
| | viewport->notifyChanged(); |
| | return newPolyline; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | RS_Polyline *RS_Modification::deletePolylineNodesBetween( |
| | RS_Polyline &polyline, |
| | const RS_Vector &node1, const RS_Vector &node2, bool createOnly){ |
| |
|
| | RS_DEBUG->print("RS_Modification::deletePolylineNodesBetween"); |
| |
|
| | if (!container){ |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::addPolylineNodesBetween: no valid container"); |
| | return nullptr; |
| | } |
| |
|
| | if (node1.valid == false || node2.valid == false){ |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::deletePolylineNodesBetween: node not valid"); |
| | return nullptr; |
| | } |
| |
|
| | if (node1.distanceTo(node2) < 1.0e-6){ |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::deletePolylineNodesBetween: nodes are identical"); |
| | return nullptr; |
| | } |
| |
|
| | |
| | for (auto e: polyline) { |
| |
|
| | if (e->isAtomic()){ |
| | auto *ae = dynamic_cast<RS_AtomicEntity *>(e); |
| | |
| | if ((node1.distanceTo(ae->getStartpoint()) < RS_TOLERANCE && |
| | node2.distanceTo(ae->getEndpoint()) < 1.0e-6) || |
| | (node2.distanceTo(ae->getStartpoint()) < 1.0e-6 && |
| | node1.distanceTo(ae->getEndpoint()) < 1.0e-6)){ |
| |
|
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::deletePolylineNodesBetween: nothing to delete"); |
| | return nullptr; |
| | } |
| | } |
| | } |
| |
|
| |
|
| | |
| | const RS_Vector &polylineStartpoint = polyline.getStartpoint(); |
| | bool startpointInvolved = isOneOfPoints(polylineStartpoint, node1, node2); |
| |
|
| |
|
| | |
| | bool deleteStart = false; |
| | if (polyline.isClosed()){ |
| | bool found = false; |
| | double length1 = 0.0; |
| | double length2 = 0.0; |
| | RS_Entity *e = polyline.firstEntity(); |
| |
|
| | if (startpointInvolved){ |
| | if (e->isAtomic()){ |
| | auto *ae = dynamic_cast<RS_AtomicEntity *>(e); |
| | length1 += ae->getLength(); |
| | } |
| | e = polyline.nextEntity(); |
| | } |
| | for (; e; e = polyline.nextEntity()) { |
| |
|
| | if (e->isAtomic()){ |
| | auto *ae = dynamic_cast<RS_AtomicEntity *>(e); |
| |
|
| | if (isOneOfPoints(ae->getStartpoint(), node1, node2)){ |
| | found = !found; |
| | } |
| |
|
| | if (found){ |
| | length2 += ae->getLength(); |
| | } else { |
| | length1 += ae->getLength(); |
| | } |
| | } |
| | } |
| | if (length1 < length2){ |
| | deleteStart = true; |
| | } else { |
| | deleteStart = false; |
| | } |
| | } |
| |
|
| | auto *newPolyline = new RS_Polyline(container); |
| | newPolyline->setClosed(polyline.isClosed()); |
| | if (!createOnly){ |
| | newPolyline->setSelected(polyline.isSelected()); |
| | newPolyline->setLayer(polyline.getLayer()); |
| | newPolyline->setPen(polyline.getPen(false)); |
| | } |
| |
|
| | if (startpointInvolved && deleteStart && polyline.isClosed()){ |
| | newPolyline->setNextBulge(0.0); |
| | newPolyline->addVertex(polylineStartpoint); |
| | } |
| |
|
| | |
| | bool first = true; |
| | bool removing = deleteStart; |
| | bool done = false; |
| | bool nextIsStraight = false; |
| | RS_Entity *lastEntity = polyline.lastEntity(); |
| | int i = 0; |
| | double bulge = 0.0; |
| |
|
| | for (auto e: polyline) { |
| |
|
| | RS_DEBUG->print("RS_Modification::deletePolylineNodesBetween: entity: %d", i++); |
| | RS_DEBUG->print("RS_Modification::deletePolylineNodesBetween: removing: %d", (int) removing); |
| |
|
| | if (e->isAtomic()){ |
| | auto ae = dynamic_cast<RS_AtomicEntity *>(e); |
| | if (ae->rtti() == RS2::EntityArc){ |
| | RS_DEBUG->print("RS_Modification::deletePolylineNodesBetween: arc segment"); |
| | auto* arc = static_cast<RS_Arc *>(ae); |
| | bulge = arc->getBulge(); |
| | } else { |
| | RS_DEBUG->print("RS_Modification::deletePolylineNodesBetween: line segment"); |
| | bulge = 0.0; |
| | } |
| |
|
| | const RS_Vector &endpoint = ae->getEndpoint(); |
| | const RS_Vector &startpoint = ae->getStartpoint(); |
| |
|
| | |
| | if (e == lastEntity && polyline.isClosed()){ |
| | RS_DEBUG->print("RS_Modification::deletePolylineNodesBetween: dropping last vertex of closed polyline"); |
| | continue; |
| | } |
| |
|
| | |
| | if (first){ |
| | if (!removing){ |
| | RS_DEBUG->print("RS_Modification::deletePolylineNodesBetween: first node: %f/%f", startpoint.x, startpoint.y); |
| | newPolyline->setNextBulge(bulge); |
| | newPolyline->addVertex(startpoint); |
| | first = false; |
| | } |
| | } |
| |
|
| |
|
| | |
| |
|
| | if (removing == true && isOneOfPoints(endpoint, node1, node2)){ |
| | RS_DEBUG->print("RS_Modification::deletePolylineNodesBetween: stop removing at: %f/%f",endpoint.x, endpoint.y); |
| | removing = false; |
| | done = true; |
| | if (first == false){ |
| | nextIsStraight = true; |
| | } |
| | } |
| |
|
| | |
| | if (removing == false && (done == false || deleteStart == false)){ |
| | RS_DEBUG->print("RS_Modification::deletePolylineNodesBetween: normal vertex shouldRemove: %f/%f", endpoint.x, endpoint.y); |
| | if (nextIsStraight){ |
| | bulge = 0.0; |
| | nextIsStraight = false; |
| | } |
| | newPolyline->setNextBulge(bulge); |
| | newPolyline->addVertex(endpoint); |
| | } |
| | |
| | else { |
| | RS_DEBUG->print("RS_Modification::deletePolylineNodesBetween: deleting vertex: %f/%f", endpoint.x, endpoint.y); |
| | } |
| |
|
| | |
| | if (done == false && removing == false && isOneOfPoints(endpoint, node1, node2)){ |
| | RS_DEBUG->print("RS_Modification::deletePolylineNodesBetween: start removing at: %f/%f", endpoint.x, endpoint.y); |
| | removing = true; |
| | } |
| |
|
| | if (done){ |
| | done = false; |
| | } |
| | } else { |
| | RS_DEBUG->print(RS_Debug::D_WARNING,"RS_Modification::deletePolylineNodesBetween: Polyline contains non-atomic entities"); |
| | } |
| | } |
| |
|
| | RS_DEBUG->print("RS_Modification::deletePolylineNodesBetween: ending polyline"); |
| | newPolyline->setNextBulge(polyline.getClosingBulge()); |
| | newPolyline->endPolyline(); |
| |
|
| | |
| | RS_DEBUG->print("RS_Modification::deletePolylineNodesBetween: adding new polyline"); |
| | container->addEntity(newPolyline); |
| | if (!createOnly){ |
| |
|
| | RS_DEBUG->print("RS_Modification::deletePolylineNodesBetween: handling undo"); |
| | if (handleUndo){ |
| | LC_UndoSection undo(document, viewport); |
| |
|
| | polyline.setUndoState(true); |
| | undo.addUndoable(&polyline); |
| | undo.addUndoable(newPolyline); |
| | } |
| | } |
| | viewport->notifyChanged(); |
| | return newPolyline; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | RS_Polyline *RS_Modification::polylineTrim( |
| | RS_Polyline &polyline, |
| | RS_AtomicEntity &segment1, |
| | RS_AtomicEntity &segment2, |
| | bool createOnly){ |
| |
|
| | RS_DEBUG->print("RS_Modification::polylineTrim"); |
| |
|
| | if (!container){ |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::addPolylineNodesBetween: no valid container"); |
| | return nullptr; |
| | } |
| |
|
| | if (segment1.getParent() != &polyline || segment2.getParent() != &polyline){ |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::polylineTrim: segments not in polyline"); |
| | return nullptr; |
| | } |
| |
|
| | if (&segment1 == &segment2){ |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::polylineTrim: segments are identical"); |
| | return nullptr; |
| | } |
| |
|
| | RS_VectorSolutions sol; |
| | sol = RS_Information::getIntersection(&segment1, &segment2, false); |
| |
|
| | if (sol.getNumber() == 0){ |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::polylineTrim: segments cannot be trimmed"); |
| | return nullptr; |
| | } |
| |
|
| | |
| | RS_AtomicEntity *firstSegment; |
| | if (polyline.findEntity(&segment1) > polyline.findEntity(&segment2)){ |
| | firstSegment = &segment2; |
| | } else { |
| | firstSegment = &segment1; |
| | } |
| |
|
| | |
| | bool reverseTrim; |
| | reverseTrim = !RS_Math::isSameDirection(firstSegment->getDirection1(), |
| | firstSegment->getStartpoint().angleTo(sol.get(0)), M_PI_2); |
| | |
| | |
| |
|
| | auto *newPolyline = new RS_Polyline(container); |
| | newPolyline->setClosed(polyline.isClosed()); |
| | if (!createOnly){ |
| | newPolyline->setSelected(polyline.isSelected()); |
| | newPolyline->setLayer(polyline.getLayer()); |
| | newPolyline->setPen(polyline.getPen(false)); |
| | } |
| |
|
| | |
| | if (!reverseTrim){ |
| | |
| | bool first = true; |
| | bool removing = false; |
| | bool nextIsStraight = false; |
| | RS_Entity *lastEntity = polyline.lastEntity(); |
| | for (auto e: polyline) { |
| |
|
| | if (e->isAtomic()){ |
| | auto ae = dynamic_cast<RS_AtomicEntity *>(e); |
| | double bulge = 0.0; |
| | if (ae->rtti() == RS2::EntityArc){ |
| | RS_DEBUG->print("RS_Modification::polylineTrim: arc segment"); |
| | bulge = ((RS_Arc *) ae)->getBulge(); |
| | } else { |
| | RS_DEBUG->print("RS_Modification::polylineTrim: line segment"); |
| | bulge = 0.0; |
| | } |
| |
|
| | |
| | if (e == lastEntity && polyline.isClosed()){ |
| | RS_DEBUG->print("RS_Modification::polylineTrim: " |
| | "dropping last vertex of closed polyline"); |
| | continue; |
| | } |
| |
|
| | |
| | if (first){ |
| | RS_DEBUG->print("RS_Modification::polylineTrim: first node: %f/%f", |
| | ae->getStartpoint().x, ae->getStartpoint().y); |
| |
|
| | newPolyline->setNextBulge(bulge); |
| | newPolyline->addVertex(ae->getStartpoint()); |
| | first = false; |
| | } |
| |
|
| | |
| | if (!removing && (ae == &segment1 || ae == &segment2)){ |
| | RS_DEBUG->print("RS_Modification::polylineTrim: " |
| | "start removing at trim point %f/%f", |
| | sol.get(0).x, sol.get(0).y); |
| | newPolyline->setNextBulge(0.0); |
| | newPolyline->addVertex(sol.get(0)); |
| | removing = true; |
| | nextIsStraight = true; |
| | } |
| |
|
| | |
| | else if (removing && (ae == &segment1 || ae == &segment2)){ |
| | RS_DEBUG->print("RS_Modification::polylineTrim: stop removing at: %f/%f", |
| | ae->getEndpoint().x, ae->getEndpoint().y); |
| | removing = false; |
| | } |
| |
|
| | |
| | if (!removing){ |
| | RS_DEBUG->print("RS_Modification::polylineTrim: normal vertex found: %f/%f", |
| | ae->getEndpoint().x, ae->getEndpoint().y); |
| | if (nextIsStraight){ |
| | newPolyline->setNextBulge(0.0); |
| | nextIsStraight = false; |
| | } else { |
| | newPolyline->setNextBulge(bulge); |
| | } |
| | newPolyline->addVertex(ae->getEndpoint()); |
| | } |
| | } else { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::polylineTrim: Polyline contains non-atomic entities"); |
| | } |
| | } |
| | } |
| |
|
| | |
| | else { |
| | |
| | |
| | bool removing = true; |
| | bool nextIsStraight = false; |
| | RS_Entity *lastEntity = polyline.lastEntity(); |
| | for (auto e: polyline) { |
| |
|
| | if (e->isAtomic()){ |
| | auto *ae = dynamic_cast<RS_AtomicEntity *>(e); |
| | double bulge = 0.0; |
| | if (ae->rtti() == RS2::EntityArc){ |
| | RS_DEBUG->print("RS_Modification::polylineTrim: arc segment"); |
| | auto* arc = static_cast<RS_Arc *>(ae); |
| | bulge = arc ->getBulge(); |
| | } else { |
| | RS_DEBUG->print("RS_Modification::polylineTrim: line segment"); |
| | bulge = 0.0; |
| | } |
| |
|
| | |
| | if (e == lastEntity && polyline.isClosed()){ |
| | RS_DEBUG->print("RS_Modification::polylineTrim: " |
| | "dropping last vertex of closed polyline"); |
| | continue; |
| | } |
| |
|
| | |
| | if (removing == true && (ae == &segment1 || ae == &segment2)){ |
| | RS_DEBUG->print("RS_Modification::polylineTrim: " |
| | "stop removing at trim point %f/%f", |
| | sol.get(0).x, sol.get(0).y); |
| | newPolyline->setNextBulge(0.0); |
| | |
| | newPolyline->addVertex(sol.get(0)); |
| | removing = false; |
| | nextIsStraight = true; |
| | } |
| |
|
| | |
| | else if (removing == false && (ae == &segment1 || ae == &segment2)){ |
| | RS_DEBUG->print("RS_Modification::polylineTrim: start removing at: %f/%f", |
| | ae->getEndpoint().x, ae->getEndpoint().y); |
| | newPolyline->setNextBulge(0.0); |
| | |
| | newPolyline->addVertex(sol.get(0)); |
| | removing = true; |
| | } |
| |
|
| | |
| | if (removing == false){ |
| | RS_DEBUG->print("RS_Modification::polylineTrim: normal vertex found: %f/%f", |
| | ae->getEndpoint().x, ae->getEndpoint().y); |
| | if (nextIsStraight){ |
| | newPolyline->setNextBulge(0.0); |
| | nextIsStraight = false; |
| | } else { |
| | newPolyline->setNextBulge(bulge); |
| | } |
| | newPolyline->addVertex(ae->getEndpoint()); |
| | } |
| | } else { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::polylineTrim: Polyline contains non-atomic entities"); |
| | } |
| | } |
| | } |
| |
|
| | RS_DEBUG->print("RS_Modification::polylineTrim: ending polyline"); |
| | newPolyline->setNextBulge(polyline.getClosingBulge()); |
| | newPolyline->endPolyline(); |
| |
|
| | |
| | RS_DEBUG->print("RS_Modification::polylineTrim: adding new polyline"); |
| | container->addEntity(newPolyline); |
| | if (!createOnly){ |
| | RS_DEBUG->print("RS_Modification::polylineTrim: handling undo"); |
| | if (handleUndo){ |
| | LC_UndoSection undo(document, viewport); |
| |
|
| | polyline.setUndoState(true); |
| | undo.addUndoable(&polyline); |
| | undo.addUndoable(newPolyline); |
| | } |
| | } |
| |
|
| | viewport->notifyChanged(); |
| | return newPolyline; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | bool RS_Modification::move(RS_MoveData &data, bool previewOnly, [[maybe_unused]]RS_EntityContainer* previewContainer){ |
| | if (!container) { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::move: no valid container"); |
| | return false; |
| | } |
| | std::vector<RS_Entity*> selectedEntities; |
| | container->collectSelected(selectedEntities, false); |
| | return move(data, selectedEntities, previewOnly, true); |
| | } |
| |
|
| |
|
| | bool RS_Modification::move(RS_MoveData& data, const std::vector<RS_Entity*> &entitiesList, bool forPreviewOnly, bool keepSelected) { |
| |
|
| | int numberOfCopies = data.obtainNumberOfCopies(); |
| | std::vector<RS_Entity*> clonesList; |
| |
|
| | for(auto e: entitiesList){ |
| | |
| | for (int num = 1; num <= numberOfCopies; num++) { |
| | RS_Entity* ec = getClone(forPreviewOnly, e); |
| | ec->move(data.offset*num); |
| | clonesList.push_back(ec); |
| | } |
| | } |
| |
|
| | setupModifiedClones(clonesList, data, forPreviewOnly, keepSelected); |
| |
|
| | deleteOriginalAndAddNewEntities(clonesList, entitiesList, forPreviewOnly, !data.keepOriginals); |
| | clonesList.clear(); |
| | return true; |
| | } |
| |
|
| | RS_Entity *RS_Modification::getClone(bool forPreviewOnly, const RS_Entity *e) const { |
| | RS_Entity* result = nullptr; |
| | if (forPreviewOnly){ |
| | int rtti = e->rtti(); |
| | switch (rtti){ |
| | case RS2::EntityText: |
| | case RS2::EntityMText:{ |
| | |
| | bool drawTextAsDraftInPreview = LC_GET_ONE_BOOL("Render","DrawTextsAsDraftInPreview", true); |
| | if (drawTextAsDraftInPreview) { |
| | result = e->cloneProxy(); |
| | } |
| | else{ |
| | result = e->clone(); |
| | } |
| | break; |
| | } |
| | case RS2::EntityImage:{ |
| | result = e->cloneProxy(); |
| | break; |
| | } |
| | default: |
| | result = e->clone(); |
| | } |
| | } |
| | else{ |
| | result = e->clone(); |
| | } |
| |
|
| | return result; |
| | } |
| |
|
| | void RS_Modification::setupModifiedClones( |
| | std::vector<RS_Entity *> &addList, const LC_ModifyOperationFlags &data, bool forPreviewOnly, bool keepSelected) const { |
| | if (!forPreviewOnly && (data.useCurrentLayer || data.useCurrentAttributes)){ |
| | RS_Layer* layer = nullptr; |
| | RS_Pen pen; |
| | if (data.useCurrentLayer) { |
| | layer = viewport->getGraphic()->getActiveLayer(); |
| | } |
| | if (data.useCurrentAttributes) { |
| | pen = document->getActivePen(); |
| | } |
| | for (auto e: addList){ |
| | if (data.useCurrentLayer) { |
| | e->setLayer(layer); |
| | } |
| | if (data.useCurrentAttributes) { |
| | e->setPen(pen); |
| | } |
| | } |
| | } |
| |
|
| | for (auto e: addList){ |
| | if (e->rtti()==RS2::EntityInsert) { |
| | ((RS_Insert*)e)->update(); |
| | } |
| | |
| | e->setSelected(keepSelected); |
| | } |
| | } |
| |
|
| | bool RS_Modification::alignRef(LC_AlignRefData & data, const std::vector<RS_Entity*> &entitiesList, bool forPreviewOnly, bool keepSelected) { |
| |
|
| | int numberOfCopies = 1; |
| | std::vector<RS_Entity*> clonesList; |
| |
|
| | RS_Vector offset = data.offset; |
| |
|
| | |
| | for(auto e: entitiesList){ |
| | |
| | for (int num = 1; num <= numberOfCopies; num++) { |
| | RS_Entity* ec = getClone(forPreviewOnly, e); |
| |
|
| | ec->rotate(data.rotationCenter, data.rotationAngle); |
| |
|
| | if (data.scale && LC_LineMath::isMeaningful(data.scaleFactor - 1.0)){ |
| | ec->scale(data.rotationCenter, data.scaleFactor); |
| | } |
| |
|
| | ec->move(offset*num); |
| |
|
| | clonesList.push_back(ec); |
| | } |
| | } |
| |
|
| | setupModifiedClones(clonesList, data, forPreviewOnly, keepSelected); |
| |
|
| | deleteOriginalAndAddNewEntities(clonesList, entitiesList, forPreviewOnly, !data.keepOriginals); |
| | clonesList.clear(); |
| | return true; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | bool RS_Modification::offset(const RS_OffsetData& data, [[maybe_unused]]bool previewOnly,[[maybe_unused]] RS_EntityContainer* previewContainer) { |
| | if (container == nullptr) { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::offset: no valid container"); |
| | return false; |
| | } |
| |
|
| | std::vector<RS_Entity*> selectedEntities; |
| | container->collectSelected(selectedEntities, false); |
| | return offset(data, selectedEntities, false, true); |
| | } |
| |
|
| | bool RS_Modification::offset(const RS_OffsetData& data, const std::vector<RS_Entity*> &entitiesList, bool forPreviewOnly, bool keepSelected) { |
| | std::vector<RS_Entity*> clonesList; |
| |
|
| | int numberOfCopies = data.obtainNumberOfCopies(); |
| |
|
| | |
| | |
| | for(auto e: entitiesList){ |
| | for (int num=1; num<= numberOfCopies; num++) { |
| | auto ec = e->clone(); |
| | |
| | ec->setHighlighted(false); |
| |
|
| | if (!ec->offset(data.coord, num*data.distance)) { |
| | delete ec; |
| | continue; |
| | } |
| |
|
| | clonesList.push_back(ec); |
| | } |
| | } |
| |
|
| | setupModifiedClones(clonesList, data, forPreviewOnly, keepSelected); |
| |
|
| | deleteOriginalAndAddNewEntities(clonesList, entitiesList, forPreviewOnly, !data.keepOriginals); |
| | clonesList.clear(); |
| | return true; |
| | } |
| |
|
| | |
| | |
| | |
| | bool RS_Modification::rotate(RS_RotateData &data, bool forPreviewOnly, bool keepSelected){ |
| | if (!container){ |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::rotate: no valid container"); |
| | return false; |
| | } |
| |
|
| | std::vector<RS_Entity*> selectedEntities; |
| | container->collectSelected(selectedEntities, false); |
| | return rotate(data, selectedEntities, forPreviewOnly, keepSelected); |
| | } |
| |
|
| | bool RS_Modification::rotate(RS_RotateData& data, const std::vector<RS_Entity*> &entitiesList, bool forPreviewOnly, bool keepSelected) { |
| | std::vector<RS_Entity *> clonesList; |
| | |
| |
|
| | int numberOfCopies = data.obtainNumberOfCopies(); |
| | for (auto e: entitiesList) { |
| | for (int num = 1; num <= numberOfCopies; num++) { |
| | RS_Entity* ec = getClone(forPreviewOnly, e); |
| |
|
| | double rotationAngle = data.angle * num; |
| | ec->rotate(data.center, rotationAngle); |
| |
|
| | bool rotateTwice = data.twoRotations; |
| | double distance = data.refPoint.distanceTo(data.center); |
| | if (distance < RS_TOLERANCE){ |
| | rotateTwice = false; |
| | } |
| |
|
| | if (rotateTwice) { |
| | RS_Vector rotatedRefPoint = data.refPoint; |
| | rotatedRefPoint.rotate(data.center, rotationAngle); |
| |
|
| | double secondRotationAngle = data.secondAngle; |
| | if (data.secondAngleIsAbsolute){ |
| | secondRotationAngle -= rotationAngle; |
| | } |
| | ec->rotate(rotatedRefPoint, secondRotationAngle); |
| | } |
| |
|
| | clonesList.push_back(ec); |
| | } |
| | } |
| | setupModifiedClones(clonesList, data, forPreviewOnly, keepSelected); |
| |
|
| | deleteOriginalAndAddNewEntities(clonesList, entitiesList, forPreviewOnly, !data.keepOriginals); |
| | clonesList.clear(); |
| | |
| | return true; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | bool RS_Modification::scale(RS_ScaleData& data, bool forPreviewOnly, const bool keepSelected) { |
| | if (container == nullptr) { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::scale: no valid container"); |
| | return false; |
| | } |
| | |
| | std::vector<RS_Entity*> selectedEntities; |
| | container->collectSelected(selectedEntities, false); |
| | return scale(data, selectedEntities, forPreviewOnly, keepSelected); |
| | } |
| | |
| | |
| | |
| | |
| | bool RS_Modification::scale(RS_ScaleData& data, const std::vector<RS_Entity*> &entitiesList, bool forPreviewOnly, const bool keepSelected) { |
| | std::vector<RS_Entity*> selectedList,clonesList; |
| |
|
| | for(auto ec: entitiesList){ |
| | if ( !data.isotropicScaling ) { |
| | RS2::EntityType rtti = ec->rtti(); |
| | if (rtti == RS2::EntityCircle ) { |
| | |
| | auto* c=dynamic_cast<RS_Circle*>(ec); |
| | ec= new RS_Ellipse{container, |
| | {c->getCenter(), {c->getRadius(),0.}, |
| | 1., |
| | 0., 0., false}}; |
| | } else if (rtti == RS2::EntityArc ) { |
| | |
| | auto *c=dynamic_cast<RS_Arc*>(ec); |
| | ec= new RS_Ellipse{container, |
| | {c->getCenter(), |
| | {c->getRadius(),0.}, |
| | 1.0, |
| | c->getAngle1(), |
| | c->getAngle2(), |
| | c->isReversed()}}; |
| | } |
| | } |
| | if (ec->rtti() == RS2::EntityImage) { |
| | ec = getClone(forPreviewOnly, ec); |
| | } |
| | selectedList.push_back(ec); |
| | } |
| |
|
| | int numberOfCopies = data.obtainNumberOfCopies(); |
| |
|
| | |
| | for(RS_Entity* e: selectedList) { |
| | if (e != nullptr) { |
| | for (int num= 1; num <= numberOfCopies; num++) { |
| | RS_Entity* ec = getClone(forPreviewOnly, e); |
| | ec->scale(data.referencePoint, RS_Math::pow(data.factor, num)); |
| | clonesList.push_back(ec); |
| | } |
| | } |
| | } |
| | selectedList.clear(); |
| | setupModifiedClones(clonesList, data, forPreviewOnly, keepSelected); |
| | deleteOriginalAndAddNewEntities(clonesList, entitiesList, forPreviewOnly, !data.keepOriginals); |
| | clonesList.clear(); |
| |
|
| | return true; |
| | } |
| | |
| | |
| | |
| | |
| | bool RS_Modification::mirror(RS_MirrorData& data, bool keepSelected) { |
| | if (!container) { |
| | RS_DEBUG->print(RS_Debug::D_WARNING,"RS_Modification::mirror: no valid container"); |
| | return false; |
| | } |
| |
|
| | std::vector<RS_Entity*> selectedEntities; |
| | container->collectSelected(selectedEntities, false); |
| | return mirror(data, selectedEntities, false, keepSelected); |
| | } |
| |
|
| | bool RS_Modification::mirror(RS_MirrorData& data, const std::vector<RS_Entity*> &entitiesList, bool forPreviewOnly, bool keepSelected) { |
| |
|
| | std::vector<RS_Entity*> clonesList; |
| |
|
| | |
| | int numberOfCopies = 1; |
| |
|
| | |
| |
|
| | for(auto e: entitiesList){ |
| | for (int num=1; num<=numberOfCopies; ++num) { |
| | RS_Entity* ec = getClone(forPreviewOnly, e); |
| | ec->mirror(data.axisPoint1, data.axisPoint2); |
| | clonesList.push_back(ec); |
| | } |
| | } |
| |
|
| | setupModifiedClones(clonesList, data, forPreviewOnly, keepSelected); |
| |
|
| | deleteOriginalAndAddNewEntities(clonesList, entitiesList, forPreviewOnly, !data.keepOriginals); |
| | clonesList.clear(); |
| | return true; |
| | } |
| |
|
| | |
| | |
| | |
| | bool RS_Modification::rotate2(RS_Rotate2Data& data,[[maybe_unused]] bool forPreviewOnly, bool keepSelected) { |
| | if (!container) { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Modification::rotate2: no valid container"); |
| | return false; |
| | } |
| | std::vector<RS_Entity*> selectedEntities; |
| | container->collectSelected(selectedEntities, false); |
| | return rotate2(data, selectedEntities, false, keepSelected); |
| | } |
| |
|
| | bool RS_Modification::rotate2(RS_Rotate2Data& data, const std::vector<RS_Entity*> &entitiesList, bool forPreviewOnly, bool keepSelected) { |
| |
|
| | std::vector<RS_Entity*> clonesList; |
| |
|
| | int numberOfCopies = data.obtainNumberOfCopies(); |
| |
|
| | |
| |
|
| | for(auto e: entitiesList){ |
| | for (int num= 1; num <= numberOfCopies; num++) { |
| | RS_Entity* ec = getClone(forPreviewOnly, e); |
| |
|
| | double angle1ForCopy = data.angle1 * num; |
| | double angle2ForCopy = data.sameAngle2ForCopies ? data.angle2 : data.angle2 * num; |
| |
|
| | ec->rotate(data.center1, angle1ForCopy); |
| |
|
| | RS_Vector center2 = data.center2; |
| | center2.rotate(data.center1, angle1ForCopy); |
| |
|
| | ec->rotate(center2, angle2ForCopy); |
| |
|
| | clonesList.push_back(ec); |
| | } |
| | } |
| | setupModifiedClones(clonesList, data, forPreviewOnly, keepSelected); |
| |
|
| | deleteOriginalAndAddNewEntities(clonesList, entitiesList, forPreviewOnly, !data.keepOriginals); |
| | clonesList.clear(); |
| | return true; |
| | } |
| |
|
| | void RS_Modification::deleteOriginalAndAddNewEntities(const std::vector<RS_Entity*> &addList, const std::vector<RS_Entity*> &originalEntities, bool addOnly, bool deleteOriginals, bool forceUndoable){ |
| | LC_UndoSection undo(document, viewport, handleUndo); |
| | if (addOnly) { |
| | for (RS_Entity *e: addList) { |
| | if (e != nullptr) { |
| | container->addEntity(e); |
| | undo.addUndoable(e); |
| | } |
| | } |
| | } else { |
| | deselectOriginals(originalEntities,deleteOriginals); |
| | addNewEntities(addList, forceUndoable); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | bool RS_Modification::moveRotate(RS_MoveRotateData& data, bool previewOnly, [[maybe_unused]]RS_EntityContainer* previewContainer, bool keepSelected) { |
| | if (!container) { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::moveRotate: no valid container"); |
| | return false; |
| | } |
| |
|
| | std::vector<RS_Entity*> selectedEntities; |
| | container->collectSelected(selectedEntities, false); |
| | return moveRotate(data, selectedEntities, previewOnly, keepSelected); |
| | } |
| |
|
| | bool RS_Modification::moveRotate(RS_MoveRotateData &data, const std::vector<RS_Entity*> &entitiesList, bool forPreviewOnly, bool keepSelected){ |
| | std::vector<RS_Entity*> clonesList; |
| |
|
| | int numberOfCopies = data.obtainNumberOfCopies(); |
| |
|
| | |
| | for(auto e: entitiesList){ |
| | for (int num=1; num <= numberOfCopies; ++num) { |
| | RS_Entity* ec = getClone(forPreviewOnly, e); |
| |
|
| | const RS_Vector &offset = data.offset * num; |
| | ec->move(offset); |
| | double angleForCopy = data.sameAngleForCopies ? data.angle : data.angle * num; |
| | ec->rotate(data.referencePoint + offset, angleForCopy); |
| |
|
| | clonesList.push_back(ec); |
| | } |
| | } |
| |
|
| | setupModifiedClones(clonesList, data, forPreviewOnly, keepSelected); |
| |
|
| |
|
| | deleteOriginalAndAddNewEntities(clonesList, entitiesList, forPreviewOnly, !data.keepOriginals); |
| | clonesList.clear(); |
| | return true; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | void RS_Modification::deselectOriginals(bool remove) { |
| | LC_UndoSection undo(document, viewport,handleUndo); |
| |
|
| | for (auto e: *container) { |
| | if (e != nullptr) { |
| | if (e->isSelected()) { |
| | e->setSelected(false); |
| | if (remove) { |
| | e->changeUndoState(); |
| | undo.addUndoable(e); |
| | } |
| | } |
| | } |
| | } |
| | } |
| |
|
| | void RS_Modification::deselectOriginals(const std::vector<RS_Entity*> &entitiesList, bool remove) { |
| | LC_UndoSection undo(document,viewport,handleUndo); |
| |
|
| | for (auto e: entitiesList) { |
| | e->setSelected(false); |
| | if (remove) { |
| | e->changeUndoState(); |
| | undo.addUndoable(e); |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | void RS_Modification::addNewEntities(const std::vector<RS_Entity*>& addList, bool forceUndoable) { |
| | LC_UndoSection undo( document, viewport, handleUndo || forceUndoable); |
| |
|
| | for (RS_Entity* e: addList) { |
| | if (e) { |
| | container->addEntity(e); |
| | undo.addUndoable(e); |
| | } |
| | } |
| |
|
| | container->calculateBorders(); |
| |
|
| | viewport->notifyChanged(); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | LC_TrimResult RS_Modification::trim(const RS_Vector& trimCoord, |
| | RS_AtomicEntity* trimEntity, |
| | const RS_Vector& limitCoord, |
| | RS_Entity* limitEntity, |
| | bool both, |
| | bool forPreview) { |
| |
|
| | LC_TrimResult result; |
| | if (trimEntity == nullptr || limitEntity == nullptr) { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::trim: At least one entity is nullptr"); |
| | return result; |
| | } |
| |
|
| | if (both && !limitEntity->isAtomic()) { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::trim: limitEntity is not atomic"); |
| | } |
| | if(trimEntity->isLocked()|| !trimEntity->isVisible()) |
| | return result; |
| |
|
| | RS_VectorSolutions sol = findIntersection(*trimEntity, *limitEntity); |
| |
|
| | |
| | if (trimEntity->rtti()==RS2::EntityLine){ |
| | auto *lin = static_cast<RS_Line *>(trimEntity); |
| | for (unsigned int i=0; i< sol.size(); i++) { |
| | RS_Vector v = sol.at(i); |
| | if (v == lin->getStartpoint()) { |
| | sol.removeAt(i); |
| | } |
| | else if (v == lin->getEndpoint()) { |
| | sol.removeAt(i); |
| | } |
| | } |
| | } |
| |
|
| | if (!sol.hasValid()) { |
| | return both ? trim( limitCoord, static_cast<RS_AtomicEntity*>(limitEntity), trimCoord, trimEntity, false, forPreview) : result; |
| | } |
| |
|
| | RS_AtomicEntity* trimmed1 = nullptr; |
| | RS_AtomicEntity* trimmed2 = nullptr; |
| | |
| | |
| |
|
| | if (trimEntity->rtti()==RS2::EntityCircle) { |
| | |
| | trimmed1 = trimCircle(*static_cast<RS_Circle*>(trimEntity), trimCoord, sol); |
| | } else { |
| | trimmed1 = static_cast<RS_AtomicEntity*>(trimEntity->clone()); |
| | trimmed1->setHighlighted(false); |
| | } |
| |
|
| | |
| | size_t ind = 0; |
| | RS_Vector is(false), is2(false); |
| |
|
| | |
| | if ( trimEntity->trimmable() ) { |
| | is = trimmed1->prepareTrim(trimCoord, sol); |
| | } else { |
| | is = sol.getClosest(limitCoord, nullptr, &ind); |
| | |
| | RS_DEBUG->print("RS_Modification::trim: limitCoord: %f/%f", limitCoord.x, limitCoord.y); |
| | RS_DEBUG->print("RS_Modification::trim: sol.get(0): %f/%f", sol.get(0).x, sol.get(0).y); |
| | RS_DEBUG->print("RS_Modification::trim: sol.get(1): %f/%f", sol.get(1).x, sol.get(1).y); |
| | RS_DEBUG->print("RS_Modification::trim: ind: %lu", ind); |
| | is2 = sol.get(ind==0 ? 1 : 0); |
| | |
| | RS_DEBUG->print("RS_Modification::trim: is2: %f/%f", is2.x, is2.y); |
| |
|
| | } |
| | if (!forPreview) { |
| | |
| | |
| | |
| | |
| | } |
| |
|
| | |
| | bool trimBoth= both && !limitEntity->isLocked() && limitEntity->isVisible(); |
| | if (trimBoth) { |
| | trimmed2 = (RS_AtomicEntity*)limitEntity->clone(); |
| | if (!forPreview) { |
| | trimmed2->setHighlighted(false); |
| | } |
| | } |
| |
|
| | trimEnding(trimCoord, trimmed1, is); |
| |
|
| | |
| | if (trimBoth) { |
| | if ( trimmed2->trimmable()) { |
| | is2 = trimmed2->prepareTrim(limitCoord, sol); |
| | } |
| | else { |
| | is2 = sol.getClosest(trimCoord); |
| | } |
| |
|
| | trimEnding(limitCoord, trimmed2, is2); |
| | } |
| |
|
| | if (!forPreview) { |
| | |
| | container->addEntity(trimmed1); |
| |
|
| | |
| | if (trimBoth) { |
| | container->addEntity(trimmed2); |
| | } |
| | } |
| | if (!forPreview) { |
| | if (handleUndo) { |
| | LC_UndoSection undo(document,viewport); |
| |
|
| | undo.addUndoable(trimmed1); |
| | trimEntity->setUndoState(true); |
| | undo.addUndoable(trimEntity); |
| | if (trimBoth) { |
| | undo.addUndoable(trimmed2); |
| | limitEntity->setUndoState(true); |
| | undo.addUndoable(limitEntity); |
| | } |
| | } |
| | } |
| | result.result = true; |
| | result.trimmed1 = trimmed1; |
| | result.trimmed2 = trimmed2; |
| | result.intersection1 = is; |
| | result.intersection2 = is2; |
| |
|
| | if (trimmed1->isArc()){ |
| | result.intersection1 = trimmed1->getStartpoint(); |
| | result.intersection2 = trimmed1->getEndpoint(); |
| | } |
| |
|
| | viewport->notifyChanged(); |
| | return result; |
| | } |
| |
|
| | void RS_Modification::trimEnding(const RS_Vector &trimCoord, RS_AtomicEntity *trimmed1, const RS_Vector &is) const { |
| | RS2::Ending ending = trimmed1->getTrimPoint(trimCoord, is); |
| | switch (ending) { |
| | case RS2::EndingStart: { |
| | trimmed1->trimStartpoint(is); |
| | break; |
| | } |
| | case RS2::EndingEnd: { |
| | trimmed1->trimEndpoint(is); |
| | break; |
| | } |
| | default: |
| | break; |
| | } |
| | } |
| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | RS_Entity* RS_Modification::trimAmount(const RS_Vector& trimCoord, |
| | RS_AtomicEntity* trimEntity, |
| | double dist, |
| | bool trimBoth, |
| | bool &trimStart, bool &trimEnd, |
| | bool forPreview) { |
| |
|
| | if (!trimEntity){ |
| | RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Modification::trimAmount: Entity is nullptr"); |
| | return nullptr; |
| | } |
| |
|
| | if (trimEntity->isLocked() || !trimEntity->isVisible()) { |
| | return nullptr; |
| | } |
| |
|
| | RS_AtomicEntity *trimmed = nullptr; |
| |
|
| | |
| | trimmed = (RS_AtomicEntity *) trimEntity->clone(); |
| |
|
| | |
| |
|
| | trimStart = false; |
| | trimEnd = false; |
| | if (trimBoth){ |
| | RS_Vector isStart = trimmed->getNearestDist(-dist, trimmed->getStartpoint()); |
| | RS_Vector isEnd = trimmed->getNearestDist(-dist, trimmed->getEndpoint()); |
| |
|
| | trimmed->trimStartpoint(isStart); |
| | trimmed->trimEndpoint(isEnd ); |
| | trimStart = true; |
| | trimEnd = true; |
| | } |
| | else { |
| | RS_Vector is = trimmed->getNearestDist(-dist, trimCoord); |
| |
|
| | if (trimCoord.distanceTo(trimmed->getStartpoint()) < |
| | trimCoord.distanceTo(trimmed->getEndpoint())){ |
| | trimmed->trimStartpoint(is); |
| | trimStart = true; |
| | } else { |
| | trimmed->trimEndpoint(is); |
| | trimEnd = true; |
| | } |
| | } |
| |
|
| | if (!forPreview){ |
| | |
| | if (container != nullptr){ |
| | container->addEntity(trimmed); |
| | } |
| |
|
| | if (handleUndo){ |
| | LC_UndoSection undo(document,viewport); |
| | undo.addUndoable(trimmed); |
| | trimEntity->setUndoState(true); |
| | undo.addUndoable(trimEntity); |
| | } |
| | } |
| | viewport->notifyChanged(); |
| | return trimmed; |
| | } |
| |
|
| |
|
| |
|
| | |
| | |
| | |
| | bool RS_Modification::cut(const RS_Vector& cutCoord, |
| | RS_AtomicEntity* cutEntity) { |
| |
|
| | #ifndef EMU_C99 |
| | using std::isnormal; |
| | #endif |
| |
|
| | if (cutEntity == nullptr) { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::cut: Entity is nullptr"); |
| | return false; |
| | } |
| | if(cutEntity->isLocked() || ! cutEntity->isVisible()) return false; |
| |
|
| | if (!cutCoord.valid) { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::cut: Point invalid."); |
| | return false; |
| | } |
| |
|
| | |
| | if (cutCoord.distanceTo(cutEntity->getStartpoint())<RS_TOLERANCE || |
| | cutCoord.distanceTo(cutEntity->getEndpoint())<RS_TOLERANCE) { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::cut: Cutting point on endpoint"); |
| | return false; |
| | } |
| |
|
| | RS_AtomicEntity* cut1 = nullptr; |
| | RS_AtomicEntity* cut2 = nullptr; |
| | double a; |
| |
|
| | const RS_Pen &originalPen = cutEntity->getPen(false); |
| | RS_Layer *originalLayer = cutEntity->getLayer(false); |
| | switch (cutEntity->rtti()) { |
| | case RS2::EntityCircle: { |
| | |
| | auto *originalCircle = dynamic_cast<RS_Circle *>(cutEntity); |
| | a = originalCircle->getCenter().angleTo(cutCoord); |
| | cut1 = new RS_Arc(cutEntity->getParent(), |
| | RS_ArcData(originalCircle->getCenter(), |
| | originalCircle->getRadius(), |
| | a, a + 2. * M_PI, false)); |
| | cut1->setPen(originalPen); |
| | cut1->setLayer(originalLayer); |
| | |
| | break; |
| | } |
| | case RS2::EntitySplinePoints: { |
| | |
| | |
| | cut2 = static_cast<LC_SplinePoints*>(cutEntity)->cut(cutCoord); |
| | cut1 = (RS_AtomicEntity *) cutEntity->clone(); |
| |
|
| | cut1->setPen(originalPen); |
| | cut1->setLayer(originalLayer); |
| | if (cut2) { |
| | cut2->setPen(originalPen); |
| | cut2->setLayer(originalLayer); |
| | } |
| | break; |
| | } |
| | case RS2::EntityEllipse:{ |
| | |
| | |
| | const auto* const ellipse=static_cast<const RS_Ellipse*>(cutEntity); |
| | if(RS_Math::isSameDirection( ellipse ->getAngle1(), ellipse ->getAngle2(), RS_TOLERANCE_ANGLE) |
| | && ! isnormal(ellipse->getAngle1()) |
| | && ! isnormal(ellipse->getAngle2()) |
| | ) { |
| | |
| | a=ellipse->getEllipseAngle(cutCoord); |
| | cut1 = new RS_Ellipse{cutEntity->getParent(), |
| | RS_EllipseData{ellipse ->getCenter(), |
| | ellipse ->getMajorP(), |
| | ellipse ->getRatio(), |
| | a,a+2.*M_PI, |
| | ellipse ->isReversed() |
| | } |
| | }; |
| | cut1->setPen(originalPen); |
| | cut1->setLayer(originalLayer); |
| | break; |
| | }else{ |
| | |
| | |
| | |
| | |
| | } |
| | } |
| | |
| | default: |
| | cut1 = (RS_AtomicEntity*)cutEntity->clone(); |
| | cut2 = (RS_AtomicEntity*)cutEntity->clone(); |
| |
|
| | cut1->trimEndpoint(cutCoord); |
| | cut2->trimStartpoint(cutCoord); |
| | } |
| | |
| | container->addEntity(cut1); |
| | if (cut2 != nullptr) { |
| | container->addEntity(cut2); |
| | } |
| |
|
| | if (handleUndo) { |
| | LC_UndoSection undo(document, viewport); |
| |
|
| | undo.addUndoable(cut1); |
| | if (cut2 != nullptr) { |
| | undo.addUndoable(cut2); |
| | } |
| | cutEntity->setUndoState(true); |
| | undo.addUndoable(cutEntity); |
| | } |
| |
|
| | viewport->notifyChanged(); |
| |
|
| | return true; |
| | } |
| |
|
| |
|
| |
|
| | |
| | |
| | |
| |
|
| | bool RS_Modification::stretch(const RS_Vector& firstCorner, |
| | const RS_Vector& secondCorner, |
| | const RS_Vector& offset, |
| | bool removeOriginals) { |
| |
|
| | if (!offset.valid) { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::stretch: Offset invalid"); |
| | return false; |
| | } |
| |
|
| | std::vector<RS_Entity*> addList; |
| |
|
| | |
| | for(auto e: *container){ |
| | if (e && e->isVisible() && !e->isLocked()) { |
| | if ((e->isInWindow(firstCorner, secondCorner) || |
| | e->hasEndpointsWithinWindow(firstCorner, secondCorner))) { |
| | RS_Entity* ec = e->clone(); |
| | ec->stretch(firstCorner, secondCorner, offset); |
| | addList.push_back(ec); |
| | e->setSelected(true); |
| | } |
| | } |
| | } |
| |
|
| | LC_UndoSection undo( document,viewport, handleUndo); |
| | if (removeOriginals) { |
| | deselectOriginals(true); |
| | } |
| | addNewEntities(addList); |
| |
|
| | return true; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | std::unique_ptr<LC_BevelResult> RS_Modification::bevel( |
| | const RS_Vector &coord1, RS_AtomicEntity *entity1, |
| | const RS_Vector &coord2, RS_AtomicEntity *entity2, |
| | RS_BevelData &data, |
| | bool previewOnly){ |
| |
|
| | RS_DEBUG->print("RS_Modification::bevel"); |
| |
|
| | if (!(entity1 && entity2)){ |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::bevel: At least one entity is nullptr"); |
| | return nullptr; |
| | } |
| | if (entity1->isLocked() || !entity1->isVisible()) { |
| | return nullptr; |
| | } |
| | if (entity2->isLocked() || !entity2->isVisible()) { |
| | return nullptr; |
| | } |
| |
|
| | RS_EntityContainer *baseContainer = container; |
| | bool isPolyline = false; |
| | |
| |
|
| | LC_UndoSection undo(document, viewport,handleUndo); |
| |
|
| | |
| |
|
| | auto result = std::make_unique<LC_BevelResult>(); |
| |
|
| | |
| | if (entity1->getParent() && entity1->getParent()->rtti() == RS2::EntityPolyline){ |
| | RS_DEBUG->print("RS_Modification::bevel: trimming polyline segments"); |
| | if (entity1->getParent() != entity2->getParent()){ |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::bevel: entities not in the same polyline"); |
| | result->error = LC_BevelResult::ERR_NOT_THE_SAME_POLYLINE; |
| | return result; |
| | } |
| | |
| | |
| |
|
| | |
| | auto *cl = dynamic_cast<RS_EntityContainer *>(entity1->getParent()->clone()); |
| | baseContainer = cl; |
| | if (handleUndo && !previewOnly){ |
| | container->addEntity(cl); |
| | |
| | undo.addUndoable(cl); |
| |
|
| | undo.addUndoable(entity1->getParent()); |
| | entity1->getParent()->setUndoState(true); |
| |
|
| | } |
| |
|
| | entity1 = (RS_AtomicEntity *) baseContainer->entityAt(entity1->getParent()->findEntity(entity1)); |
| | entity2 = (RS_AtomicEntity *) baseContainer->entityAt(entity2->getParent()->findEntity(entity2)); |
| |
|
| | |
| | isPolyline = true; |
| | |
| | } |
| |
|
| | RS_DEBUG->print("RS_Modification::bevel: getting intersection"); |
| |
|
| | RS_VectorSolutions sol = |
| | RS_Information::getIntersection(entity1, entity2, false); |
| |
|
| | if (sol.getNumber() == 0){ |
| | result->error = LC_BevelResult::ERR_NO_INTERSECTION; |
| | return result; |
| | } |
| |
|
| | RS_AtomicEntity *trimmed1 = nullptr; |
| | RS_AtomicEntity *trimmed2 = nullptr; |
| |
|
| |
|
| | result->polyline = isPolyline; |
| |
|
| | |
| | if (isPolyline){ |
| | trimmed1 = entity1; |
| | trimmed2 = entity2; |
| | |
| | |
| | data.trim = true; |
| | } else { |
| | trimmed1 = (RS_AtomicEntity *) entity1->clone(); |
| | trimmed2 = (RS_AtomicEntity *) entity2->clone(); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | RS_DEBUG->print("RS_Modification::bevel: trim entities to intersection 01"); |
| | bool start1 = false; |
| | RS_Vector is = sol.getClosest(coord2); |
| | result->intersectionPoint = is; |
| |
|
| | RS2::Ending ending1 = trimmed1->getTrimPoint(coord1, is); |
| | switch (ending1) { |
| | case RS2::EndingStart: |
| | trimmed1->trimStartpoint(is); |
| | start1 = true; |
| | break; |
| | case RS2::EndingEnd: |
| | trimmed1->trimEndpoint(is); |
| | start1 = false; |
| | break; |
| | default: |
| | break; |
| | } |
| |
|
| | result->trimStart1 = start1; |
| |
|
| | RS_DEBUG->print("RS_Modification::bevel: trim entities to intersection 02"); |
| | bool start2 = false; |
| | is = sol.getClosest(coord1); |
| | RS2::Ending ending2 = trimmed2->getTrimPoint(coord2, is); |
| | switch (ending2) { |
| | case RS2::EndingStart: |
| | trimmed2->trimStartpoint(is); |
| | start2 = true; |
| | break; |
| | case RS2::EndingEnd: |
| | trimmed2->trimEndpoint(is); |
| | start2 = false; |
| | break; |
| | default: |
| | break; |
| | } |
| | |
| | result->trimStart2 = start2; |
| |
|
| | |
| | RS_DEBUG->print("RS_Modification::bevel: find definitive bevel points"); |
| | RS_Vector bp1 = trimmed1->getNearestDist(data.length1, start1); |
| | RS_Vector bp2 = trimmed2->getNearestDist(data.length2, start2); |
| |
|
| | |
| | RS_DEBUG->print("RS_Modification::bevel: final trim"); |
| | if (data.trim){ |
| | switch (ending1) { |
| | case RS2::EndingStart: |
| | trimmed1->trimStartpoint(bp1); |
| | break; |
| | case RS2::EndingEnd: |
| | trimmed1->trimEndpoint(bp1); |
| | break; |
| | default: |
| | break; |
| | } |
| |
|
| | switch (ending2) { |
| | case RS2::EndingStart: |
| | trimmed2->trimStartpoint(bp2); |
| | break; |
| | case RS2::EndingEnd: |
| | trimmed2->trimEndpoint(bp2); |
| | break; |
| | default: |
| | break; |
| | } |
| |
|
| | |
| | if (!isPolyline && !previewOnly){ |
| | container->addEntity(trimmed1); |
| | container->addEntity(trimmed2); |
| | } |
| | } |
| |
|
| |
|
| | |
| | RS_DEBUG->print("RS_Modification::bevel: add bevel line"); |
| | RS_Line *bevel = nullptr; |
| |
|
| | if (previewOnly){ |
| | bevel = new RS_Line(nullptr, bp1, bp2); |
| | } |
| | else{ |
| | bevel = new RS_Line(baseContainer, bp1, bp2); |
| | } |
| |
|
| | result->bevel = bevel; |
| |
|
| | if (!isPolyline){ |
| | if (!previewOnly){ |
| | baseContainer->addEntity(bevel); |
| | } |
| | } else { |
| | int idx1 = baseContainer->findEntity(trimmed1); |
| | int idx2 = baseContainer->findEntity(trimmed2); |
| | int idx = idx1; |
| | |
| | if (idx1 > idx2){ |
| | |
| | idx1 = idx2; |
| | idx2 = idx; |
| | RS_AtomicEntity *trimmedTmp = trimmed1; |
| | trimmed1 = trimmed2; |
| | trimmed2 = trimmedTmp; |
| | } |
| | idx = idx1; |
| |
|
| | if (!previewOnly){ |
| | bevel->setSelected(baseContainer->isSelected()); |
| | bevel->setLayer(baseContainer->getLayer()); |
| | bevel->setPen(baseContainer->getPen(false)); |
| | } |
| |
|
| | |
| | if (trimmed1 == baseContainer->first() && trimmed2 == baseContainer->last() && baseContainer->count() > 2){ |
| | |
| | if (trimmed2->getEndpoint().distanceTo(bevel->getStartpoint()) > 1.0e-4){ |
| | bevel->reverse(); |
| | } |
| | idx = idx2; |
| | } else { |
| | |
| | if (trimmed1->getEndpoint().distanceTo(bevel->getStartpoint()) > 1.0e-4){ |
| | bevel->reverse(); |
| | } |
| | } |
| | baseContainer->insertEntity(idx + 1, bevel); |
| | } |
| |
|
| | result->trimmed1 = trimmed1; |
| | result->trimmed2 = trimmed2; |
| |
|
| | if (isPolyline){ |
| | auto* polyline = static_cast<RS_Polyline *>(baseContainer); |
| | polyline->updateEndpoints(); |
| | result->polyline = polyline; |
| | } |
| |
|
| | RS_DEBUG->print("RS_Modification::bevel: handling undo"); |
| |
|
| | if (handleUndo && !previewOnly){ |
| | if (!isPolyline && data.trim){ |
| | undo.addUndoable(trimmed1); |
| | entity1->setUndoState(true); |
| | undo.addUndoable(entity1); |
| |
|
| | undo.addUndoable(trimmed2); |
| | entity2->setUndoState(true); |
| | undo.addUndoable(entity2); |
| | } |
| |
|
| | if (!isPolyline){ |
| | undo.addUndoable(bevel); |
| | } |
| | } |
| | |
| | if (!(data.trim || isPolyline)){ |
| | RS_DEBUG->print("RS_Modification::bevel: delete trimmed elements"); |
| | delete trimmed1; |
| | delete trimmed2; |
| | RS_DEBUG->print("RS_Modification::bevel: delete trimmed elements: ok"); |
| | } |
| |
|
| | viewport->notifyChanged(); |
| | return result; |
| | } |
| |
|
| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | std::unique_ptr<LC_RoundResult> RS_Modification::round(const RS_Vector& coord, |
| | const RS_Vector& coord1, |
| | RS_AtomicEntity* entity1, |
| | const RS_Vector& coord2, |
| | RS_AtomicEntity* entity2, |
| | RS_RoundData& data){ |
| |
|
| | if (!(entity1 && entity2)){ |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::round: At least one entity is nullptr"); |
| | return nullptr; |
| | } |
| | if (entity1->isLocked() || !entity1->isVisible()) { |
| | return nullptr; |
| | } |
| | if (entity2->isLocked() || !entity2->isVisible()) { |
| | return nullptr; |
| | } |
| |
|
| | auto result = std::make_unique<LC_RoundResult>(); |
| |
|
| | RS_EntityContainer *baseContainer = container; |
| | bool isPolyline = false; |
| | |
| |
|
| | LC_UndoSection undo(document, viewport,handleUndo); |
| |
|
| | auto parent1 = entity1->getParent(); |
| | auto parent2 = entity2->getParent(); |
| | |
| | if (parent1 != nullptr && parent1->rtti() == RS2::EntityPolyline){ |
| |
|
| | if (parent1 != parent2){ |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::round: entities not in " |
| | "the same polyline"); |
| | result->error = LC_RoundResult::ERR_NOT_THE_SAME_POLYLINE; |
| | return result; |
| | } |
| |
|
| | |
| | auto cl = dynamic_cast<RS_EntityContainer *>(parent1->clone()); |
| | baseContainer = cl; |
| |
|
| | if (handleUndo){ |
| | container->addEntity(cl); |
| | undo.addUndoable(cl); |
| | undo.addUndoable(parent1); |
| | parent1->setUndoState(true); |
| | } |
| |
|
| | int index1 = parent1->findEntity(entity1); |
| | entity1 = static_cast<RS_AtomicEntity*>(baseContainer->entityAt(index1)); |
| |
|
| | int index2 = parent2->findEntity(entity2); |
| | entity2 = static_cast<RS_AtomicEntity*>(baseContainer->entityAt(index2)); |
| |
|
| | isPolyline = true; |
| | result->polyline = true; |
| | |
| | } |
| |
|
| | |
| | RS_Creation creation(nullptr, nullptr); |
| | std::unique_ptr<RS_Entity> par1 { creation.createParallel(coord, data.radius, 1, entity1)}; |
| | std::unique_ptr<RS_Entity> par2 { creation.createParallel(coord, data.radius, 1, entity2)}; |
| |
|
| | if ((par1 == nullptr) || (par2 == nullptr)) { |
| | result->error = LC_RoundResult::NO_PARALLELS; |
| | return result; |
| | } |
| |
|
| | RS_VectorSolutions sol2 = |
| | RS_Information::getIntersection(entity1, entity2, false); |
| |
|
| | RS_VectorSolutions sol = |
| | RS_Information::getIntersection(par1.get(), par2.get(), false); |
| |
|
| | if (sol.getNumber() == 0){ |
| | result->error = LC_RoundResult::ERR_NO_INTERSECTION; |
| | return result; |
| | } |
| |
|
| | |
| | RS_Vector is = sol.getClosest(coord); |
| | RS_Vector p1 = entity1->getNearestPointOnEntity(is, false); |
| | RS_Vector p2 = entity2->getNearestPointOnEntity(is, false); |
| | double ang1 = is.angleTo(p1); |
| | double ang2 = is.angleTo(p2); |
| | bool reversed = (RS_Math::getAngleDifference(ang1, ang2) > M_PI); |
| | bool isTrimming = data.radius <= RS_TOLERANCE; |
| | auto arc = std::make_unique<RS_Arc>(baseContainer, |
| | RS_ArcData(is, |
| | data.radius, |
| | ang1, ang2, |
| | reversed)); |
| |
|
| | result->round = isTrimming ? nullptr : arc.get(); |
| |
|
| | RS_AtomicEntity *trimmed1 = nullptr; |
| | RS_AtomicEntity *trimmed2 = nullptr; |
| |
|
| | if (data.trim || isPolyline){ |
| | if (isPolyline){ |
| | trimmed1 = entity1; |
| | trimmed2 = entity2; |
| | } else { |
| | trimmed1 = static_cast<RS_AtomicEntity*>(entity1->clone()); |
| | trimmed2 = static_cast<RS_AtomicEntity*>(entity2->clone()); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | RS_Vector is2 = sol2.getClosest(coord2); |
| | RS2::Ending ending1 = trimmed1->getTrimPoint(coord1, is2); |
| | switch (ending1) { |
| | case RS2::EndingStart: |
| | trimmed1->trimStartpoint(p1); |
| | result->trim1Mode = LC_RoundResult::TRIM_START; |
| | break; |
| | case RS2::EndingEnd: |
| | trimmed1->trimEndpoint(p1); |
| | result->trim1Mode = LC_RoundResult::TRIM_END; |
| | break; |
| | default: |
| | trimmed1 = trimCircleForRound(trimmed1, *arc); |
| | result->trim1Mode = LC_RoundResult::TRIM_CIRCLE; |
| | break; |
| | } |
| |
|
| | is2 = sol2.getClosest(coord1); |
| | RS2::Ending ending2 = trimmed2->getTrimPoint(coord2, is2); |
| | switch (ending2) { |
| | case RS2::EndingStart: |
| | trimmed2->trimStartpoint(p2); |
| | result -> trim2Mode = LC_RoundResult::TRIM_START; |
| | break; |
| | case RS2::EndingEnd: |
| | trimmed2->trimEndpoint(p2); |
| | result->trim2Mode = LC_RoundResult::TRIM_END; |
| | break; |
| | default: |
| | trimmed2 = trimCircleForRound(trimmed2, *arc); |
| | result->trim2Mode = LC_RoundResult::TRIM_CIRCLE; |
| | break; |
| | } |
| |
|
| | |
| | if (!isPolyline){ |
| | container->addEntity(trimmed1); |
| | container->addEntity(trimmed2); |
| | } |
| | } |
| |
|
| | |
| | if (!isPolyline){ |
| | if (!isTrimming) |
| | baseContainer->addEntity(arc.get()); |
| | } else { |
| | |
| | int idx1 = baseContainer->findEntity(trimmed1); |
| | int idx2 = baseContainer->findEntity(trimmed2); |
| |
|
| | arc->setSelected(baseContainer->isSelected()); |
| | arc->setLayer(baseContainer->getLayer()); |
| | arc->setPen(baseContainer->getPen(false)); |
| |
|
| | RS_DEBUG->print("RS_Modification::round: idx1<idx2: %d", (int) (idx1 < idx2)); |
| | RS_DEBUG->print("RS_Modification::round: idx1!=0: %d", (int) (idx1 != 0)); |
| | RS_DEBUG->print("RS_Modification::round: idx2==0: %d", (int) (idx2 == 0)); |
| | RS_DEBUG->print("RS_Modification::round: idx1==(int)baseContainer->count()-1: %d", |
| | (int) (idx1 == (int) baseContainer->count() - 1)); |
| |
|
| | bool insertAfter1 = ((idx1 < idx2 && idx1 != 0) || (idx1 == 0 && idx2 == 1) || |
| | (idx2 == 0 && idx1 == (int) baseContainer->count() - 1)); |
| |
|
| | |
| | |
| | |
| | |
| | if (insertAfter1){ |
| | if (trimmed1->getEndpoint().distanceTo(arc->getStartpoint()) > 1.0e-4){ |
| | arc->reverse(); |
| | } |
| | if (!isTrimming) |
| | baseContainer->insertEntity(idx1 + 1, arc.get()); |
| | } else { |
| | if (trimmed2->getEndpoint().distanceTo(arc->getStartpoint()) > 1.0e-4){ |
| | arc->reverse(); |
| | } |
| | if (!isTrimming) |
| | baseContainer->insertEntity(idx2 + 1, arc.get()); |
| | } |
| | } |
| |
|
| | result->trimmed1 = trimmed1; |
| | result->trimmed2 = trimmed2; |
| |
|
| | if (isPolyline){ |
| | static_cast<RS_Polyline*>(baseContainer)->updateEndpoints(); |
| | } |
| |
|
| | if (handleUndo){ |
| | if (!isPolyline && data.trim){ |
| | undo.addUndoable(trimmed1); |
| | entity1->setUndoState(true); |
| | undo.addUndoable(entity1); |
| |
|
| | undo.addUndoable(trimmed2); |
| | entity2->setUndoState(true); |
| | undo.addUndoable(entity2); |
| | } |
| |
|
| | if (!isPolyline){ |
| | undo.addUndoable(arc.release()); |
| | } |
| | } |
| |
|
| | if (!isTrimming) |
| | arc.release(); |
| |
|
| | viewport->notifyChanged(); |
| | return result; |
| | } |
| |
|
| |
|
| | |
| | |
| | |
| | static void updateExplodedChildrenRecursively( |
| | RS_EntityContainer* ec, |
| | RS_Entity* e, |
| | RS_Entity* clone, |
| | RS2::ResolveLevel rl, |
| | bool resolveLayer, |
| | bool resolvePen) { |
| |
|
| | if (ec == nullptr || e == nullptr || clone == nullptr) { |
| | return; |
| | } |
| |
|
| | if (resolveLayer) { |
| | clone->setLayer(ec->getLayer()); |
| | } else { |
| | clone->setLayer(e->getLayer()); |
| | } |
| |
|
| | if (resolvePen) { |
| | |
| | clone->setPen(ec->getPen(false)); |
| | } else { |
| | clone->setPen(e->getPen(false)); |
| | } |
| |
|
| | clone->update(); |
| |
|
| | if (clone->isContainer()) { |
| | |
| | |
| | ec = static_cast<RS_EntityContainer*>(clone); |
| | for(RS_Entity* en: lc::LC_ContainerTraverser{*ec, rl}.entities()) { |
| | if (en != nullptr) { |
| | |
| | updateExplodedChildrenRecursively(ec, clone, en, |
| | rl, resolveLayer, resolvePen); |
| | } |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | bool RS_Modification::explode(const bool remove , const bool forceUndoableOperation) { |
| | if (container == nullptr) { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::explode: no valid container for addinge entities"); |
| | return false; |
| | } |
| |
|
| | std::vector<RS_Entity*> selectedEntities; |
| | RS_Layer* activeLayer = graphic->getActiveLayer(); |
| |
|
| | |
| | |
| | const bool isFrozen = activeLayer->isFrozen(); |
| | activeLayer->freeze(false); |
| | container->collectSelected(selectedEntities, false); |
| | activeLayer->freeze(isFrozen); |
| |
|
| | return explode(selectedEntities, remove, forceUndoableOperation); |
| | } |
| |
|
| | |
| | bool RS_Modification::explode(const std::vector<RS_Entity*> &entitiesList, const bool remove, const bool forceUndoableOperation, [[maybe_unused]]const bool keepSelected) { |
| | if (container->isLocked() || ! container->isVisible()) { |
| | return false; |
| | } |
| |
|
| | std::vector<RS_Entity*> clonesList; |
| | |
| | std::vector<RS_Entity*> toDelete; |
| |
|
| | for(auto e: entitiesList){ |
| | if (e->isContainer()) { |
| | |
| | auto* ec = static_cast<RS_EntityContainer*>(e); |
| | |
| |
|
| | |
| | |
| | |
| | RS2::ResolveLevel rl; |
| | bool resolvePen; |
| | bool resolveLayer; |
| |
|
| | auto containerType = ec->rtti(); |
| | switch (containerType) { |
| | case RS2::EntityMText: |
| | case RS2::EntityText: |
| | case RS2::EntityHatch: |
| | case RS2::EntityPolyline: |
| | rl = RS2::ResolveAll; |
| | resolveLayer = true; |
| | resolvePen = true; |
| | break; |
| |
|
| | case RS2::EntityInsert: |
| | resolvePen = false; |
| | resolveLayer = false; |
| | rl = RS2::ResolveNone; |
| | break; |
| |
|
| | default: |
| | if (RS2::isDimensionalEntity(containerType)) { |
| | rl = RS2::ResolveNone; |
| | resolveLayer = true; |
| | resolvePen = true; |
| | } |
| | else { |
| | rl = RS2::ResolveAll; |
| | resolveLayer = true; |
| | resolvePen = false; |
| | } |
| | break; |
| | } |
| |
|
| | auto entities = lc::LC_ContainerTraverser{*ec, rl}.entities(); |
| | for(RS_Entity* e2: entities) { |
| | if (e2 != nullptr) { |
| | RS_Entity* clone = e2->clone(); |
| | clone->setSelected(false); |
| | clone->reparent(container); |
| |
|
| | clonesList.push_back(clone); |
| |
|
| | |
| | |
| | |
| | |
| | updateExplodedChildrenRecursively(ec, e2, clone, |
| | rl, resolveLayer, resolvePen); |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | } |
| | } |
| | toDelete.push_back(e); |
| | } else { |
| | e->setSelected(false); |
| | } |
| | } |
| |
|
| | deleteOriginalAndAddNewEntities(clonesList, toDelete, false, remove, forceUndoableOperation); |
| | clonesList.clear(); |
| |
|
| | container->updateInserts(); |
| | return true; |
| | } |
| |
|
| | bool RS_Modification::explodeTextIntoLetters(bool keepSelected) { |
| | if (container == nullptr) { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::explodeTextIntoLetters: no valid container for adding entities"); |
| | return false; |
| | } |
| |
|
| | std::vector<RS_Entity*> selectedEntities; |
| | container->collectSelected(selectedEntities, false); |
| | return explodeTextIntoLetters(selectedEntities, keepSelected); |
| | } |
| |
|
| | bool RS_Modification::explodeTextIntoLetters(const std::vector<RS_Entity*> &entitiesList, [[maybe_unused]]bool keepSelected) { |
| | if(container->isLocked() || ! container->isVisible()) { |
| | return false; |
| | } |
| | std::vector<RS_Entity*> clonesList; |
| |
|
| | for(auto e: entitiesList){ |
| | if (e->rtti()==RS2::EntityMText) { |
| | |
| | auto text = dynamic_cast<RS_MText *>(e); |
| | explodeTextIntoLetters(text, clonesList); |
| | } else if (e->rtti()==RS2::EntityText) { |
| | |
| | auto text = dynamic_cast<RS_Text *>(e); |
| | explodeTextIntoLetters(text, clonesList); |
| | } else { |
| | e->setSelected(false); |
| | } |
| | } |
| |
|
| | deleteOriginalAndAddNewEntities(clonesList, entitiesList, false, true); |
| | clonesList.clear(); |
| | return true; |
| | } |
| |
|
| | bool RS_Modification::explodeTextIntoLetters(RS_MText* text, std::vector<RS_Entity*>& addList) { |
| | if (text == nullptr) { |
| | return false; |
| | } |
| | if(text->isLocked() || ! text->isVisible()) { |
| | return false; |
| | } |
| |
|
| | |
| | for(auto e2: *text){ |
| | if (e2 == nullptr) { |
| | break; |
| | } |
| |
|
| | |
| | if (e2->rtti()==RS2::EntityContainer) { |
| |
|
| | auto line = dynamic_cast<RS_EntityContainer *>(e2); |
| |
|
| | |
| | for(auto e3: *line){ |
| | if (e3 == nullptr) { |
| | break; |
| | } |
| |
|
| | if (e3->rtti()==RS2::EntityMText) { |
| | auto e3MText = dynamic_cast<RS_MText *>(e3); |
| | explodeTextIntoLetters(e3MText, addList); |
| | } |
| | else if (e3->rtti()==RS2::EntityInsert) { |
| | auto letter = dynamic_cast<RS_Insert *>(e3); |
| |
|
| | auto tl = new RS_MText( |
| | container, |
| | RS_MTextData(letter->getInsertionPoint(), |
| | text->getHeight(), |
| | 100.0, |
| | RS_MTextData::VABottom, RS_MTextData::HALeft, |
| | RS_MTextData::LeftToRight, RS_MTextData::Exact, |
| | 1.0, |
| | letter->getName(), |
| | text->getStyle(), |
| | letter->getAngle(), |
| | RS2::Update)); |
| |
|
| | tl->setLayer(text->getLayer()); |
| | tl->setPen(text->getPen(false)); |
| |
|
| | addList.push_back(tl); |
| | tl->update(); |
| | } |
| | } |
| | } |
| | } |
| |
|
| | return true; |
| | } |
| |
|
| | bool RS_Modification::explodeTextIntoLetters(RS_Text* text, std::vector<RS_Entity*>& addList) { |
| |
|
| | if (text == nullptr) { |
| | return false; |
| | } |
| |
|
| | if(text->isLocked() || ! text->isVisible()) return false; |
| |
|
| | |
| | for(auto e2: *text){ |
| |
|
| | if (e2 == nullptr) { |
| | break; |
| | } |
| |
|
| | if (e2->rtti()==RS2::EntityInsert) { |
| |
|
| | auto letter = dynamic_cast<RS_Insert *>(e2); |
| |
|
| | auto tl = new RS_Text( |
| | container, |
| | RS_TextData(letter->getInsertionPoint(), |
| | letter->getInsertionPoint(), |
| | text->getHeight(), |
| | text->getWidthRel(), RS_TextData::VABaseline, |
| | RS_TextData::HALeft, RS_TextData::None, |
| | letter->getName(), |
| | text->getStyle(), |
| | letter->getAngle(), |
| | RS2::Update)); |
| |
|
| | tl->setLayer(text->getLayer()); |
| | tl->setPen(text->getPen(false)); |
| |
|
| | addList.push_back(tl); |
| | tl->update(); |
| | } |
| | } |
| |
|
| | return true; |
| | } |
| |
|
| | |
| | |
| | |
| | bool RS_Modification::moveRef(RS_MoveRefData& data) { |
| | if (container == nullptr) { |
| | RS_DEBUG->print(RS_Debug::D_WARNING, |
| | "RS_Modification::moveRef: no valid container"); |
| | return false; |
| | } |
| | if(container->isLocked() || ! container->isVisible()) return false; |
| |
|
| | std::vector<RS_Entity*> addList; |
| |
|
| | |
| | for(auto e: *container){ |
| | if (e != nullptr && e->isSelected()) { |
| | RS_Entity* ec = e->clone(); |
| |
|
| | ec->moveRef(data.ref, data.offset); |
| | |
| | ec->setSelected(true); |
| | addList.push_back(ec); |
| | } |
| | } |
| |
|
| | LC_UndoSection undo( document,viewport, handleUndo); |
| | deselectOriginals(true); |
| | addNewEntities(addList); |
| |
|
| | return true; |
| | } |
| |
|