/// <summary> /// Core element creation. /// </summary> /// <param name="index"></param> /// <param name="valuex"></param> /// <param name="valuey"></param> /// <param name="item"></param> /// <param name="recycler"></param> /// <param name="evs"></param> /// <returns></returns> ItemState <Path> ElementPipeline(int index, double valuex, double valuey, object item, Recycler <Path, ItemState <Path> > recycler, Evaluators evs) { var mappedy = ValueAxis.For(valuey); var mappedx = CategoryAxis.For(valuex); var markerx = mappedx + MarkerOffset; _trace.Verbose($"[{index}] {valuey} ({markerx},{mappedy})"); var mk = MarkerTemplate.LoadContent() as Geometry; // TODO allow MK to be other things like (Path or Image). // TODO allow a MarkerTemplateSelector and a value Selector/Formatter // no path yet var el = recycler.Next(null); if (el == null) { return(null); } var shim = new GeometryWithOffsetShim <Geometry>() { PathData = mk }; el.Item2.DataContext = shim; BindTo(shim, nameof(shim.Offset), el.Item2, Canvas.LeftProperty); var cs = evs.LabelFor(item); if (cs == null) { return(new ItemState_Matrix <Path>(index, mappedx, MarkerOffset, mappedy, el.Item2)); } else { return(new ItemStateCustom_Matrix <Path>(index, mappedx, MarkerOffset, mappedy, cs, el.Item2)); } }
Legend Legend() { var mk = MarkerTemplate?.LoadContent(); if (mk is Geometry gx) { // need something because it's all in NDC gx.Transform = new ScaleTransform() { ScaleX = 24, ScaleY = 24 }; return(new LegendWithGeometry() { Data = gx, Title = Title, Fill = PathStyle.Find <Brush>(Path.FillProperty), Stroke = PathStyle.Find <Brush>(Path.StrokeProperty) }); } else { return(new Legend() { Title = Title, Fill = PathStyle.Find <Brush>(Path.FillProperty), Stroke = PathStyle.Find <Brush>(Path.StrokeProperty) }); } }
/// <summary>This method is invoked when application is in idle state</summary> void IdleDraw(object sender, EventArgs e) { bool finished = true; SyncMarkerViewModels(); // Remove extra batches. Such removals take a lot of time, so we do it at idle handlers if (batches.Count > Math.Ceiling(models.Count / (double)MarkersBatchSize)) { var b = batches[batches.Count - 1]; Children.Remove(b.Panel); Children.Remove(b.Image); batches.RemoveAt(batches.Count - 1); finished = false; } // Find first batch that is ready for Image updating var batch = batches.FirstOrDefault(b => b.PanelVersion == plotVersion && b.Panel.Visibility == System.Windows.Visibility.Visible && b.IsLayoutUpdated && b.ImageVersion != plotVersion); if (batch != null) { batch.PlotRect = ActualPlotRect; var panelSize = new Size(Math.Max(1, batch.Panel.RenderSize.Width), Math.Max(1, batch.Panel.RenderSize.Height)); var renderSize = new Size( Math.Min(MaxSnapshotSize.Width, panelSize.Width), Math.Min(MaxSnapshotSize.Height, panelSize.Height)); if (batch.Content == null || batch.Content.PixelWidth != (int)Math.Ceiling(renderSize.Width) || batch.Content.PixelHeight != (int)Math.Ceiling(renderSize.Height)) { batch.Content = new RenderTargetBitmap((int)renderSize.Width, (int)renderSize.Height, 96, 96, PixelFormats.Pbgra32); } else { batch.Content.Clear(); } ScaleTransform transform = new ScaleTransform { ScaleX = renderSize.Width < panelSize.Width ? renderSize.Width / panelSize.Width : 1.0, ScaleY = renderSize.Height < panelSize.Height ? renderSize.Height / panelSize.Height : 1.0 }; var panel = batch.Panel; panel.RenderTransform = transform; batch.Content = new RenderTargetBitmap((int)renderSize.Width, (int)renderSize.Height, 96, 96, PixelFormats.Pbgra32); batch.Content.Render(panel); batch.ImageVersion = plotVersion; finished = false; } // Find first batch that should be rendered batch = batches.FirstOrDefault(b => b.PanelVersion != plotVersion); if (batch != null) { int idx = batches.IndexOf(batch); if (!batch.Panel.IsMaster && idx * MarkersBatchSize < models.Count) { if (MarkerTemplate == null) { batch.Panel.Children.Clear(); } else { int batchSize = Math.Min(MarkersBatchSize, models.Count - idx * MarkersBatchSize); while (batch.Panel.Children.Count > batchSize) { batch.Panel.Children.RemoveAt(batch.Panel.Children.Count - 1); } for (int i = 0; i < batch.Panel.Children.Count; i++) { var mvm = models[i + idx * MarkersBatchSize]; var fe = batch.Panel.Children[i] as FrameworkElement; if (fe.DataContext != mvm) { fe.DataContext = mvm; } else { mvm.Notify(batch.ChangedProperties); } } for (int i = batch.Panel.Children.Count; i < batchSize; i++) { var fe = MarkerTemplate.LoadContent() as FrameworkElement; fe.DataContext = models[i + idx * MarkersBatchSize]; if (TooltipTemplate != null) { var tc = new ContentControl { Content = fe.DataContext, ContentTemplate = TooltipTemplate }; ToolTipService.SetToolTip(fe, tc); } batch.Panel.Children.Add(fe); } markersDrawn += batchSize; } batch.IsLayoutUpdated = false; batch.Image.Visibility = System.Windows.Visibility.Collapsed; batch.Image.RenderTransform = null; batch.Panel.Visibility = System.Windows.Visibility.Visible; batch.Panel.InvalidateMeasure(); batch.PanelVersion = plotVersion; batch.ClearChangedProperties(); } finished = false; } if (finished) { idleTask.Stop(); isDrawing = false; if (currentTaskId != -1) { renderCompletion.OnNext(new MarkerGraphRenderCompletion(currentTaskId, markersDrawn)); currentTaskId = -1; } } }