/// <summary> /// Paints the axis style labels. /// </summary> /// <param name="g">Graphics environment.</param> /// <param name="coordSyst">The coordinate system. Used to get the path along the axis.</param> /// <param name="scale">Scale.</param> /// <param name="tickSpacing">If not <c>null</c>, this parameter provides a custom tick spacing that is used instead of the default tick spacing of the scale.</param> /// <param name="styleInfo">Information about begin of axis, end of axis.</param> /// <param name="outerDistance">Distance between axis and labels.</param> /// <param name="useMinorTicks">If true, minor ticks are shown.</param> public virtual void Paint(IGraphicsContext3D g, G3DCoordinateSystem coordSyst, Scale scale, TickSpacing tickSpacing, CSAxisInformation styleInfo, double outerDistance, bool useMinorTicks) { _cachedAxisStyleInfo = styleInfo; CSLineID styleID = styleInfo.Identifier; Scale raxis = scale; TickSpacing ticking = tickSpacing; var math = Matrix4x3.Identity; Logical3D r0 = styleID.GetLogicalPoint(styleInfo.LogicalValueAxisOrg); Logical3D r1 = styleID.GetLogicalPoint(styleInfo.LogicalValueAxisEnd); Logical3D outer; double[] relpositions; AltaxoVariant[] ticks; if (useMinorTicks) { relpositions = ticking.GetMinorTicksNormal(raxis); ticks = ticking.GetMinorTicksAsVariant(); } else { relpositions = ticking.GetMajorTicksNormal(raxis); ticks = ticking.GetMajorTicksAsVariant(); } if (!_suppressedLabels.IsEmpty) { var filteredTicks = new List <AltaxoVariant>(); var filteredRelPositions = new List <double>(); for (int i = 0; i < ticks.Length; i++) { if (_suppressedLabels.ByValues.Contains(ticks[i])) { continue; } if (_suppressedLabels.ByNumbers.Contains(i)) { continue; } if (_suppressedLabels.ByNumbers.Contains(i - ticks.Length)) { continue; } filteredTicks.Add(ticks[i]); filteredRelPositions.Add(relpositions[i]); } ticks = filteredTicks.ToArray(); relpositions = filteredRelPositions.ToArray(); } IMeasuredLabelItem[] labels = _labelFormatting.GetMeasuredItems(g, _font, ticks); double emSize = _font.Size; CSAxisSide labelSide = null != _labelSide ? _labelSide.Value : styleInfo.PreferredLabelSide; var labelOutlines = new RectangularObjectOutline[ticks.Length]; for (int i = 0; i < ticks.Length; i++) { double r = relpositions[i]; if (!Altaxo.Calc.RMath.IsInIntervalCC(r, -1000, 1000)) { continue; } outer = coordSyst.GetLogicalDirection(styleID.ParallelAxisNumber, labelSide); PointD3D tickorg = coordSyst.GetPositionAndNormalizedDirection(r0, r1, r, outer, out var outVector); PointD3D tickend = tickorg + outVector * outerDistance; var msize = labels[i].Size; var morg = tickend; if (_automaticRotationShift) { // if this option is choosen, we have to find a shift value that shifts the center of the text outwards so that the bounding box of the text will not cross the plane that is // defined by the tickend point and the normal vector outVector // Assume that the text is now centered x, y, and z around the point tickend (but here we use origin instead tickend) math = Matrix4x3.NewRotation(_rotationX, _rotationY, _rotationZ); // we have to find all points with negative distance to the plane spanned by tickend and the vector outVector (but again instead of tickend we use origin) var msizePad = msize + new VectorD3D( (_font.Size * 1) / 3, // whereas above and below text no padding is neccessary, it is optically nicer to have left and right padding of the string by 1/6 of font size. 0, (_font.Size * 1) / 3 // same padding applies to z ); var crect = new RectangleD3D((PointD3D)(-0.5 * msizePad), msizePad); // our text centered around origin double shift = 0; foreach (PointD3D p in crect.Vertices) { PointD3D ps = math.Transform(p); double distance = Math3D.GetDistancePointToPlane(ps, PointD3D.Empty, outVector); if (-distance > shift) { shift = -distance; // only negative distances will count here } } morg += outVector * shift; } else { morg = morg.WithXPlus(outVector.X * _font.Size / 3); } var mrect = new RectangleD3D(morg, msize); if (_automaticRotationShift) { mrect = AdjustRectangle(mrect, Alignment.Center, Alignment.Center, Alignment.Center); } else { mrect = AdjustRectangle(mrect, _alignmentX, _alignmentY, _alignmentZ); } math = Matrix4x3.Identity; math.TranslatePrepend(morg.X, morg.Y, morg.Z); if (_rotationZ != 0) { math.RotationZDegreePrepend(_rotationZ); } if (_rotationY != 0) { math.RotationYDegreePrepend(_rotationY); } if (_rotationX != 0) { math.RotationXDegreePrepend(_rotationX); } math.TranslatePrepend((mrect.X - morg.X + emSize * _offsetX), (mrect.Y - morg.Y + emSize * _offsetY), (mrect.Z - morg.Z + emSize * _offsetZ)); var gs = g.SaveGraphicsState(); g.PrependTransform(math); if (_backgroundStyle != null) { var itemRectangle = new RectangleD3D(PointD3D.Empty, msize); _backgroundStyle.Measure(itemRectangle); _backgroundStyle.Draw(g, itemRectangle); } labels[i].Draw(g, _brush, PointD3D.Empty); labelOutlines[i] = new RectangularObjectOutline(new RectangleD3D(PointD3D.Empty, msize), math); g.RestoreGraphicsState(gs); // Restore the graphics state } _cachedLabelOutlines = labelOutlines; }