Path: blob/main/src/netedit/elements/moving/GNEMoveElementEdge.cpp
185790 views
/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.3// This program and the accompanying materials are made available under the4// terms of the Eclipse Public License 2.0 which is available at5// https://www.eclipse.org/legal/epl-2.0/6// This Source Code may also be made available under the following Secondary7// Licenses when the conditions for such availability set forth in the Eclipse8// Public License 2.0 are satisfied: GNU General Public License, version 29// or later which is available at10// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later12/****************************************************************************/13/// @file GNEMoveElementEdge.cpp14/// @author Pablo Alvarez Lopez15/// @date Oct 202516///17// Class used for moving edge shapes18/****************************************************************************/19#include <config.h>2021#include <netedit/changes/GNEChange_Attribute.h>22#include <netedit/GNENet.h>23#include <netedit/GNEUndoList.h>2425#include "GNEMoveElementEdge.h"2627// ===========================================================================28// defines29// ===========================================================================3031#define ENDPOINT_TOLERANCE 23233// ===========================================================================34// Method definitions35// ===========================================================================3637GNEMoveElementEdge::GNEMoveElementEdge(GNEEdge* edge) :38GNEMoveElement(edge),39myEdge(edge) {40}414243GNEMoveElementEdge::~GNEMoveElementEdge() {}444546GNEEdge*47GNEMoveElementEdge::getEdge() const {48return myEdge;49}505152GNEMoveOperation*53GNEMoveElementEdge::getMoveOperation() {54// get geometry point radius55const double geometryPointRadius = myEdge->getGeometryPointRadius();56// check if edge is selected57if (myEdge->isAttributeCarrierSelected()) {58// check if both junctions are selected59if (myEdge->getFromJunction()->isAttributeCarrierSelected() && myEdge->getToJunction()->isAttributeCarrierSelected()) {60return processMoveBothJunctionSelected();61} else if (myEdge->getFromJunction()->isAttributeCarrierSelected()) {62return processMoveFromJunctionSelected(myEdge->getNBEdge()->getGeometry(), myEdge->getNet()->getViewNet()->getPositionInformation(), geometryPointRadius);63} else if (myEdge->getToJunction()->isAttributeCarrierSelected()) {64return processMoveToJunctionSelected(myEdge->getNBEdge()->getGeometry(), myEdge->getNet()->getViewNet()->getPositionInformation(), geometryPointRadius);65} else if (myEdge->getNet()->getViewNet()->getMoveMultipleElementValues().isMovingSelectedEdge()) {66if (myEdge->getNet()->getAttributeCarriers()->getNumberOfSelectedEdges() == 1) {67// special case: when only a single edge is selected, move all shape points (including custom end points)68return processMoveBothJunctionSelected();69} else {70// synchronized movement of a single point71return processNoneJunctionSelected(geometryPointRadius);72}73} else {74// calculate move shape operation (because there are only an edge selected)75return getEditShapeOperation(myEdge, myEdge->getNBEdge()->getGeometry(), false);76}77} else {78// calculate move shape operation79return getEditShapeOperation(myEdge, myEdge->getNBEdge()->getGeometry(), false);80}81}828384std::string85GNEMoveElementEdge::getMovingAttribute(SumoXMLAttr key) const {86return myMovedElement->getCommonAttribute(key);87}888990double91GNEMoveElementEdge::getMovingAttributeDouble(SumoXMLAttr key) const {92return myMovedElement->getCommonAttributeDouble(key);93}949596Position97GNEMoveElementEdge::getMovingAttributePosition(SumoXMLAttr key) const {98return myMovedElement->getCommonAttributePosition(key);99}100101102PositionVector103GNEMoveElementEdge::getMovingAttributePositionVector(SumoXMLAttr key) const {104return myMovedElement->getCommonAttributePositionVector(key);105}106107108void109GNEMoveElementEdge::setMovingAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {110myMovedElement->setCommonAttribute(key, value, undoList);111}112113114bool115GNEMoveElementEdge::isMovingAttributeValid(SumoXMLAttr key, const std::string& value) const {116return myMovedElement->isCommonAttributeValid(key, value);117}118119120void121GNEMoveElementEdge::setMovingAttribute(SumoXMLAttr key, const std::string& value) {122myMovedElement->setCommonAttribute(key, value);123}124125126void127GNEMoveElementEdge::removeGeometryPoint(const Position clickedPosition, GNEUndoList* undoList) {128// get geometry point radius129const double geometryPointRadius = myEdge->getGeometryPointRadius();130// declare shape to move131PositionVector shape = myEdge->getNBEdge()->getGeometry();132// obtain flags for start and end positions133const bool customStartPosition = (myEdge->getNBEdge()->getGeometry().front().distanceSquaredTo2D(myEdge->getFromJunction()->getNBNode()->getPosition()) > ENDPOINT_TOLERANCE);134const bool customEndPosition = (myEdge->getNBEdge()->getGeometry().back().distanceSquaredTo2D(myEdge->getToJunction()->getNBNode()->getPosition()) > ENDPOINT_TOLERANCE);135// get variable for last index136const int lastIndex = (int)myEdge->getNBEdge()->getGeometry().size() - 1;137// flag to enable/disable remove geometry point138bool removeGeometryPoint = true;139// obtain index140const int index = myEdge->getNBEdge()->getGeometry().indexOfClosest(clickedPosition, true);141// check index142if (index == -1) {143removeGeometryPoint = false;144}145// check distance146if (shape[index].distanceSquaredTo2D(clickedPosition) > (geometryPointRadius * geometryPointRadius)) {147removeGeometryPoint = false;148}149// check custom start position150if (!customStartPosition && (index == 0)) {151removeGeometryPoint = false;152}153// check custom end position154if (!customEndPosition && (index == lastIndex)) {155removeGeometryPoint = false;156}157// check if we can remove geometry point158if (removeGeometryPoint) {159// check if we're removing first geometry proint160if (index == 0) {161// commit new geometry start162undoList->begin(myEdge, TLF("remove first geometry point of %", myEdge->getTagStr()));163GNEChange_Attribute::changeAttribute(myEdge, GNE_ATTR_SHAPE_START, "", undoList);164undoList->end();165} else if (index == lastIndex) {166// commit new geometry end167undoList->begin(myEdge, TLF("remove last geometry point of %", myEdge->getTagStr()));168GNEChange_Attribute::changeAttribute(myEdge, GNE_ATTR_SHAPE_END, "", undoList);169undoList->end();170} else {171// remove geometry point172shape.erase(shape.begin() + index);173// get innen shape174shape.pop_front();175shape.pop_back();176// remove double points177shape.removeDoublePoints((geometryPointRadius * geometryPointRadius));178// commit new shape179undoList->begin(myEdge, TLF("remove geometry point of %", myEdge->getTagStr()));180GNEChange_Attribute::changeAttribute(myEdge, SUMO_ATTR_SHAPE, toString(shape), undoList);181undoList->end();182}183}184}185186187void188GNEMoveElementEdge::setMoveShape(const GNEMoveResult& moveResult) {189// get start and end points190const Position shapeStart = moveResult.shapeToUpdate.front();191const Position shapeEnd = moveResult.shapeToUpdate.back();192// get innen shape193PositionVector innenShape = moveResult.shapeToUpdate;194innenShape.pop_front();195innenShape.pop_back();196// set shape start197if (std::find(moveResult.geometryPointsToMove.begin(), moveResult.geometryPointsToMove.end(), 0) != moveResult.geometryPointsToMove.end()) {198myEdge->setShapeStartPos(shapeStart);199}200// set innen geometry201myEdge->setGeometry(innenShape, true);202// set shape end203if (std::find(moveResult.geometryPointsToMove.begin(), moveResult.geometryPointsToMove.end(), ((int)moveResult.shapeToUpdate.size() - 1)) != moveResult.geometryPointsToMove.end()) {204myEdge->setShapeEndPos(shapeEnd);205}206}207208209void210GNEMoveElementEdge::commitMoveShape(const GNEMoveResult& moveResult, GNEUndoList* undoList) {211// make sure that newShape isn't empty212if (moveResult.shapeToUpdate.size() > 1) {213// get innen shape214PositionVector innenShapeToUpdate = moveResult.shapeToUpdate;215innenShapeToUpdate.pop_front();216innenShapeToUpdate.pop_back();217// commit new shape218undoList->begin(myEdge, TLF("moving shape of %", myEdge->getTagStr()));219// update start position220if (std::find(moveResult.geometryPointsToMove.begin(), moveResult.geometryPointsToMove.end(), 0) != moveResult.geometryPointsToMove.end()) {221GNEChange_Attribute::changeAttribute(myEdge, GNE_ATTR_SHAPE_START, toString(moveResult.shapeToUpdate.front()), undoList);222}223// check if update shape224if (innenShapeToUpdate.size() > 0) {225GNEChange_Attribute::changeAttribute(myEdge, SUMO_ATTR_SHAPE, toString(innenShapeToUpdate), undoList);226}227// update end position228if (std::find(moveResult.geometryPointsToMove.begin(), moveResult.geometryPointsToMove.end(), ((int)moveResult.shapeToUpdate.size() - 1)) != moveResult.geometryPointsToMove.end()) {229GNEChange_Attribute::changeAttribute(myEdge, GNE_ATTR_SHAPE_END, toString(moveResult.shapeToUpdate.back()), undoList);230}231undoList->end();232}233}234235236GNEMoveOperation*237GNEMoveElementEdge::processMoveFromJunctionSelected(const PositionVector originalShape, const Position mousePosition, const double snapRadius) {238// calculate squared snapRadius239const double squaredSnapRadius = (snapRadius * snapRadius);240// declare shape to move241PositionVector shapeToMove = originalShape;242// obtain nearest index243const int nearestIndex = originalShape.indexOfClosest(mousePosition);244// obtain nearest position245const Position nearestPosition = originalShape.positionAtOffset2D(originalShape.nearest_offset_to_point2D(mousePosition));246// generate indexes247std::vector<int> indexes;248// check conditions249if (nearestIndex == -1) {250return nullptr;251} else if (nearestPosition == Position::INVALID) {252// special case for extremes253if (mousePosition.distanceSquaredTo2D(shapeToMove[nearestIndex]) <= squaredSnapRadius) {254for (int i = 1; i <= nearestIndex; i++) {255indexes.push_back(i);256}257// move extrem without creating new geometry point258return new GNEMoveOperation(this, originalShape, indexes, shapeToMove, indexes);259} else {260return nullptr;261}262} else if (nearestPosition.distanceSquaredTo2D(shapeToMove[nearestIndex]) <= squaredSnapRadius) {263for (int i = 1; i <= nearestIndex; i++) {264indexes.push_back(i);265}266// move geometry point without creating new geometry point267return new GNEMoveOperation(this, originalShape, indexes, shapeToMove, indexes);268} else {269// create new geometry point and keep new index (if we clicked near of shape)270const int newIndex = shapeToMove.insertAtClosest(nearestPosition, true);271for (int i = 1; i <= newIndex; i++) {272indexes.push_back(i);273}274// move after setting new geometry point in shapeToMove275return new GNEMoveOperation(this, originalShape, indexes, shapeToMove, indexes);276}277}278279280GNEMoveOperation*281GNEMoveElementEdge::processMoveToJunctionSelected(const PositionVector originalShape, const Position mousePosition, const double snapRadius) {282// calculate squared snapRadius283const double squaredSnapRadius = (snapRadius * snapRadius);284// declare shape to move285PositionVector shapeToMove = originalShape;286// obtain nearest index287const int nearestIndex = originalShape.indexOfClosest(mousePosition);288// obtain nearest position289const Position nearestPosition = originalShape.positionAtOffset2D(originalShape.nearest_offset_to_point2D(mousePosition));290// generate indexes291std::vector<int> indexes;292// check conditions293if (nearestIndex == -1) {294return nullptr;295} else if (nearestPosition == Position::INVALID) {296// special case for extremes297if (mousePosition.distanceSquaredTo2D(shapeToMove[nearestIndex]) <= squaredSnapRadius) {298for (int i = nearestIndex; i < ((int)originalShape.size() - 1); i++) {299indexes.push_back(i);300}301// move extrem without creating new geometry point302return new GNEMoveOperation(this, originalShape, indexes, shapeToMove, indexes);303} else {304return nullptr;305}306} else if (nearestPosition.distanceSquaredTo2D(shapeToMove[nearestIndex]) <= squaredSnapRadius) {307for (int i = nearestIndex; i < ((int)originalShape.size() - 1); i++) {308indexes.push_back(i);309}310// move geometry point without creating new geometry point311return new GNEMoveOperation(this, originalShape, indexes, shapeToMove, indexes);312} else {313// create new geometry point and keep new index (if we clicked near of shape)314const int newIndex = shapeToMove.insertAtClosest(nearestPosition, true);315for (int i = newIndex; i < ((int)originalShape.size() - 1); i++) {316indexes.push_back(i);317}318// move after setting new geometry point in shapeToMove319return new GNEMoveOperation(this, originalShape, indexes, shapeToMove, indexes);320}321}322323324GNEMoveOperation*325GNEMoveElementEdge::processMoveBothJunctionSelected() {326std::vector<int> geometryPointsToMove;327for (int i = 0; i < (int)myEdge->getNBEdge()->getGeometry().size(); i++) {328geometryPointsToMove.push_back(i);329}330// move entire shape (including extremes)331return new GNEMoveOperation(this, myEdge->getNBEdge()->getGeometry(), geometryPointsToMove, myEdge->getNBEdge()->getGeometry(), geometryPointsToMove);332}333334335GNEMoveOperation*336GNEMoveElementEdge::processNoneJunctionSelected(const double snapRadius) {337// get move multiple element values338const auto& moveMultipleElementValues = myEdge->getNet()->getViewNet()->getMoveMultipleElementValues();339// declare shape to move340PositionVector shapeToMove = myEdge->getNBEdge()->getGeometry();341// first check if kept offset is larger than geometry342if (shapeToMove.length2D() < moveMultipleElementValues.getEdgeOffset()) {343return nullptr;344}345// declare offset346double offset = 0;347// set offset depending of convex angle348if (myEdge->isConvexAngle()) {349offset = moveMultipleElementValues.getEdgeOffset();350} else {351offset = shapeToMove.length2D() - moveMultipleElementValues.getEdgeOffset();352}353// obtain offset position354const Position offsetPosition = myEdge->getNBEdge()->getGeometry().positionAtOffset2D(offset);355// obtain nearest index to offset position356const int nearestIndex = myEdge->getNBEdge()->getGeometry().indexOfClosest(offsetPosition);357// check conditions358if ((nearestIndex == -1) || (offsetPosition == Position::INVALID)) {359return nullptr;360} else if (offsetPosition.distanceSquaredTo2D(shapeToMove[nearestIndex]) <= (snapRadius * snapRadius)) {361// move geometry point without creating new geometry point362return new GNEMoveOperation(this, myEdge->getNBEdge()->getGeometry(), {nearestIndex}, shapeToMove, {nearestIndex});363} else {364// create new geometry point and keep new index (if we clicked near of shape)365const int newIndex = shapeToMove.insertAtClosest(offsetPosition, true);366// move after setting new geometry point in shapeToMove367return new GNEMoveOperation(this, myEdge->getNBEdge()->getGeometry(), {nearestIndex}, shapeToMove, {newIndex});368}369}370371372/****************************************************************************/373374375