Krita Source Code Documentation
Loading...
Searching...
No Matches
KisGridDecoration Class Reference

#include <kis_grid_decoration.h>

+ Inheritance diagram for KisGridDecoration:

Classes

struct  Private
 

Public Member Functions

 KisGridDecoration (KisView *parent)
 
void setGridConfig (const KisGridConfig &config)
 
 ~KisGridDecoration () override
 
- Public Member Functions inherited from KisCanvasDecoration
const QString & id () const
 
 KisCanvasDecoration (const QString &id, QPointer< KisView >parent)
 
virtual void notifyWindowMinimized (bool minimized)
 
void paint (QPainter &gc, const QRectF &updateRect, const KisCoordinatesConverter *converter, KisCanvas2 *canvas)
 
int priority () const
 
void setView (QPointer< KisView > imageView)
 
bool visible () const
 
 ~KisCanvasDecoration () override
 
- Public Member Functions inherited from KisShared
bool deref ()
 
bool ref ()
 
int refCount ()
 
QAtomicInt * sharedWeakReference ()
 

Protected Member Functions

void drawDecoration (QPainter &gc, const QRectF &updateArea, const KisCoordinatesConverter *converter, KisCanvas2 *canvas) override
 
- Protected Member Functions inherited from KisCanvasDecoration
int decorationThickness () const
 
void setPriority (int value)
 
QPointer< KisViewview () const
 
- Protected Member Functions inherited from KisShared
 KisShared ()
 
 ~KisShared ()
 

Private Attributes

const QScopedPointer< Privatem_d
 

Additional Inherited Members

- Public Slots inherited from KisCanvasDecoration
virtual void setVisible (bool v)
 
void toggleVisibility ()
 
- Static Public Member Functions inherited from KisCanvasDecoration
static bool comparePriority (KisCanvasDecorationSP decoration1, KisCanvasDecorationSP decoration2)
 

Detailed Description

Definition at line 19 of file kis_grid_decoration.h.

Constructor & Destructor Documentation

◆ KisGridDecoration()

KisGridDecoration::KisGridDecoration ( KisView * parent)

Definition at line 27 of file kis_grid_decoration.cpp.

28 : KisCanvasDecoration("grid", parent),
29 m_d(new Private)
30{
31 setPriority(0);
32}
KisCanvasDecoration(const QString &id, QPointer< KisView >parent)
const QScopedPointer< Private > m_d

References KisCanvasDecoration::setPriority().

◆ ~KisGridDecoration()

KisGridDecoration::~KisGridDecoration ( )
override

Definition at line 34 of file kis_grid_decoration.cpp.

35{
36
37}

Member Function Documentation

◆ drawDecoration()

void KisGridDecoration::drawDecoration ( QPainter & gc,
const QRectF & updateArea,
const KisCoordinatesConverter * converter,
KisCanvas2 * canvas )
overrideprotectedvirtual

Implements KisCanvasDecoration.

Definition at line 44 of file kis_grid_decoration.cpp.

45{
46 if (!m_d->config.showGrid()) return;
47
48 Q_UNUSED(canvas);
49
50 QTransform transform = converter->imageToWidgetTransform();
51
52 const qreal scale = KoUnit::approxTransformScale(transform);
53 const int minWidgetSize = 3;
54 const int effectiveSize = qMin(m_d->config.spacing().x(), m_d->config.spacing().y());
55
56 int scaleCoeff = 1;
57 quint32 subdivision = m_d->config.subdivision();
58
59 while (qFloor(scale * scaleCoeff * effectiveSize) <= minWidgetSize) {
60 if (subdivision > 1) {
61 scaleCoeff = subdivision;
62 subdivision = 1;
63 } else {
64 scaleCoeff *= 2;
65 }
66
67 if (scaleCoeff > 32768) {
68 qWarning() << "WARNING: Grid Scale Coeff is too high! That is surely a bug!";
69 return;
70 }
71 }
72
73 QPen mainPen = m_d->config.penMain();
74 QPen subdivisionPen = m_d->config.penSubdivision();
75
76 gc.save();
77 gc.setTransform(transform);
78
79 const QRect imageRectInImagePixels = converter->imageRectInImagePixels();
80 const QRectF updateRectInImagePixels =
81 converter->documentToImage(updateArea) &
82 imageRectInImagePixels;
83
84 // for angles. This will later be a combobox to select different types of options
85 // also add options to hide specific lines (vertical, horizontal, angle 1, etc
86 KisGridConfig::GridType gridType = m_d->config.gridType();
87
88 if (gridType == KisGridConfig::GRID_RECTANGULAR) {
89 gc.setRenderHints(QPainter::Antialiasing, false);
90 qreal x1, y1, x2, y2;
91 updateRectInImagePixels.getCoords(&x1, &y1, &x2, &y2);
92
93 // compensate the fact the getCoordt returns off-by-one pixel
94 // at the bottom right of the rect.
95 x2++;
96 y2++;
97
98 if (m_d->config.xSpacingActive()) {
99 // vertical lines
100 int offset = 0;
101 if (m_d->config.offsetActive()) {
102 offset = m_d->config.offset().x();
103 }
104 const int step = scaleCoeff * m_d->config.spacing().x();
105 const int lineIndexFirst = qCeil((x1 - offset) / step);
106 const int lineIndexLast = qFloor((x2 - offset) / step);
107
108 if (mainPen.style() != Qt::SolidLine) {
109 mainPen.setDashOffset(y1 * scale);
110 }
111 if (subdivisionPen.style() != Qt::SolidLine) {
112 subdivisionPen.setDashOffset(y1 * scale);
113 }
114
115 for (int i = lineIndexFirst; i <= lineIndexLast; i++) {
116 int w = offset + i * step;
117
118 gc.setPen(i % subdivision == 0 ? mainPen : subdivisionPen);
119 // we adjusted y2 to draw the grid correctly, clip it now...
120 gc.drawLine(QPointF(w, y1),QPointF(w, qMin(y2, qreal(imageRectInImagePixels.bottom() + 1))));
121 }
122 }
123
124 if (m_d->config.ySpacingActive()) {
125 // horizontal lines
126 int offset = 0;
127 if (m_d->config.offsetActive()) {
128 offset = m_d->config.offset().y();
129 }
130 const int step = scaleCoeff * m_d->config.spacing().y();
131 const int lineIndexFirst = qCeil((y1 - offset) / step);
132 const int lineIndexLast = qFloor((y2 - offset) / step);
133
134 if (mainPen.style() != Qt::SolidLine) {
135 mainPen.setDashOffset(x1 * scale);
136 }
137 if (subdivisionPen.style() != Qt::SolidLine) {
138 subdivisionPen.setDashOffset(x1 * scale);
139 }
140
141 for (int i = lineIndexFirst; i <= lineIndexLast; i++) {
142 int w = offset + i * step;
143
144 gc.setPen(i % subdivision == 0 ? mainPen : subdivisionPen);
145 // we adjusted x2 to draw the grid correctly, clip it now...
146 gc.drawLine(QPointF(x1, w),QPointF(qMin(x2, qreal(imageRectInImagePixels.right() + 1)), w));
147 }
148 }
149 } else if (gridType == KisGridConfig::GRID_ISOMETRIC_LEGACY) {
150 qreal x1, y1, x2, y2;
151
152 // get true coordinates, not just the updateArea
153 QRectF trueImageRect = converter->imageRectInImagePixels();
154 trueImageRect.getCoords(&x1, &y1, &x2, &y2);
155
156 // compensate the fact the getCoordt returns off-by-one pixel
157 // at the bottom right of the rect.
158 x2++;
159 y2++;
160
161 int offset = 0;
162 int offsetY = 0;
163 if (m_d->config.offsetActive()) {
164 offset = m_d->config.offset().x();
165 offsetY = m_d->config.offset().y();
166 }
167 const int cellSpacing = m_d->config.cellSpacing();
168
169 gc.setClipping(true);
170 gc.setClipRect(updateRectInImagePixels, Qt::IntersectClip);
171
172 // left angle
173 if (m_d->config.angleLeftActive()) {
174 const qreal gridXAngle = m_d->config.angleLeft();
175 const qreal bottomRightOfImageY = y2; // this should be the height of the image
176 qreal finalY = 0.0;
177
178 // figure out the spacing based off the angle. The spacing needs to be perpendicular to the angle,
179 // so we need to do a bit of trig to get the correct spacing.
180 qreal correctedAngleSpacing = cellSpacing;
181 if (gridXAngle > 0.0) {
182 correctedAngleSpacing = cellSpacing / qCos(qDegreesToRadians(gridXAngle));
183 gc.setRenderHints(QPainter::Antialiasing, true);
184 } else {
185 // horizontal line: don't want to be antialiased
186 gc.setRenderHints(QPainter::Antialiasing, false);
187 }
188
189 qreal counter = qFloor((-(offset + offsetY)) / correctedAngleSpacing);
190
191 while (finalY < bottomRightOfImageY) {
192
193 const qreal w = (counter * correctedAngleSpacing) + offsetY + offset;
194 gc.setPen(mainPen);
195
196 // calculate where the ending point will be based off the angle
197 const qreal startingY = w;
198 const qreal horizontalDistance = x2;
199
200 // qTan takes radians, so convert first before sending it
201 const qreal length2 = qTan(qDegreesToRadians(gridXAngle)) * x2;
202
203 finalY = startingY - length2;
204
205 gc.drawLine(QPointF(x1, w), QPointF(horizontalDistance, finalY));
206
207 counter = counter + 1.0;
208 }
209 }
210
211 // right angle (almost the same thing, except starting the lines on the right side)
212 if (m_d->config.angleRightActive()) {
213 const qreal gridXAngle = m_d->config.angleRight(); // TODO: add another angle property
214 const qreal bottomLeftOfImageY = y2;
215
216 // figure out the spacing based off the angle
217 qreal correctedAngleSpacing = cellSpacing;
218 if (gridXAngle > 0.0) {
219 correctedAngleSpacing = cellSpacing / qCos(qDegreesToRadians(gridXAngle));
220 gc.setRenderHints(QPainter::Antialiasing, true);
221 } else {
222 // horizontal line: don't want to be antialiased
223 gc.setRenderHints(QPainter::Antialiasing, false);
224 }
225
226 // distance is the same (width of the image)
227 const qreal horizontalDistance = x2;
228 // qTan takes radians, so convert first before sending it
229 const qreal length2 = qTan(qDegreesToRadians(gridXAngle)) * horizontalDistance;
230
231 // let's get x, y of the line that starts in the top right corder
232 const qreal yLower = 0.0;
233 const qreal yHigher = yLower - length2;
234
235 const qreal yLeftFirst = qCeil(yHigher / correctedAngleSpacing) * correctedAngleSpacing;
236 const qreal additionalOffset = yLeftFirst - yHigher;
237 qreal finalY = 0.0;
238 qreal counter = qFloor((-(offsetY - offset)) / correctedAngleSpacing);
239
240 while (finalY < bottomLeftOfImageY) {
241
242 const qreal w = (counter * correctedAngleSpacing) + offsetY - offset + additionalOffset;
243 gc.setPen(mainPen);
244
245 // calculate where the ending point will be based off the angle
246 const qreal startingY = w;
247
248 finalY = startingY - length2;
249
250 gc.drawLine(QPointF(x2, w), QPointF(0.0, finalY));
251
252 counter = counter + 1.0;
253 }
254 }
255 } else if (gridType == KisGridConfig::GRID_ISOMETRIC) {
256 qreal x1, y1, xRight, yBottom;
257
258 // get true coordinates, not just the updateArea
259 QRectF trueImageRect = converter->imageRectInImagePixels();
260 trueImageRect.getCoords(&x1, &y1, &xRight, &yBottom);
261
262 // compensate the fact the getCoordt returns off-by-one pixel
263 // at the bottom right of the rect.
264 xRight++;
265 yBottom++;
266
267 const KisGridConfig::TrigoCache trigoCache = m_d->config.trigoCache();
268
269 int offsetX = 0;
270 int offsetY = 0;
271 if (m_d->config.offsetActive()) {
272 offsetX = trigoCache.correctedAngleRightOffsetX;
273 offsetY = m_d->config.offset().y();
274 }
275
276 gc.setClipping(true);
277 gc.setClipRect(updateRectInImagePixels, Qt::IntersectClip);
278
279 // left angle
280 if (trigoCache.correctedAngleLeftCellSize > 0 && m_d->config.angleLeftActive()) {
281 if (m_d->config.angleLeft() > 0.0) {
282 gc.setRenderHints(QPainter::Antialiasing, true);
283 } else {
284 // horizontal line: don't want to be antialiased
285 gc.setRenderHints(QPainter::Antialiasing, false);
286 }
287
288 const qreal length2 = trigoCache.tanAngleLeft * xRight;
289
290 // let's get x, y of the line that starts in the top right corner
291 const qreal yLower = 0.0;
292 const qreal yHigher = yLower - length2;
293
294 const qreal yLeftFirst = qCeil(yHigher / trigoCache.correctedAngleLeftCellSize) * trigoCache.correctedAngleLeftCellSize;
295 const qreal additionalOffset = yLeftFirst - yHigher;
296
297 qreal finalY = 0.0;
298
299 // define line number used to determinate if is a subdivision or not
300 int subdivisionIndex = qCeil(yHigher / trigoCache.correctedAngleLeftCellSize);
301
302 qreal startingY = yLeftFirst + offsetY + offsetX + additionalOffset;
303
304 while (finalY < yBottom) {
305 // calculate where the ending point will be based off the angle
306 finalY = startingY - length2;
307
308 gc.setPen(qAbs(subdivisionIndex) % subdivision == 0 ? mainPen : subdivisionPen);
309 gc.drawLine(QPointF(0.0, startingY), QPointF(xRight, finalY));
310
311 subdivisionIndex++;
312 startingY += trigoCache.correctedAngleLeftCellSize;
313 }
314 }
315
316 // right angle (almost the same thing, except starting the lines on the right side)
317 if (trigoCache.correctedAngleRightCellSize > 0 && m_d->config.angleRightActive()) {
318 if (m_d->config.angleRight() > 0.0) {
319 gc.setRenderHints(QPainter::Antialiasing, true);
320 } else {
321 // horizontal line: don't want to be antialiased
322 gc.setRenderHints(QPainter::Antialiasing, false);
323 }
324
325 const qreal length2 = trigoCache.tanAngleRight * xRight;
326
327 // let's get x, y of the line that starts in the top right corner
328 const qreal yLower = 0.0;
329 const qreal yHigher = yLower - length2;
330
331 const qreal yLeftFirst = qCeil(yHigher / trigoCache.correctedAngleRightCellSize) * trigoCache.correctedAngleRightCellSize;
332 const qreal additionalOffset = yLeftFirst - yHigher;
333
334 qreal finalY = 0.0;
335
336 // define line number used to determinate if is a subdivision or not
337 int subdivisionIndex = qCeil(yHigher / trigoCache.correctedAngleRightCellSize);
338
339 qreal startingY = yLeftFirst + offsetY - offsetX + additionalOffset;
340
341 while (finalY < yBottom) {
342 // calculate where the ending point will be based off the angle
343 finalY = startingY - length2;
344
345 gc.setPen(qAbs(subdivisionIndex) % subdivision == 0 ? mainPen : subdivisionPen);
346 gc.drawLine(QPointF(xRight, startingY), QPointF(0.0, finalY));
347
348 subdivisionIndex++;
349 startingY += trigoCache.correctedAngleRightCellSize;
350 }
351 }
352
353 // vertical
354 if (trigoCache.verticalSpace > 0) {
355 QPen verticalPen = m_d->config.penVertical();
356 if (verticalPen.style() != Qt::SolidLine) {
357 verticalPen.setDashOffset(updateRectInImagePixels.top() * scale);
358 }
359 gc.setPen(verticalPen);
360 gc.setRenderHints(QPainter::Antialiasing, false);
361
362 int offset = 0;
363 if (m_d->config.offsetActive()) {
364 offset = m_d->config.offset().x();
365 }
366
367 qreal pX = (xRight - x1)/(2*trigoCache.verticalSpace);
368 pX = offset + (pX - qFloor(pX)) * trigoCache.verticalSpace;
369 pX = pX - trigoCache.verticalSpace * qFloor(pX/trigoCache.verticalSpace);
370
371 while(pX <= updateRectInImagePixels.right()) {
372 if (pX > updateRectInImagePixels.left()) {
373 gc.drawLine(QPointF(pX, updateRectInImagePixels.top()),QPointF(pX, qMin(yBottom, qreal(updateRectInImagePixels.bottom() + 1))));
374 }
375 pX += trigoCache.verticalSpace;
376 }
377 }
378 }
379
380 gc.restore();
381}
_Private::Traits< T >::Result documentToImage(const T &obj) const
static qreal approxTransformScale(const QTransform &t)
Definition KoUnit.cpp:387

References KoUnit::approxTransformScale(), KisGridConfig::TrigoCache::correctedAngleLeftCellSize, KisGridConfig::TrigoCache::correctedAngleRightCellSize, KisGridConfig::TrigoCache::correctedAngleRightOffsetX, KisCoordinatesConverter::documentToImage(), KisGridConfig::GRID_ISOMETRIC, KisGridConfig::GRID_ISOMETRIC_LEGACY, KisGridConfig::GRID_RECTANGULAR, KisCoordinatesConverter::imageRectInImagePixels(), KisCoordinatesConverter::imageToWidgetTransform(), m_d, KisGridConfig::TrigoCache::tanAngleLeft, KisGridConfig::TrigoCache::tanAngleRight, and KisGridConfig::TrigoCache::verticalSpace.

◆ setGridConfig()

void KisGridDecoration::setGridConfig ( const KisGridConfig & config)

Definition at line 39 of file kis_grid_decoration.cpp.

40{
41 m_d->config = config;
42}

References m_d.

Member Data Documentation

◆ m_d

const QScopedPointer<Private> KisGridDecoration::m_d
private

Definition at line 33 of file kis_grid_decoration.h.


The documentation for this class was generated from the following files: