gpt4 book ai didi

javascript - 如何使用所有给定参数获取椭圆的轴对齐边界框?

转载 作者:行者123 更新时间:2023-12-02 02:27:25 24 4
gpt4 key购买 nike

void ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle [, anticlockwise]);

Canvas 上下文 2D API ellipse() 方法创建一个以 (x, y) 为中心、半径为 radiusX 和 radiusY 的椭圆弧。路径从 startAngle 开始到 endAngle 结束,并按逆时针给定的方向行进。

如何获取给定参数x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise 的椭圆轴对齐边界框?

最佳答案

两种解决方案

这个答案包含两个精确解,它不是近似值。

解决方案是

  1. boundEllipseAll将找到完整椭圆的边界。如果比完整解决方案复杂得多,但您需要确保 x 半径大于 y 半径(例如,将椭圆旋转 90 度并交换 x、y 半径)

  2. boundEllipse将找到一段椭圆的边界。它适用于所有省略号,但我没有包括 CCW 标志。要获得 CCW 椭圆的边界,请交换起始 Angular 和结束 Angular 。

    它的工作原理是首先找到起点和终点的 x、y 坐标,计算沿每个轴的最小值和最大值。然后计算极值 Angular ,首先是 x 轴极值,然后是 y 轴极值。

    如果极值 Angular 介于起始 Angular 和结束 Angular 之间,则计算该 Angular x、y 位置,并根据最小和最大范围测试点。

有很大的优化空间,因为许多点只需要 x 或 y 部分,以及函数 extrema 中的内部 while 循环如果正在处理的轴的最小值和最大值发生变化,可以提前退出。

例子

该示例确保我没有犯任何错误,并使用第二种解决方案,通过移动开始和结束 Angular 、旋转和 y 轴半径来动画椭圆。绘制边界框及其边界的椭圆。

2022 年 4 月更新

示例显示使用两个完整椭圆 boundEllipseAll和椭圆段 boundEllipse .

注意 boundEllipse仅适用于 endAngle 的椭圆段n 和 startAngle m 符合规则 {m <= n <= m + 2Pi}

修复了 boundEllipse 中的错误当 endAngle == startAngle + 2 * Math.PI 时没有显示完整的椭圆

const ctx = canvas.getContext("2d");
const W = 200, H= 180;
const TAU = Math.PI * 2;
const ellipse = {
x: W / 2,
y: H / 2,
rx: W / 3,
ry: W / 3,
rotate: 0,
startAng: 0,
endAng: Math.PI * 2,
dir: false,
};
function boundEllipseAll({x, y, rx, ry, rotate}) {
const xAx = Math.cos(rotate);
const xAy = Math.sin(rotate);
const w = ((rx * xAx) ** 2 + (ry * xAy) ** 2) ** 0.5;
const h = ((rx * xAy) ** 2 + (ry * xAx) ** 2) ** 0.5;
return {x: -w + x, y: -h + y, w: w * 2, h: h * 2};
}
function boundEllipse({x, y, rx, ry, rotate, startAng, endAng}) {
const normalizeAng = ang => (ang % TAU + TAU) % TAU;
const getPoint = ang => {
const cA = Math.cos(ang);
const sA = Math.sin(ang);
return [cA * rx * xAx - sA * ry * xAy, cA * rx * xAy + sA * ry * xAx];
}
const extrema = a => { // from angle
var i = 0;
while(i < 4) {
const ang = normalizeAng(a + Math.PI * (i / 2));
if ((ang > startAng && ang < endAng) || (ang + TAU > startAng && ang + TAU < endAng)) {
const [xx, yy] = getPoint(ang);
minX = Math.min(minX, xx);
maxX = Math.max(maxX, xx);
minY = Math.min(minY, yy);
maxY = Math.max(maxY, yy);
}
i ++;
}
}
// UPDATE bug fix (1) for full ellipse
const checkFull = startAng !== endAng; // Update fix (1)
startAng = normalizeAng(startAng);
endAng = normalizeAng(endAng);
(checkFull && startAng === endAng) && (endAng += TAU); // Update fix (1)
const xAx = Math.cos(rotate);
const xAy = Math.sin(rotate);
endAng += endAng < startAng ? TAU : 0;
const [sx, sy] = getPoint(startAng);
const [ex, ey] = getPoint(endAng);
var minX = Math.min(sx, ex);
var maxX = Math.max(sx, ex);
var minY = Math.min(sy, ey);
var maxY = Math.max(sy, ey);
extrema(-Math.atan((ry * xAy) / (rx * xAx))); // Add x Axis extremas
extrema(-Math.atan((rx * xAy) / (ry * xAx))); // Add y Axis extremas
return {x: minX + x, y: minY + y, w: maxX - minX, h: maxY - minY};
}
function drawExtent({x,y,w,h}) {
ctx.moveTo(x,y);
ctx.rect(x, y, w, h);
}
function drawEllipse({x, y, rx, ry, rotate, startAng, endAng, dir}) {
ctx.ellipse(x, y, rx, ry, rotate, startAng, endAng, dir);
}
function drawFullEllipse({x, y, rx, ry, rotate, dir}) {
ctx.ellipse(x, y, rx, ry, rotate, 0, TAU, dir);
}
mainLoop(0);
function mainLoop(time) {
ctx.clearRect(0, 0, W, H);

// Animate ellipse
ellipse.startAng = time / 1000;
ellipse.endAng = time / 2000;
ellipse.rotate = Math.cos(time / 14000) * Math.PI * 2;
ellipse.ry = Math.cos(time / 6000) * (W / 4 - 10) + (W / 4);

// Draw full ellipse and bounding box.
ctx.strokeStyle = "#F008";
ctx.beginPath();
drawFullEllipse(ellipse);
drawExtent(boundEllipseAll(ellipse));
ctx.stroke();

// Draw ellipse segment and bounding box.
ctx.strokeStyle = "#0008";
ctx.beginPath();
drawEllipse(ellipse);
drawExtent(boundEllipse(ellipse));
ctx.stroke();

requestAnimationFrame(mainLoop)
}
canvas { border: 1px solid black }
<canvas id="canvas" width="200" height="180"></canvas>

关于javascript - 如何使用所有给定参数获取椭圆的轴对齐边界框?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65433347/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com