【发布时间】:2021-05-22 20:29:33
【问题描述】:
我需要在一个形状内创建线段,而不仅仅是一个视觉模式 - 我需要知道给定边界(形状)内的那些线的开始和结束坐标。我将通过我所拥有的并解释我面临的问题
我有一个由[x, y] 坐标定义的封闭不规则形状(可以有几十条边)
shape = [
[150,10], // x, y
[10,300],
[150,200],
[300,300]
];
我计算并绘制了这个形状的边界框
然后我在画布上绘制我的形状
接下来,我在边界框内投射光线,每条光线之间设置间距。光线从左到右以 1 个像素递增。
每当投射光线到达 RGB 值为100, 255, 100 的像素时,我就知道它已进入形状。如果像素值不是 100, 255, 100,我知道它何时退出形状。因此,我知道我的形状中每条线的开始和结束坐标,如果一条光线多次进入和退出该形状 - 这将生成该光线投射中的所有线段。
在大多数情况下它可以工作,但存在一些问题:
- 非常慢。也许有比投射光线更好的方法?或者也许有办法优化光线逻辑?也许比仅检查 RGB 颜色值更智能?
- 如何在边界框内以不同的角度投射光线?现在它从左到右,但是我如何用以任何指定角度投射的光线填充我的边界框?即:
我不在乎孔或曲线。这些形状都将由直线段组成,内部不会有任何孔。
编辑:对像素 RGB 采样进行了更改以提高性能。
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
lineSpacing = 15;
shape = [
[150,10], // x, y
[10,300],
[150,200],
[300,300]
];
boundingBox = [
[Infinity,Infinity],
[-Infinity,-Infinity]
]
// get bounding box coords
for(var i in shape) {
if(shape[i][0] < boundingBox[0][0]) boundingBox[0][0] = shape[i][0];
if(shape[i][1] < boundingBox[0][1]) boundingBox[0][1] = shape[i][1];
if(shape[i][0] > boundingBox[1][0]) boundingBox[1][0] = shape[i][0];
if(shape[i][1] > boundingBox[1][1]) boundingBox[1][1] = shape[i][1];
}
// display bounding box
ctx.fillStyle = 'rgba(255,0,0,.2)';
ctx.fillRect(boundingBox[0][0], boundingBox[0][1], boundingBox[1][0]-boundingBox[0][0], boundingBox[1][1]-boundingBox[0][1]);
// display shape (boundary)
ctx.beginPath();
ctx.moveTo(shape[0][0], shape[0][1]);
for(var i = 1; i < shape.length; i++) {
ctx.lineTo(shape[i][0], shape[i][1]);
}
ctx.closePath();
ctx.fillStyle = 'rgba(100,255,100,1)';
ctx.fill();
canvasData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
// loop through the shape in vertical slices
for(var i = boundingBox[0][1]+lineSpacing; i <= boundingBox[1][1]; i += lineSpacing) {
// send ray from left to right
for(var j = boundingBox[0][0], start = false; j <= boundingBox[1][0]; j++) {
x = j, y = i;
pixel = y * (canvas.width * 4) + x * 4;
// if pixel is within boundary (shape)
if(canvasData[pixel] == 100 && canvasData[pixel+1] == 255 && canvasData[pixel+2] == 100) {
// arrived at start of boundary
if(start === false) {
start = [x,y]
}
} else {
// arrived at end of boundary
if(start !== false) {
ctx.strokeStyle = 'rgba(0,0,0,1)';
ctx.beginPath();
ctx.moveTo(start[0], start[1]);
ctx.lineTo(x, y);
ctx.closePath();
ctx.stroke();
start = false;
}
}
}
// show entire cast ray for debugging purposes
ctx.strokeStyle = 'rgba(0,0,0,.2)';
ctx.beginPath();
ctx.moveTo(boundingBox[0][0], i);
ctx.lineTo(boundingBox[1][0], i);
ctx.closePath();
ctx.stroke();
}
<canvas id="canvas" width="350" height="350"></canvas>
【问题讨论】:
-
您只需要绘制它还是真的需要知道坐标? CanvasPattern 可以非常轻松地做到这一点,合成也是如此。
-
@Kaiido 我需要边界内路径的坐标(形状),事实上我什至不需要显示光线、形状等 - 它们只是为了可视化问题.稍后我将只使用形状内的光线。
-
用这些射线做什么?这些的图形表示不会有帮助吗?如果是这样,为什么要标记这个问题画布?
-
我将遍历形状内的每条射线,并使用这些坐标作为点来从另一个数据集中采样数据。反过来,采样数据将沿这些矢量线显示。本质上,光线会根据它采样的音频改变颜色。所以我需要样本点的精确坐标以及显示信息,而不仅仅是视觉表示,所有这些都将呈现在同一个 HTML 画布上,因此是画布标签;)
-
我很确定有办法通过合成来完成这一切,但无论如何。所以通过 ypur 逻辑,因为这可能会在 [windows] [mac-os] 或 [linux] 上的 [browser] 中呈现,所有这些标签都应该使用吗?如果您的问题与 canvas API 无关,并且您拒绝基于此的解决方案,则此标签无关紧要。
标签: javascript math geometry