1 // This may look like C code, but it's really -*- C++ -*- 2 /* 3 * Copyright (C) 2008 Emweb bv, Herent, Belgium. 4 * 5 * See the LICENSE file for terms of use. 6 */ 7 #ifndef WPAINTERPATH_H_ 8 #define WPAINTERPATH_H_ 9 10 #include <Wt/WJavaScriptExposableObject.h> 11 #include <Wt/WPointF.h> 12 #include <Wt/WRectF.h> 13 #include <Wt/WTransform.h> 14 15 #include <vector> 16 17 namespace Wt { 18 19 /*! The segment type 20 */ 21 enum SegmentType { 22 /*! \brief moveTo segment */ 23 MoveTo = 0, 24 25 /*! \brief lineTo segment */ 26 LineTo = 1, 27 28 /*! \brief first control point of cubic bezier curve. 29 * 30 * Always followed by a CubicC2 and CubicEnd segment 31 */ 32 CubicC1 = 2, 33 34 /*! \brief second control point of cubic bezier curve 35 * 36 * Always followed by a CubicEnd segment 37 */ 38 CubicC2 = 3, 39 40 /*! \brief end point of cubic bezier curve 41 */ 42 CubicEnd = 4, 43 44 /*! \brief control point of quadratic bezier curve 45 */ 46 QuadC = 5, 47 48 /*! \brief end point of quadratic bezier curve 49 */ 50 QuadEnd = 6, 51 52 /*! \brief center of an arc 53 * 54 * Always followed by an ArcR and ArcAngleSweep segment 55 */ 56 ArcC = 7, 57 58 /*! \brief radius of an arc 59 * Always followed by an ArcAngleSweep segment 60 */ 61 ArcR = 8, 62 63 /*! \brief the sweep of an arc 64 * 65 * x = startAngle, y = spanAngle 66 */ 67 ArcAngleSweep = 9 68 }; 69 70 /*! \class WPainterPath Wt/WPainterPath.h Wt/WPainterPath.h 71 * \brief A path defining a shape. 72 * 73 * A painter path represents a (complex) path that may be composed of 74 * lines, arcs and bezier curve segments, and painted onto a paint device 75 * using WPainter::drawPath(). 76 * 77 * The path that is composed in a painter path may consist of multiple 78 * closed sub-paths. Only the last sub-path can be left open. 79 * 80 * To compose a path, this class maintains a current position, which 81 * is the starting point for the next drawing operation. An operation 82 * may draw a line (see lineTo()), arc (see arcTo()), or bezier curve 83 * (see quadTo() and cubicTo()) from the current position to a new 84 * position. A new sub path may be started by moving the current 85 * position to a new location (see moveTo()), which automatically 86 * closes the previous sub path. 87 * 88 * When sub paths overlap, the result is undefined (it is dependent on 89 * the underlying painting device). 90 * 91 * Usage example: 92 * \if cpp 93 * \code 94 * Wt::WPainter painter(...); 95 * 96 * Wt::WPainterPath path(Wt::WPointF(10, 10)); 97 * path.lineTo(10, 20); 98 * path.lineTo(30, 20); 99 * path.closeSubPath(); 100 * 101 * painter.setPen(Wt::StandardColor::Red); 102 * painter.setBrush(Wt::StandardColor::Blue); 103 * painter.drawPath(path); 104 * \endcode 105 * \elseif java 106 * \code 107 * WPainter painter = new WPainter(); 108 * 109 * WPainterPath path = new WPainterPath(new WPointF(10, 10)); 110 * path.lineTo(10, 20); 111 * path.lineTo(30, 20); 112 * path.closeSubPath(); 113 * 114 * painter.setPen(new WPen(WColor.red)); 115 * painter.setBrush(new WBrush(WColor.blue)); 116 * painter.drawPath(path); 117 * \endcode 118 * \endif 119 * 120 * <h3>JavaScript exposability</h3> 121 * 122 * A %WPainterPath is JavaScript exposable. If a %WPainterPath \link isJavaScriptBound() 123 * is JavaScript bound\endlink, it can be accessed in your custom JavaScript 124 * code through \link WJavaScriptHandle::jsRef() its handle's jsRef()\endlink. 125 * 126 * A %WPainterPath is represented in JavaScript as an array of segments, where each segment 127 * is defined by a three element array: [x,y,type], where type is the integer representation 128 * of the type of a segment. 129 * 130 * For example, a 10 by 10 square with the top left at (10,10) is represented as: 131 * \code 132 * [ 133 * [10,10,0], // move to (10,10) 134 * [20,10,1], // line to (20,10) 135 * [20,20,1], // line to (20,20) 136 * [10,20,1], // line to (10,20) 137 * [10,10,1] // line to (10,10) 138 * ] 139 * \endcode 140 * 141 * \warning A %WPainterPath that is JavaScript exposed should be modified only through its \link WJavaScriptHandle handle\endlink. 142 * Any attempt at modifying it will cause an exception to be thrown. 143 * 144 * \sa WPainter::drawPath(), WPaintedWidget::createJSPainterPath() 145 * 146 * \ingroup painting 147 */ 148 class WT_API WPainterPath : public WJavaScriptExposableObject 149 { 150 public: 151 /*! \brief Default constructor. 152 * 153 * Creates an empty path, and sets the current position to (0, 0). 154 */ 155 WPainterPath(); 156 157 /*! \brief Construct a new path, and set the initial position. 158 * 159 * Creates an empty path, and sets the current position to 160 * \p startPoint. 161 */ 162 WPainterPath(const WPointF& startPoint); 163 164 /*! \brief Copy constructor. 165 */ 166 WPainterPath(const WPainterPath& path); 167 168 /*! \brief Assignment operator. 169 */ 170 WPainterPath& operator= (const WPainterPath& path); 171 172 #ifdef WT_TARGET_JAVA 173 WPainterPath clone() const; 174 #endif 175 176 /*! \brief Returns the current position. 177 * 178 * Returns the current position, which is the end point of the last 179 * move or draw operation, and which well be the start point of the 180 * next draw operation. 181 */ 182 WPointF currentPosition() const; 183 184 /*! \brief Returns whether the path is empty. 185 * 186 * Returns \c true if the path contains no drawing operations. Note that 187 * move operations are not considered drawing operations. 188 */ 189 bool isEmpty() const; 190 191 /*! \brief Comparison operator. 192 * 193 * Returns \c true if the paths are exactly the same. 194 */ 195 bool operator== (const WPainterPath& path) const; 196 197 /*! \brief Comparison operator. 198 * 199 * Returns \c true if the paths are different. 200 */ 201 bool operator!= (const WPainterPath& path) const; 202 203 /*! \brief Closes the last sub path. 204 * 205 * Draws a line from the current position to the start position of 206 * the last sub path (which is the end point of the last move 207 * operation), and sets the current position to (0, 0). 208 * 209 * \throws WException if the path \link isJavaScriptBound() is JavaScript bound\endlink 210 */ 211 void closeSubPath(); 212 213 /*! \brief Moves the current position to a new location. 214 * 215 * Moves the current position to a new point, implicitly closing the last 216 * sub path, unless \link openSubPathsEnabled() open subpaths are enabled\endlink. 217 * 218 * \throws WException if the path \link isJavaScriptBound() is JavaScript bound\endlink 219 * 220 * \sa closeSubPath(), moveTo(double, double), setOpenSubPathsEnabled(bool) 221 */ 222 void moveTo(const WPointF& point); 223 224 /*! \brief Moves the current position to a new location. 225 * 226 * Moves the current position to a new point, implicitly closing the last 227 * sub path, unless \link openSubPathsEnabled() open subpaths are enabled\endlink. 228 * 229 * \throws WException if the path \link isJavaScriptBound() is JavaScript bound\endlink 230 * 231 * \sa closeSubPath(), moveTo(const WPointF&), setOpenSubPathsEnabled(bool) 232 */ 233 void moveTo(double x, double y); 234 235 /*! \brief Draws a straight line. 236 * 237 * Draws a straight line from the current position to \p point, 238 * which becomes the new current position. 239 * 240 * \throws WException if the path \link isJavaScriptBound() is JavaScript bound\endlink 241 * 242 * \sa lineTo(double, double) 243 */ 244 void lineTo(const WPointF& point); 245 246 /*! \brief Draws a straight line. 247 * 248 * Draws a straight line from the current position to (\p x, 249 * \p y), which becomes the new current position. 250 * 251 * \throws WException if the path \link isJavaScriptBound() is JavaScript bound\endlink 252 * 253 * \sa lineTo(const WPointF&) 254 */ 255 void lineTo(double x, double y); 256 257 /*! \brief Draws a cubic bezier curve. 258 * 259 * Draws a cubic bezier curve from the current position to 260 * \p endPoint, which becomes the new current position. The 261 * bezier curve uses the two control points <i>c1</i> and \p c2. 262 * 263 * \throws WException if the path \link isJavaScriptBound() is JavaScript bound\endlink 264 * 265 * \sa cubicTo(double, double, double, double, double, double) 266 */ 267 void cubicTo(const WPointF& c1, const WPointF& c2, const WPointF& endPoint); 268 269 /*! \brief Draws a cubic bezier curve. 270 * 271 * This is an overloaded method provided for convenience. 272 * 273 * \throws WException if the path \link isJavaScriptBound() is JavaScript bound\endlink 274 * 275 * \sa cubicTo(const WPointF&, const WPointF&, const WPointF&) 276 */ 277 void cubicTo(double c1x, double c1y, double c2x, double c2y, 278 double endPointx, double endPointy); 279 280 /*! \brief Draws an arc. 281 * 282 * Draws an arc which is a segment of a circle. The circle is 283 * defined with center (<i>cx</i>, <i>cy</i>) and \p radius. The 284 * segment starts at \p startAngle, and spans an angle given by 285 * \p spanAngle. These angles are expressed in degrees, and are 286 * measured counter-clockwise starting from the 3 o'clock position. 287 * 288 * Implicitly draws a line from the current position to the start of 289 * the arc, if the current position is different from the start. 290 * 291 * \throws WException if the path \link isJavaScriptBound() is JavaScript bound\endlink 292 * 293 * \sa arcMoveTo() 294 */ 295 void arcTo(double cx, double cy, double radius, 296 double startAngle, double spanAngle); 297 298 /*! \brief Moves to a point on an arc. 299 * 300 * Moves to a point on a circle. The circle is defined with center 301 * (<i>cx</i>, <i>cy</i>) and \p radius, and the point is at 302 * \p angle degrees measured counter-clockwise starting from the 303 * 3 o'clock position. 304 * 305 * \throws WException if the path \link isJavaScriptBound() is JavaScript bound\endlink 306 * 307 * \sa arcTo() 308 */ 309 void arcMoveTo(double cx, double cy, double radius, double angle); 310 311 /*! \brief Move to a point on an arc. 312 * 313 * Moves to a point on an ellipse. The ellipse fits in the 314 * rectangle defined by top left position (\p x, 315 * <i>y</i>), and size <i>width</i> x \p height, and the point is at 316 * \p angle degrees measured counter-clockwise starting from the 317 * 3 o'clock position. 318 * 319 * \throws WException if the path \link isJavaScriptBound() is JavaScript bound\endlink 320 * 321 * \sa arcTo() 322 */ 323 void arcMoveTo(double x, double y, double width, double height, double angle); 324 325 /*! \brief Draws a quadratic bezier curve 326 * 327 * Draws a quadratic bezier curve from the current position to 328 * \p endPoint, which becomes the new current position. The 329 * bezier curve uses the single control point \p c. 330 * 331 * \throws WException if the path \link isJavaScriptBound() is JavaScript bound\endlink 332 * 333 * \sa quadTo(double, double, double, double) 334 */ 335 void quadTo(const WPointF& c, const WPointF& endPoint); 336 337 /*! \brief Draws a quadratic bezier curve. 338 * 339 * This is an overloaded method provided for convenience. 340 * 341 * \throws WException if the path \link isJavaScriptBound() is JavaScript bound\endlink 342 * 343 * \sa quadTo(const WPointF&, const WPointF&) 344 */ 345 void quadTo(double cx, double cy, double endPointx, double endPointy); 346 347 /*! \brief Draws an ellipse. 348 * 349 * This method closes the current sub path, and adds an ellipse that is 350 * bounded by the rectangle \p boundingRectangle. 351 * 352 * \p Note: some renderers only support circles (width == height) 353 * 354 * \throws WException if the path \link isJavaScriptBound() is JavaScript bound\endlink 355 * 356 * \sa addEllipse(double, double, double, double), arcTo() 357 */ 358 void addEllipse(const WRectF& boundingRectangle); 359 360 /*! \brief Draws an ellipse. 361 * 362 * This method closes the current sub path, and adds an ellipse that is 363 * bounded by the rectangle defined by top left position (\p x, 364 * <i>y</i>), and size <i>width</i> x \p height. 365 * 366 * \note Some renderers only support circles (width == height) 367 * 368 * \throws WException if the path \link isJavaScriptBound() is JavaScript bound\endlink 369 * 370 * \sa addEllipse(const WRectF&), arcTo() 371 */ 372 void addEllipse(double x, double y, double width, double height); 373 374 /*! \brief Draws a rectangle. 375 * 376 * This method closes the current sub path, unless \link openSubPathsEnabled() 377 * open subpaths are enabled\endlink, and adds a rectangle 378 * that is defined by \p rectangle. 379 * 380 * \throws WException if the path \link isJavaScriptBound() is JavaScript bound\endlink 381 * 382 * \sa addRect(double, double, double, double) 383 */ 384 void addRect(const WRectF& rectangle); 385 386 /*! \brief Draws a rectangle. 387 * 388 * This method closes the current sub path, unless \link openSubPathsEnabled() 389 * open subpaths are enabled\endlink, and adds a rectangle 390 * that is defined by top left position (<i>x</i>, \p y), and 391 * size <i>width</i> x \p height. 392 * 393 * \throws WException if the path \link isJavaScriptBound() is JavaScript bound\endlink 394 * 395 * \sa addRect(const WRectF&) 396 */ 397 void addRect(double x, double y, double width, double height); 398 399 /*! \brief Adds a polygon. 400 * 401 * If the first point is different from the current position, the last 402 * sub path is first closed, unless \link openSubPathsEnabled() open 403 * subpaths are enabled\endlink, otherwise the last sub path is extended 404 * with the polygon. 405 * 406 * \throws WException if the path \link isJavaScriptBound() is JavaScript bound\endlink 407 * 408 * \sa moveTo(), lineTo() 409 */ 410 void addPolygon(const std::vector<WPointF>& points); 411 412 /*! \brief Adds a path. 413 * 414 * Adds an entire \p path to the current path. If the path's 415 * begin position is different from the current position, the last 416 * sub path is first closed, unless \link openSubPathsEnabled() open 417 * subpaths are enabled\endlink, otherwise the last sub path is extended 418 * with the path's first sub path. 419 * 420 * \throws WException if the path \link isJavaScriptBound() is JavaScript bound\endlink 421 * 422 * \sa connectPath(const WPainterPath&) 423 */ 424 void addPath(const WPainterPath& path); 425 426 /*! \brief Adds a path, connecting. 427 * 428 * Adds an entire \p path to the current path. If the path's 429 * begin position is different from the current position, the last 430 * sub path is first closed, unless \link openSubPathsEnabled() open 431 * subpaths are enabled\endlink, otherwise the last sub path is extended 432 * with the path's first sub path. 433 * 434 * \throws WException if the path \link isJavaScriptBound() is JavaScript bound\endlink 435 * 436 * \sa connectPath(const WPainterPath&) 437 */ 438 void connectPath(const WPainterPath& path); 439 440 /*! \brief A segment. 441 */ 442 class Segment 443 { 444 public: 445 /*! The x parameter 446 * 447 * Depending on the type(), this is either 448 * the x position of the point, or something 449 * else. 450 */ x()451 double x() const { return x_; } 452 453 /*! The y parameter 454 * 455 * Depending on the type(), this is either 456 * the y position of the point, or something 457 * else. 458 */ y()459 double y() const { return y_; } 460 461 /*! The type of the segment 462 */ type()463 SegmentType type() const { return type_; } 464 465 bool operator== (const Segment& other) const; 466 bool operator!= (const Segment& other) const; 467 468 private: 469 Segment(double x, double y, SegmentType type); 470 471 double x_, y_; 472 SegmentType type_; 473 474 friend class WPainterPath; 475 friend WPainterPath WTransform::map(const WPainterPath& path) const; 476 }; 477 segments()478 const std::vector<Segment>& segments() const { return segments_; } 479 480 /* Returns the start position before drawing segment i */ 481 WPointF positionAtSegment(int i) const; 482 483 bool asRect(WRectF& result) const; 484 485 /*! \brief Returns the bounding box of the control points. 486 * 487 * Returns the bounding box of all control points. This is guaranteed to 488 * be a superset of the actual bounding box. 489 * 490 * The \p transform is applied to the path first. 491 */ 492 WRectF controlPointRect(const WTransform& transform = WTransform::Identity) 493 const; 494 495 /*! \brief Returns a copy of the path where straight lines are moved to be rendered crisply. 496 * 497 * This is intended to be used on rectangles, or other paths consisting of only 498 * straight line, and will nudge every edge a little bit, so that 1px straight lines are 499 * rendered as a crisp line. 500 * 501 * This will also work if the path \link isJavaScriptBound() is JavaScript bound\endlink. 502 */ 503 WPainterPath crisp() const; 504 505 /*! \brief Disables automatically closing subpaths on moveTo 506 * 507 * By default, open sub paths are disabled, and moveTo and any operation that 508 * relies on moveTo will automatically close the last subpath. Enabling this 509 * option disables that feature. 510 * 511 * \sa moveTo(), addPath(), connectPath(), addRect() 512 */ 513 void setOpenSubPathsEnabled(bool enabled = true); 514 515 /*! \brief Returns whether open subpaths are enabled. 516 * 517 * \sa setOpenSubPathsEnabled(bool) 518 */ openSubPathsEnabled()519 bool openSubPathsEnabled() const { return openSubPathsEnabled_; } 520 521 bool isPointInPath(const WPointF &p) const; 522 523 virtual std::string jsValue() const override; 524 525 protected: 526 virtual void assignFromJSON(const Json::Value &value) override; 527 528 private: 529 bool isRect_; 530 bool openSubPathsEnabled_; 531 std::vector<Segment> segments_; 532 533 WPointF getSubPathStart() const; 534 WPointF beginPosition() const; 535 536 static WPointF getArcPosition(double cx, double cy, double rx, double ry, 537 double angle); 538 539 void arcTo(double x, double y, double width, double height, 540 double startAngle, double sweepLength); 541 542 friend class WSvgImage; 543 friend WPainterPath WTransform::map(const WPainterPath& path) const; 544 }; 545 546 } 547 548 #endif // WPAINTERPATH_H_ 549