File indexing completed on 2025-02-02 04:22:12

0001 /*
0002  * SPDX-FileCopyrightText: 2008 Boudewijn Rempt <boud@valdyas.org>
0003  * SPDX-FileCopyrightText: 2011 Silvio Heinrich <plassy@web.de>
0004  *  SPDX-FileCopyrightText: 2022 Dmitry Kazakov <dimula73@gmail.com>
0005  *
0006  *  SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 #include "KisCurveOption.h"
0009 #include "KisCurveOptionData.h"
0010 #include "kis_algebra_2d.h"
0011 
0012 #include <sensors/KisDynamicSensors.h>
0013 #include <sensors/KisDynamicSensorDrawingAngle.h>
0014 #include <sensors/KisDynamicSensorDistance.h>
0015 #include <sensors/KisDynamicSensorFade.h>
0016 #include <sensors/KisDynamicSensorTime.h>
0017 #include <sensors/KisDynamicSensorFuzzy.h>
0018 
0019 namespace {
0020 template <typename Sensor, typename Data, typename... Args>
0021 void addSensor(std::vector<std::unique_ptr<KisDynamicSensor>> &sensors,
0022                const Data &data, std::optional<KisCubicCurve> commonCurve, Args... args)
0023 {
0024     if (data.isActive) {
0025         sensors.push_back(std::unique_ptr<KisDynamicSensor>(new Sensor(data, commonCurve, args...)));
0026     }
0027 }
0028 
0029 std::vector<std::unique_ptr<KisDynamicSensor>> generateSensors(const KisCurveOptionData &data)
0030 {
0031     std::vector<std::unique_ptr<KisDynamicSensor>> result;
0032 
0033     std::optional<KisCubicCurve> commonCurve;
0034     if (data.useSameCurve) {
0035         commonCurve = KisCubicCurve(data.commonCurve);
0036     }
0037 
0038     const KisKritaSensorData &sensorStruct = data.sensorStruct();
0039 
0040     addSensor<KisDynamicSensorPressure>(result, sensorStruct.sensorPressure, commonCurve);
0041     addSensor<KisDynamicSensorPressureIn>(result, sensorStruct.sensorPressureIn, commonCurve);
0042     addSensor<KisDynamicSensorTangentialPressure>(result, sensorStruct.sensorTangentialPressure, commonCurve);
0043     addSensor<KisDynamicSensorDrawingAngle>(result, sensorStruct.sensorDrawingAngle, commonCurve);
0044     addSensor<KisDynamicSensorXTilt>(result, sensorStruct.sensorXTilt, commonCurve);
0045     addSensor<KisDynamicSensorYTilt>(result, sensorStruct.sensorYTilt, commonCurve);
0046     addSensor<KisDynamicSensorTiltDirection>(result, sensorStruct.sensorTiltDirection, commonCurve);
0047     addSensor<KisDynamicSensorTiltElevation>(result, sensorStruct.sensorTiltElevation, commonCurve);
0048     addSensor<KisDynamicSensorRotation>(result, sensorStruct.sensorRotation, commonCurve);
0049     addSensor<KisDynamicSensorFuzzyPerDab>(result, sensorStruct.sensorFuzzyPerDab, commonCurve);
0050     addSensor<KisDynamicSensorFuzzyPerStroke>(result, sensorStruct.sensorFuzzyPerStroke, commonCurve, data.id.id());
0051     addSensor<KisDynamicSensorSpeed>(result, sensorStruct.sensorSpeed, commonCurve);
0052     addSensor<KisDynamicSensorFade>(result, sensorStruct.sensorFade, commonCurve);
0053     addSensor<KisDynamicSensorDistance>(result, sensorStruct.sensorDistance, commonCurve);
0054     addSensor<KisDynamicSensorTime>(result, sensorStruct.sensorTime, commonCurve);
0055     addSensor<KisDynamicSensorPerspective>(result, sensorStruct.sensorPerspective, commonCurve);
0056 
0057     return result;
0058 }
0059 }
0060 
0061 qreal KisCurveOption::ValueComponents::rotationLikeValue(qreal normalizedBaseAngle, bool absoluteAxesFlipped, qreal scalingPartCoeff, bool disableScalingPart) const {
0062     const qreal offset =
0063             !hasAbsoluteOffset ? normalizedBaseAngle :
0064                                  absoluteAxesFlipped ? 0.5 - absoluteOffset :
0065                                                        absoluteOffset;
0066 
0067     const qreal realScalingPart = hasScaling && !disableScalingPart ? KisDynamicSensor::scalingToAdditive(scaling) : 0.0;
0068     const qreal realAdditivePart = hasAdditive ? additive : 0;
0069 
0070     qreal value = KisAlgebra2D::wrapValue(2 * offset + constant * (scalingPartCoeff * realScalingPart + realAdditivePart), -1.0, 1.0);
0071     if (qIsNaN(value)) {
0072         qWarning() << "rotationLikeValue returns NaN!" << normalizedBaseAngle << absoluteAxesFlipped;
0073         value = 0;
0074     }
0075     return value;
0076 }
0077 
0078 qreal KisCurveOption::ValueComponents::sizeLikeValue() const {
0079     const qreal offset =
0080             hasAbsoluteOffset ? absoluteOffset : 1.0;
0081 
0082     const qreal realScalingPart = hasScaling ? scaling : 1.0;
0083     const qreal realAdditivePart = hasAdditive ? KisDynamicSensor::additiveToScaling(additive) : 1.0;
0084 
0085     return qBound(minSizeLikeValue,
0086                   constant * offset * realScalingPart * realAdditivePart,
0087                   maxSizeLikeValue);
0088 }
0089 
0090 
0091 KisCurveOption::KisCurveOption(const KisCurveOptionData &data)
0092     : m_isChecked(data.isChecked)
0093     , m_useCurve(data.useCurve)
0094     , m_curveMode(data.curveMode)
0095     , m_strengthValue(data.strengthValue)
0096     , m_strengthMinValue(data.strengthMinValue)
0097     , m_strengthMaxValue(data.strengthMaxValue)
0098     , m_sensors(generateSensors(data))
0099 {
0100 }
0101 
0102 KisCurveOption::ValueComponents KisCurveOption::computeValueComponents(const KisPaintInformation& info, bool useStrengthValue) const
0103 {
0104     ValueComponents components;
0105 
0106     if (m_useCurve) {
0107         QList<double> sensorValues;
0108         for (auto i = m_sensors.cbegin(); i != m_sensors.cend(); ++i) {
0109             KisDynamicSensor *s(i->get());
0110 
0111             qreal valueFromCurve = s->parameter(info);
0112             if (s->isAdditive()) {
0113                 components.additive += valueFromCurve;
0114                 components.hasAdditive = true;
0115             } else if (s->isAbsoluteRotation()) {
0116                 components.absoluteOffset = valueFromCurve;
0117                 components.hasAbsoluteOffset =true;
0118             } else {
0119                 sensorValues << valueFromCurve;
0120                 components.hasScaling = true;
0121             }
0122         }
0123 
0124         if (sensorValues.count() == 1) {
0125             components.scaling = sensorValues.first();
0126         } else {
0127 
0128             if (m_curveMode == 1){           // add
0129                 components.scaling = 0;
0130                 double i;
0131                 foreach (i, sensorValues) {
0132                     components.scaling += i;
0133                 }
0134             } else if (m_curveMode == 2){    //max
0135                 components.scaling = *std::max_element(sensorValues.begin(), sensorValues.end());
0136 
0137             } else if (m_curveMode == 3){    //min
0138                 components.scaling = *std::min_element(sensorValues.begin(), sensorValues.end());
0139 
0140             } else if (m_curveMode == 4){    //difference
0141                 double max = *std::max_element(sensorValues.begin(), sensorValues.end());
0142                 double min = *std::min_element(sensorValues.begin(), sensorValues.end());
0143                 components.scaling = max-min;
0144 
0145             } else {                         //multuply - default
0146                 double i;
0147                 foreach (i, sensorValues) {
0148                     components.scaling *= i;
0149                 }
0150             }
0151         }
0152 
0153     }
0154 
0155     if (useStrengthValue) {
0156         components.constant = m_strengthValue;
0157     }
0158 
0159     components.minSizeLikeValue = m_strengthMinValue;
0160     components.maxSizeLikeValue = m_strengthMaxValue;
0161 
0162     return components;
0163 }
0164 
0165 qreal KisCurveOption::computeSizeLikeValue(const KisPaintInformation& info, bool useStrengthValue) const
0166 {
0167     const ValueComponents components = computeValueComponents(info, useStrengthValue);
0168     return components.sizeLikeValue();
0169 }
0170 
0171 qreal KisCurveOption::computeRotationLikeValue(const KisPaintInformation& info, qreal baseValue, bool absoluteAxesFlipped, qreal scalingPartCoeff, bool disableScalingPart) const
0172 {
0173     const ValueComponents components = computeValueComponents(info, true);
0174     return components.rotationLikeValue(baseValue, absoluteAxesFlipped, scalingPartCoeff, disableScalingPart);
0175 }
0176 
0177 qreal KisCurveOption::strengthValue() const
0178 {
0179     return m_strengthValue;
0180 }
0181 
0182 qreal KisCurveOption::strengthMinValue() const
0183 {
0184     return m_strengthMinValue;
0185 }
0186 
0187 qreal KisCurveOption::strengthMaxValue() const
0188 {
0189     return m_strengthMaxValue;
0190 }
0191 
0192 bool KisCurveOption::isChecked() const
0193 {
0194     return m_isChecked;
0195 }
0196 
0197 bool KisCurveOption::isRandom() const
0198 {
0199     for (auto it = m_sensors.begin(); it != m_sensors.end(); ++it) {
0200         const KisDynamicSensor *sensor = it->get();
0201         if (sensor->id() == FuzzyPerDabId || sensor->id() == FuzzyPerStrokeId) return true;
0202     }
0203     return false;
0204 }