代码之家  ›  专栏  ›  技术社区  ›  Brandon

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

  •  14
  • Brandon  · 技术社区  · 14 年前

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

    “电缆”的用户界面概念正是我想要让用户明白这个概念的地方。ProplerHead在音频组件的原因软件中采用了类似的方法,如 此youtube视频(fast forward to 2m:50s).

    我可以在GDI中通过绘制从点A到点B的样条曲线来实现这个概念,这里必须有一种更优雅的方法来使用路径或WPF中的某些内容,但是从哪里开始呢?有没有一个很好的方法来模拟电缆摆动的动画,当你抓住它并摇晃它?

    如果已经为WPF发明了这个控制盘,我还可以向控制库(商业或开源)开放。

    更新: 感谢到目前为止答案中的链接,我就快到了。

    我已经以编程方式创建了一个 beziercurve 曲线,其中point 1 being (0,0) ,point 2 being the bottom“hang”point,and point 3 being wherewherewhere the mouse cursor is.我已经为点2创建了一个 pointanimation for point 2 with an elasticease easing function applied to it to give the“swinging”effect(即,bouncing the middle point around a bit.)。

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

    bezier curve playground

    <>代码:

    private path_path=null;
    private bezierSegment _bs=空;
    private pathFigure_pfigure=空;
    private storyboard _sb=null;
    private pointanimation _papoint2=空;
    弹性蛋白酶=空;
    
    private void cvccanvas_mousemove(对象发送器,mouseEventargs e)
    {
    var位置=e.getposition(cvcanvas);
    调整路径(位置x,位置y);
    }
    
    //基本思想:鼠标移动时,调用adjustpath,从(0,0)到鼠标位置画线,中间挂起
    专用空隙调整路径(双x,双y)
    {
    if(_path==null)
    {
    _路径=新路径();
    _ path.stroke=新SolidColorBrush(colors.blue);
    _ path.strokethicness=2;
    cvcanvas.children.add(_path);
    
    _ bs=新贝塞尔段(新点(0,0),新点(0,0),新点(0,0),真);
    
    pathSegmentCollection psCollection=new pathSegmentCollection();
    pscollection.add(_bs);
    
    _ pfigure=new pathFigure();
    _ pfigure.segments=pscollection;
    _ pfigure.startpoint=新点(0,0);
    
    
    pathFigureCollection pfCollection=new pathFigureCollection();
    pfcollection.add(_pfigure);
    
    pathgeometry pathgeometry=new pathgeometry();
    pathgeometry.figures=pfcollection;
    
    _ path.data=路径几何;
    }
    
    双底曲线=(x/2));
    双底曲线y=(y+(x*1.25));
    
    _ bs.point3=新点(x,y);
    
    如果(_sb==null)
    {
    _ papoint2=新的pointAnimation();
    
    _ papoint2.from=_bs.point2;
    _ papont2.to=新点(bottoofcurvex,bottoofcurvey);
    _ papoint2.duration=新的持续时间(TimeSpan.From毫秒(1000));
    _ eease=新弹性蛋白酶();
    
    _ papoint2.easingfunction=eease;
    _ sb=新故事板();
    
    storyboard.settarget(_papoint2,_path);
    storyboard.settargetproperty(_papoint2,new propertypath(“data.figures[0].segments[0].point2”));
    
    _添加(papoint2);
    _开始(这个);
    }
    
    _ papoint2.from=_bs.point2;
    _ papont2.to=新点(bottoofcurvex,bottoofcurvey);
    
    _开始(这个);
    }
    < /代码> <. 多个输入/输出。

    “电缆”的用户界面概念正是我想要让用户明白这个概念的地方。在音频部件的推理软件中,螺旋桨头部采用了类似的方法,如this YouTube video (fast forward to 2m:50s).

    我可以在GDI中通过绘制从点A到点B的样条曲线来实现这个概念,这里必须有一种更优雅的方法来使用路径或WPF中的某些内容,但是从哪里开始呢?有没有一个很好的方法来模拟电缆摆动的动画,当你抓住它并摇晃它?

    如果这个轮子是为WPF而发明的,那么我也对控制库(商业的或开源的)开放。

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

    alt text

    我创造了一个BezierCurve以编程的方式,第一点是(0, 0)点2是底部的“挂起”点,点3是鼠标光标所在的位置。我创造了一个PointAnimation对于带ElasticEase对其应用的放松功能,以产生“摆动”效果(即,围绕一点反弹中间点)。

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

    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 回复  |  直到 13 年前
        1
  •  9
  •   Ray Burns    14 年前

    如果你想要真正的动态运动(也就是说,当你“摇动”鼠标指针时,你可以产生沿着绳子传播的波),你需要使用有限元技术。但是,如果您对静态行为很满意,您可以简单地使用贝塞尔曲线。

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

    动态方法

    把你的“绳索”分成一个大的数字(大约1000个)元素,每个元素都有一个位置和速度矢量。使用compositionTarget.rendering事件计算每个元素的位置,如下所示:

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

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

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

    • 使用streamgeometry构建绘制线条,其开始配置后跟polylineto。对于如此多的点,几乎没有理由做额外的计算来创建一个三次贝塞尔近似。

    静态方法

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

    在这种情况下,您可能还需要渲染流几何体,使用BeginFigure和PolyBezier来构建它。

    我将把它实现为一个自定义的形状子类“悬链线”,类似于矩形和椭圆。在这种情况下,您只需覆盖DefiningGeometry属性。为了提高效率,我还将覆盖cacheDefiningGeometry、getDefiningGeometryBounds和getNaturalSize。

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

    一个可能的参数化是xoffset、yoffset和length。另一个可能是xoffset、yoffset和sagrarelativeToWidth。这取决于什么是最容易绑定的。

    定义DependencyProperties后,实现DefiningGeometry属性以计算立方体Bezier控制点,构造streamGeometry并返回它。

    如果你这样做,你可以在任何地方放下一个接触网控制,得到一个接触网曲线。

        2
  •  1
  •   Guy    14 年前
        3
  •  1
  •   Pygmy    14 年前

    imho“悬挂”(物理模拟)电缆是一种过度使用的情况-喜欢的外观胜过可用性。

    你确定你不只是在混乱用户体验吗?

    在基于节点/连接的用户界面中 清楚的 连接(如石英作曲: http://ellington.tvu.ac.uk/ma/wp-content/uploads/2006/05/images/Quartz%20Composer_screenshot_011.png )比像糖果一样的摇摆电缆更重要的是,这些电缆的走向(由于重力向下)与实际连接点所在的方向不同。(同时占用CPU周期进行模拟,这在其他地方更有用)

    只要我0.02美元