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
}
}