如何在不包括贝塞尔曲线控制点的情况下计算Android中Path的边界



Android有一个Path类,方法为computeBounds()

https://developer.android.com/reference/android/graphics/Path#computeBounds(android.graphics.RectF,%20boolean(

但是,在贝塞尔曲线路径的情况下,边界包括曲线的控制点。

这并不理想,因为我只想计算由贝塞尔曲线创建的实际形状的边界。

似乎Skia(Android使用的底层库(有一个computeTightBounds方法,它可以做我想要的事情,但Android似乎没有公开它。

https://api.skia.org/classSkPath.html#a597c8fcc5e4750542e2688b057a14e9e

如果不包括控制点,我该如何计算边界?

没有办法直接获得它。

要获得一组高精度的边界,你需要自己计算,通过遍历路径段并计算每个边框、直线、圆弧等的实际边界。不幸的是,Android没有提供在创建Path后遍历其元素的方法。因此,如果你需要一个高精度的结果,希望你在做之前就知道路径的组成

然而,有一种方法可以使用PathMeasure获得具有可配置精度的结果。请尝试以下操作此代码尚未经过测试

void computeExactBounds(RectF bounds, Path path, int accuracy)
{
bool first = true;
// Make sure accuracy defaults to something reasonable
if (accuracy < 2)
accuracy = 100;
PathMeasure pm = new PathMeasure(path, false);
// Step through all the subpaths of this path
while (nextContour())
{
float  pathLen = pm.getLength();
// To compute the bounds, we calculate the position of 'accuracy'
// number of positions along the path.  The higher that accuracy is,
// the more accurate the result, but the longer it will take.
// But note that, in some extreme cases, it is possible that the
// real path bounds may be slightly larger than the bounds that
// this method returns.

float  pos = new float[];
// Initialise bounds to the path start coordinates
pm.getPosTan(0, pos, null);
if (first) {
bounds.set(pos[0], pos[1], pos[0], pos[1]);
first = false;
}
else
bounds.union(pos[0], pos[1]);
// Step through 'accuracy' positions along the path,
// making sure bounds includes all those positions
for (int i=1; i <= accuracy; i++) {
pm.getPosTan(pathLen * i / accuracy, pos, null);
bounds.union(pos[0], pos[1]);
}
}
}

您可以通过使用二进制搜索自适应地拆分来改进此代码子路径分成更小的段(而不是以小的固定增量沿着路径倾斜(。这就是path.approximate()做请参见下文。


如果您使用API 26及更高版本,您将能够通过使用path.approximate()来计算位置来加速此代码。

此代码也未经过测试:(

void computeExactBounds(RectF bounds, Path path, float acceptableError)
{
// A typical value of acceptableError might be 0.5. But if, say,
// your path uses extremely small or large coordinate values, you
// might need to adjust this.  Choose a value that works out as
// equivalent to around half a pixel at the current scale;
float[]  pos = path.approximate(acceptableError);
for (int i = 0; i < pos.length; i += 3)
{
if (i == 0)
bounds.set(pos[i+1], pos[i+2], pos[i+1], pos[i+2]);
else
bounds.union(pos[i+1], pos[i+2]);
}
}

没有任何直接的方法。我的解决方案是在绘图过程中根据Bezier的端点更新RectF。

final RectF mRect = new RectF(999999,999999,-1,-1);
public void updateBounds(final float endPointX, final float endPointY) {
if (mRect.left == 999999) mRect.set(endPointX, endPointY, endPointX, endPointY);
else mRect.union(endPointX, endPointY);
}

然而,该解决方案不考虑StrokeWidth计数,而只考虑虚拟/不可见路径(其"加宽度"等于零(。因此,如果您的需求是">计算有效绘制路径边界";你需要减去/求和">Path.getStrokewidth((";在边界计算期间。

相关内容

最新更新