Bra*_*don 14 c# silverlight wpf graphics gdi+
我有一个非常"基于连接"的应用程序,即多个输入/输出.
"电缆"的UI概念正是我所期待的,使用户能够清楚地理解这一概念.Propellerhead在其音频组件的Reason软件中采用了类似的方法,如本YouTube视频所示(快进到2m:50s).
我可以通过绘制从A点到B点的样条来使这个概念在GDI中起作用,必须有更优雅的方式在WPF中使用Paths或者其他东西,但是你从哪里开始呢?当你抓住并摇动它时,是否有一种很好的方法来模拟电缆摆动的动画?
如果已经为WPF发明了这个轮子,我也愿意控制库(商业或开源).
更新:感谢到目前为止答案中的链接,我几乎就在那里.
我已经以BezierCurve
编程方式创建了一个,点1是(0, 0)
,点2是底部的"挂起"点,点3是鼠标光标所在的位置.我已经创建了一个PointAnimation
for Point 2,ElasticEase
它应用了一个缓动函数来提供"Swinging"效果(即,稍微反弹中间点).
唯一的问题是,动画似乎运行得有点晚.我每次鼠标移动时都会启动故事板,有没有更好的方法来制作这个动画?到目前为止,我的解决方案位于:
码:
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);
}
Run Code Online (Sandbox Code Playgroud)
如果你想要真正的动态运动(即,当你"摇动"鼠标指针时,你可以创建沿着绳索传播的波浪),你将需要使用有限元技术.但是,如果您对静态行为没问题,则可以使用Bezier曲线.
首先,我将简要介绍有限元方法,然后详细介绍静态方法.
动态方法
将"绳索"分成大量(1000个左右)"元素",每个元素都有一个位置和速度矢量.使用CompositionTarget.Rendering事件计算每个元素位置,如下所示:
计算沿着相邻元素的"绳索"的每个元素的拉力,其与元素之间的距离成比例.假设绳索本身是无质量的.
计算每个"元素"上的净力矢量,其由沿着绳索的每个相邻元素的拉力加上恒定的重力组成.
使用质量常数将力矢量转换为加速度,并使用运动方程更新位置和速度.
使用StreamGeometry构建绘制线条,其中包含BeginFigure,后跟PolyLineTo.有这么多点,没有理由进行额外的计算来创建一个三次贝塞尔近似.
静态方法
将你的绳索划分为30个段,每个段是一个与接触网y = a cosh(x/a)近似的三次贝塞尔曲线.您的终点控制点应位于悬链曲线上,平行线应与悬链线相切,控制线长度根据悬链线的二阶导数设置.
在这种情况下,您可能还想渲染StreamGeometry,使用BeginFigure和PolyBezierTo来构建它.
I would implement this as a custom Shape subclass "Catenary" similar to Rectangle and Ellipse. In that case, all you have to override the DefiningGeometry property. For efficiency I would also override CacheDefiningGeometry, GetDefiningGeometryBounds, and GetNaturalSize.
You would first decide how to parameterize your catenary, then add DependencyProperties for all your parameters. Make sure you set the AffectsMeasure and AffectsRender flags in your FrameworkPropertyMetadata.
One possible parameterization would be XOffset, YOffset, Length. Another might be XOffset, YOffset, SagRelativeToWidth. It would depend on what would be easiest to bind to.
Once your DependencyProperties are defined, implement your DefiningGeometry property to compute the cubic bezier control points, construct the StreamGeometry, and return it.
If you do this, you can drop a Catenary control anywhere and get a catenary curve.