pxmlw6n2f/Gazebo_Distributed_MPI/gazebo/gui/building/EditorView.cc

1466 lines
46 KiB
C++

/*
* Copyright (C) 2012 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <boost/bind.hpp>
#include "gazebo/math/Angle.hh"
#include "gazebo/common/Color.hh"
#include "gazebo/gui/Conversions.hh"
#include "gazebo/gui/building/ImportImageDialog.hh"
#include "gazebo/gui/building/GridLines.hh"
#include "gazebo/gui/building/EditorItem.hh"
#include "gazebo/gui/building/RectItem.hh"
#include "gazebo/gui/building/WindowItem.hh"
#include "gazebo/gui/building/DoorItem.hh"
#include "gazebo/gui/building/StairsItem.hh"
#include "gazebo/gui/building/FloorItem.hh"
#include "gazebo/gui/building/GrabberHandle.hh"
#include "gazebo/gui/building/WallSegmentItem.hh"
#include "gazebo/gui/building/BuildingMaker.hh"
#include "gazebo/gui/building/LevelInspectorDialog.hh"
#include "gazebo/gui/building/BuildingEditorEvents.hh"
#include "gazebo/gui/building/EditorView.hh"
using namespace gazebo;
using namespace gui;
/////////////////////////////////////////////////
EditorView::EditorView(QWidget *_parent)
: QGraphicsView(_parent), currentMouseItem(0)
{
this->setObjectName("editorView");
this->drawMode = NONE;
this->drawInProgress = false;
this->floorplanVisible = true;
this->elementsVisible = true;
this->connections.push_back(
gui::editor::Events::ConnectCreateBuildingEditorItem(
boost::bind(&EditorView::OnCreateEditorItem, this, _1)));
this->connections.push_back(
gui::editor::Events::ConnectColorSelected(
boost::bind(&EditorView::OnColorSelected, this, _1)));
this->connections.push_back(
gui::editor::Events::ConnectTextureSelected(
boost::bind(&EditorView::OnTextureSelected, this, _1)));
this->connections.push_back(
gui::editor::Events::ConnectNewBuildingModel(
boost::bind(&EditorView::OnDiscardModel, this)));
this->connections.push_back(
gui::editor::Events::ConnectAddBuildingLevel(
boost::bind(&EditorView::OnAddLevel, this)));
this->connections.push_back(
gui::editor::Events::ConnectDeleteBuildingLevel(
boost::bind(&EditorView::OnDeleteLevel, this)));
this->connections.push_back(
gui::editor::Events::ConnectChangeBuildingLevel(
boost::bind(&EditorView::OnChangeLevel, this, _1)));
this->connections.push_back(
gui::editor::Events::ConnectShowFloorplan(
boost::bind(&EditorView::OnShowFloorplan, this)));
this->connections.push_back(
gui::editor::Events::ConnectShowElements(
boost::bind(&EditorView::OnShowElements, this)));
this->mousePressRotation = 0;
this->buildingMaker = new BuildingMaker();
this->currentLevel = 0;
this->levelDefaultHeight = 250;
Level *newLevel = new Level();
newLevel->level = 0;
newLevel->baseHeight = 0;
newLevel->height = this->levelDefaultHeight;
newLevel->name = "Level 1";
this->levels.push_back(newLevel);
this->levelInspector = new LevelInspectorDialog();
this->levelInspector->setModal(false);
connect(this->levelInspector, SIGNAL(Applied()), this, SLOT(OnLevelApply()));
this->openLevelInspectorAct = new QAction(tr("&Open Level Inspector"), this);
this->openLevelInspectorAct->setStatusTip(tr("Open Level Inspector"));
connect(this->openLevelInspectorAct, SIGNAL(triggered()),
this, SLOT(OnOpenLevelInspector()));
this->addLevelAct = new QAction(tr("&Add Level"), this);
this->addLevelAct->setStatusTip(tr("Add Level"));
connect(this->addLevelAct, SIGNAL(triggered()),
this, SLOT(OnAddLevel()));
this->deleteLevelAct = new QAction(tr("&Delete Level"), this);
this->deleteLevelAct->setStatusTip(tr("Delete Level"));
connect(this->deleteLevelAct, SIGNAL(triggered()),
this, SLOT(OnDeleteLevel()));
this->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
// this->setRenderHint(QPainter::SmoothPixmapTransform);
// this->setRenderHint(QPainter::Antialiasing);
this->gridLines = NULL;
this->viewScale = 1.0;
this->levelCounter = 0;
this->mouseTooltip = new QGraphicsTextItem;
this->mouseTooltip->setPlainText(
"Oops! Color and texture can only be added in the 3D view.");
this->mouseTooltip->setZValue(10);
}
/////////////////////////////////////////////////
EditorView::~EditorView()
{
if (this->buildingMaker)
delete this->buildingMaker;
delete this->levelInspector;
}
/////////////////////////////////////////////////
void EditorView::scrollContentsBy(int _dx, int _dy)
{
QGraphicsView::scrollContentsBy(_dx, _dy);
if (this->gridLines && this->scene())
{
this->gridLines->setPos(this->mapToScene(QPoint(this->width()/2,
this->height()/2)));
}
}
/////////////////////////////////////////////////
void EditorView::resizeEvent(QResizeEvent *_event)
{
if (this->scene())
{
if (!this->gridLines)
{
this->gridLines = new GridLines(_event->size().width(),
_event->size().height());
this->scene()->addItem(this->gridLines);
}
else
{
this->gridLines->SetSize(_event->size().width(),
_event->size().height());
}
this->gridLines->setPos(this->mapToScene(
QPoint(_event->size().width()/2, _event->size().height()/2)));
}
}
/////////////////////////////////////////////////
void EditorView::contextMenuEvent(QContextMenuEvent *_event)
{
if (this->drawMode == COLOR || this->drawMode == TEXTURE)
return;
if (this->drawInProgress)
{
this->CancelDrawMode();
gui::editor::Events::createBuildingEditorItem(std::string());
_event->accept();
return;
}
QGraphicsItem *item = this->scene()->itemAt(this->mapToScene(_event->pos()));
if (item && item != this->levels[this->currentLevel]->backgroundPixmap)
{
_event->ignore();
QGraphicsView::contextMenuEvent(_event);
return;
}
QMenu menu(this);
menu.addAction(this->addLevelAct);
menu.addAction(this->deleteLevelAct);
menu.addAction(this->openLevelInspectorAct);
menu.exec(_event->globalPos());
_event->accept();
}
/////////////////////////////////////////////////
void EditorView::wheelEvent(QWheelEvent *_event)
{
int wheelIncr = 120;
int sign = (_event->delta() > 0) ? 1 : -1;
int delta = std::max(std::abs(_event->delta()), wheelIncr) * sign;
int numSteps = delta / wheelIncr;
QMatrix mat = matrix();
QPointF mousePosition = _event->pos();
mat.translate((width()/2) - mousePosition.x(), (height() / 2) -
mousePosition.y());
double scaleFactor = 1.15;
if (numSteps > 0)
{
mat.scale(numSteps*scaleFactor, numSteps*scaleFactor);
this->viewScale *= numSteps*scaleFactor;
}
else
{
mat.scale(-1/(numSteps*scaleFactor), -1/(numSteps*scaleFactor));
this->viewScale *= -1/(numSteps*scaleFactor);
}
mat.translate(mousePosition.x() - (this->width()/2),
mousePosition.y() -(this->height()/2));
this->setMatrix(mat);
if (this->gridLines)
{
this->gridLines->setPos(this->mapToScene(
QPoint(this->width()/2, this->height()/2)));
}
gui::editor::Events::changeBuildingEditorZoom(this->viewScale);
_event->accept();
}
/////////////////////////////////////////////////
void EditorView::mousePressEvent(QMouseEvent *_event)
{
if (!this->drawInProgress && this->drawMode != WALL && this->drawMode != COLOR
&& this->drawMode != TEXTURE && (_event->button() != Qt::RightButton))
{
QGraphicsItem *mouseItem =
this->scene()->itemAt(this->mapToScene(_event->pos()));
if (mouseItem && !mouseItem->isSelected())
{
EditorItem *editorItem = dynamic_cast<EditorItem*>(mouseItem);
if (editorItem)
{
this->scene()->clearSelection();
mouseItem->setSelected(true);
}
}
QGraphicsView::mousePressEvent(_event);
}
}
/////////////////////////////////////////////////
void EditorView::mouseReleaseEvent(QMouseEvent *_event)
{
if (this->drawMode == WALL)
{
this->DrawWall(_event->pos());
}
else if (this->drawMode != NONE)
{
if (this->drawInProgress)
{
if (this->drawMode == WINDOW)
{
this->windowList.push_back(dynamic_cast<WindowItem *>(
this->currentMouseItem));
}
else if (this->drawMode == DOOR)
{
this->doorList.push_back(dynamic_cast<DoorItem *>(
this->currentMouseItem));
}
else if (this->drawMode == STAIRS)
{
StairsItem *stairsItem = dynamic_cast<StairsItem *>(
this->currentMouseItem);
stairsItem->SetTexture3d("");
stairsItem->SetColor3d(common::Color::White);
this->stairsList.push_back(stairsItem);
if ((this->currentLevel) < static_cast<int>(floorList.size()))
{
this->buildingMaker->AttachManip(this->itemToVisualMap[stairsItem],
this->itemToVisualMap[floorList[this->currentLevel]]);
}
}
dynamic_cast<EditorItem *>(this->currentMouseItem)->
SetHighlighted(false);
this->drawMode = NONE;
this->drawInProgress = false;
gui::editor::Events::createBuildingEditorItem(std::string());
}
}
if (!this->drawInProgress)
this->currentMouseItem = NULL;
QGraphicsView::mouseReleaseEvent(_event);
}
/////////////////////////////////////////////////
void EditorView::mouseMoveEvent(QMouseEvent *_event)
{
switch (this->drawMode)
{
case NONE:
break;
case WALL:
{
WallSegmentItem *wallSegmentItem = dynamic_cast<WallSegmentItem *>(
this->currentMouseItem);
if (this->drawInProgress && wallSegmentItem)
{
this->snapToGrabber = false;
this->snapGrabberOther = NULL;
this->snapGrabberCurrent = NULL;
QPointF p1 = wallSegmentItem->line().p1();
QPointF p2 = this->mapToScene(_event->pos());
QPointF pf;
pf = p2;
if (!(QApplication::keyboardModifiers() & Qt::ShiftModifier))
{
double distanceToClose = 30;
// Snap to other walls' points
QList<QGraphicsItem *> itemsList = this->scene()->items(QRectF(
QPointF(p2.x() - distanceToClose/2, p2.y() - distanceToClose/2),
QPointF(p2.x() + distanceToClose/2, p2.y() + distanceToClose/2)));
for (QList<QGraphicsItem *>::iterator it = itemsList.begin();
it != itemsList.end(); ++it)
{
WallSegmentItem *anotherWall = dynamic_cast<WallSegmentItem *>
(*it);
if (anotherWall && anotherWall != wallSegmentItem)
{
if (ignition::math::Vector2d(Conversions::Convert(p2) -
anotherWall->StartPoint()).Length() <= distanceToClose)
{
pf = Conversions::Convert(anotherWall->StartPoint());
this->snapGrabberOther = anotherWall->Grabbers()[0];
this->snapGrabberCurrent = wallSegmentItem->Grabbers()[1];
this->snapToGrabber = true;
break;
}
else if (ignition::math::Vector2d(Conversions::Convert(p2) -
anotherWall->EndPoint()).Length() <= distanceToClose)
{
pf = Conversions::Convert(anotherWall->EndPoint());
this->snapGrabberOther = anotherWall->Grabbers()[1];
this->snapGrabberCurrent = wallSegmentItem->Grabbers()[1];
this->snapToGrabber = true;
break;
}
}
}
if (!this->snapToGrabber)
{
// Snap to angular increments
QLineF newLine(p1, p2);
double angle = GZ_DTOR(QLineF(p1, p2).angle());
double range = GZ_DTOR(SegmentItem::SnapAngle);
int angleIncrement = angle / range;
if ((angle - range*angleIncrement) > range*0.5)
angleIncrement++;
angle = -range*angleIncrement;
// Snap to length increments
double newLength = newLine.length();
double lengthIncrement = SegmentItem::SnapLength /
wallSegmentItem->Scale();
newLength = round(newLength/lengthIncrement)*lengthIncrement-
wallSegmentItem->Thickness();
pf.setX(p1.x() + qCos(angle)*newLength);
pf.setY(p1.y() + qSin(angle)*newLength);
}
}
wallSegmentItem->SetEndPoint(Conversions::Convert(pf));
wallSegmentItem->Update();
}
QApplication::setOverrideCursor(QCursor(Qt::CrossCursor));
break;
}
case WINDOW:
this->DrawWindow(_event->pos());
break;
case DOOR:
this->DrawDoor(_event->pos());
break;
case STAIRS:
this->DrawStairs(_event->pos());
break;
case COLOR:
case TEXTURE:
{
if (!this->mouseTooltip->scene())
this->scene()->addItem(this->mouseTooltip);
this->mouseTooltip->setPos(this->mapToScene(_event->pos()) +
QPointF(15, 15));
break;
}
default:
break;
}
// auto attach windows and doors to walls
QGraphicsItem *grabber = this->scene()->mouseGrabberItem();
if (!grabber)
grabber = this->currentMouseItem;
RectItem *editorItem = dynamic_cast<RectItem *>(grabber);
if (grabber && editorItem && (editorItem->ItemType() == "Window"
|| editorItem->ItemType() == "Door") )
{
if (grabber->parentItem())
{
WallSegmentItem *wallSegmentItem =
dynamic_cast<WallSegmentItem *>(grabber->parentItem());
if (wallSegmentItem)
{
QLineF segmentLine(wallSegmentItem->line());
segmentLine.setP1(wallSegmentItem->mapToScene(segmentLine.p1()));
segmentLine.setP2(wallSegmentItem->mapToScene(segmentLine.p2()));
QPointF mousePoint = this->mapToScene(_event->pos());
QPointF deltaLine = segmentLine.p2() - segmentLine.p1();
QPointF deltaMouse = mousePoint - segmentLine.p1();
double deltaLineMouse2 = deltaLine.x()*(-deltaMouse.y())
- (-deltaMouse.x())*deltaLine.y();
double deltaLine2 = (deltaLine.x()*deltaLine.x())
+ deltaLine.y()*deltaLine.y();
double mouseDotLine = deltaMouse.x()*deltaLine.x()
+ deltaMouse.y()*deltaLine.y();
double t = mouseDotLine / deltaLine2;
double distance = fabs(deltaLineMouse2) / sqrt(deltaLine2);
if (distance > 30 || t > 1.0 || t < 0.0)
{
editorItem->DetachFromParent();
wallSegmentItem->setZValue(wallSegmentItem->ZValueIdle());
editorItem->SetPositionOnWall(0);
editorItem->SetAngleOnWall(0);
this->buildingMaker->DetachFromParent(
this->itemToVisualMap[editorItem]);
editorItem->SetRotation(editorItem->Rotation()
- this->mousePressRotation);
editorItem->SetPosition(Conversions::Convert(mousePoint));
}
else
{
QPointF closest(segmentLine.p1() + t*deltaLine);
grabber->setPos(wallSegmentItem->mapFromScene(closest));
grabber->setRotation(wallSegmentItem->rotation());
QPointF absPositionOnWall = grabber->pos() -
wallSegmentItem->line().p1();
double positionLength = sqrt(absPositionOnWall.x()*
absPositionOnWall.x() +
absPositionOnWall.y()*
absPositionOnWall.y());
editorItem->SetPositionOnWall(positionLength /
wallSegmentItem->line().length());
}
return;
}
}
else
{
QList<QGraphicsItem *> overlaps = this->scene()->collidingItems(
grabber, Qt::IntersectsItemBoundingRect);
for (int i = 0; i < overlaps.size(); ++i)
{
WallSegmentItem *wallSegmentItem =
dynamic_cast<WallSegmentItem *>(overlaps[i]);
if (wallSegmentItem)
{
QPointF scenePos = grabber->scenePos();
if (wallSegmentItem->contains(wallSegmentItem->mapFromScene(
scenePos)))
{
editorItem->setParentItem(wallSegmentItem);
wallSegmentItem->setZValue(wallSegmentItem->ZValueIdle()+5);
this->buildingMaker->AttachManip(
this->itemToVisualMap[editorItem],
this->itemToVisualMap[wallSegmentItem]);
editorItem->SetPosition(
Conversions::Convert(wallSegmentItem->mapFromScene(scenePos)));
this->mousePressRotation = -wallSegmentItem->line().angle();
editorItem->SetRotation(this->mousePressRotation);
QPointF absPositionOnWall = editorItem->pos() -
wallSegmentItem->line().p1();
double positionLength = sqrt(absPositionOnWall.x()*
absPositionOnWall.x() +
absPositionOnWall.y()*
absPositionOnWall.y());
editorItem->SetPositionOnWall(positionLength /
wallSegmentItem->line().length());
return;
}
}
}
}
}
if (!drawInProgress)
{
QGraphicsView::mouseMoveEvent(_event);
}
}
/////////////////////////////////////////////////
void EditorView::leaveEvent(QEvent */*_event*/)
{
if (this->mouseTooltip &&
this->scene()->items().contains(this->mouseTooltip))
{
this->scene()->removeItem(this->mouseTooltip);
}
}
/////////////////////////////////////////////////
void EditorView::keyPressEvent(QKeyEvent *_event)
{
if (_event->key() == Qt::Key_Delete || _event->key() == Qt::Key_Backspace)
{
QList<QGraphicsItem *> selectedItems = this->scene()->selectedItems();
for (int i = 0; i < selectedItems.size(); ++i)
{
EditorItem *item = dynamic_cast<EditorItem *>(selectedItems[i]);
if (item)
{
this->DeleteItem(item);
}
}
this->drawMode = NONE;
this->drawInProgress = false;
this->currentMouseItem = NULL;
this->releaseKeyboard();
QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
gui::editor::Events::createBuildingEditorItem(std::string());
}
else if (_event->key() == Qt::Key_Escape)
{
if (this->mouseTooltip &&
this->scene()->items().contains(this->mouseTooltip))
this->scene()->removeItem(this->mouseTooltip);
this->CancelDrawMode();
gui::editor::Events::createBuildingEditorItem(std::string());
this->releaseKeyboard();
}
}
/////////////////////////////////////////////////
void EditorView::mouseDoubleClickEvent(QMouseEvent *_event)
{
if (this->drawMode == WALL)
{
WallSegmentItem *wallSegmentItem = dynamic_cast<WallSegmentItem *>(
this->currentMouseItem);
this->itemToVisualMap.erase(wallSegmentItem);
this->UnlinkGrabbers(wallSegmentItem->Grabbers()[0]);
this->scene()->removeItem(this->currentMouseItem);
delete this->currentMouseItem;
this->snapToGrabber = false;
this->snapGrabberOther = NULL;
this->snapGrabberCurrent = NULL;
this->currentMouseItem = NULL;
this->drawMode = NONE;
this->drawInProgress = false;
this->releaseKeyboard();
QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
gui::editor::Events::createBuildingEditorItem(std::string());
}
else if (this->drawMode == COLOR || this->drawMode == TEXTURE)
{
return;
}
else
{
if (!this->scene()->itemAt(this->mapToScene(_event->pos())))
this->OnOpenLevelInspector();
}
if (!this->drawInProgress)
QGraphicsView::mouseDoubleClickEvent(_event);
}
/////////////////////////////////////////////////
void EditorView::DeleteItem(EditorItem *_item)
{
if (!_item)
return;
// To make holes in the final model, windows and doors are attached to walls,
// and stairs are attached to floors above them.
// Detach 3D manip, but 2D items may remain as children.
this->buildingMaker->DetachAllChildren(this->itemToVisualMap[_item]);
_item->SetHighlighted(false);
if (_item->ItemType() == "WallSegment")
{
WallSegmentItem *wallSegmentItem = dynamic_cast<WallSegmentItem *>(_item);
// Delete item's child doors and windows before deleting item
for (int i = wallSegmentItem->childItems().size()-1; i >=0; --i)
{
// WallSegmentItems have other children besides RectItems
RectItem *rectItem = dynamic_cast<RectItem *>(
wallSegmentItem->childItems().at(i));
if (rectItem)
{
this->DeleteItem(rectItem);
}
}
this->UnlinkGrabbers(wallSegmentItem->Grabbers()[0]);
this->UnlinkGrabbers(wallSegmentItem->Grabbers()[1]);
this->wallSegmentList.erase(std::remove(this->wallSegmentList.begin(),
this->wallSegmentList.end(), dynamic_cast<WallSegmentItem *>(_item)),
this->wallSegmentList.end());
}
else if (_item->ItemType() == "Window")
{
this->windowList.erase(std::remove(this->windowList.begin(),
this->windowList.end(), dynamic_cast<WindowItem *>(_item)),
this->windowList.end());
}
else if (_item->ItemType() == "Door")
{
this->doorList.erase(std::remove(this->doorList.begin(),
this->doorList.end(), dynamic_cast<DoorItem *>(_item)),
this->doorList.end());
}
else if (_item->ItemType() == "Stairs")
{
this->stairsList.erase(std::remove(this->stairsList.begin(),
this->stairsList.end(), dynamic_cast<StairsItem *>(_item)),
this->stairsList.end());
}
else if (_item->ItemType() == "Floor")
{
this->floorList.erase(std::remove(this->floorList.begin(),
this->floorList.end(), dynamic_cast<FloorItem *>(_item)),
this->floorList.end());
}
this->itemToVisualMap.erase(_item);
delete _item;
}
/////////////////////////////////////////////////
void EditorView::DrawWall(const QPoint &_pos)
{
WallSegmentItem *wallSegmentItem = NULL;
// First point on the chain
if (!this->drawInProgress)
{
auto pointStart = Conversions::Convert(this->mapToScene(_pos));
auto pointEnd = pointStart + ignition::math::Vector2d(1, 0);
wallSegmentItem = new WallSegmentItem(pointStart, pointEnd,
this->levelDefaultHeight);
if (!(QApplication::keyboardModifiers() & Qt::ShiftModifier))
{
double distanceToClose = 30;
// Snap to other walls' points
QList<QGraphicsItem *> itemsList = this->scene()->items(QRectF(
QPointF(pointStart.X() - distanceToClose/2,
pointStart.Y() - distanceToClose/2),
QPointF(pointStart.X() + distanceToClose/2,
pointStart.Y() + distanceToClose/2)));
for (QList<QGraphicsItem *>::iterator it = itemsList.begin();
it != itemsList.end(); ++it)
{
WallSegmentItem *anotherWall = dynamic_cast<WallSegmentItem *>(*it);
if (anotherWall)
{
if (ignition::math::Vector2d(pointStart -
anotherWall->StartPoint()).Length() <= distanceToClose)
{
wallSegmentItem->SetStartPoint(anotherWall->StartPoint());
this->LinkGrabbers(anotherWall->Grabbers()[0],
wallSegmentItem->Grabbers()[0]);
break;
}
else if (ignition::math::Vector2d(pointStart -
anotherWall->EndPoint()).Length() <= distanceToClose)
{
wallSegmentItem->SetStartPoint(anotherWall->EndPoint());
this->LinkGrabbers(anotherWall->Grabbers()[1],
wallSegmentItem->Grabbers()[0]);
break;
}
}
}
}
wallSegmentItem->SetLevel(this->currentLevel);
wallSegmentItem->SetLevelBaseHeight(this->levels[this->currentLevel]->
baseHeight);
this->scene()->addItem(wallSegmentItem);
this->currentMouseItem = wallSegmentItem;
this->drawInProgress = true;
}
// Subsequent points get linked in the chain
else
{
// Snap currently finishing segment to a grabber in scene
if (this->snapToGrabber && this->snapGrabberOther &&
this->snapGrabberCurrent)
{
this->LinkGrabbers(this->snapGrabberOther, this->snapGrabberCurrent);
}
// Reset and start a new segment
this->snapToGrabber = false;
this->snapGrabberOther = NULL;
this->snapGrabberCurrent = NULL;
wallSegmentItem = dynamic_cast<WallSegmentItem*>(this->currentMouseItem);
wallSegmentItem->SetTexture3d("");
wallSegmentItem->SetColor3d(common::Color::White);
wallSegmentItem->SetHighlighted(false);
this->wallSegmentList.push_back(wallSegmentItem);
if (wallSegmentItem->Level() > 0)
{
floorList[wallSegmentItem->Level()-1]->AttachWallSegment(
wallSegmentItem);
}
// Start from previous segment's end
auto newPointStart = wallSegmentItem->EndPoint();
auto newPointEnd = newPointStart + ignition::math::Vector2d(1, 0);
GrabberHandle *grabberStart = wallSegmentItem->Grabbers()[1];
wallSegmentItem = new WallSegmentItem(newPointStart,
newPointEnd, this->levelDefaultHeight);
wallSegmentItem->SetLevel(this->currentLevel);
wallSegmentItem->SetLevelBaseHeight(this->levels[
this->currentLevel]->baseHeight);
this->scene()->addItem(wallSegmentItem);
this->currentMouseItem = wallSegmentItem;
this->LinkGrabbers(grabberStart, wallSegmentItem->Grabbers()[0]);
}
// 3D counterpart
if (wallSegmentItem)
this->Create3DVisual(wallSegmentItem);
}
/////////////////////////////////////////////////
void EditorView::DrawWindow(const QPoint &_pos)
{
WindowItem *windowItem = NULL;
if (!drawInProgress)
{
windowItem = new WindowItem();
windowItem->SetLevel(this->currentLevel);
windowItem->SetLevelBaseHeight(
this->levels[this->currentLevel]->baseHeight);
this->scene()->addItem(windowItem);
this->currentMouseItem = windowItem;
this->Create3DVisual(windowItem);
this->drawInProgress = true;
}
windowItem = dynamic_cast<WindowItem*>(this->currentMouseItem);
if (windowItem)
{
QPointF scenePos = this->mapToScene(_pos);
windowItem->SetPosition(scenePos.x(), scenePos.y());
}
}
/////////////////////////////////////////////////
void EditorView::DrawDoor(const QPoint &_pos)
{
DoorItem *doorItem = NULL;
if (!drawInProgress)
{
doorItem = new DoorItem();
doorItem->SetLevel(this->currentLevel);
doorItem->SetLevelBaseHeight(this->levels[this->currentLevel]->baseHeight);
this->scene()->addItem(doorItem);
this->currentMouseItem = doorItem;
this->Create3DVisual(doorItem);
this->drawInProgress = true;
}
doorItem = dynamic_cast<DoorItem*>(currentMouseItem);
if (doorItem)
{
QPointF scenePos = this->mapToScene(_pos);
doorItem->SetPosition(scenePos.x(), scenePos.y());
}
}
/////////////////////////////////////////////////
void EditorView::DrawStairs(const QPoint &_pos)
{
StairsItem *stairsItem = NULL;
if (!drawInProgress)
{
stairsItem = new StairsItem();
stairsItem->SetLevel(this->currentLevel);
stairsItem->SetLevelBaseHeight(
this->levels[this->currentLevel]->baseHeight);
this->scene()->addItem(stairsItem);
this->currentMouseItem = stairsItem;
this->Create3DVisual(stairsItem);
this->drawInProgress = true;
}
stairsItem = dynamic_cast<StairsItem*>(this->currentMouseItem);
if (stairsItem)
{
QPointF scenePos = this->mapToScene(_pos);
stairsItem->SetPosition(scenePos.x(), scenePos.y());
}
}
/////////////////////////////////////////////////
void EditorView::Create3DVisual(EditorItem *_item)
{
QVector3D itemPosition = Conversions::Convert(_item->ScenePosition());
itemPosition.setZ(_item->LevelBaseHeight() + itemPosition.z());
QVector3D itemSize = Conversions::Convert(_item->Size());
std::string itemName;
if (_item->ItemType() == "Stairs")
{
StairsItem *stairsItem = dynamic_cast<StairsItem *>(_item);
itemName = this->buildingMaker->AddStairs(
itemSize, itemPosition, _item->SceneRotation(),
stairsItem->Steps());
if (stairsItem->Level() < static_cast<int>(floorList.size()))
{
this->buildingMaker->AttachManip(itemName,
this->itemToVisualMap[floorList[stairsItem->Level()]]);
}
}
else if (_item->ItemType() == "WallSegment")
{
WallSegmentItem *wallSegmentItem = dynamic_cast<WallSegmentItem *>(_item);
itemSize.setZ(wallSegmentItem->Height());
itemName = this->buildingMaker->AddWall(
itemSize, itemPosition, _item->SceneRotation());
}
else if (_item->ItemType() == "Window")
{
itemName = this->buildingMaker->AddWindow(
itemSize, itemPosition, _item->SceneRotation());
}
else if (_item->ItemType() == "Door")
{
itemName = this->buildingMaker->AddDoor(
itemSize, itemPosition, _item->SceneRotation());
}
else if (_item->ItemType() == "Floor")
{
itemName = this->buildingMaker->AddFloor(
itemSize, Conversions::Convert(_item->ScenePosition()), 0);
}
this->buildingMaker->ConnectItem(itemName, _item);
this->itemToVisualMap[_item] = itemName;
_item->SetName(itemName);
}
/////////////////////////////////////////////////
void EditorView::SetBackgroundImage(const std::string &_filename,
double _resolution)
{
QImage img(QString::fromStdString(_filename));
int newHeight = (img.height() * 100) / _resolution;
img = img.scaledToHeight(newHeight);
if (!this->levels[this->currentLevel]->backgroundPixmap)
{
this->levels[this->currentLevel]->backgroundPixmap =
new QGraphicsPixmapItem(QPixmap::fromImage(img));
this->scene()->addItem(this->levels[this->currentLevel]->backgroundPixmap);
}
else
{
this->levels[this->currentLevel]->backgroundPixmap->setPixmap(
QPixmap::fromImage(img));
}
this->levels[this->currentLevel]->backgroundPixmap->
setX(img.width() * -0.5);
this->levels[this->currentLevel]->backgroundPixmap->
setY(img.height() * -0.5);
this->setSceneRect(img.width() * -0.5, img.height() * -0.5,
img.width(), img.height());
if (!this->floorplanVisible)
{
this->levels[this->currentLevel]->backgroundPixmap->setVisible(false);
gui::editor::Events::triggerShowFloorplan();
}
gui::editor::Events::createBuildingEditorItem(std::string());
}
/////////////////////////////////////////////////
void EditorView::OnCreateEditorItem(const std::string &_type)
{
this->CancelDrawMode();
if (_type == "wall")
this->drawMode = WALL;
else if (_type == "window")
this->drawMode = WINDOW;
else if (_type == "door")
this->drawMode = DOOR;
else if (_type == "stairs")
this->drawMode = STAIRS;
else if (_type == "image")
{
this->drawMode = NONE;
ImportImageDialog *importImage = new ImportImageDialog(this);
importImage->show();
}
if (this->drawInProgress && this->currentMouseItem)
{
this->scene()->removeItem(this->currentMouseItem);
this->currentMouseItem = NULL;
this->drawInProgress = false;
}
this->scene()->clearSelection();
if (this->drawMode == WALL)
QApplication::setOverrideCursor(QCursor(Qt::CrossCursor));
if (!this->elementsVisible)
gui::editor::Events::triggerShowElements();
// this->grabKeyboard();
}
/////////////////////////////////////////////////
void EditorView::OnColorSelected(QColor _color)
{
if (!_color.isValid())
return;
this->CancelDrawMode();
this->scene()->clearSelection();
this->drawMode = COLOR;
}
/////////////////////////////////////////////////
void EditorView::OnTextureSelected(QString _texture)
{
if (_texture == QString(""))
return;
this->CancelDrawMode();
this->scene()->clearSelection();
this->drawMode = TEXTURE;
}
/////////////////////////////////////////////////
void EditorView::OnDiscardModel()
{
this->wallSegmentList.clear();
this->windowList.clear();
this->doorList.clear();
this->stairsList.clear();
this->floorList.clear();
this->itemToVisualMap.clear();
this->buildingMaker->Reset();
for (unsigned int i = 0; i < this->levels.size(); ++i)
delete this->levels[i];
this->levels.clear();
this->currentLevel = 0;
Level *newLevel = new Level();
newLevel->level = 0;
newLevel->baseHeight = 0;
newLevel->height = this->levelDefaultHeight;
newLevel->name = "Level 1";
this->levels.push_back(newLevel);
this->levelCounter = 0;
// clear the scene but add the grid back in
this->scene()->clear();
this->gridLines = new GridLines(this->scene()->sceneRect().width(),
this->scene()->sceneRect().height());
this->scene()->addItem(this->gridLines);
this->currentMouseItem = NULL;
this->drawInProgress = false;
this->drawMode = NONE;
gui::editor::Events::createBuildingEditorItem(std::string());
}
/////////////////////////////////////////////////
void EditorView::OnAddLevel()
{
if (this->wallSegmentList.empty())
{
QMessageBox msgBox;
msgBox.setText("Create new levels after adding walls.");
msgBox.exec();
return;
}
if (!this->elementsVisible)
gui::editor::Events::triggerShowElements();
if (this->levels[this->currentLevel]->backgroundPixmap)
this->levels[this->currentLevel]->backgroundPixmap->setVisible(false);
int newLevelNum = this->levels.size();
std::stringstream levelNameStr;
levelNameStr << "Level " << ++this->levelCounter + 1;
std::string levelName = levelNameStr.str();
this->currentLevel = newLevelNum;
Level *newLevel = new Level();
newLevel->name = levelName;
newLevel->level = newLevelNum;
newLevel->height = this->levelDefaultHeight;
this->levels.push_back(newLevel);
gui::editor::Events::updateLevelWidget(this->currentLevel, levelName);
std::vector<WallSegmentItem *>::iterator wallIt =
this->wallSegmentList.begin();
double wallHeight = (*wallIt)->Height() + (*wallIt)->LevelBaseHeight();
double maxHeight = wallHeight;
int wallLevel = 0;
++wallIt;
for (wallIt; wallIt != this->wallSegmentList.end(); ++wallIt)
{
wallHeight = (*wallIt)->Height() + (*wallIt)->LevelBaseHeight();
if ( wallHeight > maxHeight)
{
maxHeight = wallHeight;
wallLevel = (*wallIt)->Level();
}
}
newLevel->baseHeight = maxHeight;
FloorItem *floorItem = new FloorItem();
this->levels[this->currentLevel]->floorItem = floorItem;
std::vector<WallSegmentItem *> newWalls;
std::map<WallSegmentItem *, WallSegmentItem *> clonedWallMap;
for (std::vector<WallSegmentItem *>::iterator it = wallSegmentList.begin();
it != this->wallSegmentList.end(); ++it)
{
if ((*it)->Level() != wallLevel)
continue;
WallSegmentItem *wallSegmentItem = (*it)->Clone();
clonedWallMap[(*it)] = wallSegmentItem;
wallSegmentItem->SetLevel(newLevelNum);
wallSegmentItem->SetLevelBaseHeight(this->levels[this->currentLevel]->
baseHeight);
this->scene()->addItem(wallSegmentItem);
newWalls.push_back(wallSegmentItem);
this->Create3DVisual(wallSegmentItem);
floorItem->AttachWallSegment(wallSegmentItem);
wallSegmentItem->SetTexture3d("");
wallSegmentItem->SetColor3d(common::Color::White);
wallSegmentItem->SetHighlighted(false);
}
// Clone linked grabber relations
typedef std::map<WallSegmentItem *, WallSegmentItem *>::iterator clonedIt;
for (clonedIt iterator = clonedWallMap.begin(); iterator !=
clonedWallMap.end(); ++iterator)
{
WallSegmentItem *oldWall = iterator->first;
WallSegmentItem *newWall = iterator->second;
// start / end
for (int g = 0; g < 2; ++g)
{
for (auto oldGrabber : oldWall->Grabbers()[g]->LinkedGrabbers())
{
WallSegmentItem *parentItem = dynamic_cast<WallSegmentItem*>(
oldGrabber->parentItem());
int index = oldGrabber->Index();
newWall->Grabbers()[g]->PushLinkedGrabber(
clonedWallMap[parentItem]->Grabbers()[index]);
}
}
}
this->wallSegmentList.insert(this->wallSegmentList.end(), newWalls.begin(),
newWalls.end());
floorItem->SetLevel(this->currentLevel);
floorItem->SetLevelBaseHeight(this->levels[this->currentLevel]->baseHeight);
this->Create3DVisual(floorItem);
for (std::vector<StairsItem *>::iterator it = this->stairsList.begin();
it != this->stairsList.end(); ++it)
{
if ((*it)->Level() == (newLevelNum - 1))
{
buildingMaker->AttachManip(this->itemToVisualMap[(*it)],
floorItem->Name());
}
}
this->scene()->addItem(floorItem);
this->floorList.push_back(floorItem);
floorItem->SetTexture3d("");
floorItem->SetColor3d(common::Color::White);
floorItem->SetHighlighted(false);
}
/////////////////////////////////////////////////
void EditorView::OnDeleteLevel()
{
this->DeleteLevel(this->currentLevel);
}
/////////////////////////////////////////////////
void EditorView::DeleteLevel(int _level)
{
if (this->levels.size() == 1
|| _level >= static_cast<int>(this->levels.size()))
return;
// Delete current level and move to level below or above
int newLevelIndex = _level - 1;
if (newLevelIndex < 0)
newLevelIndex = _level + 1;
if (this->levels[_level]->backgroundPixmap)
{
this->scene()->removeItem(this->levels[_level]->backgroundPixmap);
delete this->levels[_level]->backgroundPixmap;
this->levels[_level]->backgroundPixmap = NULL;
}
this->OnChangeLevel(newLevelIndex);
double deletedHeight = this->levels[_level]->height;
std::vector<EditorItem *> toBeDeleted;
for (std::vector<WindowItem *>::iterator it = this->windowList.begin();
it != this->windowList.end(); ++it)
{
if ((*it)->Level() == _level)
toBeDeleted.push_back(*it);
else if ((*it)->Level() > _level)
{
(*it)->SetLevel((*it)->Level()-1);
(*it)->SetLevelBaseHeight((*it)->LevelBaseHeight() - deletedHeight);
(*it)->WindowChanged();
}
}
for (std::vector<DoorItem *>::iterator it = this->doorList.begin();
it != this->doorList.end(); ++it)
{
if ((*it)->Level() == _level)
toBeDeleted.push_back(*it);
else if ((*it)->Level() > _level)
{
(*it)->SetLevel((*it)->Level()-1);
(*it)->SetLevelBaseHeight((*it)->LevelBaseHeight()-deletedHeight);
(*it)->DoorChanged();
}
}
for (std::vector<StairsItem *>::iterator it = this->stairsList.begin();
it != this->stairsList.end(); ++it)
{
if ((*it)->Level() == _level)
toBeDeleted.push_back(*it);
else if ((*it)->Level() > _level)
{
(*it)->SetLevel((*it)->Level()-1);
(*it)->SetLevelBaseHeight((*it)->LevelBaseHeight()-deletedHeight);
(*it)->StairsChanged();
}
}
for (std::vector<FloorItem *>::iterator it = this->floorList.begin();
it != this->floorList.end(); ++it)
{
if ((*it)->Level() == _level)
toBeDeleted.push_back(*it);
else if ((*it)->Level() > _level)
{
(*it)->SetLevel((*it)->Level()-1);
(*it)->SetLevelBaseHeight((*it)->LevelBaseHeight()-deletedHeight);
(*it)->FloorChanged();
}
}
for (std::vector<WallSegmentItem *>::iterator it =
this->wallSegmentList.begin();
it != this->wallSegmentList.end(); ++it)
{
if ((*it)->Level() == _level)
toBeDeleted.push_back(*it);
else if ((*it)->Level() > _level)
{
(*it)->SetLevel((*it)->Level()-1);
(*it)->SetLevelBaseHeight((*it)->LevelBaseHeight()-deletedHeight);
(*it)->WallSegmentChanged();
}
}
for (unsigned int i = 0; i < toBeDeleted.size(); ++i)
{
this->DeleteItem(toBeDeleted[i]);
}
int levelNum = 0;
for (unsigned int i = 0; i < this->levels.size(); ++i)
{
if (this->levels[i]->level == _level)
{
delete this->levels[i];
levelNum = i;
}
else if (this->levels[i]->level > _level)
{
this->levels[i]->level--;
this->levels[i]->baseHeight -= deletedHeight;
}
}
this->levels.erase(this->levels.begin() + levelNum);
this->currentLevel = newLevelIndex;
gui::editor::Events::updateLevelWidget(_level, "");
}
/////////////////////////////////////////////////
void EditorView::OnChangeLevel(int _level)
{
if (_level < 0)
return;
if (this->currentLevel >= static_cast<int>(this->levels.size()))
return;
if (this->levels[this->currentLevel]->backgroundPixmap)
this->levels[this->currentLevel]->backgroundPixmap->setVisible(false);
if (_level < static_cast<int>(this->levels.size()) &&
this->levels[_level]->backgroundPixmap &&
this->floorplanVisible)
{
this->levels[_level]->backgroundPixmap->setVisible(true);
}
this->currentLevel = _level;
this->ShowCurrentLevelItems();
}
/////////////////////////////////////////////////
void EditorView::OnOpenLevelInspector()
{
this->levelInspector->SetLevelName(this->levels[this->currentLevel]->name);
FloorItem *floorItem = this->levels[this->currentLevel]->floorItem;
if (floorItem)
{
this->levelInspector->ShowFloorWidget(true);
this->levelInspector->SetColor(floorItem->Color3d());
this->levelInspector->SetTexture(floorItem->Texture3d());
}
else
{
this->levelInspector->ShowFloorWidget(false);
}
this->levelInspector->move(QCursor::pos());
this->levelInspector->show();
}
/////////////////////////////////////////////////
void EditorView::OnLevelApply()
{
LevelInspectorDialog *dialog =
qobject_cast<LevelInspectorDialog *>(QObject::sender());
std::string newLevelName = dialog->LevelName();
this->levels[this->currentLevel]->name = newLevelName;
FloorItem *floorItem = this->levels[this->currentLevel]->floorItem;
if (floorItem)
{
floorItem->SetTexture3d(dialog->Texture());
floorItem->SetColor3d(dialog->Color());
floorItem->Set3dTransparency(0.4);
floorItem->FloorChanged();
}
gui::editor::Events::updateLevelWidget(this->currentLevel, newLevelName);
}
/////////////////////////////////////////////////
void EditorView::CancelDrawMode()
{
if (this->drawMode != NONE)
{
if (this->currentMouseItem)
{
EditorItem *item = dynamic_cast<EditorItem *>(this->currentMouseItem);
this->itemToVisualMap.erase(item);
WallSegmentItem *wallSegmentItem = dynamic_cast<WallSegmentItem *>(item);
if (wallSegmentItem)
{
this->UnlinkGrabbers(wallSegmentItem->Grabbers()[0]);
}
this->scene()->removeItem(this->currentMouseItem);
delete this->currentMouseItem;
}
this->snapToGrabber = false;
this->snapGrabberOther = NULL;
this->snapGrabberCurrent = NULL;
this->drawMode = NONE;
this->drawInProgress = false;
this->currentMouseItem = NULL;
QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
}
}
/////////////////////////////////////////////////
void EditorView::OnShowFloorplan()
{
this->floorplanVisible = !this->floorplanVisible;
if (this->levels[this->currentLevel]->backgroundPixmap)
this->levels[this->currentLevel]->backgroundPixmap->setVisible(
!this->levels[this->currentLevel]->backgroundPixmap->isVisible());
}
/////////////////////////////////////////////////
void EditorView::OnShowElements()
{
this->elementsVisible = !this->elementsVisible;
this->ShowCurrentLevelItems();
}
/////////////////////////////////////////////////
void EditorView::ShowCurrentLevelItems()
{
for (std::vector<WallSegmentItem *>::iterator it =
this->wallSegmentList.begin(); it != this->wallSegmentList.end(); ++it)
{
if ((*it)->Level() != this->currentLevel)
(*it)->setVisible(false);
else
(*it)->setVisible(this->elementsVisible);
}
for (std::vector<WindowItem *>::iterator it = this->windowList.begin();
it != this->windowList.end(); ++it)
{
if ((*it)->Level() != this->currentLevel)
(*it)->setVisible(false);
else
(*it)->setVisible(this->elementsVisible);
}
for (std::vector<DoorItem *>::iterator it = this->doorList.begin();
it != this->doorList.end(); ++it)
{
if ((*it)->Level() != this->currentLevel)
(*it)->setVisible(false);
else
(*it)->setVisible(this->elementsVisible);
}
for (std::vector<StairsItem *>::iterator it = this->stairsList.begin();
it != this->stairsList.end(); ++it)
{
if ((*it)->Level() != this->currentLevel && (*it)->Level() !=
(this->currentLevel - 1))
(*it)->setVisible(false);
else
(*it)->setVisible(this->elementsVisible);
}
for (std::vector<FloorItem *>::iterator it = this->floorList.begin();
it != this->floorList.end(); ++it)
{
if ((*it)->Level() != this->currentLevel)
(*it)->setVisible(false);
else
(*it)->setVisible(this->elementsVisible);
}
}
/////////////////////////////////////////////////
void EditorView::LinkGrabbers(GrabberHandle *_grabber1,
GrabberHandle *_grabber2)
{
if (_grabber1 && _grabber2 && _grabber1 != _grabber2)
{
// if _grabber2 is not yet linked to _grabber1
auto linked1 = _grabber1->LinkedGrabbers();
auto linked2 = _grabber2->LinkedGrabbers();
if (std::find(linked1.begin(), linked1.end(), _grabber2) == linked1.end())
{
// Add _grabber2 so it moves when _grabber1 is moved
_grabber1->PushLinkedGrabber(_grabber2);
// also link _grabber1 to all grabbers already linked to _grabber2
for (auto it : linked2)
{
this->LinkGrabbers(_grabber1, it);
}
}
// if _grabber1 is not yet linked to _grabber2
if (std::find(linked2.begin(), linked2.end(), _grabber1) == linked2.end())
{
// Add _grabber1 so it moves when _grabber2 is moved
_grabber2->PushLinkedGrabber(_grabber1);
// also link _grabber2 to all grabbers already linked to _grabber1
for (auto it : linked1)
{
this->LinkGrabbers(_grabber2, it);
}
}
}
}
/////////////////////////////////////////////////
void EditorView::UnlinkGrabbers(GrabberHandle *_grabber1,
GrabberHandle *_grabber2)
{
// If only one grabber, erase it from all grabbers it is linked
if (!_grabber2)
{
for (auto linked : _grabber1->LinkedGrabbers())
{
linked->EraseLinkedGrabber(_grabber1);
}
}
// TODO: add option to unlink grabbers besides when deleting one of them,
// perhaps using a hot-key or at the wall inspector
}