Rev 18007 |
Rev 18194 |
Go to most recent revision |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
For general Scribus (>=1.3.2) copyright and licensing information please refer
to the COPYING file provided with the program. Following this notice may exist
a copyright and/or license notice that predates the release of Scribus 1.3.2
for which a new license (GPL+exception) is in place.
*/
/***************************************************************************
* Copyright (C) 2007 by Franz Schmid *
* franz.schmid@altmuehlnet.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
****************************************************************************/
#include "pathdialog.h"
#include "pathalongpath.h"
#include "pageitem_group.h"
#include "scribuscore.h"
#include "scribusstructs.h"
#include "util.h"
#include "util_math.h"
int pathalongpath_getPluginAPIVersion()
{
return PLUGIN_API_VERSION;
}
ScPlugin* pathalongpath_getPlugin()
{
PathAlongPathPlugin* plug = new PathAlongPathPlugin();
Q_CHECK_PTR(plug);
return plug;
}
void pathalongpath_freePlugin(ScPlugin* plugin)
{
PathAlongPathPlugin* plug = dynamic_cast<PathAlongPathPlugin*>(plugin);
Q_ASSERT(plug);
delete plug;
}
PathAlongPathPlugin::PathAlongPathPlugin() : ScActionPlugin()
{
// Set action info in languageChange, so we only have to do
// it in one place.
languageChange();
}
PathAlongPathPlugin::~PathAlongPathPlugin() {};
void PathAlongPathPlugin::languageChange()
{
// Note that we leave the unused members unset. They'll be initialised
// with their default ctors during construction.
// Action name
m_actionInfo.name = "PathAlongPath";
// Action text for menu, including accel
m_actionInfo.text = tr("Path Along Path...");
m_actionInfo.helpText = tr("Bends a Polygon along a Path.");
// Menu
m_actionInfo.menu = "ItemPathOps";
m_actionInfo.parentMenu = "Item";
m_actionInfo.subMenuName = tr("Path Tools");
m_actionInfo.enabledOnStartup = false;
m_actionInfo.notSuitableFor.append(PageItem::Line);
m_actionInfo.notSuitableFor.append(PageItem::TextFrame);
m_actionInfo.notSuitableFor.append(PageItem::ImageFrame);
m_actionInfo.notSuitableFor.append(PageItem::PathText);
m_actionInfo.notSuitableFor.append(PageItem::LatexFrame);
m_actionInfo.notSuitableFor.append(PageItem::Symbol);
m_actionInfo.notSuitableFor.append(PageItem::RegularPolygon);
m_actionInfo.notSuitableFor.append(PageItem::Arc);
m_actionInfo.notSuitableFor.append(PageItem::Spiral);
m_actionInfo.forAppMode.append(modeNormal);
m_actionInfo.needsNumObjects = 2;
m_actionInfo.firstObjectType.append(PageItem::PolyLine);
m_actionInfo.secondObjectType.append(PageItem::Polygon);
}
const QString PathAlongPathPlugin::fullTrName() const
{
return QObject::tr("PathAlongPath");
}
const ScActionPlugin::AboutData* PathAlongPathPlugin::getAboutData() const
{
AboutData* about = new AboutData;
Q_CHECK_PTR(about);
about->authors = QString::fromUtf8("Franz Schmid <Franz.Schmid@altmuehlnet.de>");
about->shortDescription = tr("Bends a Polygon along a Polyline");
about->description = tr("This plugin bends a Polygon with the help of a Polyline.");
// about->version
// about->releaseDate
// about->copyright
about->license = "GPL";
return about;
}
void PathAlongPathPlugin::deleteAboutData(const AboutData* about) const
{
Q_ASSERT(about);
delete about;
}
bool PathAlongPathPlugin::handleSelection(ScribusDoc* doc, int SelectedType)
{
bool ret = ScActionPlugin::handleSelection(doc, SelectedType);
if (!ret)
{
if (doc->m_Selection->count() == 2)
{
PageItem *currItem = doc->m_Selection->itemAt(0);
if (currItem->isGroup())
{
currItem = doc->m_Selection->itemAt(1);
if (currItem->itemType() != PageItem::PolyLine)
ret = false;
else
ret = true;
}
else
{
if (currItem->itemType() != PageItem::PolyLine)
ret = false;
else
{
currItem = doc->m_Selection->itemAt(1);
if (currItem->isGroup())
{
ret = true;
}
}
}
}
}
return ret;
}
bool PathAlongPathPlugin::run(ScribusDoc* doc, QString)
{
firstUpdate = true;
currDoc = doc;
originalPathG.clear();
originalRotG.clear();
originalXPosG.clear();
originalYPosG.clear();
patternItemG.clear();
if (currDoc == 0)
currDoc = ScCore->primaryMainWindow()->doc;
if (currDoc->m_Selection->count() > 1)
{
if ((currDoc->m_Selection->itemAt(0)->isGroup()) || (currDoc->m_Selection->itemAt(1)->isGroup()))
{
selOffs = 0;
selCount = currDoc->m_Selection->count() - 1;
if (!currDoc->m_Selection->itemAt(0)->isGroup())
{
pathItem = currDoc->m_Selection->itemAt(0);
selOffs = 1;
}
else
pathItem = currDoc->m_Selection->itemAt(selCount);
effectPath = pathItem->PoLine.copy();
QTransform mp;
mp.rotate(pathItem->rotation());
effectPath.map(mp);
PageItem* bxi = currDoc->m_Selection->itemAt(selOffs);
bxi->asGroupFrame()->adjustXYPosition();
originalPathG.append(bxi->PoLine.copy());
originalXPosG.append(bxi->xPos());
originalYPosG.append(bxi->yPos());
originalXPosGi.append(bxi->gXpos);
originalYPosGi.append(bxi->gYpos);
originalRotG.append(bxi->rotation());
originalWidth.append(bxi->width());
originalHeight.append(bxi->height());
originalWidthG.append(bxi->groupWidth);
originalHeightG.append(bxi->groupHeight);
patternItemG.append(bxi);
QList<PageItem*> bxiL = bxi->getItemList();
for (int bx = 0; bx < bxiL.count(); ++bx)
{
PageItem* cIte = bxiL.at(bx);
originalPathG.append(cIte->PoLine.copy());
originalXPosG.append(cIte->xPos());
originalYPosG.append(cIte->yPos());
originalWidth.append(cIte->width());
originalHeight.append(cIte->height());
originalWidthG.append(cIte->groupWidth);
originalHeightG.append(cIte->groupHeight);
originalXPosGi.append(cIte->gXpos);
originalYPosGi.append(cIte->gYpos);
originalRotG.append(cIte->rotation());
patternItemG.append(cIte);
}
QPainterPath tmpPath = effectPath.toQPainterPath(false);
PathDialog *dia = new PathDialog(currDoc->scMW(), currDoc->unitIndex(), tmpPath.length(), true);
connect(dia, SIGNAL(updateValues(int, double, double, double, int)), this, SLOT(updateEffectG(int, double, double, double, int)));
if (dia->exec())
{
updateEffectG(dia->effectType, dia->offset, dia->offsetY, dia->gap, dia->rotate);
currDoc->changed();
}
else
{
updateEffectG(-1, dia->offset, dia->offsetY, dia->gap, dia->rotate);
currDoc->view()->DrawNew();
}
delete dia;
}
else
{
patternItem = currDoc->m_Selection->itemAt(0);
pathItem = currDoc->m_Selection->itemAt(1);
if (pathItem->itemType() != PageItem::PolyLine)
{
patternItem = currDoc->m_Selection->itemAt(1);
pathItem = currDoc->m_Selection->itemAt(0);
}
effectPath = pathItem->PoLine.copy();
QTransform mp;
mp.rotate(pathItem->rotation());
effectPath.map(mp);
originalPath = patternItem->PoLine.copy();
originalXPos = patternItem->xPos();
originalYPos = patternItem->yPos();
originalRot = patternItem->rotation();
QPainterPath tmpPath = effectPath.toQPainterPath(false);
PathDialog *dia = new PathDialog(currDoc->scMW(), currDoc->unitIndex(), tmpPath.length(), false);
connect(dia, SIGNAL(updateValues(int, double, double, double, int)), this, SLOT(updateEffect(int, double, double, double, int)));
if (dia->exec())
{
updateEffect(dia->effectType, dia->offset, dia->offsetY, dia->gap, dia->rotate);
patternItem->ContourLine = patternItem->PoLine.copy();
currDoc->changed();
}
else
{
patternItem->PoLine = originalPath;
patternItem->ClipEdited = true;
patternItem->FrameType = 3;
patternItem->setXYPos(originalXPos, originalYPos);
patternItem->setRotation(originalRot);
currDoc->AdjustItemSize(patternItem);
patternItem->OldB2 = patternItem->width();
patternItem->OldH2 = patternItem->height();
patternItem->updateClip();
currDoc->view()->DrawNew();
}
delete dia;
}
}
return true;
}
void PathAlongPathPlugin::updateEffectG(int effectType, double offset, double offsetY, double gap, int rotate)
{
qApp->changeOverrideCursor(QCursor(Qt::WaitCursor));
for (int bx = 0; bx < patternItemG.count(); ++bx)
{
PageItem* bxi = patternItemG[bx];
bxi->PoLine = originalPathG[bx];
bxi->ClipEdited = true;
bxi->FrameType = 3;
bxi->setXYPos(originalXPosG[bx], originalYPosG[bx], true);
bxi->setRotation(originalRotG[bx]);
bxi->gXpos = originalXPosGi[bx];
bxi->gYpos = originalYPosGi[bx];
bxi->setWidthHeight(originalWidth[bx], originalHeight[bx], true);
bxi->groupWidth = originalWidthG[bx];
bxi->groupHeight = originalHeightG[bx];
bxi->OldB2 = bxi->width();
bxi->OldH2 = bxi->height();
bxi->updateClip();
bxi->ContourLine = bxi->PoLine.copy();
}
firstUpdate = true;
if (effectType != -1)
{
Geom::Piecewise<Geom::D2<Geom::SBasis> > originaldpwd2 = FPointArray2Piecewise(effectPath, false);
Geom::Piecewise<Geom::D2<Geom::SBasis> > patternpwd2;
PageItem* bxi = patternItemG[0];
double originX = originalXPosG[0];
double originY = originalYPosG[0];
if (bxi->itemType() == PageItem::PolyLine)
patternpwd2 = FPointArray2Piecewise(originalPathG[0], false);
else
patternpwd2 = FPointArray2Piecewise(originalPathG[0], true);
setUpEffect(originaldpwd2, patternpwd2, effectType, offset / currDoc->unitRatio(), offsetY / currDoc->unitRatio(), gap / currDoc->unitRatio(), rotate);
for (int bx = 0; bx < patternItemG.count(); ++bx)
{
PageItem* bxi = patternItemG[bx];
FPointArray pathP = originalPathG[bx].copy();
FPoint tp(getMinClipF(&pathP));
double deltaX, deltaY;
deltaX = originalXPosG[bx] - originX;
deltaY = originalYPosG[bx] - originY;
QTransform mm;
mm.rotate(originalRotG[bx]);
pathP.map(mm);
pathP.translate(deltaX, deltaY);
if (bxi->itemType() == PageItem::PolyLine)
patternpwd2 = FPointArray2Piecewise(pathP, false);
else
patternpwd2 = FPointArray2Piecewise(pathP, true);
bxi->PoLine = doEffect_pwd2(patternpwd2);
bxi->PoLine.translate(-deltaX, -deltaY);
QTransform mm2;
mm2.rotate(-originalRotG[bx]);
bxi->PoLine.map(mm2);
bxi->ClipEdited = true;
bxi->FrameType = 3;
bxi->setXYPos(pathItem->xPos()+deltaX, pathItem->yPos()+deltaY, true);
double oW = bxi->width();
double oH = bxi->height();
currDoc->AdjustItemSize(bxi, true);
bxi->OldB2 = bxi->width();
bxi->OldH2 = bxi->height();
if (bxi->isGroup())
{
bxi->groupWidth = bxi->groupWidth * (bxi->OldB2 / oW);
bxi->groupHeight = bxi->groupHeight * (bxi->OldH2 / oH);
}
bxi->updateClip();
bxi->ContourLine = bxi->PoLine.copy();
}
}
qApp->changeOverrideCursor(QCursor(Qt::ArrowCursor));
if (firstUpdate)
currDoc->view()->DrawNew();
else
{
double gx, gy, gh, gw;
currDoc->m_Selection->getGroupRect(&gx, &gy, &gw, &gh);
QRectF oldR(pathItem->getBoundingRect());
QRectF newR = QRectF(gx, gy, gw, gh);
currDoc->regionsChanged()->update(newR.unite(oldR));
}
if (effectType != -1)
firstUpdate = false;
}
void PathAlongPathPlugin::updateEffect(int effectType, double offset, double offsetY, double gap, int rotate)
{
if (effectType == -1)
{
patternItem->PoLine = originalPath;
patternItem->ClipEdited = true;
patternItem->FrameType = 3;
patternItem->setXYPos(originalXPos, originalYPos);
patternItem->setRotation(originalRot);
firstUpdate = true;
}
else
{
Geom::Piecewise<Geom::D2<Geom::SBasis> > originaldpwd2 = FPointArray2Piecewise(effectPath, false);
Geom::Piecewise<Geom::D2<Geom::SBasis> > patternpwd2;
if (patternItem->itemType() == PageItem::PolyLine)
patternpwd2 = FPointArray2Piecewise(originalPath, false);
else
patternpwd2 = FPointArray2Piecewise(originalPath, true);
setUpEffect(originaldpwd2, patternpwd2, effectType, offset / currDoc->unitRatio(), offsetY / currDoc->unitRatio(), gap / currDoc->unitRatio(), rotate);
patternItem->PoLine = doEffect_pwd2(patternpwd2);
patternItem->ClipEdited = true;
patternItem->FrameType = 3;
patternItem->setXYPos(pathItem->xPos(), pathItem->yPos());
patternItem->setRotation(0);
}
currDoc->AdjustItemSize(patternItem, true);
patternItem->OldB2 = patternItem->width();
patternItem->OldH2 = patternItem->height();
patternItem->updateClip();
if (firstUpdate)
currDoc->view()->DrawNew();
else
{
QRectF oldR(pathItem->getBoundingRect());
QRectF newR(patternItem->getBoundingRect());
currDoc->regionsChanged()->update(newR.unite(oldR));
}
if (effectType != -1)
firstUpdate = false;
}
void PathAlongPathPlugin::setUpEffect(Geom::Piecewise<Geom::D2<Geom::SBasis> > &pwd2_in, Geom::Piecewise<Geom::D2<Geom::SBasis> > &pattern, int effect, double offset, double offsetY, double gap, int rotate)
{
m_offsetX = offset;
m_offsetY = offsetY;
m_gapval = gap;
m_rotate = rotate;
uskeleton = arc_length_parametrization(pwd2_in, 2, .1);
uskeleton = remove_short_cuts(uskeleton,.01);
n = rot90(derivative(uskeleton));
n = force_continuity(remove_short_cuts(n,.1));
D2<Piecewise<SBasis> > patternd2;
if (rotate == 1)
patternd2 = make_cuts_independant(rot90(pattern));
else if (rotate == 2)
patternd2 = make_cuts_independant(rot90(rot90(pattern)));
else if (rotate == 3)
patternd2 = make_cuts_independant(rot90(rot90(rot90(pattern))));
else
patternd2 = make_cuts_independant(pattern);
Piecewise<SBasis> x = Piecewise<SBasis>(patternd2[0]);
Piecewise<SBasis> y = Piecewise<SBasis>(patternd2[1]);
pattBnds = bounds_exact(x);
x -= pattBnds.min();
pattBndsY = bounds_exact(y);
y -= (pattBndsY.max()+pattBndsY.min()) / 2.0;
y -= offsetY;
m_scaling = 1.0;
nbCopies = int(uskeleton.cuts.back()/pattBnds.extent());
if (effect == 0)
{
nbCopies = 1;
m_scaling = 1.0;
}
else if (effect == 1)
{
nbCopies = 1;
m_scaling = (uskeleton.cuts.back()-m_offsetX)/pattBnds.extent();
}
else if (effect == 2)
{
nbCopies = int((uskeleton.cuts.back()-m_offsetX)/(pattBnds.extent()+m_gapval));
m_scaling = 1.0;
}
else if (effect == 3)
{
nbCopies = int((uskeleton.cuts.back()-m_offsetX)/(pattBnds.extent()+m_gapval));
m_scaling = (uskeleton.cuts.back()-m_offsetX)/((((double)nbCopies)*pattBnds.extent()) + (((double)nbCopies-1)*m_gapval));
}
pattWidth = pattBnds.extent() * m_scaling;
}
FPointArray PathAlongPathPlugin::doEffect_pwd2(Geom::Piecewise<Geom::D2<Geom::SBasis> > &pattern)
{
double offs = m_offsetX;
D2<Piecewise<SBasis> > patternd2;
if (m_rotate == 1)
patternd2 = make_cuts_independant(rot90(pattern));
else if (m_rotate == 2)
patternd2 = make_cuts_independant(rot90(rot90(pattern)));
else if (m_rotate == 3)
patternd2 = make_cuts_independant(rot90(rot90(rot90(pattern))));
else
patternd2 = make_cuts_independant(pattern);
Piecewise<SBasis> x = Piecewise<SBasis>(patternd2[0]);
Piecewise<SBasis> y = Piecewise<SBasis>(patternd2[1]);
x -= pattBnds.min();
y -= (pattBndsY.max()+pattBndsY.min()) / 2.0;
y -= m_offsetY;
if (m_scaling != 1.0)
x*=m_scaling;
FPointArray pathP;
for (int i=0; i<nbCopies; i++)
{
Piecewise<D2<SBasis> > output;
output.concat(compose(uskeleton,x+offs)+y*compose(n,x+offs));
offs+=pattWidth+m_gapval;
Piecewise2FPointArray(&pathP, output);
if (nbCopies > 1)
pathP.setMarker();
}
return pathP;
}