绘制圆形扇区中的随机点

plotting random points in circle sector

本文关键字:随机 扇区 绘制      更新时间:2023-09-26

我在画布上有一个饼状图,我想在饼的每个扇形中绘制随机点。

我得到了每个扇区的面积。使用圆弧扇形

var arcsector = Math.PI * (2 * sector / total);
var startAngle = (lastend - offset) * (radius/Math.PI);
var endAngle = (lastend + arcsector - offset) * (radius/Math.PI);
var sectorAngle = arcsector * (radius/Math.PI);
var sectorArea = .5 * (sectorAngle*Math.PI/180) * (radius*radius);

如何在该区域内随机绘制点?

饼是圆的一部分,用你的符号,它开始于startAngle,结束于endAngle。

得到一个随机点的最简单的方法是建立一个随机的角度startAngle和endAngle)和一个随机半径,然后你就有了这些线的点:

 var randAngle  = startAngle + Math.random()*( endAngle - startAngle );
 var randRadius = Math.random()*radius;
 var randX = centerX + randRadius * Math.cos(randAngle);
 var randY = centerY + randRadius * Math.sin(randAngle);
 ctx.fillRect ( randX, randY, 1, 1 ) ;

简单的方法是:

  • 在路径
  • 上创建临时圆弧
  • 创建随机点
  • 根据形状对点进行命中测试,并在
  • 内绘制

你可以像这样创建一个临时的弧形路径(根据你的情况进行调整)(不需要描边/填充):

ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.arc(cx, cy, radius, startAngle, endAngle);
ctx.closePath();

然后在边界内创建随机点,或者只是使用一种非常基本的方法(在大多数情况下可能足够快,除非你需要很多点)-与使用基于半径的方法相比,扩展甚至:

var randomX = cx + radius * 2 * Math.random() - radius;
var randomY = cy + radius * 2 * Math.random() - radius;

和最后的hit-test:

if (ctx.isPointInPath(randomX, randomY)) {
    // plot point, count etc.
}

小提琴

在圆弧形状中生成随机点的一种更有效的方法是直接绘制到屏幕外的画布上,而不使用任何边界检查和昂贵的cos/sin操作,最后将其合成在圆弧形状上(或使用圆弧作为剪辑)。

// create off-screen canvas
var ocanvas = document.createElement('canvas');
var octx = ocanvas.getContext('2d');
var d;
d = ocanvas.width = ocanvas.height = 300;
octx.fillStyle = '#fff';
while(count) {
    var randomX = d * Math.random();
    var randomY = d * Math.random();
    octx.fillRect(randomX - 1, randomY - 1, 2, 2);
    count--;
}
// composite random points with main arc    
ctx.globalCompositeOperation = 'source-atop';
ctx.drawImage(ocanvas, 0, 0);
ctx.globalCompositeOperation = 'source-over';

可以通过让屏幕外画布只代表圆弧形状的边界来进一步优化。

小提琴

演示: http://jsfiddle.net/jv6nP/3/

点在边界上是不完美的,因此它们的半径大于零会使它们重叠在饼的其他部分上。这也会导致它们越过黑色边界。

var can = $('#can')[0].getContext('2d'), 
    border=2, 
    x=100, 
    y=75, 
    r=60, 
    sRadius= 0,
    leadAngle=null, 
    points= [], 
    dotRadius=2,
    data = {
        water:[30,'#5CC5FA'],
        earth:[60,'#F0A71F'],
        air:[10,'#26EDE3']
    };

function reDraw(){
    //making border...
    can.beginPath();
    can.arc(x,y,r+border,0,2*Math.PI);
    can.fillStyle='black';
    can.fill();

var newAngle=null;
for (var k in data) { //making piechart..
    leadAngle = (2*Math.PI)*(data[k][0]/100);
    newAngle = sRadius+leadAngle;
    calPoints(sRadius,leadAngle,k);
    can.beginPath();
    can.arc(x,y,r,sRadius,newAngle);
    can.lineTo(x,y);
    can.fillStyle=data[k][1];
    can.fill();
    sRadius= newAngle;
}
//calculating points..
function calPoints(s,e,name) {
    if (name!='water') return;
    var py,px,rAngle,rRad;
    for (var i=0; i<15; i++) {
         rAngle=s+Math.random()*(e);
         rRad = Math.random()*r;
         px = (Math.cos(rAngle) * rRad)+x;
         py = (Math.sin(rAngle) * rRad)+y;
         points.push([px,py]);
    }
}

//plotting dots from data...
points.forEach(function(v){
        can.beginPath();
        can.arc(v[0],v[1],dotRadius,0,2*Math.PI);
        can.fillStyle='fff';
        can.fill();
});
    points=[];
    requestAnimationFrame(reDraw);
}
reDraw();