/// <summary> /// Add a <see cref="CurveItem"/> to the selection list. /// </summary> /// <param name="master"> /// The <see cref="MasterPane"/> that is the "owner" of the <see cref="CurveItem"/>'s. /// </param> /// <param name="ci"> /// The <see cref="CurveItem"/> to be added to the list. /// </param> public void AddToSelection(MasterPane master, CurveItem ci) { if (this.Contains(ci) == false) { this.Add(ci); } this.UpdateSelection(master); }
/// <summary> /// Find the data point that lies closest to the specified mouse (screen) point. /// </summary> /// <remarks> /// This method will search through the specified list of curves to find which point is nearest. It will only consider points that are within /// <see cref="Default.NearestTol"/> pixels of the screen point, and it will only consider <see cref="CurveItem"/>'s that are in /// <paramref name="targetCurveList"/>. /// </remarks> /// <param name="mousePt"> /// The screen point, in pixel coordinates. /// </param> /// <param name="targetCurveList"> /// A <see cref="CurveList"/> object containing a subset of <see cref="CurveItem"/>'s to be searched. /// </param> /// <param name="nearestCurve"> /// A reference to the <see cref="CurveItem"/> /// instance that contains the closest point. nearestCurve will be null if no data points are available. /// </param> /// <param name="iNearest"> /// The index number of the closest point. The actual data vpoint will then be <see cref="CurveItem.Points">CurveItem.Points[iNearest]</see> /// . iNearest will be -1 if no data points are available. /// </param> /// <returns> /// true if a point was found and that point lies within /// <see cref="Default.NearestTol"/> pixels of the screen point, false otherwise. /// </returns> public bool FindNearestPoint(PointF mousePt, CurveList targetCurveList, out CurveItem nearestCurve, out int iNearest) { CurveItem nearestBar = null; int iNearestBar = -1; nearestCurve = null; iNearest = -1; // If the point is outside the ChartRect, always return false if (!this._chart._rect.Contains(mousePt)) { return false; } double x, x2; double[] y; double[] y2; // ReverseTransform( mousePt, out x, out y, out y2 ); this.ReverseTransform(mousePt, out x, out x2, out y, out y2); if (!this.AxisRangesValid()) { return false; } ValueHandler valueHandler = new ValueHandler(this, false); // double yPixPerUnit = chartRect.Height / ( yAxis.Max - yAxis.Min ); // double y2PixPerUnit; // = chartRect.Height / ( y2Axis.Max - y2Axis.Min ); double yPixPerUnitAct, yAct, yMinAct, yMaxAct, xAct; double minDist = 1e20; double xVal, yVal, dist = 99999, distX, distY; double tolSquared = Default.NearestTol * Default.NearestTol; int iBar = 0; foreach (CurveItem curve in targetCurveList) { // test for pie first...if it's a pie rest of method superfluous if (curve is PieItem && curve.IsVisible) { if (((PieItem)curve).SlicePath != null && ((PieItem)curve).SlicePath.IsVisible(mousePt)) { nearestBar = curve; iNearestBar = 0; } } else if (curve.IsVisible) { int yIndex = curve.GetYAxisIndex(this); Axis yAxis = curve.GetYAxis(this); Axis xAxis = curve.GetXAxis(this); if (curve.IsY2Axis) { yAct = y2[yIndex]; yMinAct = this._y2AxisList[yIndex]._scale._min; yMaxAct = this._y2AxisList[yIndex]._scale._max; } else { yAct = y[yIndex]; yMinAct = this._yAxisList[yIndex]._scale._min; yMaxAct = this._yAxisList[yIndex]._scale._max; } yPixPerUnitAct = this._chart._rect.Height / (yMaxAct - yMinAct); double xPixPerUnit = this._chart._rect.Width / (xAxis._scale._max - xAxis._scale._min); xAct = xAxis is XAxis ? x : x2; IPointList points = curve.Points; float barWidth = curve.GetBarWidth(this); double barWidthUserHalf; Axis baseAxis = curve.BaseAxis(this); bool isXBaseAxis = baseAxis is XAxis || baseAxis is X2Axis; if (isXBaseAxis) { barWidthUserHalf = barWidth / xPixPerUnit / 2.0; } else { barWidthUserHalf = barWidth / yPixPerUnitAct / 2.0; } if (points != null) { for (int iPt = 0; iPt < curve.NPts; iPt++) { // xVal is the user scale X value of the current point if (xAxis._scale.IsAnyOrdinal && !curve.IsOverrideOrdinal) { xVal = iPt + 1.0; } else { xVal = points[iPt].X; } // yVal is the user scale Y value of the current point if (yAxis._scale.IsAnyOrdinal && !curve.IsOverrideOrdinal) { yVal = iPt + 1.0; } else { yVal = points[iPt].Y; } if (xVal != PointPairBase.Missing && yVal != PointPairBase.Missing) { if (curve.IsBar || curve is ErrorBarItem || curve is HiLowBarItem || curve is OHLCBarItem || curve is JapaneseCandleStickItem) { double baseVal, lowVal, hiVal; valueHandler.GetValues(curve, iPt, out baseVal, out lowVal, out hiVal); if (lowVal > hiVal) { double tmpVal = lowVal; lowVal = hiVal; hiVal = tmpVal; } if (isXBaseAxis) { double centerVal = valueHandler.BarCenterValue(curve, barWidth, iPt, xVal, iBar); if (xAct < centerVal - barWidthUserHalf || xAct > centerVal + barWidthUserHalf || yAct < lowVal || yAct > hiVal) { continue; } } else { double centerVal = valueHandler.BarCenterValue(curve, barWidth, iPt, yVal, iBar); if (yAct < centerVal - barWidthUserHalf || yAct > centerVal + barWidthUserHalf || xAct < lowVal || xAct > hiVal) { continue; } } if (nearestBar == null) { iNearestBar = iPt; nearestBar = curve; } } else if (xVal >= xAxis._scale._min && xVal <= xAxis._scale._max && yVal >= yMinAct && yVal <= yMaxAct) { if (curve is LineItem && this._lineType == LineType.Stack) { double zVal; valueHandler.GetValues(curve, iPt, out xVal, out zVal, out yVal); } distX = (xVal - xAct) * xPixPerUnit; distY = (yVal - yAct) * yPixPerUnitAct; dist = distX * distX + distY * distY; if (dist >= minDist) { continue; } minDist = dist; iNearest = iPt; nearestCurve = curve; } } } if (curve.IsBar) { iBar++; } } } } if (nearestCurve is LineItem) { float halfSymbol = ((LineItem)nearestCurve).Symbol.Size * this.CalcScaleFactor() / 2; minDist -= halfSymbol * halfSymbol; if (minDist < 0) { minDist = 0; } } if (minDist >= tolSquared && nearestBar != null) { // if no point met the tolerance, but a bar was found, use it nearestCurve = nearestBar; iNearest = iNearestBar; return true; } if (minDist < tolSquared) { // Did we find a close point, and is it within the tolerance? // (minDist is the square of the distance in pixel units) return true; } return false; }
/// <summary> /// Find the data point that lies closest to the specified mouse (screen) point. /// </summary> /// <remarks> /// This method will search through all curves in /// <see cref="GraphPane.CurveList"/> to find which point is nearest. It will only consider points that are within /// <see cref="Default.NearestTol"/> pixels of the screen point. /// </remarks> /// <param name="mousePt"> /// The screen point, in pixel coordinates. /// </param> /// <param name="nearestCurve"> /// A reference to the <see cref="CurveItem"/> /// instance that contains the closest point. nearestCurve will be null if no data points are available. /// </param> /// <param name="iNearest"> /// The index number of the closest point. The actual data vpoint will then be <see cref="CurveItem.Points">CurveItem.Points[iNearest]</see> /// . iNearest will be -1 if no data points are available. /// </param> /// <returns> /// true if a point was found and that point lies within /// <see cref="Default.NearestTol"/> pixels of the screen point, false otherwise. /// </returns> public bool FindNearestPoint(PointF mousePt, out CurveItem nearestCurve, out int iNearest) { return this.FindNearestPoint(mousePt, this._curveList, out nearestCurve, out iNearest); }
/// <summary> /// Remove the specified <see cref="CurveItem"/> from the selection list. /// </summary> /// <param name="master"> /// The <see cref="MasterPane"/> that is the "owner" of the <see cref="CurveItem"/>'s. /// </param> /// <param name="ci"> /// The <see cref="CurveItem"/> to be removed from the list. /// </param> public void RemoveFromSelection(MasterPane master, CurveItem ci) { if (this.Contains(ci)) { this.Remove(ci); } this.UpdateSelection(master); }
/// <summary> /// Do all rendering associated with this <see cref="Line"/> to the specified /// <see cref="Graphics"/> device. This method is normally only called by the Draw method of the parent <see cref="LineItem"/> object. /// </summary> /// <param name="g"> /// A graphic device object to be drawn into. This is normally e.Graphics from the PaintEventArgs argument to the Paint() method. /// </param> /// <param name="pane"> /// A reference to the <see cref="GraphPane"/> object that is the parent or owner of this object. /// </param> /// <param name="curve"> /// A <see cref="LineItem"/> representing this curve. /// </param> /// <param name="scaleFactor"> /// The scaling factor to be used for rendering objects. This is calculated and passed down by the parent <see cref="GraphPane"/> object using the /// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust font sizes, etc. according to the actual size of the graph. /// </param> public void Draw(Graphics g, GraphPane pane, CurveItem curve, float scaleFactor) { // If the line is being shown, draw it if (this.IsVisible) { // How to handle fill vs nofill? // if ( isSelected ) // GraphPane.Default.SelectedLine. SmoothingMode sModeSave = g.SmoothingMode; if (this._isAntiAlias) { g.SmoothingMode = SmoothingMode.HighQuality; } if (curve is StickItem) { this.DrawSticks(g, pane, curve, scaleFactor); } else if (this.IsSmooth || this.Fill.IsVisible) { this.DrawSmoothFilledCurve(g, pane, curve, scaleFactor); } else { this.DrawCurve(g, pane, curve, scaleFactor); } g.SmoothingMode = sModeSave; } }
/// <summary> /// Build an array of <see cref="PointF"/> values (pixel coordinates) that represents the current curve. Note that this drawing routine ignores /// <see cref="PointPairBase.Missing"/> /// values, but it does not "break" the line to indicate values are missing. /// </summary> /// <param name="pane"> /// A reference to the <see cref="GraphPane"/> object that is the parent or owner of this object. /// </param> /// <param name="curve"> /// A <see cref="LineItem"/> representing this curve. /// </param> /// <param name="arrPoints"> /// An array of <see cref="PointF"/> values in pixel coordinates representing the current curve. /// </param> /// <param name="count"> /// The number of points contained in the "arrPoints" parameter. /// </param> /// <returns> /// true for a successful points array build, false for data problems /// </returns> public bool BuildPointsArray(GraphPane pane, CurveItem curve, out PointF[] arrPoints, out int count) { arrPoints = null; count = 0; IPointList points = curve.Points; if (this.IsVisible && !this.Color.IsEmpty && points != null) { int index = 0; float curX, curY, lastX = 0, lastY = 0; double x, y, lowVal; ValueHandler valueHandler = new ValueHandler(pane, false); // Step type plots get twice as many points. Always add three points so there is // room to close out the curve for area fills. arrPoints = new PointF[(this._stepType == StepType.NonStep ? 1 : 2) * points.Count + 1]; // Loop over all points in the curve for (int i = 0; i < points.Count; i++) { // make sure that the current point is valid if (!points[i].IsInvalid) { // Get the user scale values for the current point // use the valueHandler only for stacked types if (pane.LineType == LineType.Stack) { valueHandler.GetValues(curve, i, out x, out lowVal, out y); } // otherwise, just access the values directly. Avoiding the valueHandler for // non-stacked types is an optimization to minimize overhead in case there are // a large number of points. else { x = points[i].X; y = points[i].Y; } if (x == PointPairBase.Missing || y == PointPairBase.Missing) { continue; } // Transform the user scale values to pixel locations Axis xAxis = curve.GetXAxis(pane); curX = xAxis.Scale.Transform(curve.IsOverrideOrdinal, i, x); Axis yAxis = curve.GetYAxis(pane); curY = yAxis.Scale.Transform(curve.IsOverrideOrdinal, i, y); if (curX < -1000000 || curY < -1000000 || curX > 1000000 || curY > 1000000) { continue; } // Add the pixel value pair into the points array // Two points are added for step type curves // ignore step-type setting for smooth curves if (this._isSmooth || index == 0 || this.StepType == StepType.NonStep) { arrPoints[index].X = curX; arrPoints[index].Y = curY; } else if (this.StepType == StepType.ForwardStep || this.StepType == StepType.ForwardSegment) { arrPoints[index].X = curX; arrPoints[index].Y = lastY; index++; arrPoints[index].X = curX; arrPoints[index].Y = curY; } else if (this.StepType == StepType.RearwardStep || this.StepType == StepType.RearwardSegment) { arrPoints[index].X = lastX; arrPoints[index].Y = curY; index++; arrPoints[index].X = curX; arrPoints[index].Y = curY; } lastX = curX; lastY = curY; index++; } } // Make sure there is at least one valid point if (index == 0) { return false; } // Add an extra point at the end, since the smoothing algorithm requires it arrPoints[index] = arrPoints[index - 1]; index++; count = index; return true; } return false; }
/// <summary> /// The is first line. /// </summary> /// <param name="pane"> /// The pane. /// </param> /// <param name="curve"> /// The curve. /// </param> /// <returns> /// The <see cref="bool"/>. /// </returns> private bool IsFirstLine(GraphPane pane, CurveItem curve) { CurveList curveList = pane.CurveList; for (int j = 0; j < curveList.Count; j++) { CurveItem tCurve = curveList[j]; if (tCurve is LineItem && tCurve.IsY2Axis == curve.IsY2Axis && tCurve.YAxisIndex == curve.YAxisIndex) { return tCurve == curve; } } return false; }
/// <summary> /// Render the <see cref="Line"/>'s as vertical sticks (from a <see cref="StickItem"/>) to the specified <see cref="Graphics"/> device. /// </summary> /// <param name="g"> /// A graphic device object to be drawn into. This is normally e.Graphics from the PaintEventArgs argument to the Paint() method. /// </param> /// <param name="pane"> /// A reference to the <see cref="GraphPane"/> object that is the parent or owner of this object. /// </param> /// <param name="curve"> /// A <see cref="CurveItem"/> representing this curve. /// </param> /// <param name="scaleFactor"> /// The scaling factor to be used for rendering objects. This is calculated and passed down by the parent <see cref="GraphPane"/> object using the /// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust font sizes, etc. according to the actual size of the graph. /// </param> public void DrawSticks(Graphics g, GraphPane pane, CurveItem curve, float scaleFactor) { Line source = this; if (curve.IsSelected) { source = Selection.Line; } Axis yAxis = curve.GetYAxis(pane); Axis xAxis = curve.GetXAxis(pane); float basePix = yAxis.Scale.Transform(0.0); using (Pen pen = source.GetPen(pane, scaleFactor)) { for (int i = 0; i < curve.Points.Count; i++) { PointPair pt = curve.Points[i]; if (pt.X != PointPairBase.Missing && pt.Y != PointPairBase.Missing && !double.IsNaN(pt.X) && !double.IsNaN(pt.Y) && !double.IsInfinity(pt.X) && !double.IsInfinity(pt.Y) && (!xAxis._scale.IsLog || pt.X > 0.0) && (!yAxis._scale.IsLog || pt.Y > 0.0)) { float pixY = yAxis.Scale.Transform(curve.IsOverrideOrdinal, i, pt.Y); float pixX = xAxis.Scale.Transform(curve.IsOverrideOrdinal, i, pt.X); if (pixX >= pane.Chart._rect.Left && pixX <= pane.Chart._rect.Right) { if (pixY > pane.Chart._rect.Bottom) { pixY = pane.Chart._rect.Bottom; } if (pixY < pane.Chart._rect.Top) { pixY = pane.Chart._rect.Top; } if (!curve.IsSelected && this._gradientFill.IsGradientValueType) { using (Pen tPen = this.GetPen(pane, scaleFactor, pt)) g.DrawLine(tPen, pixX, pixY, pixX, basePix); } else { g.DrawLine(pen, pixX, pixY, pixX, basePix); } } } } } }
/// <summary> /// Get the user scale values associate with a particular point of a particular curve. /// </summary> /// <remarks> /// The main purpose of this method is to handle stacked bars and lines, in which case the stacked values are returned rather than the individual data /// values. However, this method works generically for any curve type. /// </remarks> /// <param name="pane"> /// The parent <see cref="GraphPane"/> object. /// </param> /// <param name="curve"> /// A <see cref="CurveItem"/> object of interest. /// </param> /// <param name="iPt"> /// The zero-based point index for the point of interest. /// </param> /// <param name="baseVal"> /// A <see cref="double"/> value representing the value for the independent axis. /// </param> /// <param name="lowVal"> /// A <see cref="double"/> value representing the lower value for the dependent axis. /// </param> /// <param name="hiVal"> /// A <see cref="double"/> value representing the upper value for the dependent axis. /// </param> /// <returns> /// true if the data point is value, false for /// <see cref="PointPairBase.Missing"/>, invalid, etc. data. /// </returns> public static bool GetValues(GraphPane pane, CurveItem curve, int iPt, out double baseVal, out double lowVal, out double hiVal) { hiVal = PointPairBase.Missing; lowVal = PointPairBase.Missing; baseVal = PointPairBase.Missing; if (curve == null || curve.Points.Count <= iPt || !curve.IsVisible) { return false; } Axis baseAxis = curve.BaseAxis(pane); Axis valueAxis = curve.ValueAxis(pane); if (baseAxis is XAxis || baseAxis is X2Axis) { baseVal = curve.Points[iPt].X; } else { baseVal = curve.Points[iPt].Y; } // is it a stacked bar type? if (curve is BarItem && (pane._barSettings.Type == BarType.Stack || pane._barSettings.Type == BarType.PercentStack)) { double positiveStack = 0; double negativeStack = 0; double curVal; // loop through all the curves, summing up the values to get a total (only // for the current ordinal position iPt) foreach (CurveItem tmpCurve in pane.CurveList) { // Sum the value for the current curve only if it is a bar if (tmpCurve.IsBar && tmpCurve.IsVisible) { curVal = PointPairBase.Missing; // For non-ordinal curves, find a matching base value (must match exactly) if (curve.IsOverrideOrdinal || !baseAxis._scale.IsAnyOrdinal) { IPointList points = tmpCurve.Points; for (int i = 0; i < points.Count; i++) { if ((baseAxis is XAxis || baseAxis is X2Axis) && points[i].X == baseVal) { curVal = points[i].Y; break; } if (!(baseAxis is XAxis || baseAxis is X2Axis) && points[i].Y == baseVal) { curVal = points[i].X; break; } } } // otherwise, it's an ordinal type so use the value at the same ordinal position else if (iPt < tmpCurve.Points.Count) { // Get the value for the appropriate value axis if (baseAxis is XAxis || baseAxis is X2Axis) { curVal = tmpCurve.Points[iPt].Y; } else { curVal = tmpCurve.Points[iPt].X; } } // If it's a missing value, skip it if (curVal == PointPairBase.Missing) { positiveStack = PointPairBase.Missing; negativeStack = PointPairBase.Missing; } // the current curve is the target curve, save the summed values for later if (tmpCurve == curve) { // if the value is positive, use the positive stack if (curVal >= 0) { lowVal = positiveStack; hiVal = (curVal == PointPairBase.Missing || positiveStack == PointPairBase.Missing) ? PointPairBase.Missing : positiveStack + curVal; } // otherwise, use the negative stack else { hiVal = negativeStack; lowVal = (curVal == PointPairBase.Missing || negativeStack == PointPairBase.Missing) ? PointPairBase.Missing : negativeStack + curVal; } } // Add all positive values to the positive stack, and negative values to the // negative stack if (curVal >= 0) { positiveStack = (curVal == PointPairBase.Missing || positiveStack == PointPairBase.Missing) ? PointPairBase.Missing : positiveStack + curVal; } else { negativeStack = (curVal == PointPairBase.Missing || negativeStack == PointPairBase.Missing) ? PointPairBase.Missing : negativeStack + curVal; } } } // if the curve is a PercentStack type, then calculate the percent for this bar // based on the total height of the stack if (pane._barSettings.Type == BarType.PercentStack && hiVal != PointPairBase.Missing && lowVal != PointPairBase.Missing) { // Use the total magnitude of the positive plus negative bar stacks to determine // the percentage value positiveStack += Math.Abs(negativeStack); // just to avoid dividing by zero... if (positiveStack != 0) { // calculate the percentage values lowVal = lowVal / positiveStack * 100.0; hiVal = hiVal / positiveStack * 100.0; } else { lowVal = 0; hiVal = 0; } } if (baseVal == PointPairBase.Missing || lowVal == PointPairBase.Missing || hiVal == PointPairBase.Missing) { return false; } return true; } // If the curve is a stacked line type, then sum up the values similar to the stacked bar type if (curve is LineItem && pane.LineType == LineType.Stack) { double stack = 0; double curVal; // loop through all the curves, summing up the values to get a total (only // for the current ordinal position iPt) foreach (CurveItem tmpCurve in pane.CurveList) { // make sure the curve is a Line type if (tmpCurve is LineItem && tmpCurve.IsVisible) { curVal = PointPairBase.Missing; // For non-ordinal curves, find a matching base value (must match exactly) if (curve.IsOverrideOrdinal || !baseAxis._scale.IsAnyOrdinal) { IPointList points = tmpCurve.Points; for (int i = 0; i < points.Count; i++) { if (points[i].X == baseVal) { curVal = points[i].Y; break; } } } // otherwise, it's an ordinal type so use the value at the same ordinal position else if (iPt < tmpCurve.Points.Count) { // For line types, the Y axis is always the value axis curVal = tmpCurve.Points[iPt].Y; } // if the current value is missing, then the rest of the stack is missing if (curVal == PointPairBase.Missing) { stack = PointPairBase.Missing; } // if the current curve is the target curve, save the values if (tmpCurve == curve) { lowVal = stack; // if ( curVal < 0 && stack == 0 ) // { // stack = curVal; // lowVal = curVal; // hiVal = curVal; // } // else hiVal = (curVal == PointPairBase.Missing || stack == PointPairBase.Missing) ? PointPairBase.Missing : stack + curVal; } // sum all the curves to a single total. This includes both positive and // negative values (unlike the bar stack type). stack = (curVal == PointPairBase.Missing || stack == PointPairBase.Missing) ? PointPairBase.Missing : stack + curVal; } } if (baseVal == PointPairBase.Missing || lowVal == PointPairBase.Missing || hiVal == PointPairBase.Missing) { return false; } return true; } // otherwise, the curve is not a stacked type (not a stacked bar or stacked line) if ((!(curve is HiLowBarItem)) && (!(curve is ErrorBarItem))) { lowVal = 0; } else { lowVal = curve.Points[iPt].LowValue; } if (baseAxis is XAxis || baseAxis is X2Axis) { hiVal = curve.Points[iPt].Y; } else { hiVal = curve.Points[iPt].X; } // Special Exception: Bars on log scales should always plot from the Min value upwards, // since they can never be zero if (curve is BarItem && valueAxis._scale.IsLog && lowVal == 0) { lowVal = valueAxis._scale._min; } if (baseVal == PointPairBase.Missing || hiVal == PointPairBase.Missing || (lowVal == PointPairBase.Missing && (curve is ErrorBarItem || curve is HiLowBarItem))) { return false; } return true; }
/// <summary> /// Get the user scale values associate with a particular point of a particular curve. /// </summary> /// <remarks> /// The main purpose of this method is to handle stacked bars, in which case the stacked values are returned rather than the individual data /// values. /// </remarks> /// <param name="curve"> /// A <see cref="CurveItem"/> object of interest. /// </param> /// <param name="iPt"> /// The zero-based point index for the point of interest. /// </param> /// <param name="baseVal"> /// A <see cref="double"/> value representing the value for the independent axis. /// </param> /// <param name="lowVal"> /// A <see cref="double"/> value representing the lower value for the dependent axis. /// </param> /// <param name="hiVal"> /// A <see cref="double"/> value representing the upper value for the dependent axis. /// </param> /// <returns> /// true if the data point is value, false for /// <see cref="PointPairBase.Missing"/>, invalid, etc. data. /// </returns> public bool GetValues(CurveItem curve, int iPt, out double baseVal, out double lowVal, out double hiVal) { return GetValues(this._pane, curve, iPt, out baseVal, out lowVal, out hiVal); }
/// <summary> /// Calculate the user scale position of the center of the specified bar, using the /// <see cref="Axis"/> as specified by <see cref="BarSettings.Base"/>. This method is used primarily by the /// <see cref="GraphPane.FindNearestPoint(PointF,out CurveItem,out int)"/> method in order to determine the bar "location," which is defined as the /// center of the top of the individual bar. /// </summary> /// <param name="curve"> /// The <see cref="CurveItem"/> representing the bar of interest. /// </param> /// <param name="barWidth"> /// The width of each individual bar. This can be calculated using the <see cref="CurveItem.GetBarWidth"/> method. /// </param> /// <param name="iCluster"> /// The cluster number for the bar of interest. This is the ordinal position of the current point. That is, if a particular <see cref="CurveItem"/> /// has 10 points, then a value of 3 would indicate the 4th point in the data array. /// </param> /// <param name="val"> /// The actual independent axis value for the bar of interest. /// </param> /// <param name="iOrdinal"> /// The ordinal position of the <see cref="CurveItem"/> of interest. That is, the first bar series is 0, the second is 1, etc. Note that this applies /// only to the bars. If a graph includes both bars and lines, then count only the bars. /// </param> /// <returns> /// A user scale value position of the center of the bar of interest. /// </returns> public double BarCenterValue(CurveItem curve, float barWidth, int iCluster, double val, int iOrdinal) { Axis baseAxis = curve.BaseAxis(this._pane); if (curve is ErrorBarItem || curve is HiLowBarItem || curve is OHLCBarItem || curve is JapaneseCandleStickItem) { if (baseAxis._scale.IsAnyOrdinal && iCluster >= 0 && !curve.IsOverrideOrdinal) { return iCluster + 1.0; } return val; } float clusterWidth = this._pane._barSettings.GetClusterWidth(); float clusterGap = this._pane._barSettings.MinClusterGap * barWidth; float barGap = barWidth * this._pane._barSettings.MinBarGap; if (curve.IsBar && this._pane._barSettings.Type != BarType.Cluster) { iOrdinal = 0; } float centerPix = baseAxis.Scale.Transform(curve.IsOverrideOrdinal, iCluster, val) - clusterWidth / 2.0F + clusterGap / 2.0F + iOrdinal * (barWidth + barGap) + 0.5F * barWidth; return baseAxis.Scale.ReverseTransform(centerPix); }
// public static Color SelectedSymbolColor = Color.Gray; #endregion Other #if ( DOTNET1 ) // Define a "Contains" method so that this class works with .Net 1.1 or 2.0 internal bool Contains( CurveItem item ) { foreach ( CurveItem ci in this ) if ( item == ci ) return true; return false; }
/// <summary> /// Place a <see cref="CurveItem"/> in the selection list, removing all other items. /// </summary> /// <param name="master"> /// The <see cref="MasterPane"/> that is the "owner" of the <see cref="CurveItem"/>'s. /// </param> /// <param name="ci"> /// The <see cref="CurveItem"/> to be added to the list. /// </param> public void Select(MasterPane master, CurveItem ci) { // Clear the selection, but don't send the event, // the event will be sent in "AddToSelection" by calling "UpdateSelection" this.ClearSelection(master, false); this.AddToSelection(master, ci); }
/// <summary> /// Initializes a new instance of the <see cref="CurveItem"/> class. /// The Copy Constructor /// </summary> /// <param name="rhs"> /// The CurveItem object from which to copy /// </param> public CurveItem(CurveItem rhs) { this._label = rhs._label.Clone(); this._isY2Axis = rhs.IsY2Axis; this._isX2Axis = rhs.IsX2Axis; this._isVisible = rhs.IsVisible; this._isOverrideOrdinal = rhs._isOverrideOrdinal; this._yAxisIndex = rhs._yAxisIndex; if (rhs.Tag is ICloneable) { this.Tag = ((ICloneable)rhs.Tag).Clone(); } else { this.Tag = rhs.Tag; } this._points = (IPointList)rhs.Points.Clone(); this._link = rhs._link.Clone(); }
/// <summary> /// Draw the this <see cref="Bar"/> to the specified <see cref="Graphics"/> /// device as a bar at each defined point. This method is normally only called by the <see cref="BarItem.Draw"/> method of the /// <see cref="BarItem"/> object /// </summary> /// <param name="g"> /// A graphic device object to be drawn into. This is normally e.Graphics from the PaintEventArgs argument to the Paint() method. /// </param> /// <param name="pane"> /// A reference to the <see cref="GraphPane"/> object that is the parent or owner of this object. /// </param> /// <param name="curve"> /// A <see cref="CurveItem"/> object representing the /// <see cref="Bar"/>'s to be drawn. /// </param> /// <param name="baseAxis"> /// The <see cref="Axis"/> class instance that defines the base (independent) axis for the <see cref="Bar"/> /// </param> /// <param name="valueAxis"> /// The <see cref="Axis"/> class instance that defines the value (dependent) axis for the <see cref="Bar"/> /// </param> /// <param name="barWidth"> /// The width of each bar, in pixels. /// </param> /// <param name="pos"> /// The ordinal position of the this bar series (0=first bar, 1=second bar, etc.) in the cluster of bars. /// </param> /// <param name="scaleFactor"> /// The scaling factor to be used for rendering objects. This is calculated and passed down by the parent <see cref="GraphPane"/> object using the /// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust font sizes, etc. according to the actual size of the graph. /// </param> public void DrawBars(Graphics g, GraphPane pane, CurveItem curve, Axis baseAxis, Axis valueAxis, float barWidth, int pos, float scaleFactor) { // For non-cluster bar types, the position is always zero since the bars are on top // of eachother BarType barType = pane._barSettings.Type; if (barType == BarType.Overlay || barType == BarType.Stack || barType == BarType.PercentStack || barType == BarType.SortedOverlay) { pos = 0; } // Loop over each defined point and draw the corresponding bar for (int i = 0; i < curve.Points.Count; i++) { this.DrawSingleBar(g, pane, curve, i, pos, baseAxis, valueAxis, barWidth, scaleFactor); } }
/// <summary> /// Draw the this <see cref="CurveItem"/> to the specified <see cref="Graphics"/> /// device using the specified smoothing property (<see cref="Line.SmoothTension"/>). The routine draws the line segments and the area fill (if any, see /// <see cref="FillType"/>; the symbols are drawn by the <see cref="Symbol.Draw"/> method. This method is normally only called by the Draw method of /// the /// <see cref="CurveItem"/> object. Note that the <see cref="StepType"/> property is ignored for smooth lines (e.g., when <see cref="Line.IsSmooth"/> /// is true). /// </summary> /// <param name="g"> /// A graphic device object to be drawn into. This is normally e.Graphics from the PaintEventArgs argument to the Paint() method. /// </param> /// <param name="pane"> /// A reference to the <see cref="GraphPane"/> object that is the parent or owner of this object. /// </param> /// <param name="curve"> /// A <see cref="LineItem"/> representing this curve. /// </param> /// <param name="scaleFactor"> /// The scaling factor to be used for rendering objects. This is calculated and passed down by the parent <see cref="GraphPane"/> object using the /// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust font sizes, etc. according to the actual size of the graph. /// </param> public void DrawSmoothFilledCurve(Graphics g, GraphPane pane, CurveItem curve, float scaleFactor) { Line source = this; if (curve.IsSelected) { source = Selection.Line; } PointF[] arrPoints; int count; IPointList points = curve.Points; if (this.IsVisible && !this.Color.IsEmpty && points != null && this.BuildPointsArray(pane, curve, out arrPoints, out count) && count > 2) { float tension = this._isSmooth ? this._smoothTension : 0f; // Fill the curve if needed if (this.Fill.IsVisible) { Axis yAxis = curve.GetYAxis(pane); using (GraphicsPath path = new GraphicsPath(FillMode.Winding)) { path.AddCurve(arrPoints, 0, count - 2, tension); double yMin = yAxis._scale._min < 0 ? 0.0 : yAxis._scale._min; this.CloseCurve(pane, curve, arrPoints, count, yMin, path); RectangleF rect = path.GetBounds(); using (Brush brush = source._fill.MakeBrush(rect)) { if (pane.LineType == LineType.Stack && yAxis.Scale._min < 0 && this.IsFirstLine(pane, curve)) { float zeroPix = yAxis.Scale.Transform(0); RectangleF tRect = pane.Chart._rect; tRect.Height = zeroPix - tRect.Top; if (tRect.Height > 0) { Region reg = g.Clip; g.SetClip(tRect); g.FillPath(brush, path); g.SetClip(pane.Chart._rect); } } else { g.FillPath(brush, path); } // brush.Dispose(); } // restore the zero line if needed (since the fill tends to cover it up) yAxis.FixZeroLine(g, pane, scaleFactor, rect.Left, rect.Right); } } // If it's a smooth curve, go ahead and render the path. Otherwise, use the // standard drawcurve method just in case there are missing values. if (this._isSmooth) { using (Pen pen = this.GetPen(pane, scaleFactor)) { // Stroke the curve g.DrawCurve(pen, arrPoints, 0, count - 2, tension); // pen.Dispose(); } } else { this.DrawCurve(g, pane, curve, scaleFactor); } } }
/// <summary> /// Draw the specified single bar (an individual "point") of this series to the specified /// <see cref="Graphics"/> device. This method is not as efficient as /// <see cref="DrawBars"/>, which draws the bars for all points. It is intended to be used only for <see cref="BarType.SortedOverlay"/>, which /// requires special handling of each bar. /// </summary> /// <param name="g"> /// A graphic device object to be drawn into. This is normally e.Graphics from the PaintEventArgs argument to the Paint() method. /// </param> /// <param name="pane"> /// A reference to the <see cref="GraphPane"/> object that is the parent or owner of this object. /// </param> /// <param name="curve"> /// A <see cref="CurveItem"/> object representing the /// <see cref="Bar"/>'s to be drawn. /// </param> /// <param name="baseAxis"> /// The <see cref="Axis"/> class instance that defines the base (independent) axis for the <see cref="Bar"/> /// </param> /// <param name="valueAxis"> /// The <see cref="Axis"/> class instance that defines the value (dependent) axis for the <see cref="Bar"/> /// </param> /// <param name="pos"> /// The ordinal position of the this bar series (0=first bar, 1=second bar, etc.) in the cluster of bars. /// </param> /// <param name="index"> /// The zero-based index number for the single bar to be drawn. /// </param> /// <param name="barWidth"> /// The width of each bar, in pixels. /// </param> /// <param name="scaleFactor"> /// The scaling factor to be used for rendering objects. This is calculated and passed down by the parent <see cref="GraphPane"/> object using the /// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust font sizes, etc. according to the actual size of the graph. /// </param> public void DrawSingleBar( Graphics g, GraphPane pane, CurveItem curve, Axis baseAxis, Axis valueAxis, int pos, int index, float barWidth, float scaleFactor) { // Make sure that a bar value exists for the current curve and current ordinal position if (index >= curve.Points.Count) { return; } // For Overlay and Stack bars, the position is always zero since the bars are on top // of eachother if (pane._barSettings.Type == BarType.Overlay || pane._barSettings.Type == BarType.Stack || pane._barSettings.Type == BarType.PercentStack) { pos = 0; } // Draw the specified bar this.DrawSingleBar(g, pane, curve, index, pos, baseAxis, valueAxis, barWidth, scaleFactor); }
/// <summary> /// This method just handles the case where one or more of the coordinates are outrageous, or GDI+ threw an exception. This method attempts to correct /// the outrageous coordinates by interpolating them to a point (along the original line) that lies at the edge of the ChartRect so that GDI+ will handle /// it properly. GDI+ will throw an exception, or just plot the data incorrectly if the coordinates are too large (empirically, this appears to be when /// the coordinate value is greater than 5,000,000 or less than -5,000,000). Although you typically would not see coordinates like this, if you /// repeatedly zoom in on a ZedGraphControl, eventually all your points will be way outside the bounds of the plot. /// </summary> /// <param name="g"> /// The g. /// </param> /// <param name="pane"> /// The pane. /// </param> /// <param name="curve"> /// The curve. /// </param> /// <param name="lastPt"> /// The last Pt. /// </param> /// <param name="scaleFactor"> /// The scale Factor. /// </param> /// <param name="pen"> /// The pen. /// </param> /// <param name="lastX"> /// The last X. /// </param> /// <param name="lastY"> /// The last Y. /// </param> /// <param name="tmpX"> /// The tmp X. /// </param> /// <param name="tmpY"> /// The tmp Y. /// </param> private void InterpolatePoint( Graphics g, GraphPane pane, CurveItem curve, PointPair lastPt, float scaleFactor, Pen pen, float lastX, float lastY, float tmpX, float tmpY) { try { RectangleF chartRect = pane.Chart._rect; // try to interpolate values bool lastIn = chartRect.Contains(lastX, lastY); bool curIn = chartRect.Contains(tmpX, tmpY); // If both points are outside the ChartRect, make a new point that is on the LastX/Y // side of the ChartRect, and fall through to the code that handles lastIn == true if (!lastIn) { float newX, newY; if (Math.Abs(lastX) > Math.Abs(lastY)) { newX = lastX < 0 ? chartRect.Left : chartRect.Right; newY = lastY + (tmpY - lastY) * (newX - lastX) / (tmpX - lastX); } else { newY = lastY < 0 ? chartRect.Top : chartRect.Bottom; newX = lastX + (tmpX - lastX) * (newY - lastY) / (tmpY - lastY); } lastX = newX; lastY = newY; } if (!curIn) { float newX, newY; if (Math.Abs(tmpX) > Math.Abs(tmpY)) { newX = tmpX < 0 ? chartRect.Left : chartRect.Right; newY = tmpY + (lastY - tmpY) * (newX - tmpX) / (lastX - tmpX); } else { newY = tmpY < 0 ? chartRect.Top : chartRect.Bottom; newX = tmpX + (lastX - tmpX) * (newY - tmpY) / (lastY - tmpY); } tmpX = newX; tmpY = newY; } /* if ( this.StepType == StepType.ForwardStep ) { g.DrawLine( pen, lastX, lastY, tmpX, lastY ); g.DrawLine( pen, tmpX, lastY, tmpX, tmpY ); } else if ( this.StepType == StepType.RearwardStep ) { g.DrawLine( pen, lastX, lastY, lastX, tmpY ); g.DrawLine( pen, lastX, tmpY, tmpX, tmpY ); } else // non-step g.DrawLine( pen, lastX, lastY, tmpX, tmpY ); */ if (!curve.IsSelected && this._gradientFill.IsGradientValueType) { using (Pen tPen = this.GetPen(pane, scaleFactor, lastPt)) { if (this.StepType == StepType.NonStep) { g.DrawLine(tPen, lastX, lastY, tmpX, tmpY); } else if (this.StepType == StepType.ForwardStep) { g.DrawLine(tPen, lastX, lastY, tmpX, lastY); g.DrawLine(tPen, tmpX, lastY, tmpX, tmpY); } else if (this.StepType == StepType.RearwardStep) { g.DrawLine(tPen, lastX, lastY, lastX, tmpY); g.DrawLine(tPen, lastX, tmpY, tmpX, tmpY); } else if (this.StepType == StepType.ForwardSegment) { g.DrawLine(tPen, lastX, lastY, tmpX, lastY); } else { g.DrawLine(tPen, lastX, tmpY, tmpX, tmpY); } } } else { if (this.StepType == StepType.NonStep) { g.DrawLine(pen, lastX, lastY, tmpX, tmpY); } else if (this.StepType == StepType.ForwardStep) { g.DrawLine(pen, lastX, lastY, tmpX, lastY); g.DrawLine(pen, tmpX, lastY, tmpX, tmpY); } else if (this.StepType == StepType.RearwardStep) { g.DrawLine(pen, lastX, lastY, lastX, tmpY); g.DrawLine(pen, lastX, tmpY, tmpX, tmpY); } else if (this.StepType == StepType.ForwardSegment) { g.DrawLine(pen, lastX, lastY, tmpX, lastY); } else if (this.StepType == StepType.RearwardSegment) { g.DrawLine(pen, lastX, tmpY, tmpX, tmpY); } } } catch { } }
/// <summary> /// Protected internal routine that draws the specified single bar (an individual "point") of this series to the specified <see cref="Graphics"/> /// device. /// </summary> /// <param name="g"> /// A graphic device object to be drawn into. This is normally e.Graphics from the PaintEventArgs argument to the Paint() method. /// </param> /// <param name="pane"> /// A reference to the <see cref="GraphPane"/> object that is the parent or owner of this object. /// </param> /// <param name="curve"> /// A <see cref="CurveItem"/> object representing the /// <see cref="Bar"/>'s to be drawn. /// </param> /// <param name="index"> /// The zero-based index number for the single bar to be drawn. /// </param> /// <param name="pos"> /// The ordinal position of the this bar series (0=first bar, 1=second bar, etc.) in the cluster of bars. /// </param> /// <param name="baseAxis"> /// The <see cref="Axis"/> class instance that defines the base (independent) axis for the <see cref="Bar"/> /// </param> /// <param name="valueAxis"> /// The <see cref="Axis"/> class instance that defines the value (dependent) axis for the <see cref="Bar"/> /// </param> /// <param name="barWidth"> /// The width of each bar, in pixels. /// </param> /// <param name="scaleFactor"> /// The scaling factor to be used for rendering objects. This is calculated and passed down by the parent <see cref="GraphPane"/> object using the /// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust font sizes, etc. according to the actual size of the graph. /// </param> protected virtual void DrawSingleBar( Graphics g, GraphPane pane, CurveItem curve, int index, int pos, Axis baseAxis, Axis valueAxis, float barWidth, float scaleFactor) { // pixBase = pixel value for the bar center on the base axis // pixHiVal = pixel value for the bar top on the value axis // pixLowVal = pixel value for the bar bottom on the value axis float pixBase, pixHiVal, pixLowVal; float clusterWidth = pane.BarSettings.GetClusterWidth(); // float barWidth = curve.GetBarWidth( pane ); float clusterGap = pane._barSettings.MinClusterGap * barWidth; float barGap = barWidth * pane._barSettings.MinBarGap; // curBase = the scale value on the base axis of the current bar // curHiVal = the scale value on the value axis of the current bar // curLowVal = the scale value of the bottom of the bar double curBase, curLowVal, curHiVal; ValueHandler valueHandler = new ValueHandler(pane, false); valueHandler.GetValues(curve, index, out curBase, out curLowVal, out curHiVal); // Any value set to double max is invalid and should be skipped // This is used for calculated values that are out of range, divide // by zero, etc. // Also, any value <= zero on a log scale is invalid if (!curve.Points[index].IsInvalid) { // calculate a pixel value for the top of the bar on value axis pixLowVal = valueAxis.Scale.Transform(curve.IsOverrideOrdinal, index, curLowVal); pixHiVal = valueAxis.Scale.Transform(curve.IsOverrideOrdinal, index, curHiVal); // calculate a pixel value for the center of the bar on the base axis pixBase = baseAxis.Scale.Transform(curve.IsOverrideOrdinal, index, curBase); // Calculate the pixel location for the side of the bar (on the base axis) float pixSide = pixBase - clusterWidth / 2.0F + clusterGap / 2.0F + pos * (barWidth + barGap); // Draw the bar if (pane._barSettings.Base == BarBase.X) { this.Draw(g, pane, pixSide, pixSide + barWidth, pixLowVal, pixHiVal, scaleFactor, true, curve.IsSelected, curve.Points[index]); } else { this.Draw(g, pane, pixLowVal, pixHiVal, pixSide, pixSide + barWidth, scaleFactor, true, curve.IsSelected, curve.Points[index]); } } }
/// <summary> /// Build an array of <see cref="PointF"/> values (pixel coordinates) that represents the low values for the current curve. /// </summary> /// <remarks> /// Note that this drawing routine ignores <see cref="PointPairBase.Missing"/> /// values, but it does not "break" the line to indicate values are missing. /// </remarks> /// <param name="pane"> /// A reference to the <see cref="GraphPane"/> object that is the parent or owner of this object. /// </param> /// <param name="curve"> /// A <see cref="LineItem"/> representing this curve. /// </param> /// <param name="arrPoints"> /// An array of <see cref="PointF"/> values in pixel coordinates representing the current curve. /// </param> /// <param name="count"> /// The number of points contained in the "arrPoints" parameter. /// </param> /// <returns> /// true for a successful points array build, false for data problems /// </returns> public bool BuildLowPointsArray(GraphPane pane, CurveItem curve, out PointF[] arrPoints, out int count) { arrPoints = null; count = 0; IPointList points = curve.Points; if (this.IsVisible && !this.Color.IsEmpty && points != null) { int index = 0; float curX, curY, lastX = 0, lastY = 0; double x, y, hiVal; ValueHandler valueHandler = new ValueHandler(pane, false); // Step type plots get twice as many points. Always add three points so there is // room to close out the curve for area fills. arrPoints = new PointF[(this._stepType == StepType.NonStep ? 1 : 2) * (pane.LineType == LineType.Stack ? 2 : 1) * points.Count + 1]; // Loop backwards over all points in the curve // In this case an array of points was already built forward by BuildPointsArray(). // This time we build backwards to complete a loop around the area between two curves. for (int i = points.Count - 1; i >= 0; i--) { // Make sure the current point is valid if (!points[i].IsInvalid) { // Get the user scale values for the current point valueHandler.GetValues(curve, i, out x, out y, out hiVal); if (x == PointPairBase.Missing || y == PointPairBase.Missing) { continue; } // Transform the user scale values to pixel locations Axis xAxis = curve.GetXAxis(pane); curX = xAxis.Scale.Transform(curve.IsOverrideOrdinal, i, x); Axis yAxis = curve.GetYAxis(pane); curY = yAxis.Scale.Transform(curve.IsOverrideOrdinal, i, y); // Add the pixel value pair into the points array // Two points are added for step type curves // ignore step-type setting for smooth curves if (this._isSmooth || index == 0 || this.StepType == StepType.NonStep) { arrPoints[index].X = curX; arrPoints[index].Y = curY; } else if (this.StepType == StepType.ForwardStep) { arrPoints[index].X = curX; arrPoints[index].Y = lastY; index++; arrPoints[index].X = curX; arrPoints[index].Y = curY; } else if (this.StepType == StepType.RearwardStep) { arrPoints[index].X = lastX; arrPoints[index].Y = curY; index++; arrPoints[index].X = curX; arrPoints[index].Y = curY; } lastX = curX; lastY = curY; index++; } } // Make sure there is at least one valid point if (index == 0) { return false; } // Add an extra point at the end, since the smoothing algorithm requires it arrPoints[index] = arrPoints[index - 1]; index++; count = index; return true; } return false; }
/// <summary> /// Create a URL for a <see cref="CurveItem"/> that includes the index of the point that was selected. /// </summary> /// <remarks> /// An "index" parameter is added to the <see cref="Url"/> property for this link to indicate which point was selected. Further, if the X or Y axes /// that correspond to this <see cref="CurveItem"/> are of /// <see cref="AxisType.Text"/>, then an additional parameter will be added containing the text value that corresponds to the <paramref name="index"/> /// of the selected point. The <see cref="XAxis"/> text parameter will be labeled "xtext", and the <see cref="YAxis"/> text parameter will be labeled /// "ytext". /// </remarks> /// <param name="pane"> /// The <see cref="GraphPane"/> of interest /// </param> /// <param name="curve"> /// The <see cref="CurveItem"/> for which to make the url string. /// </param> /// <param name="index"> /// The zero-based index of the selected point /// </param> /// <returns> /// A string containing the url with an index parameter added. /// </returns> public virtual string MakeCurveItemUrl(GraphPane pane, CurveItem curve, int index) { string url = this._url; if (url.IndexOf('?') >= 0) { url += "&index=" + index; } else { url += "?index=" + index; } Axis xAxis = curve.GetXAxis(pane); if (xAxis.Type == AxisType.Text && index >= 0 && xAxis.Scale.TextLabels != null && index <= xAxis.Scale.TextLabels.Length) { url += "&xtext=" + xAxis.Scale.TextLabels[index]; } Axis yAxis = curve.GetYAxis(pane); if (yAxis != null && yAxis.Type == AxisType.Text && index >= 0 && yAxis.Scale.TextLabels != null && index <= yAxis.Scale.TextLabels.Length) { url += "&ytext=" + yAxis.Scale.TextLabels[index]; } return url; }
/// <summary> /// Close off a <see cref="GraphicsPath"/> that defines a curve /// </summary> /// <param name="pane"> /// A reference to the <see cref="GraphPane"/> object that is the parent or owner of this object. /// </param> /// <param name="curve"> /// A <see cref="LineItem"/> representing this curve. /// </param> /// <param name="arrPoints"> /// An array of <see cref="PointF"/> values in screen pixel coordinates representing the current curve. /// </param> /// <param name="count"> /// The number of points contained in the "arrPoints" parameter. /// </param> /// <param name="yMin"> /// The Y axis value location where the X axis crosses. /// </param> /// <param name="path"> /// The <see cref="GraphicsPath"/> class that represents the curve. /// </param> public void CloseCurve(GraphPane pane, CurveItem curve, PointF[] arrPoints, int count, double yMin, GraphicsPath path) { // For non-stacked lines, the fill area is just the area between the curve and the X axis if (pane.LineType != LineType.Stack) { // Determine the current value for the bottom of the curve (usually the Y value where // the X axis crosses) float yBase; Axis yAxis = curve.GetYAxis(pane); yBase = yAxis.Scale.Transform(yMin); // Add three points to the path to move from the end of the curve (as defined by // arrPoints) to the X axis, from there to the start of the curve at the X axis, // and from there back up to the beginning of the curve. path.AddLine(arrPoints[count - 1].X, arrPoints[count - 1].Y, arrPoints[count - 1].X, yBase); path.AddLine(arrPoints[count - 1].X, yBase, arrPoints[0].X, yBase); path.AddLine(arrPoints[0].X, yBase, arrPoints[0].X, arrPoints[0].Y); } // For stacked line types, the fill area is the area between this curve and the curve below it else { PointF[] arrPoints2; int count2; float tension = this._isSmooth ? this._smoothTension : 0f; // Find the next lower curve in the curveList that is also a LineItem type, and use // its smoothing properties for the lower side of the filled area. int index = pane.CurveList.IndexOf(curve); if (index > 0) { CurveItem tmpCurve; for (int i = index - 1; i >= 0; i--) { tmpCurve = pane.CurveList[i]; if (tmpCurve is LineItem) { tension = ((LineItem)tmpCurve).Line.IsSmooth ? ((LineItem)tmpCurve).Line.SmoothTension : 0f; break; } } } // Build another points array consisting of the low points (which are actually the points for // the curve below the current curve) this.BuildLowPointsArray(pane, curve, out arrPoints2, out count2); // Add the new points to the GraphicsPath path.AddCurve(arrPoints2, 0, count2 - 2, tension); } }
/// <summary> /// Find the data point that lies closest to the specified mouse (screen) point for the specified curve. /// </summary> /// <remarks> /// This method will search only through the points for the specified curve to determine which point is nearest the mouse point. It will only consider /// points that are within /// <see cref="Default.NearestTol"/> pixels of the screen point. /// </remarks> /// <param name="mousePt"> /// The screen point, in pixel coordinates. /// </param> /// <param name="targetCurve"> /// A <see cref="CurveItem"/> object containing the data points to be searched. /// </param> /// <param name="nearestCurve"> /// A reference to the <see cref="CurveItem"/> /// instance that contains the closest point. nearestCurve will be null if no data points are available. /// </param> /// <param name="iNearest"> /// The index number of the closest point. The actual data vpoint will then be <see cref="CurveItem.Points">CurveItem.Points[iNearest]</see> /// . iNearest will be -1 if no data points are available. /// </param> /// <returns> /// true if a point was found and that point lies within /// <see cref="Default.NearestTol"/> pixels of the screen point, false otherwise. /// </returns> public bool FindNearestPoint(PointF mousePt, CurveItem targetCurve, out CurveItem nearestCurve, out int iNearest) { CurveList targetCurveList = new CurveList(); targetCurveList.Add(targetCurve); return this.FindNearestPoint(mousePt, targetCurveList, out nearestCurve, out iNearest); }
/// <summary> /// Draw the this <see cref="CurveItem"/> to the specified <see cref="Graphics"/> /// device. The format (stair-step or line) of the curve is defined by the <see cref="StepType"/> property. The routine only draws the line segments; /// the symbols are drawn by the /// <see cref="Symbol.Draw"/> method. This method is normally only called by the Draw method of the /// <see cref="CurveItem"/> object /// </summary> /// <param name="g"> /// A graphic device object to be drawn into. This is normally e.Graphics from the PaintEventArgs argument to the Paint() method. /// </param> /// <param name="pane"> /// A reference to the <see cref="GraphPane"/> object that is the parent or owner of this object. /// </param> /// <param name="curve"> /// A <see cref="LineItem"/> representing this curve. /// </param> /// <param name="scaleFactor"> /// The scaling factor to be used for rendering objects. This is calculated and passed down by the parent <see cref="GraphPane"/> object using the /// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust font sizes, etc. according to the actual size of the graph. /// </param> public void DrawCurveOriginal(Graphics g, GraphPane pane, CurveItem curve, float scaleFactor) { Line source = this; if (curve.IsSelected) { source = Selection.Line; } float tmpX, tmpY, lastX = float.MaxValue, lastY = float.MaxValue; double curX, curY, lowVal; PointPair curPt, lastPt = new PointPair(); bool lastBad = true; IPointList points = curve.Points; ValueHandler valueHandler = new ValueHandler(pane, false); Axis yAxis = curve.GetYAxis(pane); Axis xAxis = curve.GetXAxis(pane); bool xIsLog = xAxis._scale.IsLog; bool yIsLog = yAxis._scale.IsLog; float minX = pane.Chart.Rect.Left; float maxX = pane.Chart.Rect.Right; float minY = pane.Chart.Rect.Top; float maxY = pane.Chart.Rect.Bottom; using (Pen pen = source.GetPen(pane, scaleFactor)) { if (points != null && !this._color.IsEmpty && this.IsVisible) { // bool lastOut = false; bool isOut; // Loop over each point in the curve for (int i = 0; i < points.Count; i++) { curPt = points[i]; if (pane.LineType == LineType.Stack) { if (!valueHandler.GetValues(curve, i, out curX, out lowVal, out curY)) { curX = PointPairBase.Missing; curY = PointPairBase.Missing; } } else { curX = curPt.X; curY = curPt.Y; } // Any value set to double max is invalid and should be skipped // This is used for calculated values that are out of range, divide // by zero, etc. // Also, any value <= zero on a log scale is invalid if (curX == PointPairBase.Missing || curY == PointPairBase.Missing || double.IsNaN(curX) || double.IsNaN(curY) || double.IsInfinity(curX) || double.IsInfinity(curY) || (xIsLog && curX <= 0.0) || (yIsLog && curY <= 0.0)) { // If the point is invalid, then make a linebreak only if IsIgnoreMissing is false // LastX and LastY are always the last valid point, so this works out lastBad = lastBad || !pane.IsIgnoreMissing; isOut = true; } else { // Transform the current point from user scale units to // screen coordinates tmpX = xAxis.Scale.Transform(curve.IsOverrideOrdinal, i, curX); tmpY = yAxis.Scale.Transform(curve.IsOverrideOrdinal, i, curY); isOut = (tmpX < minX && lastX < minX) || (tmpX > maxX && lastX > maxX) || (tmpY < minY && lastY < minY) || (tmpY > maxY && lastY > maxY); if (!lastBad) { try { // GDI+ plots the data wrong and/or throws an exception for // outrageous coordinates, so we do a sanity check here if (lastX > 5000000 || lastX < -5000000 || lastY > 5000000 || lastY < -5000000 || tmpX > 5000000 || tmpX < -5000000 || tmpY > 5000000 || tmpY < -5000000) { this.InterpolatePoint(g, pane, curve, lastPt, scaleFactor, pen, lastX, lastY, tmpX, tmpY); } else if (!isOut) { if (!curve.IsSelected && this._gradientFill.IsGradientValueType) { using (Pen tPen = this.GetPen(pane, scaleFactor, lastPt)) { if (this.StepType == StepType.NonStep) { g.DrawLine(tPen, lastX, lastY, tmpX, tmpY); } else if (this.StepType == StepType.ForwardStep) { g.DrawLine(tPen, lastX, lastY, tmpX, lastY); g.DrawLine(tPen, tmpX, lastY, tmpX, tmpY); } else if (this.StepType == StepType.RearwardStep) { g.DrawLine(tPen, lastX, lastY, lastX, tmpY); g.DrawLine(tPen, lastX, tmpY, tmpX, tmpY); } else if (this.StepType == StepType.ForwardSegment) { g.DrawLine(tPen, lastX, lastY, tmpX, lastY); } else { g.DrawLine(tPen, lastX, tmpY, tmpX, tmpY); } } } else { if (this.StepType == StepType.NonStep) { g.DrawLine(pen, lastX, lastY, tmpX, tmpY); } else if (this.StepType == StepType.ForwardStep) { g.DrawLine(pen, lastX, lastY, tmpX, lastY); g.DrawLine(pen, tmpX, lastY, tmpX, tmpY); } else if (this.StepType == StepType.RearwardStep) { g.DrawLine(pen, lastX, lastY, lastX, tmpY); g.DrawLine(pen, lastX, tmpY, tmpX, tmpY); } else if (this.StepType == StepType.ForwardSegment) { g.DrawLine(pen, lastX, lastY, tmpX, lastY); } else if (this.StepType == StepType.RearwardSegment) { g.DrawLine(pen, lastX, tmpY, tmpX, tmpY); } } } } catch { this.InterpolatePoint(g, pane, curve, lastPt, scaleFactor, pen, lastX, lastY, tmpX, tmpY); } } lastPt = curPt; lastX = tmpX; lastY = tmpY; lastBad = false; // lastOut = isOut; } } } } }
/// <summary> /// The graph_ point value event. /// </summary> /// <param name="sender"> /// The sender. /// </param> /// <param name="pane"> /// The pane. /// </param> /// <param name="curve"> /// The curve. /// </param> /// <param name="iPt"> /// The i pt. /// </param> /// <returns> /// The <see cref="string"/>. /// </returns> private string graph_PointValueEvent(ZedGraphControl sender, GraphPane pane, CurveItem curve, int iPt) { PointPair point = curve[iPt]; var flight = point.Tag as Flight; if (flight != null) { return flight.SummaryString; } return string.Empty; }