Sync grainline and lable position after correcting to parent bounding rect.

--HG--
branch : feature
This commit is contained in:
Roman Telezhynskyi 2017-03-02 09:12:48 +02:00
parent e91d037e0a
commit 71d10dbdcb
5 changed files with 236 additions and 295 deletions

View File

@ -2352,7 +2352,19 @@ void MainWindow::ActionLayout(bool checked)
SaveCurrentScene();
try
{
PrepareDetailsForLayout(&details);
}
catch (VException &e)
{
listDetails.clear();
QMessageBox::warning(this, tr("Layout mode"),
tr("You can't use now the Layout mode. \n%1").arg(e.ErrorMessage()),
QMessageBox::Ok, QMessageBox::Ok);
mode == Draw::Calculation ? ActionDraw(true) : ActionDetails(true);
return;
}
currentScene = tempSceneLayout;
ui->view->itemClicked(nullptr);

View File

@ -73,7 +73,7 @@ VException &VException::operator=(const VException &e)
*/
QString VException::ErrorMessage() const
{
return QString("Exception: %1").arg(error);
return tr("Exception: %1").arg(error);
}
//---------------------------------------------------------------------------------------------------------------------

View File

@ -164,6 +164,109 @@ bool FindGrainlineGeometry(const VGrainlineData& geom, const VContainer *pattern
pos = geom.GetPos();
return true;
}
//---------------------------------------------------------------------------------------------------------------------
bool IsItemContained(const QRectF &parentBoundingRect, const QVector<QPointF> &shape, qreal &dX, qreal &dY)
{
dX = 0;
dY = 0;
// single point differences
bool bInside = true;
for (int i = 0; i < shape.size(); ++i)
{
qreal dPtX = 0;
qreal dPtY = 0;
if (not parentBoundingRect.contains(shape.at(i)))
{
if (shape.at(i).x() < parentBoundingRect.left())
{
dPtX = parentBoundingRect.left() - shape.at(i).x();
}
else if (shape.at(i).x() > parentBoundingRect.right())
{
dPtX = parentBoundingRect.right() - shape.at(i).x();
}
if (shape.at(i).y() < parentBoundingRect.top())
{
dPtY = parentBoundingRect.top() - shape.at(i).y();
}
else if (shape.at(i).y() > parentBoundingRect.bottom())
{
dPtY = parentBoundingRect.bottom() - shape.at(i).y();
}
if (fabs(dPtX) > fabs(dX))
{
dX = dPtX;
}
if (fabs(dPtY) > fabs(dY))
{
dY = dPtY;
}
bInside = false;
}
}
return bInside;
}
//---------------------------------------------------------------------------------------------------------------------
QVector<QPointF> CorrectPosition(const QRectF &parentBoundingRect, QVector<QPointF> points)
{
qreal dX = 0;
qreal dY = 0;
if (not IsItemContained(parentBoundingRect, points, dX, dY))
{
for (int i =0; i < points.size(); ++i)
{
points[i] = QPointF(points.at(i).x() + dX, points.at(i).y() + dY);
}
}
return points;
}
//---------------------------------------------------------------------------------------------------------------------
QVector<QPointF> RoundPoints(const QVector<QPointF> &points)
{
QVector<QPointF> p;
for (int i=0; i < points.size(); ++i)
{
p.append(QPointF(qRound(points.at(i).x()), qRound(points.at(i).y())));
}
return p;
}
//---------------------------------------------------------------------------------------------------------------------
QVector<VSAPoint> PrepareAllowance(const QVector<QPointF> &points)
{
QVector<VSAPoint> allowancePoints;
for(int i = 0; i < points.size(); ++i)
{
allowancePoints.append(VSAPoint(points.at(i)));
}
return allowancePoints;
}
//---------------------------------------------------------------------------------------------------------------------
/**
* @brief VLayoutDetail::RotatePoint rotates a point around the center for given angle
* @param ptCenter center around which the point is rotated
* @param pt point, which is rotated around the center
* @param dAng angle of rotation
* @return position of point pt after rotating it around the center for dAng radians
*/
QPointF RotatePoint(const QPointF &ptCenter, const QPointF& pt, qreal dAng)
{
QPointF ptDest;
QPointF ptRel = pt - ptCenter;
ptDest.setX(cos(dAng)*ptRel.x() - sin(dAng)*ptRel.y());
ptDest.setY(sin(dAng)*ptRel.x() + cos(dAng)*ptRel.y());
return ptDest + ptCenter;
}
}
//---------------------------------------------------------------------------------------------------------------------
@ -201,24 +304,33 @@ VLayoutPiece VLayoutPiece::Create(const VPiece &piece, const VContainer *pattern
det.SetInternalPaths(ConvertInternalPaths(piece, pattern));
det.SetName(piece.GetName());
// Very important to set main path first!
if (det.ContourPath().isEmpty())
{
throw VException (tr("Piece %1 doesn't have shape.").arg(piece.GetName()));
}
const VPieceLabelData& data = piece.GetPatternPieceData();
if (data.IsVisible() == true)
{
det.SetDetail(piece.GetName(), data, qApp->font(), pattern);
}
const VPatternLabelData& geom = piece.GetPatternInfo();
if (geom.IsVisible() == true)
{
VAbstractPattern* pDoc = qApp->getCurrentDocument();
det.SetPatternInfo(pDoc, geom, qApp->font(), pattern->size(), pattern->height(), pattern);
}
const VGrainlineData& grainlineGeom = piece.GetGrainlineGeometry();
if (grainlineGeom.IsVisible() == true)
{
det.SetGrainline(grainlineGeom, pattern);
}
det.SetSAWidth(qApp->toPixel(piece.GetSAWidth()));
det.CreateTextItems();
det.SetForbidFlipping(piece.IsForbidFlipping());
return det;
@ -273,8 +385,6 @@ QVector<QPointF> VLayoutPiece::GetLayoutAllowancePoints() const
void VLayoutPiece::SetDetail(const QString& qsName, const VPieceLabelData& data, const QFont &font,
const VContainer *pattern)
{
d->detailData = data;
QPointF ptPos;
qreal labelWidth = 0;
qreal labelHeight = 0;
@ -292,7 +402,9 @@ void VLayoutPiece::SetDetail(const QString& qsName, const VPieceLabelData& data,
{
v[i] = RotatePoint(ptCenter, v.at(i), dAng);
}
d->detailLabel = RoundPoints(v);
QScopedPointer<QGraphicsItem> item(GetMainItem());
d->detailLabel = CorrectPosition(item->boundingRect(), RoundPoints(v));
// generate text
d->m_tmDetail.SetFont(font);
@ -307,8 +419,6 @@ void VLayoutPiece::SetDetail(const QString& qsName, const VPieceLabelData& data,
void VLayoutPiece::SetPatternInfo(const VAbstractPattern* pDoc, const VPatternLabelData& geom, const QFont &font,
qreal dSize, qreal dHeight, const VContainer *pattern)
{
d->patternGeom = geom;
QPointF ptPos;
qreal labelWidth = 0;
qreal labelHeight = 0;
@ -326,7 +436,8 @@ void VLayoutPiece::SetPatternInfo(const VAbstractPattern* pDoc, const VPatternLa
{
v[i] = RotatePoint(ptCenter, v.at(i), dAng);
}
d->patternInfo = RoundPoints(v);
QScopedPointer<QGraphicsItem> item(GetMainItem());
d->patternInfo = CorrectPosition(item->boundingRect(), RoundPoints(v));
// Generate text
d->m_tmPattern.SetFont(font);
@ -344,8 +455,6 @@ void VLayoutPiece::SetGrainline(const VGrainlineData& geom, const VContainer* pa
{
SCASSERT(pattern != nullptr)
d->grainlineGeom = geom;
QPointF pt1;
qreal dAng = 0;
qreal dLen = 0;
@ -380,7 +489,8 @@ void VLayoutPiece::SetGrainline(const VGrainlineData& geom, const VContainer* pa
v << pt2;
}
d->grainlinePoints = RoundPoints(v);
QScopedPointer<QGraphicsItem> item(GetMainItem());
d->grainlinePoints = CorrectPosition(item->boundingRect(), RoundPoints(v));
}
//---------------------------------------------------------------------------------------------------------------------
@ -629,17 +739,6 @@ QVector<QPointF> VLayoutPiece::Map(const QVector<QPointF> &points) const
return p;
}
//---------------------------------------------------------------------------------------------------------------------
QVector<QPointF> VLayoutPiece::RoundPoints(const QVector<QPointF> &points)
{
QVector<QPointF> p;
for (int i=0; i < points.size(); ++i)
{
p.append(QPointF(qRound(points.at(i).x()), qRound(points.at(i).y())));
}
return p;
}
//---------------------------------------------------------------------------------------------------------------------
QPainterPath VLayoutPiece::ContourPath() const
{
@ -678,133 +777,6 @@ QPainterPath VLayoutPiece::ContourPath() const
return path;
}
//---------------------------------------------------------------------------------------------------------------------
void VLayoutPiece::ClearTextItems()
{
d->m_liPP.clear();
}
//---------------------------------------------------------------------------------------------------------------------
void VLayoutPiece::CreateTextItems()
{
ClearTextItems();
// first add detail texts
if (d->detailLabel.count() > 0)
{
// get the mapped label vertices
QVector<QPointF> points = Map(Mirror(d->detailLabel));
// append the first point to obtain the closed rectangle
points.push_back(points.at(0));
// calculate the angle of rotation
qreal dAng = qAtan2(points.at(1).y() - points.at(0).y(), points.at(1).x() - points.at(0).x());
// calculate the label width and height
qreal dW = GetDistance(points.at(0), points.at(1));
qreal dH = GetDistance(points.at(1), points.at(2));
qreal dY = 0;
qreal dX;
// set up the rotation around top-left corner matrix
QMatrix mat;
mat.translate(points.at(0).x(), points.at(0).y());
mat.rotate(qRadiansToDegrees(dAng));
for (int i = 0; i < d->m_tmDetail.GetSourceLinesCount(); ++i)
{
const TextLine& tl = d->m_tmDetail.GetSourceLine(i);
QFont fnt = d->m_tmDetail.GetFont();
fnt.setPixelSize(d->m_tmDetail.GetFont().pixelSize() + tl.m_iFontSize);
fnt.setWeight(tl.m_eFontWeight);
fnt.setStyle(tl.m_eStyle);
QFontMetrics fm(fnt);
dY += fm.height();
// check if the next line will go out of bounds
if (dY > dH)
{
break;
}
QString qsText = tl.m_qsText;
if (fm.width(qsText) > dW)
{
qsText = fm.elidedText(qsText, Qt::ElideMiddle, static_cast<int>(dW));
}
// find the correct horizontal offset, depending on the alignment flag
if ((tl.m_eAlign & Qt::AlignLeft) > 0)
{
dX = 0;
}
else if ((tl.m_eAlign & Qt::AlignHCenter) > 0)
{
dX = (dW - fm.width(qsText))/2;
}
else
{
dX = dW - fm.width(qsText);
}
// create text path and add it to the list
QPainterPath path;
path.addText(dX, dY - (fm.height() - fm.ascent())/2, fnt, qsText);
d->m_liPP << mat.map(path);
dY += d->m_tmDetail.GetSpacing();
}
}
// and then add pattern texts
if (d->patternInfo.count() > 0)
{
// similar approach like for the detail label
QVector<QPointF> points = Map(Mirror(d->patternInfo));
points.push_back(points.at(0));
qreal dAng = qAtan2(points.at(1).y() - points.at(0).y(), points.at(1).x() - points.at(0).x());
qreal dW = GetDistance(points.at(0), points.at(1));
qreal dH = GetDistance(points.at(1), points.at(2));
qreal dY = 0;
qreal dX;
QMatrix mat;
mat.translate(points.at(0).x(), points.at(0).y());
mat.rotate(qRadiansToDegrees(dAng));
for (int i = 0; i < d->m_tmPattern.GetSourceLinesCount(); ++i)
{
const TextLine& tl = d->m_tmPattern.GetSourceLine(i);
QFont fnt = d->m_tmPattern.GetFont();
fnt.setPixelSize(d->m_tmPattern.GetFont().pixelSize() + tl.m_iFontSize);
fnt.setWeight(tl.m_eFontWeight);
fnt.setStyle(tl.m_eStyle);
QFontMetrics fm(fnt);
dY += fm.height();
if (dY > dH)
{
break;
}
QString qsText = tl.m_qsText;
if (fm.width(qsText) > dW)
{
qsText = fm.elidedText(qsText, Qt::ElideMiddle, static_cast<int>(dW));
}
if ((tl.m_eAlign & Qt::AlignLeft) > 0)
{
dX = 0;
}
else if ((tl.m_eAlign & Qt::AlignHCenter) > 0)
{
dX = (dW - fm.width(qsText))/2;
}
else
{
dX = dW - fm.width(qsText);
}
QPainterPath path;
path.addText(dX, dY - (fm.height() - fm.ascent())/2, fnt, qsText);
d->m_liPP << mat.map(path);
dY += d->m_tmPattern.GetSpacing();
}
}
}
//---------------------------------------------------------------------------------------------------------------------
void VLayoutPiece::CreateInternalPathItem(int i, QGraphicsItem *parent) const
{
@ -818,52 +790,16 @@ void VLayoutPiece::CreateInternalPathItem(int i, QGraphicsItem *parent) const
}
//---------------------------------------------------------------------------------------------------------------------
/**
* @brief CreateTextItem Creates the i-th text item
* @param i index of the requested item
* @param parent parent of this text item. Can't be null.
*/
void VLayoutPiece::CreateTextItem(int i, QGraphicsItem *parent) const
void VLayoutPiece::CreateLabel(QGraphicsItem *parent, const QPainterPath &path) const
{
SCASSERT(parent != nullptr)
if (not path.isEmpty())
{
QGraphicsPathItem* item = new QGraphicsPathItem(parent);
QPainterPath path = d->matrix.map(d->m_liPP.at(i));
if (d->mirror == true)
{
QVector<QPointF> points;
if (i < d->m_tmDetail.GetSourceLinesCount())
{
points = Map(Mirror(d->detailLabel));
}
else
{
points = Map(Mirror(d->patternInfo));
}
QPointF ptCenter = (points.at(1) + points.at(3))/2;
qreal dRot = qRadiansToDegrees(qAtan2(points.at(1).y() - points.at(0).y(),
points.at(1).x() - points.at(0).x()));
// we need to move the center back to the origin, rotate it to align it with x axis,
// then mirror it to obtain the proper text direction, rotate it and translate it back to original position.
// The operations must be added in reverse order
QTransform t;
// move the label back to its original position
t.translate(ptCenter.x(), ptCenter.y());
// rotate the label back to original angle
t.rotate(dRot);
// mirror the label horizontally
t.scale(-1, 1);
// rotate the label to normal position
t.rotate(-dRot);
// move the label center into origin
t.translate(-ptCenter.x(), -ptCenter.y());
path = t.map(path);
}
item->setPath(path);
item->setBrush(QBrush(Qt::black));
}
}
//---------------------------------------------------------------------------------------------------------------------
@ -886,19 +822,15 @@ QPainterPath VLayoutPiece::LayoutAllowancePath() const
//---------------------------------------------------------------------------------------------------------------------
QGraphicsItem *VLayoutPiece::GetItem() const
{
QGraphicsPathItem *item = new QGraphicsPathItem();
item->setPath(ContourPath());
QGraphicsPathItem *item = GetMainItem();
for (int i = 0; i < d->m_internalPaths.count(); ++i)
{
CreateInternalPathItem(i, item);
}
for (int i = 0; i < d->m_liPP.count(); ++i)
{
CreateTextItem(i, item);
}
CreateLabel(item, CreateLabelText(d->detailLabel, d->m_tmDetail));
CreateLabel(item, CreateLabelText(d->patternInfo, d->m_tmPattern));
CreateGrainlineItem(item);
return item;
@ -914,12 +846,13 @@ void VLayoutPiece::CreateGrainlineItem(QGraphicsItem *parent) const
return;
}
VGraphicsFillItem* item = new VGraphicsFillItem(parent);
QPainterPath path;
QVector<QPointF> v = Map(d->grainlinePoints);
path.moveTo(v.at(0));
for (int i = 1; i < v.count(); ++i)
QVector<QPointF> gPoints = Map(d->grainlinePoints);
path.moveTo(gPoints.at(0));
for (int i = 1; i < gPoints.count(); ++i)
{
path.lineTo(v.at(i));
path.lineTo(gPoints.at(i));
}
item->setPath(path);
}
@ -938,14 +871,11 @@ QVector<QPointF> VLayoutPiece::DetailPath() const
}
//---------------------------------------------------------------------------------------------------------------------
QVector<VSAPoint> VLayoutPiece::PrepareAllowance(const QVector<QPointF> &points)
QGraphicsPathItem *VLayoutPiece::GetMainItem() const
{
QVector<VSAPoint> allowancePoints;
for(int i = 0; i < points.size(); ++i)
{
allowancePoints.append(VSAPoint(points.at(i)));
}
return allowancePoints;
QGraphicsPathItem *item = new QGraphicsPathItem();
item->setPath(ContourPath());
return item;
}
//---------------------------------------------------------------------------------------------------------------------
@ -960,64 +890,6 @@ void VLayoutPiece::SetMirror(bool value)
d->mirror = value;
}
//---------------------------------------------------------------------------------------------------------------------
/**
* @brief VLayoutDetail::RotatePoint rotates a point around the center for given angle
* @param ptCenter center around which the point is rotated
* @param pt point, which is rotated around the center
* @param dAng angle of rotation
* @return position of point pt after rotating it around the center for dAng radians
*/
QPointF VLayoutPiece::RotatePoint(const QPointF &ptCenter, const QPointF& pt, qreal dAng)
{
QPointF ptDest;
QPointF ptRel = pt - ptCenter;
ptDest.setX(cos(dAng)*ptRel.x() - sin(dAng)*ptRel.y());
ptDest.setY(sin(dAng)*ptRel.x() + cos(dAng)*ptRel.y());
return ptDest + ptCenter;
}
//---------------------------------------------------------------------------------------------------------------------
/**
* @brief VLayoutDetail::Mirror if the detail layout is rotated, this method will flip the
* label points over vertical axis, which goes through the center of the label
* @param points list of 4 label vertices
* @return list of flipped points
*/
QVector<QPointF> VLayoutPiece::Mirror(const QVector<QPointF> &points) const
{
// should only call this method with rectangular shapes
Q_ASSERT(points.count() == 4);
if (d->mirror == false)
{
return points;
}
QVector<QPointF> v;
v.resize(4);
v[0] = points.at(2);
v[1] = points.at(3);
v[2] = points.at(0);
v[3] = points.at(1);
return v;
}
//---------------------------------------------------------------------------------------------------------------------
/**
* @brief VLayoutDetail::GetDistance calculates the Euclidian distance between the points
* @param pt1 first point
* @param pt2 second point
* @return Euclidian distance between the two points
*/
qreal VLayoutPiece::GetDistance(const QPointF &pt1, const QPointF &pt2)
{
const qreal dX = pt1.x() - pt2.x();
const qreal dY = pt1.y() - pt2.y();
return qSqrt(dX*dX + dY*dY);
}
//---------------------------------------------------------------------------------------------------------------------
QLineF VLayoutPiece::Edge(const QVector<QPointF> &path, int i) const
{
@ -1075,3 +947,74 @@ int VLayoutPiece::EdgeByPoint(const QVector<QPointF> &path, const QPointF &p1) c
}
return 0; // Did not find edge
}
//---------------------------------------------------------------------------------------------------------------------
QPainterPath VLayoutPiece::CreateLabelText(const QVector<QPointF> &labelShape, const VTextManager &tm) const
{
QPainterPath textpath;
if (labelShape.count() > 2)
{
const qreal dW = QLineF(labelShape.at(0), labelShape.at(1)).length();
const qreal dH = QLineF(labelShape.at(1), labelShape.at(2)).length();
const qreal angle = QLineF(labelShape.at(0), labelShape.at(1)).angle();
qreal dY = 0;
qreal dX;
// set up the rotation around top-left corner matrix
QTransform mat;
mat.translate(labelShape.at(0).x(), labelShape.at(0).y());
if (d->mirror)
{
mat.scale(-1, 1);
mat.rotate(angle);
mat.translate(-dW, 0);
}
else
{
mat.rotate(angle);
}
mat *= d->matrix;
for (int i = 0; i < tm.GetSourceLinesCount(); ++i)
{
const TextLine& tl = tm.GetSourceLine(i);
QFont fnt = tm.GetFont();
fnt.setPixelSize(tm.GetFont().pixelSize() + tl.m_iFontSize);
fnt.setWeight(tl.m_eFontWeight);
fnt.setStyle(tl.m_eStyle);
QFontMetrics fm(fnt);
dY += fm.height();
if (dY > dH)
{
break;
}
QString qsText = tl.m_qsText;
if (fm.width(qsText) > dW)
{
qsText = fm.elidedText(qsText, Qt::ElideMiddle, static_cast<int>(dW));
}
if ((tl.m_eAlign & Qt::AlignLeft) > 0)
{
dX = 0;
}
else if ((tl.m_eAlign & Qt::AlignHCenter) > 0)
{
dX = (dW - fm.width(qsText))/2;
}
else
{
dX = dW - fm.width(qsText);
}
QPainterPath path;
path.addText(dX, dY - (fm.height() - fm.ascent())/2, fnt, qsText);
textpath.addPath(mat.map(path));
dY += tm.GetSpacing();
}
}
return textpath;
}

View File

@ -50,9 +50,12 @@
class VLayoutPieceData;
class VLayoutPiecePath;
class QGraphicsItem;
class QGraphicsPathItem;
class VTextManager;
class VLayoutPiece :public VAbstractPiece
{
Q_DECLARE_TR_FUNCTIONS(VLayoutPiece)
public:
VLayoutPiece();
VLayoutPiece(const VLayoutPiece &detail);
@ -118,20 +121,15 @@ private:
QVector<QPointF> DetailPath() const;
void ClearTextItems();
void CreateTextItems();
QGraphicsPathItem *GetMainItem() const Q_REQUIRED_RESULT;
QPainterPath CreateLabelText(const QVector<QPointF> &labelShape, const VTextManager &tm) const;
void CreateInternalPathItem(int i, QGraphicsItem *parent) const;
void CreateTextItem(int i, QGraphicsItem *parent) const;
void CreateLabel(QGraphicsItem *parent, const QPainterPath &path) const;
void CreateGrainlineItem(QGraphicsItem *parent) const;
static QVector<VSAPoint> PrepareAllowance(const QVector<QPointF> &points);
QVector<QPointF> Map(const QVector<QPointF> &points) const;
static QVector<QPointF> RoundPoints(const QVector<QPointF> &points);
static QPointF RotatePoint(const QPointF& ptCenter, const QPointF& pt, qreal dAng);
QVector<QPointF> Mirror(const QVector<QPointF>& points) const;
static qreal GetDistance(const QPointF& pt1, const QPointF& pt2);
QLineF Edge(const QVector<QPointF> &path, int i) const;
int EdgeByPoint(const QVector<QPointF> &path, const QPointF &p1) const;

View File

@ -59,12 +59,8 @@ public:
detailLabel(),
patternInfo(),
grainlinePoints(),
detailData(),
patternGeom(),
grainlineGeom(),
m_tmDetail(),
m_tmPattern(),
m_liPP()
m_tmPattern()
{}
VLayoutPieceData(const VLayoutPieceData &detail)
@ -79,12 +75,8 @@ public:
detailLabel(detail.detailLabel),
patternInfo(detail.patternInfo),
grainlinePoints(detail.grainlinePoints),
detailData(detail.detailData),
patternGeom(detail.patternGeom),
grainlineGeom(detail.grainlineGeom),
m_tmDetail(detail.m_tmDetail),
m_tmPattern(detail.m_tmPattern),
m_liPP(detail.m_liPP)
m_tmPattern(detail.m_tmPattern)
{}
~VLayoutPieceData() {}
@ -111,22 +103,18 @@ public:
/** @brief detailLabel detail label rectangle */
QVector<QPointF> detailLabel;
/** @brief patternInfo pattern info rectangle */
QVector<QPointF> patternInfo;
/** @brief grainlineInfo line */
QVector<QPointF> grainlinePoints;
/** @brief detailData detail data */
VPieceLabelData detailData;
/** @brief patternGeom pattern geometry */
VPatternLabelData patternGeom;
/** @brief grainlineGeom grainline geometry */
VGrainlineData grainlineGeom;
/** @brief m_tmDetail text manager for laying out detail info */
VTextManager m_tmDetail;
/** @brief m_tmPattern text manager for laying out pattern info */
VTextManager m_tmPattern;
/** @bried m_liPP list of generated text painter paths */
QList<QPainterPath> m_liPP;
private:
VLayoutPieceData &operator=(const VLayoutPieceData &) Q_DECL_EQ_DELETE;