#include "frame-container.h"
#include <QColor>
#include <QDebug>
#include <QGraphicsScene>
#include <math.h>
#include "branch-container.h"
#include "misc.h" //for roof function
/////////////////////////////////////////////////////////////////
// FrameContainer
/////////////////////////////////////////////////////////////////
FrameContainer::FrameContainer()
{
//qDebug() << "Constr FrameContainer this=" << this;
init();
}
FrameContainer::~FrameContainer()
{
//qDebug() << "Destr FrameContainer";
clear();
}
void FrameContainer::init()
{
containerType = Frame;
frameTypeInt = NoFrame;
clear();
framePen.setColor(Qt::black);
framePen.setWidth(1);
frameBrush.setColor(Qt::white);
frameBrush.setStyle(Qt::SolidPattern);
framePaddingInt = 0;
setLayout(Container::Horizontal);
setHorizontalDirection(Container::LeftToRight);
setVisible(true);
usage = Undefined;
parentBranchContainer = nullptr;
}
void FrameContainer::clear()
{
switch (frameTypeInt) {
case NoFrame:
break;
case Rectangle:
delete rectFrame;
break;
case RoundedRectangle:
delete pathFrame;
break;
case Ellipse:
case Circle:
delete ellipseFrame;
break;
case Pipe:
case Cloud:
delete pathFrame;
break;
}
frameTypeInt = NoFrame;
}
void FrameContainer::repaint()
{
// Repaint, when e.g. borderWidth has changed or a color
switch (frameTypeInt) {
case Rectangle:
rectFrame->setPen(framePen);
rectFrame->setBrush(frameBrush);
break;
case RoundedRectangle:
pathFrame->setPen(framePen);
pathFrame->setBrush(frameBrush);
break;
case Ellipse:
case Circle:
ellipseFrame->setPen(framePen);
ellipseFrame->setBrush(frameBrush);
break;
case Pipe:
case Cloud:
pathFrame->setPen(framePen);
pathFrame->setBrush(frameBrush);
break;
default:
qWarning() << "FrameContainer::repaint unknown frame type " << frameTypeInt;
break;
}
}
void FrameContainer::reposition()
{
// Assumption: FrameContainer only has one child (Ornamentscontainer or Outer/InnerContainer)
if (childContainers().count() > 1) {
qWarning() << "FrameContainer has more than one container! Parent=" << parentContainer()->info();
foreach (Container *c, childContainers())
qWarning() << " - " << c->info();
return;
}
if (childContainers().isEmpty()) {
qWarning() << "FrameContainer has no container!";
return;
}
// FrameContainer only has one child (Inner-, Outer-, OrnamentsContainer)
Container *c = childContainers().first();
c->reposition();
c->setPos(0, 0); // For cloud this might change again in updateGeometry()
updateGeometry(c->rect());
}
FrameContainer::FrameType FrameContainer::frameType() { return frameTypeInt; }
FrameContainer::FrameType FrameContainer::frameTypeFromString(const QString &s)
{
if (s == "Rectangle")
return Rectangle;
else if (s == "RoundedRectangle")
return RoundedRectangle;
else if (s == "Pipe")
return Pipe;
else if (s == "Ellipse")
return Ellipse;
else if (s == "Circle")
return Circle;
else if (s == "Cloud")
return Cloud;
return NoFrame;
}
QString FrameContainer::frameTypeString(int ftype)
{
switch (ftype) {
case Rectangle:
return "Rectangle";
case RoundedRectangle:
return "RoundedRectangle";
case Pipe:
return "Pipe";
case Ellipse:
return "Ellipse";
case Circle:
return "Circle";
case Cloud:
return "Cloud";
case NoFrame:
return "NoFrame";
default:
qWarning() << "FrameContainer::setFrameType unknown frame type " << ftype;
}
return QString();
}
QString FrameContainer::frameTypeString()
{
return frameTypeString(frameTypeInt);
}
void FrameContainer::setFrameType(const FrameType &t)
{
if (t != frameTypeInt) {
clear();
frameTypeInt = t;
switch (frameTypeInt) {
case NoFrame:
break;
case Rectangle:
rectFrame = new QGraphicsRectItem; // FIXME-3 Use my own Container QGraphicsRectItem!
rectFrame->setPen(framePen);
rectFrame->setBrush(frameBrush);
rectFrame->setFlag(ItemStacksBehindParent, true);
rectFrame->setParentItem(this);
rectFrame->show();
break;
case Ellipse:
case Circle:
ellipseFrame = scene()->addEllipse(QRectF(0, 0, 0, 0),
framePen, frameBrush);
ellipseFrame->setPen(framePen);
ellipseFrame->setBrush(frameBrush);
ellipseFrame->setFlag(ItemStacksBehindParent, true);
ellipseFrame->setParentItem(this);
ellipseFrame->show();
break;
case RoundedRectangle: {
QPainterPath path;
pathFrame = new QGraphicsPathItem;
pathFrame->setPen(framePen);
pathFrame->setBrush(frameBrush);
pathFrame->setFlag(ItemStacksBehindParent, true);
pathFrame->setParentItem(this);
pathFrame->show();
} break;
case Pipe:
case Cloud: {
QPainterPath path;
pathFrame = scene()->addPath(path, framePen, frameBrush);
pathFrame->setPen(framePen);
pathFrame->setBrush(frameBrush);
pathFrame->setFlag(ItemStacksBehindParent, true);
pathFrame->setParentItem(this);
pathFrame->show();
}
break;
default:
qWarning() << "FrameContainer::setframeType unknown frame type " << frameTypeInt;
break;
}
}
// reposition() is called in vymmodel for all containers
}
void FrameContainer::setFrameType(const QString &t)
{
if (t == "Rectangle")
setFrameType(Rectangle);
else if (t == "RoundedRectangle")
setFrameType(RoundedRectangle);
else if (t == "Pipe")
setFrameType(Pipe);
else if (t == "Ellipse")
setFrameType(Ellipse);
else if (t == "Circle")
setFrameType(Circle);
else if (t == "Cloud")
setFrameType(Cloud);
else
setFrameType(NoFrame);
}
void FrameContainer::updateGeometry(const QRectF &childRect)
{
QRectF r; // Final rectanble of FrameContainer
qreal pad = framePaddingInt + (framePen.width() - 1) / 2; // "Inner" padding and pen width
switch (frameTypeInt) {
case NoFrame:
break;
case Rectangle:
rectFrame->setRect(
childRect.left() - pad,
childRect.top() - pad,
childRect.width() + pad * 2,
childRect.height() + pad * 2);
r.setRect(
childRect.left() - pad * 2,
childRect.top() - pad * 2,
childRect.width() + pad * 4,
childRect.height() + pad * 4);
break;
case RoundedRectangle: {
qreal radius = 10;
qreal radius_2 = radius * 2;
qreal radius_h = radius / 2;
QPointF tl = childRect.topLeft() + QPointF(- radius_h - pad, - radius_h - pad);
QPointF tr = childRect.topRight() + QPointF(radius_h + pad, - radius_h - pad);
QPointF bl = childRect.bottomLeft() + QPointF(- radius_h - pad, radius_h + pad);
QPointF br = childRect.bottomRight() + QPointF(radius_h + pad, + radius_h + pad);
QPainterPath path;
path.moveTo(tl.x() + radius, tl.y());
// Top path
path.lineTo(tr.x() - radius, tr.y());
path.arcTo(tr.x() - radius_2, tr.y(), radius_2, radius_2, 90, -90);
path.lineTo(br.x(), br.y() - radius);
path.arcTo(br.x() - radius_2, br.y() - radius_2, radius_2, radius_2, 0, -90);
path.lineTo(bl.x() + radius, br.y());
path.arcTo(bl.x(), bl.y() - radius_2, radius_2, radius_2, -90, -90);
path.lineTo(tl.x(), tl.y() + radius);
path.arcTo(tl.x(), tl.y(), radius_2, radius_2, 180, -90);
pathFrame->setPath(path);
r.setRect(
childRect.left() - pad * 2 - radius_h,
childRect.top() - pad * 2 - radius_h,
childRect.width() + pad * 4 + radius_h * 2,
childRect.height() + pad * 4 + radius_h * 2);
} break;
case Pipe: {
QPointF tl = childRect.topLeft() + QPointF( - pad, - pad);
QPointF tr = childRect.topRight() + QPointF( pad, - pad);
QPointF br = childRect.bottomRight() + QPointF( pad, + pad);
QPointF bl = childRect.bottomLeft() + QPointF( - pad, + pad);
QPainterPath path;
path.moveTo(tl);
float w = tr.x() - tl.x();
float h = bl.y() - tl.y();
// Top path
path.lineTo(tr);
// Right path
path.cubicTo(tr + QPointF(h, 0), br + QPointF(h, 0), br);
// Bottom path
path.lineTo(bl);
// Left path
path.cubicTo(bl + QPointF( - h, 0), tl + QPointF( - h, 0), tl);
pathFrame->setPath(path);
QRectF r = path.boundingRect();
// center of pathFrame might be outside of origin, due to cloud not completely symmetrical
// Correct position of pathFrame and child
QPointF p(pad / 2, 0);
pathFrame->setPos(- r.center() + p);
childContainers().first()->setPos(- r.center() + p);
r.setRect(
- (r.width() + pad) / 2,
- (r.height() + pad) / 2, // Vertically centered anyway later...
r.width() + 2 * pad,
r.height() + 2 * pad);
}
break;
case Ellipse: {
// This approach assumes, that proportions in childRect are
// the same as in ellips. See also calculation in
// https://stackoverflow.com/questions/433371/ellipse-bounding-a-rectangle
qreal w = childRect.width() + pad * 2;
qreal h = childRect.height() + pad * 2;
qreal a = w / sqrt(2);
qreal b = h / sqrt(2);
ellipseFrame->setRect(- a, - b, a * 2, b * 2);
r.setRect(
- a - pad,
- b - pad,
(a + pad) * 2,
(b + pad) * 2);
}
break;
case Circle: {
qreal radius = sqrt(childRect.height() * childRect.height() + childRect.width() * childRect.width()) / 2;
qreal radius_2 = radius * 2;
ellipseFrame->setRect(
QRectF(
- radius - pad,
- radius - pad,
radius_2 + 2 * pad,
radius_2 + 2 * pad));
r.setRect(
- radius - pad * 2,
- radius - pad * 2,
radius * 2 + pad * 4,
radius * 2 + pad * 4);
}
break;
case Cloud: {
QPointF tl = childRect.topLeft() + QPointF( - pad, - pad);
QPointF tr = childRect.topRight() + QPointF( pad, - pad);
QPointF bl = childRect.bottomLeft() + QPointF( - pad, + pad);
QPainterPath path;
path.moveTo(tl);
float w = tr.x() - tl.x();
float h = bl.y() - tl.y();
// Cloud factor: Distance of control points from curve point
qreal cfx = w;
qreal cfx2 = cfx / 2;
qreal cfy = h;
qreal cfy2 = cfy / 2;
// Top path
int n = max (3, w / 40); // number of intervalls
if (n % 2 == 0 && n > 1) n -= 1;
float d = w / n; // width of interwall
for (float i = 0; i < n; i++) {
path.cubicTo(
tl.x() + i * d, tl.y() - cfx2 * roof((i + 0.5) / n),
tl.x() + (i + 1) * d, tl.y() - cfx2 * roof((i + 0.5) / n),
tl.x() + (i + 1) * d - 0 * (cfx2 / 5) * roof((i + 1) / n), tl.y() - cfx2 * roof((i + 1) / n));
}
// Right path
n = max(1, h / 40);
if (n % 2 == 0 && n > 1) n -= 1;
d = h / n;
for (float i = 0; i < n; i++) {
path.cubicTo(tr.x() + cfy2 * roof((i + 0.5) / n), tr.y() + i * d,
tr.x() + cfy2 * roof((i + 0.5) / n), tr.y() + (i + 1) * d,
tr.x() + cfy2 * roof((i + 1) / n), tr.y() + (i + 1) * d);
}
// Bottom path
n = max (2, w / 60); // number of intervalls
if (n % 2 == 0 && n > 1) n -= 1;
d = w / n;
for (float i = n; i > 0; i--) {
path.cubicTo(bl.x() + i * d, bl.y() + cfy2 * roof((i - 0.5) / n),
bl.x() + (i - 1) * d, bl.y() + cfy2 * roof((i - 0.5) / n),
bl.x() + (i - 1) * d - 0 * (cfy / 5 ) * roof((i - 1) / n), bl.y() + cfy2 * roof((i - 1) / n));
}
// Left path
n = max(1, h / 40);
if (n % 2 == 0 && n > 1) n -= 1;
d = h / n;
for (float i = n; i > 0; i--) {
path.cubicTo(tl.x() - cfy2 * roof((i - 0.5) / n), tr.y() + i * d,
tl.x() - cfy2 * roof((i - 0.5) / n), tr.y() + (i - 1) * d,
tl.x() - cfy2 * roof((i - 1) / n), tr.y() + (i - 1) * d);
}
pathFrame->setPath(path);
QRectF br = path.boundingRect();
// center of pathFrame might be outside of origin, due to cloud not completely symmetrical
// Correct position of pathFrame and child
QPointF p(pad / 2, 0);
pathFrame->setPos(- br.center() + p);
childContainers().first()->setPos(- br.center() + p);
r.setRect(
- (br.width() + pad) / 2,
- (br.height() + pad) / 2, // Vertically centered anyway later...
br.width() + 2 * pad,
br.height() + 2 * pad);
}
break;
default:
qWarning() << "FrameContainer::setFrameRect unknown frame type " << frameTypeInt;
break;
}
setRect(r);
}
int FrameContainer::framePadding()
{
if (frameTypeInt == NoFrame)
return 0;
else
return framePaddingInt;
}
void FrameContainer::setFramePadding(const int &i)
{
framePaddingInt = i;
updateGeometry(childContainers().first()->rect());
}
int FrameContainer::framePenWidth() { return framePen.width(); }
void FrameContainer::setFramePenWidth(const int &i)
{
framePen.setWidth(i);
repaint();
}
QColor FrameContainer::framePenColor() { return framePen.color(); }
void FrameContainer::setFramePenColor(const QColor &col)
{
framePen.setColor(col);
repaint();
}
QColor FrameContainer::frameBrushColor() { return frameBrush.color(); }
void FrameContainer::setFrameBrushColor(const QColor &col)
{
frameBrush.setColor(col);
repaint();
}
void FrameContainer::setParentBranchContainer(BranchContainer *pbc)
{
parentBranchContainer = pbc;
}
void FrameContainer::setUsage(FrameUsage u)
{
usage = u;
}
QString FrameContainer::saveFrame()
{
QStringList attrList;
if (usage == InnerFrame)
attrList << attribute("frameUsage", "innerFrame");
else if (usage == OuterFrame)
attrList << attribute("frameUsage", "outerFrame");
bool ad;
if (parentBranchContainer) {
if (usage == InnerFrame)
ad = parentBranchContainer->frameAutoDesign(true);
else
ad = parentBranchContainer->frameAutoDesign(false);
}
attrList << attribute("autoDesign", toS(ad));
if(!ad) {
attrList << attribute("frameType", frameTypeString());
attrList << attribute("penColor", framePen.color().name(QColor::HexArgb));
attrList << attribute("brushColor", frameBrush.color().name(QColor::HexArgb));
attrList << attribute("padding", QString::number(framePaddingInt));
attrList << attribute("penWidth", QString::number(framePen.width()));
}
return singleElement("frame", attrList.join(" "));
}