using System; using System.Drawing; using System.Windows.Forms; using Core.Common.Controls.Charting.Series; using Core.Common.Controls.Charting.Properties; using log4net; using Steema.TeeChart; using Steema.TeeChart.Styles; using Steema.TeeChart.Tools; namespace Core.Common.Controls.Charting.Tools { /// /// Tool that enables interactive adding of points to a series. /// public class AddPointTool : ChartViewSeriesToolBase, IAddPointTool { public event EventHandler PointAdded; private static readonly ILog log = LogManager.GetLogger(typeof(AddPointTool)); private MouseButtons button = MouseButtons.Left; private bool dragging = false; /// /// Tool that enables interactive adding of points to a series. /// /// public AddPointTool(Steema.TeeChart.Chart c) : base(c) { Cursor = Cursors.Hand; //default } /// /// Tool that enables interactive adding of points to a series. /// public AddPointTool() : this(null) {} /// /// Gets descriptive text. /// public override string Description { get { return "AddPoint"; } } /// /// Gets detailed descriptive text. /// public override string Summary { get { return Texts.DragPointSummary; } } public bool Insert { get; set; } /// /// Sets which mousebutton activates DragPoint. /// public MouseButtons Button { get { return button; } set { button = value; } } /// /// Determines the type of DragPoint Cursor displayed. /// public Cursor Cursor { get; set; } public bool AddOnlyIfOnLine { get; set; } public ILineChartSeries Series { get { return (ILineChartSeries) base.Series; } set { base.Series = value; } } protected override void Assign(Tool t) { base.Assign(t); AddPointTool tmp = t as AddPointTool; tmp.Button = Button; tmp.Cursor = Cursor; //tmp.Style = Style; } protected override void OnMouseMove(MouseEventArgs e, ref Cursor c) { Point P = new Point(e.X, e.Y); if (!dragging) { Steema.TeeChart.Styles.Series s = ClickedSeries(P); if (s != null) { int abovePoint = s.Clicked(P); if (abovePoint > -1) { c = Cursor; //new Cursor(Assembly.GetExecutingAssembly().GetManifestResourceStream("Steema.TeeChart.Cursors.moveTracker.cur")); } Chart.CancelMouse = true; } else { c = Cursors.Default; } } } protected override void OnMouseUp(MouseEventArgs e) { dragging = false; } protected override void OnMouseDown(MouseEventArgs e) { Point p = new Point(e.X, e.Y); //button == Button.Left if (Steema.TeeChart.Utils.GetMouseButton(e) == button) { dragging = false; if (LastSelectedSeries == null) { foreach (Steema.TeeChart.Styles.Series s in Chart.Series) { if (s.Active) { dragging = (s.Clicked(p) != -1); if (dragging) { LastSelectedSeries = s; break; } } } } if (LastSelectedSeries != null) { { if (AddOnlyIfOnLine && !IsPointOnLine(new Point(e.X, e.Y))) { return; } //check if user clicked near enough to line. //calculate real x value from screen value double x = LastSelectedSeries.XScreenToValue(e.X); //interpolate y value double y = LastSelectedSeries.YScreenToValue(e.Y); //add/insert point to the series string oldOrderName = Enum.GetName(typeof(ValueListOrder), LastSelectedSeries.XValues.Order); LastSelectedSeries.XValues.Order = ValueListOrder.None; // .Ascending; if (x > Chart.Axes.Bottom.MinXValue && x < Chart.Axes.Bottom.MaxXValue && y > Chart.Axes.Left.MinYValue && y < Chart.Axes.Left.MaxYValue ) { // y = LinearInterPolation(theSeries.XValues, theSeries.YValues, x); if (Insert) { // do not use nearest point but try to calculate nearest line segment double minDistance = double.MaxValue; int minIndex = -1; for (int i = 1; i < LastSelectedSeries.XValues.Count; i++) { double dist = LinePointDistance(LastSelectedSeries.XValues[i - 1], LastSelectedSeries.YValues[i - 1], LastSelectedSeries.XValues[i], LastSelectedSeries.YValues[i], x, y); if (dist < minDistance) { minDistance = dist; minIndex = i; } } LastSelectedSeries.Add(x, y); if (PointAdded != null) { PointAdded(this, new PointEventArgs(GetChartSeriesFromInternalSeries(LastSelectedSeries), minIndex, x, y)); } } else { int index = LastSelectedSeries.Add(x, y); //notify listeners a point was inserted. if (PointAdded != null) { PointAdded(this, new PointEventArgs(GetChartSeriesFromInternalSeries(LastSelectedSeries), index, x, y)); } } } LastSelectedSeries.XValues.Order = (ValueListOrder) Enum.Parse(typeof(ValueListOrder), oldOrderName); } } } } private bool IsPointOnLine(Point clickedPoint) { Steema.TeeChart.Styles.Series s = ClickedSeries(clickedPoint); if (s != null) { int abovePoint = s.Clicked(clickedPoint); if (abovePoint > -1) { return true; } } return false; } // HACK private static double Distance(double x1, double y1, double X2, double Y2) { return Math.Sqrt((x1 - X2)*(x1 - X2) + (y1 - Y2)*(y1 - Y2)); } private static double CrossProduct(double Ax, double Ay, double Bx, double By, double cx, double cy) { return (Bx - Ax)*(cy - Ay) - (By - Ay)*(cx - Ax); } private static double Dot(double Ax, double Ay, double Bx, double By, double cx, double cy) { return (Bx - Ax)*(cx - Bx) + (By - Ay)*(cy - By); } private static double LinePointDistance(double Ax, double Ay, double Bx, double By, double cx, double cy) { var dist = Distance(Ax, Ay, Bx, By); if (dist < 0.000001) { return double.MaxValue; } dist = CrossProduct(Ax, Ay, Bx, By, cx, cy)/dist; // if (isSegment) always true var dot1 = Dot(Ax, Ay, Bx, By, cx, cy); if (dot1 > 0) { return Distance(Bx, By, cx, cy); } var dot2 = Dot(Bx, By, Ax, Ay, cx, cy); if (dot2 > 0) { return Distance(Ax, Ay, cx, cy); } return Math.Abs(dist); } } public interface IAddPointTool : IChartViewTool { event EventHandler PointAdded; MouseButtons Button { get; set; } Cursor Cursor { get; set; } bool Insert { get; set; } bool AddOnlyIfOnLine { get; set; } ILineChartSeries Series { get; set; } } }