public Handle(Anchor a, bool left) { anchor = a; Left = left; Slope = Left ? a.point.Value.ArriveTangent : a.point.Value.LeaveTangent; Line line = new Line(); line.bind(Line.X1Property, a, "X"); line.bind(Line.Y1Property, a, "Y", new YConverter(), a.graph.ActualHeight); line.bind(Line.X2Property, this, "X"); line.bind(Line.Y2Property, this, "Y", new YConverter(), a.graph.ActualHeight); line.bind(VisibilityProperty, this, "Visibility"); line.Style = a.graph.FindResource("HandleLine") as Style; a.graph.graph.Children.Add(line); this.DragDelta += OnDragDelta; double hScale = a.graph.HorizontalScale; double vScale = a.graph.VerticalScale; double xLength = (HANDLE_LENGTH * (Left ? -1 : 1)) / Math.Sqrt(Math.Pow(hScale, 2) + Math.Pow(Slope, 2) * Math.Pow(vScale, 2)); X = xLength * hScale + a.X; Y = Slope * xLength * vScale + a.Y; }
private void PathBetween(Anchor a1, Anchor a2, CurveMode interpMode = CurveMode.CIM_Linear) { Line line; BezierSegment bez; switch (interpMode) { case CurveMode.CIM_Linear: line = new Line(); line.bind(Line.X1Property, a1, "X"); line.bind(Line.Y1Property, a1, "Y", new YConverter(), ActualHeight); line.bind(Line.X2Property, a2, "X"); line.bind(Line.Y2Property, a2, "Y", new YConverter(), ActualHeight); graph.Children.Add(line); break; case CurveMode.CIM_Constant: line = new Line(); line.bind(Line.X1Property, a1, "X"); line.bind(Line.Y1Property, a1, "Y", new YConverter(), ActualHeight); line.bind(Line.X2Property, a2, "X"); line.bind(Line.Y2Property, a1, "Y", new YConverter(), ActualHeight); graph.Children.Add(line); line = new Line(); line.bind(Line.X1Property, a2, "X"); line.bind(Line.Y1Property, a1, "Y", new YConverter(), ActualHeight); line.bind(Line.X2Property, a2, "X"); line.bind(Line.Y2Property, a2, "Y", new YConverter(), ActualHeight); graph.Children.Add(line); break; case CurveMode.CIM_CurveAuto: case CurveMode.CIM_CurveUser: case CurveMode.CIM_CurveBreak: case CurveMode.CIM_CurveAutoClamped: bez = new BezierSegment(this); bez.Slope1 = a1.point.Value.LeaveTangent; bez.Slope2 = a2.point.Value.ArriveTangent; bez.bind(BezierSegment.X1Property, a1, "X"); bez.bind(BezierSegment.Y1Property, a1, "Y"); bez.bind(BezierSegment.X2Property, a2, "X"); bez.bind(BezierSegment.Y2Property, a2, "Y"); graph.Children.Add(bez); a1.rightBez = bez; a2.leftBez = bez; break; default: break; } }
public void Paint(bool recomputeView = false) { graph.Children.Clear(); LinkedList<CurvePoint> points = SelectedCurve.CurvePoints; if (points.Count > 0 && recomputeView) { float timeSpan = points.Last().InVal - points.First().InVal; timeSpan = timeSpan > 0 ? timeSpan : 2; HorizontalOffset = Math.Round(points.First().InVal - (timeSpan * 0.2)); double hSpan = Math.Ceiling(timeSpan * 1.2); if (hSpan + HorizontalOffset <= timeSpan) { hSpan += 1; } HorizontalScale = graph.ActualWidth / hSpan; if (HorizontalOffset >= points.First().InVal - (hSpan / 10)) { HorizontalOffset = points.First().InVal - (hSpan / 10); } else if (HorizontalOffset + hSpan <= points.Last().InVal + (hSpan / 10)) { HorizontalOffset += hSpan / 10; } float max = points.Max(x => x.OutVal); float min = points.Min(x => x.OutVal); float valSpan = max - min; valSpan = valSpan > 0 ? valSpan : 2; VerticalOffset = Math.Round(min - Math.Ceiling(valSpan * 0.1)); double vSpan = Math.Ceiling(valSpan * 1.2); if (vSpan + VerticalOffset <= max) { vSpan += 1; } VerticalScale = graph.ActualHeight / vSpan; } int numXLines = Convert.ToInt32(Math.Ceiling(ActualWidth / LINE_SPACING)); int numYLines = Convert.ToInt32(Math.Ceiling(ActualHeight / LINE_SPACING)); double upperXBound = unrealX(ActualWidth); double upperYBound = unrealY(ActualHeight); double lineXSpacing = (upperXBound - HorizontalOffset) / numXLines; int xGranularity = lineXSpacing > 0.75 ? 1 : (lineXSpacing > 0.25 ? 2 : 10); lineXSpacing = Math.Ceiling(lineXSpacing * xGranularity) / xGranularity; double lineYSpacing = (upperYBound - VerticalOffset) / numYLines; int yGranularity = lineYSpacing > 0.75 ? 1 : (lineYSpacing > 0.25 ? 2 : 10); lineYSpacing = Math.Ceiling(lineYSpacing * yGranularity) / yGranularity; Line line; Label label; double linepos; for (int i = 0; i < numXLines; i++) { linepos = HorizontalOffset + (lineXSpacing * (i + 1)); line = new Line(); Canvas.SetLeft(line, localX(linepos)); line.Style = FindResource("VerticalLine") as Style; graph.Children.Add(line); label = new Label(); Canvas.SetLeft(label, localX(linepos)); Canvas.SetBottom(label, 0); label.Content = linepos.ToString("0.00"); graph.Children.Add(label); } for (int i = 0; i < numYLines; i++) { linepos = VerticalOffset + (lineYSpacing * (i + 1)); line = new Line(); Canvas.SetBottom(line, localY(linepos)); line.Style = FindResource("HorizontalLine") as Style; graph.Children.Add(line); label = new Label(); Canvas.SetBottom(label, localY(linepos)); label.Content = linepos.ToString("0.00"); graph.Children.Add(label); } Anchor lastAnchor = null; for (LinkedListNode<CurvePoint> node = points.First; node != null; node = node.Next) { switch (node.Value.InterpMode) { case CurveMode.CIM_CurveAuto: case CurveMode.CIM_CurveUser: node.Value.LeaveTangent = node.Value.ArriveTangent; break; case CurveMode.CIM_CurveAutoClamped: node.Value.ArriveTangent = node.Value.LeaveTangent = 0f; break; case CurveMode.CIM_CurveBreak: case CurveMode.CIM_Constant: case CurveMode.CIM_Linear: default: break; } Anchor a = new Anchor(this, node); if (node.Value == SelectedPoint) { a.IsSelected = true; } graph.Children.Add(a); if (node.Previous == null) { line = new Line(); line.X1 = -10; line.bind(Line.Y1Property, a, "Y", new YConverter(), ActualHeight); line.bind(Line.X2Property, a, "X"); line.bind(Line.Y2Property, a, "Y", new YConverter(), ActualHeight); graph.Children.Add(line); } else { PathBetween(lastAnchor, a, node.Previous.Value.InterpMode); } if (node.Next == null) { line = new Line(); line.bind(Line.X1Property, a, "X"); line.bind(Line.Y1Property, a, "Y", new YConverter(), ActualHeight); line.X2 = ActualWidth + 10; line.bind(Line.Y2Property, a, "Y", new YConverter(), ActualHeight); graph.Children.Add(line); } lastAnchor = a; } }