Beispiel #1
0
		/// <summary>
		/// Gets the sub points for a given range. For step connections, at least one points needs to be inserted inbetween two original points, for some step connection styles two points.
		/// </summary>
		/// <param name="pdata">The pdata.</param>
		/// <param name="range">The range.</param>
		/// <param name="layer">The layer.</param>
		/// <param name="connectCircular">if set to <c>true</c> [connect circular].</param>
		/// <param name="numberOfPointsPerOriginalPoint">The number of points per original point. For most step styles one additional point is inserted, thus the return value is 2. For some connection styles, two points are inserted inbetween two original points, thus the return value will be 3.</param>
		/// <param name="lastIndex">The last index.</param>
		/// <returns></returns>
		protected abstract PointF[] GetStepPolylinePoints(
		PointF[] pdata,
		IPlotRange range,
		IPlotArea layer,
		bool connectCircular,
		out int numberOfPointsPerOriginalPoint,
		out int lastIndex);
Beispiel #2
0
        protected override PointF[] GetStepPolylinePoints(
            PointF[] allLinePoints,
            IPlotRange range,
            IPlotArea layer,
            bool connectCircular,
            out int numberOfPointsPerOriginalPoint,
            out int lastIndex)
        {
            numberOfPointsPerOriginalPoint = 2;

            var subLinePoints = new PointF[range.Length * 2 - 1 + (connectCircular ? 2 : 0)];
            int end = range.UpperBound - 1;
            int i, j;

            for (i = 0, j = range.LowerBound; j < end; i += 2, j++)
            {
                subLinePoints[i]       = allLinePoints[j];
                subLinePoints[i + 1].X = allLinePoints[j + 1].X;
                subLinePoints[i + 1].Y = allLinePoints[j].Y;
            }
            subLinePoints[i] = allLinePoints[j];
            lastIndex        = i;

            if (connectCircular)
            {
                subLinePoints[i + 1] = new PointF(allLinePoints[range.LowerBound].X, allLinePoints[j].Y);
                subLinePoints[i + 2] = allLinePoints[range.LowerBound];
                lastIndex            = i + 2;
            }
            return(subLinePoints);
        }
Beispiel #3
0
        /// <summary>
        /// Template to get a fill path.
        /// </summary>
        /// <param name="gp">Graphics path to fill with data.</param>
        /// <param name="pdata">The plot data. Don't use the Range property of the pdata, since it is overriden by the next argument.</param>
        /// <param name="range">The plot range to use.</param>
        /// <param name="layer">Graphics layer.</param>
        /// <param name="fillDirection">Designates a bound to fill to.</param>
        /// <param name="linePoints">The points that mark the line.</param>
        /// <param name="connectCircular">If true, a circular connection is drawn.</param>
        private void FillOneRange_PreprocessedPoints(
            GraphicsPath gp,
            Processed2DPlotData pdata,
            IPlotRange range,
            IPlotArea layer,
            CSPlaneID fillDirection,
            PointF[] linePoints,
            bool connectCircular,
            double logicalShiftX,
            double logicalShiftY
            )
        {
            if (connectCircular)
            {
                gp.AddBeziers(linePoints);
                gp.CloseFigure();
            }
            else
            {
                Logical3D r0 = layer.GetLogical3D(pdata, range.OriginalFirstPoint);
                r0.RX += logicalShiftX;
                r0.RY += logicalShiftY;
                layer.CoordinateSystem.GetIsolineFromPlaneToPoint(gp, fillDirection, r0);
                gp.AddBeziers(linePoints);

                Logical3D r1 = layer.GetLogical3D(pdata, range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + linePoints.Length - 1));
                r1.RX += logicalShiftX;
                r1.RY += logicalShiftY;

                layer.CoordinateSystem.GetIsolineFromPointToPlane(gp, r1, fillDirection);
                layer.CoordinateSystem.GetIsolineOnPlane(gp, fillDirection, r1, r0);

                gp.CloseFigure();
            }
        }
Beispiel #4
0
 /// <summary>
 /// Gets the sub points for a given range. For step connections, at least one points needs to be inserted inbetween two original points, for some step connection styles two points.
 /// </summary>
 /// <param name="pdata">The pdata.</param>
 /// <param name="range">The range.</param>
 /// <param name="layer">The layer.</param>
 /// <param name="connectCircular">if set to <c>true</c> [connect circular].</param>
 /// <param name="numberOfPointsPerOriginalPoint">The number of points per original point. For most step styles one additional point is inserted, thus the return value is 2. For some connection styles, two points are inserted inbetween two original points, thus the return value will be 3.</param>
 /// <param name="lastIndex">The last index.</param>
 /// <returns></returns>
 protected abstract PointF[] GetStepPolylinePoints(
     PointF[] pdata,
     IPlotRange range,
     IPlotArea layer,
     bool connectCircular,
     out int numberOfPointsPerOriginalPoint,
     out int lastIndex);
		protected override PointF[] GetStepPolylinePoints(
		PointF[] allLinePoints,
		IPlotRange range,
		IPlotArea layer,
		bool connectCircular,
		out int numberOfPointsPerOriginalPoint,
		out int lastIndex)
		{
			numberOfPointsPerOriginalPoint = 3;

			PointF[] subLinePoints = new PointF[numberOfPointsPerOriginalPoint * (range.Length - 1 + (connectCircular ? 1 : 0)) + 1];
			int end = range.UpperBound - 1;
			int i, j;
			for (i = 0, j = range.LowerBound; j < end; i += numberOfPointsPerOriginalPoint, j++)
			{
				subLinePoints[i] = allLinePoints[j];
				subLinePoints[i + 1] = new PointF((allLinePoints[j].X + allLinePoints[j + 1].X) / 2, allLinePoints[j].Y);
				subLinePoints[i + 2] = new PointF((allLinePoints[j].X + allLinePoints[j + 1].X) / 2, allLinePoints[j + 1].Y);
			}
			subLinePoints[i] = allLinePoints[j];
			lastIndex = i;

			if (connectCircular)
			{
				subLinePoints[i + 1] = new PointF((allLinePoints[j].X + allLinePoints[range.LowerBound].X) / 2, allLinePoints[j].Y);
				subLinePoints[i + 2] = new PointF((allLinePoints[j].X + allLinePoints[range.LowerBound].X) / 2, allLinePoints[range.LowerBound].Y);
				subLinePoints[i + 3] = allLinePoints[range.LowerBound];
				lastIndex = i + 3;
			}
			return subLinePoints;
		}
Beispiel #6
0
		/// <summary>
		/// Template to make a line draw.
		/// </summary>
		/// <param name="g">Graphics context.</param>
		/// <param name="allLinePoints">The plot data. Don't use the Range property of the pdata, since it is overriden by the next argument.</param>
		/// <param name="range">The plot range to use.</param>
		/// <param name="layer">Graphics layer.</param>
		/// <param name="linePen">The pen to draw the line.</param>
		/// <param name="symbolGap">The size of the symbol gap. Argument is the original index of the data. The return value is the absolute symbol gap at this index.
		/// This function is null if no symbol gap is required.</param>
		/// <param name="skipFrequency">Skip frequency. Normally 1, thus all gaps are taken into account. If 2, only every 2nd gap is taken into account, and so on.</param>
		/// <param name="connectCircular">If true, there is a line connecting the start and the end of the range.</param>
		/// <param name="linePlotStyle">The line plot style.</param>
		public override void PaintOneRange(
			Graphics g,
			PointF[] allLinePoints,
			IPlotRange range,
			IPlotArea layer,
			PenX linePen,
			Func<int, double> symbolGap,
			int skipFrequency,
			bool connectCircular,
			LinePlotStyle linePlotStyle)
		{
			if (range.Length < 2)
				return;

			int lastIdx;
			int numberOfPointsPerOriginalPoint;
			PointF[] stepPolylinePoints = GetStepPolylinePoints(allLinePoints, range, layer, connectCircular, out numberOfPointsPerOriginalPoint, out lastIdx);

			GraphicsPath gp = new GraphicsPath();

			if (null != symbolGap)
			{
				int end = range.UpperBound - 1;

				var subPointsLength = skipFrequency * numberOfPointsPerOriginalPoint + 1;
				for (int i = 0; i < range.Length; i += skipFrequency)
				{

					int partialPolylineLength = Math.Min(subPointsLength, stepPolylinePoints.Length - numberOfPointsPerOriginalPoint * i);
					if (partialPolylineLength < 2)
						continue; // happens probably at the end of the range if there are not enough points to draw

					double gapAtStart = symbolGap(range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + i));
					double gapAtEnd;
					if (connectCircular && skipFrequency >= (range.Length - i))
						gapAtEnd = symbolGap(range.OriginalFirstPoint);
					else if (skipFrequency <= (range.Length - 1 - i))
						gapAtEnd = symbolGap(range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + i + skipFrequency));
					else
						gapAtEnd = 0;

					int startOfPartialPolyline = numberOfPointsPerOriginalPoint * i;
					var shortenedPolyline = stepPolylinePoints.ShortenPartialPolylineByDistanceFromStartAndEnd(startOfPartialPolyline, startOfPartialPolyline + partialPolylineLength - 1, gapAtStart / 2, gapAtEnd / 2);

					if (null != shortenedPolyline)
						g.DrawLines(linePen, shortenedPolyline);
				}
			}
			else
			{
				if (connectCircular)
					g.DrawPolygon(linePen, stepPolylinePoints);
				else
					g.DrawLines(linePen, stepPolylinePoints);
			}
		}
		/// <summary>
		/// Template to make a line draw.
		/// </summary>
		/// <param name="g">Graphics context.</param>
		/// <param name="allLinePoints">The plot data. Don't use the Range property of the pdata, since it is overriden by the next argument.</param>
		/// <param name="range">The plot range to use.</param>
		/// <param name="layer">Graphics layer.</param>
		/// <param name="pen">The pen to draw the line.</param>
		/// <param name="symbolGap">The size of the symbol gap. Argument is the original index of the data. The return value is the absolute symbol gap at this index.
		/// This function is null if no symbol gap is required.</param>
		/// <param name="skipFrequency">Skip frequency. Normally 1, thus all gaps are taken into account. If 2, only every 2nd gap is taken into account, and so on.</param>
		/// <param name="connectCircular">If true, the line is connected circular, and the area is the polygon inside of that circular connection.</param>
		/// <param name="linePlotStyle">The line plot style.</param>
		public abstract void PaintOneRange(
			Graphics g,
			PointF[] allLinePoints,
			IPlotRange range,
			IPlotArea layer,
			PenX pen,
			Func<int, double> symbolGap,
			int skipFrequency,
			bool connectCircular,
			LinePlotStyle linePlotStyle
);
 /// <summary>
 /// Template to make a line draw.
 /// </summary>
 /// <param name="g">Graphics context.</param>
 /// <param name="allLinePoints">The plot data. Don't use the Range property of the pdata, since it is overriden by the next argument.</param>
 /// <param name="range">The plot range to use.</param>
 /// <param name="layer">Graphics layer.</param>
 /// <param name="pen">The pen to draw the line.</param>
 /// <param name="symbolGap">The size of the symbol gap. Argument is the original index of the data. The return value is the absolute symbol gap at this index.
 /// This function is null if no symbol gap is required.</param>
 /// <param name="skipFrequency">Skip frequency. Normally 1, thus all gaps are taken into account. If 2, only every 2nd gap is taken into account, and so on.</param>
 /// <param name="connectCircular">If true, the line is connected circular, and the area is the polygon inside of that circular connection.</param>
 /// <param name="linePlotStyle">The line plot style.</param>
 public abstract void PaintOneRange(
     Graphics g,
     PointF[] allLinePoints,
     IPlotRange range,
     IPlotArea layer,
     PenX pen,
     Func <int, double> symbolGap,
     int skipFrequency,
     bool connectCircular,
     LinePlotStyle linePlotStyle
     );
		/// <summary>
		/// Template to get a fill path.
		/// </summary>
		/// <param name="gp">Graphics path to fill with data.</param>
		/// <param name="pdata">The plot data. Do not use the plot point positions from here, since they are not shifted when shifting group styles are present.</param>
		/// <param name="range">The plot range to use.</param>
		/// <param name="layer">Graphics layer.</param>
		/// <param name="fillDirection">Designates a bound to fill to.</param>
		/// <param name="ignoreMissingDataPoints">If true, missing data points are ignored.</param>
		/// <param name="connectCircular">If true, the line is connected circular, and the area is the polygon inside of that circular connection.</param>
		/// <param name="allLinePointsShiftedAlready">The plot positions, already shifted when a logical shift needed to be applied. Don't use the Range property of the pdata, since it is overriden by the next argument.</param>
		/// <param name="logicalShiftX">The logical shift in x-direction.</param>
		/// <param name="logicalShiftY">The logical shift in x-direction.</param>
		public abstract void FillOneRange(
			GraphicsPath gp,
			Processed2DPlotData pdata,
			IPlotRange range,
			IPlotArea layer,
			CSPlaneID fillDirection,
			bool ignoreMissingDataPoints,
			bool connectCircular,
			PointF[] allLinePointsShiftedAlready,
			double logicalShiftX,
			double logicalShiftY
			);
 /// <summary>
 /// Template to get a fill path.
 /// </summary>
 /// <param name="gp">Graphics path to fill with data.</param>
 /// <param name="pdata">The plot data. Do not use the plot point positions from here, since they are not shifted when shifting group styles are present.</param>
 /// <param name="range">The plot range to use.</param>
 /// <param name="layer">Graphics layer.</param>
 /// <param name="fillDirection">Designates a bound to fill to.</param>
 /// <param name="ignoreMissingDataPoints">If true, missing data points are ignored.</param>
 /// <param name="connectCircular">If true, the line is connected circular, and the area is the polygon inside of that circular connection.</param>
 /// <param name="allLinePointsShiftedAlready">The plot positions, already shifted when a logical shift needed to be applied. Don't use the Range property of the pdata, since it is overriden by the next argument.</param>
 /// <param name="logicalShiftX">The logical shift in x-direction.</param>
 /// <param name="logicalShiftY">The logical shift in x-direction.</param>
 public abstract void FillOneRange(
     GraphicsPath gp,
     Processed2DPlotData pdata,
     IPlotRange range,
     IPlotArea layer,
     CSPlaneID fillDirection,
     bool ignoreMissingDataPoints,
     bool connectCircular,
     PointF[] allLinePointsShiftedAlready,
     double logicalShiftX,
     double logicalShiftY
     );
Beispiel #11
0
        /// <summary>
        /// Template to make a line draw.
        /// </summary>
        /// <param name="g">Graphics context.</param>
        /// <param name="allLinePoints">The plot data. Don't use the Range property of the pdata, since it is overriden by the next argument.</param>
        /// <param name="range">The plot range to use.</param>
        /// <param name="layer">Graphics layer.</param>
        /// <param name="linePen">The pen to draw the line.</param>
        /// <param name="symbolGap">The size of the symbol gap. Argument is the original index of the data. The return value is the absolute symbol gap at this index.
        /// This function is null if no symbol gap is required.</param>
        /// <param name="skipFrequency">Skip frequency. Normally 1, thus all gaps are taken into account. If 2, only every 2nd gap is taken into account, and so on.</param>
        /// <param name="connectCircular">If true, there is a line connecting the start and the end of the range.</param>
        /// <param name="linePlotStyle">The line plot style.</param>
        public override void PaintOneRange(
            Graphics g,
            PointF[] allLinePoints,
            IPlotRange range,
            IPlotArea layer,
            PenX linePen,
            Func <int, double> symbolGap,
            int skipFrequency,
            bool connectCircular,
            LinePlotStyle linePlotStyle)
        {
            if (range.Length <= 1)
            {
                return; // seems to be only a single point, thus no connection possible
            }
            PointF[] stepPolylinePoints = GetStepPolylinePoints(allLinePoints, range, layer, connectCircular, out var numberOfPointsPerOriginalPoint, out var lastIdx);

            if (null != symbolGap)
            {
                foreach (var segmentRange in GetSegmentRanges(range, symbolGap, skipFrequency, connectCircular))
                {
                    if (segmentRange.IsFullRangeClosedCurve) // test if this is a closed polygon without any gaps -> draw a closed polygon and return
                    {
                        // use the whole circular arry to draw a closed polygon without any gaps
                        g.DrawPolygon(linePen, stepPolylinePoints);
                    }
                    else
                    {
                        int plotIndexAtStart  = segmentRange.IndexAtSubRangeStart * numberOfPointsPerOriginalPoint;
                        int plotIndexAtEnd    = segmentRange.IndexAtSubRangeEnd * numberOfPointsPerOriginalPoint;
                        var shortenedPolyline = stepPolylinePoints.ShortenPartialPolylineByDistanceFromStartAndEnd(plotIndexAtStart, plotIndexAtEnd, segmentRange.GapAtSubRangeStart / 2, segmentRange.GapAtSubRangeEnd / 2);

                        if (null != shortenedPolyline)
                        {
                            g.DrawLines(linePen, shortenedPolyline);
                        }
                    }
                }
            }
            else
            {
                if (connectCircular)
                {
                    g.DrawPolygon(linePen, stepPolylinePoints);
                }
                else
                {
                    g.DrawLines(linePen, stepPolylinePoints);
                }
            }
        }
Beispiel #12
0
        /// <inheritdoc/>
        public override void FillOneRange(
            GraphicsPath gp,
            Processed2DPlotData pdata,
            IPlotRange range,
            IPlotArea layer,
            CSPlaneID fillDirection,
            bool ignoreMissingDataPoints,
            bool connectCircular,
            PointF[] allLinePointsShiftedAlready,
            double logicalShiftX,
            double logicalShiftY
            )
        {
            if (range.Length < 2)
            {
                return;
            }

            PointF[] linePoints = Segment2Connection_GetSubPoints(allLinePointsShiftedAlready, range, layer, connectCircular, out var lastIdx);

            if (connectCircular)
            {
                gp.AddLines(linePoints);
                gp.CloseFigure();
            }
            else
            {
                int offs = range.LowerBound;
                for (int i = 0; i < linePoints.Length - 1; i += 2)
                {
                    Logical3D r0 = layer.GetLogical3D(pdata, range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + i));
                    r0.RX += logicalShiftX;
                    r0.RY += logicalShiftY;

                    layer.CoordinateSystem.GetIsolineFromPlaneToPoint(gp, fillDirection, r0);
                    gp.AddLine(linePoints[i].X, linePoints[i].Y, linePoints[i + 1].X, linePoints[i + 1].Y);
                    Logical3D r1 = layer.GetLogical3D(pdata, range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + i + 1));
                    r1.RX += logicalShiftX;
                    r1.RY += logicalShiftY;

                    layer.CoordinateSystem.GetIsolineFromPointToPlane(gp, r1, fillDirection);
                    layer.CoordinateSystem.GetIsolineOnPlane(gp, fillDirection, r1, r0);
                    gp.StartFigure();
                }

                gp.CloseFigure();
            }
        }
Beispiel #13
0
        /// <inheritdoc/>
        public override void FillOneRange(
            GraphicsPath gp,
            Processed2DPlotData pdata,
            IPlotRange range,
            IPlotArea layer,
            CSPlaneID fillDirection,
            bool ignoreMissingDataPoints,
            bool connectCircular,
            PointF[] allLinePoints,
            double logicalShiftX,
            double logicalShiftY
            )
        {
            if (range.Length < 4)
            {
                return;
            }

            if (connectCircular)
            {
                var circularLinePointsLengthM1 = 2 + TrimToValidBezierLength(range.Length);
                var circularLinePoints         = new PointF[circularLinePointsLengthM1 + 1];
                Array.Copy(allLinePoints, range.LowerBound, circularLinePoints, 0, range.Length); // Extract
                circularLinePoints[circularLinePointsLengthM1] = circularLinePoints[0];

                // amend missing control points
                if (circularLinePointsLengthM1 - range.Length >= 1)
                {
                    circularLinePoints[circularLinePointsLengthM1 - 1] = GdiExtensionMethods.Interpolate(circularLinePoints[circularLinePointsLengthM1 - 3], circularLinePoints[circularLinePointsLengthM1], 0.5); // Last Control point should be halfway between
                }
                if (circularLinePointsLengthM1 - range.Length >= 2)
                {
                    circularLinePoints[circularLinePointsLengthM1 - 2] = GdiExtensionMethods.Interpolate(circularLinePoints[circularLinePointsLengthM1 - 3], circularLinePoints[circularLinePointsLengthM1], 0.5); // Middle Control point should be halfway between previous fixed point and last(=first) fixed point
                }
                FillOneRange_PreprocessedPoints(gp, pdata, range, layer, fillDirection, circularLinePoints, connectCircular, logicalShiftX, logicalShiftY);
            }
            else
            {
                var trimmedLinePointsLength = TrimToValidBezierLength(range.Length);
                var trimmedLinePoints       = new PointF[trimmedLinePointsLength];
                Array.Copy(allLinePoints, range.LowerBound, trimmedLinePoints, 0, trimmedLinePointsLength); // Extract
                FillOneRange_PreprocessedPoints(gp, pdata, range, layer, fillDirection, trimmedLinePoints, connectCircular, logicalShiftX, logicalShiftY);
            }
        }
Beispiel #14
0
        private PointF[] Segment2Connection_GetSubPoints(
            PointF[] allLinePoints,
            IPlotRange range,
            IPlotArea layer,
            bool connectCircular,
            out int lastIdx)
        {
            var circularLinePoints = new PointF[range.Length + (connectCircular ? 1 : 0)];

            Array.Copy(allLinePoints, range.LowerBound, circularLinePoints, 0, range.Length); // Extract
            if (connectCircular)
            {
                circularLinePoints[range.Length] = circularLinePoints[0];
            }

            lastIdx = range.Length - 1 + (connectCircular ? 1 : 0);

            return(circularLinePoints);
        }
Beispiel #15
0
        /// <inheritdoc/>
        public override void FillOneRange(
            GraphicsPath gp,
            Processed2DPlotData pdata,
            IPlotRange range,
            IPlotArea layer,
            CSPlaneID fillDirection,
            bool ignoreMissingDataPoints,
            bool connectCircular,
            PointF[] allLinePoints,
            double logicalShiftX,
            double logicalShiftY
            )
        {
            var circularLinePoints = new PointF[range.Length + (connectCircular ? 1 : 0)];

            Array.Copy(allLinePoints, range.LowerBound, circularLinePoints, 0, range.Length); // Extract
            if (connectCircular)
            {
                circularLinePoints[circularLinePoints.Length - 1] = circularLinePoints[0];
            }

            if (connectCircular)
            {
                gp.AddLines(circularLinePoints);
                gp.CloseFigure();
            }
            else // not circular
            {
                Logical3D r0 = layer.GetLogical3D(pdata, range.OriginalFirstPoint);
                r0.RX += logicalShiftX;
                r0.RY += logicalShiftY;

                layer.CoordinateSystem.GetIsolineFromPlaneToPoint(gp, fillDirection, r0);
                gp.AddLines(circularLinePoints);
                Logical3D r1 = layer.GetLogical3D(pdata, connectCircular ? range.OriginalFirstPoint : range.OriginalLastPoint);
                r1.RX += logicalShiftX;
                r1.RY += logicalShiftY;

                layer.CoordinateSystem.GetIsolineFromPointToPlane(gp, r1, fillDirection);
                layer.CoordinateSystem.GetIsolineOnPlane(gp, fillDirection, r1, r0);
                gp.CloseFigure();
            }
        }
Beispiel #16
0
        private PointF[] Segment3Connection_GetSubPoints(
            PointF[] allLinePoints,
            IPlotRange range,
            IPlotArea layer,
            bool connectCircular,
            out int lastIndex)
        {
            var layerSize     = layer.Size;
            var subLinePoints = new PointF[range.Length + (connectCircular ? 1 : 0)];

            Array.Copy(allLinePoints, range.LowerBound, subLinePoints, 0, range.Length); // Extract
            if (connectCircular)
            {
                subLinePoints[range.Length] = subLinePoints[0];
            }

            lastIndex = range.Length - 1 + (connectCircular ? 1 : 0);

            return(subLinePoints);
        }
Beispiel #17
0
 /// <inheritdoc/>
 public override void FillOneRange(
     GraphicsPath gp,
     Processed2DPlotData pdata,
     IPlotRange range,
     IPlotArea layer,
     CSPlaneID fillDirection,
     bool ignoreMissingDataPoints,
     bool connectCircular,
     PointF[] allLinePoints,
     double logicalShiftX,
     double logicalShiftY
     )
 {
     if (range.Length < 2)
     {
         return;
     }
     PointF[] linepts = GetStepPolylinePoints(allLinePoints, range, layer, connectCircular, out var numberOfPointsPerOriginalPoint, out var lastIdx);
     FillOneRange(gp, pdata, range, layer, fillDirection, linepts, connectCircular, allLinePoints, logicalShiftX, logicalShiftY);
 }
Beispiel #18
0
        /// <summary>
        /// Template to get a fill path.
        /// </summary>
        /// <param name="gp">Graphics path to fill with data.</param>
        /// <param name="pdata">The plot data. Don't use the Range property of the pdata, since it is overriden by the next argument.</param>
        /// <param name="range">The plot range to use.</param>
        /// <param name="layer">Graphics layer.</param>
        /// <param name="fillDirection">Designates a bound to fill to.</param>
        /// <param name="linePoints">The points that mark the line.</param>
        /// <param name="connectCircular">If true, a circular connection is drawn.</param>
        /// <param name="allLinePointsShiftedAlready">The plot positions, already shifted when a logical shift needed to be applied. Don't use the Range property of the pdata, since it is overriden by the next argument.</param>
        /// <param name="logicalShiftX">The logical shift in x-direction.</param>
        /// <param name="logicalShiftY">The logical shift in x-direction.</param>
        public virtual void FillOneRange(
            GraphicsPath gp,
            Processed2DPlotData pdata,
            IPlotRange range,
            IPlotArea layer,
            CSPlaneID fillDirection,
            PointF[] linePoints,
            bool connectCircular,
            PointF[] allLinePointsShiftedAlready,
            double logicalShiftX,
            double logicalShiftY
            )
        {
            if (connectCircular)
            {
                gp.AddLines(linePoints);
                gp.CloseFigure();
            }
            else
            {
                Logical3D r0 = layer.GetLogical3D(pdata, range.OriginalFirstPoint);
                r0.RX += logicalShiftX;
                r0.RY += logicalShiftY;

                layer.CoordinateSystem.GetIsolineFromPlaneToPoint(gp, fillDirection, r0);
                gp.AddLines(linePoints);

                Logical3D r1 = layer.GetLogical3D(pdata, range.OriginalLastPoint);
                r1.RX += logicalShiftX;
                r1.RY += logicalShiftY;

                layer.CoordinateSystem.GetIsolineFromPointToPlane(gp, r1, fillDirection);
                layer.CoordinateSystem.GetIsolineOnPlane(gp, fillDirection, r1, r0);
                gp.CloseFigure();
            }
        }
Beispiel #19
0
		/// <summary>
		/// Template to make a line draw.
		/// </summary>
		/// <param name="g">Graphics context.</param>
		/// <param name="allLinePoints">The plot data. Don't use the Range property of the pdata, since it is overriden by the next argument.</param>
		/// <param name="range">The plot range to use.</param>
		/// <param name="layer">Graphics layer.</param>
		/// <param name="linePen">The pen to draw the line.</param>
		/// <param name="symbolGap">The size of the symbol gap. Argument is the original index of the data. The return value is the absolute symbol gap at this index.
		/// This function is null if no symbol gap is required.</param>
		/// <param name="skipFrequency">Skip frequency. Normally 1, thus all gaps are taken into account. If 2, only every 2nd gap is taken into account, and so on.</param>
		/// <param name="connectCircular">If true, there is a line connecting the start and the end of the range.</param>
		/// <param name="linePlotStyle">The line plot style.</param>
		public override void PaintOneRange(
			Graphics g,
			PointF[] allLinePoints,
			IPlotRange range,
			IPlotArea layer,
			PenX linePen,
			Func<int, double> symbolGap,
			int skipFrequency,
			bool connectCircular,
			LinePlotStyle linePlotStyle)
		{
			PointF[] subLinePoints;
			if (range.LowerBound == 0 && range.UpperBound == allLinePoints.Length)
			{
				// under optimal conditions we can use allLinePoints directly
				subLinePoints = allLinePoints;
			}
			else
			{
				// otherwise, make a new array
				subLinePoints = new PointF[range.Length];
				Array.Copy(allLinePoints, range.LowerBound, subLinePoints, 0, range.Length); // Extract
			}

			int lastIdx = range.Length - 1;
			var layerSize = layer.Size;

			if (connectCircular)
			{
				if (symbolGap != null)
				{
					// convert points to bezier segments
					var bezierSegments = GdiExtensionMethods.ClosedCardinalSplineToBezierSegments(subLinePoints, subLinePoints.Length);
					var subBezierSegments = new PointF[0];
					int subPointLengthM1, subBezierLength;
					for (int i = 0; i < (range.Length); i += skipFrequency)
					{
						subPointLengthM1 = Math.Min(skipFrequency, range.Length - i);
						int originalIndexAtStart = range.GetOriginalRowIndexFromPlotPointIndex(i + range.LowerBound);
						double gapAtStart = symbolGap(originalIndexAtStart);
						int originalIndexAtEnd = ((i + skipFrequency) < range.Length) ? range.GetOriginalRowIndexFromPlotPointIndex(i + range.LowerBound + skipFrequency) : range.OriginalFirstPoint;
						double gapAtEnd = symbolGap(originalIndexAtEnd);
						subBezierLength = 3 * subPointLengthM1 + 1;
						if (subBezierSegments.Length != subBezierLength)
							subBezierSegments = new PointF[subBezierLength];

						Array.Copy(bezierSegments, i * 3, subBezierSegments, 0, subBezierLength);
						var shortenedBezierSegments = GdiExtensionMethods.ShortenBezierCurve(subBezierSegments, gapAtStart / 2, gapAtEnd / 2);

						if (null != shortenedBezierSegments)
						{
							g.DrawBeziers(linePen, shortenedBezierSegments);
						}
					}
				}
				else
				{
					g.DrawClosedCurve(linePen, subLinePoints);
				}
			}
			else
			{
				if (symbolGap != null)
				{
					// convert points to bezier segments
					var bezierSegments = GdiExtensionMethods.OpenCardinalSplineToBezierSegments(subLinePoints, subLinePoints.Length);
					var subBezierSegments = new PointF[0];
					int subPointLengthM1, subBezierLength;
					for (int i = 0; i < (range.Length - 1); i += skipFrequency)
					{
						subPointLengthM1 = Math.Min(skipFrequency, range.Length - 1 - i);
						int originalIndex = range.GetOriginalRowIndexFromPlotPointIndex(i + range.LowerBound);
						double gapAtStart = symbolGap(originalIndex);
						double gapAtEnd = subPointLengthM1 == skipFrequency ? symbolGap(range.GetOriginalRowIndexFromPlotPointIndex(i + range.LowerBound + skipFrequency)) : 0;
						subBezierLength = 3 * subPointLengthM1 + 1;
						if (subBezierSegments.Length != subBezierLength)
							subBezierSegments = new PointF[subBezierLength];

						Array.Copy(bezierSegments, i * 3, subBezierSegments, 0, subBezierLength);
						var shortenedBezierSegments = GdiExtensionMethods.ShortenBezierCurve(subBezierSegments, gapAtStart / 2, gapAtEnd / 2);

						if (null != shortenedBezierSegments)
						{
							g.DrawBeziers(linePen, shortenedBezierSegments);
						}
					}
				}
				else
				{
					g.DrawCurve(linePen, subLinePoints);
				}
			}
		}
Beispiel #20
0
		/// <inheritdoc/>
		public override void FillOneRange(
		GraphicsPath gp,
			Processed2DPlotData pdata,
			IPlotRange range,
			IPlotArea layer,
			CSPlaneID fillDirection,
			bool ignoreMissingDataPoints,
			bool connectCircular,
			PointF[] allLinePointsShiftedAlready,
			double logicalShiftX,
			double logicalShiftY
		)
		{
			if (range.Length < 2)
				return;

			int lastIdx;
			PointF[] linePoints = Segment2Connection_GetSubPoints(allLinePointsShiftedAlready, range, layer, connectCircular, out lastIdx);


			if (connectCircular)
			{
				gp.AddLines(linePoints);
				gp.CloseFigure();
			}
			else
			{
				int offs = range.LowerBound;
				for (int i = 0; i < linePoints.Length - 1; i += 2)
				{
					Logical3D r0 = layer.GetLogical3D(pdata, range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + i));
					r0.RX += logicalShiftX;
					r0.RY += logicalShiftY;

					layer.CoordinateSystem.GetIsolineFromPlaneToPoint(gp, fillDirection, r0);
					gp.AddLine(linePoints[i].X, linePoints[i].Y, linePoints[i + 1].X, linePoints[i + 1].Y);
					Logical3D r1 = layer.GetLogical3D(pdata, range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + i + 1));
					r1.RX += logicalShiftX;
					r1.RY += logicalShiftY;

					layer.CoordinateSystem.GetIsolineFromPointToPlane(gp, r1, fillDirection);
					layer.CoordinateSystem.GetIsolineOnPlane(gp, fillDirection, r1, r0);
					gp.StartFigure();
				}

				gp.CloseFigure();
			}
		}
Beispiel #21
0
		/// <summary>
		/// Template to make a line draw.
		/// </summary>
		/// <param name="g">Graphics context.</param>
		/// <param name="allLinePoints">The plot data. Don't use the Range property of the pdata, since it is overriden by the next argument.</param>
		/// <param name="range">The plot range to use.</param>
		/// <param name="layer">Graphics layer.</param>
		/// <param name="linePen">The pen to draw the line.</param>
		/// <param name="symbolGap">The size of the symbol gap. Argument is the original index of the data. The return value is the absolute symbol gap at this index.
		/// This function is null if no symbol gap is required.</param>
		/// <param name="skipFrequency">Skip frequency. Normally 1, thus all gaps are taken into account. If 2, only every 2nd gap is taken into account, and so on.</param>
		/// <param name="connectCircular">If true, there is a line connecting the start and the end of the range.</param>
		/// <param name="linePlotStyle">The line plot style.</param>
		public override void PaintOneRange(
			Graphics g,
			PointF[] allLinePoints,
			IPlotRange range,
			IPlotArea layer,
			PenX linePen,
			Func<int, double> symbolGap,
			int skipFrequency,
			bool connectCircular,
			LinePlotStyle linePlotStyle)
		{
			int lastIdx;
			PointF[] subLinePoints = Segment2Connection_GetSubPoints(allLinePoints, range, layer, connectCircular, out lastIdx);

			GraphicsPath gp = new GraphicsPath();
			int i;

			// special efforts are necessary to realize a line/symbol gap
			// I decided to use a path for this
			// and hope that not so many segments are added to the path due
			// to the exclusion criteria that a line only appears between two symbols (rel<0.5)
			// if the symbols do not overlap. So for a big array of points it is very likely
			// that the symbols overlap and no line between the symbols needs to be plotted
			if (null != symbolGap)
			{
				float startx, starty, stopx, stopy;
				for (i = 0; i < lastIdx; i += 2)
				{

					var diff = GdiExtensionMethods.Subtract(subLinePoints[i + 1], subLinePoints[i]);
					var diffLength = GdiExtensionMethods.VectorLength(diff);

					int originalIndex = range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + i);
					double gapAtStart = 0 == i % skipFrequency ? symbolGap(originalIndex) : 0;
					double gapAtEnd;
					if ((0 == (i + 1) % skipFrequency) || ((i + 1) == range.Length))
					{
						gapAtEnd = ((i + 1) != range.Length) ? symbolGap(originalIndex + 1) : symbolGap(range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound));
					}
					else
					{
						gapAtEnd = 0;
					}

					var relAtStart = (float)(0.5 * gapAtStart / diffLength); // 0.5 because symbolGap is the full gap between two lines, thus between the symbol center and the beginning of the line it is only 1/2
					var relAtEnd = (float)(0.5 * gapAtEnd / diffLength); // 0.5 because symbolGap is the full gap between two lines, thus between the symbol center and the beginning of the line it is only 1/2
					if ((relAtStart + relAtEnd) < 1) // a line only appears if sum of the gaps  is smaller than 1
					{
						startx = subLinePoints[i].X + relAtStart * diff.X;
						starty = subLinePoints[i].Y + relAtStart * diff.Y;
						stopx = subLinePoints[i + 1].X - relAtEnd * diff.X;
						stopy = subLinePoints[i + 1].Y - relAtEnd * diff.Y;

						gp.AddLine(startx, starty, stopx, stopy);
						gp.StartFigure();
					}
				} // end for
				g.DrawPath(linePen, gp);
				gp.Reset();
			}
			else // no line symbol gap required, so we can use DrawLines to draw the lines
			{
				for (i = 0; i < lastIdx; i += 2)
				{
					gp.AddLine(subLinePoints[i].X, subLinePoints[i].Y, subLinePoints[i + 1].X, subLinePoints[i + 1].Y);
					gp.StartFigure();
				} // end for
				g.DrawPath(linePen, gp);
				gp.Reset();
			}
		}
Beispiel #22
0
		public void PaintOneRange(Graphics g, IPlotArea layer, IPlotRange range, Processed2DPlotData pdata)
		{
			if (this._labelColumnProxy.Document == null)
				return;

			_cachedStringFormat.Alignment = GdiExtensionMethods.ToGdi(_alignmentX);
			_cachedStringFormat.LineAlignment = GdiExtensionMethods.ToGdi(_alignmentY);

			if (null != _attachedPlane)
				_attachedPlane = layer.UpdateCSPlaneID(_attachedPlane);

			var ptArray = pdata.PlotPointsInAbsoluteLayerCoordinates;
			Altaxo.Data.IReadableColumn labelColumn = this._labelColumnProxy.Document;

			bool isUsingVariableColorForLabelText = null != _cachedColorForIndexFunction && IsColorReceiver;
			bool isUsingVariableColorForLabelBackground = null != _cachedColorForIndexFunction &&
				(null != _backgroundStyle && _backgroundStyle.SupportsBrush && (_backgroundColorLinkage == ColorLinkage.Dependent || _backgroundColorLinkage == ColorLinkage.PreserveAlpha));
			bool isUsingVariableColor = isUsingVariableColorForLabelText || isUsingVariableColorForLabelBackground;
			BrushX clonedTextBrush = null;
			BrushX clonedBackBrush = null;
			if (isUsingVariableColorForLabelText)
				clonedTextBrush = _brush.Clone();
			if (isUsingVariableColorForLabelBackground)
				clonedBackBrush = _backgroundStyle.Brush.Clone();

			// save the graphics stat since we have to translate the origin
			var gs = g.Save();

			double xpos = 0, ypos = 0;
			double xpre, ypre;
			double xdiff, ydiff;

			bool isFormatStringContainingBraces = _labelFormatString?.IndexOf('{') >= 0;
			var culture = System.Threading.Thread.CurrentThread.CurrentCulture;

			bool mustUseLogicalCoordinates = null != this._attachedPlane || 0 != _cachedLogicalShiftX || 0 != _cachedLogicalShiftY;

			int lower = range.LowerBound;
			int upper = range.UpperBound;
			for (int j = lower; j < upper; j+=_skipFrequency)
			{
				int originalRowIndex = range.GetOriginalRowIndexFromPlotPointIndex(j);
				string label;
				if (string.IsNullOrEmpty(_labelFormatString))
				{
					label = labelColumn[originalRowIndex].ToString();
				}
				else if (!isFormatStringContainingBraces)
				{
					label = labelColumn[originalRowIndex].ToString(_labelFormatString, culture);
				}
				else
				{
					// the label format string can contain {0} for the label column item, {1} for the row index, {2} .. {4} for the x, y and z component of the data point
					label = string.Format(_labelFormatString, labelColumn[originalRowIndex], originalRowIndex, pdata.GetPhysical(0, originalRowIndex), pdata.GetPhysical(1, originalRowIndex), pdata.GetPhysical(2, originalRowIndex));
				}

				if (string.IsNullOrEmpty(label))
					continue;

				double localSymbolSize = _symbolSize;
				if (null != _cachedSymbolSizeForIndexFunction)
				{
					localSymbolSize = _cachedSymbolSizeForIndexFunction(originalRowIndex);
				}

				double localFontSize = _fontSizeOffset + _fontSizeFactor * localSymbolSize;
				if (!(localFontSize > 0))
					continue;

				_font = _font.WithSize(localFontSize);

				// Start of preparation of brushes, if a variable color is used
				if (isUsingVariableColor)
				{
					Color c = _cachedColorForIndexFunction(originalRowIndex);

					if (isUsingVariableColorForLabelText)
					{
						clonedTextBrush.Color = new NamedColor(AxoColor.FromArgb(c.A, c.R, c.G, c.B), "e");
					}
					if (isUsingVariableColorForLabelBackground)
					{
						if (_backgroundColorLinkage == ColorLinkage.PreserveAlpha)
							clonedBackBrush.Color = new NamedColor(AxoColor.FromArgb(clonedBackBrush.Color.Color.A, c.R, c.G, c.B), "e");
						else
							clonedBackBrush.Color = new NamedColor(AxoColor.FromArgb(c.A, c.R, c.G, c.B), "e");
					}
				}
				// end of preparation of brushes for variable colors

				if (mustUseLogicalCoordinates) // we must use logical coordinates because either there is a shift of logical coordinates, or an attached plane
				{
					Logical3D r3d = layer.GetLogical3D(pdata, originalRowIndex);
					r3d.RX += _cachedLogicalShiftX;
					r3d.RY += _cachedLogicalShiftY;

					if (null != this._attachedPlane)
					{
						var pp = layer.CoordinateSystem.GetPointOnPlane(this._attachedPlane, r3d);
						xpre = pp.X;
						ypre = pp.Y;
					}
					else
					{
						PointD3D pt;
						layer.CoordinateSystem.LogicalToLayerCoordinates(r3d, out xpre, out ypre);
					}
				}
				else // no shifting, thus we can use layer coordinates
				{
					xpre = ptArray[j].X;
					ypre = ptArray[j].Y;
				}

				xdiff = xpre - xpos;
				ydiff = ypre - ypos;
				xpos = xpre;
				ypos = ypre;
				g.TranslateTransform((float)xdiff, (float)ydiff);
				if (this._rotation != 0)
					g.RotateTransform((float)-this._rotation);

				this.PaintOneItem(g, label, localSymbolSize, clonedTextBrush, clonedBackBrush);

				if (this._rotation != 0)
					g.RotateTransform((float)this._rotation);

			}

			g.Restore(gs); // Restore the graphics state
		}
Beispiel #23
0
		private PointF[] Segment2Connection_GetSubPoints(
			PointF[] allLinePoints,
			IPlotRange range,
			IPlotArea layer,
			bool connectCircular,
			out int lastIdx)
		{
			PointF[] circularLinePoints = new PointF[range.Length + (connectCircular ? 1 : 0)];
			Array.Copy(allLinePoints, range.LowerBound, circularLinePoints, 0, range.Length); // Extract
			if (connectCircular)
			{
				circularLinePoints[range.Length] = circularLinePoints[0];
			}

			lastIdx = range.Length - 1 + (connectCircular ? 1 : 0);

			return circularLinePoints;
		}
Beispiel #24
0
		protected void PaintOneRange(int axisNumber, Graphics g, IPlotArea layer, IPlotRange range, Processed2DPlotData pdata)
		{
			const double logicalClampMinimum = -10;
			const double logicalClampMaximum = 11;

			_skipFrequency = Math.Max(1, _skipFrequency);

			// Plot error bars for the dependent variable (y)
			var ptArray = pdata.PlotPointsInAbsoluteLayerCoordinates;
			var posErrCol = PositiveErrorColumn;
			var negErrCol = NegativeErrorColumn;

			if (null != posErrCol && !typeof(double).IsAssignableFrom(posErrCol.ItemType))
				posErrCol = null; // TODO make this an runtime paint error to be reported

			if (null != negErrCol && !typeof(double).IsAssignableFrom(negErrCol.ItemType))
				negErrCol = null; // TODO make this an runtime paint error to be reported

			if (posErrCol == null && negErrCol == null)
				return; // nothing to do if both error columns are null

			var strokePen = _pen.Clone();

			System.Drawing.Drawing2D.GraphicsPath errorBarPath = new System.Drawing.Drawing2D.GraphicsPath();

			Region oldClippingRegion = g.Clip;
			Region newClip = (Region)oldClippingRegion.Clone();


			int lower = range.LowerBound;
			int upper = range.UpperBound;

			for (int j = lower; j < upper; j += _skipFrequency)
			{
				int originalRowIndex = range.GetOriginalRowIndexFromPlotPointIndex(j);
				double symbolSize = null == _cachedSymbolSizeForIndexFunction ? _symbolSize : _cachedSymbolSizeForIndexFunction(originalRowIndex);
				strokePen.Width = (_lineWidth1Offset + _lineWidth1Factor * symbolSize);

				if (null != _cachedColorForIndexFunction)
					strokePen.Color = GdiColorHelper.ToNamedColor(_cachedColorForIndexFunction(originalRowIndex), "VariableColor");
				if (null != strokePen.EndCap)
					strokePen.EndCap = strokePen.EndCap.WithMinimumAbsoluteAndRelativeSize(symbolSize * _endCapSizeFactor + _endCapSizeOffset, 1 + 1E-6);

				AltaxoVariant vMeanPhysical = pdata.GetPhysical(axisNumber, originalRowIndex);
				Logical3D logicalMean = layer.GetLogical3D(pdata, originalRowIndex);
				logicalMean.RX += _cachedLogicalShiftX;
				logicalMean.RY += _cachedLogicalShiftY;

				if (!Calc.RMath.IsInIntervalCC(logicalMean.RX, logicalClampMinimum, logicalClampMaximum))
					continue;
				if (!Calc.RMath.IsInIntervalCC(logicalMean.RY, logicalClampMinimum, logicalClampMaximum))
					continue;

				var vMeanLogical = logicalMean.GetR(axisNumber);

				Logical3D logicalPos = logicalMean;
				Logical3D logicalNeg = logicalMean;
				bool logicalPosValid = false;
				bool logicalNegValid = false;

				switch (_meaningOfValues)
				{
					case ValueInterpretation.AbsoluteError:
						{
							if (posErrCol != null)
							{
								var vPosLogical = layer.Scales[axisNumber].PhysicalVariantToNormal(vMeanPhysical + Math.Abs(posErrCol[originalRowIndex]));
								vPosLogical = Calc.RMath.ClampToInterval(vPosLogical, logicalClampMinimum, logicalClampMaximum);
								logicalPos.SetR(axisNumber, vPosLogical);
								logicalPosValid = !logicalPos.IsNaN && vPosLogical != vMeanLogical;
							}

							if (negErrCol != null)
							{
								var vNegLogical = layer.Scales[axisNumber].PhysicalVariantToNormal(vMeanPhysical - Math.Abs(negErrCol[originalRowIndex]));
								vNegLogical = Calc.RMath.ClampToInterval(vNegLogical, logicalClampMinimum, logicalClampMaximum);
								logicalNeg.SetR(axisNumber, vNegLogical);
								logicalNegValid = !logicalNeg.IsNaN && vNegLogical != vMeanLogical;
							}
						}
						break;

					case ValueInterpretation.RelativeError:
						{
							if (posErrCol != null)
							{
								var vPosLogical = layer.Scales[axisNumber].PhysicalVariantToNormal(vMeanPhysical * (1 + Math.Abs(posErrCol[originalRowIndex])));
								vPosLogical = Calc.RMath.ClampToInterval(vPosLogical, logicalClampMinimum, logicalClampMaximum);
								logicalPos.SetR(axisNumber, vPosLogical);
								logicalPosValid = !logicalPos.IsNaN && vPosLogical != vMeanLogical;
							}

							if (negErrCol != null)
							{
								var vNegLogical = layer.Scales[axisNumber].PhysicalVariantToNormal(vMeanPhysical * (1 - Math.Abs(negErrCol[originalRowIndex])));
								vNegLogical = Calc.RMath.ClampToInterval(vNegLogical, logicalClampMinimum, logicalClampMaximum);
								logicalNeg.SetR(axisNumber, vNegLogical);
								logicalNegValid = !logicalNeg.IsNaN && vNegLogical != vMeanLogical;
							}
						}
						break;

					case ValueInterpretation.AbsoluteValue:
						{
							if (posErrCol != null)
							{
								var vPosLogical = layer.Scales[axisNumber].PhysicalVariantToNormal(posErrCol[originalRowIndex]);
								vPosLogical = Calc.RMath.ClampToInterval(vPosLogical, logicalClampMinimum, logicalClampMaximum);
								logicalPos.SetR(axisNumber, vPosLogical);
								logicalPosValid = !logicalPos.IsNaN && vPosLogical != vMeanLogical;
							}

							if (negErrCol != null)
							{
								var vNegLogical = layer.Scales[axisNumber].PhysicalVariantToNormal(negErrCol[originalRowIndex]);
								vNegLogical = Calc.RMath.ClampToInterval(vNegLogical, logicalClampMinimum, logicalClampMaximum);
								logicalNeg.SetR(axisNumber, vNegLogical);
								logicalNegValid = !logicalNeg.IsNaN && vNegLogical != vMeanLogical;
							}

							if (object.ReferenceEquals(negErrCol, posErrCol))
							{
								logicalNegValid = false; // then we need only to plot the positive column, since both colums are identical
							}
						}
						break;
				} // end switch

				if (!(logicalPosValid || logicalNegValid))
					continue; // nothing to do for this point if both pos and neg logical point are invalid.

				if (logicalNegValid)
				{
					errorBarPath.Reset();
					layer.CoordinateSystem.GetIsoline(errorBarPath, logicalMean, logicalNeg);
					PointF[] shortenedPathPoints = null;
					bool shortenedPathPointsCalculated = false;
					if (_useSymbolGap)
					{
						double gap = _symbolGapOffset + _symbolGapFactor * symbolSize;
						if (gap > 0)
						{
							errorBarPath.Flatten();
							var pathPoints = errorBarPath.PathPoints;
							shortenedPathPoints = GdiExtensionMethods.ShortenedBy(pathPoints, RADouble.NewAbs(gap / 2), RADouble.NewAbs(0));
							shortenedPathPointsCalculated = true;
							if (null == shortenedPathPoints && _forceVisibilityOfEndCap && !(strokePen.EndCap is Altaxo.Graph.Gdi.LineCaps.FlatCap))
							{
								var totalLineLength = GdiExtensionMethods.TotalLineLength(pathPoints);
								var shortTheLineBy = Math.Max(0, totalLineLength - 0.125 * strokePen.Width);
								shortenedPathPoints = GdiExtensionMethods.ShortenedBy(pathPoints, RADouble.NewAbs(shortTheLineBy), RADouble.NewAbs(0));
							}
						}
					}

					if (shortenedPathPointsCalculated)
					{
						if (null != shortenedPathPoints)
						{
							g.DrawLines(strokePen, shortenedPathPoints);
						}
					}
					else
					{
						g.DrawPath(strokePen, errorBarPath);
					}
				}

				if (logicalPosValid)
				{
					errorBarPath.Reset();
					layer.CoordinateSystem.GetIsoline(errorBarPath, logicalMean, logicalPos);
					PointF[] shortenedPathPoints = null;
					bool shortenedPathPointsCalculated = false;


					if (_useSymbolGap)
					{
						double gap = _symbolGapOffset + _symbolGapFactor * symbolSize;
						if (gap > 0)
						{
							errorBarPath.Flatten();
							var pathPoints = errorBarPath.PathPoints;
							shortenedPathPoints = GdiExtensionMethods.ShortenedBy(pathPoints, RADouble.NewAbs(gap / 2), RADouble.NewAbs(0));
							shortenedPathPointsCalculated = true;
							if (null == shortenedPathPoints && _forceVisibilityOfEndCap && !(strokePen.EndCap is Altaxo.Graph.Gdi.LineCaps.FlatCap))
							{
								var totalLineLength = GdiExtensionMethods.TotalLineLength(pathPoints);
								var shortTheLineBy = Math.Max(0, totalLineLength - 0.125 * strokePen.Width);
								shortenedPathPoints = GdiExtensionMethods.ShortenedBy(pathPoints, RADouble.NewAbs(shortTheLineBy), RADouble.NewAbs(0));
							}

						}
					}

					if (shortenedPathPointsCalculated)
					{
						if (null != shortenedPathPoints)
						{
							g.DrawLines(strokePen, shortenedPathPoints);
						}
					}
					else
					{
						g.DrawPath(strokePen, errorBarPath);
					}
				}
			}

			g.Clip = oldClippingRegion;

		}
Beispiel #25
0
        private void PaintOneRange(Graphics g, IPlotArea layer, IPlotRange range, Processed2DPlotData pdata)
        {
            // adjust the skip frequency if it was not set appropriate
            if (_skipFrequency <= 0)
            {
                _skipFrequency = 1;
            }

            var dropTargets = new List <CSPlaneID>(_dropTargets.Select(id => layer.UpdateCSPlaneID(id)));

            if (_additionalDropTargetIsEnabled)
            {
                CSPlaneID userPlane;
                if (_additionalDropTargetUsePhysicalBaseValue)
                {
                    userPlane = new CSPlaneID(_additionalDropTargetPerpendicularAxis, layer.Scales[_additionalDropTargetPerpendicularAxis].PhysicalVariantToNormal(_additionalDropTargetBaseValue));
                }
                else
                {
                    userPlane = new CSPlaneID(_additionalDropTargetPerpendicularAxis, _additionalDropTargetBaseValue);
                }
                dropTargets.Add(userPlane);
            }

            // paint the scatter style

            PointD3D pos   = PointD3D.Empty;
            var      gpath = new GraphicsPath();

            if (null == _cachedSymbolSizeForIndexFunction && null == _cachedColorForIndexFunction) // using a constant symbol size and constant color
            {
                // update pen widths
                var    pen = _pen.Clone();
                double w1  = _lineWidth1Offset + _lineWidth1Factor * _cachedSymbolSize;
                pen.Width = w1;

                var gapStart = 0.5 * (_gapAtStartOffset + _gapAtStartFactor * _cachedSymbolSize);
                var gapEnd   = 0.5 * (_gapAtEndOffset + _gapAtEndFactor * _cachedSymbolSize);

                int lower = range.LowerBound;
                int upper = range.UpperBound;

                for (int j = lower; j < upper; j += _skipFrequency)
                {
                    var originalRowIndex = range.GetOriginalRowIndexFromPlotPointIndex(j);

                    Logical3D r3d = layer.GetLogical3D(pdata, originalRowIndex);
                    r3d.RX += _cachedLogicalShiftX;
                    r3d.RY += _cachedLogicalShiftY;

                    foreach (CSPlaneID id in dropTargets)
                    {
                        gpath.Reset();
                        layer.CoordinateSystem.GetIsolineFromPointToPlane(gpath, r3d, id);
                        PointF[] shortenedPathPoints = null;
                        if (gapStart != 0 || gapEnd != 0)
                        {
                            gpath.Flatten();
                            var pathPoints = gpath.PathPoints;
                            shortenedPathPoints = GdiExtensionMethods.ShortenedBy(pathPoints, RADouble.NewAbs(gapStart), RADouble.NewAbs(gapEnd));
                            if (null != shortenedPathPoints)
                            {
                                g.DrawLines(pen, shortenedPathPoints);
                            }
                        }
                        else
                        {
                            g.DrawPath(pen, gpath);
                        }
                    }
                }
            }
            else // using a variable symbol size or variable symbol color
            {
                int lower = range.LowerBound;
                int upper = range.UpperBound;
                for (int j = lower; j < upper; j += _skipFrequency)
                {
                    var originalRowIndex = range.GetOriginalRowIndexFromPlotPointIndex(j);
                    var pen = _pen.Clone();
                    if (null == _cachedColorForIndexFunction)
                    {
                        _cachedSymbolSize = _cachedSymbolSizeForIndexFunction(originalRowIndex);
                        double w1 = _lineWidth1Offset + _lineWidth1Factor * _cachedSymbolSize;
                        pen.Width = w1;
                    }
                    else
                    {
                        _cachedSymbolSize = null == _cachedSymbolSizeForIndexFunction ? _cachedSymbolSize : _cachedSymbolSizeForIndexFunction(originalRowIndex);
                        double w1 = _lineWidth1Offset + _lineWidth1Factor * _cachedSymbolSize;

                        var customSymbolColor = _cachedColorForIndexFunction(originalRowIndex);
                        pen.Width = w1;
                        pen.Color = NamedColor.FromArgb(customSymbolColor.A, customSymbolColor.R, customSymbolColor.G, customSymbolColor.B);
                    }

                    var gapStart = 0.5 * (_gapAtStartOffset + _gapAtStartFactor * _cachedSymbolSize);
                    var gapEnd   = 0.5 * (_gapAtEndOffset + _gapAtEndFactor * _cachedSymbolSize);

                    Logical3D r3d = layer.GetLogical3D(pdata, originalRowIndex);
                    r3d.RX += _cachedLogicalShiftX;
                    r3d.RY += _cachedLogicalShiftY;

                    foreach (CSPlaneID id in _dropTargets)
                    {
                        gpath.Reset();
                        layer.CoordinateSystem.GetIsolineFromPointToPlane(gpath, r3d, id);
                        PointF[] shortenedPathPoints = null;
                        if (gapStart != 0 || gapEnd != 0)
                        {
                            gpath.Flatten();
                            var pathPoints = gpath.PathPoints;
                            shortenedPathPoints = GdiExtensionMethods.ShortenedBy(pathPoints, RADouble.NewAbs(gapStart), RADouble.NewAbs(gapEnd));
                            if (null != shortenedPathPoints)
                            {
                                g.DrawLines(pen, shortenedPathPoints);
                            }
                        }
                        else
                        {
                            g.DrawPath(pen, gpath);
                        }
                    }
                }
            }
        }
Beispiel #26
0
		/// <summary>
		/// Template to make a line draw.
		/// </summary>
		/// <param name="g">Graphics context.</param>
		/// <param name="allLinePoints">The plot data. Don't use the Range property of the pdata, since it is overriden by the next argument.</param>
		/// <param name="range">The plot range to use.</param>
		/// <param name="layer">Graphics layer.</param>
		/// <param name="linePen">The pen to draw the line.</param>
		/// <param name="symbolGap">The size of the symbol gap. Argument is the original index of the data. The return value is the absolute symbol gap at this index.
		/// This function is null if no symbol gap is required.</param>
		/// <param name="skipFrequency">Skip frequency. Normally 1, thus all gaps are taken into account. If 2, only every 2nd gap is taken into account, and so on.</param>
		/// <param name="connectCircular">If true, there is a line connecting the start and the end of the range.</param>
		/// <param name="linePlotStyle">The line plot style.</param>
		public override void PaintOneRange(
			Graphics g,
			PointF[] allLinePoints,
			IPlotRange range,
			IPlotArea layer,
			PenX linePen,
			Func<int, double> symbolGap,
			int skipFrequency,
			bool connectCircular,
			LinePlotStyle linePlotStyle)
		{
			PointF[] circularLinePoints;

			if (!connectCircular && range.LowerBound == 0 && range.UpperBound == allLinePoints.Length)
			{
				// under optimal conditions we can use allLinePoints directly
				circularLinePoints = allLinePoints;
			}
			else
			{
				// otherwise, make a new array
				circularLinePoints = new PointF[range.Length + (connectCircular ? 1 : 0)];
				Array.Copy(allLinePoints, range.LowerBound, circularLinePoints, 0, range.Length); // Extract
				if (connectCircular)
					circularLinePoints[circularLinePoints.Length - 1] = circularLinePoints[0];
			}

			int lastIdx = range.Length - 1 + (connectCircular ? 1 : 0);
			GraphicsPath gp = new GraphicsPath();
			var layerSize = layer.Size;
			var rangeLowerBound = range.LowerBound;

			// special efforts are necessary to realize a line/symbol gap
			// I decided to use a path for this
			// and hope that not so many segments are added to the path due
			// to the exclusion criteria that a line only appears between two symbols (rel<0.5)
			// if the symbols do not overlap. So for a big array of points it is very likely
			// that the symbols overlap and no line between the symbols needs to be plotted
			if (null != symbolGap)
			{
				float xdiff, ydiff, startx, starty, stopx, stopy;
				if (skipFrequency <= 1) // skip all scatter symbol gaps -> thus skipOffset can be ignored
				{
					for (int i = 0; i < lastIdx; i++)
					{
						xdiff = circularLinePoints[i + 1].X - circularLinePoints[i].X;
						ydiff = circularLinePoints[i + 1].Y - circularLinePoints[i].Y;
						var diffLength = System.Math.Sqrt(xdiff * xdiff + ydiff * ydiff);
						double gapAtStart = symbolGap(range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + i));
						double gapAtEnd;
						if (connectCircular && skipFrequency >= (range.Length - i))
							gapAtEnd = symbolGap(range.OriginalFirstPoint);
						else if (skipFrequency <= (range.Length - 1 - i))
							gapAtEnd = symbolGap(range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + i + skipFrequency));
						else
							gapAtEnd = 0;

						var relAtStart = (float)(0.5 * gapAtStart / diffLength); // 0.5 because symbolGap is the full gap between two lines, thus between the symbol center and the beginning of the line it is only 1/2
						var relAtEnd = (float)(0.5 * gapAtEnd / diffLength); // 0.5 because symbolGap is the full gap between two lines, thus between the symbol center and the beginning of the line it is only 1/2

						if ((relAtStart + relAtEnd) < 1) // a line only appears if sum of the gaps  is smaller than 1
						{
							startx = circularLinePoints[i].X + relAtStart * xdiff;
							starty = circularLinePoints[i].Y + relAtStart * ydiff;
							stopx = circularLinePoints[i + 1].X - relAtEnd * xdiff;
							stopy = circularLinePoints[i + 1].Y - relAtEnd * ydiff;

							gp.AddLine(startx, starty, stopx, stopy);
							gp.StartFigure();
						}
					} // end for
					g.DrawPath(linePen, gp);
					gp.Reset();
				}
				else // skipFrequency is > 1
				{
					for (int i = 0; i < lastIdx; i += skipFrequency)
					{
						int subPointLengthM1 = Math.Min(skipFrequency, circularLinePoints.Length - 1 - i);
						double gapAtStart = symbolGap(range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + i));
						double gapAtEnd;
						if (connectCircular && skipFrequency >= (range.Length - i))
							gapAtEnd = symbolGap(range.OriginalFirstPoint);
						else if (skipFrequency <= (range.Length - 1 - i))
							gapAtEnd = symbolGap(range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + i + skipFrequency));
						else
							gapAtEnd = 0;

						if (subPointLengthM1 >= 1)
						{
							var polyline = circularLinePoints.ShortenPartialPolylineByDistanceFromStartAndEnd(i, i + subPointLengthM1, gapAtStart / 2, gapAtEnd / 2);

							if (null != polyline)
								g.DrawLines(linePen, polyline);
						}
					} // end for
				}
			}
			else // no line symbol gap required, so we can use DrawLines to draw the lines
			{
				if (circularLinePoints.Length > 1) // we don't want to have a drawing exception if number of points is only one
				{
					g.DrawLines(linePen, circularLinePoints);
				}
			}
		}
Beispiel #27
0
		private void PaintOneRange(
			Graphics g,
			IPlotArea layer,
			PointF[] plotPositions,
			IPlotRange range,
			IScatterSymbol scatterSymbol,
			ref CachedPathData cachedPathData,
			ref CachedBrushData cachedBrushData)
		{
			var ptArray = plotPositions;

			float xpos = 0, ypos = 0;
			float xdiff, ydiff;

			int originalIndex;

			// save the graphics stat since we have to translate the origin
			System.Drawing.Drawing2D.GraphicsState gs = g.Save();

			if (null == _cachedSymbolSizeForIndexFunction && null == _cachedColorForIndexFunction) // using a constant symbol size
			{
				// calculate the path only once
				CalculatePaths(scatterSymbol, _symbolSize, ref cachedPathData);
				CalculateBrushes(scatterSymbol, _color, cachedPathData, ref cachedBrushData);



				for (int plotPointIndex = range.LowerBound; plotPointIndex < range.UpperBound; plotPointIndex += _skipFreq)
				{
					xdiff = ptArray[plotPointIndex].X - xpos;
					ydiff = ptArray[plotPointIndex].Y - ypos;
					xpos = ptArray[plotPointIndex].X;
					ypos = ptArray[plotPointIndex].Y;
					g.TranslateTransform(xdiff, ydiff);

					if (null != cachedPathData.InsetPath)
						g.FillPath(cachedBrushData.InsetBrush, cachedPathData.InsetPath);

					if (null != cachedPathData.FillPath)
						g.FillPath(cachedBrushData.FillBrush, cachedPathData.FillPath);

					if (null != cachedPathData.FramePath)
						g.FillPath(cachedBrushData.FrameBrush, cachedPathData.FramePath);
				} // end for


			}
			else // using a variable symbol size or variable symbol color
			{
				CalculatePaths(scatterSymbol, _symbolSize, ref cachedPathData);
				CalculateBrushes(scatterSymbol, _color, cachedPathData, ref cachedBrushData);

				for (int plotPointIndex = range.LowerBound; plotPointIndex < range.UpperBound; plotPointIndex += _skipFreq)
				{
					originalIndex = range.GetOriginalRowIndexFromPlotPointIndex(plotPointIndex);

					if (null == _cachedColorForIndexFunction)
					{
						double customSymbolSize = _cachedSymbolSizeForIndexFunction(originalIndex);
						CalculatePaths(scatterSymbol, customSymbolSize, ref cachedPathData);
					}
					else
					{
						double customSymbolSize = null == _cachedSymbolSizeForIndexFunction ? _symbolSize : _cachedSymbolSizeForIndexFunction(originalIndex);
						var customSymbolColor = _cachedColorForIndexFunction(originalIndex);
						CalculatePaths(scatterSymbol, customSymbolSize, ref cachedPathData);
						CalculateBrushes(scatterSymbol, NamedColor.FromArgb(customSymbolColor.A, customSymbolColor.R, customSymbolColor.G, customSymbolColor.B), cachedPathData, ref cachedBrushData);
					}

					xdiff = ptArray[plotPointIndex].X - xpos;
					ydiff = ptArray[plotPointIndex].Y - ypos;
					xpos = ptArray[plotPointIndex].X;
					ypos = ptArray[plotPointIndex].Y;
					g.TranslateTransform(xdiff, ydiff);

					if (null != cachedPathData.InsetPath)
						g.FillPath(cachedBrushData.InsetBrush, cachedPathData.InsetPath);

					if (null != cachedPathData.FillPath)
						g.FillPath(cachedBrushData.FillBrush, cachedPathData.FillPath);

					if (null != cachedPathData.FramePath)
						g.FillPath(cachedBrushData.FrameBrush, cachedPathData.FramePath);

				}
			}

			g.Restore(gs); // Restore the graphics state
		}
Beispiel #28
0
		/// <summary>
		/// Template to get a fill path.
		/// </summary>
		/// <param name="gp">Graphics path to fill with data.</param>
		/// <param name="pdata">The plot data. Don't use the Range property of the pdata, since it is overriden by the next argument.</param>
		/// <param name="range">The plot range to use.</param>
		/// <param name="layer">Graphics layer.</param>
		/// <param name="fillDirection">Designates a bound to fill to.</param>
		/// <param name="linePoints">The points that mark the line.</param>
		/// <param name="connectCircular">If true, a circular connection is drawn.</param>
		/// <param name="allLinePointsShiftedAlready">The plot positions, already shifted when a logical shift needed to be applied. Don't use the Range property of the pdata, since it is overriden by the next argument.</param>
		/// <param name="logicalShiftX">The logical shift in x-direction.</param>
		/// <param name="logicalShiftY">The logical shift in x-direction.</param>
		public virtual void FillOneRange(
		GraphicsPath gp,
			Processed2DPlotData pdata,
			IPlotRange range,
			IPlotArea layer,
			CSPlaneID fillDirection,
			PointF[] linePoints,
			bool connectCircular,
			PointF[] allLinePointsShiftedAlready,
			double logicalShiftX,
			double logicalShiftY
		)
		{
			if (connectCircular)
			{
				gp.AddLines(linePoints);
				gp.CloseFigure();
			}
			else
			{
				Logical3D r0 = layer.GetLogical3D(pdata, range.OriginalFirstPoint);
				r0.RX += logicalShiftX;
				r0.RY += logicalShiftY;

				layer.CoordinateSystem.GetIsolineFromPlaneToPoint(gp, fillDirection, r0);
				gp.AddLines(linePoints);

				Logical3D r1 = layer.GetLogical3D(pdata, range.OriginalLastPoint);
				r1.RX += logicalShiftX;
				r1.RY += logicalShiftY;

				layer.CoordinateSystem.GetIsolineFromPointToPlane(gp, r1, fillDirection);
				layer.CoordinateSystem.GetIsolineOnPlane(gp, fillDirection, r1, r0);
				gp.CloseFigure();
			}
		}
Beispiel #29
0
		private void PaintOneRange(Graphics g, IPlotArea layer, IPlotRange range, Processed2DPlotData pdata)
		{
			// adjust the skip frequency if it was not set appropriate
			if (_skipFrequency <= 0)
				_skipFrequency = 1;

			var dropTargets = new List<CSPlaneID>(_dropTargets.Select(id => layer.UpdateCSPlaneID(id)));
			if (_additionalDropTargetIsEnabled)
			{
				CSPlaneID userPlane;
				if (_additionalDropTargetUsePhysicalBaseValue)
				{
					userPlane = new CSPlaneID(_additionalDropTargetPerpendicularAxis, layer.Scales[_additionalDropTargetPerpendicularAxis].PhysicalVariantToNormal(_additionalDropTargetBaseValue));
				}
				else
				{
					userPlane = new CSPlaneID(_additionalDropTargetPerpendicularAxis, _additionalDropTargetBaseValue);
				}
				dropTargets.Add(userPlane);
			}

			// paint the scatter style

			PointD3D pos = PointD3D.Empty;
			var gpath = new GraphicsPath();

			if (null == _cachedSymbolSizeForIndexFunction && null == _cachedColorForIndexFunction) // using a constant symbol size and constant color
			{
				// update pen widths
				var pen = _pen.Clone();
				double w1 = _lineWidth1Offset + _lineWidth1Factor * _cachedSymbolSize;
				pen.Width = w1;

				var gapStart = 0.5 * (_gapAtStartOffset + _gapAtStartFactor * _cachedSymbolSize);
				var gapEnd = 0.5 * (_gapAtEndOffset + _gapAtEndFactor * _cachedSymbolSize);


				int lower = range.LowerBound;
				int upper = range.UpperBound;

				for (int j = lower; j < upper; j += _skipFrequency)
				{
					var originalRowIndex = range.GetOriginalRowIndexFromPlotPointIndex(j);

					Logical3D r3d = layer.GetLogical3D(pdata, originalRowIndex);
					r3d.RX += _cachedLogicalShiftX;
					r3d.RY += _cachedLogicalShiftY;

					foreach (CSPlaneID id in dropTargets)
					{
						gpath.Reset();
						layer.CoordinateSystem.GetIsolineFromPointToPlane(gpath, r3d, id);
						PointF[] shortenedPathPoints = null;
						if (gapStart != 0 || gapEnd != 0)
						{
							gpath.Flatten();
							var pathPoints = gpath.PathPoints;
							shortenedPathPoints = GdiExtensionMethods.ShortenedBy(pathPoints, RADouble.NewAbs(gapStart), RADouble.NewAbs(gapEnd));
							if (null != shortenedPathPoints)
								g.DrawLines(pen, shortenedPathPoints);
						}
						else
						{
							g.DrawPath(pen, gpath);
						}
					}
				}

			}
			else // using a variable symbol size or variable symbol color
			{

				int lower = range.LowerBound;
				int upper = range.UpperBound;
				for (int j = lower; j < upper; j += _skipFrequency)
				{
					var originalRowIndex = range.GetOriginalRowIndexFromPlotPointIndex(j);
					var pen = _pen.Clone();
					if (null == _cachedColorForIndexFunction)
					{
						_cachedSymbolSize = _cachedSymbolSizeForIndexFunction(originalRowIndex);
						double w1 = _lineWidth1Offset + _lineWidth1Factor * _cachedSymbolSize;
						pen.Width = w1;
					}
					else
					{
						_cachedSymbolSize = null == _cachedSymbolSizeForIndexFunction ? _cachedSymbolSize : _cachedSymbolSizeForIndexFunction(originalRowIndex);
						double w1 = _lineWidth1Offset + _lineWidth1Factor * _cachedSymbolSize;

						var customSymbolColor = _cachedColorForIndexFunction(originalRowIndex);
						pen.Width = w1;
						pen.Color = NamedColor.FromArgb(customSymbolColor.A, customSymbolColor.R, customSymbolColor.G, customSymbolColor.B);
					}

					var gapStart = 0.5 * (_gapAtStartOffset + _gapAtStartFactor * _cachedSymbolSize);
					var gapEnd = 0.5 * (_gapAtEndOffset + _gapAtEndFactor * _cachedSymbolSize);

					Logical3D r3d = layer.GetLogical3D(pdata, originalRowIndex);
					r3d.RX += _cachedLogicalShiftX;
					r3d.RY += _cachedLogicalShiftY;

					foreach (CSPlaneID id in _dropTargets)
					{
						gpath.Reset();
						layer.CoordinateSystem.GetIsolineFromPointToPlane(gpath, r3d, id);
						PointF[] shortenedPathPoints = null;
						if (gapStart != 0 || gapEnd != 0)
						{
							gpath.Flatten();
							var pathPoints = gpath.PathPoints;
							shortenedPathPoints = GdiExtensionMethods.ShortenedBy(pathPoints, RADouble.NewAbs(gapStart), RADouble.NewAbs(gapEnd));
							if (null != shortenedPathPoints)
								g.DrawLines(pen, shortenedPathPoints);
						}
						else
						{
							g.DrawPath(pen, gpath);
						}
					}
				}

			}
		}
Beispiel #30
0
		/// <inheritdoc/>
		public override void FillOneRange(
		GraphicsPath gp,
			Processed2DPlotData pdata,
			IPlotRange range,
			IPlotArea layer,
			CSPlaneID fillDirection,
			bool ignoreMissingDataPoints,
			bool connectCircular,
			PointF[] allLinePoints,
			double logicalShiftX,
			double logicalShiftY
		)
		{
			if (range.Length < 2)
				return;

			int lastIdx;
			int numberOfPointsPerOriginalPoint;
			PointF[] linepts = GetStepPolylinePoints(allLinePoints, range, layer, connectCircular, out numberOfPointsPerOriginalPoint, out lastIdx);
			FillOneRange(gp, pdata, range, layer, fillDirection, linepts, connectCircular, allLinePoints, logicalShiftX, logicalShiftY);
		}
Beispiel #31
0
		/// <summary>
		/// Template to get a fill path.
		/// </summary>
		/// <param name="gp">Graphics path to fill with data.</param>
		/// <param name="pdata">The plot data. Don't use the Range property of the pdata, since it is overriden by the next argument.</param>
		/// <param name="range">The plot range to use.</param>
		/// <param name="layer">Graphics layer.</param>
		/// <param name="fillDirection">Designates a bound to fill to.</param>
		/// <param name="linePoints">The points that mark the line.</param>
		/// <param name="connectCircular">If true, a circular connection is drawn.</param>
		private void FillOneRange_PreprocessedPoints(
		GraphicsPath gp,
			Processed2DPlotData pdata,
			IPlotRange range,
			IPlotArea layer,
			CSPlaneID fillDirection,
			PointF[] linePoints,
			bool connectCircular,
			double logicalShiftX,
			double logicalShiftY
		)
		{
			if (connectCircular)
			{
				gp.AddBeziers(linePoints);
				gp.CloseFigure();

			}
			else
			{
				Logical3D r0 = layer.GetLogical3D(pdata, range.OriginalFirstPoint);
				r0.RX += logicalShiftX;
				r0.RY += logicalShiftY;
				layer.CoordinateSystem.GetIsolineFromPlaneToPoint(gp, fillDirection, r0);
				gp.AddBeziers(linePoints);

				Logical3D r1 = layer.GetLogical3D(pdata, range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + linePoints.Length - 1));
				r1.RX += logicalShiftX;
				r1.RY += logicalShiftY;

				layer.CoordinateSystem.GetIsolineFromPointToPlane(gp, r1, fillDirection);
				layer.CoordinateSystem.GetIsolineOnPlane(gp, fillDirection, r1, r0);

				gp.CloseFigure();
			}

		}
		protected void PaintOneRange(Graphics g, IPlotArea layer, IPlotRange range, Processed2DPlotData pdata)
		{
			const double logicalClampMinimum = -10;
			const double logicalClampMaximum = 11;

			// Plot error bars for the dependent variable (y)
			var columnX = ColumnX;
			var columnY = ColumnY;

			if (columnX == null || columnY == null)
				return; // nothing to do if both error columns are null

			if (!typeof(double).IsAssignableFrom(columnX.ItemType) || !typeof(double).IsAssignableFrom(columnY.ItemType))
				return; // TODO make this an runtime paint error to be reported

			var strokePen = _strokePen.Clone();

			using (GraphicsPath isoLine = new GraphicsPath())
			{

				int lower = range.LowerBound;
				int upper = range.UpperBound;


				for (int j = lower; j < upper; j += _skipFrequency)
				{
					int originalRowIndex = range.GetOriginalRowIndexFromPlotPointIndex(j);
					double symbolSize = null == _cachedSymbolSizeForIndexFunction ? _symbolSize : _cachedSymbolSizeForIndexFunction(originalRowIndex);

					strokePen.Width = (_lineWidth1Offset + _lineWidth1Factor * symbolSize);

					if (null != _cachedColorForIndexFunction)
						strokePen.Color = GdiColorHelper.ToNamedColor(_cachedColorForIndexFunction(originalRowIndex), "VariableColor");

					if (!(strokePen.EndCap is LineCaps.FlatCap))
						strokePen.EndCap = strokePen.EndCap.WithMinimumAbsoluteAndRelativeSize(symbolSize * _endCapSizeFactor + _endCapSizeOffset, 1 + 1E-6);

					// Calculate target
					AltaxoVariant targetX, targetY;
					switch (_meaningOfValues)
					{
						case ValueInterpretation.AbsoluteDifference:
							{
								targetX = pdata.GetXPhysical(originalRowIndex) + columnX[originalRowIndex];
								targetY = pdata.GetYPhysical(originalRowIndex) + columnY[originalRowIndex];
							}
							break;

						case ValueInterpretation.AbsoluteValue:
							{
								targetX = columnX[originalRowIndex];
								targetY = columnY[originalRowIndex];
							}
							break;

						default:
							throw new NotImplementedException(nameof(_meaningOfValues));
					}

					var logicalTarget = layer.GetLogical3D(targetX, targetY);
					var logicalOrigin = layer.GetLogical3D(pdata, originalRowIndex);

					if (!_independentOnShiftingGroupStyles && (0 != _cachedLogicalShiftX || 0 != _cachedLogicalShiftY))
					{
						logicalOrigin.RX += _cachedLogicalShiftX;
						logicalOrigin.RY += _cachedLogicalShiftY;
						logicalTarget.RX += _cachedLogicalShiftX;
						logicalTarget.RY += _cachedLogicalShiftY;
					}

					if (!Calc.RMath.IsInIntervalCC(logicalOrigin.RX, logicalClampMinimum, logicalClampMaximum))
						continue;
					if (!Calc.RMath.IsInIntervalCC(logicalOrigin.RY, logicalClampMinimum, logicalClampMaximum))
						continue;
					if (!Calc.RMath.IsInIntervalCC(logicalOrigin.RZ, logicalClampMinimum, logicalClampMaximum))
						continue;

					if (!Calc.RMath.IsInIntervalCC(logicalTarget.RX, logicalClampMinimum, logicalClampMaximum))
						continue;
					if (!Calc.RMath.IsInIntervalCC(logicalTarget.RY, logicalClampMinimum, logicalClampMaximum))
						continue;
					if (!Calc.RMath.IsInIntervalCC(logicalTarget.RZ, logicalClampMinimum, logicalClampMaximum))
						continue;

					isoLine.Reset();

					layer.CoordinateSystem.GetIsoline(isoLine, logicalOrigin, logicalTarget);
					if (null == isoLine)
						continue;

					PointF[] isoLinePathPoints = null;

					if (_useManualVectorLength)
					{
						isoLine.Flatten();
						isoLinePathPoints = isoLine.PathPoints;

						double length = _vectorLengthOffset + _vectorLengthFactor * symbolSize;
						double isoLineLength = isoLinePathPoints.TotalLineLength();
						isoLinePathPoints = isoLinePathPoints.ShortenedBy(RADouble.NewAbs(0), RADouble.NewAbs(isoLineLength - length));
						if (null == isoLine)
							continue;
					}

					if (_useSymbolGap)
					{
						if (null == isoLinePathPoints)
						{
							isoLine.Flatten();
							isoLinePathPoints = isoLine.PathPoints;
						}

						double gap = _symbolGapOffset + _symbolGapFactor * symbolSize;
						if (gap != 0)
						{
							isoLinePathPoints = isoLinePathPoints.ShortenedBy(RADouble.NewAbs(gap / 2), RADouble.NewAbs(0));
							if (null == isoLine)
								continue;
						}
					}

					if (null != isoLinePathPoints)
						g.DrawLines(_strokePen, isoLinePathPoints);
					else
						g.DrawPath(strokePen, isoLine);
				}

			}
		}
Beispiel #33
0
		/// <summary>
		/// Template to make a line draw.
		/// </summary>
		/// <param name="g">Graphics context.</param>
		/// <param name="allLinePoints">The plot data. Don't use the Range property of the pdata, since it is overriden by the next argument.</param>
		/// <param name="range">The plot range to use.</param>
		/// <param name="layer">Graphics layer.</param>
		/// <param name="linePen">The pen to draw the line.</param>
		/// <param name="symbolGap">The size of the symbol gap. Argument is the original index of the data. The return value is the absolute symbol gap at this index.
		/// This function is null if no symbol gap is required.</param>
		/// <param name="skipFrequency">Skip frequency. Normally 1, thus all gaps are taken into account. If 2, only every 2nd gap is taken into account, and so on.</param>
		/// <param name="connectCircular">If true, there is a line connecting the start and the end of the range.</param>
		/// <param name="linePlotStyle">The line plot style.</param>
		public override void PaintOneRange(
			Graphics g,
			PointF[] allLinePoints,
			IPlotRange range,
			IPlotArea layer,
			PenX linePen,
			Func<int, double> symbolGap,
			int skipFrequency,
			bool connectCircular,
			LinePlotStyle linePlotStyle)
		{
			// Bezier is only supported with point numbers n=4+3*k
			// so trim the range appropriately
			if (range.Length < 4)
				return; // then too less points are in this range

			if (connectCircular)
			{
				var circularLinePointsLengthM1 = 2 + TrimToValidBezierLength(range.Length);
				var circularLinePoints = new PointF[circularLinePointsLengthM1 + 1];
				Array.Copy(allLinePoints, range.LowerBound, circularLinePoints, 0, range.Length); // Extract
				circularLinePoints[circularLinePointsLengthM1] = circularLinePoints[0];

				// amend missing control points
				if (circularLinePointsLengthM1 - range.Length >= 1)
					circularLinePoints[circularLinePointsLengthM1 - 1] = GdiExtensionMethods.Interpolate(circularLinePoints[circularLinePointsLengthM1 - 3], circularLinePoints[circularLinePointsLengthM1], 0.5); // Last Control point should be halfway between
				if (circularLinePointsLengthM1 - range.Length >= 2)
					circularLinePoints[circularLinePointsLengthM1 - 2] = GdiExtensionMethods.Interpolate(circularLinePoints[circularLinePointsLengthM1 - 3], circularLinePoints[circularLinePointsLengthM1], 0.5); // Middle Control point should be halfway between previous fixed point and last(=first) fixed point

				if (null != symbolGap) // circular with symbol gap
				{
					var realSkipFrequency = skipFrequency % 3 == 0 ? skipFrequency : skipFrequency * 3; // least common multiple of skipFrequency and 3
					for (int i = 0; i < range.Length; i += realSkipFrequency)
					{
						var skipLinePointsLength = Math.Min(realSkipFrequency + 1, TrimToValidBezierLength(circularLinePoints.Length - i));
						if (skipLinePointsLength >= 4)
						{
							var skipLinePoints = new PointF[skipLinePointsLength];
							Array.Copy(circularLinePoints, i, skipLinePoints, 0, skipLinePointsLength); // Extract

							var gapAtStart = symbolGap(range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + i));
							double gapAtEnd;
							if (connectCircular && realSkipFrequency >= (range.Length - 1 - i))
								gapAtEnd = symbolGap(range.OriginalFirstPoint);
							else if (realSkipFrequency <= (range.Length - 1 - i))
								gapAtEnd = symbolGap(range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + i + realSkipFrequency));
							else
								gapAtEnd = 0;

							if (gapAtStart != 0 || gapAtEnd != 0)
							{
								skipLinePoints = GdiExtensionMethods.ShortenBezierCurve(skipLinePoints, gapAtStart / 2, gapAtEnd / 2);
							}

							if (null != skipLinePoints)
							{
								g.DrawBeziers(linePen, skipLinePoints);
							}
						}
					}
				}
				else // circular without symbol gap
				{
					g.DrawBeziers(linePen, circularLinePoints);
				}
			}
			else // not circular
			{
				if (null != symbolGap) // not circular with symbol gap
				{
					var realSkipFrequency = skipFrequency % 3 == 0 ? skipFrequency : skipFrequency * 3; // least common multiple of skipFrequency and 3
					for (int i = 0; i < range.Length; i += realSkipFrequency)
					{
						var skipLinePointsLength = Math.Min(realSkipFrequency + 1, TrimToValidBezierLength(range.Length - i));
						if (skipLinePointsLength >= 4)
						{
							var skipLinePoints = new PointF[skipLinePointsLength];
							Array.Copy(allLinePoints, range.LowerBound + i, skipLinePoints, 0, skipLinePointsLength); // Extract

							var gapAtStart = symbolGap(range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + i));
							var gapAtEnd = symbolGap(range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + i + skipLinePointsLength - 1));

							if (gapAtStart != 0 || gapAtEnd != 0)
							{
								skipLinePoints = GdiExtensionMethods.ShortenBezierCurve(skipLinePoints, gapAtStart / 2, gapAtEnd / 2);
							}

							if (null != skipLinePoints)
							{
								g.DrawBeziers(linePen, skipLinePoints);
							}
						}
					}
				}
				else // not circular without symbol gap
				{
					var trimmedLength = TrimToValidBezierLength(range.Length);
					var subLinePoints = new PointF[trimmedLength];
					Array.Copy(allLinePoints, range.LowerBound, subLinePoints, 0, trimmedLength); // Extract
					g.DrawBeziers(linePen, subLinePoints);
				}
			}
		}
Beispiel #34
0
		/// <inheritdoc/>
		public override void FillOneRange(
		GraphicsPath gp,
			Processed2DPlotData pdata,
			IPlotRange range,
			IPlotArea layer,
			CSPlaneID fillDirection,
			bool ignoreMissingDataPoints,
			bool connectCircular,
			PointF[] allLinePoints,
			double logicalShiftX,
			double logicalShiftY
		)
		{
			PointF[] circularLinePoints = new PointF[range.Length + (connectCircular ? 1 : 0)];
			Array.Copy(allLinePoints, range.LowerBound, circularLinePoints, 0, range.Length); // Extract
			if (connectCircular)
				circularLinePoints[circularLinePoints.Length - 1] = circularLinePoints[0];

			if (connectCircular)
			{
				gp.AddLines(circularLinePoints);
				gp.CloseFigure();
			}
			else // not circular
			{
				Logical3D r0 = layer.GetLogical3D(pdata, range.OriginalFirstPoint);
				r0.RX += logicalShiftX;
				r0.RY += logicalShiftY;

				layer.CoordinateSystem.GetIsolineFromPlaneToPoint(gp, fillDirection, r0);
				gp.AddLines(circularLinePoints);
				Logical3D r1 = layer.GetLogical3D(pdata, connectCircular ? range.OriginalFirstPoint : range.OriginalLastPoint);
				r1.RX += logicalShiftX;
				r1.RY += logicalShiftY;

				layer.CoordinateSystem.GetIsolineFromPointToPlane(gp, r1, fillDirection);
				layer.CoordinateSystem.GetIsolineOnPlane(gp, fillDirection, r1, r0);
				gp.CloseFigure();
			}
		}
Beispiel #35
0
        /// <summary>
        /// Template to make a line draw.
        /// </summary>
        /// <param name="g">Graphics context.</param>
        /// <param name="allLinePoints">The plot data. Don't use the Range property of the pdata, since it is overriden by the next argument.</param>
        /// <param name="range">The plot range to use.</param>
        /// <param name="layer">Graphics layer.</param>
        /// <param name="linePen">The pen to draw the line.</param>
        /// <param name="symbolGap">The size of the symbol gap. Argument is the original index of the data. The return value is the absolute symbol gap at this index.
        /// This function is null if no symbol gap is required.</param>
        /// <param name="skipFrequency">Skip frequency. Normally 1, thus all gaps are taken into account. If 2, only every 2nd gap is taken into account, and so on.</param>
        /// <param name="connectCircular">If true, there is a line connecting the start and the end of the range.</param>
        /// <param name="linePlotStyle">The line plot style.</param>
        public override void PaintOneRange(
            Graphics g,
            PointF[] allLinePoints,
            IPlotRange range,
            IPlotArea layer,
            PenX linePen,
            Func <int, double> symbolGap,
            int skipFrequency,
            bool connectCircular,
            LinePlotStyle linePlotStyle)
        {
            // Bezier is only supported with point numbers n=4+3*k
            // so trim the range appropriately
            if (range.Length < 4)
            {
                return; // then too less points are in this range
            }
            PointF[] circularLinePoints;

            if (connectCircular)
            {
                var circularLinePointsLengthM1 = 2 + TrimToValidBezierLength(range.Length);
                circularLinePoints = new PointF[circularLinePointsLengthM1 + 1];
                Array.Copy(allLinePoints, range.LowerBound, circularLinePoints, 0, range.Length); // Extract
                circularLinePoints[circularLinePointsLengthM1] = circularLinePoints[0];

                // amend missing control points
                if (circularLinePointsLengthM1 - range.Length >= 1)
                {
                    circularLinePoints[circularLinePointsLengthM1 - 1] = GdiExtensionMethods.Interpolate(circularLinePoints[circularLinePointsLengthM1 - 3], circularLinePoints[circularLinePointsLengthM1], 0.5); // Last Control point should be halfway between
                }
                if (circularLinePointsLengthM1 - range.Length >= 2)
                {
                    circularLinePoints[circularLinePointsLengthM1 - 2] = GdiExtensionMethods.Interpolate(circularLinePoints[circularLinePointsLengthM1 - 3], circularLinePoints[circularLinePointsLengthM1], 0.5); // Middle Control point should be halfway between previous fixed point and last(=first) fixed point
                }
                range = range.WithUpperBoundExtendedBy(circularLinePointsLengthM1 - range.Length);
            }
            else // not circular
            {
                var trimmedLength = TrimToValidBezierLength(range.Length);
                if (range.Length != trimmedLength)
                {
                    range = range.WithUpperBoundShortenedBy(range.Length - trimmedLength);
                }

                if (range.LowerBound == 0 && trimmedLength == allLinePoints.Length)
                {
                    circularLinePoints = allLinePoints;
                }
                else
                {
                    circularLinePoints = new PointF[trimmedLength];
                    Array.Copy(allLinePoints, range.LowerBound, circularLinePoints, 0, trimmedLength); // Extract
                }
            }

            if (null != symbolGap)                                                                  // circular with symbol gap
            {
                var realSkipFrequency = skipFrequency % 3 == 0 ? skipFrequency : skipFrequency * 3; // least common multiple of skipFrequency and 3
                var skipLinePoints    = new PointF[0];
                foreach (var segmentRange in GetSegmentRanges(range, symbolGap, realSkipFrequency, connectCircular))
                {
                    if (segmentRange.IsFullRangeClosedCurve) // test if this is a closed polygon without any gaps -> draw a closed polygon and return
                    {
                        // use the whole circular arry to draw a closed polygon without any gaps
                        g.DrawBeziers(linePen, circularLinePoints);
                    }
                    else
                    {
                        var skipLinePointsLength = 1 + segmentRange.Length;
                        if (skipLinePoints.Length != skipLinePointsLength)
                        {
                            skipLinePoints = new PointF[skipLinePointsLength];
                        }
                        Array.Copy(circularLinePoints, segmentRange.IndexAtSubRangeStart, skipLinePoints, 0, skipLinePointsLength);

                        PointF[] shortenedLinePoints;
                        if (segmentRange.GapAtSubRangeStart != 0 || segmentRange.GapAtSubRangeEnd != 0)
                        {
                            shortenedLinePoints = GdiExtensionMethods.ShortenBezierCurve(skipLinePoints, segmentRange.GapAtSubRangeStart / 2, segmentRange.GapAtSubRangeEnd / 2);
                        }
                        else
                        {
                            shortenedLinePoints = skipLinePoints;
                        }

                        if (null != shortenedLinePoints)
                        {
                            g.DrawBeziers(linePen, shortenedLinePoints);
                        }
                    }
                }
            }
            else // no symbol gap
            {
                g.DrawBeziers(linePen, circularLinePoints);
            }
        }
Beispiel #36
0
        /// <summary>
        /// Template to make a line draw.
        /// </summary>
        /// <param name="g">Graphics context.</param>
        /// <param name="allLinePoints">The plot data. Don't use the Range property of the pdata, since it is overriden by the next argument.</param>
        /// <param name="range">The plot range to use.</param>
        /// <param name="layer">Graphics layer.</param>
        /// <param name="linePen">The pen to draw the line.</param>
        /// <param name="symbolGap">The size of the symbol gap. Argument is the original index of the data. The return value is the absolute symbol gap at this index.
        /// This function is null if no symbol gap is required.</param>
        /// <param name="skipFrequency">Skip frequency. Normally 1, thus all gaps are taken into account. If 2, only every 2nd gap is taken into account, and so on.</param>
        /// <param name="connectCircular">If true, there is a line connecting the start and the end of the range.</param>
        /// <param name="linePlotStyle">The line plot style.</param>
        public override void PaintOneRange(
            Graphics g,
            PointF[] allLinePoints,
            IPlotRange range,
            IPlotArea layer,
            PenX linePen,
            Func <int, double> symbolGap,
            int skipFrequency,
            bool connectCircular,
            LinePlotStyle linePlotStyle)
        {
            PointF[] subLinePoints;
            if (range.LowerBound == 0 && range.UpperBound == allLinePoints.Length)
            {
                // under optimal conditions we can use allLinePoints directly
                subLinePoints = allLinePoints;
            }
            else
            {
                // otherwise, make a new array
                subLinePoints = new PointF[range.Length];
                Array.Copy(allLinePoints, range.LowerBound, subLinePoints, 0, range.Length); // Extract
            }

            int lastIdx   = range.Length - 1;
            var layerSize = layer.Size;

            if (connectCircular)
            {
                if (null != symbolGap)
                {
                    // convert points to bezier segments
                    var bezierSegments    = GdiExtensionMethods.ClosedCardinalSplineToBezierSegments(subLinePoints, subLinePoints.Length);
                    var subBezierSegments = new PointF[0];
                    foreach (var segmentRange in GetSegmentRanges(range, symbolGap, skipFrequency, connectCircular))
                    {
                        if (segmentRange.IsFullRangeClosedCurve) // test if this is a closed polygon without any gaps -> draw a closed polygon and return
                        {
                            // use the whole circular arry to draw a closed polygon without any gaps
                            g.DrawClosedCurve(linePen, subLinePoints);
                        }
                        else
                        {
                            var subBezierLength = 3 * segmentRange.Length + 1;
                            if (subBezierSegments.Length != subBezierLength)
                            {
                                subBezierSegments = new PointF[subBezierLength];
                            }

                            Array.Copy(bezierSegments, segmentRange.IndexAtSubRangeStart * 3, subBezierSegments, 0, subBezierLength);
                            var shortenedBezierSegments = GdiExtensionMethods.ShortenBezierCurve(subBezierSegments, segmentRange.GapAtSubRangeStart / 2, segmentRange.GapAtSubRangeEnd / 2);

                            if (null != shortenedBezierSegments)
                            {
                                g.DrawBeziers(linePen, shortenedBezierSegments);
                            }
                        }
                    }
                }
                else
                {
                    g.DrawClosedCurve(linePen, subLinePoints);
                }
            }
            else // not circular
            {
                if (symbolGap != null)
                {
                    // convert points to bezier segments
                    var bezierSegments    = GdiExtensionMethods.OpenCardinalSplineToBezierSegments(subLinePoints, subLinePoints.Length);
                    var subBezierSegments = new PointF[0];

                    foreach (var segmentRange in GetSegmentRanges(range, symbolGap, skipFrequency, connectCircular))
                    {
                        if (segmentRange.IsFullRangeClosedCurve) // test if this is a closed polygon without any gaps -> draw a closed polygon and return
                        {
                            // use the whole circular arry to draw a closed polygon without any gaps
                            g.DrawCurve(linePen, subLinePoints);
                        }
                        else
                        {
                            var subBezierLength = 3 * segmentRange.Length + 1;
                            if (subBezierSegments.Length != subBezierLength)
                            {
                                subBezierSegments = new PointF[subBezierLength];
                            }

                            Array.Copy(bezierSegments, segmentRange.IndexAtSubRangeStart * 3, subBezierSegments, 0, subBezierLength);
                            var shortenedBezierSegments = GdiExtensionMethods.ShortenBezierCurve(subBezierSegments, segmentRange.GapAtSubRangeStart / 2, segmentRange.GapAtSubRangeEnd / 2);

                            if (null != shortenedBezierSegments)
                            {
                                g.DrawBeziers(linePen, shortenedBezierSegments);
                            }
                        }
                    }
                }
                else
                {
                    g.DrawCurve(linePen, subLinePoints);
                }
            }
        }
        protected static IEnumerable <SegmentRange> GetSegmentRanges(
            IPlotRange range,
            Func <int, double> symbolGap,
            int skipFrequency, bool connectCircular)
        {
            int lengthNonCircular    = range.Length;
            int lengthCircularMinus1 = range.Length - 1 + (connectCircular ? 1 : 0);
            // SubRange is a number of consecutive line segments, with joints where the symbol gap is zero
            double gapAtSubRangeStart = symbolGap(range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound)); // start index of the subrange
            double gapAtSubRangeEnd   = 0;
            int    indexAtSubRangeEnd;

            for (int indexAtSubRangeStart = 0; indexAtSubRangeStart < lengthCircularMinus1; indexAtSubRangeStart = indexAtSubRangeEnd, gapAtSubRangeStart = gapAtSubRangeEnd)
            {
                // search for the last index of this subrange, i.e. for a point where the gap is non-zero
                for (indexAtSubRangeEnd = indexAtSubRangeStart + 1, gapAtSubRangeEnd = 0; indexAtSubRangeEnd < lengthNonCircular; ++indexAtSubRangeEnd)
                {
                    if (0 == indexAtSubRangeEnd % skipFrequency) // calculate gap only if in skip period
                    {
                        gapAtSubRangeEnd = symbolGap(range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + indexAtSubRangeEnd));
                    }
                    if (0 != gapAtSubRangeEnd)
                    {
                        break;
                    }
                }

                // if this is circular connected and is the last point, then use the gap of the very first point
                if (connectCircular && indexAtSubRangeEnd == lengthNonCircular)
                {
                    gapAtSubRangeEnd = symbolGap(range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound));
                }

                // test if this is a closed polygon without any gaps -> draw a closed polygon and return
                if (connectCircular && 0 == gapAtSubRangeStart && indexAtSubRangeEnd == (lengthCircularMinus1 + 1))
                {
                    // use the whole circular array to draw a closed polygon without any gaps
                    yield return(new SegmentRange()
                    {
                        IsFullRangeClosedCurve = true,
                        IndexAtSubRangeStart = indexAtSubRangeStart,
                        IndexAtSubRangeEnd = indexAtSubRangeStart,
                        GapAtSubRangeStart = 0,
                        GapAtSubRangeEnd = 0
                    });

                    break;
                }

                // adjust the end index to be valid
                if (!(indexAtSubRangeEnd <= lengthCircularMinus1))
                {
                    indexAtSubRangeEnd = lengthCircularMinus1;
                }

                yield return(new SegmentRange()
                {
                    IsFullRangeClosedCurve = false,
                    IndexAtSubRangeStart = indexAtSubRangeStart,
                    IndexAtSubRangeEnd = indexAtSubRangeEnd,
                    GapAtSubRangeStart = gapAtSubRangeStart,
                    GapAtSubRangeEnd = gapAtSubRangeEnd
                });
            }
        }
Beispiel #38
0
        private void PaintOneRange(
            Graphics g,
            IPlotArea layer,
            PointF[] plotPositions,
            IPlotRange range,
            IScatterSymbol scatterSymbol,
            ref CachedPathData cachedPathData,
            ref CachedBrushData cachedBrushData)
        {
            var ptArray = plotPositions;

            float xpos = 0, ypos = 0;
            float xdiff, ydiff;

            int originalIndex;

            // save the graphics stat since we have to translate the origin
            System.Drawing.Drawing2D.GraphicsState gs = g.Save();

            if (null == _cachedSymbolSizeForIndexFunction && null == _cachedColorForIndexFunction) // using a constant symbol size
            {
                // calculate the path only once
                CalculatePaths(scatterSymbol, _symbolSize, ref cachedPathData);
                CalculateBrushes(scatterSymbol, _color, cachedPathData, ref cachedBrushData);

                for (int plotPointIndex = range.LowerBound; plotPointIndex < range.UpperBound; plotPointIndex += _skipFreq)
                {
                    xdiff = ptArray[plotPointIndex].X - xpos;
                    ydiff = ptArray[plotPointIndex].Y - ypos;
                    xpos  = ptArray[plotPointIndex].X;
                    ypos  = ptArray[plotPointIndex].Y;
                    g.TranslateTransform(xdiff, ydiff);

                    if (null != cachedPathData.InsetPath)
                    {
                        g.FillPath(cachedBrushData.InsetBrush, cachedPathData.InsetPath);
                    }

                    if (null != cachedPathData.FillPath)
                    {
                        g.FillPath(cachedBrushData.FillBrush, cachedPathData.FillPath);
                    }

                    if (null != cachedPathData.FramePath)
                    {
                        g.FillPath(cachedBrushData.FrameBrush, cachedPathData.FramePath);
                    }
                } // end for
            }
            else  // using a variable symbol size or variable symbol color
            {
                CalculatePaths(scatterSymbol, _symbolSize, ref cachedPathData);
                CalculateBrushes(scatterSymbol, _color, cachedPathData, ref cachedBrushData);

                for (int plotPointIndex = range.LowerBound; plotPointIndex < range.UpperBound; plotPointIndex += _skipFreq)
                {
                    originalIndex = range.GetOriginalRowIndexFromPlotPointIndex(plotPointIndex);

                    if (null == _cachedColorForIndexFunction)
                    {
                        double customSymbolSize = _cachedSymbolSizeForIndexFunction(originalIndex);
                        CalculatePaths(scatterSymbol, customSymbolSize, ref cachedPathData);
                    }
                    else
                    {
                        double customSymbolSize  = null == _cachedSymbolSizeForIndexFunction ? _symbolSize : _cachedSymbolSizeForIndexFunction(originalIndex);
                        var    customSymbolColor = _cachedColorForIndexFunction(originalIndex);
                        CalculatePaths(scatterSymbol, customSymbolSize, ref cachedPathData);
                        CalculateBrushes(scatterSymbol, NamedColor.FromArgb(customSymbolColor.A, customSymbolColor.R, customSymbolColor.G, customSymbolColor.B), cachedPathData, ref cachedBrushData);
                    }

                    xdiff = ptArray[plotPointIndex].X - xpos;
                    ydiff = ptArray[plotPointIndex].Y - ypos;
                    xpos  = ptArray[plotPointIndex].X;
                    ypos  = ptArray[plotPointIndex].Y;
                    g.TranslateTransform(xdiff, ydiff);

                    if (null != cachedPathData.InsetPath)
                    {
                        g.FillPath(cachedBrushData.InsetBrush, cachedPathData.InsetPath);
                    }

                    if (null != cachedPathData.FillPath)
                    {
                        g.FillPath(cachedBrushData.FillBrush, cachedPathData.FillPath);
                    }

                    if (null != cachedPathData.FramePath)
                    {
                        g.FillPath(cachedBrushData.FrameBrush, cachedPathData.FramePath);
                    }
                }
            }

            g.Restore(gs); // Restore the graphics state
        }
Beispiel #39
0
        /// <summary>
        /// Template to make a line draw.
        /// </summary>
        /// <param name="g">Graphics context.</param>
        /// <param name="allLinePoints">The plot data. Don't use the Range property of the pdata, since it is overriden by the next argument.</param>
        /// <param name="range">The plot range to use.</param>
        /// <param name="layer">Graphics layer.</param>
        /// <param name="linePen">The pen to draw the line.</param>
        /// <param name="symbolGap">The size of the symbol gap. Argument is the original index of the data. The return value is the absolute symbol gap at this index.
        /// This function is null if no symbol gap is required.</param>
        /// <param name="skipFrequency">Skip frequency. Normally 1, thus all gaps are taken into account. If 2, only every 2nd gap is taken into account, and so on.</param>
        /// <param name="connectCircular">If true, there is a line connecting the start and the end of the range.</param>
        /// <param name="linePlotStyle">The line plot style.</param>
        public override void PaintOneRange(
            Graphics g,
            PointF[] allLinePoints,
            IPlotRange range,
            IPlotArea layer,
            PenX linePen,
            Func <int, double> symbolGap,
            int skipFrequency,
            bool connectCircular,
            LinePlotStyle linePlotStyle)
        {
            PointF[] subLinePoints = Segment2Connection_GetSubPoints(allLinePoints, range, layer, connectCircular, out var lastIdx);

            var gp = new GraphicsPath();
            int i;

            // special efforts are necessary to realize a line/symbol gap
            // I decided to use a path for this
            // and hope that not so many segments are added to the path due
            // to the exclusion criteria that a line only appears between two symbols (rel<0.5)
            // if the symbols do not overlap. So for a big array of points it is very likely
            // that the symbols overlap and no line between the symbols needs to be plotted
            if (null != symbolGap)
            {
                float startx, starty, stopx, stopy;
                for (i = 0; i < lastIdx; i += 2)
                {
                    var diff       = GdiExtensionMethods.Subtract(subLinePoints[i + 1], subLinePoints[i]);
                    var diffLength = GdiExtensionMethods.VectorLength(diff);

                    int    originalIndex = range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + i);
                    double gapAtStart    = 0 == i % skipFrequency ? symbolGap(originalIndex) : 0;
                    double gapAtEnd;
                    if ((0 == (i + 1) % skipFrequency) || ((i + 1) == range.Length))
                    {
                        gapAtEnd = ((i + 1) != range.Length) ? symbolGap(originalIndex + 1) : symbolGap(range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound));
                    }
                    else
                    {
                        gapAtEnd = 0;
                    }

                    var relAtStart = (float)(0.5 * gapAtStart / diffLength); // 0.5 because symbolGap is the full gap between two lines, thus between the symbol center and the beginning of the line it is only 1/2
                    var relAtEnd   = (float)(0.5 * gapAtEnd / diffLength);   // 0.5 because symbolGap is the full gap between two lines, thus between the symbol center and the beginning of the line it is only 1/2
                    if ((relAtStart + relAtEnd) < 1)                         // a line only appears if sum of the gaps  is smaller than 1
                    {
                        startx = subLinePoints[i].X + relAtStart * diff.X;
                        starty = subLinePoints[i].Y + relAtStart * diff.Y;
                        stopx  = subLinePoints[i + 1].X - relAtEnd * diff.X;
                        stopy  = subLinePoints[i + 1].Y - relAtEnd * diff.Y;

                        gp.AddLine(startx, starty, stopx, stopy);
                        gp.StartFigure();
                    }
                } // end for
                g.DrawPath(linePen, gp);
                gp.Reset();
            }
            else // no line symbol gap required, so we can use DrawLines to draw the lines
            {
                for (i = 0; i < lastIdx; i += 2)
                {
                    gp.AddLine(subLinePoints[i].X, subLinePoints[i].Y, subLinePoints[i + 1].X, subLinePoints[i + 1].Y);
                    gp.StartFigure();
                } // end for
                g.DrawPath(linePen, gp);
                gp.Reset();
            }
        }
Beispiel #40
0
        /// <summary>
        /// Template to make a line draw.
        /// </summary>
        /// <param name="g">Graphics context.</param>
        /// <param name="allLinePoints">The plot data. Don't use the Range property of the pdata, since it is overriden by the next argument.</param>
        /// <param name="range">The plot range to use.</param>
        /// <param name="layer">Graphics layer.</param>
        /// <param name="linePen">The pen to draw the line.</param>
        /// <param name="symbolGap">The size of the symbol gap. Argument is the original index of the data. The return value is the absolute symbol gap at this index.
        /// This function is null if no symbol gap is required.</param>
        /// <param name="skipFrequency">Skip frequency. Normally 1, thus all gaps are taken into account. If 2, only every 2nd gap is taken into account, and so on.</param>
        /// <param name="connectCircular">If true, there is a line connecting the start and the end of the range.</param>
        /// <param name="linePlotStyle">The line plot style.</param>
        public override void PaintOneRange(
            Graphics g,
            PointF[] allLinePoints,
            IPlotRange range,
            IPlotArea layer,
            PenX linePen,
            Func <int, double> symbolGap,
            int skipFrequency,
            bool connectCircular,
            LinePlotStyle linePlotStyle)
        {
            if (range.Length <= 1)
            {
                return; // seems to be only a single point, thus no connection possible
            }
            PointF[] circularLinePoints;
            int      indexBasePlotPoints; // index of the first plot point of this range in circularLinePoints array

            if (connectCircular)          // we have to copy the array in order to append the first point to the end
            {
                // otherwise, make a new array
                circularLinePoints = new PointF[range.Length + 1];
                Array.Copy(allLinePoints, range.LowerBound, circularLinePoints, 0, range.Length); // Extract
                circularLinePoints[circularLinePoints.Length - 1] = circularLinePoints[0];
                indexBasePlotPoints = 0;
            }
            else // use the array directly without copying
            {
                circularLinePoints  = allLinePoints;
                indexBasePlotPoints = range.LowerBound;
            }

            if (null != symbolGap)
            {
                foreach (var segmentRange in GetSegmentRanges(range, symbolGap, skipFrequency, connectCircular))
                {
                    if (segmentRange.IsFullRangeClosedCurve) // test if this is a closed polygon without any gaps -> draw a closed polygon and return
                    {
                        // use the whole circular arry to draw a closed polygon without any gaps
                        g.DrawPolygon(linePen, circularLinePoints);
                    }
                    else if (segmentRange.Length == 1) // special case only one line segment
                    {
                        int plotIndexAtStart = segmentRange.IndexAtSubRangeStart + indexBasePlotPoints;
                        int plotIndexAtEnd   = segmentRange.IndexAtSubRangeEnd + indexBasePlotPoints;

                        var xdiff      = circularLinePoints[plotIndexAtEnd].X - circularLinePoints[plotIndexAtStart].X;
                        var ydiff      = circularLinePoints[plotIndexAtEnd].Y - circularLinePoints[plotIndexAtStart].Y;
                        var diffLength = System.Math.Sqrt(xdiff * xdiff + ydiff * ydiff);

                        var relAtStart = (float)(0.5 * segmentRange.GapAtSubRangeStart / diffLength); // 0.5 because symbolGap is the full gap between two lines, thus between the symbol center and the beginning of the line it is only 1/2
                        var relAtEnd   = (float)(0.5 * segmentRange.GapAtSubRangeEnd / diffLength);   // 0.5 because symbolGap is the full gap between two lines, thus between the symbol center and the beginning of the line it is only 1/2

                        if ((relAtStart + relAtEnd) < 1)                                              // a line only appears if sum of the gaps  is smaller than 1
                        {
                            var startx = circularLinePoints[plotIndexAtStart].X + relAtStart * xdiff;
                            var starty = circularLinePoints[plotIndexAtStart].Y + relAtStart * ydiff;
                            var stopx  = circularLinePoints[plotIndexAtEnd].X - relAtEnd * xdiff;
                            var stopy  = circularLinePoints[plotIndexAtEnd].Y - relAtEnd * ydiff;

                            g.DrawLine(linePen, startx, starty, stopx, stopy);
                        }
                    }
                    else
                    {
                        int plotIndexAtStart  = segmentRange.IndexAtSubRangeStart + indexBasePlotPoints;
                        int plotIndexAtEnd    = segmentRange.IndexAtSubRangeEnd + indexBasePlotPoints;
                        var shortenedPolyline = circularLinePoints.ShortenPartialPolylineByDistanceFromStartAndEnd(plotIndexAtStart, plotIndexAtEnd, segmentRange.GapAtSubRangeStart / 2, segmentRange.GapAtSubRangeEnd / 2);

                        if (null != shortenedPolyline)
                        {
                            g.DrawLines(linePen, shortenedPolyline);
                        }
                    }
                } // end for
            }
            else  // no line symbol gap required, so we can use DrawLines or DrawPolygon to draw the lines
            {
                if (connectCircular) // array was already copied from original array
                {
                    g.DrawPolygon(linePen, circularLinePoints);
                }
                else if (indexBasePlotPoints == 0 && range.Length == circularLinePoints.Length) // can use original array directly
                {
                    g.DrawLines(linePen, circularLinePoints);
                }
                else
                {
                    circularLinePoints = new PointF[range.Length];
                    Array.Copy(allLinePoints, range.LowerBound, circularLinePoints, 0, range.Length);
                    g.DrawLines(linePen, circularLinePoints);
                }
            }
        }
        protected void PaintOneRange(Graphics g, IPlotArea layer, IPlotRange range, Processed2DPlotData pdata)
        {
            const double logicalClampMinimum = -10;
            const double logicalClampMaximum = 11;

            // Plot error bars for the dependent variable (y)
            var columnX = ColumnX;
            var columnY = ColumnY;

            if (columnX == null || columnY == null)
            {
                return; // nothing to do if both error columns are null
            }
            if (!typeof(double).IsAssignableFrom(columnX.ItemType) || !typeof(double).IsAssignableFrom(columnY.ItemType))
            {
                return; // TODO make this an runtime paint error to be reported
            }
            var strokePen = _strokePen.Clone();

            using (var isoLine = new GraphicsPath())
            {
                int lower = range.LowerBound;
                int upper = range.UpperBound;

                for (int j = lower; j < upper; j += _skipFrequency)
                {
                    int    originalRowIndex = range.GetOriginalRowIndexFromPlotPointIndex(j);
                    double symbolSize       = null == _cachedSymbolSizeForIndexFunction ? _symbolSize : _cachedSymbolSizeForIndexFunction(originalRowIndex);

                    strokePen.Width = (_lineWidth1Offset + _lineWidth1Factor * symbolSize);

                    if (null != _cachedColorForIndexFunction)
                    {
                        strokePen.Color = GdiColorHelper.ToNamedColor(_cachedColorForIndexFunction(originalRowIndex), "VariableColor");
                    }

                    if (!(strokePen.EndCap is LineCaps.FlatCap))
                    {
                        strokePen.EndCap = strokePen.EndCap.WithMinimumAbsoluteAndRelativeSize(symbolSize * _endCapSizeFactor + _endCapSizeOffset, 1 + 1E-6);
                    }

                    // Calculate target
                    AltaxoVariant targetX, targetY;
                    switch (_meaningOfValues)
                    {
                    case ValueInterpretation.AbsoluteDifference:
                    {
                        targetX = pdata.GetXPhysical(originalRowIndex) + columnX[originalRowIndex];
                        targetY = pdata.GetYPhysical(originalRowIndex) + columnY[originalRowIndex];
                    }
                    break;

                    case ValueInterpretation.AbsoluteValue:
                    {
                        targetX = columnX[originalRowIndex];
                        targetY = columnY[originalRowIndex];
                    }
                    break;

                    default:
                        throw new NotImplementedException(nameof(_meaningOfValues));
                    }

                    var logicalTarget = layer.GetLogical3D(targetX, targetY);
                    var logicalOrigin = layer.GetLogical3D(pdata, originalRowIndex);

                    if (!_independentOnShiftingGroupStyles && (0 != _cachedLogicalShiftX || 0 != _cachedLogicalShiftY))
                    {
                        logicalOrigin.RX += _cachedLogicalShiftX;
                        logicalOrigin.RY += _cachedLogicalShiftY;
                        logicalTarget.RX += _cachedLogicalShiftX;
                        logicalTarget.RY += _cachedLogicalShiftY;
                    }

                    if (!Calc.RMath.IsInIntervalCC(logicalOrigin.RX, logicalClampMinimum, logicalClampMaximum))
                    {
                        continue;
                    }
                    if (!Calc.RMath.IsInIntervalCC(logicalOrigin.RY, logicalClampMinimum, logicalClampMaximum))
                    {
                        continue;
                    }
                    if (!Calc.RMath.IsInIntervalCC(logicalOrigin.RZ, logicalClampMinimum, logicalClampMaximum))
                    {
                        continue;
                    }

                    if (!Calc.RMath.IsInIntervalCC(logicalTarget.RX, logicalClampMinimum, logicalClampMaximum))
                    {
                        continue;
                    }
                    if (!Calc.RMath.IsInIntervalCC(logicalTarget.RY, logicalClampMinimum, logicalClampMaximum))
                    {
                        continue;
                    }
                    if (!Calc.RMath.IsInIntervalCC(logicalTarget.RZ, logicalClampMinimum, logicalClampMaximum))
                    {
                        continue;
                    }

                    isoLine.Reset();

                    layer.CoordinateSystem.GetIsoline(isoLine, logicalOrigin, logicalTarget);
                    if (null == isoLine)
                    {
                        continue;
                    }

                    PointF[] isoLinePathPoints = null;

                    if (_useManualVectorLength)
                    {
                        isoLine.Flatten();
                        isoLinePathPoints = isoLine.PathPoints;

                        double length        = _vectorLengthOffset + _vectorLengthFactor * symbolSize;
                        double isoLineLength = isoLinePathPoints.TotalLineLength();
                        isoLinePathPoints = isoLinePathPoints.ShortenedBy(RADouble.NewAbs(0), RADouble.NewAbs(isoLineLength - length));
                        if (null == isoLine)
                        {
                            continue;
                        }
                    }

                    if (_useSymbolGap)
                    {
                        if (null == isoLinePathPoints)
                        {
                            isoLine.Flatten();
                            isoLinePathPoints = isoLine.PathPoints;
                        }

                        double gap = _symbolGapOffset + _symbolGapFactor * symbolSize;
                        if (gap != 0)
                        {
                            isoLinePathPoints = isoLinePathPoints.ShortenedBy(RADouble.NewAbs(gap / 2), RADouble.NewAbs(0));
                            if (null == isoLine)
                            {
                                continue;
                            }
                        }
                    }

                    if (null != isoLinePathPoints)
                    {
                        g.DrawLines(_strokePen, isoLinePathPoints);
                    }
                    else
                    {
                        g.DrawPath(strokePen, isoLine);
                    }
                }
            }
        }
Beispiel #42
0
		/// <inheritdoc/>
		public override void FillOneRange(
		GraphicsPath gp,
			Processed2DPlotData pdata,
			IPlotRange range,
			IPlotArea layer,
			CSPlaneID fillDirection,
			bool ignoreMissingDataPoints,
			bool connectCircular,
			PointF[] allLinePoints,
			double logicalShiftX,
			double logicalShiftY
		)
		{
			if (range.Length < 4)
				return;


			if (connectCircular)
			{
				var circularLinePointsLengthM1 = 2 + TrimToValidBezierLength(range.Length);
				var circularLinePoints = new PointF[circularLinePointsLengthM1 + 1];
				Array.Copy(allLinePoints, range.LowerBound, circularLinePoints, 0, range.Length); // Extract
				circularLinePoints[circularLinePointsLengthM1] = circularLinePoints[0];

				// amend missing control points
				if (circularLinePointsLengthM1 - range.Length >= 1)
					circularLinePoints[circularLinePointsLengthM1 - 1] = GdiExtensionMethods.Interpolate(circularLinePoints[circularLinePointsLengthM1 - 3], circularLinePoints[circularLinePointsLengthM1], 0.5); // Last Control point should be halfway between
				if (circularLinePointsLengthM1 - range.Length >= 2)
					circularLinePoints[circularLinePointsLengthM1 - 2] = GdiExtensionMethods.Interpolate(circularLinePoints[circularLinePointsLengthM1 - 3], circularLinePoints[circularLinePointsLengthM1], 0.5); // Middle Control point should be halfway between previous fixed point and last(=first) fixed point

				FillOneRange_PreprocessedPoints(gp, pdata, range, layer, fillDirection, circularLinePoints, connectCircular, logicalShiftX, logicalShiftY);
			}
			else
			{
				var trimmedLinePointsLength = TrimToValidBezierLength(range.Length);
				var trimmedLinePoints = new PointF[trimmedLinePointsLength];
				Array.Copy(allLinePoints, range.LowerBound, trimmedLinePoints, 0, trimmedLinePointsLength); // Extract
				FillOneRange_PreprocessedPoints(gp, pdata, range, layer, fillDirection, trimmedLinePoints, connectCircular, logicalShiftX, logicalShiftY);
			}
		}