泊松圆环采样(Poisson-Disc II)

该动画演示了Bridson的Poisson-disc采样算法的工作原理。

红点表示“活动”样本。在每次迭代中,从所有活动样本的集合中随机选择一个。然后,在所选样本周围的环形区域内随机生成多达k个候选样本(显示为空心黑点)。

环面从半径r延伸到2r,其中r是任意两个样本之间的最小允许距离。半径r范围内的现有样本的候选样本将被拒绝;该“排除区”显示为灰色,并用黑线将被拒绝的候选对象与太接近的现有样本连接起来。如果可以接受,则将其添加为新的有效样本。

大小为r /√2的背景网格用于加速每个候选者的距离检查。由于每个单元最多只能包含一个样本,因此仅需要检查固定数量的相邻单元。

如果k个候选者都不可接受,则所选的活动样本将标记为非活动,并且将不再用于生成候选者。不活动的样品以黑色显示。

当没有样本保持活动状态时,算法结束。







public class PoissonDiscSampler {

    private Point[][] mPoints;
    private int mWidth;
    private int mHeight;
    private int mNumCandidates = 10;
    private QuadTree mQuadTree;

    public PoissonDiscSampler(QuadTree quadTree) {

        mPoints = new Point[mWidth][mHeight];
        mQuadTree = quadTree;
        mWidth = mQuadTree.getWidth();
        mHeight = mQuadTree.getHeight();
    }

    public void setNumCandidates(int numCandidates) {

        mNumCandidates = numCandidates;
    }

    public void addNumPoints(int numPointsToAdd) {

        // add a point to the quadtree if it's empty
        if (mQuadTree.getCount() == 0) {
            mQuadTree.set(generateRandomPoint());
            numPointsToAdd--;
        }

        Point candidatePoints[] = new Point[mNumCandidates];
        double furthestDistance;
        double distance;
        int furthestDistanceIndex;

        // this loop iterates once for each point to be added to the quadtree
        for (; numPointsToAdd > 0; numPointsToAdd--) {

            // generate random candidate points that aren't already in the quadtree
            for (int i = 0; i < mNumCandidates; i++) {

                candidatePoints[i] = generateRandomPoint();
                while (mQuadTree.find(candidatePoints[i]) != null) { // while point already exists in quadtree

                    candidatePoints[i] = generateRandomPoint();          // generate a new point to replace it
                }
            }

            // if any of the candidate points we've created share
            // the same coordinates of another candidate point,
            // overwrite one of the duplicate points until all
            // candidate points are unique. -- TODO: TOO SLOW? FIX...?
            for (int j = 0; j < mNumCandidates; j++) {

                for (int k = 0; k < mNumCandidates; k++) {
                    if (j != k) {
                        while (candidatePoints[j].hasCoordinatesOf(candidatePoints[k])) {
                            candidatePoints[j] = generateRandomPoint();
                        }
                    }
                }
            }
            furthestDistance = 0;
            furthestDistanceIndex = 0;

            // for each candidate point:
            //   1. find distance between candidate point and
            //      nearest point in the quadtree
            //   2. if the distance is larger than the distances
            //      for the previous points, save the index of
            //      the candidate point to furthestDistanceIndex

            for (int i = 0; i < mNumCandidates; i++) {

                distance = findDistance(findNearestPointInQuadTree(candidatePoints[i]),
                        candidatePoints[i]);

                if (distance > furthestDistance) {
                    furthestDistance = distance;
                    furthestDistanceIndex = i;
                }
            }

            // add the candidate point that is furthest from the
            // other points in the quadtree to the quadtree
            mQuadTree.set(candidatePoints[furthestDistanceIndex]);
        }
    }

    public QuadTree getQuadTree() {
        return mQuadTree;
    }

    // This grabs one or more points that are near
    // referencePoint in the quadtree.
    // There is no guarantee that the point(s)
    // will be the closest point(s) to referencePoint,
    // but they/it will tend to be in the vicinity.

    public Point[] findPointsUnderParentNodeOf(Point referencePoint) {

        boolean refPointAlreadyInQuadTree = false;

        // if reference point is already in the quadtree,
        // adding it to the quadtree in order to facilitate our search would be redundant.
        // instead, we'll just use the already existing point.
        if (mQuadTree.find(referencePoint) != null)
            refPointAlreadyInQuadTree = true;
        else
            mQuadTree.set(referencePoint);

        Node node = mQuadTree.find(referencePoint);   // find the node containing our reference point.

        // go up a node in the tree until the current
        // node has 2+ points beneath it.
        while (mQuadTree.searchWithin(node).length < 2) {
            node = node.getParent();
        }

        // remove the reference point from quadtree
        // if it wasn't part of the quadtree
        // before this search.
        if (!refPointAlreadyInQuadTree) {
            mQuadTree.remove(referencePoint);
        }

        return mQuadTree.searchWithin(node); // find all points within the node (and its children) and return them
    }

    public Point findNearestPoint(Point referencePoint, Point[] candidatePoints) {

        // set var with a large enough value that any valid
        // distance we calculate is guaranteed to be smaller
        double nearestCandidateDistance = mWidth * mHeight;

        Point nearestCandidatePoint = null;

        for (Point curPoint : candidatePoints) {

            double curCandidateDistance = findDistance(referencePoint, curPoint);

            if (curCandidateDistance < nearestCandidateDistance
                    && !(curPoint.hasCoordinatesOf(referencePoint))) {

                nearestCandidateDistance = curCandidateDistance;
                nearestCandidatePoint = curPoint;
            }
        }

        // in the unlikely event that all of the randomly
        // generated candidate points share the same x,y coords
        // with referencePoint, generate random points until
        // we find one that doesn't.
        if (nearestCandidatePoint == null) {
            nearestCandidatePoint = generateRandomPoint();

            while (nearestCandidatePoint.hasCoordinatesOf(referencePoint)) {
                nearestCandidatePoint = generateRandomPoint();
            }
        }
        return nearestCandidatePoint;
    }

    public Point findNearestPointInQuadTree(Point referencePoint) {

        // grab some points in the vicinity of referencePoint
        Point points[] = findPointsUnderParentNodeOf(referencePoint);

        // pick the one that is closest to referencePoint and find distance
        // between this point and reference point (and multiply distance by 2)
        Point closePoint = findNearestPoint(referencePoint, points);
        double dist = (findDistance(referencePoint, closePoint)) * 2;

        // create a search area around referencePoint using above calculated distance
        double minX = referencePoint.getX() - dist;
        double minY = referencePoint.getY() - dist;
        double maxX = referencePoint.getX() + dist;
        double maxY = referencePoint.getY() + dist;

        // find the nearest points to referencePoint using the above search area
        Point nearestPoints[] = mQuadTree.searchWithin(minX, minY, maxX, maxY);

        return findNearestPoint(referencePoint, nearestPoints);
    }

    // use pythagorean theorum to find the distance between two points
    public double findDistance(Point point1, Point point2) {

        double xDiff = point1.getX() - point2.getX();
        double yDiff = point1.getY() - point2.getY();

        return Math.sqrt((xDiff * xDiff) + (yDiff * yDiff));
    }

    public Point generateRandomPoint() {

        int randX = (int) (Math.random() * mWidth);
        int randY = (int) (Math.random() * mHeight);
        while (randX > mWidth) {
            randX--;
        }
        while (randY > mHeight) {
            randY--;
        }
        Point point = new Point(randX, randY, null);

        return point;
    }

    public Point[] toArray() {
        return mQuadTree.searchWithin(-1, -1, mWidth, mHeight);
    }

}

你可能感兴趣的:(泊松圆环采样(Poisson-Disc II))