-
Mikhail Svechnikov authoredMikhail Svechnikov authored
PolygonView.cpp 5.81 KiB
// ************************************************************************************************
//
// BornAgain: simulate and fit reflection and scattering
//
//! @file GUI/View/Mask/PolygonView.cpp
//! @brief Implements PolygonView class
//!
//! @homepage http://www.bornagainproject.org
//! @license GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2018
//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
// ************************************************************************************************
#include "GUI/View/Mask/PolygonView.h"
#include "GUI/Model/Device/MaskItems.h"
#include "GUI/View/Mask/PolygonPointView.h"
#include <QCursor>
#include <QPainter>
namespace {
const double bbox_margins = 5; // additional margins around points to form bounding box
}
PolygonView::PolygonView(PolygonItem* item)
: IShape2DView(item)
, m_item(item)
, m_block_on_point_update(false)
, m_close_polygon_request(false)
{
setFlag(QGraphicsItem::ItemIsSelectable);
setFlag(QGraphicsItem::ItemIsMovable);
setFlag(QGraphicsItem::ItemSendsGeometryChanges);
}
MaskItemObject* PolygonView::parameterizedItem() const
{
return m_item;
}
void PolygonView::addView(IShape2DView* childView)
{
if (childItems().contains(childView))
return;
auto* pointView = dynamic_cast<PolygonPointView*>(childView);
ASSERT(pointView);
pointView->setParentItem(this);
// polygon consisting from more than 2 points can be closed via hover event by clicking
// on first polygon point
if (!isClosedPolygon() && childItems().size() > 2)
childItems()[0]->setAcceptHoverEvents(true);
// during the drawing process the polygon is selected
pointView->setVisible(isSelected());
update_polygon();
connect(pointView, &PolygonPointView::propertyChanged, this, &PolygonView::update_view);
connect(pointView, &PolygonPointView::closePolygonRequest, this,
&PolygonView::onClosePolygonRequest);
}
//! Returns last added poligon point in scene coordinates
QPointF PolygonView::lastAddedPoint() const
{
return !childItems().empty() ? childItems().back()->scenePos() : QPointF();
}
QPainterPath PolygonView::shape() const
{
QPainterPath path;
path.addPolygon(m_polygon);
path.closeSubpath();
return path;
}
//! Returns true if there was a request to close polygon (emitted by its start point),
//! and then closes a polygon. Returns true if polygon was closed.
bool PolygonView::closePolygonIfNecessary()
{
if (m_close_polygon_request) {
for (QGraphicsItem* childItem : childItems()) {
childItem->setFlag(QGraphicsItem::ItemIsMovable);
childItem->setFlag(QGraphicsItem::ItemSendsGeometryChanges);
childItem->setAcceptHoverEvents(false);
childItem->setCursor(Qt::SizeAllCursor);
}
m_item->setIsClosed(true);
update();
}
return isClosedPolygon();
}
void PolygonView::onClosePolygonRequest(bool value)
{
m_close_polygon_request = value;
}
bool PolygonView::isClosedPolygon()
{
return m_item->isClosed();
}
void PolygonView::paint(QPainter* painter, const QStyleOptionGraphicsItem* o, QWidget* w)
{
if (isClosedPolygon())
IShape2DView::paint(painter, o, w);
else {
ASSERT(m_item);
const bool mask_value = static_cast<PolygonItem*>(m_item)->maskValue();
painter->setRenderHints(QPainter::Antialiasing);
painter->setPen(MaskEditorHelper::getMaskPen(mask_value));
painter->drawPolyline(m_polygon.toPolygon());
}
}
QVariant PolygonView::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant& value)
{
if (change == QGraphicsItem::ItemSelectedHasChanged)
setChildrenVisible(this->isSelected());
return value;
}
void PolygonView::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{
IShape2DView::mouseMoveEvent(event);
update_points();
}
void PolygonView::update_view()
{
update_polygon();
update();
}
//! Runs through all PolygonPointItem and calculate bounding rectangle.
//! Determines position of the rectangle in scene.
//! Calculates position of PolygonPointView in local polygon coordinates
void PolygonView::update_polygon()
{
if (m_block_on_point_update)
return;
m_block_on_point_update = true;
if (!m_item->points().isEmpty()) {
m_polygon.clear();
for (auto* point : m_item->points())
m_polygon << QPointF(toSceneX(point->posX()), toSceneY(point->posY()));
const QRectF scene_rect = m_polygon.boundingRect().marginsAdded(
QMarginsF(bbox_margins, bbox_margins, bbox_margins, bbox_margins));
m_bounding_rect = QRectF(0.0, 0.0, scene_rect.width(), scene_rect.height());
setPos(scene_rect.x(), scene_rect.y());
update(); // to propagate changes to scene
m_polygon = mapFromScene(m_polygon);
int index(0);
for (auto* childView : childItems())
childView->setPos(m_polygon[index++]);
setPos(scene_rect.x(), scene_rect.y());
}
m_block_on_point_update = false;
}
//! When polygon moves as a whole thing across the scene, given method updates coordinates
//! of PolygonPointItem's
void PolygonView::update_points()
{
if (m_block_on_point_update)
return;
for (QGraphicsItem* childItem : childItems()) {
auto* view = dynamic_cast<PolygonPointView*>(childItem);
QPointF pos = view->scenePos();
disconnect(view, &PolygonPointView::propertyChanged, this, &PolygonView::update_view);
view->updateParameterizedItem(pos);
connect(view, &PolygonPointView::propertyChanged, this, &PolygonView::update_view);
}
}
void PolygonView::setChildrenVisible(bool value)
{
for (QGraphicsItem* childItem : childItems())
childItem->setVisible(value);
}