[12503] | 1 | using System;
|
---|
| 2 | using System.Collections.Generic;
|
---|
| 3 | using System.Linq;
|
---|
| 4 | using System.Text;
|
---|
| 5 | using System.Windows.Markup;
|
---|
| 6 | using System.Windows;
|
---|
| 7 | using System.Windows.Data;
|
---|
| 8 | using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
|
---|
| 9 | using System.Windows.Threading;
|
---|
| 10 | using Microsoft.Research.DynamicDataDisplay.Converters;
|
---|
| 11 | using System.Globalization;
|
---|
| 12 | using System.Windows.Controls;
|
---|
| 13 | using System.Diagnostics.Contracts;
|
---|
| 14 | using System.Windows.Shapes;
|
---|
| 15 | using System.Windows.Media;
|
---|
| 16 | using System.Diagnostics;
|
---|
| 17 | using System.ComponentModel;
|
---|
| 18 |
|
---|
| 19 | namespace Microsoft.Research.DynamicDataDisplay.Charts.Shapes
|
---|
| 20 | {
|
---|
| 21 | public class PivotSegmentEditor : FrameworkElement, IPlotterElement, INotifyPropertyChanged
|
---|
| 22 | {
|
---|
| 23 | private Plotter2D plotter = null;
|
---|
| 24 | private ViewportHostPanel panel = new ViewportHostPanel();
|
---|
| 25 | private Segment segment;
|
---|
| 26 | private DraggablePoint startThumb;
|
---|
| 27 | private DraggablePoint endThumb;
|
---|
| 28 | private Func<double, string> xMapping = d => d.ToString("F");
|
---|
| 29 | private readonly ViewportRay leftRay;
|
---|
| 30 | private readonly ViewportRay rightRay;
|
---|
| 31 |
|
---|
| 32 | /// <summary>
|
---|
| 33 | /// Initializes a new instance of the <see cref="PivotSegmentEditor"/> class.
|
---|
| 34 | /// </summary>
|
---|
| 35 | public PivotSegmentEditor()
|
---|
| 36 | {
|
---|
| 37 | ResourceDictionary resources = new ResourceDictionary
|
---|
| 38 | {
|
---|
| 39 | Source = new Uri("/DynamicDataDisplay;component/Charts/Shapes/PivotSegmentEditor.xaml", UriKind.Relative)
|
---|
| 40 | };
|
---|
| 41 |
|
---|
| 42 | panel.BeginBatchAdd();
|
---|
| 43 |
|
---|
| 44 | ControlTemplate segmentTemplate = (ControlTemplate)resources["segment"];
|
---|
| 45 | segment = (Segment)segmentTemplate.LoadContent();
|
---|
| 46 | segment.DataContext = this;
|
---|
| 47 |
|
---|
| 48 | ControlTemplate startThumbTemplate = (ControlTemplate)resources["leftThumb"];
|
---|
| 49 | startThumb = (DraggablePoint)startThumbTemplate.LoadContent();
|
---|
| 50 | startThumb.DataContext = this;
|
---|
| 51 |
|
---|
| 52 | ControlTemplate endThumbTemplate = (ControlTemplate)resources["rightThumb"];
|
---|
| 53 | endThumb = (DraggablePoint)endThumbTemplate.LoadContent();
|
---|
| 54 | endThumb.DataContext = this;
|
---|
| 55 |
|
---|
| 56 | ControlTemplate leftRayTemplate = (ControlTemplate)resources["leftRay"];
|
---|
| 57 | leftRay = (ViewportRay)leftRayTemplate.LoadContent();
|
---|
| 58 | leftRay.DataContext = this;
|
---|
| 59 |
|
---|
| 60 | ControlTemplate rightRayTemplate = (ControlTemplate)resources["rightRay"];
|
---|
| 61 | rightRay = (ViewportRay)rightRayTemplate.LoadContent();
|
---|
| 62 | rightRay.DataContext = this;
|
---|
| 63 |
|
---|
| 64 | ControlTemplate mTextTemplate = (ControlTemplate)resources["mText"];
|
---|
| 65 | TextBlock mText = (TextBlock)mTextTemplate.LoadContent();
|
---|
| 66 | panel.Children.Add(mText);
|
---|
| 67 | mText.DataContext = this;
|
---|
| 68 |
|
---|
| 69 | ControlTemplate leftPointGridTemplate = (ControlTemplate)resources["leftPointGrid"];
|
---|
| 70 | Panel leftPointGrid = (Panel)leftPointGridTemplate.LoadContent();
|
---|
| 71 | panel.Children.Add(leftPointGrid);
|
---|
| 72 | leftPointGrid.DataContext = this;
|
---|
| 73 |
|
---|
| 74 | ControlTemplate rightPointGridTemplate = (ControlTemplate)resources["rightPointGrid"];
|
---|
| 75 | Panel rightPointGrid = (Panel)rightPointGridTemplate.LoadContent();
|
---|
| 76 | panel.Children.Add(rightPointGrid);
|
---|
| 77 | rightPointGrid.DataContext = this;
|
---|
| 78 |
|
---|
| 79 | ControlTemplate leftTextTemplate = (ControlTemplate)resources["leftText"];
|
---|
| 80 | FrameworkElement leftBorder = (FrameworkElement)leftTextTemplate.LoadContent();
|
---|
| 81 | panel.Children.Add(leftBorder);
|
---|
| 82 | leftBorder.DataContext = this;
|
---|
| 83 |
|
---|
| 84 | ControlTemplate rightTextTemplate = (ControlTemplate)resources["rightText"];
|
---|
| 85 | FrameworkElement rightBorder = (FrameworkElement)rightTextTemplate.LoadContent();
|
---|
| 86 | panel.Children.Add(rightBorder);
|
---|
| 87 | rightBorder.DataContext = this;
|
---|
| 88 | }
|
---|
| 89 |
|
---|
| 90 | #region Properties
|
---|
| 91 |
|
---|
| 92 | #region XMapping property
|
---|
| 93 |
|
---|
| 94 | [NotNull]
|
---|
| 95 | public Func<double, string> XMapping
|
---|
| 96 | {
|
---|
| 97 | get { return xMapping; }
|
---|
| 98 | set
|
---|
| 99 | {
|
---|
| 100 | Contract.Assert(value != null);
|
---|
| 101 |
|
---|
| 102 | xMapping = value;
|
---|
| 103 | PropertyChanged.Raise(this, "");
|
---|
| 104 | }
|
---|
| 105 | }
|
---|
| 106 |
|
---|
| 107 | #endregion
|
---|
| 108 |
|
---|
| 109 | #region Point1 property
|
---|
| 110 |
|
---|
| 111 | public Point Point1
|
---|
| 112 | {
|
---|
| 113 | get { return (Point)GetValue(Point1Property); }
|
---|
| 114 | set { SetValue(Point1Property, value); }
|
---|
| 115 | }
|
---|
| 116 |
|
---|
| 117 | public static readonly DependencyProperty Point1Property = DependencyProperty.Register(
|
---|
| 118 | "Point1",
|
---|
| 119 | typeof(Point),
|
---|
| 120 | typeof(PivotSegmentEditor),
|
---|
| 121 | new FrameworkPropertyMetadata(new Point(0, 0), OnPointChanged));
|
---|
| 122 |
|
---|
| 123 | private static void OnPointChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
---|
| 124 | {
|
---|
| 125 | PivotSegmentEditor owner = (PivotSegmentEditor)d;
|
---|
| 126 | owner.CoerceValue(LeftYProperty);
|
---|
| 127 | owner.CoerceValue(RightYProperty);
|
---|
| 128 | owner.OnPointChanged();
|
---|
| 129 | }
|
---|
| 130 |
|
---|
| 131 | private void OnPointChanged()
|
---|
| 132 | {
|
---|
| 133 | PropertyChanged.Raise(this, "");
|
---|
| 134 | }
|
---|
| 135 |
|
---|
| 136 | #endregion
|
---|
| 137 |
|
---|
| 138 | #region Point2 property
|
---|
| 139 |
|
---|
| 140 | public Point Point2
|
---|
| 141 | {
|
---|
| 142 | get { return (Point)GetValue(Point2Property); }
|
---|
| 143 | set { SetValue(Point2Property, value); }
|
---|
| 144 | }
|
---|
| 145 |
|
---|
| 146 | public static readonly DependencyProperty Point2Property = DependencyProperty.Register(
|
---|
| 147 | "Point2",
|
---|
| 148 | typeof(Point),
|
---|
| 149 | typeof(PivotSegmentEditor),
|
---|
| 150 | new FrameworkPropertyMetadata(new Point(1, 1), OnPointChanged));
|
---|
| 151 |
|
---|
| 152 | #endregion
|
---|
| 153 |
|
---|
| 154 | #region LineStroke property
|
---|
| 155 |
|
---|
| 156 | public Brush LineStroke
|
---|
| 157 | {
|
---|
| 158 | get { return (Brush)GetValue(LineStrokeProperty); }
|
---|
| 159 | set { SetValue(LineStrokeProperty, value); }
|
---|
| 160 | }
|
---|
| 161 |
|
---|
| 162 | public static readonly DependencyProperty LineStrokeProperty = DependencyProperty.Register(
|
---|
| 163 | "LineStroke",
|
---|
| 164 | typeof(Brush),
|
---|
| 165 | typeof(PivotSegmentEditor),
|
---|
| 166 | new FrameworkPropertyMetadata(Brushes.Black));
|
---|
| 167 |
|
---|
| 168 | #endregion
|
---|
| 169 |
|
---|
| 170 | #region LineThickness property
|
---|
| 171 |
|
---|
| 172 | public double LineThickness
|
---|
| 173 | {
|
---|
| 174 | get { return (double)GetValue(LineThicknessProperty); }
|
---|
| 175 | set { SetValue(LineThicknessProperty, value); }
|
---|
| 176 | }
|
---|
| 177 |
|
---|
| 178 | public static readonly DependencyProperty LineThicknessProperty = DependencyProperty.Register(
|
---|
| 179 | "LineThickbess",
|
---|
| 180 | typeof(double),
|
---|
| 181 | typeof(PivotSegmentEditor),
|
---|
| 182 | new FrameworkPropertyMetadata(2.0));
|
---|
| 183 |
|
---|
| 184 | #endregion
|
---|
| 185 |
|
---|
| 186 | #region LeftY property
|
---|
| 187 |
|
---|
| 188 | public double LeftY
|
---|
| 189 | {
|
---|
| 190 | get { return (double)GetValue(LeftYProperty); }
|
---|
| 191 | }
|
---|
| 192 |
|
---|
| 193 | private static readonly DependencyPropertyKey LeftYPropertyKey = DependencyProperty.RegisterReadOnly(
|
---|
| 194 | "LeftY",
|
---|
| 195 | typeof(double),
|
---|
| 196 | typeof(PivotSegmentEditor),
|
---|
| 197 | new FrameworkPropertyMetadata(null, OnLeftYCoerce));
|
---|
| 198 |
|
---|
| 199 | public static readonly DependencyProperty LeftYProperty = LeftYPropertyKey.DependencyProperty;
|
---|
| 200 |
|
---|
| 201 | private Func<double, double> GetLineFunc()
|
---|
| 202 | {
|
---|
| 203 | Viewport2D viewport = plotter.Viewport;
|
---|
| 204 | double deltaX = Point1.X - Point2.X;
|
---|
| 205 | double deltaY = Point1.Y - Point2.Y;
|
---|
| 206 | double m = deltaY / deltaX;
|
---|
| 207 | double b = Point1.Y - Point1.X * deltaY / deltaX;
|
---|
| 208 |
|
---|
| 209 | Func<double, double> func = x => m * x + b;
|
---|
| 210 |
|
---|
| 211 | return func;
|
---|
| 212 | }
|
---|
| 213 |
|
---|
| 214 | private static object OnLeftYCoerce(DependencyObject source, object value)
|
---|
| 215 | {
|
---|
| 216 | PivotSegmentEditor editor = (PivotSegmentEditor)source;
|
---|
| 217 | if (editor.plotter == null)
|
---|
| 218 | return value;
|
---|
| 219 |
|
---|
| 220 | Func<double, double> func = editor.GetLineFunc();
|
---|
| 221 | double xmin = editor.plotter.Viewport.Visible.XMin;
|
---|
| 222 | double result = func(xmin);
|
---|
| 223 |
|
---|
| 224 | return result;
|
---|
| 225 | }
|
---|
| 226 |
|
---|
| 227 | #endregion
|
---|
| 228 |
|
---|
| 229 | #region RightY property
|
---|
| 230 |
|
---|
| 231 | public double RightY
|
---|
| 232 | {
|
---|
| 233 | get { return (double)GetValue(RightYProperty); }
|
---|
| 234 | }
|
---|
| 235 |
|
---|
| 236 | private static readonly DependencyPropertyKey RightYPropertyKey = DependencyProperty.RegisterReadOnly(
|
---|
| 237 | "RightY",
|
---|
| 238 | typeof(double),
|
---|
| 239 | typeof(PivotSegmentEditor),
|
---|
| 240 | new FrameworkPropertyMetadata(null, OnRightYCoerce));
|
---|
| 241 |
|
---|
| 242 | public static readonly DependencyProperty RightYProperty = RightYPropertyKey.DependencyProperty;
|
---|
| 243 |
|
---|
| 244 | private static object OnRightYCoerce(DependencyObject source, object value)
|
---|
| 245 | {
|
---|
| 246 | PivotSegmentEditor editor = (PivotSegmentEditor)source;
|
---|
| 247 | if (editor.plotter == null)
|
---|
| 248 | return value;
|
---|
| 249 |
|
---|
| 250 | Func<double, double> func = editor.GetLineFunc();
|
---|
| 251 | double xmax = editor.plotter.Viewport.Visible.XMax;
|
---|
| 252 | double result = func(xmax);
|
---|
| 253 |
|
---|
| 254 | return result;
|
---|
| 255 | }
|
---|
| 256 | #endregion
|
---|
| 257 |
|
---|
| 258 | public double M
|
---|
| 259 | {
|
---|
| 260 | get { return (Point1.Y - Point2.Y) / (Point1.X - Point2.X); }
|
---|
| 261 | set { Debug.WriteLine("M set = " + value.ToString()); }
|
---|
| 262 | }
|
---|
| 263 |
|
---|
| 264 | public string LeftName
|
---|
| 265 | {
|
---|
| 266 | get
|
---|
| 267 | {
|
---|
| 268 | return String.Format("({0}, {1:F})", xMapping(Point1.X), Point1.Y);
|
---|
| 269 | }
|
---|
| 270 | }
|
---|
| 271 |
|
---|
| 272 | public string RightName
|
---|
| 273 | {
|
---|
| 274 | get
|
---|
| 275 | {
|
---|
| 276 | return String.Format("({0}, {1:F})", xMapping(Point2.X), Point2.Y);
|
---|
| 277 | }
|
---|
| 278 | }
|
---|
| 279 |
|
---|
| 280 | public Point Center
|
---|
| 281 | {
|
---|
| 282 | get
|
---|
| 283 | {
|
---|
| 284 | return Point1 + (Point2 - Point1) / 2;
|
---|
| 285 | }
|
---|
| 286 | }
|
---|
| 287 |
|
---|
| 288 | #endregion
|
---|
| 289 |
|
---|
| 290 | #region IPlotterElement Members
|
---|
| 291 |
|
---|
| 292 | public void OnPlotterAttached(Plotter plotter)
|
---|
| 293 | {
|
---|
| 294 | this.plotter = (Plotter2D)plotter;
|
---|
| 295 |
|
---|
| 296 | this.plotter.Viewport.PropertyChanged += OnViewport_PropertyChanged;
|
---|
| 297 |
|
---|
| 298 | plotter.Dispatcher.BeginInvoke(() =>
|
---|
| 299 | {
|
---|
| 300 | plotter.Children.AddMany(
|
---|
| 301 | segment,
|
---|
| 302 | startThumb,
|
---|
| 303 | endThumb,
|
---|
| 304 | panel,
|
---|
| 305 | leftRay,
|
---|
| 306 | rightRay);
|
---|
| 307 |
|
---|
| 308 | CoerceValue(LeftYProperty);
|
---|
| 309 | CoerceValue(RightYProperty);
|
---|
| 310 |
|
---|
| 311 | PropertyChanged.Raise(this, "");
|
---|
| 312 | }, DispatcherPriority.Normal);
|
---|
| 313 | }
|
---|
| 314 |
|
---|
| 315 | private void OnViewport_PropertyChanged(object sender, ExtendedPropertyChangedEventArgs e)
|
---|
| 316 | {
|
---|
| 317 | CoerceValue(LeftYProperty);
|
---|
| 318 | CoerceValue(RightYProperty);
|
---|
| 319 | }
|
---|
| 320 |
|
---|
| 321 | public void OnPlotterDetaching(Plotter plotter)
|
---|
| 322 | {
|
---|
| 323 | this.plotter.Viewport.PropertyChanged -= OnViewport_PropertyChanged;
|
---|
| 324 |
|
---|
| 325 | plotter.Dispatcher.BeginInvoke(() =>
|
---|
| 326 | {
|
---|
| 327 | plotter.Children.RemoveAll(
|
---|
| 328 | segment,
|
---|
| 329 | startThumb,
|
---|
| 330 | endThumb,
|
---|
| 331 | panel,
|
---|
| 332 | leftRay,
|
---|
| 333 | rightRay);
|
---|
| 334 | }, DispatcherPriority.Normal);
|
---|
| 335 |
|
---|
| 336 | this.plotter = null;
|
---|
| 337 | }
|
---|
| 338 |
|
---|
| 339 | public Plotter2D Plotter
|
---|
| 340 | {
|
---|
| 341 | get { return plotter; }
|
---|
| 342 | }
|
---|
| 343 |
|
---|
| 344 | Plotter IPlotterElement.Plotter
|
---|
| 345 | {
|
---|
| 346 | get { return plotter; }
|
---|
| 347 | }
|
---|
| 348 |
|
---|
| 349 | #endregion
|
---|
| 350 |
|
---|
| 351 | #region INotifyPropertyChanged Members
|
---|
| 352 |
|
---|
| 353 | public event PropertyChangedEventHandler PropertyChanged;
|
---|
| 354 |
|
---|
| 355 | #endregion
|
---|
| 356 | }
|
---|
| 357 |
|
---|
| 358 | public sealed class MToVerticalOffsetConverter : GenericValueConverter<double>
|
---|
| 359 | {
|
---|
| 360 | public override object ConvertCore(double value, Type targetType, object parameter, CultureInfo culture)
|
---|
| 361 | {
|
---|
| 362 | if (value > 0)
|
---|
| 363 | return 2.0;
|
---|
| 364 | else
|
---|
| 365 | return -2.0;
|
---|
| 366 | }
|
---|
| 367 | }
|
---|
| 368 |
|
---|
| 369 | public sealed class MToVerticalAlignmentInvertedConverter : GenericValueConverter<double>
|
---|
| 370 | {
|
---|
| 371 | public override object ConvertCore(double value, Type targetType, object parameter, CultureInfo culture)
|
---|
| 372 | {
|
---|
| 373 | if (value > 0)
|
---|
| 374 | return VerticalAlignment.Bottom;
|
---|
| 375 | else
|
---|
| 376 | return VerticalAlignment.Top;
|
---|
| 377 | }
|
---|
| 378 | }
|
---|
| 379 |
|
---|
| 380 | public sealed class MToVerticalAlignmentConverter : GenericValueConverter<double>
|
---|
| 381 | {
|
---|
| 382 | public override object ConvertCore(double value, Type targetType, object parameter, CultureInfo culture)
|
---|
| 383 | {
|
---|
| 384 | if (value > 0)
|
---|
| 385 | return VerticalAlignment.Top;
|
---|
| 386 | else
|
---|
| 387 | return VerticalAlignment.Bottom;
|
---|
| 388 | }
|
---|
| 389 | }
|
---|
| 390 | }
|
---|
| 391 |
|
---|