如何在WPF中模拟悬挂电缆?

时间:2010-11-17 16:57:28

标签: c# wpf silverlight graphics gdi+

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

“电缆”的UI概念正是我所期待的,使用户能够清楚地理解这一概念。 Propellerhead在其音频组件的Reason软件中采用了类似的方法,如this YouTube video (fast forward to 2m:50s)所述。

我可以通过在点A到点B绘制样条来使这个概念在GDI中起作用,必须有一种更优雅的方式在WPF中使用Paths或者其他东西,但是你从哪里开始?当你抓住并摇动它时,有没有一种很好的方法来模拟电缆摆动的动画?

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

更新:感谢到目前为止答案中的链接,我几乎就在那里。

alt text

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

唯一的问题是,动画似乎运行得有点晚。我每次鼠标移动时都会启动故事板,有没有更好的方法来制作这个动画?到目前为止,我的解决方案位于:

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);
}

3 个答案:

答案 0 :(得分:9)

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

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

动态方法

将“绳索”分成大量(1000个左右)“元素”,每个元素都有一个位置和速度矢量。使用CompositionTarget.Rendering事件计算每个元素位置,如下所示:

  • 计算沿着相邻元素“cord”的每个元素的拉力,这与元素之间的距离成比例。假设绳索本身无质量。

  • 计算每个“元素”上的净力矢量,该元素由沿着绳索的每个相邻元素的拉力加上恒定的重力组成。

  • 使用质量常数将力矢量转换为加速度,并使用运动方程更新位置和速度。

  • 使用带有BeginFigure后跟PolyLineTo的StreamGeometry构建绘制线条。有这么多点,没有理由进行额外的计算来创建一个三次贝塞尔近似。

静态方法

将你的绳索划分为30个区段,每个区段是一个与接触网y = a cosh(x / a)近似的三次贝塞尔曲线。您的终点控制点应位于悬链线上,平行线应与悬链线相切,控制线长度根据悬链线的二阶导数设置。

在这种情况下,您可能还想渲染StreamGeometry,使用BeginFigure和PolyBezierTo来构建它。

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

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

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

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

如果这样做,您可以将Catenary控件放在任何位置并获得悬链曲线。

答案 1 :(得分:1)

答案 2 :(得分:1)

恕我直言'悬挂'(物理模拟)电缆是一个过度做的情况 - 有利于看起来可用性。

您确定不仅仅是在混淆用户体验吗?

在基于节点/连接的UI中,我发现 clear 连接(比如在Quartz Composer:http://ellington.tvu.ac.uk/ma/wp-content/uploads/2006/05/images/Quartz%20Composer_screenshot_011.png中)比眼睛更像重要的方式,比如朝不同方向前进的摆动电缆(由于重力下降)比实际连接点的位置。 (同时,在模拟中占用CPU周期,这可能在其他地方更有用)

只需我0.02美元