1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * geometry.cpp: Geometry classes
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
28 SetObjectType (Type::GEOMETRY
);
31 local_bounds
= Rect (0,0, -INFINITY
, -INFINITY
);
34 Geometry::~Geometry ()
37 moon_path_destroy (path
);
41 Geometry::Draw (cairo_t
*cr
)
43 Transform
*transform
= GetTransform ();
45 cairo_get_matrix (cr
, &saved
);
48 cairo_matrix_t matrix
;
49 transform
->GetTransform (&matrix
);
50 cairo_transform (cr
, &matrix
);
56 // Geometry is used for Clip so Fill (normally setting the fill rule) is never called
57 cairo_set_fill_rule (cr
, convert_fill_rule (GetFillRule ()));
60 cairo_append_path (cr
, &path
->cairo
);
62 cairo_set_matrix (cr
, &saved
);
66 Geometry::InvalidateCache ()
69 moon_path_clear (path
);
71 local_bounds
= Rect (0, 0, -INFINITY
, -INFINITY
);
75 Geometry::GetBounds ()
77 bool compute
= local_bounds
.IsEmpty (true);
85 local_bounds
= ComputePathBounds ();
87 Rect bounds
= local_bounds
;
89 Transform
*transform
= GetTransform ();
91 cairo_matrix_t matrix
;
92 transform
->GetTransform (&matrix
);
93 bounds
= bounds
.Transform (&matrix
);
100 Geometry::ComputePathBounds()
105 if (!path
|| (path
->cairo
.num_data
== 0))
108 cairo_t
*cr
= measuring_context_create ();
110 cairo_append_path (cr
, &path
->cairo
);
112 double x1
, y1
, x2
, y2
;
114 cairo_path_extents (cr
, &x1
, &y1
, &x2
, &y2
);
116 Rect bounds
= Rect (MIN (x1
, x2
), MIN (y1
, y2
), fabs (x2
- x1
), fabs (y2
- y1
));
118 measuring_context_destroy (cr
);
124 Geometry::OnPropertyChanged (PropertyChangedEventArgs
*args
, MoonError
*error
)
126 // no need to clear the path for Geometry itself as FillRule and Transform properties are
127 // only used when drawing, i.e. they do not affect the path itself
128 if (args
->GetProperty ()->GetOwnerType() != Type::GEOMETRY
&&
129 args
->GetId () != PathGeometry::FillRuleProperty
&&
130 args
->GetId () != GeometryGroup::FillRuleProperty
) {
131 DependencyObject::OnPropertyChanged (args
, error
);
133 // not sure why we're doing this inside this block.. seems like it should happen outside it?
139 NotifyListenersOfPropertyChange (args
, error
);
143 Geometry::OnSubPropertyChanged (DependencyProperty
*prop
, DependencyObject
*obj
, PropertyChangedEventArgs
*subobj_args
)
145 NotifyListenersOfPropertyChange (prop
, NULL
);
147 DependencyObject::OnSubPropertyChanged (prop
, obj
, subobj_args
);
151 // GeometryCollection
154 GeometryCollection::GeometryCollection ()
156 SetObjectType (Type::GEOMETRY_COLLECTION
);
159 GeometryCollection::~GeometryCollection ()
167 GeometryGroup::GeometryGroup ()
169 SetObjectType (Type::GEOMETRYGROUP
);
172 GeometryGroup::~GeometryGroup ()
177 GeometryGroup::OnCollectionChanged (Collection
*col
, CollectionChangedEventArgs
*args
)
181 if (col
!= GetChildren ()) {
182 Geometry::OnCollectionChanged (col
, args
);
186 NotifyListenersOfPropertyChange (GeometryGroup::ChildrenProperty
, NULL
);
190 GeometryGroup::OnCollectionItemChanged (Collection
*col
, DependencyObject
*obj
, PropertyChangedEventArgs
*args
)
194 if (col
!= GetChildren ()) {
195 Geometry::OnCollectionItemChanged (col
, obj
, args
);
199 NotifyListenersOfPropertyChange (GeometryGroup::ChildrenProperty
, NULL
);
203 GeometryGroup::Draw (cairo_t
*cr
)
205 Transform
*transform
= GetTransform ();
206 cairo_matrix_t saved
;
207 cairo_get_matrix (cr
, &saved
);
210 cairo_matrix_t matrix
;
211 transform
->GetTransform (&matrix
);
212 cairo_transform (cr
, &matrix
);
215 GeometryCollection
*children
= GetChildren ();
218 // GeometryGroup is used for Clip (as a Geometry) so Fill (normally setting the fill rule) is never called
219 cairo_set_fill_rule (cr
, convert_fill_rule (GetFillRule ()));
221 for (int i
= 0; i
< children
->GetCount (); i
++) {
222 geometry
= children
->GetValueAt (i
)->AsGeometry ();
227 cairo_set_matrix (cr
, &saved
);
231 GeometryGroup::ComputePathBounds ()
233 GeometryCollection
*children
= GetChildren ();
234 Rect bounds
= Rect (0.0, 0.0, 0.0, 0.0);
237 for (int i
= 0; i
< children
->GetCount (); i
++) {
238 geometry
= children
->GetValueAt (i
)->AsGeometry ();
240 bounds
= bounds
.Union (geometry
->GetBounds (), true);
243 //g_warning ("GeometryGroup::ComputeBounds - x %g y %g w %g h %g", bounds.x, bounds.y, bounds.w, bounds.h);
249 Geometry::GetOriginPoint ()
257 moon_get_origin (path
, &x
, &y
);
266 EllipseGeometry::EllipseGeometry ()
268 SetObjectType (Type::ELLIPSEGEOMETRY
);
271 EllipseGeometry::~EllipseGeometry ()
276 EllipseGeometry::Build ()
278 double rx
= GetRadiusX ();
279 double ry
= GetRadiusY ();
280 Point
*pt
= GetCenter ();
281 double x
= pt
? pt
->x
: 0.0;
282 double y
= pt
? pt
->y
: 0.0;
284 path
= moon_path_renew (path
, MOON_PATH_ELLIPSE_LENGTH
);
285 moon_ellipse (path
, x
- rx
, y
- ry
, rx
* 2.0, ry
* 2.0);
289 EllipseGeometry::ComputePathBounds ()
291 // code written to minimize divisions
293 double hw
= GetRadiusX ();
294 double hh
= GetRadiusY ();
295 // point is at center, so left-top corner is minus half width / half height
296 Point
*pt
= GetCenter ();
297 double x
= pt
? pt
->x
: 0.0;
298 double y
= pt
? pt
->y
: 0.0;
301 bounds
= Rect (x
- hw
, y
- hh
, hw
* 2.0, hh
* 2.0);
310 LineGeometry::LineGeometry ()
312 SetObjectType (Type::LINEGEOMETRY
);
315 LineGeometry::~LineGeometry ()
320 LineGeometry::Build ()
322 Point
*p1
= GetStartPoint ();
323 Point
*p2
= GetEndPoint ();
325 path
= moon_path_renew (path
, MOON_PATH_MOVE_TO_LENGTH
+ MOON_PATH_LINE_TO_LENGTH
);
326 moon_move_to (path
, p1
? p1
->x
: 0.0, p1
? p1
->y
: 0.0);
327 moon_line_to (path
, p2
? p2
->x
: 0.0, p2
? p2
->y
: 0.0);
331 LineGeometry::ComputePathBounds ()
333 Point
*p1
= GetStartPoint ();
334 Point
*p2
= GetEndPoint ();
335 PenLineCap start_cap
;
339 start_cap
= PenLineCapFlat
;
340 end_cap
= PenLineCapFlat
;
342 calc_line_bounds (p1
? p1
->x
: 0.0,
355 // PathFigureCollection
358 PathFigureCollection::PathFigureCollection ()
360 SetObjectType (Type::PATHFIGURE_COLLECTION
);
363 PathFigureCollection::~PathFigureCollection ()
371 PathGeometry::PathGeometry ()
373 SetObjectType (Type::PATHGEOMETRY
);
376 PathGeometry::~PathGeometry ()
380 // special case for the XAML parser when Path Markup Language (PML) is being used
381 PathGeometry::PathGeometry (moon_path
*pml
)
383 SetObjectType (Type::PATHGEOMETRY
);
388 PathGeometry::OnCollectionChanged (Collection
*col
, CollectionChangedEventArgs
*args
)
390 if (col
!= GetFigures ()) {
391 Geometry::OnCollectionChanged (col
, args
);
397 NotifyListenersOfPropertyChange (PathGeometry::FiguresProperty
, NULL
);
401 PathGeometry::OnCollectionItemChanged (Collection
*col
, DependencyObject
*obj
, PropertyChangedEventArgs
*args
)
403 if (col
!= GetFigures ()) {
404 Geometry::OnCollectionItemChanged (col
, obj
, args
);
410 NotifyListenersOfPropertyChange (PathGeometry::FiguresProperty
, NULL
);
414 PathGeometry::Build ()
416 PathFigureCollection
*figures
;
419 path
= moon_path_renew (path
, 0);
421 if (!(figures
= GetFigures ()))
424 for (int i
= 0; i
< figures
->GetCount (); i
++) {
425 figure
= figures
->GetValueAt (i
)->AsPathFigure ();
427 if (!figure
->IsBuilt ())
430 moon_merge (path
, figure
->path
);
435 PathGeometry::ComputePathBounds ()
440 PathFigureCollection
*figures
= GetFigures ();
441 if (!figures
&& (!path
|| (path
->cairo
.num_data
== 0)))
444 cairo_t
*cr
= measuring_context_create ();
446 cairo_append_path (cr
, &path
->cairo
);
448 double x1
, y1
, x2
, y2
;
450 cairo_path_extents (cr
, &x1
, &y1
, &x2
, &y2
);
452 Rect bounds
= Rect (MIN (x1
, x2
), MIN (y1
, y2
), fabs (x2
- x1
), fabs (y2
- y1
));
454 measuring_context_destroy (cr
);
463 RectangleGeometry::RectangleGeometry ()
465 SetObjectType (Type::RECTANGLEGEOMETRY
);
468 RectangleGeometry::~RectangleGeometry ()
473 RectangleGeometry::Build ()
475 Rect
*rect
= GetRect ();
479 double radius_x
= GetRadiusX ();
480 double radius_y
= GetRadiusY ();
481 path
= moon_path_renew (path
, MOON_PATH_ROUNDED_RECTANGLE_LENGTH
);
482 moon_rounded_rectangle (path
, rect
->x
, rect
->y
, rect
->width
, rect
->height
, radius_x
, radius_y
);
486 RectangleGeometry::ComputePathBounds ()
488 Rect
*rect
= GetRect ();
492 return Rect (0.0, 0.0, 0.0, 0.0);
500 // PathSegmentCollection
503 PathSegmentCollection::PathSegmentCollection ()
505 SetObjectType (Type::PATHSEGMENT_COLLECTION
);
508 PathSegmentCollection::~PathSegmentCollection ()
516 PathFigure::PathFigure ()
518 SetObjectType (Type::PATHFIGURE
);
522 PathFigure::~PathFigure ()
525 moon_path_destroy (path
);
529 PathFigure::OnPropertyChanged (PropertyChangedEventArgs
*args
, MoonError
*error
)
531 if (args
->GetProperty ()->GetOwnerType() != Type::PATHFIGURE
) {
532 DependencyObject::OnPropertyChanged (args
, error
);
537 moon_path_clear (path
);
539 NotifyListenersOfPropertyChange (args
, error
);
543 PathFigure::OnCollectionChanged (Collection
*col
, CollectionChangedEventArgs
*args
)
545 if (col
!= GetSegments ()) {
546 DependencyObject::OnCollectionChanged (col
, args
);
551 moon_path_clear (path
);
553 NotifyListenersOfPropertyChange (PathFigure::SegmentsProperty
, NULL
);
557 PathFigure::OnCollectionItemChanged (Collection
*col
, DependencyObject
*obj
, PropertyChangedEventArgs
*args
)
559 if (col
!= GetSegments ()) {
560 DependencyObject::OnCollectionItemChanged (col
, obj
, args
);
565 moon_path_clear (path
);
567 NotifyListenersOfPropertyChange (PathFigure::SegmentsProperty
, NULL
);
573 PathSegmentCollection
*segments
= GetSegments ();
574 PathSegment
*segment
;
577 moon_path_clear (path
);
579 path
= moon_path_new (MOON_PATH_MOVE_TO_LENGTH
+ (segments
->GetCount () * 4) + MOON_PATH_CLOSE_PATH_LENGTH
);
581 Point
*start
= GetStartPoint ();
582 moon_move_to (path
, start
? start
->x
: 0.0, start
? start
->y
: 0.0);
584 for (int i
= 0; i
< segments
->GetCount (); i
++) {
585 segment
= segments
->GetValueAt (i
)->AsPathSegment ();
587 segment
->Append (path
);
591 moon_close_path (path
);
598 PathSegment::PathSegment ()
600 SetObjectType (Type::PATHSEGMENT
);
603 PathSegment::~PathSegment ()
608 PathSegment::Build ()
613 PathSegment::Append (moon_path
*path
)
621 ArcSegment::ArcSegment ()
623 SetObjectType (Type::ARCSEGMENT
);
626 ArcSegment::~ArcSegment ()
631 ArcSegment::Append (moon_path
*path
)
633 Size
*size
= GetSize ();
634 double width
= size
? size
->width
: 0.0;
635 double height
= size
? size
->height
: 0.0;
637 Point
*end_point
= GetPoint ();
638 double ex
= end_point
? end_point
->x
: 0.0;
639 double ey
= end_point
? end_point
->y
: 0.0;
641 moon_arc_to (path
, width
, height
, GetRotationAngle (), GetIsLargeArc (), GetSweepDirection (), ex
, ey
);
648 BezierSegment::BezierSegment ()
650 SetObjectType (Type::BEZIERSEGMENT
);
653 BezierSegment::~BezierSegment ()
658 BezierSegment::Append (moon_path
*path
)
660 Point
*p1
= GetPoint1 ();
661 Point
*p2
= GetPoint2 ();
662 Point
*p3
= GetPoint3 ();
664 double x1
= p1
? p1
->x
: 0.0;
665 double y1
= p1
? p1
->y
: 0.0;
666 double x2
= p2
? p2
->x
: 0.0;
667 double y2
= p2
? p2
->y
: 0.0;
668 double x3
= p3
? p3
->x
: 0.0;
669 double y3
= p3
? p3
->y
: 0.0;
671 moon_curve_to (path
, x1
, y1
, x2
, y2
, x3
, y3
);
678 LineSegment::LineSegment ()
680 SetObjectType (Type::LINESEGMENT
);
683 LineSegment::~LineSegment ()
688 LineSegment::Append (moon_path
*path
)
690 Point
*p
= GetPoint ();
692 double x
= p
? p
->x
: 0.0;
693 double y
= p
? p
->y
: 0.0;
695 moon_line_to (path
, x
, y
);
702 PolyBezierSegment::PolyBezierSegment ()
704 SetObjectType (Type::POLYBEZIERSEGMENT
);
707 PolyBezierSegment::~PolyBezierSegment ()
712 PolyBezierSegment::Append (moon_path
*path
)
714 PointCollection
*col
;
719 // we need at least 3 points
720 if (!col
|| (col
->GetCount() % 3) != 0)
723 points
= col
->Array();
725 for (int i
= 0; i
< col
->GetCount() - 2; i
+= 3) {
727 ((Value
*)g_ptr_array_index(points
, i
))->AsPoint()->x
,
728 ((Value
*)g_ptr_array_index(points
, i
))->AsPoint()->y
,
730 ((Value
*)g_ptr_array_index(points
, i
+1))->AsPoint()->x
,
731 ((Value
*)g_ptr_array_index(points
, i
+1))->AsPoint()->y
,
733 ((Value
*)g_ptr_array_index(points
, i
+2))->AsPoint()->x
,
734 ((Value
*)g_ptr_array_index(points
, i
+2))->AsPoint()->y
);
739 PolyBezierSegment::GetPathSize ()
741 PointCollection
*points
= GetPoints ();
742 int n
= points
? points
->GetCount() : 0;
744 return (n
/ 3) * MOON_PATH_CURVE_TO_LENGTH
;
751 PolyLineSegment::PolyLineSegment ()
753 SetObjectType (Type::POLYLINESEGMENT
);
756 PolyLineSegment::~PolyLineSegment ()
761 PolyLineSegment::Append (moon_path
*path
)
763 PointCollection
*col
;
771 points
= col
->Array();
773 for (int i
= 0; i
< col
->GetCount(); i
++)
775 ((Value
*)g_ptr_array_index(points
, i
))->AsPoint()->x
,
776 ((Value
*)g_ptr_array_index(points
, i
))->AsPoint()->y
);
780 PolyLineSegment::GetPathSize ()
782 PointCollection
*points
= GetPoints ();
783 int n
= points
? points
->GetCount() : 0;
785 return n
* MOON_PATH_LINE_TO_LENGTH
;
789 // PolyQuadraticBezierSegment
792 // quadratic to cubic bezier, the original control point and the end control point are the same
793 // http://web.archive.org/web/20020209100930/http://www.icce.rug.nl/erikjan/bluefuzz/beziers/beziers/node2.html
795 // note: we dont call moon_quad_curve_to to avoid calling moon_get_current_point
796 // on each curve (since all but the first one is known)
798 PolyQuadraticBezierSegment::PolyQuadraticBezierSegment ()
800 SetObjectType (Type::POLYQUADRATICBEZIERSEGMENT
);
803 PolyQuadraticBezierSegment::~PolyQuadraticBezierSegment ()
808 PolyQuadraticBezierSegment::Append (moon_path
*path
)
810 PointCollection
*col
;
815 if (!col
|| ((col
->GetCount() % 2) != 0))
821 moon_get_current_point (path
, &x0
, &y0
);
823 points
= col
->Array();
825 // we need at least 2 points
826 for (int i
= 0; i
< col
->GetCount() - 1; i
+=2) {
827 double x1
= ((Value
*)g_ptr_array_index(points
, i
))->AsPoint()->x
;
828 double y1
= ((Value
*)g_ptr_array_index(points
, i
))->AsPoint()->y
;
829 double x2
= ((Value
*)g_ptr_array_index(points
, i
+1))->AsPoint()->x
;
830 double y2
= ((Value
*)g_ptr_array_index(points
, i
+1))->AsPoint()->y
;
834 x2
= x1
+ (x2
- x1
) / 3;
835 y2
= y1
+ (y2
- y1
) / 3;
836 x1
= x0
+ 2 * (x1
- x0
) / 3;
837 y1
= y0
+ 2 * (y1
- y0
) / 3;
839 moon_curve_to (path
, x1
, y1
, x2
, y2
, x3
, y3
);
848 PolyQuadraticBezierSegment::GetPathSize ()
850 PointCollection
* points
= GetPoints ();
852 int n
= points
? points
->GetCount() : 0;
854 return (n
/ 2) * MOON_PATH_CURVE_TO_LENGTH
;
858 // QuadraticBezierSegment
861 QuadraticBezierSegment::QuadraticBezierSegment ()
863 SetObjectType (Type::QUADRATICBEZIERSEGMENT
);
866 QuadraticBezierSegment::~QuadraticBezierSegment ()
871 QuadraticBezierSegment::Append (moon_path
*path
)
873 Point
*p1
= GetPoint1 ();
874 Point
*p2
= GetPoint2 ();
876 double x1
= p1
? p1
->x
: 0.0;
877 double y1
= p1
? p1
->y
: 0.0;
878 double x2
= p2
? p2
->x
: 0.0;
879 double y2
= p2
? p2
->y
: 0.0;
881 moon_quad_curve_to (path
, x1
, y1
, x2
, y2
);