private void MenuRemovePoint(object sender, RoutedEventArgs e)
 {
     RemovePoint(selectedLinePoint);
     selectedLinePoint = null;
     selectedPoint     = null;
     UpdateRawPoints();
 }
        private void GraphControl_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.Key)
            {
            case Key.LeftShift:
                currentMode = ManipulateMode.Vertical;
                Cursor      = Cursors.SizeNS;
                break;

            case Key.LeftCtrl:
                currentMode = ManipulateMode.Horizontal;
                Cursor      = Cursors.SizeWE;
                break;

            case Key.Delete:
            {
                if (selectedLinePoint != null)
                {
                    RemovePoint(selectedLinePoint);
                    selectedLinePoint = null;
                }
            }
            break;
            }
        }
        private void MainWindow_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            CustomPoint point = GetPointAt(e.GetPosition(this));

            if (point == null)
            {
                AddPoint(e.GetPosition(this));
            }
        }
        CustomPoint GetClosestLinePoint(CustomPoint controlPoint)
        {
            int i = points.IndexOf(controlPoint);

            if (points[i - 1].isLinePoint)
            {
                return(points[i - 1]);
            }
            else
            {
                return(points[i + 1]);
            }
        }
        List <CustomPoint> GetControlPoints(CustomPoint linePoint)
        {
            int i = points.IndexOf(linePoint);
            List <CustomPoint> cpPoints = new List <CustomPoint>();

            if (i > 0)
            {
                cpPoints.Add(points[i - 1]);
            }
            if (i < points.Count - 1)
            {
                cpPoints.Add(points[i + 1]);
            }

            return(cpPoints);
        }
        CustomPoint GetMirrorControlPoint(CustomPoint controlPoint)
        {
            int i = points.IndexOf(controlPoint);

            if (points[i + 1].isLinePoint && points.Count > i + 2)
            {
                return(points[i + 2]);
            }
            else if (i > 1 && !points[i - 2].isLinePoint)
            {
                return(points[i - 2]);
            }
            else
            {
                return(null);
            }
        }
        void SelectPointAt(Point position)
        {
            CustomPoint point = GetPointAt(position);


            foreach (CustomPoint p in points)
            {
                if (!p.isLinePoint)
                {
                    p.visible = false;
                }
            }
            selectedPoint     = null;
            selectedLinePoint = null;

            if (point != null)
            {
                int i = points.IndexOf(point);
                if (point.isLinePoint)
                {
                    if (points.Count > i + 1)
                    {
                        points[i + 1].visible = true;
                    }

                    if (i > 1)
                    {
                        points[i - 1].visible = true;
                    }

                    selectedLinePoint = point;
                }
                else
                {
                    point.visible = true;
                    CustomPoint mirror = GetMirrorControlPoint(point);
                    if (mirror != null)
                    {
                        mirror.visible = true;
                    }

                    selectedLinePoint = GetClosestLinePoint(point);
                }
                selectedPoint = point;
            }
        }
        CustomPoint GetLinePointToTheRight(CustomPoint controlPoint)
        {
            int i = points.IndexOf(controlPoint);

            i++;
            while (i < points.Count)
            {
                if (points[i].isLinePoint)
                {
                    return(points[i]);
                }
                else
                {
                    i++;
                }
            }
            return(null);
        }
        CustomPoint GetLinePointToTheLeft(CustomPoint controlPoint)
        {
            int i = points.IndexOf(controlPoint);

            i--;
            while (i >= 0)
            {
                if (points[i].isLinePoint)
                {
                    return(points[i]);
                }
                else
                {
                    i--;
                }
            }
            return(null);
        }
 public void AddPoints(List <Point> newPoints)
 {
     ClearList();
     for (int i = 0; i < newPoints.Count; ++i)
     {
         Point       point = newPoints[i];
         CustomPoint cp    = new CustomPoint(point.X * gridSpacing, point.Y * gridSpacing);
         if (points.Count == 0)
         {
             points.Add(cp);
         }
         else
         {
             points.Add(cp);
             rawPoints.Add(cp.p);
         }
         if (i % 3 == 0)
         {
             cp.visible     = true;
             cp.isLinePoint = true;
         }
     }
 }
        void RemovePoint(CustomPoint point)
        {
            int  pointIndex = points.IndexOf(point);
            bool lastPoint  = pointIndex == points.Count - 1;

            foreach (CustomPoint cp in GetControlPoints(point))
            {
                points.Remove(cp);
            }
            points.Remove(point);

            if (pointIndex == 0 && points.Count > 0)
            {
                points.RemoveAt(0);
            }
            else if (lastPoint && points.Count > 0)
            {
                points.RemoveAt(points.Count - 1);
            }

            UpdateRawPoints();
            InvalidateVisual();
        }
        private void MainWindow_MouseMove(object sender, MouseEventArgs e)
        {
            if (currentMode == ManipulateMode.All)
            {
                Cursor = Cursors.Arrow;
            }
            CustomPoint over = GetPointAt(e.GetPosition(this));

            if (over != null)
            {
                Cursor = Cursors.SizeAll;
            }

            if (e.LeftButton == MouseButtonState.Pressed)
            {
                if (selectedPoint != null)
                {
                    Cursor = Cursors.SizeAll;
                    HandleMovement(e.GetPosition(this));
                    InvalidateVisual();
                    OnPointsChanged?.Invoke();

                    UpdateRawPoints();
                }
                else
                {
                    double deltaX = e.GetPosition(this).X - prevMouseX;
                    double deltaY = e.GetPosition(this).Y - prevMouseY;
                    Cursor = Cursors.Hand;
                    translateTransform.X += deltaX;
                    translateTransform.Y -= deltaY;
                    InvalidateVisual();
                }
            }
            prevMouseX = e.GetPosition(this).X;
            prevMouseY = e.GetPosition(this).Y;
        }
        private void HandleMovement(Point targetPoint)
        {
            Point validPos      = GetValidPosition(selectedPoint, targetPoint);
            Point deltaMovement = new Point(selectedPoint.p.X - validPos.X, selectedPoint.p.Y - validPos.Y);

            if (selectedPoint == selectedLinePoint)
            {
                foreach (CustomPoint cp in GetControlPoints(selectedLinePoint))
                {
                    Point newPos = new Point(cp.p.X - deltaMovement.X, cp.p.Y - deltaMovement.Y);
                    cp.MoveTo(GetValidPosition(cp, newPos));
                }
                validPos = GetValidPosition(selectedPoint, targetPoint);
                selectedPoint.MoveTo(validPos);
            }
            else //is a control point
            {
                selectedPoint.MoveTo(validPos);
                CustomPoint mirror = GetMirrorControlPoint(selectedPoint);
                if (mirror != null && !selectedLinePoint.unlockedControlPoints)
                {
                    Point cpDelta = new Point(selectedPoint.p.X - selectedLinePoint.p.X, selectedPoint.p.Y - selectedLinePoint.p.Y);
                    Point newPos  = new Point(selectedLinePoint.p.X - cpDelta.X, selectedLinePoint.p.Y - cpDelta.Y);
                    mirror.MoveTo(GetValidPosition(mirror, newPos));

                    int i = points.IndexOf(selectedPoint) % 3;

                    if ((i == 1 && selectedPoint.X <= mirror.X) || (i == 2 && selectedPoint.X >= mirror.X))
                    {
                        int a = points.IndexOf(selectedPoint);
                        int b = points.IndexOf(mirror);
                        points[a] = mirror;
                        points[b] = selectedPoint;
                    }
                }
            }
        }
 public void OnLostFocus()
 {
     selectedLinePoint = null;
     selectedPoint     = null;
     InvalidateVisual();
 }
        protected override void OnRender(DrawingContext drawingContext)
        {
            if (points.Count > 0)
            {
                start.StartPoint = points[0].p;
            }



            int dynamicGridSpacingX = (int)(gridSpacing / scaleTransform.ScaleX);
            int dynamicGridSpacingY = (int)(gridSpacing / scaleTransform.ScaleY);

            double width  = RenderSize.Width != 0 ? RenderSize.Width : 1000;
            double height = RenderSize.Height != 0 ? RenderSize.Height : 1000;

            int startX = (int)(Math.Round((-translateTransform.X) / dynamicGridSpacingX) * dynamicGridSpacingX) - dynamicGridSpacingX;
            int endX   = (int)(Math.Round(((-translateTransform.X + width)) / dynamicGridSpacingX) * dynamicGridSpacingX) + dynamicGridSpacingX;

            int startY = (int)(Math.Round((-translateTransform.Y) / dynamicGridSpacingY) * dynamicGridSpacingY) - dynamicGridSpacingY;
            int endY   = (int)(Math.Round(((-translateTransform.Y + height)) / dynamicGridSpacingY) * dynamicGridSpacingY) + dynamicGridSpacingY;

            drawingContext.PushTransform(new ScaleTransform(1, -1));


            drawingContext.DrawRectangle(Brushes.Gray, null,
                                         new Rect(startX - gridSpacing, startY - gridSpacing,
                                                  Math.Abs(endX - startX) + gridSpacing, Math.Abs(endY - startY) + gridSpacing));
            //grid

            double largestScale = (scaleTransform.ScaleX + scaleTransform.ScaleY) / 2;


            for (int x = startX; x < endX; x += dynamicGridSpacingX) // x-axis
            {
                drawingContext.DrawLine(new Pen(Brushes.DimGray, 1.0 / scaleTransform.ScaleX), new Point(x, startY), new Point(x, endY));
                drawingContext.PushTransform(new ScaleTransform(scaleTransform.ScaleY, scaleTransform.ScaleX));
                if (x != 0)
                {
                    FormattedText ft = new FormattedText((x / (float)gridSpacing).ToString(),
                                                         CultureInfo.GetCultureInfo("en-us"),
                                                         FlowDirection.LeftToRight,
                                                         new Typeface("Verdana"),
                                                         12 / Math.Pow(largestScale, 2), System.Windows.Media.Brushes.Black);
                    ft.TextAlignment = TextAlignment.Center;
                    drawingContext.DrawText(ft, new System.Windows.Point(x / scaleTransform.ScaleY, 1.0 / scaleTransform.ScaleX));
                }
                drawingContext.PushTransform(new ScaleTransform(1 / scaleTransform.ScaleY, 1 / scaleTransform.ScaleX));
            }

            for (int y = startY; y < endY; y += (int)(gridSpacing / scaleTransform.ScaleY)) //y-axis
            {
                drawingContext.DrawLine(new Pen(Brushes.DimGray, 1.0 / scaleTransform.ScaleY), new Point(startX, y), new Point(endX, y));
                drawingContext.PushTransform(new ScaleTransform(scaleTransform.ScaleY, scaleTransform.ScaleX));
                if (y != 0)
                {
                    FormattedText ft = new FormattedText((-y / (float)gridSpacing).ToString(),
                                                         CultureInfo.GetCultureInfo("en-us"),
                                                         FlowDirection.LeftToRight,
                                                         new Typeface("Verdana"),
                                                         12 / Math.Pow(largestScale, 2), System.Windows.Media.Brushes.Black);
                    ft.TextAlignment = TextAlignment.Right;
                    drawingContext.DrawText(ft, new System.Windows.Point(-1.0 / scaleTransform.ScaleY, (y / scaleTransform.ScaleX)));
                }
                //else
                //{
                //    FormattedText ft = new FormattedText("0",
                //         CultureInfo.GetCultureInfo("en-us"),
                //         FlowDirection.LeftToRight,
                //         new Typeface("Verdana"),
                //         1.0/ avgScale, System.Windows.Media.Brushes.Black);
                //    ft.TextAlignment = TextAlignment.Right;
                //    drawingContext.DrawText(ft, new System.Windows.Point(-1.0/ scaleTransform.ScaleY, 2.0/ scaleTransform.ScaleX));
                //}
                drawingContext.PushTransform(new ScaleTransform(1 / scaleTransform.ScaleY, 1 / scaleTransform.ScaleX));
            }
            //  drawingContext.PushTransform(new ScaleTransform(1/scaleTransform.ScaleY, 1/scaleTransform.ScaleX));

            //axis
            drawingContext.DrawLine(new Pen(Brushes.Black, 1.0 / largestScale), new Point(startX, 0), new Point(endX, 0));
            drawingContext.DrawLine(new Pen(Brushes.Black, 1.0 / largestScale), new Point(0, startY), new Point(0, endY));

            //unselected points

            drawingContext.PushTransform(new ScaleTransform(1, -1));
            foreach (CustomPoint point in points)
            {
                if (point.isLinePoint && point != selectedLinePoint)
                {
                    drawingContext.DrawRectangle(Brushes.Red, null, point.GetOffsetRect(scaleTransform));
                }
            }


            //selected point
            if (selectedLinePoint != null)
            {
                CustomPoint point = selectedLinePoint;
                drawingContext.DrawRectangle(Brushes.Green, null, point.GetOffsetRect(scaleTransform));
                foreach (CustomPoint cpPoint in GetControlPoints(selectedLinePoint))
                {
                    point = cpPoint;
                    Rect offsetRect = point.GetOffsetRect(scaleTransform);
                    drawingContext.DrawEllipse(Brushes.Green, null, point.p, offsetRect.Width / 2, offsetRect.Height / 2);
                    drawingContext.DrawLine(new Pen(Brushes.Teal, 2 / scaleTransform.ScaleX), selectedLinePoint.p, cpPoint.p);
                }
            }
            base.OnRender(drawingContext);
        }
 private void GraphControl_MouseUp(object sender, MouseButtonEventArgs e)
 {
     selectedPoint = null;
 }
        Point GetValidPosition(CustomPoint targetPoint, Point TargetPos)
        {
            int   i          = points.IndexOf(targetPoint);
            float mouseX     = (float)TargetPos.X;
            bool  movingLeft = targetPoint.p.X > mouseX;

            if (!targetPoint.isLinePoint) //control point
            {
                if (movingLeft)
                {
                    CustomPoint leftPoint = GetLinePointToTheLeft(targetPoint);
                    if (leftPoint.p.X > mouseX)
                    {
                        TargetPos.X = leftPoint.X;
                    }
                }
                else
                {
                    CustomPoint rightPoint = GetLinePointToTheRight(targetPoint);
                    if (rightPoint.p.X < mouseX)
                    {
                        TargetPos.X = rightPoint.X;
                    }
                }
            }
            else
            {
                if (movingLeft && i != 0)
                {
                    CustomPoint leftCP = points[i - 1];
                    if (leftCP.p.X > mouseX)
                    {
                        TargetPos.X = leftCP.X;
                    }

                    if (i - 2 > 0)
                    {
                        CustomPoint leftLineRCP = points[i - 2];
                        if (leftLineRCP.p.X > mouseX)
                        {
                            TargetPos.X = leftLineRCP.X;
                        }
                    }
                }
                else if (i < points.Count - 1)
                {
                    CustomPoint rightCP = points[i + 1];
                    if (rightCP.p.X < mouseX)
                    {
                        TargetPos.X = rightCP.X;
                    }

                    if (i + 2 < points.Count)
                    {
                        CustomPoint RightLineLCP = points[i + 2];
                        if (RightLineLCP.p.X < mouseX)
                        {
                            TargetPos.X = RightLineLCP.X;
                        }
                    }
                }
            }
            return(TargetPos);
        }
        private void AddPoint(Point pos)
        {
            CustomPoint linePoint = new CustomPoint(pos.X, pos.Y);

            if (points.Count == 0)
            {
                points.Add(linePoint);
            }
            else
            {
                CustomPoint prevPoint = null;
                int         i;
                for (i = 0; i < points.Count; i += 3)
                {
                    if (linePoint.X < points[i].X)
                    {
                        break;
                    }
                    prevPoint = points[i];
                }

                if (prevPoint == null)
                {
                    prevPoint = points[0];
                    Vector      dir = new Vector(prevPoint.X - pos.X, prevPoint.Y - pos.Y);
                    CustomPoint cp1 = new CustomPoint(pos.X + dir.X * 0.45, pos.Y);
                    CustomPoint cp2 = new CustomPoint(prevPoint.X - dir.X * 0.45, prevPoint.Y);
                    points.Insert(0, cp2);
                    points.Insert(0, cp1);
                    points.Insert(0, linePoint);
                }
                else if (prevPoint != points[points.Count - 1])
                {
                    CustomPoint nextPoint = points[i];
                    Vector      dir1      = new Vector(prevPoint.X - pos.X, prevPoint.Y - pos.Y);
                    Vector      dir2      = new Vector(nextPoint.X - pos.X, nextPoint.Y - pos.Y);


                    Vector dir = dir1.Length > dir2.Length ? dir2 : dir1;

                    CustomPoint cp1 = new CustomPoint(pos.X - Math.Abs(dir.X) * 0.45, pos.Y);
                    CustomPoint cp2 = new CustomPoint(pos.X + Math.Abs(dir.X) * 0.45, pos.Y);

                    //Update previousPoint's right CP
                    Vector prevCPvec   = new Vector(points[i - 2].X - prevPoint.X, points[i - 2].Y - prevPoint.Y);
                    Point  tempCP      = new Point(points[i - 2].X, points[i - 2].Y);
                    Point  prevAsPoint = new Point(prevPoint.X, prevPoint.Y);
                    Vector tempUnit    = (prevCPvec / prevCPvec.Length);
                    double scalar      = (-dir1 * tempUnit);
                    tempCP        = prevAsPoint + tempUnit * scalar * 0.45;
                    points[i - 2] = new CustomPoint(tempCP.X, tempCP.Y);

                    //Update nextPoint's left CP
                    Vector nextCPvec = new Vector(points[i - 1].X - nextPoint.X, points[i - 1].Y - nextPoint.Y);
                    tempCP = new Point(points[i - 1].X, points[i - 1].Y);
                    Point nextAsPoint = new Point(nextPoint.X, nextPoint.Y);
                    tempUnit      = (prevCPvec / prevCPvec.Length);
                    scalar        = (-dir2 * tempUnit);
                    tempCP        = nextAsPoint + tempUnit * scalar * 0.45;
                    points[i - 1] = new CustomPoint(tempCP.X, tempCP.Y);

                    points.Insert(i - 1, cp2);
                    points.Insert(i - 1, linePoint);
                    points.Insert(i - 1, cp1);
                }
                else
                {
                    Vector      dir = new Vector(pos.X - prevPoint.X, pos.Y - prevPoint.Y);
                    CustomPoint cp1 = new CustomPoint(prevPoint.X + dir.X * 0.45, prevPoint.Y);
                    CustomPoint cp2 = new CustomPoint(pos.X - dir.X * 0.45, pos.Y);

                    points.Add(cp1);
                    points.Add(cp2);
                    points.Add(linePoint);
                }
            }

            UpdateRawPoints();
            linePoint.visible     = true;
            linePoint.isLinePoint = true;

            InvalidateVisual();
        }