コード例 #1
0
ファイル: G2DPlotItem.cs プロジェクト: olesar/Altaxo
        /// <summary>
        /// Test wether the mouse hits a plot item.
        /// </summary>
        /// <param name="layer">The layer in which this plot item is drawn into.</param>
        /// <param name="hitpoint">The point where the mouse is pressed.</param>
        /// <returns>Null if no hit, or a <see cref="IHitTestObject" /> if there was a hit.</returns>
        public override IHitTestObject HitTest(IPlotArea layer, PointD2D hitpoint)
        {
            Processed2DPlotData pdata = _cachedPlotDataUsedForPainting;

            if (null == pdata)
            {
                return(null);
            }

            PlotRangeList rangeList = pdata.RangeList;

            PointF[] ptArray = pdata.PlotPointsInAbsoluteLayerCoordinates;

            if (ptArray.Length < 2)
            {
                return(null);
            }

            if (ptArray.Length < 2048)
            {
                if (GdiExtensionMethods.IsPointIntoDistance((PointF)hitpoint, 2.5, ptArray))
                {
                    var gp = new GraphicsPath();
                    gp.AddLines(ptArray);
                    gp.Widen(new Pen(Color.Black, 5));
                    return(new HitTestObject(gp, this));
                }
            }
            else // we have too much points for the graphics path, so make a hit test first
            {
                int hitindex = -1;
                for (int i = 1; i < ptArray.Length; i++)
                {
                    if (Math2D.IsPointIntoDistance((PointF)hitpoint, 5, ptArray[i - 1], ptArray[i]))
                    {
                        hitindex = i;
                        break;
                    }
                }
                if (hitindex < 0)
                {
                    return(null);
                }
                var gp    = new GraphicsPath();
                int start = Math.Max(0, hitindex - 2);
                gp.AddLine(ptArray[start], ptArray[start + 1]);
                gp.AddLine(ptArray[start + 1], ptArray[start + 2]);
                gp.Widen(new Pen(Color.Black, 5));
                return(new HitTestObject(gp, this));
            }

            return(null);
        }
コード例 #2
0
ファイル: BezierConnection.cs プロジェクト: olesar/Altaxo
        /// <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);
            }
        }
コード例 #3
0
ファイル: Segment2Connection.cs プロジェクト: olesar/Altaxo
        /// <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();
            }
        }
コード例 #4
0
ファイル: BezierConnection.cs プロジェクト: olesar/Altaxo
        /// <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);
            }
        }
コード例 #5
0
ファイル: DropLinePlotStyle.cs プロジェクト: olesar/Altaxo
        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);
                        }
                    }
                }
            }
        }
コード例 #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)
        {
            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);
                }
            }
        }