2009-12-07 Rolf Bjarne Kvinge <RKvinge@novell.com>
[moon.git] / src / geometry.cpp
blob3115cf0b68112fdecdabbd37a66ea54741a63d22
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * geometry.cpp: Geometry classes
5 * Contact:
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.
14 #include <config.h>
16 #include <math.h>
18 #include "utils.h"
19 #include "geometry.h"
20 #include "shape.h"
23 // Geometry
26 Geometry::Geometry ()
28 SetObjectType (Type::GEOMETRY);
30 path = NULL;
31 local_bounds = Rect (0,0, -INFINITY, -INFINITY);
34 Geometry::~Geometry ()
36 if (path)
37 moon_path_destroy (path);
40 void
41 Geometry::Draw (cairo_t *cr)
43 Transform *transform = GetTransform ();
44 cairo_matrix_t saved;
45 cairo_get_matrix (cr, &saved);
47 if (transform) {
48 cairo_matrix_t matrix;
49 transform->GetTransform (&matrix);
50 cairo_transform (cr, &matrix);
53 if (!IsBuilt ())
54 Build ();
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 ()));
59 if (path)
60 cairo_append_path (cr, &path->cairo);
62 cairo_set_matrix (cr, &saved);
65 void
66 Geometry::InvalidateCache ()
68 if (path)
69 moon_path_clear (path);
71 local_bounds = Rect (0, 0, -INFINITY, -INFINITY);
74 Rect
75 Geometry::GetBounds ()
77 bool compute = local_bounds.IsEmpty (true);
79 if (!IsBuilt ()) {
80 Build ();
81 compute = true;
84 if (compute)
85 local_bounds = ComputePathBounds ();
87 Rect bounds = local_bounds;
89 Transform *transform = GetTransform ();
90 if (transform) {
91 cairo_matrix_t matrix;
92 transform->GetTransform (&matrix);
93 bounds = bounds.Transform (&matrix);
96 return bounds;
99 Rect
100 Geometry::ComputePathBounds()
102 if (!IsBuilt ())
103 Build ();
105 if (!path || (path->cairo.num_data == 0))
106 return Rect ();
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);
120 return bounds;
123 void
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?
134 InvalidateCache ();
136 return;
139 NotifyListenersOfPropertyChange (args, error);
142 void
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 ()
164 // GeometryGroup
167 GeometryGroup::GeometryGroup ()
169 SetObjectType (Type::GEOMETRYGROUP);
172 GeometryGroup::~GeometryGroup ()
176 void
177 GeometryGroup::OnCollectionChanged (Collection *col, CollectionChangedEventArgs *args)
179 InvalidateCache ();
181 if (col != GetChildren ()) {
182 Geometry::OnCollectionChanged (col, args);
183 return;
186 NotifyListenersOfPropertyChange (GeometryGroup::ChildrenProperty, NULL);
189 void
190 GeometryGroup::OnCollectionItemChanged (Collection *col, DependencyObject *obj, PropertyChangedEventArgs *args)
192 InvalidateCache ();
194 if (col != GetChildren ()) {
195 Geometry::OnCollectionItemChanged (col, obj, args);
196 return;
199 NotifyListenersOfPropertyChange (GeometryGroup::ChildrenProperty, NULL);
202 void
203 GeometryGroup::Draw (cairo_t *cr)
205 Transform *transform = GetTransform ();
206 cairo_matrix_t saved;
207 cairo_get_matrix (cr, &saved);
209 if (transform) {
210 cairo_matrix_t matrix;
211 transform->GetTransform (&matrix);
212 cairo_transform (cr, &matrix);
215 GeometryCollection *children = GetChildren ();
216 Geometry *geometry;
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 ();
224 geometry->Draw (cr);
227 cairo_set_matrix (cr, &saved);
230 Rect
231 GeometryGroup::ComputePathBounds ()
233 GeometryCollection *children = GetChildren ();
234 Rect bounds = Rect (0.0, 0.0, 0.0, 0.0);
235 Geometry *geometry;
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);
244 return bounds;
247 #if 0
248 Point
249 Geometry::GetOriginPoint ()
251 double x = 0.0;
252 double y = 0.0;
254 if (!IsBuilt ())
255 Build ();
257 moon_get_origin (path, &x, &y);
258 return Point (x, y);
260 #endif
263 // EllipseGeometry
266 EllipseGeometry::EllipseGeometry ()
268 SetObjectType (Type::ELLIPSEGEOMETRY);
271 EllipseGeometry::~EllipseGeometry ()
275 void
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);
288 Rect
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;
299 Rect bounds;
301 bounds = Rect (x - hw, y - hh, hw * 2.0, hh * 2.0);
303 return bounds;
307 // LineGeometry
310 LineGeometry::LineGeometry ()
312 SetObjectType (Type::LINEGEOMETRY);
315 LineGeometry::~LineGeometry ()
319 void
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);
330 Rect
331 LineGeometry::ComputePathBounds ()
333 Point *p1 = GetStartPoint ();
334 Point *p2 = GetEndPoint ();
335 PenLineCap start_cap;
336 PenLineCap end_cap;
337 Rect bounds;
339 start_cap = PenLineCapFlat;
340 end_cap = PenLineCapFlat;
342 calc_line_bounds (p1 ? p1->x : 0.0,
343 p2 ? p2->x : 0.0,
344 p1 ? p1->y : 0.0,
345 p2 ? p2->y : 0.0,
346 0.0,
347 start_cap,
348 end_cap,
349 &bounds);
351 return bounds;
355 // PathFigureCollection
358 PathFigureCollection::PathFigureCollection ()
360 SetObjectType (Type::PATHFIGURE_COLLECTION);
363 PathFigureCollection::~PathFigureCollection ()
368 // PathGeometry
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);
384 path = pml;
387 void
388 PathGeometry::OnCollectionChanged (Collection *col, CollectionChangedEventArgs *args)
390 if (col != GetFigures ()) {
391 Geometry::OnCollectionChanged (col, args);
392 return;
395 InvalidateCache ();
397 NotifyListenersOfPropertyChange (PathGeometry::FiguresProperty, NULL);
400 void
401 PathGeometry::OnCollectionItemChanged (Collection *col, DependencyObject *obj, PropertyChangedEventArgs *args)
403 if (col != GetFigures ()) {
404 Geometry::OnCollectionItemChanged (col, obj, args);
405 return;
408 InvalidateCache ();
410 NotifyListenersOfPropertyChange (PathGeometry::FiguresProperty, NULL);
413 void
414 PathGeometry::Build ()
416 PathFigureCollection *figures;
417 PathFigure *figure;
419 path = moon_path_renew (path, 0);
421 if (!(figures = GetFigures ()))
422 return;
424 for (int i = 0; i < figures->GetCount (); i++) {
425 figure = figures->GetValueAt (i)->AsPathFigure ();
427 if (!figure->IsBuilt ())
428 figure->Build ();
430 moon_merge (path, figure->path);
434 Rect
435 PathGeometry::ComputePathBounds ()
437 if (!IsBuilt ())
438 Build ();
440 PathFigureCollection *figures = GetFigures ();
441 if (!figures && (!path || (path->cairo.num_data == 0)))
442 return Rect ();
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);
456 return bounds;
460 // RectangleGeometry
463 RectangleGeometry::RectangleGeometry ()
465 SetObjectType (Type::RECTANGLEGEOMETRY);
468 RectangleGeometry::~RectangleGeometry ()
472 void
473 RectangleGeometry::Build ()
475 Rect *rect = GetRect ();
476 if (!rect)
477 return;
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);
485 Rect
486 RectangleGeometry::ComputePathBounds ()
488 Rect *rect = GetRect ();
489 Rect bounds;
491 if (!rect)
492 return Rect (0.0, 0.0, 0.0, 0.0);
494 bounds = *rect;
496 return bounds;
500 // PathSegmentCollection
503 PathSegmentCollection::PathSegmentCollection ()
505 SetObjectType (Type::PATHSEGMENT_COLLECTION);
508 PathSegmentCollection::~PathSegmentCollection ()
513 // PathFigure
516 PathFigure::PathFigure ()
518 SetObjectType (Type::PATHFIGURE);
519 path = NULL;
522 PathFigure::~PathFigure ()
524 if (path)
525 moon_path_destroy (path);
528 void
529 PathFigure::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
531 if (args->GetProperty ()->GetOwnerType() != Type::PATHFIGURE) {
532 DependencyObject::OnPropertyChanged (args, error);
533 return;
536 if (path)
537 moon_path_clear (path);
539 NotifyListenersOfPropertyChange (args, error);
542 void
543 PathFigure::OnCollectionChanged (Collection *col, CollectionChangedEventArgs *args)
545 if (col != GetSegments ()) {
546 DependencyObject::OnCollectionChanged (col, args);
547 return;
550 if (path)
551 moon_path_clear (path);
553 NotifyListenersOfPropertyChange (PathFigure::SegmentsProperty, NULL);
556 void
557 PathFigure::OnCollectionItemChanged (Collection *col, DependencyObject *obj, PropertyChangedEventArgs *args)
559 if (col != GetSegments ()) {
560 DependencyObject::OnCollectionItemChanged (col, obj, args);
561 return;
564 if (path)
565 moon_path_clear (path);
567 NotifyListenersOfPropertyChange (PathFigure::SegmentsProperty, NULL);
570 void
571 PathFigure::Build ()
573 PathSegmentCollection *segments = GetSegments ();
574 PathSegment *segment;
576 if (path)
577 moon_path_clear (path);
578 else
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);
590 if (GetIsClosed ())
591 moon_close_path (path);
595 // PathSegment
598 PathSegment::PathSegment ()
600 SetObjectType (Type::PATHSEGMENT);
603 PathSegment::~PathSegment ()
607 void
608 PathSegment::Build ()
612 void
613 PathSegment::Append (moon_path *path)
618 // ArcSegment
621 ArcSegment::ArcSegment ()
623 SetObjectType (Type::ARCSEGMENT);
626 ArcSegment::~ArcSegment ()
630 void
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);
645 // BezierSegment
648 BezierSegment::BezierSegment ()
650 SetObjectType (Type::BEZIERSEGMENT);
653 BezierSegment::~BezierSegment ()
657 void
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);
675 // LineSegment
678 LineSegment::LineSegment ()
680 SetObjectType (Type::LINESEGMENT);
683 LineSegment::~LineSegment ()
687 void
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);
699 // PolyBezierSegment
702 PolyBezierSegment::PolyBezierSegment ()
704 SetObjectType (Type::POLYBEZIERSEGMENT);
707 PolyBezierSegment::~PolyBezierSegment ()
711 void
712 PolyBezierSegment::Append (moon_path *path)
714 PointCollection *col;
715 GPtrArray *points;
717 col = GetPoints ();
719 // we need at least 3 points
720 if (!col || (col->GetCount() % 3) != 0)
721 return;
723 points = col->Array();
725 for (int i = 0; i < col->GetCount() - 2; i += 3) {
726 moon_curve_to (path,
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;
748 // PolyLineSegment
751 PolyLineSegment::PolyLineSegment ()
753 SetObjectType (Type::POLYLINESEGMENT);
756 PolyLineSegment::~PolyLineSegment ()
760 void
761 PolyLineSegment::Append (moon_path *path)
763 PointCollection *col;
764 GPtrArray *points;
766 col = GetPoints ();
768 if (!col)
769 return;
771 points = col->Array();
773 for (int i = 0; i < col->GetCount(); i++)
774 moon_line_to (path,
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 ()
807 void
808 PolyQuadraticBezierSegment::Append (moon_path *path)
810 PointCollection *col;
811 GPtrArray *points;
813 col = GetPoints ();
815 if (!col || ((col->GetCount() % 2) != 0))
816 return;
818 // origin
819 double x0 = 0.0;
820 double y0 = 0.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;
831 double x3 = x2;
832 double y3 = y2;
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);
841 // set new origin
842 x0 = x3;
843 y0 = 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 ()
870 void
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);