如何检测贝塞尔曲线是否与指定的椭圆相交?
How to detect, if a bezier curve intersects a specified ellipse?
我有一个自定义控件,它呈现一条从左上角到右下角的贝塞尔曲线。我想修改命中测试行为,以便控件仅在悬停在贝塞尔曲线上方或附近时才“命中”,但 FillContainsWithDetail
没有 return 预期结果。
我错过了什么?
这是一个派生的例子class:
public class BezierControl : FrameworkElement
{
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
{
if (_geometry == null) return null;
var p = hitTestParameters.HitPoint;
EllipseGeometry expandedHitTestArea = new EllipseGeometry(p, 10.0, 10.0);
var intersection = expandedHitTestArea.FillContainsWithDetail(_geometry);
if (intersection > IntersectionDetail.Empty)
{
return new PointHitTestResult(this, hitTestParameters.HitPoint);
}
return null;
}
private StreamGeometry _geometry;
private StreamGeometry getGeometry()
{
var result = new StreamGeometry();
using (var context = result.Open())
{
var start = new Point(0, 0);
var startCp = new Point(10, 0);
var end = new Point(this.ActualWidth, this.ActualHeight);
var endCp = new Point(this.ActualWidth - 10, this.ActualHeight);
context.BeginFigure(start, false, false);
context.BezierTo(startCp, endCp, end, true, false);
}
result.Freeze();
return result;
}
protected override void OnRender(DrawingContext dc)
{
base.OnRender(dc);
if (_geometry == null) _geometry = getGeometry();
dc.DrawGeometry(null, _pen, _geometry);
}
private Pen _pen = new Pen(Brushes.Red, 1.0);
}
编辑:下面批准的答案很好,但它也让我想到了另一个选择——或者告诉我为什么我尝试的解决方案失败了:我正在创建这样的几何体:
context.BeginFigure(bezier.Start, false, false);
第一个 bool
参数称为 isFilled
。将此设置为 true
允许在 _geometry
和 expandedHitTestArea
之间相交。
在注意到这一点之前,我实施了下面接受的答案,并出于性能原因决定延迟创建 widenedGeometry
。现在我想知道:从性能的角度来看,哪种方法更好?或者,在这种情况下这可以忽略不计,因为几何图形无论如何都不贵?
您不需要那个 EllipseGeometry。只需检查命中点是否在原始几何体的加宽路径几何体内(当然也可以在创建_geometry
时创建一次):
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
{
if (_geometry != null)
{
var widenedGeometry = _geometry.GetWidenedPathGeometry(new Pen(null, 20));
if (widenedGeometry.FillContains(hitTestParameters.HitPoint))
{
return new PointHitTestResult(this, hitTestParameters.HitPoint);
}
}
return null;
}
我有一个自定义控件,它呈现一条从左上角到右下角的贝塞尔曲线。我想修改命中测试行为,以便控件仅在悬停在贝塞尔曲线上方或附近时才“命中”,但 FillContainsWithDetail
没有 return 预期结果。
我错过了什么?
这是一个派生的例子class:
public class BezierControl : FrameworkElement
{
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
{
if (_geometry == null) return null;
var p = hitTestParameters.HitPoint;
EllipseGeometry expandedHitTestArea = new EllipseGeometry(p, 10.0, 10.0);
var intersection = expandedHitTestArea.FillContainsWithDetail(_geometry);
if (intersection > IntersectionDetail.Empty)
{
return new PointHitTestResult(this, hitTestParameters.HitPoint);
}
return null;
}
private StreamGeometry _geometry;
private StreamGeometry getGeometry()
{
var result = new StreamGeometry();
using (var context = result.Open())
{
var start = new Point(0, 0);
var startCp = new Point(10, 0);
var end = new Point(this.ActualWidth, this.ActualHeight);
var endCp = new Point(this.ActualWidth - 10, this.ActualHeight);
context.BeginFigure(start, false, false);
context.BezierTo(startCp, endCp, end, true, false);
}
result.Freeze();
return result;
}
protected override void OnRender(DrawingContext dc)
{
base.OnRender(dc);
if (_geometry == null) _geometry = getGeometry();
dc.DrawGeometry(null, _pen, _geometry);
}
private Pen _pen = new Pen(Brushes.Red, 1.0);
}
编辑:下面批准的答案很好,但它也让我想到了另一个选择——或者告诉我为什么我尝试的解决方案失败了:我正在创建这样的几何体:
context.BeginFigure(bezier.Start, false, false);
第一个 bool
参数称为 isFilled
。将此设置为 true
允许在 _geometry
和 expandedHitTestArea
之间相交。
在注意到这一点之前,我实施了下面接受的答案,并出于性能原因决定延迟创建 widenedGeometry
。现在我想知道:从性能的角度来看,哪种方法更好?或者,在这种情况下这可以忽略不计,因为几何图形无论如何都不贵?
您不需要那个 EllipseGeometry。只需检查命中点是否在原始几何体的加宽路径几何体内(当然也可以在创建_geometry
时创建一次):
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
{
if (_geometry != null)
{
var widenedGeometry = _geometry.GetWidenedPathGeometry(new Pen(null, 20));
if (widenedGeometry.FillContains(hitTestParameters.HitPoint))
{
return new PointHitTestResult(this, hitTestParameters.HitPoint);
}
}
return null;
}