diff --git a/libs/ui/KisDecorationsWrapperLayer.cpp b/libs/ui/KisDecorationsWrapperLayer.cpp index cf70ce559b..537f6644e2 100644 --- a/libs/ui/KisDecorationsWrapperLayer.cpp +++ b/libs/ui/KisDecorationsWrapperLayer.cpp @@ -1,141 +1,152 @@ /* * Copyright (c) 2019 Dmitry Kazakov * * 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 "KisDecorationsWrapperLayer.h" #include "KisDocument.h" #include "kis_node_visitor.h" #include "kis_processing_visitor.h" #include "kis_grid_config.h" +#include "kis_guides_config.h" struct KisDecorationsWrapperLayer::Private { KisDocument *document = 0; }; KisDecorationsWrapperLayer::KisDecorationsWrapperLayer(KisDocument *document) : KisExternalLayer(document->image(), "decorations-wrapper-layer", OPACITY_OPAQUE_U8), m_d(new Private) { m_d->document = document; } KisDecorationsWrapperLayer::KisDecorationsWrapperLayer(const KisDecorationsWrapperLayer &rhs) : KisExternalLayer(rhs.image(), "decorations-wrapper-layer", OPACITY_OPAQUE_U8), m_d(new Private) { m_d->document = rhs.m_d->document; } KisDecorationsWrapperLayer::~KisDecorationsWrapperLayer() { } void KisDecorationsWrapperLayer::setDocument(KisDocument *document) { m_d->document = document; KIS_SAFE_ASSERT_RECOVER(image() == document->image()) { setImage(document->image()); } } KisDocument *KisDecorationsWrapperLayer::document() const { return m_d->document; } bool KisDecorationsWrapperLayer::allowAsChild(KisNodeSP) const { return false; } bool KisDecorationsWrapperLayer::accept(KisNodeVisitor &visitor) { return visitor.visit(this); } void KisDecorationsWrapperLayer::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) { visitor.visit(this, undoAdapter); } KisNodeSP KisDecorationsWrapperLayer::clone() const { return new KisDecorationsWrapperLayer(*this); } KisPaintDeviceSP KisDecorationsWrapperLayer::original() const { return 0; } KisPaintDeviceSP KisDecorationsWrapperLayer::paintDevice() const { return 0; } bool KisDecorationsWrapperLayer::isFakeNode() const { return true; } bool KisDecorationsWrapperLayer::supportsPerspectiveTransform() const { return false; } KUndo2Command *KisDecorationsWrapperLayer::crop(const QRect &rect) { return transform(QTransform::fromTranslate(-rect.x(), -rect.y())); } KUndo2Command *KisDecorationsWrapperLayer::transform(const QTransform &transform) { KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(m_d->document, 0); struct UndoCommand : public KUndo2Command { UndoCommand(KisDocument *document, const QTransform &transform) : m_document(document), m_transform(transform) {} void undo() override { doTransform(m_transform.inverted()); } void redo() override { doTransform(m_transform); } private: void doTransform(const QTransform &transform) { KisGridConfig gridConfig = m_document->gridConfig(); if (gridConfig.showGrid()) { gridConfig.transform(transform); m_document->setGridConfig(gridConfig); } + + KisGuidesConfig guidesConfig = m_document->guidesConfig(); + if (guidesConfig.hasGuides()) { + const QTransform imageToDocument = + QTransform::fromScale(1 / m_document->image()->xRes(), + 1 / m_document->image()->yRes()); + + guidesConfig.transform(imageToDocument.inverted() * transform * imageToDocument); + m_document->setGuidesConfig(guidesConfig); + } } private: KisDocument *m_document; QTransform m_transform; }; return new UndoCommand(m_d->document, transform); } diff --git a/libs/ui/canvas/kis_grid_config.cpp b/libs/ui/canvas/kis_grid_config.cpp index 98cb300061..e5186087ee 100644 --- a/libs/ui/canvas/kis_grid_config.cpp +++ b/libs/ui/canvas/kis_grid_config.cpp @@ -1,124 +1,125 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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 "kis_grid_config.h" #include #include "kis_config.h" #include "kis_dom_utils.h" #include "kis_algebra_2d.h" struct KisGridConfigStaticRegistrar { KisGridConfigStaticRegistrar() { qRegisterMetaType("KisGridConfig"); } }; static KisGridConfigStaticRegistrar __registrar; Q_GLOBAL_STATIC(KisGridConfig, staticDefaultObject) const KisGridConfig& KisGridConfig::defaultGrid() { staticDefaultObject->loadStaticData(); return *staticDefaultObject; } void KisGridConfig::transform(const QTransform &transform) { if (transform.type() >= QTransform::TxShear) return; KisAlgebra2D::DecomposedMatix m(transform); if (m_gridType == 0) { QTransform t = m.scaleTransform(); const qreal eps = 1e-3; - if (KisAlgebra2D::wrapValue(m.angle, 90.0) <= eps) { + const qreal wrappedRotation = KisAlgebra2D::wrapValue(m.angle, 90.0); + if (wrappedRotation <= eps || wrappedRotation >= 90.0 - eps) { t *= m.rotateTransform(); } m_spacing = KisAlgebra2D::abs(t.map(m_spacing)); } else { if (qFuzzyCompare(m.scaleX, m.scaleY)) { m_cellSpacing = qRound(qAbs(m_cellSpacing * m.scaleX)); } } m_offset = KisAlgebra2D::wrapValue(transform.map(m_offset), m_spacing); } void KisGridConfig::loadStaticData() { KisConfig cfg(true); m_lineTypeMain = LineTypeInternal(cfg.getGridMainStyle()); m_lineTypeSubdivision = LineTypeInternal(cfg.getGridSubdivisionStyle()); m_colorMain = cfg.getGridMainColor(); m_colorSubdivision = cfg.getGridSubdivisionColor(); } void KisGridConfig::saveStaticData() const { KisConfig cfg(false); cfg.setGridMainStyle(m_lineTypeMain); cfg.setGridSubdivisionStyle(m_lineTypeSubdivision); cfg.setGridMainColor(m_colorMain); cfg.setGridSubdivisionColor(m_colorSubdivision); } QDomElement KisGridConfig::saveDynamicDataToXml(QDomDocument& doc, const QString &tag) const { QDomElement gridElement = doc.createElement(tag); KisDomUtils::saveValue(&gridElement, "showGrid", m_showGrid); KisDomUtils::saveValue(&gridElement, "snapToGrid", m_snapToGrid); KisDomUtils::saveValue(&gridElement, "offset", m_offset); KisDomUtils::saveValue(&gridElement, "spacing", m_spacing); KisDomUtils::saveValue(&gridElement, "offsetAspectLocked", m_offsetAspectLocked); KisDomUtils::saveValue(&gridElement, "spacingAspectLocked", m_spacingAspectLocked); KisDomUtils::saveValue(&gridElement, "subdivision", m_subdivision); KisDomUtils::saveValue(&gridElement, "angleLeft", m_angleLeft); KisDomUtils::saveValue(&gridElement, "angleRight", m_angleRight); KisDomUtils::saveValue(&gridElement, "cellSpacing", m_cellSpacing); KisDomUtils::saveValue(&gridElement, "gridType", m_gridType); return gridElement; } bool KisGridConfig::loadDynamicDataFromXml(const QDomElement &gridElement) { bool result = true; result &= KisDomUtils::loadValue(gridElement, "showGrid", &m_showGrid); result &= KisDomUtils::loadValue(gridElement, "snapToGrid", &m_snapToGrid); result &= KisDomUtils::loadValue(gridElement, "offset", &m_offset); result &= KisDomUtils::loadValue(gridElement, "spacing", &m_spacing); result &= KisDomUtils::loadValue(gridElement, "offsetAspectLocked", &m_offsetAspectLocked); result &= KisDomUtils::loadValue(gridElement, "spacingAspectLocked", &m_spacingAspectLocked); result &= KisDomUtils::loadValue(gridElement, "subdivision", &m_subdivision); result &= KisDomUtils::loadValue(gridElement, "angleLeft", &m_angleLeft); result &= KisDomUtils::loadValue(gridElement, "angleRight", &m_angleRight); result &= KisDomUtils::loadValue(gridElement, "gridType", &m_gridType); return result; } diff --git a/libs/ui/canvas/kis_guides_config.cpp b/libs/ui/canvas/kis_guides_config.cpp index 53bd58a9a0..7f42bc9f28 100644 --- a/libs/ui/canvas/kis_guides_config.cpp +++ b/libs/ui/canvas/kis_guides_config.cpp @@ -1,302 +1,358 @@ /* This file is part of the KDE project Copyright (C) 2006 Laurent Montel Copyright (C) 2008 Jan Hambrecht Copyright (c) 2015 Dmitry Kazakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_guides_config.h" #include #include #include #include "kis_config.h" #include "kis_dom_utils.h" +#include "kis_algebra_2d.h" +#include "kis_global.h" + + +struct KisGuidesConfigStaticRegistrar { + KisGuidesConfigStaticRegistrar() { + qRegisterMetaType("KisGuidesConfig"); + } +}; +static KisGuidesConfigStaticRegistrar __registrar; class Q_DECL_HIDDEN KisGuidesConfig::Private { public: Private() : showGuides(false) , snapToGuides(false) , lockGuides(false) , rulersMultiple2(false) , unitType(KoUnit::Pixel) {} bool operator==(const Private &rhs) { return horzGuideLines == rhs.horzGuideLines && vertGuideLines == rhs.vertGuideLines && showGuides == rhs.showGuides && snapToGuides == rhs.snapToGuides && lockGuides == rhs.lockGuides && guidesColor == rhs.guidesColor && guidesLineType == rhs.guidesLineType && rulersMultiple2 == rhs.rulersMultiple2 && unitType == rhs.unitType; } QList horzGuideLines; QList vertGuideLines; bool showGuides; bool snapToGuides; bool lockGuides; bool rulersMultiple2; KoUnit::Type unitType; QColor guidesColor; LineTypeInternal guidesLineType; Qt::PenStyle toPenStyle(LineTypeInternal type); }; KisGuidesConfig::KisGuidesConfig() : d(new Private()) { loadStaticData(); } KisGuidesConfig::~KisGuidesConfig() { } KisGuidesConfig::KisGuidesConfig(const KisGuidesConfig &rhs) : d(new Private(*rhs.d)) { } KisGuidesConfig& KisGuidesConfig::operator=(const KisGuidesConfig &rhs) { if (&rhs != this) { *d = *rhs.d; } return *this; } bool KisGuidesConfig::operator==(const KisGuidesConfig &rhs) const { return *d == *rhs.d; } bool KisGuidesConfig::hasSamePositionAs(const KisGuidesConfig &rhs) const { return horizontalGuideLines() == rhs.horizontalGuideLines() && verticalGuideLines() == rhs.verticalGuideLines(); } void KisGuidesConfig::setHorizontalGuideLines(const QList &lines) { d->horzGuideLines = lines; } void KisGuidesConfig::setVerticalGuideLines(const QList &lines) { d->vertGuideLines = lines; } void KisGuidesConfig::addGuideLine(Qt::Orientation o, qreal pos) { if (o == Qt::Horizontal) { d->horzGuideLines.append(pos); } else { d->vertGuideLines.append(pos); } } bool KisGuidesConfig::showGuideLines() const { return d->showGuides; } void KisGuidesConfig::setShowGuideLines(bool show) { d->showGuides = show; } bool KisGuidesConfig::showGuides() const { return d->showGuides; } void KisGuidesConfig::setShowGuides(bool value) { d->showGuides = value; } bool KisGuidesConfig::lockGuides() const { return d->lockGuides; } void KisGuidesConfig::setLockGuides(bool value) { d->lockGuides = value; } bool KisGuidesConfig::snapToGuides() const { return d->snapToGuides; } void KisGuidesConfig::setSnapToGuides(bool value) { d->snapToGuides = value; } bool KisGuidesConfig::rulersMultiple2() const { return d->rulersMultiple2; } void KisGuidesConfig::setRulersMultiple2(bool value) { d->rulersMultiple2 = value; } KoUnit::Type KisGuidesConfig::unitType() const { return d->unitType; } void KisGuidesConfig::setUnitType(const KoUnit::Type type) { d->unitType = type; } KisGuidesConfig::LineTypeInternal KisGuidesConfig::guidesLineType() const { return d->guidesLineType; } void KisGuidesConfig::setGuidesLineType(LineTypeInternal value) { d->guidesLineType = value; } QColor KisGuidesConfig::guidesColor() const { return d->guidesColor; } void KisGuidesConfig::setGuidesColor(const QColor &value) { d->guidesColor = value; } Qt::PenStyle KisGuidesConfig::Private::toPenStyle(LineTypeInternal type) { return type == LINE_SOLID ? Qt::SolidLine : type == LINE_DASHED ? Qt::DashLine : type == LINE_DOTTED ? Qt::DotLine : Qt::DashDotDotLine; } QPen KisGuidesConfig::guidesPen() const { return QPen(d->guidesColor, 0, d->toPenStyle(d->guidesLineType)); } const QList& KisGuidesConfig::horizontalGuideLines() const { return d->horzGuideLines; } const QList& KisGuidesConfig::verticalGuideLines() const { return d->vertGuideLines; } bool KisGuidesConfig::hasGuides() const { return !d->horzGuideLines.isEmpty() || !d->vertGuideLines.isEmpty(); } void KisGuidesConfig::loadStaticData() { KisConfig cfg(true); d->guidesLineType = LineTypeInternal(cfg.guidesLineStyle()); d->guidesColor = cfg.guidesColor(); } void KisGuidesConfig::saveStaticData() const { KisConfig cfg(false); cfg.setGuidesLineStyle(d->guidesLineType); cfg.setGuidesColor(d->guidesColor); } QDomElement KisGuidesConfig::saveToXml(QDomDocument& doc, const QString &tag) const { QDomElement guidesElement = doc.createElement(tag); KisDomUtils::saveValue(&guidesElement, "showGuides", d->showGuides); KisDomUtils::saveValue(&guidesElement, "snapToGuides", d->snapToGuides); KisDomUtils::saveValue(&guidesElement, "lockGuides", d->lockGuides); KisDomUtils::saveValue(&guidesElement, "horizontalGuides", d->horzGuideLines.toVector()); KisDomUtils::saveValue(&guidesElement, "verticalGuides", d->vertGuideLines.toVector()); KisDomUtils::saveValue(&guidesElement, "rulersMultiple2", d->rulersMultiple2); KoUnit tmp(d->unitType); KisDomUtils::saveValue(&guidesElement, "unit", tmp.symbol()); return guidesElement; } bool KisGuidesConfig::loadFromXml(const QDomElement &parent) { bool result = true; result &= KisDomUtils::loadValue(parent, "showGuides", &d->showGuides); result &= KisDomUtils::loadValue(parent, "snapToGuides", &d->snapToGuides); result &= KisDomUtils::loadValue(parent, "lockGuides", &d->lockGuides); QVector hGuides; QVector vGuides; result &= KisDomUtils::loadValue(parent, "horizontalGuides", &hGuides); result &= KisDomUtils::loadValue(parent, "verticalGuides", &vGuides); d->horzGuideLines = QList::fromVector(hGuides); d->vertGuideLines = QList::fromVector(vGuides); result &= KisDomUtils::loadValue(parent, "rulersMultiple2", &d->rulersMultiple2); QString unit; result &= KisDomUtils::loadValue(parent, "unit", &unit); bool ok = false; KoUnit tmp = KoUnit::fromSymbol(unit, &ok); if (ok) { d->unitType = tmp.type(); } result &= ok; return result; } bool KisGuidesConfig::isDefault() const { KisGuidesConfig defaultObject; defaultObject.loadStaticData(); return *this == defaultObject; } + +void KisGuidesConfig::transform(const QTransform &transform) +{ + if (transform.type() >= QTransform::TxShear) return; + + KisAlgebra2D::DecomposedMatix m(transform); + + QTransform t = m.scaleTransform(); + + const qreal eps = 1e-3; + int numWraps = 0; + const qreal wrappedRotation = KisAlgebra2D::wrapValue(m.angle, 90.0); + if (wrappedRotation <= eps || wrappedRotation >= 90.0 - eps) { + t *= m.rotateTransform(); + numWraps = qRound(normalizeAngleDegrees(m.angle) / 90.0); + } + + t *= m.translateTransform(); + + + QList newHorzGuideLines; + QList newVertGuideLines; + + Q_FOREACH (qreal hRuler, d->horzGuideLines) { + const QPointF pt = t.map(QPointF(0, hRuler)); + + if (numWraps & 0x1) { + newVertGuideLines << pt.x(); + } else { + newHorzGuideLines << pt.y(); + } + } + + Q_FOREACH (qreal vRuler, d->vertGuideLines) { + const QPointF pt = t.map(QPointF(vRuler, 0)); + + if (!(numWraps & 0x1)) { + newVertGuideLines << pt.x(); + } else { + newHorzGuideLines << pt.y(); + } + } + + d->horzGuideLines = newHorzGuideLines; + d->vertGuideLines = newVertGuideLines; +} diff --git a/libs/ui/canvas/kis_guides_config.h b/libs/ui/canvas/kis_guides_config.h index ca4748f80b..94e883f6f2 100644 --- a/libs/ui/canvas/kis_guides_config.h +++ b/libs/ui/canvas/kis_guides_config.h @@ -1,131 +1,133 @@ /* This file is part of the KDE project Copyright (C) 2006 Laurent Montel Copyright (C) 2008 Jan Hambrecht Copyright (c) 2015 Dmitry Kazakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KOGUIDESDATA_H #define KOGUIDESDATA_H #include "kritaui_export.h" #include #include #include #include class QDomElement; class QDomDocument; class QColor; class QPen; class KRITAUI_EXPORT KisGuidesConfig : boost::equality_comparable { public: enum LineTypeInternal { LINE_SOLID = 0, LINE_DASHED, LINE_DOTTED }; public: KisGuidesConfig(); ~KisGuidesConfig(); KisGuidesConfig(const KisGuidesConfig &rhs); KisGuidesConfig& operator=(const KisGuidesConfig &rhs); bool operator==(const KisGuidesConfig &rhs) const; bool hasSamePositionAs(const KisGuidesConfig &rhs) const; /** * @brief Set the positions of the horizontal guide lines * * @param lines a list of positions of the horizontal guide lines */ void setHorizontalGuideLines(const QList &lines); /** * @brief Set the positions of the vertical guide lines * * @param lines a list of positions of the vertical guide lines */ void setVerticalGuideLines(const QList &lines); /** * @brief Add a guide line to the canvas. * * @param orientation the orientation of the guide line * @param position the position in document coordinates of the guide line */ void addGuideLine(Qt::Orientation orientation, qreal position); /** * @brief Display or not guide lines */ bool showGuideLines() const; /** * @param show display or not guide line */ void setShowGuideLines(bool show); bool showGuides() const; void setShowGuides(bool value); bool lockGuides() const; void setLockGuides(bool value); bool snapToGuides() const; void setSnapToGuides(bool value); bool rulersMultiple2() const; void setRulersMultiple2(bool value); KoUnit::Type unitType() const; void setUnitType(KoUnit::Type type); LineTypeInternal guidesLineType() const; void setGuidesLineType(LineTypeInternal value); QColor guidesColor() const; void setGuidesColor(const QColor &value); QPen guidesPen() const; /// Returns the list of horizontal guide lines. const QList& horizontalGuideLines() const; /// Returns the list of vertical guide lines. const QList& verticalGuideLines() const; bool hasGuides() const; void loadStaticData(); void saveStaticData() const; QDomElement saveToXml(QDomDocument& doc, const QString &tag) const; bool loadFromXml(const QDomElement &parent); bool isDefault() const; + void transform(const QTransform &transform); + private: class Private; const QScopedPointer d; }; #endif