/// <summary>
        /// Adjust transforms for the various components.
        /// Geometry: scaled to actual values in cartesian coordinates as indicated by axes.
        /// </summary>
        /// <param name="icrc"></param>
        void IRequireTransforms.Transforms(IChartRenderContext icrc)
        {
            if (CategoryAxis == null || ValueAxis == null)
            {
                return;
            }
            if (ItemState.Count == 0)
            {
                return;
            }
            var mp = MatrixSupport.DataArea(CategoryAxis, ValueAxis, icrc.Area, 1);
            // cancel x-offset
            var proj2 = mp.Item2;

            proj2.OffsetX = 0;
            // get the local marker matrix
            var marker = MatrixSupport.LocalFor(mp.Item1, MarkerWidth, icrc.Area, -MarkerOrigin.X, -MarkerOrigin.Y);
            // get the offset matrix
            var mato = MatrixSupport.Multiply(mp.Item1, proj2);

            foreach (var state in ItemState)
            {
                // assemble Mk * M * P transform for this path
                var iworld = (state as IItemStateMatrix).World;
                var model  = MatrixSupport.Multiply(marker, iworld);
                var matx   = MatrixSupport.Multiply(model, proj2);
                var mt     = new MatrixTransform()
                {
                    Matrix = matx
                };
                if (state.Element.DataContext is GeometryWithOffsetShim <Geometry> gs)
                {
                    gs.GeometryTransform = mt;
                    var output = mato.Transform(new Point(state.XValue, 0));
                    gs.Offset = icrc.Area.Left + output.X;
                }
                else
                {
                    state.Element.Data.Transform = mt;
                }
                if (ClipToDataRegion)
                {
                    state.Element.Clip = new RectangleGeometry()
                    {
                        Rect = icrc.SeriesArea
                    };
                }
            }
            _trace.Verbose($"{Name} mat:{mp.Item1} clip:{icrc.SeriesArea}");
        }
        /// <summary>
        /// The geometry is a circle of radius 10 centered at (0,0).
        /// </summary>
        /// <param name="area">Projection area.</param>
        /// <returns>MP matrix for this component's geometry.</returns>
        Matrix IProvideCustomTransform.TransformFor(Rect area)
        {
            var range = 2.0 * RADIUS;
            var mmatx = new Matrix(1.0 / range, 0, 0, -1.0 / range, RADIUS / range, RADIUS / range);
            var pmatx = MatrixSupport.ProjectForQuadrant(4, area);
            // lock aspect ratio to smallest dimension so it's a circle
            var scale = Math.Min(pmatx.M11, pmatx.M22);

            pmatx.M11 = pmatx.M22 = scale;
            // TODO adjust offset so it's centered
            var matx = MatrixSupport.Multiply(mmatx, pmatx);

            _trace.Verbose($"{Name} mat:{matx} M:{mmatx} P:{pmatx}");
            return(matx);
        }
        void IRequireAfterAxesFinalized.AxesFinalized(IChartRenderContext icrc)
        {
            if (icrc.Type == RenderType.TransformsOnly)
            {
                return;
            }
            var mat   = MatrixSupport.DataArea(CategoryAxis1, CategoryAxis2, icrc.SeriesArea, 4);
            var matmp = MatrixSupport.Multiply(mat.Item1, mat.Item2);

            _trace.Verbose($"{Name} matmp:{matmp} clip:{icrc.SeriesArea}");
            var mt = new MatrixTransform()
            {
                Matrix = matmp
            };

            StyleGenerator?.Reset();
            foreach (var istate in ItemState)
            {
                if (istate.Element.DataContext is GeometryWith2OffsetShim <RectangleGeometry> gs)
                {
                    gs.GeometryTransform = mt;
                }
                else
                {
                    istate.Element.Data.Transform = mt;
                }
                if (StyleGenerator != null)
                {
                    var style = StyleGenerator.For(new Category2StyleContext(CategoryAxis1, CategoryAxis2, this, new double[3] {
                        istate.Value, istate.XValue, (istate as IProvideYValue).YValue
                    }));
                    if (style != null)
                    {
                        istate.Element.Style = style;
                    }
                    else
                    {
                        if (PathStyle != null)
                        {
                            BindTo(this, nameof(PathStyle), istate.Element, FrameworkElement.StyleProperty);
                        }
                    }
                }
            }
            StyleGenerator?.UpdateLegend(new Category2StyleContext(CategoryAxis1, CategoryAxis2, this, new double[0]));
        }
        /// <summary>
        /// Adjust transforms for the various components.
        /// Geometry: scaled to actual values in cartesian coordinates as indicated by axes.
        /// </summary>
        /// <param name="icrc"></param>
        void IRequireTransforms.Transforms(IChartRenderContext icrc)
        {
            if (CategoryAxis1 == null || CategoryAxis2 == null)
            {
                return;
            }
            if (ItemState.Count == 0)
            {
                return;
            }
            // TODO quad depends on orientations of both axes this is assuming default orientations
            var mat   = MatrixSupport.DataArea(CategoryAxis1, CategoryAxis2, icrc.SeriesArea, 4);
            var matmp = MatrixSupport.Multiply(mat.Item1, mat.Item2);

            _trace.Verbose($"{Name} matmp:{matmp} clip:{icrc.SeriesArea}");
            var mt = new MatrixTransform()
            {
                Matrix = matmp
            };

            foreach (var state in ItemState)
            {
                if (state.Element.DataContext is GeometryWith2OffsetShim <RectangleGeometry> gs)
                {
                    gs.GeometryTransform = mt;
                    var output = matmp.Transform(new Point(state.XValue, (state as IProvideYValue).YValue));
                    gs.Offset  = output.X - icrc.SeriesArea.Left;
                    gs.Offset2 = output.Y - icrc.SeriesArea.Top;
                }
                else
                {
                    state.Element.Data.Transform = mt;
                }
                if (ClipToDataRegion)
                {
                    var cg = new RectangleGeometry()
                    {
                        Rect = icrc.SeriesArea
                    };
                    state.Element.Clip = cg;
                }
            }
        }
Example #5
0
        /// <summary>
        /// Adjust transforms for the various components.
        /// Geometry: scaled to actual values in cartesian coordinates as indicated by axes.
        /// </summary>
        /// <param name="icrc"></param>
        void IRequireTransforms.Transforms(IChartRenderContext icrc)
        {
            if (CategoryAxis == null || ValueAxis == null)
            {
                return;
            }
            if (ItemState.Count == 0)
            {
                return;
            }
            var mp = MatrixSupport.DataArea(CategoryAxis, ValueAxis, icrc.Area, 1);
            // get the offset matrix
            var mato = MatrixSupport.Multiply(mp.Item1, mp.Item2);
            // TODO preserve aspect ratio of image; will require ImageOpened evh
            var dimx = MarkerWidth * mato.M11;

            _trace.Verbose($"dimx {dimx}");
            foreach (var state in ItemState)
            {
                if (state.Element.DataContext is ImageSourceShim iss)
                {
                    var output = mato.Transform(new Point(state.XValue + MarkerOffset, state.Value));
                    _trace.Verbose($"output {output.X:F1},{output.Y:F1}  value {state.XValue},{state.Value}  image {state.Element.ActualWidth:F1}x{state.Element.ActualHeight:F1}");
                    var hw = dimx * MarkerOrigin.X;
                    var hh = dimx * MarkerOrigin.Y;
                    // these have bindings so effect is immediate
                    iss.OffsetX = output.X - hw;
                    iss.OffsetY = output.Y - hh;
                    iss.Width   = dimx;
                    iss.Height  = dimx;
                }
                if (ClipToDataRegion)
                {
                    state.Element.Clip = new RectangleGeometry()
                    {
                        Rect = icrc.SeriesArea
                    };
                }
            }
            _trace.Verbose($"{Name} mat:{mp.Item1} clip:{icrc.SeriesArea}");
        }