/// <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>
        /// rule coordinates (x:[0..1], y:axis)
        /// </summary>
        /// <param name="icrc">The context.</param>
        void IRequireTransforms.Transforms(IChartRenderContext icrc)
        {
            if (ValueAxis == null)
            {
                return;
            }
            var matx = MatrixSupport.TransformFor(icrc.SeriesArea, ValueAxis);

            _trace.Verbose($"transforms sy:{matx.M22:F3} matx:{matx} sa:{icrc.SeriesArea}");
            if (ClipToDataRegion)
            {
                Path.Clip = new RectangleGeometry()
                {
                    Rect = icrc.SeriesArea
                };
            }
            var vx     = ValueAxis.For(Value);
            var offset = matx.Transform(new Point(0, vx));

            Rule.SetValue(Canvas.TopProperty, offset.Y);
            matx.OffsetY   = 0;
            Rule.Transform = new MatrixTransform()
            {
                Matrix = matx
            };
        }
Пример #3
0
        /// <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));
            }
        }
Пример #4
0
        /// <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));
            }
        }
Пример #5
0
        /// <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));
            }
        }
        /// <summary>
        /// Rule coordinates:
        ///		x: "normalized" [0..1] and scaled to the area-width
        ///		y: "axis" scale
        /// </summary>
        /// <param name="icrc">The context.</param>
        void IRequireRender.Render(IChartRenderContext icrc)
        {
            if (ValueAxis == null)
            {
                return;
            }
            _trace.Verbose($"{Name} max:{Value1} min:{Value2}");
            var vmin = ValueAxis.For(Value2);
            var vmax = ValueAxis.For(Value1);
            var mmin = DoMinMax ? Math.Min(vmin, vmax) : vmin;
            var mmax = DoMinMax ? Math.Max(vmin, vmax) : vmax;

#if false
            Value1Rule.StartPoint = new Point(0, mmin);
            Value1Rule.EndPoint   = new Point(1, mmin);
            Value2Rule.StartPoint = new Point(0, mmax);
            Value2Rule.EndPoint   = new Point(1, mmax);
            Band.Rect             = new Rect(Value1Rule.StartPoint, Value2Rule.EndPoint);
#else
            Band.Rect = new Rect(new Point(0, 0), new Point(1, mmax - mmin));
#endif
            Dirty = false;
        }
        /// <summary>
        /// rule coordinates (x:[0..1], y:axis)
        /// </summary>
        /// <param name="icrc">The context.</param>
        void IRequireTransforms.Transforms(IChartRenderContext icrc)
        {
            if (ValueAxis == null)
            {
                return;
            }
            var matx = MatrixSupport.TransformFor(icrc.SeriesArea, ValueAxis);

            _trace.Verbose($"transforms sy:{matx.M22:F3} matx:{matx} sa:{icrc.SeriesArea}");
            if (ClipToDataRegion)
            {
                Value1Path.Clip = new RectangleGeometry()
                {
                    Rect = icrc.SeriesArea
                };
                Value2Path.Clip = new RectangleGeometry()
                {
                    Rect = icrc.SeriesArea
                };
                BandPath.Clip = new RectangleGeometry()
                {
                    Rect = icrc.SeriesArea
                };
            }
#if true
            var vmin    = ValueAxis.For(Value2);
            var vmax    = ValueAxis.For(Value1);
            var mmin    = DoMinMax ? Math.Min(vmin, vmax) : vmin;
            var mmax    = DoMinMax ? Math.Max(vmin, vmax) : vmax;
            var offset1 = matx.Transform(new Point(0, vmin));
            Value1Path.SetValue(Canvas.TopProperty, offset1.Y);
            var offset2 = matx.Transform(new Point(0, vmax));
            Value2Path.SetValue(Canvas.TopProperty, offset2.Y);
            var matx2 = matx;
            matx2.OffsetY        = 0;
            Value1Rule.Transform = new MatrixTransform()
            {
                Matrix = matx2
            };
            Value2Rule.Transform = new MatrixTransform()
            {
                Matrix = matx2
            };
            BandPath.SetValue(Canvas.TopProperty, offset1.Y);
            Band.Transform = new MatrixTransform()
            {
                Matrix = matx2
            };
#else
            Value1Rule.Transform = new MatrixTransform()
            {
                Matrix = matx
            };
            Value2Rule.Transform = new MatrixTransform()
            {
                Matrix = matx
            };
            Band.Transform = new MatrixTransform()
            {
                Matrix = matx
            };
#endif
        }
Пример #9
0
        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));
            }
        }