File indexing completed on 2024-12-22 04:16:23

0001 /*
0002  *  SPDX-FileCopyrightText: 2022 Agata Cacko <cacko.azh@gmail.com>
0003  *  SPDX-FileCopyrightText: 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
0004  * 
0005  *
0006  *  SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 #include "KisSprayOpOptionWidget.h"
0009 
0010 #include <QButtonGroup>
0011 #include <QMetaProperty>
0012 
0013 #include <lager/constant.hpp>
0014 #include "ui_wdgsprayoptions.h"
0015 
0016 #include "KisSprayOpOptionModel.h"
0017 #include <KisSpinBoxI18nHelper.h>
0018 #include "kis_curve_widget.h"
0019 #include <KisCurveWidgetConnectionHelper.h>
0020 
0021 namespace {
0022 
0023 class KisSprayOptionsWidget: public QWidget, public Ui::WdgSprayOptions
0024 {
0025 public:
0026     KisSprayOptionsWidget(QWidget *parent = 0)
0027         : QWidget(parent) {
0028         setupUi(this);
0029         
0030         diameterSpinBox->setRange(1, 1000, 0);
0031         diameterSpinBox->setValue(100);
0032         diameterSpinBox->setExponentRatio(1.5);
0033         diameterSpinBox->setSuffix(i18n(" px"));
0034 
0035         aspectSPBox->setRange(0.0, 2.0, 2);
0036         aspectSPBox->setSingleStep(0.01);
0037         aspectSPBox->setValue(1.0);
0038 
0039         rotationAngleSelector->setDecimals(0);
0040         rotationAngleSelector->setIncreasingDirection(KisAngleGauge::IncreasingDirection_Clockwise);
0041 
0042         scaleSpin->setRange(0.0, 10.0, 2);
0043         scaleSpin->setSingleStep(0.01);
0044         scaleSpin->setValue(1.0);
0045 
0046         spacingSpin->setRange(0.0, 5.0, 2);
0047         spacingSpin->setSingleStep(0.01);
0048         spacingSpin->setValue(0.5);
0049 
0050         jitterMovementSpin->setRange(0.0,5.0, 1);
0051         jitterMovementSpin->setSingleStep(0.1);
0052         jitterMovementSpin->setValue(1.0);
0053 
0054         particlesSpinBox->setRange(1.0, 1000.0, 0);
0055         particlesSpinBox->setValue(12);
0056         particlesSpinBox->setExponentRatio(3.0);
0057 
0058         coverageSpin->setRange(0.001, 0.02, 3);
0059         coverageSpin->setSingleStep(0.001);
0060         coverageSpin->setValue(0.003);
0061         KisSpinBoxI18nHelper::setText(coverageSpin, i18nc("{n} is the number value, % is the percent sign", "{n}%"));
0062         coverageSpin->setVisible(false);
0063         
0064         angularDistCombo->setToolTip(i18n("Select how the particles are distributed as a function of the angle to the center of the spray area."));
0065 
0066         curveAngularDistWidget->setToolTip(i18n(
0067             "Set a custom distribution of the particles."
0068             "\nThe horizontal axis represents the angle from 0 to 360 degrees."
0069             "\nThe vertical axis represents how probable it is for a particle ending at that angle."
0070             "\nThe higher the curve at a given angle, the more particles will end at that angle."
0071         ));
0072         curveAngularDistSpin->setPrefix(i18n("Repeat: "));
0073         curveAngularDistSpin->setSuffix(i18nc("Times symbol, like in 10x", "x"));
0074         curveAngularDistSpin->setRange(1, 10);
0075         curveAngularDistSpin->setToolTip(i18n(
0076             "Set how many times should the curve repeat from 0 degrees to 360 degrees."
0077         ));
0078 
0079         radialDistCombo->setToolTip(i18n("Select how the particles are distributed as a function of the distance from the center of the spray area."));
0080 
0081 
0082         centerBiasedPolarDistanceBox->setToolTip(i18n("Activates the old behavior where the particles are more accumulated towards the center of the spray area."));
0083 
0084         stdDeviationRadialDistSpin->setPrefix(i18n("Standard deviation: "));
0085         stdDeviationRadialDistSpin->setRange(0.01, 1.0, 2);
0086         stdDeviationRadialDistSpin->setSingleStep(0.01);
0087         stdDeviationRadialDistSpin->setToolTip(i18n(
0088             "Set the standard deviation for the gaussian distribution."
0089             "\nLower values will make the particles concentrate towards the center of the spray area."
0090         ));
0091 
0092         clusterRadialDistSpin->setPrefix(i18n("Clustering amount: "));
0093         clusterRadialDistSpin->setRange(-100.0, 100.0, 2);
0094         clusterRadialDistSpin->setSoftRange(-10.0, 10.0);
0095         clusterRadialDistSpin->setToolTip(i18n(
0096             "Set how the particles should spread in the spray area."
0097             "\nPositive values will make the particles concentrate towards the center of the spray area."
0098             "\nNegative values will make the particles concentrate towards the border of the spray area."
0099             "\nValues near 0 will make the particles spread more uniformly."
0100         ));
0101 
0102         curveRadialDistWidget->setToolTip(i18n(
0103             "Set a custom distribution of the particles."
0104             "\nThe horizontal axis represents the distance from the center to the border of the spray area."
0105             "\nThe vertical axis represents how probable it is for a particle ending at that distance."
0106             "\nThe higher the curve at a given distance, the more particles will end at that distance."
0107         ));
0108         curveRadialDistSpin->setPrefix(i18n("Repeat: "));
0109         curveRadialDistSpin->setSuffix(i18nc("Times symbol, like in 10x", "x"));
0110         curveRadialDistSpin->setRange(1, 10);
0111         curveRadialDistSpin->setToolTip(i18n(
0112             "Set how many times should the curve repeat from the center to the border of the spray area."
0113         ));
0114 
0115         layoutAngularDist->takeAt(1);
0116         
0117         curveAngularDistContainer->setVisible(false);
0118         
0119         layoutRadialDist->takeAt(1);
0120         layoutRadialDist->takeAt(1);
0121         layoutRadialDist->takeAt(1);
0122         
0123         stdDeviationRadialDistSpin->setVisible(false);
0124         clusterRadialDistSpin->setVisible(false);
0125         curveRadialDistContainer->setVisible(false);
0126     }
0127 };
0128 }
0129 
0130 struct KisSprayOpOptionWidget::Private
0131 {
0132     Private(lager::cursor<KisSprayOpOptionData> optionData)
0133         : model(optionData)
0134     {
0135     }
0136 
0137     KisSprayOpOptionModel model;
0138 };
0139 
0140 namespace {
0141     void slotSetupAngularDistributionWidget(KisSprayOptionsWidget *widget, int index)
0142     {
0143         if (index == 0 && widget->layoutAngularDist->count() == 3) {
0144             widget->layoutAngularDist->takeAt(1);
0145             widget->curveAngularDistContainer->setVisible(false);
0146         } else if (index == 1 && widget->layoutAngularDist->count() == 2) {
0147             widget->layoutAngularDist->insertWidget(1, widget->curveAngularDistContainer, 0);
0148             widget->curveAngularDistContainer->setVisible(true);
0149         }
0150     }
0151     
0152     void slotSetupRadialDistributionWidget(KisSprayOptionsWidget *widget, int index)
0153     {
0154         while (widget->layoutRadialDist->count() > 2) {
0155             widget->layoutRadialDist->takeAt(1)->widget()->setVisible(false);
0156         }
0157         if (index == 0) {
0158             widget->layoutRadialDist->insertWidget(1, widget->centerBiasedPolarDistanceBox, 0);
0159             widget->centerBiasedPolarDistanceBox->setVisible(true);
0160         } else if (index == 1) {
0161             widget->layoutRadialDist->insertWidget(1, widget->centerBiasedPolarDistanceBox, 0);
0162             widget->layoutRadialDist->insertWidget(1, widget->stdDeviationRadialDistSpin, 0);
0163             widget->centerBiasedPolarDistanceBox->setVisible(true);
0164             widget->stdDeviationRadialDistSpin->setVisible(true);
0165         } else if (index == 2) {
0166             widget->layoutRadialDist->insertWidget(1, widget->clusterRadialDistSpin, 0);
0167             widget->clusterRadialDistSpin->setVisible(true);
0168         } else if (index == 3) {
0169             widget->layoutRadialDist->insertWidget(1, widget->curveRadialDistContainer, 0);
0170             widget->curveRadialDistContainer->setVisible(true);
0171         }
0172     }
0173 }
0174 
0175 
0176 KisSprayOpOptionWidget::KisSprayOpOptionWidget(lager::cursor<KisSprayOpOptionData> optionData)
0177     : KisPaintOpOption(i18n("Spray Area"), KisPaintOpOption::GENERAL, true)
0178     , m_d(new Private(optionData))
0179 {
0180     
0181     KisSprayOptionsWidget *widget = new KisSprayOptionsWidget();
0182     setObjectName("KisSprayOpOption");
0183     
0184     m_checkable = false;
0185     
0186     using namespace KisWidgetConnectionUtils;
0187     
0188     connectControl(widget->diameterSpinBox, &m_d->model, "diameter");
0189     connectControl(widget->aspectSPBox, &m_d->model, "aspect");
0190     connectControl(widget->rotationAngleSelector, &m_d->model, "brushRotation");
0191     connectControl(widget->scaleSpin, &m_d->model, "scale");
0192     connectControl(widget->spacingSpin, &m_d->model, "spacing");
0193     connectControl(widget->jitterMoveBox, &m_d->model, "jitterMovement");
0194     connectControl(widget->jitterMovementSpin, &m_d->model, "jitterAmount");
0195     
0196     connectControl(widget->densityRadioButton, &m_d->model, "useDensity");
0197     
0198     
0199     connectControl(widget->particlesSpinBox, &m_d->model, "particleCount");
0200     connectControl(widget->coverageSpin, &m_d->model, "coverage");
0201     
0202     
0203     connectControl(widget->angularDistCombo, &m_d->model, "angularDistributionType");
0204     
0205     connectControl(widget->curveAngularDistWidget, &m_d->model, "angularDistributionCurve");
0206     connectControl(widget->curveAngularDistSpin, &m_d->model, "angularDistributionCurveRepeat");
0207     
0208     // enable widgets for the angular distribution
0209     slotSetupAngularDistributionWidget(widget, m_d->model.angularDistributionType() == KisSprayOpOptionData::ParticleDistribution_Uniform ? 0 : 1);
0210     connect(&m_d->model, &KisSprayOpOptionModel::angularDistributionTypeChanged, widget, [this, widget] (int index) {
0211         slotSetupAngularDistributionWidget(widget, index);
0212     });
0213     
0214     
0215     connectControl(widget->radialDistCombo, &m_d->model, "radialDistributionType");
0216     connectControl(widget->stdDeviationRadialDistSpin, &m_d->model, "radialDistributionStdDeviation");
0217     connectControl(widget->clusterRadialDistSpin, &m_d->model, "radialDistributionClusteringAmount");
0218     
0219     connectControl(widget->curveRadialDistWidget, &m_d->model, "radialDistributionCurve");
0220     
0221     // enable widgets for radial distribution
0222     slotSetupRadialDistributionWidget(widget, (int)m_d->model.radialDistributionType());
0223     connect(&m_d->model, &KisSprayOpOptionModel::radialDistributionTypeChanged, widget, [this, widget] (int index) {
0224         slotSetupRadialDistributionWidget(widget, index);
0225     });
0226     
0227     
0228     connectControl(widget->curveRadialDistSpin, &m_d->model, "radialDistributionCurveRepeat");
0229     connectControl(widget->centerBiasedPolarDistanceBox, &m_d->model, "radialDistributionCenterBiased");
0230     
0231     widget->jitterMovementSpin->setEnabled(m_d->model.jitterMovement());
0232     connect(&m_d->model, &KisSprayOpOptionModel::jitterMovementChanged, widget->jitterMovementSpin, &KisDoubleSliderSpinBox::setEnabled);
0233     
0234     connectWidgetVisibleToProperty(widget->particlesSpinBox, &m_d->model, "isNumParticlesVisible");
0235     connectWidgetVisibleToProperty(widget->coverageSpin, &m_d->model, "useDensity");
0236     
0237     m_d->model.optionData.bind(std::bind(&KisSprayOpOptionWidget::emitSettingChanged, this));
0238     
0239     setConfigurationPage(widget);
0240 }
0241 
0242 KisSprayOpOptionWidget::~KisSprayOpOptionWidget()
0243 {
0244 }
0245 
0246 void KisSprayOpOptionWidget::writeOptionSetting(KisPropertiesConfigurationSP setting) const
0247 {
0248     KisSprayOpOptionData data = *m_d->model.optionData;
0249     data.write(setting.data());
0250 }
0251 
0252 void KisSprayOpOptionWidget::readOptionSetting(const KisPropertiesConfigurationSP setting)
0253 {
0254     KisSprayOpOptionData data = *m_d->model.optionData;
0255     data.read(setting.data());
0256     m_d->model.optionData.set(data);
0257 }
0258 
0259 lager::cursor<qreal> KisSprayOpOptionWidget::scale() const
0260 {
0261     return m_d->model.LAGER_QT(scale);
0262 }
0263 
0264 lager::cursor<int> KisSprayOpOptionWidget::diameter() const
0265 {
0266     return m_d->model.LAGER_QT(diameter);
0267 }