/// <summary> /// Sets the item's position according to its tikzitem's value /// </summary> public override bool AdjustPosition(double Resolution) { Rect r = new Rect(0, 0, 0, 0); bool hasone = false; foreach (OverlayShape o in children) { o.AdjustPosition(Resolution); Rect rr = o.View.GetBB(); if (hasone) r.Union(rr); else { r = rr; hasone = true; } } if (hasone) { r.Inflate(20, 20); //r = new Rect(10, 10, 100, 100); View.SetSize( r.Width, r.Height); View.SetPosition(r.X, r.Y); return true; } else return false; }
protected Rect GetBoundsFromChildren() { Rect bounds = new Rect(new Size(10, 10)); // default if there are no children. Plot2DItem child; for (int i = 0; i < plotItems.Count; ++i) { child = plotItems[i]; if (i == 0) bounds = child.PaddedBounds; else bounds.Union(child.PaddedBounds); } return bounds; }
public override Rect TransformBounds (Rect rect) { Point p1 = new Point (rect.Left, rect.Top); Point p2 = new Point (rect.Right, rect.Top); Point p3 = new Point (rect.Left, rect.Bottom); Point p4 = new Point (rect.Right, rect.Bottom); Rect r1 = new Rect (Transform (p1), Transform (p2)); Rect r2 = new Rect (Transform (p3), Transform (p4)); r1.Union (r2); return r1; }
protected override Size MeasureOverride(Size availableSize) { var bounds = new Rect(); var midPoint = new Point(0, 0); var arc = new Arc(midPoint, this.MinAngle, this.MaxAngle, this.ReservedSpace, this.IsDirectionReversed); for (int i = 0; i < this.AllTicks.Count; i++) { var tick = this.AllTicks[i]; var text = this.AllTexts[i]; var angle = TickHelper.ToAngle(tick, this.Minimum, this.Maximum, arc); var point = arc.GetPoint(angle); var textPosition = new TextPosition(text, new TextPositionOptions(this.TextOrientation, angle), point, angle); bounds.Union(textPosition.TransformedBounds); } var points = new[] { bounds.TopLeft, bounds.TopRight, bounds.BottomRight, bounds.BottomLeft }; this.TextSpace = 2 * points.Max(p => (p - midPoint).Length); return bounds.Size; }
private void setupSelectionAdorner() { var selectedBound = new Rect(); bool firstShape = true; foreach (Stroke stroke in referencedStrokes) { addStylingToStroke(stroke); var bounds = stroke.GetBounds(); if (firstShape) { selectedBound.X = bounds.Left - 5; selectedBound.Y = bounds.Top - 5; firstShape = false; } var points = new Point[] { new Point(bounds.Left, bounds.Top), new Point(bounds.Right, bounds.Bottom) }; foreach (Point point in points) selectedBound.Union(point); } this.Width = selectedBound.Width + 10; this.Height = selectedBound.Height + 10; Canvas.SetLeft(this, selectedBound.X); Canvas.SetTop(this, selectedBound.Y); }
private Rect AddToRegion(KeyValuePair<DrawingVisual, uint> entry, Rect region) { Rect compRect = entry.Key.ContentBounds; if (compRect == Rect.Empty) return region; TranslateTransform transform = entry.Key.Transform as TranslateTransform; if (transform != null) { double x = transform.X; double y = transform.Y; compRect.Offset(new Vector(x, y)); } if (region.Width == 0) return compRect; else { region.Union(compRect); return region; } }
public void Union() { Rect r; // fully contained r = new Rect(0, 0, 50, 50); r.Union(new Rect(10, 10, 10, 10)); Assert.AreEqual(new Rect(0, 0, 50, 50), r); // crosses top side r = new Rect(0, 0, 50, 50); r.Union(new Rect(5, -5, 10, 10)); Assert.AreEqual(new Rect(0, -5, 50, 55), r); // crosses right side r = new Rect(0, 0, 50, 50); r.Union(new Rect(5, 5, 50, 10)); Assert.AreEqual(new Rect(0, 0, 55, 50), r); // crosses bottom side r = new Rect(0, 0, 50, 50); r.Union(new Rect(5, 5, 10, 50)); Assert.AreEqual(new Rect(0, 0, 50, 55), r); // crosses left side r = new Rect(0, 0, 50, 50); r.Union(new Rect(-5, 5, 10, 10)); Assert.AreEqual(new Rect(-5, 0, 55, 50), r); // completely outside (top) r = new Rect(0, 0, 50, 50); r.Union(new Rect(5, -5, 1, 1)); Assert.AreEqual(new Rect(0, -5, 50, 55), r); // completely outside (right) r = new Rect(0, 0, 50, 50); r.Union(new Rect(75, 5, 1, 1)); Assert.AreEqual(new Rect(0, 0, 76, 50), r); // completely outside (bottom) r = new Rect(0, 0, 50, 50); r.Union(new Rect(5, 75, 1, 1)); Assert.AreEqual(new Rect(0,0, 50, 76), r); // completely outside (left) r = new Rect(0, 0, 50, 50); r.Union(new Rect(-25, 5, 1, 1)); Assert.AreEqual(new Rect(-25, 0, 75, 50), r); }
/// <summary> /// Determine the size of the union of the bounds of the positioned and rotated child elements. /// </summary> /// <param name="availableSize"></param> /// <returns></returns> protected override Size MeasureOverride(Size availableSize) { Part part = Diagram.FindAncestor<Part>(this); if (part == null) return new Size(); if (!part.IsMeasuringArranging) return new Size(); Link link = part as Link; if (link == null) { Adornment ad = part as Adornment; if (ad != null) link = ad.AdornedPart as Link; if (link == null) return new Size(); } //Diagram.Debug(" LinkPanelM- " + (link.Data != null ? link.Data.ToString() : "")); Shape stroke = this.Path; // may be null link.Path = stroke; // the Link caches what the Path really is Route route = link.Route; Rect routeBounds = route.RouteBounds; // in model coordinates Rect linkBounds = new Rect(0, 0, routeBounds.Width, routeBounds.Height); // will include all decorations childrenBounds = new List<Rect>(); // in local coordinates if (stroke != null) { stroke.Measure(Geo.Unlimited); Size sz = stroke.DesiredSize; double thick = stroke.StrokeThickness; linkBounds.Union(new Rect(-thick/2, -thick/2, sz.Width, sz.Height)); // local coordinates childrenBounds.Add(new Rect(0, 0, linkBounds.Width, linkBounds.Height)); } IList<Point> pts = (List<Point>)route.Points; int nPoints = pts.Count; foreach (UIElement e in this.Children) { if (e == stroke) continue; // already measured the stroke, above Path otherpath = e as Path; if (otherpath != null && !GetIsLinkShape(e)) otherpath = null; if (otherpath != null) { // IsLinkShape="True" otherpath.Data = Adornment.Copy(route.DefiningGeometry); } e.Measure(Geo.Unlimited); //if (e.GetType().Name.Contains("Expander")) Diagram.Debug(e.ToString() + " measured: " + Diagram.Str(e.DesiredSize)); if (nPoints < 2) continue; Size sz = e.DesiredSize; int index = GetIndex(e); double frac = ComputeFraction(GetFraction(e)); Spot align = GetAlignment(e); if (align.IsNoSpot) align = Spot.Center; LabelOrientation orient = GetOrientation(e); Point eltpt; // local coordinates if (index < -nPoints || index >= nPoints) { // beyond range? assume at the MidPoint, with the MidAngle Point mid = route.MidPoint; if (this.Implementation == LinkPanelImplementation.Stretch) { Point p0 = pts[0]; Point pn = pts[nPoints-1]; sz.Width = Math.Sqrt(Geo.DistanceSquared(pn, p0)); } double segangle = route.MidAngle; // maybe rotate the label double labelangle = 0; if (orient != LabelOrientation.None) { labelangle = ComputeAngle(e, orient, segangle); link.SetAngle(e, labelangle, align); } // maybe the alignment point is away from the line eltpt = new Point(mid.X - routeBounds.X, mid.Y - routeBounds.Y); // local coordinates Point offset = ComputeOffset(e, index, segangle, sz, labelangle); eltpt = Geo.Add(eltpt, offset); } else { // on a particular segment, given by Index, at a point given by Fraction // negative index means start from last point, going "backwards" Point a, b; if (index >= 0) { a = pts[index]; b = (index < nPoints-1) ? pts[index+1] : a; } else { int i = nPoints + index; // remember that index is negative here a = pts[i]; b = (i > 0) ? pts[i-1] : a; } // compute the fractional point along the line, in local coordinates eltpt = new Point(a.X + (b.X-a.X)*frac - routeBounds.X, a.Y + (b.Y-a.Y)*frac - routeBounds.Y); double segangle = (index >= 0 ? Geo.GetAngle(a, b) : Geo.GetAngle(b, a)); // maybe rotate the label double labelangle = 0; if (orient != LabelOrientation.None) { labelangle = ComputeAngle(e, orient, segangle); Arrowhead toArrow = GetToArrow(e); Arrowhead fromArrow = GetFromArrow(e); if (toArrow == Arrowhead.None && fromArrow == Arrowhead.None) { link.SetAngle(e, labelangle, align); } else { double arrowScale = 0.0; if (toArrow != Arrowhead.None) arrowScale = GetToArrowScale(e); else arrowScale = GetFromArrowScale(e); link.SetAngleAndScale(e, labelangle, align, new Size(arrowScale, arrowScale)); } } // maybe the alignment point is away from the line Point offset = ComputeOffset(e, index, segangle, sz, labelangle); eltpt = Geo.Add(eltpt, offset); } if (otherpath != null) { // IsLinkShape="True" double thick = otherpath.StrokeThickness; linkBounds.Union(new Rect(-thick/2, -thick/2, sz.Width, sz.Height)); // local coordinates childrenBounds.Add(new Rect(0, 0, linkBounds.Width, linkBounds.Height)); } else { Rect cb = align.RectForPoint(eltpt, sz); childrenBounds.Add(cb); // local coordinates linkBounds.Union(new Rect(cb.X, cb.Y, cb.Width, cb.Height)); // local coordinates } } // if this panel is the "whole" link, update the link's Bounds if (link.VisualElement == this) { link.Bounds = new Rect(routeBounds.X, routeBounds.Y, linkBounds.Width, linkBounds.Height); // convert to model coordinates } //Diagram.Debug(" LinkPanelM+ " + (link.Data != null ? link.Data.ToString() : "") + " " + Diagram.Str(routeBounds) + Diagram.Str(linkBounds)); return new Size(routeBounds.Width, routeBounds.Height); }
/// <summary> /// Union - Return the result of the union of rect and point. /// </summary> public static Rect Union(Rect rect, Point point) { rect.Union(new Rect(point, point)); return(rect); }
protected override Size MeasureOverride(Size availableSize) { var rect = new Rect(); var arc = new Arc(new Point(0, 0), this.MinAngle, this.MaxAngle, this.ReservedSpace / 2 + this.TickLength, this.IsDirectionReversed); rect.Union(arc.GetPoint(arc.Start)); var a = TickHelper.ToAngle(this.Value, this.Minimum, this.Maximum, arc); rect.Union(arc.GetPoint(a)); foreach (var p in arc.GetQuadrants(arc.Start, a)) { rect.Union(p); } return rect.Size; }
protected override Size MeasureOverride( Size availableSize ) { DateTime calcBeginDate = DateTime.MaxValue; DateTime calcEndDate = DateTime.MinValue; foreach( UIElement child in InternalChildren ) { DateTime date = GetDate( child ); DateTime dateEnd = GetDateEnd( child ); if( date < calcBeginDate ) { calcBeginDate = date; } if( date > calcEndDate ) { calcEndDate = date; } if( dateEnd > calcEndDate ) { calcEndDate = dateEnd; } } if( BeginDate == DateTime.MinValue ) { BeginDate = calcBeginDate; } if( EndDate == DateTime.MinValue ) { EndDate = calcEndDate; } foreach( UIElement child in InternalChildren ) { DateTime date = GetDate( child ); DateTime dateEnd = GetDateEnd( child ); Size childSize = availableSize; if( dateEnd > DateTime.MinValue && dateEnd > date ) { if( Orientation == Orientation.Horizontal ) { if( UnitTimeSpan != TimeSpan.Zero && UnitSize > 0 ) { double size = ( double )( dateEnd.Ticks - date.Ticks ) / ( double )UnitTimeSpan.Ticks; childSize.Width = size * UnitSize; } else if( !double.IsPositiveInfinity( availableSize.Width ) ) { // width is DateEnd - Date childSize.Width = CalculateTimelineOffset( dateEnd, availableSize.Width ) - CalculateTimelineOffset( date, availableSize.Width ); } } else { if( UnitTimeSpan != TimeSpan.Zero && UnitSize > 0 ) { double size = ( double )( dateEnd.Ticks - date.Ticks ) / ( double )UnitTimeSpan.Ticks; childSize.Height = size * UnitSize; } else if( !double.IsPositiveInfinity( availableSize.Height ) ) { // height is DateEnd - Date childSize.Height = CalculateTimelineOffset( dateEnd, availableSize.Height ) - CalculateTimelineOffset( date, availableSize.Height ); } } } child.Measure( childSize ); } Size newAvailableSize = new Size( availableSize.Width, availableSize.Height ); if( UnitTimeSpan != TimeSpan.Zero && UnitSize > 0 ) { double size = ( double )( EndDate.Ticks - BeginDate.Ticks ) / ( double )UnitTimeSpan.Ticks; if( Orientation == Orientation.Horizontal ) { newAvailableSize.Width = size * UnitSize; } else { newAvailableSize.Height = size * UnitSize; } } Size desiredSize = new Size(); if( ( Orientation == Orientation.Vertical && double.IsPositiveInfinity( newAvailableSize.Height ) ) || ( Orientation == Orientation.Horizontal && double.IsPositiveInfinity( newAvailableSize.Width ) ) ) { // Our panel cannot layout items when we have positive infinity in the layout direction // so defer to arrange pass. _visibleElements = null; } else { LayoutItems( InternalChildren, newAvailableSize ); Rect desiredRect = new Rect(); foreach( DateElement child in _visibleElements ) { desiredRect.Union( child.PlacementRectangle ); } if( Orientation == Orientation.Horizontal ) { desiredSize.Width = newAvailableSize.Width; desiredSize.Height = desiredRect.Size.Height; } else { desiredSize.Width = desiredRect.Size.Width; desiredSize.Height = newAvailableSize.Height; } } if( IsScrolling ) { Size viewport = new Size( availableSize.Width, availableSize.Height ); Size extent = new Size( desiredSize.Width, desiredSize.Height ); Vector offset = new Vector( Math.Max( 0, Math.Min( _offset.X, extent.Width - viewport.Width ) ), Math.Max( 0, Math.Min( _offset.Y, extent.Height - viewport.Height ) ) ); SetScrollingData( viewport, extent, offset ); desiredSize.Width = Math.Min( desiredSize.Width, availableSize.Width ); desiredSize.Height = Math.Min( desiredSize.Height, availableSize.Height ); _physicalViewport = availableSize; } return desiredSize; }
internal void SaveWorkspaceAsImage(string path) { var initialized = false; var bounds = new Rect(); double minX = 0.0, minY = 0.0; var dragCanvas = WpfUtilities.ChildOfType<DragCanvas>(this); var childrenCount = VisualTreeHelper.GetChildrenCount(dragCanvas); for (int index = 0; index < childrenCount; ++index) { var child = VisualTreeHelper.GetChild(dragCanvas, index); var firstChild = VisualTreeHelper.GetChild(child, 0); switch (firstChild.GetType().Name) { case "NodeView": case "NoteView": case "AnnotationView": break; // Until we completely removed InfoBubbleView (or fixed its broken // size calculation), we will not be including it in our size // calculation here. This means that the info bubble, if any, will // still go beyond the boundaries of the final PNG file. I would // prefer not to add this hack here as it introduces multiple issues // (including NaN for Grid inside the view and the fix would be too // ugly to type in). Suffice to say that InfoBubbleView is not // included in the size calculation for screen capture (work-around // should be obvious). // // case "InfoBubbleView": // child = WpfUtilities.ChildOfType<Grid>(child); // break; // We do not take anything other than those above // into consideration when the canvas size is measured. default: continue; } // Determine the smallest corner of all given visual elements on the // graph. This smallest top-left corner value will be useful in making // the offset later on. // var childBounds = VisualTreeHelper.GetDescendantBounds(child as Visual); minX = childBounds.X < minX ? childBounds.X : minX; minY = childBounds.Y < minY ? childBounds.Y : minY; childBounds.X = (double)(child as Visual).GetValue(Canvas.LeftProperty); childBounds.Y = (double)(child as Visual).GetValue(Canvas.TopProperty); if (initialized) { bounds.Union(childBounds); } else { initialized = true; bounds = childBounds; } } // Nothing found in the canvas, bail out. if (!initialized) return; // Add padding to the edge and make them multiples of two (pad 10px on each side). bounds.Width = 20 + ((((int)Math.Ceiling(bounds.Width)) + 1) & ~0x01); bounds.Height = 20 + ((((int)Math.Ceiling(bounds.Height)) + 1) & ~0x01); var currentTransformGroup = WorkspaceElements.RenderTransform as TransformGroup; WorkspaceElements.RenderTransform = new TranslateTransform(10.0 - bounds.X - minX, 10.0 - bounds.Y - minY); WorkspaceElements.UpdateLayout(); var rtb = new RenderTargetBitmap(((int)bounds.Width), ((int)bounds.Height), 96, 96, PixelFormats.Default); rtb.Render(WorkspaceElements); WorkspaceElements.RenderTransform = currentTransformGroup; try { using (var stm = System.IO.File.Create(path)) { // Encode as PNG format var pngEncoder = new PngBitmapEncoder(); pngEncoder.Frames.Add(BitmapFrame.Create(rtb)); pngEncoder.Save(stm); } } catch (Exception) { } }
/// <summary> /// Expand the content area to fit the diagram-elements. /// </summary> private void ExpandContent() { double smallestX = 0; double smallestY = 0; var contentRect = new Rect(0, 0, 0, 0); foreach (DiagramElement diagramElement in Controller.DiagramElements) { if (diagramElement.TopLeft.X < smallestX) { smallestX = diagramElement.TopLeft.X; } if (diagramElement.TopLeft.Y < smallestY) { smallestY = diagramElement.TopLeft.Y; } contentRect.Union(new Rect(diagramElement.TopLeft.X, diagramElement.TopLeft.Y, diagramElement.Width, diagramElement.Height)); } // Translate all diagram-elements so they are in positive space. smallestX = Math.Abs(smallestX); smallestY = Math.Abs(smallestY); if (smallestX > 0.01 || smallestY > 0.01) { Controller.DiagramElements.ToList().ForEach(element => element.AdjustCoordinatesAfterCanvasExpansion(smallestX, smallestY)); } Diagram.ContentWidth = contentRect.Width; Diagram.ContentHeight = contentRect.Height; }
/// <summary> /// Arranges the content of a <see cref="StiZoomableCanvas"/> element. /// </summary> /// <param name="finalSize">The size that this <see cref="StiZoomableCanvas"/> element should use to arrange its child elements.</param> /// <returns>A <see cref="Size"/> that represents the arranged size of this <see cref="StiZoomableCanvas"/> element and its descendants.</returns> protected override Size ArrangeOverride(Size finalSize) { bool applyTransform = ApplyTransform; Point offset = applyTransform ? new Point() : Offset; double scale = applyTransform ? 1.0 : Scale; ChildrenExtent = Rect.Empty; foreach (UIElement child in InternalChildren) { if (child != null) { // Get bounds information from the element. Rect bounds = new Rect(Canvas.GetLeft(child).GetValueOrDefault(), Canvas.GetTop(child).GetValueOrDefault(), child.DesiredSize.Width / scale, child.DesiredSize.Height / scale); // If we are maintaining our own spatial wrapper then update its bounds. if (PrivateIndex != null) { int index = IndexFromContainer(child); //TODO Rect oldBounds = PrivateIndex[index]; const double tolerance = .0001; // The exact values during arrange can vary slightly. if (Math.Abs(oldBounds.Top - bounds.Top) > tolerance || Math.Abs(oldBounds.Left - bounds.Left) > tolerance || Math.Abs(oldBounds.Width - bounds.Width) > tolerance || Math.Abs(oldBounds.Height - bounds.Height) > tolerance) { PrivateIndex[index] = bounds; } } // Update the children extent for scrolling. ChildrenExtent.Union(bounds); // So far everything has been in canvas coordinates. Here we adjust the result for the final call to Arrange. bounds.X *= scale; bounds.X -= offset.X; bounds.Y *= scale; bounds.Y -= offset.Y; bounds.Width = Math.Ceiling(bounds.Width*scale); bounds.Height = Math.Ceiling(bounds.Height*scale); // WPF Arrange will crash if the values are too large. bounds.X = bounds.X.AtLeast(Single.MinValue / 2); bounds.Y = bounds.Y.AtLeast(Single.MinValue / 2); bounds.Width = bounds.Width.AtMost(Single.MaxValue); bounds.Height = bounds.Height.AtMost(Single.MaxValue); child.Arrange(bounds); } } InvalidateExtent(); return finalSize; }
public Bars(Plot2D plot2D, ILArray<double> barStart, ILArray<double> barEnd, ILArray<double> barPosition, ILArray<double> barThickness, BarType barType) { this.plot2D = plot2D; int n = barStart.Length; rectangles = new List<Path>(); Geometry geometry; Path rectangle; Rect bounds = new Rect(); Rect rectangleBounds = new Rect(); for (int i = 0; i < n; ++i) { rectangle = new Path(); rectangles.Add(rectangle); if (barType == BarType.Horizontal) { rectangleBounds = new Rect(new Point(barStart.GetValue(i), barPosition.GetValue(i) + barThickness.GetValue(i) / 2), new Point(barEnd.GetValue(i), barPosition.GetValue(i) - barThickness.GetValue(i) / 2)); } else { rectangleBounds = new Rect(new Point(barPosition.GetValue(i) + barThickness.GetValue(i) / 2, barStart.GetValue(i)), new Point(barPosition.GetValue(i) - barThickness.GetValue(i) / 2, barEnd.GetValue(i))); } geometry = new RectangleGeometry(rectangleBounds); rectangle.Data = geometry; geometry.Transform = plot2D.GraphToCanvas; rectangle.Fill = (Brush)(this.GetValue(FillProperty)); rectangle.StrokeThickness = (double)(this.GetValue(StrokeThicknessProperty)); rectangle.Stroke = (Brush)(this.GetValue(StrokeProperty)); if (i == 0) bounds = rectangleBounds; else { bounds.Union(rectangleBounds); } } Bounds = bounds; SetBindings(); }
protected override Size MeasureOverride(Size availableSize) { if (Children == null || Children.Count < 1) { return new Size (0, 0); } var resultingRect = new Rect (); object accumulatedState = null; try { var count = Children.Count; for (var iter = 0; iter < count; ++iter) { var child = Children[iter]; child.Measure (availableSize); var desiredSize = child.DesiredSize; var args = new LayoutItemEventArgs { LayoutItemState = LayoutItemState.IsMeasuring, UiElement = child, AccumulateState = accumulatedState, State = GetUIElementState (child), AvailableSize = availableSize, DesiredSize = desiredSize, Count = count, Index = iter, Transform = child.RenderTransform, }; OnItemLayout (args); accumulatedState = args.AccumulateState; var childRect = (args.Transform ?? s_identityTransform).TransformBounds (new Rect (0, 0, desiredSize.Width, desiredSize.Height)); resultingRect.Union (childRect); } return availableSize.Merge (resultingRect.ToSize ()); } finally { Common.Dispose (accumulatedState); } }
private Rect CoerceVisible(Rect newVisible) { if (Plotter == null) { return newVisible; } bool isDefaultValue = newVisible == (Rect)VisibleProperty.DefaultMetadata.DefaultValue; if (isDefaultValue) { newVisible = Rect.Empty; } if (isDefaultValue && IsFittedToView) { Rect bounds = Rect.Empty; foreach (var g in Plotter.Children) { var graph = g as DependencyObject; if (graph != null) { var uiElement = g as UIElement; if (uiElement == null || (uiElement != null && uiElement.Visibility == Visibility.Visible)) { bounds.Union((Rect)graph.GetValue(ViewportElement2D.ContentBoundsProperty)); } } } Rect viewportBounds = bounds; if (!bounds.IsEmpty) { bounds = bounds.DataToViewport(transform); } //Rect defaultRect = (Rect)VisibleProperty.DefaultMetadata.DefaultValue; //if (bounds.Width.IsInfinite()) // bounds.Width = defaultRect.Width; //if (bounds.Height.IsInfinite()) // bounds.Height = defaultRect.Height; //if (bounds.X.IsInfinite()) // bounds.X = defaultRect.X; //if (bounds.Y.IsInfinite()) // bounds.Y = defaultRect.Y; if (!bounds.IsEmpty) { bounds = CoordinateUtilities.RectZoom(bounds, bounds.GetCenter(), clipToBoundsFactor); } else { bounds = (Rect)VisibleProperty.DefaultMetadata.DefaultValue; } newVisible.Union(bounds); } if (newVisible.IsEmpty) { newVisible = (Rect)VisibleProperty.DefaultMetadata.DefaultValue; } else if (newVisible.Width == 0 || newVisible.Height == 0) { Rect defRect = (Rect)VisibleProperty.DefaultMetadata.DefaultValue; Size size = newVisible.Size; Point loc = newVisible.Location; if (newVisible.Width == 0) { size.Width = defRect.Width; loc.X -= size.Width / 2; } if (newVisible.Height == 0) { size.Height = defRect.Height; loc.Y -= size.Height / 2; } newVisible = new Rect(loc, size); } newVisible = ApplyRestrictions(Visible, newVisible); // applying transform's data domain restriction if (!transform.DataTransform.DataDomain.IsEmpty) { var newDataRect = newVisible.ViewportToData(transform); newDataRect = Rect.Intersect(newDataRect, transform.DataTransform.DataDomain); newVisible = newDataRect.DataToViewport(transform); } if (newVisible.IsEmpty) return new Rect(0, 0, 1, 1); return newVisible; }
/// <summary> /// When overridden in a derived class, participates in rendering operations that are directed by the layout system. /// In this case the rendering draws additional line between info box and its origin vertex control, when /// the mouse is over either vertex control or its info box. /// </summary> /// <param name="drawingContext">The drawing instructions for a specific element. This context is provided to the layout system.</param> protected override void OnRender(DrawingContext drawingContext) { var element = OriginElement as FrameworkElement; if (element != null && (IsMouseOver || element.IsMouseOver)) { var mainWindow = this.GetParent<DockableGraph>(null); var gt = element.TransformToVisual(this); var elementCenter = gt.Transform(new Point(element.ActualWidth / 2, element.ActualHeight / 2)); var elementSize = new Size(element.ActualWidth, element.ActualHeight); var elementRect = new Rect(gt.Transform(new Point(0, 0)), elementSize); var thisCenter = new Point(ActualWidth / 2, ActualHeight / 2); // The total area we'll be drawing in. var totalRect = new Rect(elementRect.Location, elementRect.Size); totalRect.Union(new Rect(new Point(0, 0), new Size(ActualWidth, ActualHeight))); // Create geometry objects to work with. var elementRectangleGeometry = new RectangleGeometry(elementRect); var totalRectangleGeometry = new RectangleGeometry(totalRect); // We'll now remove the element's rectangle from where we're drawing, so that we // don't draw on top of it, but will draw on top of everything else. CombinedGeometry clipGeometry = new CombinedGeometry(GeometryCombineMode.Exclude, totalRectangleGeometry, elementRectangleGeometry); drawingContext.PushClip(clipGeometry); //var node = OriginElement.Vertex as TraceLab.Core.Experiments.ExperimentNode; //Point vertexCenter = new Point(node.Data.X, node.Data.Y); Pen solidPen = new Pen(System.Windows.Media.Brushes.LightCoral, 1.0); drawingContext.DrawLine(solidPen, elementCenter, thisCenter); // Pop off the RectangleGeometry drawingContext.Pop(); } base.OnRender(drawingContext); }
/// <summary> /// Create Gant TimeLine ranges. /// </summary> protected override Size _CreateVisualChildren(Size dimension, double rangeWidth, int rangeStepInHour) { Rect boundingRect = new Rect(); // Calculate range boundbox. Size itemSize = new Size(rangeWidth, dimension.Height); Rect itemRect = new Rect(new Point(0, 0), itemSize); DateTime currentHour; // Define start hour: "0" if _startDate in MaxValue if (_startHour.Date == DateTime.MaxValue.Date) currentHour = DateTime.MinValue; else currentHour = _startHour; int current = 0; while (current < (int)_duration) { // Create TimeLine range. DrawingVisual visualItem = _CreateVisualItem(currentHour, rangeStepInHour, itemRect); _children.Add(visualItem); boundingRect.Union(visualItem.ContentBounds); // Relocate for next element. itemRect.Offset(rangeWidth, 0); currentHour = currentHour.AddHours(rangeStepInHour); current += rangeStepInHour; } return boundingRect.Size; }
private ValidSetOfPointsCollection IsValid(List<TouchPoint2> points) { bool result = true; ValidSetOfPointsCollection sets = new ValidSetOfPointsCollection(); if (points.Count > 0) { //double minX = int.MinValue, minY = int.MinValue, maxX = int.MaxValue, maxY = int.MaxValue; Rect area = new Rect(points[0].Position, new Size(0, 0)); // Calculate the bounding box that covers all points foreach (var point in points) { if (_data.HistoryLevel > 0) { Rect location = RuleValidationHelper.GetBoundingBox(point); List<TouchPoint2> selectedPoints = new List<TouchPoint2>(); result = RuleValidationHelper.HasPreviousTouchPoints(location, point, _data.HistoryLevel, point.StartTime.AddSeconds(-_data.HistoryTimeLine), selectedPoints); if (result) // Has required number of previous touch points in selected location { foreach (var p in selectedPoints) { area.Union(p.Position); } } else { break; } } area.Union(point.Position); } // TODO: We need to implement circular area too if (result && area.Height <= _data.Height && area.Width <= _data.Width) { sets.Add(new ValidSetOfTouchPoints(points)); } } return sets; }
private void RebuildMap() { Rect canvRect = new Rect( new Point(cam.MapToScreen(new Vector3(baseMap.width + 1, 0, 0)).X, cam.MapToScreen(new Vector3(0, 0, 0)).Y), new Point(cam.MapToScreen(new Vector3(0, baseMap.height + 1, 0)).X, cam.MapToScreen(new Vector3(baseMap.width + 1, baseMap.height + 1, 0)).Y)); Tiles.Children.Clear(); int maxHeight = 0; for (int i = 0; i < baseMap.tiles.Count; i++) { for (int j = 0; j < baseMap.tiles[i].Count; j++) { int height = 0; foreach (Map.TileReference tref in baseMap.tiles[i][j]) { Tile tile = baseMap.GetTile(tref); Point loc = cam.MapToScreen(new Vector3(j, i, height)); Rect tRect = new Rect(loc.X - (64 * (tile.OriginX - tile.LeftX) + 32), loc.Y - (64 * (tile.OriginY - tile.TopY) + 32), 64 * (tile.RightX - tile.LeftX + 1), 64 * (tile.BottomY - tile.TopY + 1)); Rectangle rec = new Rectangle(); Canvas.SetLeft(rec, tRect.Left); Canvas.SetTop(rec, tRect.Top); Panel.SetZIndex(rec, i + j + height); rec.Width = tRect.Width; rec.Height = tRect.Height; rec.Fill = ((TileSets.Items[tref.TileSetIndex] as TabItem).Content as TileSetEditor).getTileBrush(tile); rec.Opacity = (height > clipZ || (height == clipZ && !tile.IsFloor)) ? .5*Math.Pow(.7,height-clipZ) : 1; Tiles.Children.Add(rec); canvRect.Union(tRect); if (tile.IsBlock) height++; } if (height > maxHeight) maxHeight = height; } } canvRect.Union(cam.MapToScreen(new Vector3(0, 0, clipZ))); canvas.Width = canvRect.Width; canvas.Height = canvRect.Height; Canvas.SetLeft(Origin, -canvRect.Left); Canvas.SetTop(Origin, -canvRect.Top); PointCollection mapBorder = new PointCollection(); mapBorder.Add(cam.MapToScreen(new Vector3(0, 0, clipZ))); mapBorder.Add(cam.MapToScreen(new Vector3(baseMap.width, 0, clipZ))); mapBorder.Add(cam.MapToScreen(new Vector3(baseMap.width, baseMap.height, clipZ))); mapBorder.Add(cam.MapToScreen(new Vector3(0, baseMap.height, clipZ))); MapEdge.Points = mapBorder; ZClip.Maximum = maxHeight+1; PreviewPaint(); }
private void Window_Loaded(object sender, RoutedEventArgs e) { var r = new Rect(); ScreenHelper.AllScreens.Select(x => x.Bounds).ToList().ForEach(x => r.Union(x)); Left = r.Left; Top = r.Top; Width = r.Width; Height = r.Height; _m.IsHooked = true; _m.MouseMove += m_MouseMove; _m.MouseUp += m_MouseUp; _m.MouseDown += m_MouseDown; _k.IsHooked = true; _k.KeyDown += (s, ev) => { if (ev.Key == Key.System || ev.Key == Key.LeftAlt || ev.Key == Key.RightAlt) { IsAltDown = true; e.Handled = true; } }; _k.KeyUp += (s, ev) => { if (ev.Key == Key.System || ev.Key == Key.LeftAlt || ev.Key == Key.RightAlt) { IsAltDown = false; e.Handled = true; } }; }
protected virtual Rect CoerceVisible(Rect newVisible) { PerformanceCounter.startStopwatch("Coercing visible"); if (Plotter == null) { return newVisible; } bool isDefaultValue = (newVisible == defaultRect); if (isDefaultValue) { newVisible = Rect.Empty; } //if (isDefaultValue && IsFittedToView) if(isDefaultValue) { Rect bounds = Rect.Empty; foreach (var g in Plotter.Children) { var graph = g as DependencyObject; if (graph != null) { var uiElement = g as UIElement; if (uiElement == null || (uiElement != null && uiElement.Visibility == Visibility.Visible)) { bounds.Union((Rect)graph.GetValue(ViewportElement2D.ContentBoundsProperty)); } } } Rect viewportBounds = bounds; if (!bounds.IsEmpty) { bounds = CoordinateUtilities.RectZoom(bounds, bounds.GetCenter(), clipToBoundsFactor); } else { bounds = defaultRect; } newVisible.Union(bounds); } if (newVisible.IsEmpty) { newVisible = defaultRect; } else if (newVisible.Width == 0 || newVisible.Height == 0) { Rect defRect = defaultRect; Size size = new Size(newVisible.Width,newVisible.Height); Point loc = new Point(newVisible.X,newVisible.Y); if (newVisible.Width == 0) { size.Width = defRect.Width; loc.X -= size.Width / 2; } if (newVisible.Height == 0) { size.Height = defRect.Height; loc.Y -= size.Height / 2; } newVisible = new Rect(loc, size); } newVisible = ApplyRestrictions(Visible, newVisible); // applying transform's data domain restriction if (!transform.DataTransform.DataDomain.IsEmpty) { var newDataRect = newVisible.ViewportToData(transform); newDataRect = RectExt.Intersect(newDataRect, transform.DataTransform.DataDomain); newVisible = newDataRect.DataToViewport(transform); } if (newVisible.IsEmpty) return new Rect(0, 0, 1, 1); //RaisePropertyChangedEvent(); PerformanceCounter.stopStopwatch("Coercing visible"); return newVisible; }
/// <summary> /// Returns an estimate for the Bounding Box of the container. /// In fact, it simply returns the smallest rectangle around all occuring coordinates in children. /// </summary> /// <param name="r">Will hold the rectangle upon return.</param> /// <returns></returns> public override bool GetBB(out Rect r) { bool hasone = false; r = new Rect(0, 0, 0, 0); foreach (TikzParseItem tpi in Children) { Rect rr; if (tpi.GetBB(out rr)) { if (!hasone) { r = rr; hasone = true; } else r.Union(rr); } } return hasone; }
/// <summary> /// Expand the content area to fit the rectangles. /// </summary> private void ExpandContent() { double xOffset = 0; double yOffset = 0; Rect contentRect = new Rect(0, 0, 0, 0); foreach (BoardObject rectangleData in DataModel.Instance.BoardObjects) { if (rectangleData.X < xOffset) { xOffset = rectangleData.X; } if (rectangleData.Y < yOffset) { yOffset = rectangleData.Y; } contentRect.Union(new Rect(rectangleData.X, rectangleData.Y, rectangleData.Width, rectangleData.Height)); } // // Translate all rectangles so they are in positive space. // xOffset = Math.Abs(xOffset); yOffset = Math.Abs(yOffset); foreach (BoardObject rectangleData in DataModel.Instance.BoardObjects) { rectangleData.X += xOffset; rectangleData.Y += yOffset; } DataModel.Instance.ContentWidth = contentRect.Width; DataModel.Instance.ContentHeight = contentRect.Height; }
/// <summary> /// Sets the item's position according to its tikzitem's value /// </summary> public override bool AdjustPosition(Func<Point, Point> TikzToScreen) { Rect r = new Rect(0, 0, 0, 0); bool hasone = false; foreach (OverlayShapeVM o in children) { o.AdjustPosition(TikzToScreen); Rect rr = o.BB; if (hasone) r.Union(rr); else { r = rr; hasone = true; } } if (hasone) { r.Inflate(20, 20); //r = new Rect(10, 10, 100, 100); //ScopeView.SetSize(r.Width, r.Height); //ScopeView.SetPosition(r.X, r.Y); BB = r; return true; } else return false; }
private void Window_Loaded(object sender, RoutedEventArgs e) { var r = new Rect(); ScreenHelper.AllScreens.Select(x => x.Bounds).ToList().ForEach(x => r.Union(x)); Left = r.Left; Top = r.Top; Width = r.Width; Height = r.Height; _m.IsHooked = true; _m.MouseMove += m_MouseMove; _m.MouseUp += m_MouseUp; _m.MouseDown += m_MouseDown; }
internal static void CalcGeometryAndBounds(StrokeNodeIterator iterator, DrawingAttributes drawingAttributes, #if DEBUG_RENDERING_FEEDBACK DrawingContext debugDC, double feedbackSize, bool showFeedback, #endif bool calculateBounds, out Geometry geometry, out Rect bounds) { Debug.Assert(iterator != null && drawingAttributes != null); //we can use our new algorithm for identity only. Matrix stylusTipTransform = drawingAttributes.StylusTipTransform; if (stylusTipTransform != Matrix.Identity && stylusTipTransform._type != MatrixTypes.TRANSFORM_IS_SCALING) { //second best optimization CalcGeometryAndBoundsWithTransform(iterator, drawingAttributes, stylusTipTransform._type, calculateBounds, out geometry, out bounds); } else { StreamGeometry streamGeometry = new StreamGeometry(); streamGeometry.FillRule = FillRule.Nonzero; StreamGeometryContext context = streamGeometry.Open(); geometry = streamGeometry; Rect empty = Rect.Empty; bounds = empty; try { // // We keep track of three StrokeNodes as we iterate across // the Stroke. Since these are structs, the default ctor will // be called and .IsValid will be false until we initialize them // StrokeNode emptyStrokeNode = new StrokeNode(); StrokeNode prevPrevStrokeNode = new StrokeNode(); StrokeNode prevStrokeNode = new StrokeNode(); StrokeNode strokeNode = new StrokeNode(); Rect prevPrevStrokeNodeBounds = empty; Rect prevStrokeNodeBounds = empty; Rect strokeNodeBounds = empty; //percentIntersect is a function of drawingAttributes height / width double percentIntersect = 95d; double maxExtent = Math.Max(drawingAttributes.Height, drawingAttributes.Width); percentIntersect += Math.Min(4.99999d, ((maxExtent / 20d) * 5d)); double prevAngle = double.MinValue; bool isStartOfSegment = true; bool isEllipse = drawingAttributes.StylusTip == StylusTip.Ellipse; bool ignorePressure = drawingAttributes.IgnorePressure; // // Two List<Point>'s that get reused for adding figures // to the streamgeometry. // List<Point> pathFigureABSide = new List<Point>();//don't prealloc. It causes Gen2 collections to rise and doesn't help execution time List<Point> pathFigureDCSide = new List<Point>(); List<Point> polyLinePoints = new List<Point>(4); int iteratorCount = iterator.Count; for (int index = 0, previousIndex = -1; index < iteratorCount; ) { if (!prevPrevStrokeNode.IsValid) { if (prevStrokeNode.IsValid) { //we're sliding our pointers forward prevPrevStrokeNode = prevStrokeNode; prevPrevStrokeNodeBounds = prevStrokeNodeBounds; prevStrokeNode = emptyStrokeNode; } else { prevPrevStrokeNode = iterator[index++, previousIndex++]; prevPrevStrokeNodeBounds = prevPrevStrokeNode.GetBounds(); continue; //so we always check if index < iterator.Count } } //we know prevPrevStrokeNode is valid if (!prevStrokeNode.IsValid) { if (strokeNode.IsValid) { //we're sliding our pointers forward prevStrokeNode = strokeNode; prevStrokeNodeBounds = strokeNodeBounds; strokeNode = emptyStrokeNode; } else { //get the next strokeNode, but don't automatically update previousIndex prevStrokeNode = iterator[index++, previousIndex]; prevStrokeNodeBounds = prevStrokeNode.GetBounds(); RectCompareResult result = FuzzyContains( prevStrokeNodeBounds, prevPrevStrokeNodeBounds, isStartOfSegment ? 99.99999d : percentIntersect); if (result == RectCompareResult.Rect1ContainsRect2) { // this node already contains the prevPrevStrokeNodeBounds (PP): // // |------------| // | |----| | // | | PP | P | // | |----| | // |------------| // prevPrevStrokeNode = iterator[index - 1, prevPrevStrokeNode.Index - 1]; ; prevPrevStrokeNodeBounds = Rect.Union(prevStrokeNodeBounds, prevPrevStrokeNodeBounds); // at this point prevPrevStrokeNodeBounds already contains this node // we can just ignore this node prevStrokeNode = emptyStrokeNode; // update previousIndex to point to this node previousIndex = index - 1; // go back to our main loop continue; } else if (result == RectCompareResult.Rect2ContainsRect1) { // this prevPrevStrokeNodeBounds (PP) already contains this node: // // |------------| // | |----|| // | PP | P || // | |----|| // |------------| // //prevPrevStrokeNodeBounds already contains this node //we can just ignore this node prevStrokeNode = emptyStrokeNode; // go back to our main loop, but do not update previousIndex // because it should continue to point to previousPrevious continue; } Debug.Assert(!prevStrokeNode.GetConnectingQuad().IsEmpty, "prevStrokeNode.GetConnectingQuad() is Empty!"); // if neither was true, we now have two of our three nodes required to // start our computation, we need to update previousIndex to point // to our current, valid prevStrokeNode previousIndex = index - 1; continue; //so we always check if index < iterator.Count } } //we know prevPrevStrokeNode and prevStrokeNode are both valid if (!strokeNode.IsValid) { strokeNode = iterator[index++, previousIndex]; strokeNodeBounds = strokeNode.GetBounds(); RectCompareResult result = FuzzyContains( strokeNodeBounds, prevStrokeNodeBounds, isStartOfSegment ? 99.99999 : percentIntersect); RectCompareResult result2 = FuzzyContains( strokeNodeBounds, prevPrevStrokeNodeBounds, isStartOfSegment ? 99.99999 : percentIntersect); if ( isStartOfSegment && result == RectCompareResult.Rect1ContainsRect2 && result2 == RectCompareResult.Rect1ContainsRect2) { if (pathFigureABSide.Count > 0) { //we've started a stroke, we need to end it before resetting //prevPrev #if DEBUG_RENDERING_FEEDBACK prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide, debugDC, feedbackSize, showFeedback); #else prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide); #endif //render ReverseDCPointsRenderAndClear(context, pathFigureABSide, pathFigureDCSide, polyLinePoints, isEllipse, true/*clear the point collections*/); } //we're resetting //prevPrevStrokeNode. We need to gen one //without a connecting quad prevPrevStrokeNode = iterator[index - 1, prevPrevStrokeNode.Index - 1]; prevPrevStrokeNodeBounds = prevPrevStrokeNode.GetBounds(); prevStrokeNode = emptyStrokeNode; strokeNode = emptyStrokeNode; // increment previousIndex to to point to this node previousIndex = index - 1; continue; } else if (result == RectCompareResult.Rect1ContainsRect2) { // this node (C) already contains the prevStrokeNodeBounds (P): // // |------------| // |----| | |----| | // | PP | | | P | C | // |----| | |----| | // |------------| // //we have to generate a new stroke node that points //to pp since the connecting quad from C to P could be empty //if they have the same point strokeNode = iterator[index - 1, prevStrokeNode.Index - 1]; if (!strokeNode.GetConnectingQuad().IsEmpty) { //only update prevStrokeNode if we have a valid connecting quad prevStrokeNode = strokeNode; prevStrokeNodeBounds = Rect.Union(strokeNodeBounds, prevStrokeNodeBounds); // update previousIndex, since it should point to this node now previousIndex = index - 1; } // at this point we can just ignore this node strokeNode = emptyStrokeNode; //strokeNodeBounds = empty; prevAngle = double.MinValue; //invalidate // go back to our main loop continue; } else if (result == RectCompareResult.Rect2ContainsRect1) { // this prevStrokeNodeBounds (P) already contains this node (C): // // |------------| // |----| | |----|| // | PP | | P | C || // |----| | |----|| // |------------| // //prevStrokeNodeBounds already contains this node //we can just ignore this node strokeNode = emptyStrokeNode; // go back to our main loop, but do not update previousIndex // because it should continue to point to previous continue; } Debug.Assert(!strokeNode.GetConnectingQuad().IsEmpty, "strokeNode.GetConnectingQuad was empty, this is unexpected"); // // NOTE: we do not check if C contains PP, or PP contains C because // that indicates a change in direction, which we handle below // // if neither was true P and C are separate, // we now have all three nodes required to // start our computation, we need to update previousIndex to point // to our current, valid prevStrokeNode previousIndex = index - 1; } // see if we have an overlap between the first and third node bool overlap = prevPrevStrokeNodeBounds.IntersectsWith(strokeNodeBounds); // prevPrevStrokeNode, prevStrokeNode and strokeNode are all // valid nodes now. Now we need to figure out what do add to our // PathFigure. First calc bounds on the strokeNode we know we need to render if (calculateBounds) { bounds.Union(prevStrokeNodeBounds); } // determine what points to add to pathFigureABSide and pathFigureDCSide // from prevPrevStrokeNode if (pathFigureABSide.Count == 0) { Debug.Assert(pathFigureDCSide.Count == 0); if (calculateBounds) { bounds.Union(prevPrevStrokeNodeBounds); } if (isStartOfSegment && overlap) { //render a complete first stroke node or we can get artifacts prevPrevStrokeNode.GetContourPoints(polyLinePoints); AddFigureToStreamGeometryContext(context, polyLinePoints, prevPrevStrokeNode.IsEllipse/*isBezierFigure*/); polyLinePoints.Clear(); } // we're starting a new pathfigure // we need to add parts of the prevPrevStrokeNode contour // to pathFigureABSide and pathFigureDCSide #if DEBUG_RENDERING_FEEDBACK prevStrokeNode.GetPointsAtStartOfSegment(pathFigureABSide, pathFigureDCSide, debugDC, feedbackSize, showFeedback); #else prevStrokeNode.GetPointsAtStartOfSegment(pathFigureABSide, pathFigureDCSide); #endif //set our marker, we're no longer at the start of the stroke isStartOfSegment = false; } if (prevAngle == double.MinValue) { //prevAngle is no longer valid prevAngle = GetAngleBetween(prevPrevStrokeNode.Position, prevStrokeNode.Position); } double delta = GetAngleDeltaFromLast(prevStrokeNode.Position, strokeNode.Position, ref prevAngle); bool directionChangedOverAbsoluteThreshold = Math.Abs(delta) > 90d && Math.Abs(delta) < (360d - 90d); bool directionChangedOverOverlapThreshold = overlap && !(ignorePressure || strokeNode.PressureFactor == 1f) && Math.Abs(delta) > 30d && Math.Abs(delta) < (360d - 30d); double prevArea = prevStrokeNodeBounds.Height * prevStrokeNodeBounds.Width; double currArea = strokeNodeBounds.Height * strokeNodeBounds.Width; bool areaChanged = !(prevArea == currArea && prevArea == (prevPrevStrokeNodeBounds.Height * prevPrevStrokeNodeBounds.Width)); bool areaChangeOverThreshold = false; if (overlap && areaChanged) { if ((Math.Min(prevArea, currArea) / Math.Max(prevArea, currArea)) <= 0.90d) { //the min area is < 70% of the max area areaChangeOverThreshold = true; } } if (areaChanged || delta != 0.0d || index >= iteratorCount) { //the area changed between the three nodes OR there was an angle delta OR we're at the end //of the stroke... either way, this is a significant node. If not, we're going to drop it. if ((overlap && (directionChangedOverOverlapThreshold || areaChangeOverThreshold)) || directionChangedOverAbsoluteThreshold) { // // we need to stop the pathfigure at P // and render the pathfigure // // |--| |--| |--||--| |------| // |PP|------|P | |PP||P | |PP P C| // |--| |--| |--||--| |------| // / |C | // |--| |--| // |C | // |--| #if DEBUG_RENDERING_FEEDBACK prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide, debugDC, feedbackSize, showFeedback); #else //end the figure prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide); #endif //render ReverseDCPointsRenderAndClear(context, pathFigureABSide, pathFigureDCSide, polyLinePoints, isEllipse, true/*clear the point collections*/); if (areaChangeOverThreshold) { //render a complete stroke node or we can get artifacts prevStrokeNode.GetContourPoints(polyLinePoints); AddFigureToStreamGeometryContext(context, polyLinePoints, prevStrokeNode.IsEllipse/*isBezierFigure*/); polyLinePoints.Clear(); } } else { // // direction didn't change over the threshold, add the midpoint data // |--| |--| // |PP|------|P | // |--| |--| // \ // |--| // |C | // |--| bool endSegment; //flag that tell us if we missed an intersection #if DEBUG_RENDERING_FEEDBACK strokeNode.GetPointsAtMiddleSegment(prevStrokeNode, delta, pathFigureABSide, pathFigureDCSide, out endSegment, debugDC, feedbackSize, showFeedback); #else strokeNode.GetPointsAtMiddleSegment(prevStrokeNode, delta, pathFigureABSide, pathFigureDCSide, out endSegment); #endif if (endSegment) { //we have a missing intersection, we need to end the //segment at P #if DEBUG_RENDERING_FEEDBACK prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide, debugDC, feedbackSize, showFeedback); #else //end the figure prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide); #endif //render ReverseDCPointsRenderAndClear(context, pathFigureABSide, pathFigureDCSide, polyLinePoints, isEllipse, true/*clear the point collections*/); } } } // // either way... slide our pointers forward, to do this, we simply mark // our first pointer as 'empty' // prevPrevStrokeNode = emptyStrokeNode; prevPrevStrokeNodeBounds = empty; } // // anything left to render? // if (prevPrevStrokeNode.IsValid) { if (prevStrokeNode.IsValid) { if (calculateBounds) { bounds.Union(prevPrevStrokeNodeBounds); bounds.Union(prevStrokeNodeBounds); } Debug.Assert(!strokeNode.IsValid); // // we never made it to strokeNode, render two points, OR // strokeNode was a dupe // if (pathFigureABSide.Count > 0) { #if DEBUG_RENDERING_FEEDBACK prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide, debugDC, feedbackSize, showFeedback); #else // // strokeNode was a dupe, we just need to render the end of the stroke // which is at prevStrokeNode // prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide); #endif //render ReverseDCPointsRenderAndClear(context, pathFigureABSide, pathFigureDCSide, polyLinePoints, isEllipse, false/*clear the point collections*/); } else { // we've only seen two points to render Debug.Assert(pathFigureDCSide.Count == 0); //contains all the logic to render two stroke nodes RenderTwoStrokeNodes( context, prevPrevStrokeNode, prevPrevStrokeNodeBounds, prevStrokeNode, prevStrokeNodeBounds, pathFigureABSide, pathFigureDCSide, polyLinePoints #if DEBUG_RENDERING_FEEDBACK ,debugDC, feedbackSize, showFeedback #endif ); } } else { if (calculateBounds) { bounds.Union(prevPrevStrokeNodeBounds); } // we only have a single point to render Debug.Assert(pathFigureABSide.Count == 0); prevPrevStrokeNode.GetContourPoints(pathFigureABSide); AddFigureToStreamGeometryContext(context, pathFigureABSide, prevPrevStrokeNode.IsEllipse/*isBezierFigure*/); } } else if (prevStrokeNode.IsValid && strokeNode.IsValid) { if (calculateBounds) { bounds.Union(prevStrokeNodeBounds); bounds.Union(strokeNodeBounds); } // typical case, we hit the end of the stroke // see if we need to start a stroke, or just end one if (pathFigureABSide.Count > 0) { #if DEBUG_RENDERING_FEEDBACK strokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide, debugDC, feedbackSize, showFeedback); #else strokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide); #endif //render ReverseDCPointsRenderAndClear(context, pathFigureABSide, pathFigureDCSide, polyLinePoints, isEllipse, false/*clear the point collections*/); if (FuzzyContains(strokeNodeBounds, prevStrokeNodeBounds, 70d) != RectCompareResult.NoItersection) { //render a complete stroke node or we can get artifacts strokeNode.GetContourPoints(polyLinePoints); AddFigureToStreamGeometryContext(context, polyLinePoints, strokeNode.IsEllipse/*isBezierFigure*/); } } else { Debug.Assert(pathFigureDCSide.Count == 0); //contains all the logic to render two stroke nodes RenderTwoStrokeNodes( context, prevStrokeNode, prevStrokeNodeBounds, strokeNode, strokeNodeBounds, pathFigureABSide, pathFigureDCSide, polyLinePoints #if DEBUG_RENDERING_FEEDBACK ,debugDC, feedbackSize, showFeedback #endif ); } } } finally { context.Close(); geometry.Freeze(); } } }
protected override Size ArrangeOverride( Size finalSize ) { Rect finalRect = new Rect(); if( _visibleElements == null ) { LayoutItems( InternalChildren, finalSize ); } foreach( DateElement child in _visibleElements ) { if( IsScrolling ) { Rect placement = new Rect( child.PlacementRectangle.Location, child.PlacementRectangle.Size ); placement.Offset( -_offset ); child.Element.Arrange( placement ); } else { child.Element.Arrange( child.PlacementRectangle ); } finalRect.Union( child.PlacementRectangle ); } Size renderSize; if( Orientation == Orientation.Horizontal ) { renderSize = new Size( finalSize.Width, finalRect.Size.Height ); } else { renderSize = new Size( finalRect.Size.Width, finalSize.Height ); } return renderSize; }
/// <summary> /// Calculate the StreamGeometry for the StrokeNodes. /// This method is one of our most sensitive perf paths. It has been optimized to /// create the minimum path figures in the StreamGeometry. There are two structures /// we create for each point in a stroke, the strokenode and the connecting quad. Adding /// strokenodes is very expensive later when MIL renders it, so this method has been optimized /// to only add strokenodes when either pressure changes, or the angle of the stroke changes. /// </summary> internal static void CalcGeometryAndBoundsWithTransform(StrokeNodeIterator iterator, DrawingAttributes drawingAttributes, MatrixTypes stylusTipMatrixType, bool calculateBounds, out Geometry geometry, out Rect bounds) { Debug.Assert(iterator != null); Debug.Assert(drawingAttributes != null); StreamGeometry streamGeometry = new StreamGeometry(); streamGeometry.FillRule = FillRule.Nonzero; StreamGeometryContext context = streamGeometry.Open(); geometry = streamGeometry; bounds = Rect.Empty; try { List<Point> connectingQuadPoints = new List<Point>(iterator.Count * 4); //the index that the cb quad points are copied to int cdIndex = iterator.Count * 2; //the index that the ab quad points are copied to int abIndex = 0; for (int x = 0; x < cdIndex; x++) { //initialize so we can start copying to cdIndex later connectingQuadPoints.Add(new Point(0d, 0d)); } List<Point> strokeNodePoints = new List<Point>(); double lastAngle = 0.0d; bool previousPreviousNodeRendered = false; Rect lastRect = new Rect(0, 0, 0, 0); for (int index = 0; index < iterator.Count; index++) { StrokeNode strokeNode = iterator[index]; System.Diagnostics.Debug.Assert(true == strokeNode.IsValid); //the only code that calls this with !calculateBounds //is dynamic rendering, which already draws enough strokeNodes //to hide any visual artifacts. //static rendering calculatesBounds, and we use those //bounds below to figure out what angle to lay strokeNodes down for. Rect strokeNodeBounds = strokeNode.GetBounds(); if (calculateBounds) { bounds.Union(strokeNodeBounds); } //if the angle between this and the last position has changed //too much relative to the angle between the last+1 position and the last position //we need to lay down stroke node double delta = Math.Abs(GetAngleDeltaFromLast(strokeNode.PreviousPosition, strokeNode.Position, ref lastAngle)); double angleTolerance = 45d; if (stylusTipMatrixType == MatrixTypes.TRANSFORM_IS_UNKNOWN) { //probably a skew is thrown in, we need to fall back to being very conservative //about how many strokeNodes we prune angleTolerance = 10d; } else if (strokeNodeBounds.Height > 40d || strokeNodeBounds.Width > 40d) { //if the strokeNode gets above a certain size, we need to lay down more strokeNodes //to prevent visual artifacts angleTolerance = 20d; } bool directionChanged = delta > angleTolerance && delta < (360d - angleTolerance); double prevArea = lastRect.Height * lastRect.Width; double currArea = strokeNodeBounds.Height * strokeNodeBounds.Width; bool areaChangedOverThreshold = false; if ((Math.Min(prevArea, currArea) / Math.Max(prevArea, currArea)) <= 0.70d) { //the min area is < 70% of the max area areaChangedOverThreshold = true; } lastRect = strokeNodeBounds; //render the stroke node for the first two nodes and last two nodes always if (index <= 1 || index >= iterator.Count - 2 || directionChanged || areaChangedOverThreshold) { //special case... the direction has changed and we need to //insert a stroke node in the StreamGeometry before we render the current one if (directionChanged && !previousPreviousNodeRendered && index > 1 && index < iterator.Count - 1) { //insert a stroke node for the previous node strokeNodePoints.Clear(); strokeNode.GetPreviousContourPoints(strokeNodePoints); AddFigureToStreamGeometryContext(context, strokeNodePoints, strokeNode.IsEllipse/*isBezierFigure*/); previousPreviousNodeRendered = true; } //render the stroke node strokeNodePoints.Clear(); strokeNode.GetContourPoints(strokeNodePoints); AddFigureToStreamGeometryContext(context, strokeNodePoints, strokeNode.IsEllipse/*isBezierFigure*/); } if (!directionChanged) { previousPreviousNodeRendered = false; } //add the end points of the connecting quad Quad quad = strokeNode.GetConnectingQuad(); if (!quad.IsEmpty) { connectingQuadPoints[abIndex++] = quad.A; connectingQuadPoints[abIndex++] = quad.B; connectingQuadPoints.Add(quad.D); connectingQuadPoints.Add(quad.C); } if (strokeNode.IsLastNode) { Debug.Assert(index == iterator.Count - 1); if (abIndex > 0) { //we added something to the connecting quad points. //now we need to do three things //1) Shift the dc points down to the ab points int cbStartIndex = iterator.Count * 2; int cbEndIndex = connectingQuadPoints.Count - 1; for (int i = abIndex, j = cbStartIndex; j <= cbEndIndex; i++, j++) { connectingQuadPoints[i] = connectingQuadPoints[j]; } //2) trim the exess off the end of the array int countToRemove = cbStartIndex - abIndex; connectingQuadPoints.RemoveRange((cbEndIndex - countToRemove) + 1, countToRemove); //3) reverse the dc points to make them cd points for (int i = abIndex, j = connectingQuadPoints.Count - 1; i < j; i++, j--) { Point temp = connectingQuadPoints[i]; connectingQuadPoints[i] = connectingQuadPoints[j]; connectingQuadPoints[j] = temp; } //now render away! AddFigureToStreamGeometryContext(context, connectingQuadPoints, false/*isBezierFigure*/); } } } } finally { context.Close(); geometry.Freeze(); } }
void DynamoViewModelRequestSaveImage(object sender, ImageSaveEventArgs e) { if (string.IsNullOrEmpty(e.Path)) return; var workspace = dynamoViewModel.Model.CurrentWorkspace; if (workspace == null) return; if (!workspace.Nodes.Any() && (!workspace.Notes.Any())) return; // An empty graph. var initialized = false; var bounds = new Rect(); var dragCanvas = WpfUtilities.ChildOfType<DragCanvas>(this); var childrenCount = VisualTreeHelper.GetChildrenCount(dragCanvas); for (int index = 0; index < childrenCount; ++index) { var child = VisualTreeHelper.GetChild(dragCanvas, index); var firstChild = VisualTreeHelper.GetChild(child, 0); if ((!(firstChild is NodeView)) && (!(firstChild is NoteView))) continue; var childBounds = VisualTreeHelper.GetDescendantBounds(child as Visual); childBounds.X = (double)(child as Visual).GetValue(Canvas.LeftProperty); childBounds.Y = (double)(child as Visual).GetValue(Canvas.TopProperty); if (initialized) bounds.Union(childBounds); else { initialized = true; bounds = childBounds; } } // Add padding to the edge and make them multiples of two. bounds.Width = (((int)Math.Ceiling(bounds.Width)) + 1) & ~0x01; bounds.Height = (((int)Math.Ceiling(bounds.Height)) + 1) & ~0x01; var drawingVisual = new DrawingVisual(); using (var drawingContext = drawingVisual.RenderOpen()) { var targetRect = new Rect(0, 0, bounds.Width, bounds.Height); var visualBrush = new VisualBrush(dragCanvas); drawingContext.DrawRectangle(visualBrush, null, targetRect); // drawingContext.DrawRectangle(null, new Pen(Brushes.Blue, 1.0), childBounds); } // var m = PresentationSource.FromVisual(this).CompositionTarget.TransformToDevice; // region.Scale(m.M11, m.M22); var rtb = new RenderTargetBitmap(((int)bounds.Width), ((int)bounds.Height), 96, 96, PixelFormats.Default); rtb.Render(drawingVisual); //endcode as PNG var pngEncoder = new PngBitmapEncoder(); pngEncoder.Frames.Add(BitmapFrame.Create(rtb)); try { using (var stm = File.Create(e.Path)) { pngEncoder.Save(stm); } } catch { dynamoViewModel.Model.Logger.Log(Wpf.Properties.Resources.MessageFailedToSaveAsImage); } }
/// <summary> /// Union - Return the result of the union of rect1 and rect2. /// </summary> public static Rect Union(Rect rect1, Rect rect2) { rect1.Union(rect2); return(rect1); }