Krita Source Code Documentation
Loading...
Searching...
No Matches
MyPaintSensorPack.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2022 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#include "MyPaintSensorPack.h"
8
9#include <KisCppQuirks.h>
10#include "KisSensorData.h"
11#include "kis_assert.h"
12
15
17#include <libmypaint/mypaint-brush-settings-gen.h>
18#include <libmypaint/mypaint-brush.h>
19#include <kis_algebra_2d.h>
21#include <QJsonDocument>
22#include <QJsonObject>
24
25namespace detail {
26
28 auto addFactory = [](const KoID &id,
29 int minimumValue,
30 int maximumValue,
31 const QString &minimumLabel,
32 const QString &maximumLabel,
33 const QString &valueSuffix) {
35 minimumValue,
36 maximumValue,
37 minimumLabel,
38 maximumLabel,
39 valueSuffix));
40 };
41
42 addFactory(MyPaintPressureId, 0, 20, "", "", "");
43 addFactory(MyPaintFineSpeedId, -20, 20, "", "", "");
44 addFactory(MyPaintGrossSpeedId, -20, 20, "", "", "");
45 addFactory(MyPaintRandomId, 0, 1, "", "", "");
46 addFactory(MyPaintStrokeId, 0, 1, "", "", "");
47 addFactory(MyPaintDirectionId, 0, 180, "", "", "");
48 addFactory(MyPaintDeclinationId, 0, 90, "", "", i18n("%"));
49 addFactory(MyPaintAscensionId, -180, 180, "", "", i18n("%"));
50 addFactory(MyPaintCustomId, -20, 20, "", "", i18n("%"));
51}
52
53template <typename Data,
54 typename SensorData =
57inline std::vector<SensorData*> sensors(Data *data)
58{
59 std::vector<SensorData*> result;
60
61 result.reserve(9);
62
63 result.push_back(&data->sensorPressure);
64 result.push_back(&data->sensorFineSpeed);
65 result.push_back(&data->sensorGrossSpeed);
66 result.push_back(&data->sensorRandom);
67 result.push_back(&data->sensorStroke);
68 result.push_back(&data->sensorDirection);
69 result.push_back(&data->sensorDeclination);
70 result.push_back(&data->sensorAscension);
71 result.push_back(&data->sensorCustom);
72
73 return result;
74}
75
76} // namespace detail
77
79 : KisSensorData(id)
80{
81 QList<QPointF> points;
82
83 if (id == MyPaintPressureId) {
84 points = {{0,0}, {1,1}};
85 } else if (id == MyPaintFineSpeedId) {
86 points = {{0,0}, {4,1}};
87 } else if (id == MyPaintGrossSpeedId) {
88 points = {{0,0}, {4,1}};
89 } else if (id == MyPaintRandomId) {
90 points = {{0,0}, {1,1}};
91 } else if (id == MyPaintStrokeId) {
92 points = {{0,0}, {1,1}};
93 } else if (id == MyPaintDirectionId) {
94 points = {{0,0}, {180,1}};
95 } else if (id == MyPaintDeclinationId) {
96 points = {{0,0}, {90,1}};
97 } else if (id == MyPaintAscensionId) {
98 points = {{-180,0}, {180,1}};
99 } else if (id == MyPaintCustomId) {
100 points = {{-10,0}, {10,1}};
101 } else {
102 qWarning() << "MyPaintSensorDataWithRange: unknown sensor type:" << id;
103 points = {{0,0}, {1,1}};
104 }
105
106 curve = KisCubicCurve(points).toString();
108 reshapeCurve();
109}
110
112{
113 return curveRange;
114}
115
120
125
130
132 : sensorPressure(MyPaintPressureId),
133 sensorFineSpeed(MyPaintFineSpeedId),
134 sensorGrossSpeed(MyPaintGrossSpeedId),
135 sensorRandom(MyPaintRandomId),
136 sensorStroke(MyPaintStrokeId),
137 sensorDirection(MyPaintDirectionId),
138 sensorDeclination(MyPaintDeclinationId),
139 sensorAscension(MyPaintAscensionId),
140 sensorCustom(MyPaintCustomId)
141{
143}
144
146{
147 return new MyPaintSensorPack(*this);
148}
149
150std::vector<const KisSensorData *> MyPaintSensorPack::constSensors() const
151{
152 return detail::sensors(&m_data);
153}
154
155std::vector<KisSensorData *> MyPaintSensorPack::sensors()
156{
157 return detail::sensors(&m_data);
158}
159
164
169
171{
172 const MyPaintSensorPack *pack = dynamic_cast<const MyPaintSensorPack*>(rhs);
174
175 return m_data == pack->m_data;
176}
177
178MyPaintBrushInput sensorIdToMyPaintBrushInput(const KoID &id)
179{
180 MyPaintBrushInput result = MYPAINT_BRUSH_INPUT_PRESSURE;
181
182 if (id == MyPaintPressureId) {
183 result = MYPAINT_BRUSH_INPUT_PRESSURE;
184 } else if (id == MyPaintFineSpeedId) {
185 result = MYPAINT_BRUSH_INPUT_SPEED1;
186 } else if (id == MyPaintGrossSpeedId) {
187 result = MYPAINT_BRUSH_INPUT_SPEED2;
188 } else if (id == MyPaintRandomId) {
189 result = MYPAINT_BRUSH_INPUT_RANDOM;
190 } else if (id == MyPaintStrokeId) {
191 result = MYPAINT_BRUSH_INPUT_STROKE;
192 } else if (id == MyPaintDirectionId) {
193 result = MYPAINT_BRUSH_INPUT_DIRECTION;
194 } else if (id == MyPaintDeclinationId) {
195 result = MYPAINT_BRUSH_INPUT_TILT_DECLINATION;
196 } else if (id == MyPaintAscensionId) {
197 result = MYPAINT_BRUSH_INPUT_TILT_ASCENSION;
198 } else if (id == MyPaintCustomId) {
199 result = MYPAINT_BRUSH_INPUT_CUSTOM;
200 } else {
201 qWarning() << "sensorIdToMyPaintBrushInput: unknown sensor id";
202 }
203
204 return result;
205}
206
208{
209 QString result = "pressure";
210
211 if (id == MyPaintPressureId) {
212 result = "pressure";
213 } else if (id == MyPaintFineSpeedId) {
214 result = "speed1";
215 } else if (id == MyPaintGrossSpeedId) {
216 result = "speed2";
217 } else if (id == MyPaintRandomId) {
218 result = "random";
219 } else if (id == MyPaintStrokeId) {
220 result = "stroke";
221 } else if (id == MyPaintDirectionId) {
222 result = "direction";
223 } else if (id == MyPaintDeclinationId) {
224 result = "tilt_declination";
225 } else if (id == MyPaintAscensionId) {
226 result = "tilt_ascension";
227 } else if (id == MyPaintCustomId) {
228 result = "custom";
229 } else {
230 qWarning() << "sensorIdToMyPaintBrushInputJsonKey: unknown sensor id";
231 }
232
233 return result;
234}
235
236MyPaintBrushSetting optionIdToMyPaintBrushSettings(const KoID &id) {
237 if (id.id() == "eraser")
238 return MYPAINT_BRUSH_SETTING_ERASER;
239 else if (id.id() == "opaque")
240 return MYPAINT_BRUSH_SETTING_OPAQUE;
241 else if (id.id() == "smudge")
242 return MYPAINT_BRUSH_SETTING_SMUDGE;
243 else if (id.id() == "color_h")
244 return MYPAINT_BRUSH_SETTING_COLOR_H;
245 else if (id.id() == "color_s")
246 return MYPAINT_BRUSH_SETTING_COLOR_S;
247 else if (id.id() == "color_v")
248 return MYPAINT_BRUSH_SETTING_COLOR_V;
249 else if (id.id() == "colorize")
250 return MYPAINT_BRUSH_SETTING_COLORIZE;
251 else if (id.id() == "hardness")
252 return MYPAINT_BRUSH_SETTING_HARDNESS;
253 else if (id.id() == "speed1_gamma")
254 return MYPAINT_BRUSH_SETTING_SPEED1_GAMMA;
255 else if (id.id() == "speed2_gamma")
256 return MYPAINT_BRUSH_SETTING_SPEED2_GAMMA;
257 else if (id.id() == "anti_aliasing")
258 return MYPAINT_BRUSH_SETTING_ANTI_ALIASING;
259 else if (id.id() == "restore_color")
260 return MYPAINT_BRUSH_SETTING_RESTORE_COLOR;
261 else if (id.id() == "slow_tracking")
262 return MYPAINT_BRUSH_SETTING_SLOW_TRACKING;
263 else if (id.id() == "smudge_length")
264 return MYPAINT_BRUSH_SETTING_SMUDGE_LENGTH;
265 else if (id.id() == "snap_to_pixel")
266 return MYPAINT_BRUSH_SETTING_SNAP_TO_PIXEL;
267 else if (id.id() == "change_color_h")
268 return MYPAINT_BRUSH_SETTING_CHANGE_COLOR_H;
269 else if (id.id() == "change_color_l")
270 return MYPAINT_BRUSH_SETTING_CHANGE_COLOR_L;
271 else if (id.id() == "change_color_v")
272 return MYPAINT_BRUSH_SETTING_CHANGE_COLOR_V;
273 else if (id.id() == "tracking_noise")
274 return MYPAINT_BRUSH_SETTING_TRACKING_NOISE;
275 else if (id.id() == "dabs_per_second")
276 return MYPAINT_BRUSH_SETTING_DABS_PER_SECOND;
277 else if (id.id() == "offset_by_speed")
278 return MYPAINT_BRUSH_SETTING_OFFSET_BY_SPEED;
279 else if (id.id() == "opaque_multiply")
280 return MYPAINT_BRUSH_SETTING_OPAQUE_MULTIPLY;
281 else if (id.id() == "speed1_slowness")
282 return MYPAINT_BRUSH_SETTING_SPEED1_SLOWNESS;
283 else if (id.id() == "speed2_slowness")
284 return MYPAINT_BRUSH_SETTING_SPEED2_SLOWNESS;
285 else if (id.id() == "stroke_holdtime")
286 return MYPAINT_BRUSH_SETTING_STROKE_HOLDTIME;
287 else if (id.id() == "direction_filter")
288 return MYPAINT_BRUSH_SETTING_DIRECTION_FILTER;
289 else if (id.id() == "offset_by_random")
290 return MYPAINT_BRUSH_SETTING_OFFSET_BY_RANDOM;
291 else if (id.id() == "opaque_linearize")
292 return MYPAINT_BRUSH_SETTING_OPAQUE_LINEARIZE;
293 else if (id.id() == "radius_by_random")
294 return MYPAINT_BRUSH_SETTING_RADIUS_BY_RANDOM;
295 else if (id.id() == "stroke_threshold")
296 return MYPAINT_BRUSH_SETTING_STROKE_THRESHOLD;
297 else if (id.id() == "pressure_gain_log")
298 return MYPAINT_BRUSH_SETTING_PRESSURE_GAIN_LOG;
299 else if (id.id() == "smudge_radius_log")
300 return MYPAINT_BRUSH_SETTING_SMUDGE_RADIUS_LOG;
301 else if (id.id() == "change_color_hsl_s")
302 return MYPAINT_BRUSH_SETTING_CHANGE_COLOR_HSL_S;
303 else if (id.id() == "change_color_hsv_s")
304 return MYPAINT_BRUSH_SETTING_CHANGE_COLOR_HSV_S;
305 else if (id.id() == "radius_logarithmic")
306 return MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC;
307 else if (id.id() == "elliptical_dab_angle")
308 return MYPAINT_BRUSH_SETTING_ELLIPTICAL_DAB_ANGLE;
309 else if (id.id() == "elliptical_dab_ratio")
310 return MYPAINT_BRUSH_SETTING_ELLIPTICAL_DAB_RATIO;
311 else if (id.id() == "custom_input_slowness")
312 return MYPAINT_BRUSH_SETTING_CUSTOM_INPUT_SLOWNESS;
313 else if (id.id() == "custom_input")
314 return MYPAINT_BRUSH_SETTING_CUSTOM_INPUT;
315 else if (id.id() == "dabs_per_basic_radius")
316 return MYPAINT_BRUSH_SETTING_DABS_PER_BASIC_RADIUS;
317 else if (id.id() == "slow_tracking_per_dab")
318 return MYPAINT_BRUSH_SETTING_SLOW_TRACKING_PER_DAB;
319 else if (id.id() == "dabs_per_actual_radius")
320 return MYPAINT_BRUSH_SETTING_DABS_PER_ACTUAL_RADIUS;
321 else if (id.id() == "offset_by_speed_slowness")
322 return MYPAINT_BRUSH_SETTING_OFFSET_BY_SPEED_SLOWNESS;
323 else if (id.id() == "stroke_duration_logarithmic")
324 return MYPAINT_BRUSH_SETTING_STROKE_DURATION_LOGARITHMIC;
325 else if (id.id() == "smudge_length_log")
326 return MYPAINT_BRUSH_SETTING_SMUDGE_LENGTH_LOG;
327 else if (id.id() == "smudge_bucket")
328 return MYPAINT_BRUSH_SETTING_SMUDGE_BUCKET;
329 else if (id.id() == "smudge_transparency")
330 return MYPAINT_BRUSH_SETTING_SMUDGE_TRANSPARENCY;
331 else if (id.id() == "posterize")
332 return MYPAINT_BRUSH_SETTING_POSTERIZE;
333 else if (id.id() == "posterize_num")
334 return MYPAINT_BRUSH_SETTING_POSTERIZE_NUM;
335
336 return MYPAINT_BRUSH_SETTING_ERASER;
337}
338
340{
341 data.isChecked = !data.isCheckable || setting->getBool("Pressure" + data.id.id(), false);
342
343 std::vector<KisSensorData *> sensors = data.sensors();
344
345 // TODO: refactor into library resource
346 std::unique_ptr<MyPaintBrush, decltype(&mypaint_brush_unref)>
347 brush(mypaint_brush_new(), &mypaint_brush_unref);
348
349 const MyPaintBrushSetting brushSetting = optionIdToMyPaintBrushSettings(data.id);
350 mypaint_brush_from_string(brush.get(), setting->getProperty(MYPAINT_JSON).toByteArray());
351
352 for (auto it = sensors.begin(); it != sensors.end(); ++it) {
353 const MyPaintBrushInput input = sensorIdToMyPaintBrushInput((*it)->id);
354 const int numPoints = mypaint_brush_get_mapping_n(brush.get(), brushSetting, input);
355
356 QList<QPointF> points;
357 for(int i = 0; i < numPoints; i++) {
358 float x, y;
359 mypaint_brush_get_mapping_point(brush.get(), brushSetting, input, i, &x, &y);
360 points << QPointF(qreal(x), qreal(y));
361 }
362
363 if (points.size() > 1) {
364 (*it)->isActive = true;
365 (*it)->curve = KisCubicCurve(points).toString();
366 (*it)->setBaseCurveRange(KisAlgebra2D::accumulateBounds(points));
367 dynamic_cast<MyPaintSensorDataWithRange*>(*it)->reshapeCurve();
368 } else {
369 (*it)->reset();
370 }
371 }
372
373 // At least one sensor needs to be active
374 if (std::find_if(sensors.begin(), sensors.end(),
375 std::mem_fn(&KisSensorData::isActive)) == sensors.end()) {
376
377 sensors.front()->isActive = true;
378 data.useCurve = false;
379 } else {
380 data.useCurve = true;
381 }
382
383 data.strengthValue = qreal(mypaint_brush_get_base_value(brush.get(), brushSetting));
384
385 data.useSameCurve = false; // not used in mypaint engine
386 data.commonCurve = DEFAULT_CURVE_STRING; // not used in mypaint engine
387 data.curveMode = 0; // not used in mypaint engine
388
389 return true;
390}
391
393{
394 setting->setProperty("Pressure" + data.id.id(), data.isChecked || !data.isCheckable);
395
396 QVector<const KisSensorData*> activeSensors;
397 Q_FOREACH(const KisSensorData *sensor, data.sensors()) {
398 if (sensor->isActive) {
399 activeSensors.append(sensor);
400 }
401 }
402
403 QJsonDocument doc = QJsonDocument::fromJson(setting->getProperty(MYPAINT_JSON).toByteArray());
404
405 QJsonObject brush_json = doc.object();
406 QVariantMap map = brush_json.toVariantMap();
407 QVariantMap settings_map = map["settings"].toMap();
408 QVariantMap name_map = settings_map[data.id.id()].toMap();
409 QVariantMap inputs_map = name_map["inputs"].toMap();
410
411 const std::vector<const KisSensorData*> sensors = data.sensors();
412
413 for (auto it = sensors.begin(); it != sensors.end(); ++it) {
414 const KisSensorData *sensor = *it;
415 const QString sensorJsonId = sensorIdToMyPaintBrushInputJsonKey(sensor->id);
416
417 if (!sensor->isActive || !data.useCurve) {
418 inputs_map.remove(sensorJsonId);
419 } else {
420 KisCubicCurve curve(sensor->curve);
421 const QList<KisCubicCurvePoint> &points = curve.curvePoints();
422
423 QVariantList pointsList;
424
425 Q_FOREACH(const KisCubicCurvePoint &pt, points) {
426 pointsList.push_back(QVariantList{pt.x(), pt.y()});
427 }
428 inputs_map[sensorJsonId] = pointsList;
429 }
430 }
431
432 name_map["inputs"] = inputs_map;
433 name_map["base_value"] = data.strengthValue;
434
435 settings_map[data.id.id()] = name_map;
436 map["settings"] = settings_map;
437
438 doc = QJsonDocument(QJsonObject::fromVariantMap(map));
439 setting->setProperty(MYPAINT_JSON, doc.toJson());
440
441 // not used in mypaint engine
442 setting->setProperty(data.id.id() + "UseSameCurve", false);
443 setting->setProperty(data.id.id() + "commonCurve", DEFAULT_CURVE_STRING);
444 setting->setProperty(data.id.id() + "curveMode", 0);
445}
MyPaintBrushSetting optionIdToMyPaintBrushSettings(const KoID &id)
MyPaintBrushInput sensorIdToMyPaintBrushInput(const KoID &id)
QString sensorIdToMyPaintBrushInputJsonKey(const KoID &id)
const KoID MyPaintDeclinationId("mypaint_tilt_declination", ki18nc("Pen tilt declination", "Declination"))
const KoID MyPaintCustomId("mypaint_custom", ki18n("Custom"))
const KoID MyPaintDirectionId("mypaint_direction", ki18nc("Drawing Angle", "Direction"))
const KoID MyPaintAscensionId("mypaint_tilt_ascension", ki18nc("Pen tilt ascension", "Ascension"))
const KoID MyPaintPressureId("mypaint_pressure", ki18n("Pressure"))
const QString MYPAINT_JSON
const KoID MyPaintStrokeId("mypaint_stroke", ki18nc("The duration of a brush stroke", "Stroke"))
const KoID MyPaintRandomId("mypaint_random", ki18n("Random"))
const KoID MyPaintGrossSpeedId("mypaint_speed2", ki18n("Gross Speed"))
const KoID MyPaintFineSpeedId("mypaint_speed1", ki18n("Fine Speed"))
static KisDynamicSensorFactoryRegistry * instance()
Definition KoID.h:30
QString id() const
Definition KoID.cpp:63
static std::tuple< QString, QRectF > reshapeCurve(std::tuple< QString, QRectF > curve)
std::vector< KisSensorData * > sensors() override
void write(const KisCurveOptionDataCommon &data, KisPropertiesConfiguration *setting) const override
bool compare(const KisSensorPackInterface *rhs) const override
const MyPaintSensorData & constSensorsStruct() const
KisSensorPackInterface * clone() const override
MyPaintSensorData m_data
MyPaintSensorPack()=default
std::vector< const KisSensorData * > constSensors() const override
MyPaintSensorData & sensorsStruct()
bool read(KisCurveOptionDataCommon &data, const KisPropertiesConfiguration *setting) const override
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
const QString DEFAULT_CURVE_STRING
void accumulateBounds(const Point &pt, Rect *bounds)
addFactory(MyPaintPressureId, 0, 20, "", "", "")
std::vector< SensorData * > sensors(Data *data)
typename copy_const< Src, Dst >::type copy_const_t
QString toString() const
const QList< KisCubicCurvePoint > & curvePoints() const
std::vector< const KisSensorData * > sensors() const
virtual void setProperty(const QString &name, const QVariant &value)
bool getBool(const QString &name, bool def=false) const
virtual bool getProperty(const QString &name, QVariant &value) const
MyPaintSensorDataWithRange(const KoID &id)
void setBaseCurveRange(const QRectF &rect) override
QRectF baseCurveRange() const override
MyPaintSensorDataWithRange sensorPressure