9#include <QXmlStreamReader>
21#include <QGlobalStatic>
24#include <QPixmapCache>
26#include <QDomDocument>
27#include <QPainterPath>
51 dbgUI <<
"KisPaintingAssistantHandle ctor";
107 if (!assistant->
handles().contains(
this)) {
158 return m11 == b.m11 &&
m12 == b.m12 &&
m21 == b.m21 &&
m22 == b.m22;
204 dbgUI <<
"handle not found in the map, creating a new one...";
208 handleMap.insert(origHandle, mappedHandle);
210 dbgUI <<
"orig handle is null, not doing anything";
214 if (mappedHandle && registerAssistant) {
222 return d->
s->useCustomColor;
232 d->
s->assistantCustomColor = color;
237 return d->
s->assistantCustomColor;
242 d->
s->assistantGlobalColorCache = color;
247 return d->
s->useCustomColor ?
d->
s->assistantCustomColor :
d->
s->assistantGlobalColorCache;
254 d->
s->isSnappingActive =
true;
255 d->
s->outlineVisible =
true;
260 QMap<KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP> &handleMap)
261 : m_hasBeenInsideLocalRect(rhs.m_hasBeenInsideLocalRect)
264 dbgUI <<
"creating handles...";
271#define _REUSE_H(name) d->name = d->reuseOrCreateHandle(handleMap, rhs.d->name, this, false)
286 return d->
s->isSnappingActive;
291 d->
s->isSnappingActive = set;
296 d->
s->adjustedPositionValid =
false;
297 d->
s->followBrushPosition =
false;
303 d->
s->adjustedBrushPosition = position;
304 d->
s->adjustedPositionValid =
true;
309 d->
s->followBrushPosition = follow;
324 return d->
s->isLocal;
334 return d->
s->isLocked;
349 return d->
s->isDuplicating;
354 return d->
s->editorWidgetOffset;
359 d->
s->editorWidgetOffset = offset;
369 paintingColor.setAlpha(0.2 * paintingColor.alpha());
374 pen_a.setCosmetic(
true);
375 painter.setPen(pen_a);
376 painter.drawPath(path);
384 pen_a.setStyle(Qt::SolidLine);
385 pen_a.setCosmetic(
true);
386 painter.setPen(pen_a);
387 painter.drawPath(path);
396 pen_a.setCosmetic(
true);
397 painter.setPen(pen_a);
398 painter.drawPath(path);
405 path.moveTo(QPointF(pt.x() - 5.0, pt.y() - 5.0)); path.lineTo(QPointF(pt.x() + 5.0, pt.y() + 5.0));
406 path.moveTo(QPointF(pt.x() - 5.0, pt.y() + 5.0)); path.lineTo(QPointF(pt.x() + 5.0, pt.y() - 5.0));
407 drawPath(painter, path, displayRenderInterface);
444 Q_ASSERT(
d->
handles.contains(_handle));
446 Q_ASSERT(!
d->
handles.contains(_handle));
453 Q_ASSERT(!
d->
handles.contains(handle));
469 const int padding = 16;
471 editorWidgetPos.rx() = qBound(0.0,
473 canvasSize.width() - (editorSize.width() + padding));
474 editorWidgetPos.ry() = qBound(0.0,
476 canvasSize.height() - (editorSize.height() + padding));
483 Q_UNUSED(updateRect);
485 Q_UNUSED(previewVisible);
491 drawCache(gc, converter, displayRenderInterface, assistantVisible);
497 if (bound.isEmpty()) {
502 const QRect widgetBound =
transform.mapRect(bound);
504 const QRect paintRect =
transform.mapRect(bound).intersected(gc.viewport());
505 if (paintRect.isEmpty())
return;
508 bool found = QPixmapCache::find(
d->
s->cached, &cached);
512 d->
s->cachedRect.translated(widgetBound.topLeft()).contains(paintRect))) {
514 const QRect cacheRect = gc.viewport().adjusted(-100, -100, 100, 100).intersected(widgetBound);
515 Q_ASSERT(!cacheRect.isEmpty());
517 if (cached.isNull() || cached.size() != cacheRect.size()) {
518 cached = QPixmap(cacheRect.size());
521 cached.fill(Qt::transparent);
522 QPainter painter(&cached);
523 painter.setRenderHint(QPainter::Antialiasing);
524 painter.setWindow(cacheRect);
525 drawCache(painter, converter, displayRenderInterface, assistantVisible);
528 d->
s->cachedRect = cacheRect.translated(-widgetBound.topLeft());
529 d->
s->cached = QPixmapCache::insert(cached);
532 gc.drawPixmap(paintRect, cached, paintRect.translated(-widgetBound.topLeft() -
d->
s->cachedRect.topLeft()));
536 d->
s->m_canvas = canvas;
542 d->
s->cached = QPixmapCache::Key();
549 r = r.united(QRectF(*h, QSizeF(1,1)));
551 return r.adjusted(-2, -2, 2, 2).toAlignedRect();
579 QXmlStreamWriter xml(&data);
580 xml.writeStartDocument();
581 xml.writeStartElement(
"assistant");
582 xml.writeAttribute(
"type",
d->
s->id);
583 xml.writeAttribute(
"active", QString::number(
d->
s->isSnappingActive));
584 xml.writeAttribute(
"useCustomColor", QString::number(
d->
s->useCustomColor));
586 xml.writeAttribute(
"locked", QString::number(
d->
s->isLocked));
587 xml.writeAttribute(
"editorWidgetOffset_X", QString::number((
double)(
d->
s->editorWidgetOffset.x()),
'f', 3));
588 xml.writeAttribute(
"editorWidgetOffset_Y", QString::number((
double)(
d->
s->editorWidgetOffset.y()),
'f', 3));
595 xml.writeStartElement(
"handles");
597 int id = handleMap.size();
598 if (!handleMap.contains(handle)){
599 handleMap.insert(handle,
id);
601 id = handleMap.value(handle);
602 xml.writeStartElement(
"handle");
603 xml.writeAttribute(
"id", QString::number(
id));
604 xml.writeAttribute(
"x", QString::number(
double(handle->x()),
'f', 3));
605 xml.writeAttribute(
"y", QString::number(
double(handle->y()),
'f', 3));
606 xml.writeEndElement();
608 xml.writeEndElement();
610 xml.writeStartElement(
"sidehandles");
611 QMap<KisPaintingAssistantHandleSP, int> sideHandleMap;
613 int id = sideHandleMap.size();
614 sideHandleMap.insert(handle,
id);
615 xml.writeStartElement(
"sidehandle");
616 xml.writeAttribute(
"id", QString::number(
id));
617 xml.writeAttribute(
"x", QString::number(
double(handle->x()),
'f', 3));
618 xml.writeAttribute(
"y", QString::number(
double(handle->y()),
'f', 3));
619 xml.writeEndElement();
623 xml.writeEndElement();
624 xml.writeEndDocument();
636 double x = 0.0, y = 0.0;
638 QByteArray data = store->
read(store->
size());
639 QXmlStreamReader xml(data);
640 QMap<int, KisPaintingAssistantHandleSP> sideHandleMap;
641 while (!xml.atEnd()) {
642 switch (xml.readNext()) {
643 case QXmlStreamReader::StartElement:
644 if (xml.name() ==
"assistant") {
646 auto active = xml.attributes().value(
"active");
650 if ( xml.attributes().hasAttribute(
"useCustomColor")) {
653 bool usingColor =
false;
662 if (xml.attributes().hasAttribute(
"editorWidgetOffset_X") && xml.attributes().hasAttribute(
"editorWidgetOffset_Y")) {
663 setEditorWidgetOffset(QPointF(xml.attributes().value(
"editorWidgetOffset_X").toDouble(), xml.attributes().value(
"editorWidgetOffset_Y").toDouble()));
666 if ( xml.attributes().hasAttribute(
"customColor")) {
667 auto customColor = xml.attributes().value(
"customColor");
672 if ( xml.attributes().hasAttribute(
"locked")) {
673 auto locked = xml.attributes().value(
"locked");
681 if (xml.name() ==
"handle") {
682 QString strId = xml.attributes().value(
"id").toString(),
683 strX = xml.attributes().value(
"x").toString(),
684 strY = xml.attributes().value(
"y").toString();
685 if (!strId.isEmpty() && !strX.isEmpty() && !strY.isEmpty()) {
689 if (!handleMap.contains(
id)) {
694 }
else if (xml.name() ==
"sidehandle") {
695 QString strId = xml.attributes().value(
"id").toString(),
696 strX = xml.attributes().value(
"x").toString(),
697 strY = xml.attributes().value(
"y").toString();
698 if (!strId.isEmpty() && !strX.isEmpty() && !strY.isEmpty()) {
702 if (!sideHandleMap.contains(
id)) {
725 if (
d->
s->id ==
"ellipse"){
726 QDomElement assistantElement = doc.createElement(
"assistant");
727 assistantElement.setAttribute(
"type",
"ellipse");
728 assistantElement.setAttribute(
"filename", QString(
"ellipse%1.assistant").arg(count));
729 assistantsElement.appendChild(assistantElement);
731 else if (
d->
s->id ==
"spline"){
732 QDomElement assistantElement = doc.createElement(
"assistant");
733 assistantElement.setAttribute(
"type",
"spline");
734 assistantElement.setAttribute(
"filename", QString(
"spline%1.assistant").arg(count));
735 assistantsElement.appendChild(assistantElement);
737 else if (
d->
s->id ==
"perspective"){
738 QDomElement assistantElement = doc.createElement(
"assistant");
739 assistantElement.setAttribute(
"type",
"perspective");
740 assistantElement.setAttribute(
"filename", QString(
"perspective%1.assistant").arg(count));
741 assistantsElement.appendChild(assistantElement);
743 else if (
d->
s->id ==
"vanishing point"){
744 QDomElement assistantElement = doc.createElement(
"assistant");
745 assistantElement.setAttribute(
"type",
"vanishing point");
746 assistantElement.setAttribute(
"filename", QString(
"vanishing point%1.assistant").arg(count));
747 assistantsElement.appendChild(assistantElement);
749 else if (
d->
s->id ==
"infinite ruler"){
750 QDomElement assistantElement = doc.createElement(
"assistant");
751 assistantElement.setAttribute(
"type",
"infinite ruler");
752 assistantElement.setAttribute(
"filename", QString(
"infinite ruler%1.assistant").arg(count));
753 assistantsElement.appendChild(assistantElement);
755 else if (
d->
s->id ==
"parallel ruler"){
756 QDomElement assistantElement = doc.createElement(
"assistant");
757 assistantElement.setAttribute(
"type",
"parallel ruler");
758 assistantElement.setAttribute(
"filename", QString(
"parallel ruler%1.assistant").arg(count));
759 assistantsElement.appendChild(assistantElement);
761 else if (
d->
s->id ==
"concentric ellipse"){
762 QDomElement assistantElement = doc.createElement(
"assistant");
763 assistantElement.setAttribute(
"type",
"concentric ellipse");
764 assistantElement.setAttribute(
"filename", QString(
"concentric ellipse%1.assistant").arg(count));
765 assistantsElement.appendChild(assistantElement);
767 else if (
d->
s->id ==
"fisheye-point"){
768 QDomElement assistantElement = doc.createElement(
"assistant");
769 assistantElement.setAttribute(
"type",
"fisheye-point");
770 assistantElement.setAttribute(
"filename", QString(
"fisheye-point%1.assistant").arg(count));
771 assistantsElement.appendChild(assistantElement);
773 else if (
d->
s->id ==
"ruler"){
774 QDomElement assistantElement = doc.createElement(
"assistant");
775 assistantElement.setAttribute(
"type",
"ruler");
776 assistantElement.setAttribute(
"filename", QString(
"ruler%1.assistant").arg(count));
777 assistantsElement.appendChild(assistantElement);
779 else if (
d->
s->id ==
"two point"){
780 QDomElement assistantElement = doc.createElement(
"assistant");
781 assistantElement.setAttribute(
"type",
"two point");
782 assistantElement.setAttribute(
"filename", QString(
"two point%1.assistant").arg(count));
783 assistantsElement.appendChild(assistantElement);
785 else if (
d->
s->id ==
"perspective ellipse"){
786 QDomElement assistantElement = doc.createElement(
"assistant");
787 assistantElement.setAttribute(
"type",
"perspective ellipse");
788 assistantElement.setAttribute(
"filename", QString(
"perspective ellipse%1.assistant").arg(count));
789 assistantsElement.appendChild(assistantElement);
791 else if (
d->
s->id ==
"curvilinear-perspective"){
792 QDomElement assistantElement = doc.createElement(
"assistant");
793 assistantElement.setAttribute(
"type",
"curvilinear-perspective");
794 assistantElement.setAttribute(
"filename", QString(
"curvilinear-perspective%1.assistant").arg(count));
795 assistantsElement.appendChild(assistantElement);
802 uint vHole = 0,hHole = 0;
804 if (
d->
handles.size() == 4 &&
d->
s->id ==
"perspective") {
809 hHandlesList.append(handle);
810 hHole = hHandlesList.size() - 1;
811 vHandlesList.append(handle);
812 vHole = vHandlesList.size() - 1;
816 while(hHole > 0 && hHandlesList.at(hHole -1).data()->x() > handle.
data()->x()) {
817 hHandlesList.swapItemsAt(hHole - 1, hHole);
823 while(vHole > 0 && vHandlesList.at(vHole -1).data()->y() > handle.
data()->y()) {
824 vHandlesList.swapItemsAt(vHole-1, vHole);
832 if(vHandlesList.at(0).data()->x() > vHandlesList.at(1).data()->x()) {
840 if(vHandlesList.at(2).data()->x() > vHandlesList.at(3).data()->x()) {
858 if(hHandlesList.at(0).data()->y() > hHandlesList.at(1).data()->y()) {
866 if(hHandlesList.at(2).data()->y() > hHandlesList.at(3).data()->y()) {
922 QPointF intersection(0,0);
923 if((QLineF(
d->
handles.at(0).data()->toPoint(),
d->
handles.at(1).data()->toPoint()).intersects(QLineF(
d->
handles.at(2).data()->toPoint(),
d->
handles.at(3).data()->toPoint()), &intersection) != QLineF::NoIntersection)
924 && (QLineF(
d->
handles.at(0).data()->toPoint(),
d->
handles.at(1).data()->toPoint()).intersects(QLineF(
d->
handles.at(2).data()->toPoint(),
d->
handles.at(3).data()->toPoint()), &intersection) != QLineF::UnboundedIntersection))
928 else if((QLineF(
d->
handles.at(0).data()->toPoint(),
d->
handles.at(2).data()->toPoint()).intersects(QLineF(
d->
handles.at(1).data()->toPoint(),
d->
handles.at(3).data()->toPoint()), &intersection) != QLineF::NoIntersection)
929 && (QLineF(
d->
handles.at(0).data()->toPoint(),
d->
handles.at(2).data()->toPoint()).intersects(QLineF(
d->
handles.at(1).data()->toPoint(),
d->
handles.at(3).data()->toPoint()), &intersection) != QLineF::UnboundedIntersection))
1043 int m_handleSize = 16;
1045 QRectF handlerect(pointTwo - QPointF(m_handleSize * 0.5, m_handleSize * 0.5), QSizeF(m_handleSize, m_handleSize));
1046 return handlerect.contains(pointOne);
1051 if (!
d->
s->m_canvas) {
1071 QPointF documentCoord =
d->
s->m_canvas->image()->pixelToDocument(pixelCoords);
1072 return d->
s->m_canvas->viewConverter()->documentToView(documentCoord);
1079 if (
d->
s->followBrushPosition &&
d->
s->adjustedPositionValid) {
1081 }
else if (canvas) {
1083 mousePos= canvas->
canvasWidget()->mapFromGlobal(QCursor::pos());
1086 mousePos = QCursor::pos();
1087 dbgUI <<
"no canvas given for assistant, you may have passed arguments incorrectly:";
1111 QPointF
topLeft = QPointF(qMin(first->x(), second->x()), qMin(first->y(), second->y()));
1112 QPointF
bottomRight = QPointF(qMax(first->x(), second->x()), qMax(first->y(), second->y()));
1120 return p.x() *
p.x() +
p.y() *
p.y();
1130 QMap<KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP> handleMap;
1132 for (
auto i = list.begin(); i != list.end(); ++i) {
1133 clonedList << (*i)->clone(handleMap);
1158 Q_FOREACH (
const QString &
id,
keys()) {
1161 dbgRegistry <<
"deleting KisPaintingAssistantFactoryRegistry ";
float value(const T *src, size_t ch)
Q_GLOBAL_STATIC(KisStoragePluginRegistry, s_instance)
KisAbstractCanvasWidget * canvasWidget
QColor defaultAssistantsColor(bool defaultValue=false) const
_Private::Traits< T >::Result widgetToDocument(const T &obj) const
QTransform documentToWidgetTransform() const
_Private::Traits< T >::Result documentToWidget(const T &obj) const
QSizeF getCanvasWidgetSize() const
~KisPaintingAssistantFactoryRegistry() override
KisPaintingAssistantFactoryRegistry()
static KisPaintingAssistantFactoryRegistry * instance()
virtual ~KisPaintingAssistantFactory()
KisPaintingAssistantFactory()
void mergeWith(KisPaintingAssistantHandleSP)
void registerAssistant(KisPaintingAssistant *)
void unregisterAssistant(KisPaintingAssistant *)
KisPaintingAssistant * chiefAssistant() const
KisPaintingAssistantHandle(double x, double y)
~KisPaintingAssistantHandle()
bool containsAssistant(KisPaintingAssistant *) const
KisPaintingAssistantHandle & operator=(const QPointF &)
virtual QRect boundingRect() const
virtual KisPaintingAssistantHandleSP secondLocalHandle() const
secondLocalHandle Note: this doesn't guarantee it will be the bottomRight corner! For that,...
void drawError(QPainter &painter, const QPainterPath &path, const KoColorDisplayRendererInterface *displayRenderInterface)
const KisPaintingAssistantHandleSP topMiddle() const
static QList< KisPaintingAssistantSP > cloneAssistantList(const QList< KisPaintingAssistantSP > &list)
QPointF effectiveBrushPosition(const KisCoordinatesConverter *converter, KisCanvas2 *canvas) const
Query the effective brush position to be used for preview lines. This is intended to be used for pain...
bool isDuplicating()
isDuplicating
void setDecorationThickness(int thickness)
void setLocked(bool value)
setLocked
bool isSnappingActive() const
void setDuplicating(bool value)
setDuplicating
virtual bool isAssistantComplete() const
void addHandle(KisPaintingAssistantHandleSP handle, HandleType type)
virtual QPointF getDefaultEditorPosition() const =0
void drawPreview(QPainter &painter, const QPainterPath &path, const KoColorDisplayRendererInterface *displayRenderInterface)
void drawX(QPainter &painter, const QPointF &pt, const KoColorDisplayRendererInterface *displayRenderInterface)
virtual void transform(const QTransform &transform)
virtual void drawAssistant(QPainter &gc, const QRectF &updateRect, const KisCoordinatesConverter *converter, const KoColorDisplayRendererInterface *displayRenderInterface, bool cached, KisCanvas2 *canvas=0, bool assistantVisible=true, bool previewVisible=true)
const KisPaintingAssistantHandleSP bottomMiddle() const
virtual void saveCustomXml(QXmlStreamWriter *xml)
void drawPath(QPainter &painter, const QPainterPath &path, const KoColorDisplayRendererInterface *displayRenderInterface, bool drawActive=true)
void setAssistantGlobalColorCache(const QColor &color)
KisPaintingAssistantHandleSP topLeft()
QColor effectiveAssistantColor() const
const QString & name() const
void copySharedData(KisPaintingAssistantSP assistant)
const KisPaintingAssistantHandleSP bottomRight() const
QRectF getLocalRect() const
getLocalRect The function deals with local handles not being topLeft and bottomRight gracefully and r...
bool isLocal() const
isLocal
QColor assistantCustomColor()
const KisPaintingAssistantHandleSP topLeft() const
virtual void setAdjustedBrushPosition(const QPointF position)
const QList< KisPaintingAssistantHandleSP > & sideHandles() const
KisPaintingAssistantHandleSP closestCornerHandleFromPoint(QPointF point)
virtual bool canBeLocal() const
canBeLocal
virtual void drawCache(QPainter &gc, const KisCoordinatesConverter *converter, const KoColorDisplayRendererInterface *displayRenderInterface, bool assistantVisible=true)=0
performance layer where the graphics can be drawn from a cache instead of generated every render upda...
void saveXmlList(QDomDocument &doc, QDomElement &assistantsElement, int count)
static double norm2(const QPointF &p)
void setEditorWidgetOffset(QPointF offset)
void findPerspectiveAssistantHandleLocation()
virtual ~KisPaintingAssistant()
const KisPaintingAssistantHandleSP bottomLeft() const
void setAssistantCustomColor(QColor color)
QPointF pixelToView(const QPoint pixelCoords) const
bool m_hasBeenInsideLocalRect
virtual bool loadCustomXml(QXmlStreamReader *xml)
virtual QPointF getEditorPosition() const
virtual KisPaintingAssistantHandleSP firstLocalHandle() const
firstLocalHandle Note: this doesn't guarantee it will be the topleft corner! For that,...
const KisPaintingAssistantHandleSP rightMiddle() const
QPointF editorWidgetOffset()
void setLocal(bool value)
setLocal
KisPaintingAssistant(const QString &id, const QString &name)
virtual void setFollowBrushPosition(bool follow)
const QString & id() const
void loadXml(KoStore *store, QMap< int, KisPaintingAssistantHandleSP > &handleMap, QString path)
void setSnappingActive(bool set)
QPointF viewportConstrainedEditorPosition(const KisCoordinatesConverter *converter, const QSize editorSize)
const QList< KisPaintingAssistantHandleSP > & handles() const
void setUseCustomColor(bool useCustomColor)
void replaceHandle(KisPaintingAssistantHandleSP _handle, KisPaintingAssistantHandleSP _with)
KisPaintingAssistantHandleSP oppHandleOne()
bool areTwoPointsClose(const QPointF &pointOne, const QPointF &pointTwo)
const KisPaintingAssistantHandleSP leftMiddle() const
QByteArray saveXml(QMap< KisPaintingAssistantHandleSP, int > &handleMap)
const KisPaintingAssistantHandleSP topRight() const
KisPaintingAssistantHandleSP bottomRight()
void initHandles(QList< KisPaintingAssistantHandleSP > _handles)
virtual QColor convertColorToDisplayColorSpace(const KoColor color) const =0
convertColorToDisplayColorSpace
KisPaintingAssistantFactory * get(const QString &id) const
QList< QString > keys() const
bool open(const QString &name)
QByteArray read(qint64 max)
KisSharedPtr< KisPaintingAssistantHandle > KisPaintingAssistantHandleSP
QString qColorToQString(QColor color)
QColor qStringToQColor(QString colorString)
QList< KisPaintingAssistant * > assistants
QColor assistantCustomColor
QPointF adjustedBrushPosition
QPointF editorWidgetOffset
struct KisPaintingAssistant::Private::SharedData::TranslationInvariantTransform cachedTransform
bool adjustedPositionValid
QColor assistantGlobalColorCache
KisPaintingAssistantHandleSP reuseOrCreateHandle(QMap< KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP > &handleMap, KisPaintingAssistantHandleSP origHandle, KisPaintingAssistant *q, bool registerAssistant=true)
QList< KisPaintingAssistantHandleSP > handles
KisPaintingAssistantHandleSP bottomLeft
const int previewLineWidth
QList< KisPaintingAssistantHandleSP > sideHandles
KisPaintingAssistantHandleSP topRight
KisPaintingAssistantHandleSP topMiddle
QSharedPointer< SharedData > s
KisPaintingAssistantHandleSP rightMiddle
KisPaintingAssistantHandleSP leftMiddle
KisPaintingAssistantHandleSP topLeft
KisPaintingAssistantHandleSP bottomRight
KisPaintingAssistantHandleSP bottomMiddle
static KoColorSpaceRegistry * instance()