我有一个非常“基于连接”的应用程序,即多个输入/输出。

我正在寻找“电缆”的UI概念,以使该概念对用户清晰可见。 Propellerhead在其Reason软件中采用了类似的方法来处理音频组件,如this YouTube video (fast forward to 2m:50s)所示。

我可以通过绘制从A点到B点的样条曲线来在GDI中实现这一概念,必须有一种更为优雅的方法来使用Paths或WPF中的某些东西,但是从哪里开始呢?捕获并摇晃电缆摆动时,是否有一种很好的方法来模拟电缆摆动的动画?

如果已经为WPF发明了这种轮子,我也愿意控制库(商业或开源)。

更新:由于到目前为止答案中的链接,我快到了。

我已经以编程方式创建了一个BezierCurve,其中Point 1是(0, 0),Point 2是底部的“悬挂”点,Point 3是鼠标光标所在的位置。我为Point 2创建了一个PointAnimation,并对其应用了ElasticEase缓动函数以提供“摇摆”效果(即,将中间点弹起一点)。

唯一的问题是,动画似乎播放得有点晚。每当鼠标移动时,我都会启动 Storyboard ,是否有更好的方法来制作此动画?到目前为止,我的解决方案位于:

Bezier Curve Playground

码:

private Path _path = null;
private BezierSegment _bs = null;
private PathFigure _pFigure = null;
private Storyboard _sb = null;
private PointAnimation _paPoint2 = null;
ElasticEase _eEase = null;

private void cvCanvas_MouseMove(object sender, MouseEventArgs e)
{
    var position = e.GetPosition(cvCanvas);
    AdjustPath(position.X, position.Y);
}

// basic idea: when mouse moves, call AdjustPath and draw line from (0,0) to mouse position with a "hang" in the middle
private void AdjustPath(double x, double y)
{
    if (_path == null)
    {
        _path = new Path();
        _path.Stroke = new SolidColorBrush(Colors.Blue);
        _path.StrokeThickness = 2;
        cvCanvas.Children.Add(_path);

        _bs = new BezierSegment(new Point(0, 0), new Point(0, 0), new Point(0, 0), true);

        PathSegmentCollection psCollection = new PathSegmentCollection();
        psCollection.Add(_bs);

        _pFigure = new PathFigure();
        _pFigure.Segments = psCollection;
        _pFigure.StartPoint = new Point(0, 0);


        PathFigureCollection pfCollection = new PathFigureCollection();
        pfCollection.Add(_pFigure);

        PathGeometry pathGeometry = new PathGeometry();
        pathGeometry.Figures = pfCollection;

        _path.Data = pathGeometry;
    }

    double bottomOfCurveX = ((x / 2));
    double bottomOfCurveY = (y + (x * 1.25));

    _bs.Point3 = new Point(x, y);

    if (_sb == null)
    {
        _paPoint2 = new PointAnimation();

        _paPoint2.From = _bs.Point2;
        _paPoint2.To = new Point(bottomOfCurveX, bottomOfCurveY);
        _paPoint2.Duration = new Duration(TimeSpan.FromMilliseconds(1000));
        _eEase = new ElasticEase();

        _paPoint2.EasingFunction = _eEase;
        _sb = new Storyboard();

        Storyboard.SetTarget(_paPoint2, _path);
        Storyboard.SetTargetProperty(_paPoint2, new PropertyPath("Data.Figures[0].Segments[0].Point2"));

        _sb.Children.Add(_paPoint2);
        _sb.Begin(this);
    }

    _paPoint2.From = _bs.Point2;
    _paPoint2.To = new Point(bottomOfCurveX, bottomOfCurveY);

    _sb.Begin(this);
}

最佳答案

如果要进行真正的动态运动(即,当您“摇动”鼠标指针时,您可以创建沿绳索传播的波浪),则需要使用有限元技术。但是,如果您对静态行为没问题,则可以简单地使用Bezier曲线。

首先,我将简要描述有限元方法,然后再详细介绍静态方法。

动态方法

将您的“绳索”划分为大量(大约1000个)“元素”,每个元素都有一个位置和速度 vector 。使用CompositionTarget.Rendering事件来计算每个元素的位置,如下所示:

  • 计算相邻元素沿“帘线”沿相邻元素的拉力,该拉力与元素之间的距离成比例。假设电源线本身没有质量。
  • 计算每个“元素”上的净力 vector ,该 vector 由沿着绳索的每个相邻元素的拉力以及恒定的重力组成。
  • 使用质量常数将力 vector 转换为加速度,并使用运动方程式更新位置和速度。
  • 使用StreamGeometry构建绘制线条,并带有BeginFigure和PolyLineTo。有这么多的点,几乎没有理由进行额外的计算以创建三次贝塞尔近似。

  • 静态方法

    将您的绳索分成大约30个部分,每个部分近似于悬链线y = a cosh(x / a)的立方贝塞尔曲线。您的最终控制点应在悬链线上,平行线应与悬链线相切,并且控制线的长度应基于悬链线的二阶导数设置。

    在这种情况下,您可能还希望使用BeginFigure和PolyBezierTo来构建StreamGeometry。

    我将其实现为类似于Rectangle和Ellipse的自定义Shape子类“Catenary”。在这种情况下,您只需要重写DefiningGeometry属性即可。为了提高效率,我还将覆盖CacheDefiningGeometry,GetDefiningGeometryBounds和GetNaturalSize。

    您将首先决定如何参数化悬链线,然后为所有参数添加DependencyProperties。确保在FrameworkPropertyMetadata中设置AffectsMeasure和AffectsRender标志。

    一种可能的参数化是XOffset,YOffset,Length。另一个可能是XOffset,YOffset,SagRelativeToWidth。这将取决于最容易绑定(bind)的内容。

    定义DependencyProperty后,实现DefiningGeometry属性以计算三次贝塞尔曲线控制点,构造StreamGeometry并返回它。

    如果执行此操作,则可以将“悬链线”控件放在任何位置,并获得悬链线曲线。

    关于c# - 如何在WPF中模拟悬挂式电缆?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/4206983/

    10-12 12:48