1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
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.
19 #include "collection.h"
21 #include "moon-path.h"
24 #define DEBUG_HITTEST 0
28 // StylusPointCollection
32 StylusPointCollection::CanAdd (Value
*value
)
34 // We skip DependencyObjectCollection::CanAdd because that one
35 // mandates 1 parent per DO, which StylusPoints violate.
36 return Collection::CanAdd (value
) && !Contains (value
);
40 StylusPointCollection::AddStylusPoints (StylusPointCollection
*points
)
43 return 1.0; // documented as such, needs testing
45 for (int i
= 0; i
< points
->GetCount (); i
++)
46 Add (points
->GetValueAt (i
)->AsDependencyObject ());
48 return array
->len
- 1;
52 StylusPointCollection::GetBounds ()
55 return Rect (0, 0, 0, 0);
57 StylusPoint
*point
= GetValueAt (0)->AsStylusPoint ();
58 Rect r
= Rect (point
->GetX (), point
->GetY (), 0, 0);
60 for (guint i
= 1; i
< array
->len
; i
++) {
61 point
= GetValueAt (i
)->AsStylusPoint ();
62 r
= r
.ExtendTo (point
->GetX (), point
->GetY ());
75 SetObjectType (Type::STROKE
);
82 Stroke::HitTestEndcapSegment (Point c
,
90 fprintf (stderr
, "HitTestEndcapSegment: (%g,%g / %g, %g) hits segment (%g,%g - %g,%g)?\n",
100 fprintf (stderr
, "dx == 0, returning %d\n", p1
.x
>= (c
.x
- w
/2) && p1
.x
<= (c
.x
+ w
/2));
102 if (p1
.x
>= (c
.x
- w
/2) && p1
.x
<= (c
.x
+ w
/2)) {
103 if (p1
.y
< (c
.y
- h
/2) && p2
.y
< (c
.y
- h
/2))
105 if (p1
.y
> (c
.y
+ h
/2) && p2
.y
> (c
.y
+ h
/2))
115 // this body of code basically uses the following form of the line:
119 // and the equation for an ellipse:
125 // where a > b (as leave off the center point of the ellipse
126 // because we've subtracted it out above).
128 // we substitute the line equation in for y in the ellipse
129 // equation, and use the quadratic formula to find our roots
130 // (if there are any).
132 double m
= (p2
.y
- p1
.y
)/(p2
.x
- p1
.x
);
133 double b_
= p1
.y
- m
* p1
.x
;
145 if (b
== 0 || a
== 0) {
149 double aq
= (m
*m
) / (b
*b
) + 1 / (a
*a
);
150 double bq
= (2 * m
* b_
) / (b
* b
);
151 double cq
= (b_
* b_
) / (b
* b
) - 1;
153 double discr
= bq
* bq
- 4 * aq
* cq
;
156 fprintf (stderr
, "HitTestEndCapSegment: discr = %g\n", discr
);
159 // if we have roots we need to check if they occur on the line
160 // segment (using the parametric form of the line).
164 double sqrt_discr
= discr
> 0 ? sqrt(discr
) : 0;
166 double root_1
= ((- bq
) - sqrt_discr
) / (2 * aq
);
167 if (root_1
> p1
.x
&& (root_1
- p1
.x
) < (p2
.x
- p1
.x
))
170 double root_2
= (- bq
+ sqrt_discr
) / (2 * aq
);
171 return (root_2
> p1
.x
&& (root_2
- p1
.x
) < (p2
.x
- p1
.x
));
176 intersect_line_2d (Point p1
, Point p2
, Point p3
, Point p4
)
178 // taken from http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
179 // line segments are p1 - p2 and p3 - p4
181 double denom
= (p4
.y
- p3
.y
) * (p2
.x
- p1
.x
) - (p4
.x
- p3
.x
) * (p2
.y
- p1
.y
);
183 if (denom
== 0) // they're parallel
186 double ua
= (p4
.x
- p3
.x
) * (p1
.y
- p3
.y
) - (p4
.y
- p3
.y
) * (p1
.x
- p3
.x
);
189 double ub
= (p2
.x
- p1
.x
) * (p1
.y
- p3
.y
) - (p2
.y
- p1
.y
) * (p1
.x
- p3
.x
);
191 if (ua
>= 0 && ua
<= 1 && ub
>= 0 && ub
<= 1)
197 // given the line segment between @p1 and @p2, with an ellipse with
198 // width = @w and height = @h centered at point @p, return the left
199 // (lesser x coordinate if the x's are different, and lesser y
200 // coordinate if they're the same) and right (greater x coordinate, or
201 // greater y coordinate) intersection points of the line through the
202 // same point @p but perpendicular to line @p2 - @p1.
204 calc_perpendicular_intersection_points (Point p1
, Point p2
,
207 Point
*left_point
, Point
*right_point
)
209 if (p2
.y
== p1
.y
) { // horizontal line
210 *left_point
= Point (p
.x
, p
.y
- h
/ 2);
211 *right_point
= Point (p
.x
, p
.y
+ h
/ 2);
213 else if (p2
.x
== p1
.x
) { // vertical line
214 *left_point
= Point (p
.x
- w
/ 2, p
.y
);
215 *right_point
= Point (p
.x
+ w
/ 2, p
.y
);
218 // slope of the perpendicular line
219 double m
= -(p2
.x
- p1
.x
)/p2
.y
- p1
.y
;
231 double aq
= (m
*m
) / (b
*b
) + 1 / (a
*a
);
233 double discr
= 4 * aq
;
236 g_warning ("should never happen, there should always be two roots");
241 double sqrt_discr
= sqrt (discr
);
243 double root
= (sqrt_discr
) / (2 * aq
);
245 *left_point
= Point (-root
+ p
.x
, (-root
* m
) + p
.y
);
246 *right_point
= Point (root
+ p
.x
, (root
* m
) + p
.y
);
253 Stroke::HitTestSegmentSegment (Point stroke_p1
, Point stroke_p2
,
258 fprintf (stderr
, "HitTestSegmentSegment: (%g,%g - %g,%g / %g, %g) hits segment (%g,%g - %g,%g) ?\n",
259 stroke_p1
.x
, stroke_p1
.y
,
260 stroke_p2
.x
, stroke_p2
.y
,
265 Point left_stroke_p1
, right_stroke_p1
;
266 Point left_stroke_p2
, right_stroke_p2
;
268 calc_perpendicular_intersection_points (stroke_p1
, stroke_p2
, stroke_p1
, w
, h
, &left_stroke_p1
, &right_stroke_p1
);
269 calc_perpendicular_intersection_points (stroke_p1
, stroke_p2
, stroke_p2
, w
, h
, &left_stroke_p2
, &right_stroke_p2
);
271 if (intersect_line_2d (left_stroke_p1
, left_stroke_p2
, p1
, p2
))
273 if (intersect_line_2d (right_stroke_p1
, right_stroke_p2
, p1
, p2
))
280 Stroke::HitTestEndcapPoint (Point c
,
285 fprintf (stderr
, "HitTestEndcapPoint: (%g,%g / %g, %g) hits point %g,%g ?\n",
297 bool rv
= ((dp
.x
* dp
.x
) / (a
* a
) + (dp
.y
* dp
.y
) / (b
* b
)) < 1;
299 fprintf (stderr
, " + %s\n", rv
? "TRUE" : "FALSE");
305 point_gte_line (Point p
,
308 // return true if the point is to the right of or beneath the line segment
313 else if (p1
.x
== p2
.x
)
316 double m
= (p2
.y
- p1
.y
) / (p2
.x
- p1
.x
);
319 return p
.y
< (p1
.y
+ m
* p
.x
);
321 return p
.y
> (p1
.y
+ m
* p
.x
);
326 point_lte_line (Point p
,
329 // return true if the point is to the right of or beneath the line segment
333 else if (p1
.x
== p2
.x
)
336 double m
= (p2
.y
- p1
.y
) / (p2
.x
- p1
.x
);
339 return p
.y
> (p1
.y
+ m
* p
.x
);
341 return p
.y
< (p1
.y
+ m
* p
.x
);
346 Stroke::HitTestSegmentPoint (Point stroke_p1
, Point stroke_p2
,
351 fprintf (stderr
, "HitTestSegment: (%g,%g - %g,%g / %g, %g) hits point (%g,%g) ?\n",
352 stroke_p1
.x
, stroke_p1
.y
,
353 stroke_p2
.x
, stroke_p2
.y
,
358 Point left_stroke_p1
, right_stroke_p1
;
359 Point left_stroke_p2
, right_stroke_p2
;
361 calc_perpendicular_intersection_points (stroke_p1
, stroke_p2
, stroke_p1
, w
, h
, &left_stroke_p1
, &right_stroke_p1
);
362 calc_perpendicular_intersection_points (stroke_p1
, stroke_p2
, stroke_p2
, w
, h
, &left_stroke_p2
, &right_stroke_p2
);
364 return point_gte_line (p
, left_stroke_p1
, left_stroke_p2
) && point_lte_line (p
, right_stroke_p1
, right_stroke_p2
);
369 Stroke::HitTestSegment (Point p1
, Point p2
, double w
, double h
, StylusPointCollection
*stylusPoints
)
373 if (HitTestEndcap (p1
, w
, h
, stylusPoints
))
376 if (HitTestEndcap (p2
, w
, h
, stylusPoints
))
379 for (int i
= 0; i
< stylusPoints
->GetCount (); i
++) {
380 sp
= stylusPoints
->GetValueAt (i
)->AsStylusPoint ();
382 if (i
+ 1 == stylusPoints
->GetCount ()) {
383 Point
p (sp
->GetX (), sp
->GetY ());
385 if (!bounds
.PointInside (p
))
388 if (HitTestSegmentPoint (p1
, p2
,
394 StylusPoint
*next_sp
= stylusPoints
->GetValueAt (i
+ 1)->AsStylusPoint ();
397 Point
p (sp
->GetX (), sp
->GetY ());
398 Point
next_p (next_sp
->GetX (), next_sp
->GetY ());
400 if (HitTestSegmentSegment (p1
, p2
,
411 Stroke::HitTestEndcap (Point p
, double w
, double h
, StylusPointCollection
*stylusPoints
)
413 StylusPoint
*sp
= stylusPoints
->GetValueAt (0)->AsStylusPoint ();
419 if (stylusPoints
->GetCount () < 2) {
420 // singleton input point to match against
421 if (bounds
.PointInside (cur
)) {
422 if (HitTestEndcapPoint (p
, w
, h
, cur
))
426 fprintf (stderr
, "\t(%f, %f) EndcapPoint failed\n",
431 fprintf (stderr
, "\t(%f, %f) is not within bounds\n",
437 for (int i
= 1; i
< stylusPoints
->GetCount (); i
++) {
438 sp
= stylusPoints
->GetValueAt (i
)->AsStylusPoint ();
439 next
.x
= sp
->GetX ();
440 next
.y
= sp
->GetY ();
442 if (HitTestEndcapSegment (p
, w
, h
, cur
, next
))
446 fprintf (stderr
, "\t(%f, %f) (%f, %f) EndcapSegment failed\n",
447 cur
.x
, cur
.y
, next
.x
, next
.y
);
458 Stroke::HitTest (StylusPointCollection
*stylusPoints
)
460 StylusPointCollection
*myStylusPoints
= GetStylusPoints ();
462 if (myStylusPoints
->GetCount () == 0) {
464 fprintf (stderr
, "no points in the collection, returning false!\n");
469 DrawingAttributes
*da
= GetDrawingAttributes ();
472 double height
, width
;
475 height
= da
->GetHeight ();
476 width
= da
->GetWidth ();
478 Color
*col
= da
->GetOutlineColor ();
479 if (col
->a
!= 0x00) {
485 height
= width
= 6.0;
490 fprintf (stderr
, "Stroke::HitTest()\n");
491 fprintf (stderr
, "\tInput points:\n");
493 for (int i
= 0; i
< stylusPoints
->GetCount (); i
++) {
494 sp
= stylusPoints
->GetValueAt (i
)->AsStylusPoint ();
496 fprintf (stderr
, "\t\tPoint: (%f, %f)\n", sp
->GetX (), sp
->GetY ());
499 fprintf (stderr
, "\tStroke points:\n");
501 for (int i
= 0; i
< myStylusPoints
->GetCount (); i
++) {
502 sp
= myStylusPoints
->GetValueAt (i
)->AsStylusPoint ();
504 fprintf (stderr
, "\t\tPoint: (%f, %f)\n", sp
->GetX (), sp
->GetY ());
507 if (!GetBounds ().IntersectsWith (stylusPoints
->GetBounds ()))
510 /* test the beginning endcap */
511 sp
= myStylusPoints
->GetValueAt (0)->AsStylusPoint ();
513 if (HitTestEndcap (Point (sp
->GetX (), sp
->GetY ()),
514 width
, height
, stylusPoints
)) {
516 fprintf (stderr
, "\tA point matched the beginning endcap\n");
521 /* test all the interior line segments */
522 StylusPoint
*prev_point
= sp
;
523 for (int i
= 1; i
< myStylusPoints
->GetCount (); i
++) {
524 sp
= myStylusPoints
->GetValueAt (i
)->AsStylusPoint ();
526 if (HitTestSegment (Point (prev_point
->GetX (), prev_point
->GetY ()),
527 Point (sp
->GetX (), sp
->GetY ()),
528 width
, height
, stylusPoints
)) {
530 fprintf (stderr
, "\tA point matched an interior line segment\n");
536 /* the the ending endcap */
537 if (myStylusPoints
->GetCount () > 1) {
538 sp
= myStylusPoints
->GetValueAt (myStylusPoints
->GetCount () - 1)->AsStylusPoint ();
540 if (HitTestEndcap (Point (sp
->GetX (), sp
->GetY ()),
541 width
, height
, stylusPoints
)) {
543 fprintf (stderr
, "\tA point matched the ending endcap\n");
550 fprintf (stderr
, "\tso sad, no points intersected...\n");
557 Stroke::AddStylusPointToBounds (StylusPoint
*stylus_point
, const Rect
&bounds
)
559 DrawingAttributes
*da
= GetDrawingAttributes ();
560 double height
, width
;
563 height
= da
->GetHeight ();
564 width
= da
->GetWidth ();
566 Color
*col
= da
->GetOutlineColor ();
567 if (col
->a
!= 0x00) {
572 height
= width
= 6.0;
575 return bounds
.Union (Rect (stylus_point
->GetX () - width
/ 2,
576 stylus_point
->GetY () - height
/ 2,
581 Stroke::ComputeBounds ()
585 StylusPointCollection
*spc
= GetStylusPoints ();
589 for (int i
= 0; i
< spc
->GetCount (); i
++)
590 bounds
= AddStylusPointToBounds (spc
->GetValueAt (i
)->AsStylusPoint (), bounds
);
594 Stroke::OnCollectionChanged (Collection
*col
, CollectionChangedEventArgs
*args
)
598 if (col
!= GetStylusPoints ()) {
599 DependencyObject::OnCollectionChanged (col
, args
);
605 switch (args
->GetChangedAction ()) {
606 case CollectionChangedActionAdd
:
607 // add previous point to dirty
608 if (args
->GetIndex() > 0)
609 dirty
= AddStylusPointToBounds (col
->GetValueAt (args
->GetIndex() - 1)->AsStylusPoint (), dirty
);
611 // add new point to dirty
612 dirty
= AddStylusPointToBounds (args
->GetNewItem()->AsStylusPoint (), dirty
);
614 // add next point to dirty
615 if (args
->GetIndex() + 1 < col
->GetCount ())
616 dirty
= AddStylusPointToBounds (col
->GetValueAt (args
->GetIndex() + 1)->AsStylusPoint (), dirty
);
618 // update official bounds
619 bounds
= bounds
.Union (dirty
);
621 case CollectionChangedActionRemove
:
622 case CollectionChangedActionReplace
:
623 case CollectionChangedActionCleared
:
625 dirty
= dirty
.Union (old_bounds
.Union (bounds
));
627 case CollectionChangedActionClearing
:
628 // nothing needed here.
632 NotifyListenersOfPropertyChange (Stroke::StylusPointsProperty
, NULL
);
636 Stroke::OnCollectionItemChanged (Collection
*col
, DependencyObject
*obj
, PropertyChangedEventArgs
*args
)
638 if (col
!= GetStylusPoints ()) {
639 DependencyObject::OnCollectionItemChanged (col
, obj
, args
);
647 dirty
= old_bounds
.Union (bounds
);
649 NotifyListenersOfPropertyChange (Stroke::StylusPointsProperty
, NULL
);
653 Stroke::OnPropertyChanged (PropertyChangedEventArgs
*args
, MoonError
*error
)
655 if (args
->GetProperty ()->GetOwnerType() != Type::STROKE
) {
656 DependencyObject::OnPropertyChanged (args
, error
);
659 if (args
->GetId () == Stroke::DrawingAttributesProperty
) {
663 NotifyListenersOfPropertyChange (args
, error
);
667 Stroke::OnSubPropertyChanged (DependencyProperty
*prop
, DependencyObject
*obj
, PropertyChangedEventArgs
*subobj_args
)
669 if (prop
->GetId () == Stroke::DrawingAttributesProperty
) {
670 if (subobj_args
->GetId () == DrawingAttributes::WidthProperty
||
671 subobj_args
->GetId () == DrawingAttributes::HeightProperty
||
672 subobj_args
->GetId () == DrawingAttributes::OutlineColorProperty
) {
677 DependencyObject::OnSubPropertyChanged (prop
, obj
, subobj_args
);
686 StrokeCollection::CanAdd (Value
*value
)
688 // We skip DependencyObjectCollection::CanAdd because that one
689 // mandates 1 parent per DO, which strokes violate.
690 return Collection::CanAdd (value
) && !Contains (value
);
694 StrokeCollection::AddedToCollection (Value
*value
, MoonError
*error
)
696 DependencyObject
*obj
= value
->AsDependencyObject ();
698 obj
->SetSurface (GetSurface ());
699 obj
->SetParent (this, error
);
700 obj
->AddPropertyChangeListener (this);
702 // Bypass DependencyObjectCollection::AddedToCollection(), we
703 // are handling everything it would normally do. Also Clear()
704 // the MoonError because we ignore any errors from
705 // SetLogicalParent() since we'll only get an error if the
706 // stroke already has a logical parent (which is OK for
711 return Collection::AddedToCollection (value
, error
);
715 StrokeCollection::GetBounds ()
717 Rect r
= Rect (0, 0, 0, 0);
719 for (guint i
= 0; i
< array
->len
; i
++)
720 r
= r
.Union (((Value
*) array
->pdata
[i
])->AsStroke ()->GetBounds ());
726 StrokeCollection::HitTest (StylusPointCollection
*stylusPoints
)
728 StrokeCollection
*result
= new StrokeCollection ();
730 if (stylusPoints
->GetCount () == 0)
733 for (guint i
= 0; i
< array
->len
; i
++) {
734 Stroke
*s
= ((Value
*) array
->pdata
[i
])->AsStroke ();
736 if (s
->HitTest(stylusPoints
))
744 drawing_attributes_quick_render (cairo_t
*cr
, double thickness
, Color
*color
, StylusPointCollection
*collection
)
749 if (collection
->GetCount () == 0)
752 sp
= collection
->GetValueAt (0)->AsStylusPoint ();
756 cairo_move_to (cr
, x
, y
);
758 if (collection
->GetCount () > 1) {
759 for (int i
= 1; i
< collection
->GetCount (); i
++) {
760 sp
= collection
->GetValueAt (i
)->AsStylusPoint ();
764 cairo_line_to (cr
, x
, y
);
767 cairo_line_to (cr
, x
, y
);
771 cairo_set_source_rgba (cr
, color
->r
, color
->g
, color
->b
, color
->a
);
773 cairo_set_source_rgba (cr
, 0.0, 0.0, 0.0, 1.0);
775 cairo_set_line_width (cr
, thickness
);
780 drawing_attributes_normal_render (cairo_t
*cr
, double width
, double height
, Color
*color
, Color
*outline
, StylusPointCollection
*collection
)
782 // FIXME: use cairo_stroke_to_path once available
783 // until then draw bigger with the outline color and smaller with the inner color
784 drawing_attributes_quick_render (cr
, height
+ 4.0, outline
, collection
);
785 drawing_attributes_quick_render (cr
, (height
> 4.0) ? height
- 2.0 : 2.0, color
, collection
);
789 DrawingAttributes::Render (cairo_t
*cr
, StylusPointCollection
*collection
)
794 double height
= GetHeight ();
795 double width
= GetWidth ();
796 Color
*color
= GetColor ();
797 Color
*outline
= GetOutlineColor ();
799 // we can render very quickly if the pen is round, i.e. Width==Height (circle)
800 // and when no OutlineColor are specified (e.g. NULL, transparent)
801 if ((!outline
|| outline
->a
== 0x00) && (height
== width
)) {
802 drawing_attributes_quick_render (cr
, height
, color
, collection
);
803 // TODO - we could add another fast-path in the case where height!=width and without an outline
804 // in this case we would need a scaling transform (for the pen) and adjust the coordinates
806 drawing_attributes_normal_render (cr
, width
, height
, color
, outline
, collection
);
811 DrawingAttributes::RenderWithoutDrawingAttributes (cairo_t
*cr
, StylusPointCollection
*collection
)
813 // default values that (seems to) match the output when no DrawingAttributes are specified
814 drawing_attributes_quick_render (cr
, 2.0, NULL
, collection
);
822 InkPresenter::InkPresenter ()
824 SetObjectType (Type::INKPRESENTER
);
828 InkPresenter::PostRender (cairo_t
*cr
, Region
*region
, bool front_to_back
)
830 // render our chidren if not in front to back mode
831 if (!front_to_back
) {
832 VisualTreeWalker walker
= VisualTreeWalker (this, ZForward
);
833 while (UIElement
*child
= walker
.Step ())
834 child
->DoRender (cr
, region
);
837 cairo_set_matrix (cr
, &absolute_xform
);
838 cairo_set_line_cap (cr
, CAIRO_LINE_CAP_ROUND
);
839 cairo_set_line_join (cr
, CAIRO_LINE_JOIN_ROUND
);
841 StrokeCollection
*strokes
= GetStrokes ();
842 // for each stroke in collection
843 for (int i
= 0; i
< strokes
->GetCount (); i
++) {
844 Stroke
*stroke
= strokes
->GetValueAt (i
)->AsStroke ();
845 DrawingAttributes
*da
= stroke
->GetDrawingAttributes ();
846 StylusPointCollection
*spc
= stroke
->GetStylusPoints ();
849 da
->Render (cr
, spc
);
851 DrawingAttributes::RenderWithoutDrawingAttributes (cr
, spc
);
854 stroke
->ResetDirty ();
857 // Chain up in front_to_back mode since we've alread rendered content
858 UIElement::PostRender (cr
, region
, true);
863 InkPresenter::OnPropertyChanged (PropertyChangedEventArgs
*args
, MoonError
*error
)
865 if (args
->GetProperty ()->GetOwnerType() != Type::INKPRESENTER
) {
866 Canvas::OnPropertyChanged (args
, error
);
870 if (args
->GetId () == InkPresenter::StrokesProperty
) {
871 // be smart about invalidating only the union of the
872 // old stroke bounds and the new stroke bounds
874 if (args
->GetOldValue()) {
875 StrokeCollection
*strokes
= args
->GetOldValue()->AsStrokeCollection();
877 Invalidate (strokes
->GetBounds().Transform (&absolute_xform
));
881 if (args
->GetNewValue()) {
882 StrokeCollection
*strokes
= args
->GetNewValue()->AsStrokeCollection();
884 Invalidate (strokes
->GetBounds().Transform (&absolute_xform
));
891 NotifyListenersOfPropertyChange (args
, error
);
895 InkPresenter::OnCollectionChanged (Collection
*col
, CollectionChangedEventArgs
*args
)
899 if (col
!= GetStrokes ()) {
900 Canvas::OnCollectionChanged (col
, args
);
904 switch (args
->GetChangedAction()) {
905 case CollectionChangedActionAdd
:
906 stroke
= args
->GetNewItem()->AsStroke ();
907 Invalidate (stroke
->GetBounds().Transform (&absolute_xform
));
910 case CollectionChangedActionRemove
:
911 stroke
= args
->GetOldItem()->AsStroke ();
912 Invalidate (stroke
->GetOldBounds ().Transform (&absolute_xform
));
913 Invalidate (stroke
->GetBounds ().Transform (&absolute_xform
));
916 case CollectionChangedActionReplace
:
917 stroke
= args
->GetOldItem()->AsStroke ();
918 Invalidate (stroke
->GetOldBounds ().Transform (&absolute_xform
));
919 stroke
= args
->GetNewItem()->AsStroke ();
920 Invalidate (stroke
->GetBounds().Transform (&absolute_xform
));
923 case CollectionChangedActionCleared
:
924 Invalidate (render_bounds
);
925 Invalidate (((StrokeCollection
*)col
)->GetBounds().Transform (&absolute_xform
));
928 case CollectionChangedActionClearing
:
929 // nothing needed here.
935 InkPresenter::OnCollectionItemChanged (Collection
*col
, DependencyObject
*obj
, PropertyChangedEventArgs
*args
)
937 Stroke
*stroke
= (Stroke
*) obj
;
939 if (col
!= GetStrokes ()) {
940 Canvas::OnCollectionItemChanged (col
, obj
, args
);
944 Invalidate (stroke
->GetDirty ().Transform (&absolute_xform
));
949 InkPresenter::ComputeBounds ()
951 Canvas::ComputeBounds ();
953 render_bounds
= bounds
;
955 StrokeCollection
*strokes
= GetStrokes ();
959 Rect stroke_bounds
= strokes
->GetBounds ();
960 stroke_bounds
= stroke_bounds
.Transform (&absolute_xform
);
961 bounds_with_children
= bounds_with_children
.Union (stroke_bounds
);
963 render_bounds
= render_bounds
.Union (stroke_bounds
);
967 InkPresenter::GetRenderBounds ()
969 return render_bounds
;
973 InkPresenter::ShiftPosition (Point p
)
975 double dx
= p
.x
- bounds
.x
;
976 double dy
= p
.y
- bounds
.y
;
978 // need to do this after computing the delta
979 Canvas::ShiftPosition (p
);
981 render_bounds
.x
+= dx
;
982 render_bounds
.y
+= dy
;
986 stroke_get_bounds (Stroke
*stroke
, Rect
*bounds
)
988 *bounds
= stroke
->GetBounds ();
992 stroke_collection_get_bounds (StrokeCollection
*collection
, Rect
*bounds
)
994 *bounds
= collection
->GetBounds ();