Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_tool_transform_config_widget.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2013 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#include <QVector3D>
9
10#include <kis_icon.h>
11#include "rotation_icons.h"
12#include "kis_canvas2.h"
13#include <KisSignalMapper.h>
15
16#include "KisMainWindow.h"
17#include "KisViewManager.h"
18#include "kis_transform_utils.h"
19#include "kis_config_notifier.h"
20#include <kstandardguiitem.h>
22
23
24template<typename T> inline T sign(T x) {
25 return x > 0 ? 1 : x == (T)0 ? 0 : -1;
26}
27
29
30
32 : QWidget(parent),
33 m_transaction(transaction),
34 m_notificationsBlocked(0),
35 m_uiSlotsBlocked(0),
36 m_configChanged(false)
37{
38 setupUi(this);
39
40 KConfigGroup group = KSharedConfig::openConfig()->group("KisToolTransform");
41 bool useInStackPreview = !group.readEntry("useOverlayPreviewStyle", false);
42 bool forceLodMode = group.readEntry("forceLodMode", true);
43
44 if (useInStackPreview && !forceLodMode) {
45 cmbPreviewMode->setCurrentIndex(0);
46 }
47 else if (useInStackPreview && forceLodMode) {
48 cmbPreviewMode->setCurrentIndex(1);
49 }
50 else {
51 cmbPreviewMode->setCurrentIndex(2);
52 }
53
54 connect(cmbPreviewMode, SIGNAL(currentIndexChanged(int)), this, SLOT(slotPreviewChanged(int)));
55
56 flipXButton->setIcon(KisIconUtils::loadIcon("transform_icons_mirror_x"));
57 flipYButton->setIcon(KisIconUtils::loadIcon("transform_icons_mirror_y"));
58 rotateCWButton->setIcon(KisIconUtils::loadIcon("transform_icons_rotate_cw"));
59 rotateCCWButton->setIcon(KisIconUtils::loadIcon("transform_icons_rotate_ccw"));
60
61 // Granularity can only be specified in the power of 2's
62 QStringList granularityValues{"4","8","16","32"};
63 changeGranularity->addItems(granularityValues);
64 changeGranularity->setCurrentIndex(1);
65 granularityPreview->addItems(granularityValues);
66 granularityPreview->setCurrentIndex(2);
67
68 connect(changeGranularity,SIGNAL(currentTextChanged(QString)),
69 this,SLOT(slotGranularityChanged(QString)));
70 connect(granularityPreview, SIGNAL(currentTextChanged(QString)),
71 this,SLOT(slotPreviewGranularityChanged(QString)));
72
73 // Init Filter combo
74 cmbFilter->setIDList(KisFilterStrategyRegistry::instance()->listKeys());
75 cmbFilter->setCurrent("Bicubic");
76 cmbFilter->setToolTip(i18nc("@info:tooltip",
77 "<p>Select filtering mode:\n"
78 "<ul>"
79 "<li><b>Nearest neighbor</b> for pixel art. Does not produce new color.</li>"
80 "<li><b>Bilinear</b> for areas with uniform color to avoid artifacts.</li>"
81 "<li><b>Bicubic</b> for smoother results.</li>"
82 "<li><b>Lanczos3</b> for sharp results. May produce aerials.</li>"
83 "</ul></p>"));
84 connect(cmbFilter, SIGNAL(activated(KoID)),
85 this, SLOT(slotFilterChanged(KoID)));
86
87 // Init Warp Type combo
88 cmbWarpType->insertItem(KisWarpTransformWorker::AFFINE_TRANSFORM,i18n("Default (Affine)"));
89 cmbWarpType->insertItem(KisWarpTransformWorker::RIGID_TRANSFORM,i18n("Strong (Rigid)"));
90 cmbWarpType->insertItem(KisWarpTransformWorker::SIMILITUDE_TRANSFORM,i18n("Strongest (Similitude)"));
91 cmbWarpType->setCurrentIndex(KisWarpTransformWorker::AFFINE_TRANSFORM);
92 connect(cmbWarpType, SIGNAL(currentIndexChanged(int)), this, SLOT(slotWarpTypeChanged(int)));
93
94 // Init Rotation Center buttons
95 m_handleDir[0] = QPointF(1, 0);
96 m_handleDir[1] = QPointF(1, -1);
97 m_handleDir[2] = QPointF(0, -1);
98 m_handleDir[3] = QPointF(-1, -1);
99 m_handleDir[4] = QPointF(-1, 0);
100 m_handleDir[5] = QPointF(-1, 1);
101 m_handleDir[6] = QPointF(0, 1);
102 m_handleDir[7] = QPointF(1, 1);
103 m_handleDir[8] = QPointF(0, 0); // also add the center
104
105 m_rotationCenterButtons = new QButtonGroup(0);
106 // we set the ids to match m_handleDir
107 m_rotationCenterButtons->addButton(middleRightButton, 0);
108 m_rotationCenterButtons->addButton(topRightButton, 1);
109 m_rotationCenterButtons->addButton(middleTopButton, 2);
110 m_rotationCenterButtons->addButton(topLeftButton, 3);
111 m_rotationCenterButtons->addButton(middleLeftButton, 4);
112 m_rotationCenterButtons->addButton(bottomLeftButton, 5);
113 m_rotationCenterButtons->addButton(middleBottomButton, 6);
114 m_rotationCenterButtons->addButton(bottomRightButton, 7);
115 m_rotationCenterButtons->addButton(centerButton, 8);
116
117 QToolButton *nothingSelected = new QToolButton(0);
118 nothingSelected->setCheckable(true);
119 nothingSelected->setAutoExclusive(true);
120 nothingSelected->hide(); // a convenient button for when no button is checked in the group
121 m_rotationCenterButtons->addButton(nothingSelected, 9);
122
123
124 // initialize values for free transform sliders
125 shearXBox->setSuffix(QStringLiteral("%"));
126 shearYBox->setSuffix(QStringLiteral("%"));
127 shearXBox->setRange(-500, 500, 2);
128 shearYBox->setRange(-500, 500, 2);
129 shearXBox->setSingleStep(1);
130 shearYBox->setSingleStep(1);
131 shearXBox->setValue(0.0);
132 shearYBox->setValue(0.0);
133
134
135 translateXBox->setSuffix(i18n(" px"));
136 translateYBox->setSuffix(i18n(" px"));
137 translateXBox->setRange(-10000, 10000);
138 translateYBox->setRange(-10000, 10000);
139
140
141 scaleXBox->setRange(-10000, 10000);
142 scaleYBox->setRange(-10000, 10000);
143 scaleXBox->setValue(100.0);
144 scaleYBox->setValue(100.0);
145 KisSpinBoxI18nHelper::setText(scaleXBox, i18nc("{n} is the number value, % is the percent sign", "{n}%"));
146 KisSpinBoxI18nHelper::setText(scaleYBox, i18nc("{n} is the number value, % is the percent sign", "{n}%"));
147
148 m_scaleRatio = 1.0;
149
150
151 aXBox->setIncreasingDirection(KisAngleGauge::IncreasingDirection_Clockwise);
152 aYBox->setIncreasingDirection(KisAngleGauge::IncreasingDirection_Clockwise);
153 aZBox->setIncreasingDirection(KisAngleGauge::IncreasingDirection_Clockwise);
154 aXBox->setFlipOptionsMode(KisAngleSelector::FlipOptionsMode_MenuButton);
155 aYBox->setFlipOptionsMode(KisAngleSelector::FlipOptionsMode_MenuButton);
156 aZBox->setFlipOptionsMode(KisAngleSelector::FlipOptionsMode_MenuButton);
157
158 cameraHeightBox->setRange(1, 20000, 2);
159
160 connect(m_rotationCenterButtons, SIGNAL(idPressed(int)), this, SLOT(slotRotationCenterChanged(int)));
161 connect(btnTransformAroundPivotPoint, SIGNAL(clicked(bool)), this, SLOT(slotTransformAroundRotationCenter(bool)));
162
163 // Init Free Transform Values
164 connect(scaleXBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetScaleX(int)));
165 connect(scaleYBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetScaleY(int)));
166 connect(shearXBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetShearX(qreal)));
167 connect(shearYBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetShearY(qreal)));
168 connect(translateXBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetTranslateX(int)));
169 connect(translateYBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetTranslateY(int)));
170 connect(aXBox, SIGNAL(angleChanged(qreal)), this, SLOT(slotSetAX(qreal)));
171 connect(aYBox, SIGNAL(angleChanged(qreal)), this, SLOT(slotSetAY(qreal)));
172 connect(aZBox, SIGNAL(angleChanged(qreal)), this, SLOT(slotSetAZ(qreal)));
173 connect(cameraHeightBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetCameraHeight(qreal)));
174 connect(aspectButton, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(slotSetKeepAspectRatio(bool)));
175 connect(flipXButton, SIGNAL(clicked(bool)), this, SLOT(slotFlipX()));
176 connect(flipYButton, SIGNAL(clicked(bool)), this, SLOT(slotFlipY()));
177 connect(rotateCWButton, SIGNAL(clicked(bool)), this, SLOT(slotRotateCW()));
178 connect(rotateCCWButton, SIGNAL(clicked(bool)), this, SLOT(slotRotateCCW()));
179
180 // toggle visibility of different free buttons
181 connect(freeMoveRadioButton, SIGNAL(clicked(bool)), SLOT(slotTransformAreaVisible(bool)));
182 connect(freeRotationRadioButton, SIGNAL(clicked(bool)), SLOT(slotTransformAreaVisible(bool)));
183 connect(freeScaleRadioButton, SIGNAL(clicked(bool)), SLOT(slotTransformAreaVisible(bool)));
184 connect(freeShearRadioButton, SIGNAL(clicked(bool)), SLOT(slotTransformAreaVisible(bool)));
185
186 // only first group for free transform
187 rotationGroup->hide();
188 moveGroup->show();
189 scaleGroup->hide();
190 shearGroup->hide();
191
192
193
194 // Init Warp Transform Values
195 alphaBox->setSingleStep(0.1);
196 alphaBox->setRange(0, 10, 1);
197
198 connect(alphaBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetWarpAlpha(qreal)));
199 connect(densityBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetWarpDensity(int)));
200
201 connect(defaultRadioButton, SIGNAL(clicked(bool)), this, SLOT(slotWarpDefaultPointsButtonClicked(bool)));
202 connect(customRadioButton, SIGNAL(clicked(bool)), this, SLOT(slotWarpCustomPointsButtonClicked(bool)));
203 connect(lockUnlockPointsButton, SIGNAL(clicked()), this, SLOT(slotWarpLockPointsButtonClicked()));
204 connect(resetPointsButton, SIGNAL(clicked()), this, SLOT(slotWarpResetPointsButtonClicked()));
205
206 // Init Cage Transform Values
207 cageTransformButtonGroup->setId(cageAddEditRadio, 0); // we need to set manually since Qt Designer generates negative by default
208 cageTransformButtonGroup->setId(cageDeformRadio, 1);
209 connect(cageTransformButtonGroup, SIGNAL(idClicked(int)), this, SLOT(slotCageOptionsChanged(int)));
210
211 // Init Liquify Transform Values
212 liquifySizeSlider->setRange(KisLiquifyProperties::minSize(),
214 liquifySizeSlider->setExponentRatio(3);
215 liquifySizeSlider->setValue(60.0);
216 connect(liquifySizeSlider, SIGNAL(valueChanged(qreal)), this, SLOT(liquifySizeChanged(qreal)));
217 connect(liquifySizeSlider, SIGNAL(editingFinished()), SLOT(notifyEditingFinished()));
218 liquifySizeSlider->setToolTip(i18nc("@info:tooltip", "Size of the deformation brush"));
219
220 liquifyAmountSlider->setRange(0.0, 1.0, 2);
221 liquifyAmountSlider->setSingleStep(0.01);
222 liquifyAmountSlider->setValue(0.05);
223 connect(liquifyAmountSlider, SIGNAL(valueChanged(qreal)), this, SLOT(liquifyAmountChanged(qreal)));
224 connect(liquifyAmountSlider, SIGNAL(editingFinished()), SLOT(notifyEditingFinished()));
225 liquifyAmountSlider->setToolTip(i18nc("@info:tooltip", "Amount of the deformation you get"));
226
227 liquifyFlowSlider->setRange(0.0, 1.0, 2);
228 liquifyFlowSlider->setSingleStep(0.01);
229 liquifyFlowSlider->setValue(1.0);
230 connect(liquifyFlowSlider, SIGNAL(valueChanged(qreal)), this, SLOT(liquifyFlowChanged(qreal)));
231 connect(liquifyFlowSlider, SIGNAL(editingFinished()), SLOT(notifyEditingFinished()));
232 liquifyFlowSlider->setToolTip(i18nc("@info:tooltip", "When in non-buildup mode, shows how fast the deformation limit is reached."));
233
234 buildupModeComboBox->setCurrentIndex(0); // set to build-up mode by default
235 connect(buildupModeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(liquifyBuildUpChanged(int)));
236 connect(buildupModeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(notifyEditingFinished()));
237 buildupModeComboBox->setToolTip("<p>" + i18nc("@info:tooltip", "Switch between Build Up and Wash mode of painting. Build Up mode adds deformations one on top of the other without any limits. Wash mode gradually deforms the piece to the selected deformation level.") + "</p>");
238
239 liquifySpacingSlider->setRange(0.0, 3.0, 2);
240 liquifySpacingSlider->setExponentRatio(3);
241 liquifySpacingSlider->setSingleStep(0.01);
242 liquifySpacingSlider->setValue(0.2);
243 connect(liquifySpacingSlider, SIGNAL(valueChanged(qreal)), this, SLOT(liquifySpacingChanged(qreal)));
244 connect(liquifySpacingSlider, SIGNAL(editingFinished()), SLOT(notifyEditingFinished()));
245 liquifySpacingSlider->setToolTip(i18nc("@info:tooltip", "Space between two sequential applications of the deformation"));
246
247 liquifySizePressureBox->setChecked(true);
248 connect(liquifySizePressureBox, SIGNAL(toggled(bool)), this, SLOT(liquifySizePressureChanged(bool)));
249 connect(liquifySizePressureBox, SIGNAL(toggled(bool)), this, SLOT(notifyEditingFinished()));
250 liquifySizePressureBox->setToolTip(i18nc("@info:tooltip", "Scale <b>Size</b> value according to current stylus pressure"));
251
252 liquifyAmountPressureBox->setChecked(true);
253 connect(liquifyAmountPressureBox, SIGNAL(toggled(bool)), this, SLOT(liquifyAmountPressureChanged(bool)));
254 connect(liquifyAmountPressureBox, SIGNAL(toggled(bool)), this, SLOT(notifyEditingFinished()));
255 liquifyAmountPressureBox->setToolTip(i18nc("@info:tooltip", "Scale <b>Amount</b> value according to current stylus pressure"));
256
257 liquifyReverseDirectionChk->setChecked(false);
258 connect(liquifyReverseDirectionChk, SIGNAL(toggled(bool)), this, SLOT(liquifyReverseDirectionChanged(bool)));
259 connect(liquifyReverseDirectionChk, SIGNAL(toggled(bool)), this, SLOT(notifyEditingFinished()));
260 liquifyReverseDirectionChk->setToolTip(i18nc("@info:tooltip", "Reverse direction of the current deformation tool"));
261
262 KisSignalMapper *liquifyModeMapper = new KisSignalMapper(this);
263 connect(liquifyMove, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
264 connect(liquifyScale, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
265 connect(liquifyRotate, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
266 connect(liquifyOffset, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
267 connect(liquifyUndo, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
268 liquifyModeMapper->setMapping(liquifyMove, (int)KisLiquifyProperties::MOVE);
269 liquifyModeMapper->setMapping(liquifyScale, (int)KisLiquifyProperties::SCALE);
270 liquifyModeMapper->setMapping(liquifyRotate, (int)KisLiquifyProperties::ROTATE);
271 liquifyModeMapper->setMapping(liquifyOffset, (int)KisLiquifyProperties::OFFSET);
272 liquifyModeMapper->setMapping(liquifyUndo, (int)KisLiquifyProperties::UNDO);
273 connect(liquifyModeMapper, SIGNAL(mapped(int)), SLOT(slotLiquifyModeChanged(int)));
274 connect(liquifyModeMapper, SIGNAL(mapped(int)), SLOT(notifyEditingFinished()));
275
276 liquifyMove->setToolTip(i18nc("@info:tooltip", "Move: drag the image along the brush stroke"));
277 liquifyScale->setToolTip(i18nc("@info:tooltip", "Scale: grow/shrink image under cursor"));
278 liquifyRotate->setToolTip(i18nc("@info:tooltip", "Rotate: twirl image under cursor"));
279 liquifyOffset->setToolTip(i18nc("@info:tooltip", "Offset: shift the image to the right of the stroke direction"));
280 liquifyUndo->setToolTip(i18nc("@info:tooltip", "Undo: erase actions of other tools"));
281
282 // Connect all edit boxes to the Editing Finished signal
283 connect(densityBox, SIGNAL(editingFinished()), this, SLOT(notifyEditingFinished()));
284
285
286
287 // Connect other widget (not having editingFinished signal) to
288 // the same slot. From Qt 4.6 onwards the sequence of the signal
289 // delivery is definite.
290 connect(cmbFilter, SIGNAL(activated(KoID)), this, SLOT(notifyEditingFinished()));
291 connect(cmbWarpType, SIGNAL(currentIndexChanged(int)), this, SLOT(notifyEditingFinished()));
292 connect(m_rotationCenterButtons, SIGNAL(idPressed(int)), this, SLOT(notifyEditingFinished()));
293 connect(aspectButton, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(notifyEditingFinished()));
294
295 connect(lockUnlockPointsButton, SIGNAL(clicked()), this, SLOT(notifyEditingFinished()));
296 connect(resetPointsButton, SIGNAL(clicked()), this, SLOT(notifyEditingFinished()));
297
298 connect(defaultRadioButton, SIGNAL(clicked(bool)), this, SLOT(notifyEditingFinished()));
299 connect(customRadioButton, SIGNAL(clicked(bool)), this, SLOT(notifyEditingFinished()));
300
301 // Liquify
302 //
303 // liquify brush options do not affect the image directly and are not
304 // saved to undo, so we don't Q_EMIT notifyEditingFinished() for them
305
306 // Connect Apply/Reset buttons
307 connect(buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(slotButtonBoxClicked(QAbstractButton*)));
308
309 // Mesh. First set up, then connect.
310 intNumRows->setRange(1, 999);
311 intNumColumns->setRange(1, 999);
312
313 connect(chkShowControlPoints, SIGNAL(toggled(bool)), this, SLOT(slotMeshShowHandlesChanged()));
314 connect(chkSymmetricalHandles, SIGNAL(toggled(bool)), this, SLOT(slotMeshSymmetricalHandlesChanged()));
315 connect(chkScaleHandles, SIGNAL(toggled(bool)), this, SLOT(slotMeshScaleHandlesChanged()));
316 connect(intNumColumns, SIGNAL(valueChanged(int)), this, SLOT(slotMeshSizeChanged()));
317 connect(intNumRows, SIGNAL(valueChanged(int)), this, SLOT(slotMeshSizeChanged()));
318 connect(intNumColumns, SIGNAL(editingFinished()), this, SLOT(notifyEditingFinished()));
319 connect(intNumRows, SIGNAL(editingFinished()), this, SLOT(notifyEditingFinished()));
320
321 // Mode switch buttons
322 connect(freeTransformButton, SIGNAL(clicked(bool)), this, SLOT(slotSetFreeTransformModeButtonClicked(bool)));
323 connect(warpButton, SIGNAL(clicked(bool)), this, SLOT(slotSetWarpModeButtonClicked(bool)));
324 connect(cageButton, SIGNAL(clicked(bool)), this, SLOT(slotSetCageModeButtonClicked(bool)));
325 connect(perspectiveTransformButton, SIGNAL(clicked(bool)), this, SLOT(slotSetPerspectiveModeButtonClicked(bool)));
326 connect(liquifyButton, SIGNAL(clicked(bool)), this, SLOT(slotSetLiquifyModeButtonClicked(bool)));
327 connect(meshButton, SIGNAL(clicked(bool)), this, SLOT(slotSetMeshModeButtonClicked(bool)));
328
329
330 tooBigLabelWidget->hide();
331
332 connect(canvas->viewManager()->mainWindow(), SIGNAL(themeChanged()), SLOT(slotUpdateIcons()), Qt::UniqueConnection);
334
335 KGuiItem::assign(buttonBox->button(QDialogButtonBox::Apply), KStandardGuiItem::apply());
336 KGuiItem::assign(buttonBox->button(QDialogButtonBox::Reset), KStandardGuiItem::reset());
337}
338
340{
341 freeTransformButton->setIcon(KisIconUtils::loadIcon("transform_icons_main"));
342 warpButton->setIcon(KisIconUtils::loadIcon("transform_icons_warp"));
343 cageButton->setIcon(KisIconUtils::loadIcon("transform_icons_cage"));
344 perspectiveTransformButton->setIcon(KisIconUtils::loadIcon("transform_icons_perspective"));
345 liquifyButton->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_main"));
346 meshButton->setIcon(KisIconUtils::loadIcon("transform_icons_mesh"));
347
348 liquifyMove->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_move"));
349 liquifyScale->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_resize"));
350 liquifyRotate->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_rotate"));
351 liquifyOffset->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_offset"));
352 liquifyUndo->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_erase"));
353
354
355
356 middleRightButton->setIcon(KisIconUtils::loadIcon("arrow-right"));
357 topRightButton->setIcon(KisIconUtils::loadIcon("arrow-topright"));
358 middleTopButton->setIcon(KisIconUtils::loadIcon("arrow-up"));
359 topLeftButton->setIcon(KisIconUtils::loadIcon("arrow-topleft"));
360 middleLeftButton->setIcon(KisIconUtils::loadIcon("arrow-left"));
361 bottomLeftButton->setIcon(KisIconUtils::loadIcon("arrow-downleft"));
362 middleBottomButton->setIcon(KisIconUtils::loadIcon("arrow-down"));
363 bottomRightButton->setIcon(KisIconUtils::loadIcon("arrow-downright"));
364
365 btnTransformAroundPivotPoint->setIcon(KisIconUtils::loadIcon("pivot-point"));
366
367
368 // pressure icons
369 liquifySizePressureBox->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure"));
370 liquifyAmountPressureBox->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure"));
371}
372
374{
375 blockUiSlots();
376
378 KisLiquifyProperties *props =
379 config->liquifyProperties();
380
381 const bool useWashMode = props->useWashMode();
382
383 liquifySizeSlider->setValue(props->size());
384 liquifyAmountSlider->setValue(props->amount());
385 liquifyFlowSlider->setValue(props->flow());
386 buildupModeComboBox->setCurrentIndex(useWashMode);
387
388 liquifySpacingSlider->setValue(props->spacing());
389 liquifySizePressureBox->setChecked(props->sizeHasPressure());
390 liquifyAmountPressureBox->setChecked(props->amountHasPressure());
391 liquifyReverseDirectionChk->setChecked(props->reverseDirection());
392
393
395 static_cast<KisLiquifyProperties::LiquifyMode>(props->mode());
396
397 bool canInverseDirection =
399
400 bool canUseWashMode = mode != KisLiquifyProperties::UNDO;
401
402 liquifyReverseDirectionChk->setEnabled(canInverseDirection);
403 liquifyFlowSlider->setEnabled(canUseWashMode && useWashMode);
404 buildupModeComboBox->setEnabled(canUseWashMode);
405
406 const qreal maxAmount = canUseWashMode ? 5.0 : 1.0;
407 liquifyAmountSlider->setRange(0.0, maxAmount, 2);
408
410}
411
413{
414 if (m_uiSlotsBlocked) return;
415
417
418 KisLiquifyProperties *props =
419 config->liquifyProperties();
420
423
424 if (mode == props->mode()) return;
425
426 props->setMode(mode);
427 props->loadMode();
428
430
431 notifyConfigChanged(false);
432}
433
435{
436 if (m_uiSlotsBlocked) return;
437
439 KisLiquifyProperties *props =
440 config->liquifyProperties();
441
442 props->setSize(value);
443 notifyConfigChanged(false);
444}
445
447{
448 if (m_uiSlotsBlocked) return;
449
451 KisLiquifyProperties *props =
452 config->liquifyProperties();
453
454 props->setAmount(value);
455 notifyConfigChanged(false);
456}
457
459{
460 if (m_uiSlotsBlocked) return;
461
463 KisLiquifyProperties *props =
464 config->liquifyProperties();
465
466 props->setFlow(value);
467 notifyConfigChanged(false);
468}
469
471{
472 if (m_uiSlotsBlocked) return;
473
475 KisLiquifyProperties *props =
476 config->liquifyProperties();
477
478 props->setUseWashMode(value); // 0 == build up mode / 1 == wash mode
479
480 notifyConfigChanged(false);
481
482 // we need to enable/disable flow slider
484}
485
487{
488 if (m_uiSlotsBlocked) return;
489
491 KisLiquifyProperties *props =
492 config->liquifyProperties();
493
494 props->setSpacing(value);
495 notifyConfigChanged(false);
496}
497
509
521
533
535{
536 blockUiSlots();
537
538 if (config.mode() == ToolTransformArgs::FREE_TRANSFORM ||
540
541 quickTransformGroup->setEnabled(config.mode() == ToolTransformArgs::FREE_TRANSFORM);
542
543 stackedWidget->setCurrentIndex(0);
544
545 bool freeTransformIsActive = config.mode() == ToolTransformArgs::FREE_TRANSFORM;
546
547 if (freeTransformIsActive)
548 {
549 freeTransformButton->setChecked(true);
550 }
551 else
552 {
553 perspectiveTransformButton->setChecked(true);
554 }
555
556 aXBox->setEnabled(freeTransformIsActive);
557 aYBox->setEnabled(freeTransformIsActive);
558 aZBox->setEnabled(freeTransformIsActive);
559 cameraHeightBox->setEnabled(freeTransformIsActive);
560 freeRotationRadioButton->setEnabled(freeTransformIsActive);
561
562 scaleXBox->setValue(config.scaleX() * 100.);
563 scaleYBox->setValue(config.scaleY() * 100.);
564 shearXBox->setValue(config.shearX() * 100.);
565 shearYBox->setValue(config.shearY() * 100.);
566
567 const QPointF anchorPoint = config.originalCenter() + config.rotationCenterOffset();
568 const KisTransformUtils::MatricesPack m(config);
569 const QPointF anchorPointView = m.finalTransform().map(anchorPoint);
570
571 translateXBox->setValue(anchorPointView.x());
572 translateYBox->setValue(anchorPointView.y());
573
574 aXBox->setAngle(normalizeAngleDegrees(kisRadiansToDegrees(config.aX())));
575 aYBox->setAngle(normalizeAngleDegrees(kisRadiansToDegrees(config.aY())));
576 aZBox->setAngle(normalizeAngleDegrees(kisRadiansToDegrees(config.aZ())));
577 cameraHeightBox->setValue(config.cameraPos().z());
578 aspectButton->setKeepAspectRatio(config.keepAspectRatio());
579 cmbFilter->setCurrent(config.filterId());
580
582 pt.rx() /= m_transaction->originalHalfWidth();
583 pt.ry() /= m_transaction->originalHalfHeight();
584
585 for (int i = 0; i < 9; i++) {
586 if (qFuzzyCompare(m_handleDir[i].x(), pt.x()) &&
587 qFuzzyCompare(m_handleDir[i].y(), pt.y())) {
588
589 m_rotationCenterButtons->button(i)->setChecked(true);
590 break;
591 }
592 }
593
594 btnTransformAroundPivotPoint->setChecked(config.transformAroundRotationCenter());
595
596 } else if (config.mode() == ToolTransformArgs::WARP) {
597
598 stackedWidget->setCurrentIndex(1);
599 warpButton->setChecked(true);
600
601 if (config.defaultPoints()) {
602 densityBox->setValue(std::sqrt(config.numPoints()));
603 }
604
605 cmbWarpType->setCurrentIndex((int)config.warpType());
606 defaultRadioButton->setChecked(config.defaultPoints());
607 customRadioButton->setChecked(!config.defaultPoints());
608 densityBox->setEnabled(config.defaultPoints());
609 customWarpWidget->setEnabled(!config.defaultPoints());
610
612
613 } else if (config.mode() == ToolTransformArgs::CAGE) {
614
615 // default UI options
617
618 // we need at least 3 point before we can start actively deforming
619 if (config.origPoints().size() >= 3)
620 {
621 cageTransformDirections->setText(i18n("Switch between editing and deforming cage"));
622 cageAddEditRadio->setVisible(true);
623 cageDeformRadio->setVisible(true);
624
625 // update to correct radio button
626 if (config.isEditingTransformPoints())
627 cageAddEditRadio->setChecked(true);
628 else
629 cageDeformRadio->setChecked(true);
630
631 changeGranularity->setCurrentIndex(log2(config.pixelPrecision()) - 2);
632 granularityPreview->setCurrentIndex(log2(config.previewPixelPrecision()) - 2);
633
634 }
635
636 } else if (config.mode() == ToolTransformArgs::LIQUIFY) {
637
638 stackedWidget->setCurrentIndex(3);
639 liquifyButton->setChecked(true);
640
641 const KisLiquifyProperties *props =
642 config.liquifyProperties();
643
644 switch (props->mode()) {
646 liquifyMove->setChecked(true);
647 break;
649 liquifyScale->setChecked(true);
650 break;
652 liquifyRotate->setChecked(true);
653 break;
655 liquifyOffset->setChecked(true);
656 break;
658 liquifyUndo->setChecked(true);
659 break;
661 qFatal("Unsupported mode");
662 }
663
665 } else if (config.mode() == ToolTransformArgs::MESH) {
666 stackedWidget->setCurrentIndex(4);
667 intNumColumns->setValue(config.meshTransform()->size().width() - 1);
668 intNumRows->setValue(config.meshTransform()->size().height() - 1);
669 chkShowControlPoints->setChecked(config.meshShowHandles());
670 chkSymmetricalHandles->setChecked(config.meshSymmetricalHandles());
671 chkScaleHandles->setChecked(config.meshScaleHandles());
672 }
673
675}
676
678{
680
681 if (config->isEditingTransformPoints()) {
682 lockUnlockPointsButton->setText(i18n("Lock Points"));
683 } else {
684 lockUnlockPointsButton->setText(i18n("Unlock Points"));
685 }
686}
687
689{
690 QAbstractButton *applyButton = buttonBox->button(QDialogButtonBox::Apply);
691 QAbstractButton *resetButton = buttonBox->button(QDialogButtonBox::Reset);
692
693 Q_ASSERT(applyButton);
694 Q_ASSERT(resetButton);
695
696 applyButton->setDisabled(disabled);
697 resetButton->setDisabled(disabled);
698}
699
701{
702 int checkedId = m_rotationCenterButtons->checkedId();
703
704 if (checkedId >= 0 && checkedId <= 8) {
705 // uncheck the current checked button
706 m_rotationCenterButtons->button(9)->setChecked(true);
707 }
708}
709
711{
712 tooBigLabelWidget->setVisible(value);
713}
714
719
724
725void KisToolTransformConfigWidget::notifyConfigChanged(bool needsPreviewRecalculation)
726{
728 Q_EMIT sigConfigChanged(needsPreviewRecalculation);
729 }
730 m_configChanged = true;
731}
732
737
742
750
751
753{
754 // reset tool states since we are done (probably can encapsulate this later)
756 if (config->mode() == ToolTransformArgs::CAGE)
757 {
758 cageAddEditRadio->setVisible(false);
759 cageAddEditRadio->setChecked(true);
760 cageDeformRadio->setVisible(false);
761 cageTransformDirections->setText(i18n("Create 3 points on the canvas to begin"));
762
763 // ensure we are on the right options view
764 stackedWidget->setCurrentIndex(2);
765 }
766
767
768
769}
770
772{
773 QAbstractButton *applyButton = buttonBox->button(QDialogButtonBox::Apply);
774 QAbstractButton *resetButton = buttonBox->button(QDialogButtonBox::Reset);
775
777
778 if (button == applyButton) {
779 Q_EMIT sigApplyTransform();
780 }
781 else if (button == resetButton) {
782 Q_EMIT sigCancelTransform();
783 }
784
785}
786
788{
789 if (!value) return;
790
791 lblTransformType->setText(meshButton->toolTip());
792
794}
795
797{
798 if (!value) return;
799
800 lblTransformType->setText(freeTransformButton->toolTip());
801
803}
804
806{
807 if (!value) return;
808
809 lblTransformType->setText(warpButton->toolTip());
810
812}
813
815{
816 if (!value) return;
817
818 lblTransformType->setText(cageButton->toolTip());
819
821}
822
824{
825 if (!value) return;
826
827 lblTransformType->setText(liquifyButton->toolTip());
828
830}
831
833{
834 if (!value) return;
835
836 lblTransformType->setText(perspectiveTransformButton->toolTip());
837
839}
840
842{
844 config->setFilterId(filterId.id());
846}
847
849{
850 if (m_uiSlotsBlocked) return;
851
852 if (index >= 0 && index <= 8) {
854
855 double i = m_handleDir[index].x();
856 double j = m_handleDir[index].y();
857
860
862 updateConfig(*config);
863 }
864}
865
875
877{
878 if (m_uiSlotsBlocked) return;
879
881
882 {
884 config->setScaleX(value / 100.);
885 }
886
887 if (config->keepAspectRatio()) {
888
890 int calculatedValue = int( value/ m_scaleRatio );
891
892 scaleYBox->blockSignals(true);
893 scaleYBox->setValue(calculatedValue);
894 {
896 config->setScaleY(calculatedValue / 100.);
897 }
898 scaleYBox->blockSignals(false);
899
901 }
902
905}
906
908{
909 if (m_uiSlotsBlocked) return;
910
912
913 {
915 config->setScaleY(value / 100.);
916 }
917
918 if (config->keepAspectRatio()) {
920 int calculatedValue = int(m_scaleRatio * value);
921 scaleXBox->blockSignals(true);
922 scaleXBox->setValue(calculatedValue);
923 {
925 config->setScaleX(calculatedValue / 100.);
926 }
927 scaleXBox->blockSignals(false);
929 }
930
933}
934
936{
937 if (m_uiSlotsBlocked) return;
938
940
941 {
943 config->setShearX((double)value / 100.);
944 }
945
948}
949
951{
952 if (m_uiSlotsBlocked) return;
953
955
956 {
958 config->setShearY((double)value / 100.);
959 }
960
961
964}
965
967{
968 if (m_uiSlotsBlocked) return;
969
971
972 const QPointF anchorPoint = config->originalCenter() + config->rotationCenterOffset();
973 const KisTransformUtils::MatricesPack m(*config);
974
975 const QPointF anchorPointView = m.finalTransform().map(anchorPoint);
976 const QPointF newAnchorPointView(value, anchorPointView.y());
977 config->setTransformedCenter(config->transformedCenter() + newAnchorPointView - anchorPointView);
978 translateXBox->setValue(value);
980}
981
983{
984 if (m_uiSlotsBlocked) return;
985
987
988 const QPointF anchorPoint = config->originalCenter() + config->rotationCenterOffset();
989 const KisTransformUtils::MatricesPack m(*config);
990
991 const QPointF anchorPointView = m.finalTransform().map(anchorPoint);
992 const QPointF newAnchorPointView(anchorPointView.x(), value);
993 config->setTransformedCenter(config->transformedCenter() + newAnchorPointView - anchorPointView);
994 translateYBox->setValue(value);
996}
997
1010
1012{
1013 if (m_uiSlotsBlocked) return;
1014
1016
1017 {
1020 }
1021
1024}
1025
1027{
1028 if (m_uiSlotsBlocked) return;
1029
1031
1032 {
1035 }
1036
1039}
1040
1042{
1043 if (m_uiSlotsBlocked) return;
1044
1046 config->setCameraPos(QVector3D(0,0,value));
1047
1050}
1051
1053{
1055
1056 {
1058 config->setScaleX(config->scaleX() * -1);
1059 }
1060
1063}
1064
1066{
1068
1069 {
1071 config->setScaleY(config->scaleY() * -1);
1072 }
1073
1076}
1077
1079{
1081
1082 {
1084 config->setAZ(normalizeAngle(config->aZ() + M_PI_2));
1085 }
1086
1089}
1090
1092{
1094
1095 {
1097 config->setAZ(normalizeAngle(config->aZ() - M_PI_2));
1098 }
1099
1102}
1103
1104
1105// change free transform setting we want to alter (all radio buttons call this)
1107{
1108 Q_UNUSED(value);
1109
1110 //QCheckBox sender = (QCheckBox)(*)(QObject::sender());
1111 QString senderName = QObject::sender()->objectName();
1112
1113
1114 // only show setting with what we have selected
1115 rotationGroup->hide();
1116 shearGroup->hide();
1117 scaleGroup->hide();
1118 moveGroup->hide();
1119
1120
1121 if ("freeMoveRadioButton" == senderName)
1122 {
1123 moveGroup->show();
1124 }
1125 else if ("freeShearRadioButton" == senderName)
1126 {
1127 shearGroup->show();
1128 }
1129 else if ("freeScaleRadioButton" == senderName)
1130 {
1131 scaleGroup->show();
1132 }
1133 else
1134 {
1135 rotationGroup->show();
1136 }
1137
1138}
1139
1141{
1142 if (m_uiSlotsBlocked) return;
1143
1145 config->setKeepAspectRatio(value);
1146
1147 if (value) {
1149 int tmpXScaleBox = scaleXBox->value();
1150 int tmpYScaleBox = scaleYBox->value();
1151 m_scaleRatio = (tmpXScaleBox / (double) tmpYScaleBox);
1153 }
1154
1155 blockUiSlots();
1156 aspectButton->setKeepAspectRatio(value);
1158
1160}
1161
1163{
1164 if (m_uiSlotsBlocked) return;
1165
1167
1168 config->setAlpha((double)value);
1171}
1172
1178
1186
1188{
1190
1191 densityBox->setEnabled(!enabled);
1192 customWarpWidget->setEnabled(enabled);
1193
1194 if (!enabled) {
1195 config->setEditingTransformPoints(false);
1196 setDefaultWarpPoints(densityBox->value());
1197 config->setWarpCalculation(KisWarpTransformWorker::WarpCalculation::GRID);
1198 } else {
1199 config->setEditingTransformPoints(true);
1200 config->setWarpCalculation(KisWarpTransformWorker::WarpCalculation::DRAW);
1202 }
1203
1204
1205
1206
1208}
1209
1216
1223
1230
1232{
1233 if (m_uiSlotsBlocked) return;
1234
1237
1238 if (config->isEditingTransformPoints()) {
1239 // reinit the transf points to their original value
1241 int nbPoints = config->origPoints().size();
1242 for (int i = 0; i < nbPoints; ++i) {
1243 config->transfPoint(i) = config->origPoint(i);
1244 }
1245 }
1246
1249}
1250
1252{
1253 if (m_uiSlotsBlocked) return;
1254
1256
1257 switch (index) {
1262 break;
1263 default:
1265 break;
1266 }
1267
1269}
1270
1272{
1273 if ( value == 0)
1274 {
1275 slotEditCagePoints(true);
1276 }
1277 else
1278 {
1279 slotEditCagePoints(false);
1280 }
1281
1283}
1284
1286{
1287 if (m_uiSlotsBlocked) return;
1288
1290 config->refTransformedPoints() = config->origPoints();
1291
1294}
1295
1304
1313
1315{
1316 if (m_uiSlotsBlocked) return;
1317
1319 KisBezierTransformMesh &mesh = *config->meshTransform();
1320
1321 if (mesh.size().width() != intNumColumns->value() + 1) {
1322 mesh.reshapeMeshHorizontally(intNumColumns->value() + 1);
1323 }
1324
1325 if (mesh.size().height() != intNumRows->value() + 1) {
1326 mesh.reshapeMeshVertically(intNumRows->value() + 1);
1327 }
1328
1330}
1331
1333{
1334 if (m_uiSlotsBlocked) return;
1336 config->setMeshShowHandles(this->chkShowControlPoints->isChecked());
1339}
1340
1342{
1343 if (m_uiSlotsBlocked) return;
1345 config->setMeshSymmetricalHandles(this->chkSymmetricalHandles->isChecked());
1348}
1349
1351{
1352 if (m_uiSlotsBlocked) return;
1354 config->setMeshScaleHandles(this->chkScaleHandles->isChecked());
1357}
1358
1360{
1361 KConfigGroup group = KSharedConfig::openConfig()->group("KisToolTransform");
1362 switch(index) {
1363 case 0:
1364 group.writeEntry("useOverlayPreviewStyle", false);
1365 group.writeEntry("forceLodMode", false);
1366 break;
1367 case 1:
1368 group.writeEntry("useOverlayPreviewStyle", false);
1369 group.writeEntry("forceLodMode", true);
1370 break;
1371 default:
1372 group.writeEntry("useOverlayPreviewStyle", true);
1373 }
1374
1380 Q_EMIT sigUpdateGlobalConfig();
1382}
float value(const T *src, size_t ch)
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
@ IncreasingDirection_Clockwise
@ FlipOptionsMode_MenuButton
The flip options are shown as a menu accessible via a options button.
void reshapeMeshVertically(int numRows)
void reshapeMeshHorizontally(int numColumns)
KisViewManager * viewManager() const
static KisFilterStrategyRegistry * instance()
void setMode(LiquifyMode value)
void setReverseDirection(bool value)
void setSizeHasPressure(bool value)
void setAmountHasPressure(bool value)
The KisSignalMapper class bundles signals from identifiable senders.
void setMapping(QObject *sender, int id)
void sigResetTransform(ToolTransformArgs::TransformMode mode)
void slotButtonBoxClicked(QAbstractButton *button)
TransformTransactionProperties * m_transaction
void sigConfigChanged(bool needsPreviewRecalculation)
void notifyConfigChanged(bool needsPreviewRecalculation=true)
void updateConfig(const ToolTransformArgs &config)
KisToolTransformConfigWidget(TransformTransactionProperties *transaction, KisCanvas2 *canvas, QWidget *parent)
static void setDefaultWarpPoints(int pointsPerLine, const TransformTransactionProperties *transaction, ToolTransformArgs *config)
KisMainWindow * mainWindow() const
enum KisWarpTransformWorker::WarpType_ WarpType
Definition KoID.h:30
QString id() const
Definition KoID.cpp:63
void setCameraPos(const QVector3D &pos)
void setShearX(double shearX)
QPointF transformedCenter() const
bool meshScaleHandles() const
void setFilterId(const QString &id)
void setWarpCalculation(KisWarpTransformWorker::WarpCalculation warpCalc)
const QVector< QPointF > & origPoints() const
void setKeepAspectRatio(bool value)
QPointF & origPoint(int i)
QVector< QPointF > & refTransformedPoints()
void setTransformAroundRotationCenter(bool value)
bool transformAroundRotationCenter() const
void setRotationCenterOffset(QPointF rotationCenterOffset)
int previewPixelPrecision() const
bool isEditingTransformPoints() const
void setAlpha(double alpha)
void setScaleX(double scaleX)
KisWarpTransformWorker::WarpType warpType() const
void setMeshShowHandles(bool value)
void setTransformedCenter(QPointF transformedCenter)
void setPreviewPixelPrecision(int precision)
const KisLiquifyProperties * liquifyProperties() const
void setScaleY(double scaleY)
void setMeshScaleHandles(bool meshScaleHandles)
bool meshSymmetricalHandles() const
QPointF originalCenter() const
void setMeshSymmetricalHandles(bool meshSymmetricalHandles)
QPointF rotationCenterOffset() const
void setShearY(double shearY)
const KisBezierTransformMesh * meshTransform() const
void setEditingTransformPoints(bool value)
QPointF & transfPoint(int i)
void setPixelPrecision(int precision)
QString filterId() const
void setWarpType(KisWarpTransformWorker::WarpType warpType)
TransformMode mode() const
QVector3D cameraPos() const
static bool qFuzzyCompare(half p1, half p2)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
T kisRadiansToDegrees(T radians)
Definition kis_global.h:181
std::enable_if< std::is_floating_point< T >::value, T >::type normalizeAngleDegrees(T a)
Definition kis_global.h:132
T kisDegreesToRadians(T degrees)
Definition kis_global.h:176
std::enable_if< std::is_floating_point< T >::value, T >::type normalizeAngle(T a)
Definition kis_global.h:121
QString button(const QWheelEvent &ev)
QIcon loadIcon(const QString &name)
void setText(QSpinBox *spinBox, const QStringView textTemplate)