using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Threading; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Threading; using Microsoft.Research.DynamicDataDisplay.Common; namespace Microsoft.Research.DynamicDataDisplay { /// ViewportElement2D is intended to be a child of Viewport2D. Specifics /// of ViewportElement2D is Viewport2D attached property public abstract class ViewportElement2D : PlotterElement, INotifyPropertyChanged { protected ViewportElement2D() { } protected virtual Panel GetHostPanel(Plotter plotter) { return plotter.CentralGrid; } protected override void OnPlotterAttached(Plotter plotter) { base.OnPlotterAttached(plotter); plotter2D = (Plotter2D)plotter; GetHostPanel(plotter).Children.Add(this); viewport = plotter2D.Viewport; viewport.PropertyChanged += OnViewportPropertyChanged; } private void OnViewportPropertyChanged(object sender, ExtendedPropertyChangedEventArgs e) { if (e.PropertyName == "Visible") { OnVisibleChanged((DataRect)e.NewValue, (DataRect)e.OldValue); } else if (e.PropertyName == "Output") { OnOutputChanged((Rect)e.NewValue, (Rect)e.OldValue); } else if (e.PropertyName == "Transform") { Update(); } else { // other properties changed are now not interesting for us } } protected override void OnPlotterDetaching(Plotter plotter) { base.OnPlotterDetaching(plotter); viewport.PropertyChanged -= OnViewportPropertyChanged; viewport = null; GetHostPanel(plotter).Children.Remove(this); plotter2D = null; } private Plotter2D plotter2D; protected Plotter2D Plotter2D { get { return plotter2D; } } public int ZIndex { get { return Panel.GetZIndex(this); } set { Panel.SetZIndex(this, value); } } #region Viewport private Viewport2D viewport; protected Viewport2D Viewport { get { return viewport; } } #endregion private Vector offset = new Vector(); protected internal Vector Offset { get { return offset; } set { offset = value; } } //bool SizeEqual(Size s1, Size s2, double eps) //{ // double width = Math.Min(s1.Width, s2.Width); // double height = Math.Min(s1.Height, s2.Height); // return Math.Abs(s1.Width - s2.Width) < width * eps && // Math.Abs(s1.Height - s2.Height) < height * eps; //} protected virtual void OnVisibleChanged(DataRect newRect, DataRect oldRect) { if (newRect.Size == oldRect.Size) { var transform = viewport.Transform; offset += oldRect.Location.DataToScreen(transform) - newRect.Location.DataToScreen(transform); if (ManualTranslate) { Update(); } } else { offset = new Vector(); Update(); } } protected virtual void OnOutputChanged(Rect newRect, Rect oldRect) { offset = new Vector(); Update(); } /// /// Gets a value indicating whether this instance is translated. /// /// /// true if this instance is translated; otherwise, false. /// protected bool IsTranslated { get { return offset.X != 0 || offset.Y != 0; } } #region IsLevel public bool IsLayer { get { return (bool)GetValue(IsLayerProperty); } set { SetValue(IsLayerProperty, value); } } public static readonly DependencyProperty IsLayerProperty = DependencyProperty.Register( "IsLayer", typeof(bool), typeof(ViewportElement2D), new FrameworkPropertyMetadata( false )); #endregion #region Rendering & caching options protected object GetValueSync(DependencyProperty property) { return Dispatcher.Invoke( DispatcherPriority.Send, (DispatcherOperationCallback)delegate { return GetValue(property); }, property); } protected void SetValueAsync(DependencyProperty property, object value) { Dispatcher.BeginInvoke(DispatcherPriority.Send, (SendOrPostCallback)delegate { SetValue(property, value); }, value); } private bool manualClip; /// /// Gets or sets a value indicating whether descendant graph class /// relies on autotic clipping by Viewport.Output or /// does its own clipping. /// public bool ManualClip { get { return manualClip; } set { manualClip = value; } } private bool manualTranslate; /// /// Gets or sets a value indicating whether descendant graph class /// relies on automatic translation of it, or does its own. /// public bool ManualTranslate { get { return manualTranslate; } set { manualTranslate = value; } } private RenderTo renderTarget = RenderTo.Screen; /// /// Gets or sets a value indicating whether descendant graph class /// uses cached rendering of its content to image, or not. /// public RenderTo RenderTarget { get { return renderTarget; } set { renderTarget = value; } } private enum ImageKind { Real, BeingRendered, Empty } #endregion private RenderState CreateRenderState(DataRect renderVisible, RenderTo renderingType) { Rect output = Viewport.Output; return new RenderState(renderVisible, Viewport.Visible, output, renderingType); } protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) { base.OnPropertyChanged(e); if (e.Property == VisibilityProperty) { Update(); } } private bool updateCalled; private bool beforeFirstUpdate = true; protected void Update() { if (Viewport == null) return; UpdateCore(); if (!beforeFirstUpdate) { updateCalled = true; InvalidateVisual(); } beforeFirstUpdate = false; } protected virtual void UpdateCore() { } protected void TranslateVisual() { if (!ManualTranslate) { shouldReRender = false; } InvalidateVisual(); } #region Thumbnail private ImageSource thumbnail; public ImageSource Thumbnail { get { if (!CreateThumbnail) { CreateThumbnail = true; } return thumbnail; } } private bool createThumbnail; public bool CreateThumbnail { get { return createThumbnail; } set { if (createThumbnail != value) { createThumbnail = value; if (value) { RenderThumbnail(); } else { thumbnail = null; RaisePropertyChanged("Thumbnail"); } } } } private bool ShouldCreateThumbnail { get { return IsLayer && createThumbnail; } } private void RenderThumbnail() { if (Viewport == null) return; Rect output = Viewport.Output; if (output.Width == 0 || output.Height == 0) return; DataRect visible = Viewport.Visible; var transform = viewport.Transform; DrawingVisual visual = new DrawingVisual(); using (DrawingContext dc = visual.RenderOpen()) { Point outputStart = visible.Location.DataToScreen(transform); double x = -outputStart.X + offset.X; double y = -outputStart.Y + output.Bottom - output.Top + offset.Y; bool translate = !manualTranslate && IsTranslated; if (translate) { dc.PushTransform(new TranslateTransform(x, y)); } const byte c = 240; Brush brush = new SolidColorBrush(Color.FromArgb(120, c, c, c)); Pen pen = new Pen(Brushes.Black, 1); dc.DrawRectangle(brush, pen, output); dc.DrawDrawing(graphContents); if (translate) { dc.Pop(); } } RenderTargetBitmap bmp = new RenderTargetBitmap((int)output.Width, (int)output.Height, 96, 96, PixelFormats.Pbgra32); bmp.Render(visual); thumbnail = bmp; RaisePropertyChanged("Thumbnail"); } #endregion private bool shouldReRender = true; private DrawingGroup graphContents; protected sealed override void OnRender(DrawingContext drawingContext) { if (Viewport == null) return; Rect output = Viewport.Output; if (output.Width == 0 || output.Height == 0) return; if (output.IsEmpty) return; if (Visibility != Visibility.Visible) return; if (shouldReRender || manualTranslate || renderTarget == RenderTo.Image || beforeFirstUpdate || updateCalled) { if (graphContents == null) { graphContents = new DrawingGroup(); } if (beforeFirstUpdate) { Update(); } using (DrawingContext context = graphContents.Open()) { if (renderTarget == RenderTo.Screen) { RenderState state = CreateRenderState(Viewport.Visible, RenderTo.Screen); OnRenderCore(context, state); } else { // for future use } } updateCalled = false; } // thumbnail is not created, if // 1) CreateThumbnail is false // 2) this graph has IsLayer property, set to false if (ShouldCreateThumbnail) { RenderThumbnail(); } if (!manualClip) { drawingContext.PushClip(new RectangleGeometry(output)); } bool translate = !manualTranslate && IsTranslated; if (translate) { drawingContext.PushTransform(new TranslateTransform(offset.X, offset.Y)); } drawingContext.DrawDrawing(graphContents); if (translate) { drawingContext.Pop(); } if (!manualClip) { drawingContext.Pop(); } shouldReRender = true; } protected abstract void OnRenderCore(DrawingContext dc, RenderState state); #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } #endregion } }