/// <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}"); }
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; } } }
/// <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}"); }