如何剪裁非闭合几何体
Posted
技术标签:
【中文标题】如何剪裁非闭合几何体【英文标题】:How to clip non-closed geometry 【发布时间】:2014-08-29 07:56:51 【问题描述】:简介
我在实现剪辑时发现了一个问题(请参阅this)。
看起来 UIElement.Clip 仍然渲染不可见的部分
渲染相对较小的几何体(仅填充 1920x1200 区域 ~ 2000 条垂直线的线条)需要很多时间。当使用Clip
和移动屏幕外的几何体(以便剪辑应该删除它的重要部分)时,仍然需要相同时间(大约1秒)。
好的,我发现使用 Geometry.Combine
会进行剪辑(在剪辑几何体后,渲染时间会按比例减少)。完美!
问题
Geometry.Combine
不适用于非闭合几何。它产生封闭的几何图形。而且它看起来很丑,连接第一点和最后一点:
问题
如何对非封闭图形执行裁剪(减少要渲染的几何图形数量)?
编辑
这是之前的几何图形(图片中显示的小和平)
M0; 50L0; 50L1; 53,1395259764657L2; 56,2666616782152L3; 59,3690657292862L4; 62,4344943582427L5; 65,4508497187474L6; 68,4062276342339L7; 71,2889645782536L8; ...
之后
F1M54,9999923706055;34,5491371154785L53,9999885559082;37,5655174255371 53,0000114440918;40,6309471130371 52,000,30371 52,00006076293946p>4762935876p;
通知一开始的变化,是M 0;50 L ...
,变成F 1 M 55;34 L ...
F1
表示NonZero
填充
确定一个点是否在路径的填充区域中的规则,方法是从该点向任意方向无限远地绘制一条射线,然后检查形状的一部分与射线相交的位置。从 0 开始,每次一条线段从左到右穿过射线时加一,每次路径段从右到左穿过射线时减一。在计算交叉点后,如果结果为零,则该点位于路径之外。否则,它在里面。
我完全不知道这意味着什么。但也许它很重要?
编辑
我应该一直在看字符串的结尾。 Path.Data
末尾有z
,表示图形已关闭。
奇怪的是,尝试删除 z
(通过使用 Geometry.ToString()
/Geometry.Parse()
组合)不起作用。经过一番调查,我发现Combine
产生的物理封闭数字(命令L x;y
,其中x;y
是最左边的点)。最糟糕的是它并不总是最后一点,因此在解析之前简单地删除最后一个L x;y
也不起作用。 =(
编辑
演示问题的示例:
Xaml:
<Path x:Name="path" Stroke="Red"/>
代码:
var geometry1 = new RectangleGeometry(new Rect(100, 100, 100, 100));
var geometry2 = new PathGeometry(new[] new PathFigure(new Point(0,0), new[]
new LineSegment(new Point(300, 300), true),
new LineSegment(new Point(300, 0), true),
, false) );
//path.Data = geometry1;
//path.Data = geometry2;
//path.Data = Geometry.Combine(geometry1, geometry2, GeometryCombineMode.Intersect, null);
geometry1
和geometry2
的图片:
结果Combine
:
可以看到剪裁后2行变成3行,调试证明:
F1M100;100L200;100 200;200 100;100z
注意,不只是z
,最后还有100;100
点,连接起点。
【问题讨论】:
您能否分享一些针对同一问题的工作代码? @pushpraj,见编辑。 好的,请给我一些时间。这里有点晚了,希望我明天之前回复你不会介意。 Geometry.Combine 创建一个形状而不是路径(据我对msdn.microsoft.com/en-us/library/… 的理解),如果不关闭它,您无法从路径中创建形状(查看您的结果)我不知道是否有是合并几何的给定方法,但我认为 geometry.Combine 是错误的开始。 @SebastianL 看看Geometry.Combine
方法的返回类型。它不会创建一个形状,但实际上是一个PathGeometry
。但是,这并不意味着 Combine
不会自动关闭生成的几何图形,因此这里的方法是错误的。
【参考方案1】:
我尝试基于this线相交算法实现非封闭几何的裁剪解决方案
代码
public static PathGeometry ClipGeometry(PathGeometry geom, Rect clipRect)
PathGeometry clipped = new PathGeometry();
foreach (var fig in geom.Figures)
PathSegmentCollection segments = new PathSegmentCollection();
Point lastPoint = fig.StartPoint;
foreach (LineSegment seg in fig.Segments)
List<Point> points;
if (LineIntersectsRect(lastPoint, seg.Point, clipRect, out points))
LineSegment newSeg = new LineSegment(points[1], true);
PathFigure newFig = new PathFigure(points[0], new[] newSeg , false);
clipped.Figures.Add(newFig);
lastPoint = seg.Point;
return clipped;
static bool LineIntersectsRect(Point lineStart, Point lineEnd, Rect rect, out List<Point> points)
points = new List<Point>();
if (rect.Contains(lineStart) && rect.Contains(lineEnd))
points.Add(lineStart);
points.Add(lineEnd);
return true;
Point outPoint;
if (Intersects(lineStart, lineEnd, rect.TopLeft, rect.TopRight, out outPoint))
points.Add(outPoint);
if (Intersects(lineStart, lineEnd, rect.BottomLeft, rect.BottomRight, out outPoint))
points.Add(outPoint);
if (Intersects(lineStart, lineEnd, rect.TopLeft, rect.BottomLeft, out outPoint))
points.Add(outPoint);
if (Intersects(lineStart, lineEnd, rect.TopRight, rect.BottomRight, out outPoint))
points.Add(outPoint);
if (points.Count == 1)
if (rect.Contains(lineStart))
points.Add(lineStart);
else
points.Add(lineEnd);
return points.Count > 0;
static bool Intersects(Point a1, Point a2, Point b1, Point b2, out Point intersection)
intersection = new Point(0, 0);
Vector b = a2 - a1;
Vector d = b2 - b1;
double bDotDPerp = b.X * d.Y - b.Y * d.X;
if (bDotDPerp == 0)
return false;
Vector c = b1 - a1;
double t = (c.X * d.Y - c.Y * d.X) / bDotDPerp;
if (t < 0 || t > 1)
return false;
double u = (c.X * b.Y - c.Y * b.X) / bDotDPerp;
if (u < 0 || u > 1)
return false;
intersection = a1 + t * b;
return true;
目前解决方案适用于基于线的几何图形,如果需要,可能需要包含其他类型。
测试 xaml
<UniformGrid Columns="2"
Margin="250,250,0,0">
<Grid>
<Path x:Name="pathClip"
Fill="#22ff0000" />
<Path x:Name="path"
Stroke="Black" />
</Grid>
<Path x:Name="path2"
Margin="100,0,0,0"
Stroke="Black" />
</UniformGrid>
测试代码 1
void test()
var geometry = new PathGeometry(new[] new PathFigure(new Point(0,0), new[]
new LineSegment(new Point(300, 300), true),
new LineSegment(new Point(300, 0), true),
, false) );
Rect clipRect= new Rect(10, 10, 180, 180);
path.Data = ClipGeometry(geometry, clipRect);
path2.Data = geometry;
pathClip.Data = new RectangleGeometry(clipRect);
结果
测试代码 2
void test()
var radius = 1.0;
var figures = new List<LineSegment>();
for (int i = 0; i < 2000; i++, radius += 0.1)
var segment = new LineSegment(new Point(radius * Math.Sin(i), radius * Math.Cos(i)), true);
segment.Freeze();
figures.Add(segment);
var geometry = new PathGeometry(new[] new PathFigure(figures[0].Point, figures, false) );
Rect clipRect= new Rect(10, 10, 180, 180);
path.Data = ClipGeometry(geometry, clipRect);
path2.Data = geometry;
pathClip.Data = new RectangleGeometry(clipRect);
结果
试一试,看看距离有多近。
【讨论】:
【参考方案2】:如果我是对的,您的主要问题是:“如何提高绘制多种形状的性能?”
要使其正常工作,您必须了解 Geometry Math
。
几何对象只有在连接或重叠时才能合并/组合。并且路径几何和形状几何有很大的区别。
例如,如果两个圆圈重叠,您可以在 WPF 中组合它们
获取重叠区域:Intersect
获取差异:Xor
获取组合曲面:Union
仅获取一种形状的差异:Exclude
路径几何有点不同,因为路径没有表面,路径不能Intersect
| Xor
| Union
| Exclude
另一个路径或形状。
但是 WPF 认为您只是忘记关闭路径并且正在为您执行此操作,这会导致您的问题出现给定的结果。
因此,为了提高性能,您必须首先过滤所有几何图形的形状和路径。
foreach(Shape geometryObj in ControlsOrWhatEver)
if(geometryObj is Line || geometryObj is Path || geometryObj is Polypath)
pathList.Add(geometryObj);
else
shapeList.Add(geometryObj);
对于 shapeList 你可以使用Geometry.Combine
,但是对于 pathList 你必须做一些其他的工作。您必须检查是否在某个点连接,无论开始点、结束点还是介于两者之间的某个位置都无关紧要。
如果你已经这样做了,你可以像这样合并而不是合并路径:
public Polyline mergePaths(Shape line1, Shape line2)
if(!checkLineType(line1) || !checkLineType(line2))
return null;
if(hitTest(line1, line2))
//here you have to do some math to determine the overlapping points
//on these points you can do something like this:
foreach(Point p in Overlapping Points)
//add the first line until p then add line2 and go on to add lin 1 until another p
else
return null;
【讨论】:
我原来的问题仍然是“如何裁剪非封闭几何体”。你正确地称之为path。我现在比评论更好地理解你的观点,谢谢。您的回答让我想到了尝试使 path 成为 geometry,因此我必须生成更多而不是 2 行(例如,向前两次细线和落后)。或者甚至尝试使用 fake shadow 技术(封闭的图形,然后是另一个,移动几个像素)来与封闭的几何图形相交 裁剪。仍然,看起来不是一个好的解决方案..如果存在的话..以上是关于如何剪裁非闭合几何体的主要内容,如果未能解决你的问题,请参考以下文章