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