-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathshape.cpp
More file actions
166 lines (134 loc) · 4.53 KB
/
Copy pathshape.cpp
File metadata and controls
166 lines (134 loc) · 4.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#include "shape.h"
#include <QJsonValue>
#include <QPainterPathStroker>
#include <QUuid>
#include <algorithm>
namespace {
qreal clampScale(qreal value) {
return std::clamp(value, 0.1, 20.0);
}
} // namespace
bool operator==(const ShapeStyle& lhs, const ShapeStyle& rhs) {
return lhs.strokeColor == rhs.strokeColor
&& lhs.fillColor == rhs.fillColor
&& lhs.penWidth == rhs.penWidth;
}
bool ShapeSeed::isValid() const {
return !geometry.isEmpty();
}
void Shape::regenerateId() {
id = createShapeId();
}
void Shape::bindDefinition(const IShapeDefinition* shapeDefinition) {
definition = shapeDefinition;
}
QString Shape::displayName() const {
return definition ? definition->displayName() : typeId;
}
bool Shape::supportsFill() const {
return definition && definition->supportsFill();
}
bool Shape::isValid() const {
return definition && definition->isGeometryValid(geometry);
}
QPainterPath Shape::localPath() const {
return definition ? definition->localPath(geometry) : QPainterPath();
}
QRectF Shape::localGeometryBounds() const {
return localPath().boundingRect();
}
QJsonArray pointToJson(const QPointF& point) {
return QJsonArray{point.x(), point.y()};
}
QPointF pointFromJson(const QJsonValue& value) {
const QJsonArray array = value.toArray();
if (array.size() != 2) {
return {};
}
return QPointF(array[0].toDouble(), array[1].toDouble());
}
QJsonArray pointsToJson(const QVector<QPointF>& points) {
QJsonArray array;
for (const QPointF& point : points) {
array.append(pointToJson(point));
}
return array;
}
QVector<QPointF> pointsFromJson(const QJsonValue& value) {
QVector<QPointF> points;
const QJsonArray array = value.toArray();
points.reserve(array.size());
for (const QJsonValue& pointValue : array) {
points.append(pointFromJson(pointValue));
}
return points;
}
QJsonArray rectToJson(const QRectF& rect) {
return QJsonArray{rect.x(), rect.y(), rect.width(), rect.height()};
}
QRectF rectFromJson(const QJsonValue& value) {
const QJsonArray array = value.toArray();
if (array.size() != 4) {
return {};
}
return QRectF(array[0].toDouble(), array[1].toDouble(), array[2].toDouble(), array[3].toDouble());
}
QJsonObject styleToJson(const ShapeStyle& style) {
QJsonObject object;
object["stroke"] = style.strokeColor.name(QColor::HexArgb);
object["fill"] = style.fillColor.name(QColor::HexArgb);
object["penWidth"] = style.penWidth;
return object;
}
bool styleFromJson(const QJsonObject& object, ShapeStyle* outStyle) {
if (!outStyle || !object.contains("stroke") || !object.contains("fill") || !object.contains("penWidth")) {
return false;
}
const QColor stroke(object["stroke"].toString());
const QColor fill(object["fill"].toString());
if (!stroke.isValid() || !fill.isValid()) {
return false;
}
outStyle->strokeColor = stroke;
outStyle->fillColor = fill;
outStyle->penWidth = std::clamp(object["penWidth"].toInt(2), 1, 20);
return true;
}
QTransform shapeTransform(const Shape& shape) {
QTransform transform;
transform.translate(shape.position.x(), shape.position.y());
transform.rotate(shape.rotation);
transform.scale(clampScale(shape.scaleX), clampScale(shape.scaleY));
return transform;
}
QPainterPath shapePath(const Shape& shape) {
return shapeTransform(shape).map(shape.localPath());
}
QPainterPath shapeStrokePath(const Shape& shape, qreal extraPadding) {
QPainterPathStroker stroker;
stroker.setCapStyle(Qt::RoundCap);
stroker.setJoinStyle(Qt::RoundJoin);
stroker.setWidth(std::max<qreal>(shape.style.penWidth + extraPadding, 8.0));
return stroker.createStroke(shapePath(shape));
}
QRectF shapeBounds(const Shape& shape) {
return shapePath(shape).boundingRect();
}
bool shapeContains(const Shape& shape, const QPointF& scenePoint) {
const QPainterPath transformed = shapePath(shape);
if (shape.supportsFill() && transformed.contains(scenePoint)) {
return true;
}
return shapeStrokePath(shape).contains(scenePoint);
}
QSizeF effectiveShapeSize(const Shape& shape) {
const QRectF bounds = shape.localGeometryBounds();
return QSizeF(std::max<qreal>(1.0, bounds.width() * clampScale(shape.scaleX)),
std::max<qreal>(1.0, bounds.height() * clampScale(shape.scaleY)));
}
QString createShapeId() {
return QUuid::createUuid().toString(QUuid::WithoutBraces);
}
QString createLayerId() {
return QUuid::createUuid().toString(QUuid::WithoutBraces);
}