AS3:不规则形状上的随机点

发布于 2024-12-06 07:00:43 字数 230 浏览 1 评论 0原文

我有一个不规则形状的 MovieClip,如下所示:

在此处输入图像描述

我需要在此生成一个随机点形状。

我可以通过在边界框内生成点来使用强力,然后进行测试以查看它们是否位于不规则形状上。但是,我确信有一种更有效的方法来解决这个问题。

在不规则形状上生成随机点的最有效方法是什么?

I have a MovieClip holding an irregular shape such as this one:

enter image description here

I need to generate a random point on this shape.

I can use brute force by generating points within the bounding box and then hitTesting to see if they reside on the irregular shape. However, I'm sure there's a more efficient way to tackle this problem.

What is the most efficient way to generate a random point on an irregular shape?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

‖放下 2024-12-13 07:00:43

您提到了 hitTest,但我认为您的意思是 hitTestPoint()

如果是这样,一个函数会获取您提到的随机点,看起来有点像这样:

function getRandomPointsInClip(target:MovieClip,numPoints:int):Vector.<Point>{
    var points:Vector.<Point> = new Vector.<Point>(numPoints,true);
    var width:Number = target.width,height:Number = target.height;
    for(var i:int =  0; i < numPoints ; i++){
        var point:Point = new Point(target.x+Math.random() * width,target.y+Math.random() * height);
        if(target.hitTestPoint(point.x,point.y,true)) points[i] = point;//is the random coord inside ?
        else i = i-1;//nope, go back one step - > retry above until it is inside
    }
    return points;
}

我在评论中暗示的另一个涉及循环遍历对象的位图数据中的非透明像素。与之前的方法相反,此方法可以确保您不会有太多重复项,但这也意味着您对创建的点数量的控制较少,并且需要额外的内存来创建位图。尽管如此,出于文档目的,这里是函数:

function getGridPointsInClip(target:MovieClip,res:int,offset:Number = 3):Vector.<Point>{
    var points:Vector.<Point> = new Vector.<Point>();
    var x:int,y:int,alpha:int,w:int = int(target.width),h:int = int(target.height);
    var bmd:BitmapData = new BitmapData(w,h,true,0x00FFFFFF);bmd.draw(target);
    var pixels:Vector.<uint> = bmd.getVector(bmd.rect),numPixels:int = w*h;
    for(var i:int = 0; i < numPixels; i+=res) {
        x = i%bmd.width;
        y = int(i/bmd.width);
        alpha = pixels[i] >>> 24;
        if(alpha > 0) points.push(new Point(x+random(-offset,offset),y+random(-offset,offset)));
    }
    return points;
}
function random(from:Number,to:Number):Number {
    if (from >= to) return from;
    var diff:Number = to - from;
    return (Math.random()*diff) + from;
}

这是一个非常基本的测试:

var pts:Vector.<Point> = getRandomPointsInClip(mc,300);
//var pts:Vector.<Point> = getGridPointsInClip(mc,100,4);
for(var i:int = 0 ; i < pts.length; i++) drawCircle(pts[i].x,pts[i].y,3,0x009900);

function getRandomPointsInClip(target:MovieClip,numPoints:int):Vector.<Point>{
    var points:Vector.<Point> = new Vector.<Point>(numPoints,true);
    var width:Number = target.width,height:Number = target.height;
    for(var i:int =  0; i < numPoints ; i++){
        var point:Point = new Point(target.x+Math.random() * width,target.y+Math.random() * height);
        if(target.hitTestPoint(point.x,point.y,true)) points[i] = point;//is the random coord inside ?
        else i = i-1;//nope, go back one step - > retry above until it is inside
    }
    return points;
}
function getGridPointsInClip(target:MovieClip,res:int,offset:Number = 3):Vector.<Point>{
    var points:Vector.<Point> = new Vector.<Point>();
    var x:int,y:int,alpha:int,w:int = int(target.width),h:int = int(target.height);
    var bmd:BitmapData = new BitmapData(w,h,true,0x00FFFFFF);bmd.draw(target);
    var pixels:Vector.<uint> = bmd.getVector(bmd.rect),numPixels:int = w*h;
    for(var i:int = 0; i < numPixels; i+=res) {
        x = i%bmd.width;
        y = int(i/bmd.width);
        alpha = pixels[i] >>> 24;
        if(alpha > 0) points.push(new Point(x+random(-offset,offset),y+random(-offset,offset)));
    }
    return points;
}
function random(from:Number,to:Number):Number {
    if (from >= to) return from;
    var diff:Number = to - from;
    return (Math.random()*diff) + from;
}
function drawCircle(x:Number,y:Number,radius:Number,color:uint):void{
    graphics.lineStyle(1,color);
    graphics.drawCircle(x-radius,y-radius,radius);
}

HTH

You mentioned hitTest, but I assume you meant hitTestPoint().

If so, a function go get the random points you mention, would look a bit like this:

function getRandomPointsInClip(target:MovieClip,numPoints:int):Vector.<Point>{
    var points:Vector.<Point> = new Vector.<Point>(numPoints,true);
    var width:Number = target.width,height:Number = target.height;
    for(var i:int =  0; i < numPoints ; i++){
        var point:Point = new Point(target.x+Math.random() * width,target.y+Math.random() * height);
        if(target.hitTestPoint(point.x,point.y,true)) points[i] = point;//is the random coord inside ?
        else i = i-1;//nope, go back one step - > retry above until it is inside
    }
    return points;
}

The other I hinted at in my comment involves looping through non transparent pixels in a bitmap data of your object. This method would insure you don't have many duplicates, as opposed to the previous method, but it also means, you have less control over the number of points created and there's extra memory used for creating the bitmap. Still, for documentation purposes, here is the function:

function getGridPointsInClip(target:MovieClip,res:int,offset:Number = 3):Vector.<Point>{
    var points:Vector.<Point> = new Vector.<Point>();
    var x:int,y:int,alpha:int,w:int = int(target.width),h:int = int(target.height);
    var bmd:BitmapData = new BitmapData(w,h,true,0x00FFFFFF);bmd.draw(target);
    var pixels:Vector.<uint> = bmd.getVector(bmd.rect),numPixels:int = w*h;
    for(var i:int = 0; i < numPixels; i+=res) {
        x = i%bmd.width;
        y = int(i/bmd.width);
        alpha = pixels[i] >>> 24;
        if(alpha > 0) points.push(new Point(x+random(-offset,offset),y+random(-offset,offset)));
    }
    return points;
}
function random(from:Number,to:Number):Number {
    if (from >= to) return from;
    var diff:Number = to - from;
    return (Math.random()*diff) + from;
}

And here'a very basic test:

var pts:Vector.<Point> = getRandomPointsInClip(mc,300);
//var pts:Vector.<Point> = getGridPointsInClip(mc,100,4);
for(var i:int = 0 ; i < pts.length; i++) drawCircle(pts[i].x,pts[i].y,3,0x009900);

function getRandomPointsInClip(target:MovieClip,numPoints:int):Vector.<Point>{
    var points:Vector.<Point> = new Vector.<Point>(numPoints,true);
    var width:Number = target.width,height:Number = target.height;
    for(var i:int =  0; i < numPoints ; i++){
        var point:Point = new Point(target.x+Math.random() * width,target.y+Math.random() * height);
        if(target.hitTestPoint(point.x,point.y,true)) points[i] = point;//is the random coord inside ?
        else i = i-1;//nope, go back one step - > retry above until it is inside
    }
    return points;
}
function getGridPointsInClip(target:MovieClip,res:int,offset:Number = 3):Vector.<Point>{
    var points:Vector.<Point> = new Vector.<Point>();
    var x:int,y:int,alpha:int,w:int = int(target.width),h:int = int(target.height);
    var bmd:BitmapData = new BitmapData(w,h,true,0x00FFFFFF);bmd.draw(target);
    var pixels:Vector.<uint> = bmd.getVector(bmd.rect),numPixels:int = w*h;
    for(var i:int = 0; i < numPixels; i+=res) {
        x = i%bmd.width;
        y = int(i/bmd.width);
        alpha = pixels[i] >>> 24;
        if(alpha > 0) points.push(new Point(x+random(-offset,offset),y+random(-offset,offset)));
    }
    return points;
}
function random(from:Number,to:Number):Number {
    if (from >= to) return from;
    var diff:Number = to - from;
    return (Math.random()*diff) + from;
}
function drawCircle(x:Number,y:Number,radius:Number,color:uint):void{
    graphics.lineStyle(1,color);
    graphics.drawCircle(x-radius,y-radius,radius);
}

HTH

笨死的猪 2024-12-13 07:00:43

如果您想到一些非斑点状的形状,很明显,检查随机像素,然后重试方法并不是一个真正的好方法。与形状区域相比,边界框区域可能会很大。

抱歉 - 只是绘画,没有矢量图形。

您可以采取的措施来提高效率获取形状的 BitmapData 的向量。它应该包含边界框的所有像素。 更新 - 现在如果我们可以选择一个随机点,如果它不在形状内,则将其从向量中删除,那就太好了。不幸的是,向量只包含像素的颜色,而不包含隐式的位置,并且只有在我们不改变向量的长度的情况下才是正确的。由于我们不需要知道实际颜色,因此可以省略所有透明像素并将内部像素的位置存储为向量中的值。这样我们就不需要为形状的每个像素创建一个新对象(这将非常昂贵!)。

var v:Vector.<uint> shapeBoxBitmap.getVector(shapeBoxBitmap.rect);
var pixelNum:int = v.length;
for(var i:uint = 0; i < pixelNum; i++) {
    if( v[i] && 0xFF000000 == 0) { // transparent pixel, outside off shape
       v.splice(i,1);
    } else {
      v[i] = i;
    }
}

//get random point
var randomPixel:int = v[Math.floor(Math.random()*v.length)];
var point:Point = new Point(randomPixel%shapeBitmap.width,int(randomPixel/shapeBitmap.width));

If you think of some non-blob like shapes, it's clear the check random pixel, try again method isn't really a good way. The bounding box area could be huge compared to the shape area.

sorry - just paint, no vector graphic.

What you could do to improve the effectiveness is getting a vector of the BitmapData of the shape. It should contain all pixels of the bounding box. Update - it would be nice now if we could pick a random point, and remove it from the vector if it isn't inside the shape. Unfortunately the vector only contains the pixels' colour, not the position which is implicit and only correct if we don't change the vector's length. Since we don't need to know the actual colour, we can omit all transparent pixels and store an inside pixel's position as it's value in the vector. This way we don't need to create a new object for each pixel of the shape (that would be quite expensive!).

var v:Vector.<uint> shapeBoxBitmap.getVector(shapeBoxBitmap.rect);
var pixelNum:int = v.length;
for(var i:uint = 0; i < pixelNum; i++) {
    if( v[i] && 0xFF000000 == 0) { // transparent pixel, outside off shape
       v.splice(i,1);
    } else {
      v[i] = i;
    }
}

//get random point
var randomPixel:int = v[Math.floor(Math.random()*v.length)];
var point:Point = new Point(randomPixel%shapeBitmap.width,int(randomPixel/shapeBitmap.width));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文