Как я могу имитировать висячий кабель в WPF?


У меня есть приложение, которое очень "основано на соединении", т. е. несколько входов/выходов.

Концепция пользовательского интерфейса "кабеля" - это именно то, что я ищу, чтобы сделать эту концепцию понятной пользователю. Propellerhead использовали аналогичный подход в своем программном обеспечении Reason для аудиокомпонентов, проиллюстрированном в этом видео YouTube (быстрая перемотка вперед на 2m: 50s) .

Я могу заставить эту концепцию работать в GDI, нарисовав сплайн из точки A в точку B, должен быть более элегантный способ использования Пути или что-то в WPF для этого, но с чего вы начинаете? Есть ли хороший способ имитировать анимацию качания кабеля, когда вы хватаете его и встряхиваете?

Я также открыт для управления библиотеками (коммерческими или с открытым исходным кодом), если это колесо уже было изобретено для WPF.

Обновление: благодаря ссылкам в ответах до сих пор, я почти там.

текст Alt

Я создал BezierCurve программно, причем точка 1 - это (0, 0), Точка 2-это нижний " висячий" точка, А Точка 3 находится там, где находится курсор мыши. Я создал PointAnimation Для точки 2 с функцией ослабления ElasticEase, примененной к ней, чтобы дать эффект "качания" (то есть, немного подпрыгивая в средней точке).

Единственная проблема в том, что анимация, кажется, немного запаздывает. Я запускаю раскадровку каждый раз, когда мышь движется, есть ли лучший способ сделать эту анимацию? Мое решение пока находится здесь:

Кривая Безье Площадка

Код:

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 14

3 ответа:

Если вы хотите истинное динамическое движение (то есть, когда вы "встряхиваете" указатель мыши, вы можете создавать волны, которые перемещаются вдоль шнура), вам нужно будет использовать методы конечных элементов. Однако если вас устраивает статическое поведение, вы можете просто использовать кривые Безье.

Сначала я кратко опишу подход конечных элементов, а затем более подробно остановлюсь на статическом подходе.

Динамический подход

Разделите свой "шнур" на большое число (1000 или около того) "элементов", каждый из которых имеет положение и вектор скорости. Используйте CompositionTarget.Событие визуализации для вычисления положения каждого элемента следующим образом:

  • Вычислите натяжение каждого элемента вдоль "шнура" от соседних элементов, которое пропорционально расстоянию между элементами. Предположим, что сам Шнур не имеет массы.

  • Вычислите вектор чистой силы на каждом "элементе", который состоит из тяги от каждого соседнего элемента вдоль шнура, плюс постоянная сила серьезность.

  • Используйте постоянную массы, чтобы преобразовать вектор силы в ускорение, и обновите положение и скорость, используя уравнения движения.

  • Нарисуйте линию, используя построение StreamGeometry с BeginFigure, за которым следует PolyLineTo. При таком количестве точек нет особых причин делать дополнительные вычисления для создания кубического приближения Безье.

Статический подход

Разделите шнур примерно на 30 сегментов, каждый из которых что является кубическим приближением Безье к цепной системе y = a cosh (x/a). Конечные контрольные точки должны находиться на цепной кривой, параллели должны касаться цепных линий, а длины управляющих линий должны быть установлены на основе второй производной цепной линии.

В этом случае вы, вероятно, также захотите отрисовать StreamGeometry, используя BeginFigure и PolyBezierTo для его построения.

Я бы реализовал это как пользовательский подкласс формы "Catenary", подобный прямоугольнику и эллипсу. В в этом случае все, что вам нужно, это переопределить свойство DefiningGeometry. Для эффективности я бы также переопределил CacheDefiningGeometry, GetDefiningGeometryBounds и GetNaturalSize.

Сначала вы должны решить, как параметризовать вашу цепную сеть, а затем добавить DependencyProperties для всех ваших параметров. Убедитесь, что вы установили флаги AffectsMeasure и AffectsRender в вашем FrameworkPropertyMetadata.

Одной из возможных параметризаций будет XOffset, YOffset, Length. Другой может быть XOffset, YOffset, SagRelativeToWidth. Это будет зависеть от того, к чему легче всего привязаться.

Как только ваши свойства DependencyProperties определены, реализуйте свое свойство DefiningGeometry для вычисления кубических контрольных точек Безье, постройте StreamGeometry и верните его.

Если вы сделаете это, вы можете отбросить цепной элемент управления в любом месте и получить цепную кривую.

Сегменты кривой пользователя Безье в пути.

Http://www.c-sharpcorner.com/UploadFile/dbeniwal321/WPFBezier01302009015211AM/WPFBezier.aspx

IMHO "висячие" (физически смоделированные) кабели-это случай чрезмерного использования-предпочтительный взгляд на удобство использования.

Вы уверены, что не просто загромождаете пользовательский интерфейс ?

В пользовательском интерфейсе, основанном на узле/соединении, я нахожу четкие соединения (как в Quartz Composer : http://ellington.tvu.ac.uk/ma/wp-content/uploads/2006/05/images/Quartz%20Composer_screenshot_011.png) намного важнее, чем глазные конфеты, такие как качающиеся кабели, которые направляются в другом направлении (вниз из-за гравитации), чем там, где на самом деле находится точка соединения. (И в то же время съедают CPU-циклы для моделирования, которое может быть более полезным в другом месте)

Только мои $ 0.02