Krita Source Code Documentation
Loading...
Searching...
No Matches
RectangleShape.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 SPDX-FileCopyrightText: 2006-2008 Thorsten Zachmann <zachmann@kde.org>
3 SPDX-FileCopyrightText: 2006-2008 Jan Hambrecht <jaham@gmx.net>
4 SPDX-FileCopyrightText: 2009 Thomas Zander <zander@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "RectangleShape.h"
10
11#include <KoParameterShape_p.h>
12#include <KoPathPoint.h>
14#include <KoXmlWriter.h>
15#include <KoXmlNS.h>
16#include <KoUnit.h>
17#include <SvgSavingContext.h>
18#include <SvgLoadingContext.h>
19#include <SvgUtil.h>
20#include <SvgStyleWriter.h>
21
24 , m_cornerRadiusX(0)
25 , m_cornerRadiusY(0)
26{
28 handles.push_back(QPointF(100, 0));
29 handles.push_back(QPointF(100, 0));
31 QSizeF size(100, 100);
33}
34
36 : KoParameterShape(rhs),
37 m_cornerRadiusX(rhs.m_cornerRadiusX),
38 m_cornerRadiusY(rhs.m_cornerRadiusY)
39{
40}
41
45
47{
48 return new RectangleShape(*this);
49}
50
51void RectangleShape::moveHandleAction(int handleId, const QPointF &point, Qt::KeyboardModifiers modifiers)
52{
53 Q_UNUSED(modifiers);
54 QPointF p(point);
55
56 qreal width2 = size().width() / 2.0;
57 qreal height2 = size().height() / 2.0;
58 switch (handleId) {
59 case 0:
60 if (p.x() < width2) {
61 p.setX(width2);
62 } else if (p.x() > size().width()) {
63 p.setX(size().width());
64 }
65 p.setY(0);
66 m_cornerRadiusX = (size().width() - p.x()) / width2 * 100.0;
67 if (!(modifiers & Qt::ControlModifier)) {
68 m_cornerRadiusY = (size().width() - p.x()) / height2 * 100.0;
69 }
70 break;
71 case 1:
72 if (p.y() < 0) {
73 p.setY(0);
74 } else if (p.y() > height2) {
75 p.setY(height2);
76 }
77 p.setX(size().width());
78 m_cornerRadiusY = p.y() / height2 * 100.0;
79 if (!(modifiers & Qt::ControlModifier)) {
80 m_cornerRadiusX = p.y() / width2 * 100.0;
81 }
82 break;
83 }
84 // this is needed otherwise undo/redo might not end in the same result
85 if (100 - m_cornerRadiusX < 1e-10) {
86 m_cornerRadiusX = 100;
87 }
88 if (100 - m_cornerRadiusY < 1e-10) {
89 m_cornerRadiusY = 100;
90 }
91
93}
94
96{
98 handles.append(QPointF(size().width() - m_cornerRadiusX / 100.0 * 0.5 * size().width(), 0.0));
99 handles.append(QPointF(size().width(), m_cornerRadiusY / 100.0 * 0.5 * size().height()));
101}
102
103void RectangleShape::updatePath(const QSizeF &size)
104{
105 qreal rx = 0;
106 qreal ry = 0;
107 if (m_cornerRadiusX > 0 && m_cornerRadiusY > 0) {
108 rx = size.width() / 200.0 * m_cornerRadiusX;
109 ry = size.height() / 200.0 * m_cornerRadiusY;
110 }
111
112 qreal x2 = size.width() - rx;
113 qreal y2 = size.height() - ry;
114
115 QPointF curvePoints[12];
116
117 int requiredCurvePointCount = 4;
118 if (rx && m_cornerRadiusX < 100) {
119 requiredCurvePointCount += 2;
120 }
121 if (ry && m_cornerRadiusY < 100) {
122 requiredCurvePointCount += 2;
123 }
124
125 createPoints(requiredCurvePointCount);
126
127 KoSubpath &points = *subpaths()[0];
128
129 int cp = 0;
130
131 // first path starts and closes path
132 points[cp]->setProperty(KoPathPoint::StartSubpath);
133 points[cp]->setProperty(KoPathPoint::CloseSubpath);
134 points[cp]->setPoint(QPointF(rx, 0));
135 points[cp]->removeControlPoint1();
136 points[cp]->removeControlPoint2();
137
138 if (m_cornerRadiusX < 100 || m_cornerRadiusY == 0) {
139 // end point of the top edge
140 points[++cp]->setPoint(QPointF(x2, 0));
141 points[cp]->removeControlPoint1();
142 points[cp]->removeControlPoint2();
143 }
144
145 if (rx) {
146 // the top right radius
147 arcToCurve(rx, ry, 90, -90, points[cp]->point(), curvePoints);
148 points[cp]->setControlPoint2(curvePoints[0]);
149 points[++cp]->setControlPoint1(curvePoints[1]);
150 points[cp]->setPoint(curvePoints[2]);
151 points[cp]->removeControlPoint2();
152 }
153
154 if (m_cornerRadiusY < 100 || m_cornerRadiusX == 0) {
155 // the right edge
156 points[++cp]->setPoint(QPointF(size.width(), y2));
157 points[cp]->removeControlPoint1();
158 points[cp]->removeControlPoint2();
159 }
160
161 if (rx) {
162 // the bottom right radius
163 arcToCurve(rx, ry, 0, -90, points[cp]->point(), curvePoints);
164 points[cp]->setControlPoint2(curvePoints[0]);
165 points[++cp]->setControlPoint1(curvePoints[1]);
166 points[cp]->setPoint(curvePoints[2]);
167 points[cp]->removeControlPoint2();
168 }
169
170 if (m_cornerRadiusX < 100 || m_cornerRadiusY == 0) {
171 // the bottom edge
172 points[++cp]->setPoint(QPointF(rx, size.height()));
173 points[cp]->removeControlPoint1();
174 points[cp]->removeControlPoint2();
175 }
176
177 if (rx) {
178 // the bottom left radius
179 arcToCurve(rx, ry, 270, -90, points[cp]->point(), curvePoints);
180 points[cp]->setControlPoint2(curvePoints[0]);
181 points[++cp]->setControlPoint1(curvePoints[1]);
182 points[cp]->setPoint(curvePoints[2]);
183 points[cp]->removeControlPoint2();
184 }
185
186 if ((m_cornerRadiusY < 100 || m_cornerRadiusX == 0) && ry) {
187 // the right edge
188 points[++cp]->setPoint(QPointF(0, ry));
189 points[cp]->removeControlPoint1();
190 points[cp]->removeControlPoint2();
191 }
192
193 if (rx) {
194 // the top left radius
195 arcToCurve(rx, ry, 180, -90, points[cp]->point(), curvePoints);
196 points[cp]->setControlPoint2(curvePoints[0]);
197 points[0]->setControlPoint1(curvePoints[1]);
198 points[0]->setPoint(curvePoints[2]);
199 }
200
201 // unset all stop/close path properties
202 for (int i = 1; i < cp; ++i) {
203 points[i]->unsetProperty(KoPathPoint::StopSubpath);
204 points[i]->unsetProperty(KoPathPoint::CloseSubpath);
205 }
206
207 // last point stops and closes path
208 points.last()->setProperty(KoPathPoint::StopSubpath);
209 points.last()->setProperty(KoPathPoint::CloseSubpath);
210
212}
213
214void RectangleShape::createPoints(int requiredPointCount)
215{
216 if (subpaths().count() != 1) {
217 clear();
218 subpaths().append(new KoSubpath());
219 }
220 int currentPointCount = subpaths()[0]->count();
221 if (currentPointCount > requiredPointCount) {
222 for (int i = 0; i < currentPointCount - requiredPointCount; ++i) {
223 delete subpaths()[0]->front();
224 subpaths()[0]->pop_front();
225 }
226 } else if (requiredPointCount > currentPointCount) {
227 for (int i = 0; i < requiredPointCount - currentPointCount; ++i) {
228 subpaths()[0]->append(new KoPathPoint(this, QPointF()));
229 }
230 }
231
233}
234
236{
237 return m_cornerRadiusX;
238}
239
241{
242 radius = qBound(0.0, radius, 100.0);
243 m_cornerRadiusX = radius;
244 updatePath(size());
246}
247
249{
250 return m_cornerRadiusY;
251}
252
254{
255 radius = qBound(0.0, radius, 100.0);
256 m_cornerRadiusY = radius;
257 updatePath(size());
259}
260
262{
263 return RectangleShapeId;
264}
265
267{
268 // let basic path saiving code handle our saving
269 if (!isParametricShape()) return false;
270
271 context.shapeWriter().startElement("rect");
272 context.shapeWriter().addAttribute("id", context.getID(this));
274
275 SvgStyleWriter::saveSvgStyle(this, context);
276 SvgStyleWriter::saveMetadata(this, context);
277
278 const QSizeF size = this->size();
279 context.shapeWriter().addAttribute("width", size.width());
280 context.shapeWriter().addAttribute("height", size.height());
281
282 double rx = cornerRadiusX();
283 if (rx > 0.0) {
284 context.shapeWriter().addAttribute("rx", 0.01 * rx * 0.5 * size.width());
285 }
286 double ry = cornerRadiusY();
287 if (ry > 0.0) {
288 context.shapeWriter().addAttribute("ry", 0.01 * ry * 0.5 * size.height());
289 }
290
291 context.shapeWriter().endElement();
292
293 return true;
294}
295
296bool RectangleShape::loadSvg(const QDomElement &element, SvgLoadingContext &context)
297{
298 const qreal x = SvgUtil::parseUnitX(context.currentGC(), context.resolvedProperties(), element.attribute("x"));
299 const qreal y = SvgUtil::parseUnitY(context.currentGC(), context.resolvedProperties(), element.attribute("y"));
300 const qreal w = SvgUtil::parseUnitX(context.currentGC(), context.resolvedProperties(), element.attribute("width"));
301 const qreal h = SvgUtil::parseUnitY(context.currentGC(), context.resolvedProperties(), element.attribute("height"));
302 const QString rxStr = element.attribute("rx");
303 const QString ryStr = element.attribute("ry");
304 qreal rx = rxStr.isEmpty() ? 0.0 : SvgUtil::parseUnitX(context.currentGC(), context.resolvedProperties(), rxStr);
305 qreal ry = ryStr.isEmpty() ? 0.0 : SvgUtil::parseUnitY(context.currentGC(), context.resolvedProperties(), ryStr);
306 // if one radius is given but not the other, use the same value for both
307 if (!rxStr.isEmpty() && ryStr.isEmpty()) {
308 ry = rx;
309 }
310 if (rxStr.isEmpty() && !ryStr.isEmpty()) {
311 rx = ry;
312 }
313
314 setSize(QSizeF(w, h));
315 setPosition(QPointF(x, y));
316 if (rx >= 0.0) {
317 setCornerRadiusX(qMin(qreal(100.0), qreal(rx / (0.5 * w) * 100.0)));
318 }
319 if (ry >= 0.0) {
320 setCornerRadiusY(qMin(qreal(100.0), qreal(ry / (0.5 * h) * 100.0)));
321 }
322 if (w == 0.0 || h == 0.0) {
323 setVisible(false);
324 }
325
326 return true;
327}
const Params2D p
QList< KoPathPoint * > KoSubpath
a KoSubpath contains a path from a moveTo until a close or a new moveTo
Definition KoPathShape.h:31
#define RectangleShapeId
QList< QPointF > handles
the handles that the user can grab and change
void setSize(const QSizeF &size) override
reimplemented from KoShape
void setHandles(const QList< QPointF > &handles)
bool isParametricShape() const
Check if object is a parametric shape.
A KoPathPoint represents a point in a path.
@ StartSubpath
it starts a new subpath by a moveTo command
Definition KoPathPoint.h:38
@ CloseSubpath
it closes a subpath (only applicable on StartSubpath and StopSubpath)
Definition KoPathPoint.h:40
@ StopSubpath
it stops a subpath (last point of subpath)
Definition KoPathPoint.h:39
const KoSubpathList & subpaths() const
QSizeF size() const override
reimplemented
void notifyPointsChanged()
int arcToCurve(qreal rx, qreal ry, qreal startAngle, qreal sweepAngle, const QPointF &offset, QPointF *curvePoints) const
Add an arc.
void clear()
Removes all subpaths and their points from the path.
QTransform transformation() const
Returns the shapes local transformation matrix.
Definition KoShape.cpp:424
virtual void setPosition(const QPointF &position)
Set the position of the shape in pt.
Definition KoShape.cpp:295
void setVisible(bool on)
Definition KoShape.cpp:972
qreal cornerRadiusX() const
Returns the corner radius in x-direction.
void moveHandleAction(int handleId, const QPointF &point, Qt::KeyboardModifiers modifiers=Qt::NoModifier) override
Updates the internal state of a KoParameterShape.
QString pathShapeId() const override
reimplemented
~RectangleShape() override
void setCornerRadiusY(qreal radius)
void setCornerRadiusX(qreal radius)
qreal m_cornerRadiusX
in percent of half of the rectangle width (a number between 0 and 100)
qreal cornerRadiusY() const
Returns the corner radius in y-direction.
KoShape * cloneShape() const override
creates a deep copy of the shape or shape's subtree
void createPoints(int requiredPointCount)
void updatePath(const QSizeF &size) override
Update the path of the parameter shape.
bool loadSvg(const QDomElement &element, SvgLoadingContext &context) override
reimplemented from SvgShape
bool saveSvg(SvgSavingContext &context) override
reimplemented from SvgShape
qreal m_cornerRadiusY
in percent of half of the rectangle height (a number between 0 and 100)
Contains data used for loading svg.
SvgGraphicsContext * currentGC() const
Returns the current graphics context.
KoSvgTextProperties resolvedProperties() const
These are the text properties, completely resolved, ensuring that everything is inherited and the siz...
Context for saving svg files.
QScopedPointer< KoXmlWriter > shapeWriter
QString getID(const KoShape *obj)
Returns the unique id for the given shape.
static void saveSvgStyle(KoShape *shape, SvgSavingContext &context)
Saves the style of the specified shape.
static void saveMetadata(const KoShape *shape, SvgSavingContext &context)
static qreal parseUnitX(SvgGraphicsContext *gc, const KoSvgTextProperties &resolved, const QString &unit)
parses a length attribute in x-direction
Definition SvgUtil.cpp:304
static void writeTransformAttributeLazy(const QString &name, const QTransform &transform, KoXmlWriter &shapeWriter)
Writes a transform as an attribute name iff the transform is not empty.
Definition SvgUtil.cpp:124
static qreal parseUnitY(SvgGraphicsContext *gc, const KoSvgTextProperties &resolved, const QString &unit)
parses a length attribute in y-direction
Definition SvgUtil.cpp:313