/// <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)); } }
/// <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 <Image> ElementPipeline(int index, double valuex, double valuey, object item, Recycler <Image, ItemState <Image> > recycler, Evaluators evs) { var mappedy = ValueAxis.For(valuey); var mappedx = CategoryAxis.For(valuex); var markerx = mappedx + MarkerOffset; _trace.Verbose($"[{index}] {valuey} ({markerx},{mappedy})"); // TODO allow a MarkerTemplateSelector and a value Selector/Formatter // no path yet var el = recycler.Next(null); if (el == null) { return(null); } var cs = evs.LabelFor(item); if (cs == null) { return(new ItemState_Matrix <Image>(index, mappedx, MarkerOffset, mappedy, el.Item2)); } else { return(new ItemStateCustom_Matrix <Image>(index, mappedx, MarkerOffset, mappedy, cs, el.Item2)); } }
/// <summary> /// Prepare the item state, but no <see cref="Geometry"/>. /// </summary> /// <param name="index"></param> /// <param name="valuex"></param> /// <param name="valuey"></param> /// <param name="item"></param> /// <param name="evs"></param> /// <returns></returns> ItemState <Path> ElementPipeline(int index, double valuex, double valuey, object item, Evaluators evs) { var mappedy = ValueAxis.For(valuey); var mappedx = CategoryAxis.For(valuex); var linex = mappedx + CategoryAxisOffset; _trace.Verbose($"{Name}[{index}] v:({valuex},{valuey}) m:({linex},{mappedy})"); var cs = evs.LabelFor(item); if (cs == null) { return(new ItemState <Path>(index, mappedx, CategoryAxisOffset, mappedy, Segments)); } else { return(new ItemStateCustom <Path>(index, mappedx, CategoryAxisOffset, mappedy, cs, Segments)); } }
/// <summary> /// Create the next series state. /// </summary> /// <param name="index"></param> /// <param name="valuex"></param> /// <param name="item"></param> /// <param name="recycler"></param> /// <param name="evs"></param> /// <returns>New instance or NULL.</returns> SeriesItemState ElementPipeline(int index, double valuex, object item, Recycler <Path, SeriesItemState> recycler, Evaluators evs) { var leftx = CategoryAxis.For(valuex); var barx = BarOffset; var rightx = barx + BarWidth; var sis = evs.byl == null ? new SeriesItemState(index, leftx, BarOffset) : new SeriesItemState_Custom(index, leftx, BarOffset, evs.byl.For(item)); for (int ix = 0; ix < evs.bys.Length; ix++) { var valuey = CoerceValue(item, evs.bys[ix]); if (double.IsNaN(valuey)) { continue; } var colbase = valuey >= 0 ? sis.Max : sis.Min; var colend = colbase + valuey; var y1 = ValueAxis.For(colend); var y2 = ValueAxis.For(colbase); var topy = Math.Max(y1, y2); var bottomy = Math.Min(y1, y2); sis.UpdateLimits(y1); _trace.Verbose($"{Name}[{index},{ix}] {valuey} ({barx},{topy}) ({rightx},{bottomy}) sis ({sis.Min},{sis.Max})"); var path = recycler.Next(null); if (path == null) { return(null); } var shim = new GeometryWithOffsetShim <RectangleGeometry>() { PathData = new RectangleGeometry() { Rect = new Rect(new Point(barx, topy), new Point(rightx, bottomy)) } }; path.Item2.DataContext = shim; BindTo(ColumnStack[ix], "PathStyle", path.Item2, FrameworkElement.StyleProperty); // bind offset BindTo(shim, nameof(shim.Offset), path.Item2, Canvas.LeftProperty); UpdateLimits(valuex, sis.Min, sis.Max); sis.Elements.Add(new Tuple <double, Path>(valuey, path.Item2)); } return(sis); }
/// <summary> /// Core element processing. /// The <see cref="RectangleGeometry"/> inside the <see cref="Path"/> is now location-invariant wrt x-axis. /// This means that during incremental updates, no re-calculation is required, only adjusting the <see cref="Canvas.LeftProperty"/>. /// </summary> /// <param name="index"></param> /// <param name="valuex"></param> /// <param name="valuey"></param> /// <param name="item"></param> /// <param name="recycler"></param> /// <param name="byl"></param> /// <returns></returns> ItemState <Path> ElementPipeline(int index, double valuex, double valuey, object item, Recycler <Path, ItemState <Path> > recycler, BindingEvaluator byl) { var y1 = ValueAxis.For(valuey); var y2 = ValueAxis.For(0); var topy = Math.Max(y1, y2); var bottomy = Math.Min(y1, y2); var leftx = CategoryAxis.For(valuex); var barx = BarOffset; var rightx = barx + BarWidth; _trace.Verbose($"{Name}[{index}] {valuey} ({barx},{topy}) ({rightx},{bottomy})"); var path = recycler.Next(null); if (path == null) { return(null); } var shim = new GeometryWithOffsetShim <RectangleGeometry>() { PathData = new RectangleGeometry() { Rect = new Rect(new Point(barx, topy), new Point(rightx, bottomy)) } }; path.Item2.DataContext = shim; // connect the shim to template root element's Visibility BindTo(shim, nameof(shim.Visibility), path.Item2, UIElement.VisibilityProperty); BindTo(shim, nameof(shim.Offset), path.Item2, Canvas.LeftProperty); if (byl == null) { return(new SeriesItemState_Double(index, leftx, BarOffset, y1, path.Item2)); } else { var cs = byl.For(item); return(new SeriesItemState_Custom(index, leftx, BarOffset, y1, cs, path.Item2)); } }
ItemState <Path> ElementPipeline( int index, double valuex, double valueO, double valueH, double valueL, double valueC, object item, Recycler <Path, ItemState <Path> > recycler, BindingEvaluator bvl ) { // map through axes var y1 = ValueAxis.For(valueO); var y2 = ValueAxis.For(valueC); var y3 = ValueAxis.For(valueH); var y4 = ValueAxis.For(valueL); var leftx = CategoryAxis.For(valuex); var offsetx = BarOffset; var rightx = offsetx + BarWidth; // force them to be a min/max var topy = Math.Max(y1, y2); var bottomy = Math.Min(y1, y2); var highy = Math.Max(y3, y4); var lowy = Math.Min(y3, y4); _trace.Verbose($"{Name}[{index}] {valueO}/{valueH}/{valueL}/{valueC} ({offsetx},{topy}) ({rightx},{bottomy})"); // create geometry var path = recycler.Next(null); if (path == null) { return(null); } var pg = new PathGeometry(); // body (open/close) var body = PathHelper.Rectangle(offsetx, topy, rightx, bottomy); pg.Figures.Add(body); // upper shadow (high) var centerx = offsetx + (rightx - offsetx) / 2; var upper = PathHelper.Line(centerx, topy, centerx, highy); pg.Figures.Add(upper); // lower shadow (low) var lower = PathHelper.Line(centerx, bottomy, centerx, lowy); pg.Figures.Add(lower); var shim = new GeometryWithOffsetShim <PathGeometry>() { PathData = pg }; path.Item2.DataContext = shim; // establish the style for "forward" or "reverse" polarity BindTo(this, valueO < valueC ? nameof(PathStyle) : nameof(ReversePathStyle), path.Item2, Path.StyleProperty); // bind offset BindTo(shim, nameof(shim.Offset), path.Item2, Canvas.LeftProperty); var figs = new Tuple <double, PathFigure> [4]; figs[0] = new Tuple <double, PathFigure>(y1, body); figs[1] = new Tuple <double, PathFigure>(y2, body); figs[2] = new Tuple <double, PathFigure>(y3, upper); figs[3] = new Tuple <double, PathFigure>(y4, lower); if (bvl == null) { return(new SeriesItemState(index, leftx, BarOffset, y1, path.Item2, figs)); } else { var cs = bvl.For(item); return(new SeriesItemState_Custom(index, leftx, BarOffset, y1, cs, path.Item2, figs)); } }