Initial commit
This commit is contained in:
commit
7cdac8ded9
45 changed files with 7275 additions and 0 deletions
448
BlueNoise/BLUE_NOISE.cpp
Normal file
448
BlueNoise/BLUE_NOISE.cpp
Normal file
|
|
@ -0,0 +1,448 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// File : BLUE_NOISE.h
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// LumosQuad - A Lightning Generator
|
||||
// Copyright 2007
|
||||
// The University of North Carolina at Chapel Hill
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// The University of North Carolina at Chapel Hill makes no representations
|
||||
// about the suitability of this software for any purpose. It is provided
|
||||
// "as is" without express or implied warranty.
|
||||
//
|
||||
// Permission to use, copy, modify and distribute this software and its
|
||||
// documentation for educational, research and non-profit purposes, without
|
||||
// fee, and without a written agreement is hereby granted, provided that the
|
||||
// above copyright notice and the following three paragraphs appear in all
|
||||
// copies.
|
||||
//
|
||||
// THE UNIVERSITY OF NORTH CAROLINA SPECIFICALLY DISCLAIM ANY WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
|
||||
// "AS IS" BASIS, AND THE UNIVERSITY OF NORTH CAROLINA HAS NO OBLIGATION TO
|
||||
// PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
//
|
||||
// Please send questions and comments about LumosQuad to kim@cs.unc.edu.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// This program uses OpenEXR, which has the following restrictions:
|
||||
//
|
||||
// Copyright (c) 2002, Industrial Light & Magic, a division of Lucas
|
||||
// Digital Ltd. LLC
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Industrial Light & Magic nor the names of
|
||||
// its contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// This class is a very thin wrapper to Daniel Dunbar's blue noise generator.
|
||||
// With the exception of BLUE_NOISE.h and BLUE_NOISE.cpp, the other files
|
||||
// in this directory are unmodified copies of his code.
|
||||
//
|
||||
// For the original, untainted code, see:
|
||||
// http://www.cs.virginia.edu/~gfx/pubs/antimony/
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// $Id: PDSampling.cpp,v 1.12 2006/07/11 16:45:22 zr Exp $
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <cmath>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "BLUE_NOISE.h"
|
||||
#include "RangeList.h"
|
||||
#include "ScallopedSector.h"
|
||||
#include "WeightedDiscretePDF.h"
|
||||
|
||||
typedef std::vector<int> IntVector;
|
||||
|
||||
///
|
||||
|
||||
BLUE_NOISE::BLUE_NOISE(float _radius, bool _isTiled, bool usesGrid) :
|
||||
m_rng(123456),
|
||||
radius(_radius),
|
||||
isTiled(_isTiled)
|
||||
{
|
||||
if (usesGrid) {
|
||||
// grid size is chosen so that 4*radius search only
|
||||
// requires searching adjacent cells, this also
|
||||
// determines max points per cell
|
||||
m_gridSize = (int) ceil(2./(4.*_radius));
|
||||
if (m_gridSize<2) m_gridSize = 2;
|
||||
|
||||
m_gridCellSize = 2.0f/m_gridSize;
|
||||
m_grid = new int[m_gridSize*m_gridSize][kMaxPointsPerCell];
|
||||
|
||||
for (int y=0; y<m_gridSize; y++) {
|
||||
for (int x=0; x<m_gridSize; x++) {
|
||||
for (int k=0; k<kMaxPointsPerCell; k++) {
|
||||
m_grid[y*m_gridSize + x][k] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_gridSize = 0;
|
||||
m_gridCellSize = 0;
|
||||
m_grid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool BLUE_NOISE::pointInDomain(Vec2 &a)
|
||||
{
|
||||
return -1<=a.x && -1<=a.y && 1>=a.x && 1>=a.y;
|
||||
}
|
||||
|
||||
Vec2 BLUE_NOISE::randomPoint()
|
||||
{
|
||||
return Vec2(2*m_rng.getFloatL()-1, 2*m_rng.getFloatL()-1);
|
||||
}
|
||||
|
||||
Vec2 BLUE_NOISE::getTiled(Vec2 v)
|
||||
{
|
||||
float x = v.x, y = v.y;
|
||||
|
||||
if (isTiled) {
|
||||
if (x<-1) x += 2;
|
||||
else if (x>1) x -= 2;
|
||||
|
||||
if (y<-1) y += 2;
|
||||
else if (y>1) y -= 2;
|
||||
}
|
||||
|
||||
return Vec2(x,y);
|
||||
}
|
||||
|
||||
void BLUE_NOISE::getGridXY(Vec2 &v, int *gx_out, int *gy_out)
|
||||
{
|
||||
int gx = *gx_out = (int) floor(.5*(v.x + 1)*m_gridSize);
|
||||
int gy = *gy_out = (int) floor(.5*(v.y + 1)*m_gridSize);
|
||||
if (gx<0 || gx>=m_gridSize || gy<0 || gy>=m_gridSize) {
|
||||
printf("Internal error, point outside grid was generated, ignoring.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void BLUE_NOISE::addPoint(Vec2 pt)
|
||||
{
|
||||
int i, gx, gy, *cell;
|
||||
|
||||
points.push_back(pt);
|
||||
|
||||
if (m_grid) {
|
||||
getGridXY(pt, &gx, &gy);
|
||||
cell = m_grid[gy*m_gridSize + gx];
|
||||
for (i=0; i<kMaxPointsPerCell; i++) {
|
||||
if (cell[i]==-1) {
|
||||
cell[i] = (int) points.size()-1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i==kMaxPointsPerCell) {
|
||||
printf("Internal error, overflowed max points per grid cell. Exiting.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int BLUE_NOISE::findNeighbors(Vec2 &pt, float distance)
|
||||
{
|
||||
if (!m_grid) {
|
||||
printf("Internal error, sampler cannot search without grid.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
float distanceSqrd = distance*distance;
|
||||
int i, j, k, gx, gy, N = (int) ceil(distance/m_gridCellSize);
|
||||
if (N>(m_gridSize>>1)) N = m_gridSize>>1;
|
||||
|
||||
m_neighbors.clear();
|
||||
getGridXY(pt, &gx, &gy);
|
||||
for (j=-N; j<=N; j++) {
|
||||
for (i=-N; i<=N; i++) {
|
||||
int cx = (gx+i+m_gridSize)%m_gridSize;
|
||||
int cy = (gy+j+m_gridSize)%m_gridSize;
|
||||
int *cell = m_grid[cy*m_gridSize + cx];
|
||||
|
||||
for (k=0; k<kMaxPointsPerCell; k++) {
|
||||
if (cell[k]==-1) {
|
||||
break;
|
||||
} else {
|
||||
if (getDistanceSquared(pt, points[cell[k]])<distanceSqrd)
|
||||
m_neighbors.push_back(cell[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (int) m_neighbors.size();
|
||||
}
|
||||
|
||||
float BLUE_NOISE::findClosestNeighbor(Vec2 &pt, float distance)
|
||||
{
|
||||
if (!m_grid) {
|
||||
printf("Internal error, sampler cannot search without grid.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
float closestSqrd = distance*distance;
|
||||
int i, j, k, gx, gy, N = (int) ceil(distance/m_gridCellSize);
|
||||
if (N>(m_gridSize>>1)) N = m_gridSize>>1;
|
||||
|
||||
getGridXY(pt, &gx, &gy);
|
||||
for (j=-N; j<=N; j++) {
|
||||
for (i=-N; i<=N; i++) {
|
||||
int cx = (gx+i+m_gridSize)%m_gridSize;
|
||||
int cy = (gy+j+m_gridSize)%m_gridSize;
|
||||
int *cell = m_grid[cy*m_gridSize + cx];
|
||||
|
||||
for (k=0; k<kMaxPointsPerCell; k++) {
|
||||
if (cell[k]==-1) {
|
||||
break;
|
||||
} else {
|
||||
float d = getDistanceSquared(pt, points[cell[k]]);
|
||||
|
||||
if (d<closestSqrd)
|
||||
closestSqrd = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sqrt(closestSqrd);
|
||||
}
|
||||
|
||||
void BLUE_NOISE::findNeighborRanges(int index, RangeList &rl)
|
||||
{
|
||||
if (!m_grid) {
|
||||
printf("Internal error, sampler cannot search without grid.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Vec2 &candidate = points[index];
|
||||
float rangeSqrd = 4*4*radius*radius;
|
||||
int i, j, k, gx, gy, N = (int) ceil(4*radius/m_gridCellSize);
|
||||
if (N>(m_gridSize>>1)) N = m_gridSize>>1;
|
||||
|
||||
getGridXY(candidate, &gx, &gy);
|
||||
|
||||
int xSide = (candidate.x - (-1 + gx*m_gridCellSize))>m_gridCellSize*.5;
|
||||
int ySide = (candidate.y - (-1 + gy*m_gridCellSize))>m_gridCellSize*.5;
|
||||
int iy = 1;
|
||||
for (j=-N; j<=N; j++) {
|
||||
int ix = 1;
|
||||
|
||||
if (j==0) iy = ySide;
|
||||
else if (j==1) iy = 0;
|
||||
|
||||
for (i=-N; i<=N; i++) {
|
||||
if (i==0) ix = xSide;
|
||||
else if (i==1) ix = 0;
|
||||
|
||||
// offset to closest cell point
|
||||
float dx = candidate.x - (-1 + (gx+i+ix)*m_gridCellSize);
|
||||
float dy = candidate.y - (-1 + (gy+j+iy)*m_gridCellSize);
|
||||
|
||||
if (dx*dx+dy*dy<rangeSqrd) {
|
||||
int cx = (gx+i+m_gridSize)%m_gridSize;
|
||||
int cy = (gy+j+m_gridSize)%m_gridSize;
|
||||
int *cell = m_grid[cy*m_gridSize + cx];
|
||||
|
||||
for (k=0; k<kMaxPointsPerCell; k++) {
|
||||
if (cell[k]==-1) {
|
||||
break;
|
||||
} else if (cell[k]!=index) {
|
||||
Vec2 &pt = points[cell[k]];
|
||||
Vec2 v = getTiled(pt-candidate);
|
||||
float distSqrd = v.x*v.x + v.y*v.y;
|
||||
|
||||
if (distSqrd<rangeSqrd) {
|
||||
float dist = sqrt(distSqrd);
|
||||
float angle = atan2(v.y,v.x);
|
||||
float theta = acos(.25f*dist/radius);
|
||||
|
||||
rl.subtract(angle-theta, angle+theta);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BLUE_NOISE::maximize()
|
||||
{
|
||||
RangeList rl(0,0);
|
||||
int i, N = (int) points.size();
|
||||
|
||||
for (i=0; i<N; i++) {
|
||||
Vec2 &candidate = points[i];
|
||||
|
||||
rl.reset(0, (float) M_PI*2);
|
||||
findNeighborRanges(i, rl);
|
||||
while (rl.numRanges) {
|
||||
RangeEntry &re = rl.ranges[m_rng.getInt31()%rl.numRanges];
|
||||
float angle = re.min + (re.max-re.min)*m_rng.getFloatL();
|
||||
Vec2 pt = getTiled(Vec2(candidate.x + cos(angle)*2*radius,
|
||||
candidate.y + sin(angle)*2*radius));
|
||||
|
||||
addPoint(pt);
|
||||
rl.subtract(angle - (float) M_PI/3, angle + (float) M_PI/3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BLUE_NOISE::relax()
|
||||
{
|
||||
FILE *tmp = fopen("relaxTmpIn.txt","w");
|
||||
int dim, numVerts, numFaces;
|
||||
Vec2 *verts = 0;
|
||||
int numPoints = (int) points.size();
|
||||
|
||||
// will overwrite later
|
||||
fprintf(tmp, "2 \n");
|
||||
for (int i=0; i<(int) points.size(); i++) {
|
||||
Vec2 &pt = points[i];
|
||||
fprintf(tmp, "%f %f\n", pt.x, pt.y);
|
||||
}
|
||||
for (int y=-1; y<=1; y++) {
|
||||
for (int x=-1; x<=1; x++) {
|
||||
if (x || y) {
|
||||
for (int i=0; i<(int) points.size(); i++) {
|
||||
Vec2 &pt = points[i];
|
||||
if (fabs(pt.x+x*2)-1<radius*4 || fabs(pt.y+y*2)-1<radius*4) {
|
||||
fprintf(tmp, "%f %f\n", pt.x+x*2, pt.y+y*2);
|
||||
numPoints++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fseek(tmp, 0, 0);
|
||||
fprintf(tmp, "2 %d", numPoints);
|
||||
fclose(tmp);
|
||||
|
||||
tmp = fopen("relaxTmpOut.txt", "w");
|
||||
fclose(tmp);
|
||||
system("qvoronoi p FN < relaxTmpIn.txt > relaxTmpOut.txt");
|
||||
|
||||
tmp = fopen("relaxTmpOut.txt", "r");
|
||||
fscanf(tmp, "%d\n%d\n", &dim, &numVerts);
|
||||
|
||||
if (dim!=2) {
|
||||
printf("Error calling out to qvoronoi, skipping relaxation.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
verts = new Vec2[numVerts];
|
||||
for (int i=0; i<numVerts; i++) {
|
||||
fscanf(tmp, "%f %f\n", &verts[i].x, &verts[i].y);
|
||||
}
|
||||
|
||||
fscanf(tmp, "%d\n", &numFaces);
|
||||
|
||||
for (int i=0; i<(int) points.size(); i++) {
|
||||
Vec2 center(0,0);
|
||||
int N, skip=0;
|
||||
|
||||
fscanf(tmp, "%d", &N);
|
||||
for (int j=0; j<N; j++) {
|
||||
int index;
|
||||
|
||||
fscanf(tmp, "%d", &index);
|
||||
if (index<0) {
|
||||
skip = 1;
|
||||
} else {
|
||||
center += verts[index];
|
||||
}
|
||||
}
|
||||
|
||||
if (!skip) {
|
||||
center *= (1.0f/N);
|
||||
points[i] = getTiled(center);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
if (verts) delete verts;
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
typedef std::map<int, ScallopedRegion*> RegionMap;
|
||||
|
||||
void BLUE_NOISE::complete()
|
||||
{
|
||||
RangeList rl(0,0);
|
||||
IntVector candidates;
|
||||
|
||||
addPoint(randomPoint());
|
||||
candidates.push_back((int) points.size()-1);
|
||||
|
||||
while (candidates.size()) {
|
||||
int c = m_rng.getInt32()%candidates.size();
|
||||
int index = candidates[c];
|
||||
Vec2 candidate = points[index];
|
||||
candidates[c] = candidates[candidates.size()-1];
|
||||
candidates.pop_back();
|
||||
|
||||
rl.reset(0, (float) M_PI*2);
|
||||
findNeighborRanges(index, rl);
|
||||
while (rl.numRanges) {
|
||||
RangeEntry &re = rl.ranges[m_rng.getInt32()%rl.numRanges];
|
||||
float angle = re.min + (re.max-re.min)*m_rng.getFloatL();
|
||||
Vec2 pt = getTiled(Vec2(candidate.x + cos(angle)*2*radius,
|
||||
candidate.y + sin(angle)*2*radius));
|
||||
|
||||
addPoint(pt);
|
||||
candidates.push_back((int) points.size()-1);
|
||||
|
||||
rl.subtract(angle - (float) M_PI/3, angle + (float) M_PI/3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BLUE_NOISE::writeToBool(bool* noise, int size)
|
||||
{
|
||||
// wipe
|
||||
int index = 0;
|
||||
for (index = 0; index < size * size; index++)
|
||||
noise[index] = false;
|
||||
|
||||
for (int x = 0; x < points.size(); x++)
|
||||
{
|
||||
int i = (points[x].x + 1.0f) * 0.5f * size;
|
||||
int j = (points[x].y + 1.0f) * 0.5f * size;
|
||||
index = i + j * size;
|
||||
noise[index] = true;
|
||||
}
|
||||
}
|
||||
183
BlueNoise/BLUE_NOISE.h
Normal file
183
BlueNoise/BLUE_NOISE.h
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// File : BLUE_NOISE.h
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// LumosQuad - A Lightning Generator
|
||||
// Copyright 2007
|
||||
// The University of North Carolina at Chapel Hill
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// The University of North Carolina at Chapel Hill makes no representations
|
||||
// about the suitability of this software for any purpose. It is provided
|
||||
// "as is" without express or implied warranty.
|
||||
//
|
||||
// Permission to use, copy, modify and distribute this software and its
|
||||
// documentation for educational, research and non-profit purposes, without
|
||||
// fee, and without a written agreement is hereby granted, provided that the
|
||||
// above copyright notice and the following three paragraphs appear in all
|
||||
// copies.
|
||||
//
|
||||
// THE UNIVERSITY OF NORTH CAROLINA SPECIFICALLY DISCLAIM ANY WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
|
||||
// "AS IS" BASIS, AND THE UNIVERSITY OF NORTH CAROLINA HAS NO OBLIGATION TO
|
||||
// PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
//
|
||||
// Please send questions and comments about LumosQuad to kim@cs.unc.edu.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// This program uses OpenEXR, which has the following restrictions:
|
||||
//
|
||||
// Copyright (c) 2002, Industrial Light & Magic, a division of Lucas
|
||||
// Digital Ltd. LLC
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Industrial Light & Magic nor the names of
|
||||
// its contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// This class is a very thin wrapper to Daniel Dunbar's blue noise generator.
|
||||
// With the exception of BLUE_NOISE.h and BLUE_NOISE.cpp, the other files
|
||||
// in this directory are unmodified copies of his code.
|
||||
//
|
||||
// For the original, untainted code, see:
|
||||
// http://www.cs.virginia.edu/~gfx/pubs/antimony/
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// $Id: PDSampling.h,v 1.6 2006/07/06 23:13:18 zr Exp $
|
||||
|
||||
#include "RNG.h"
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
#define kMaxPointsPerCell 9
|
||||
|
||||
class RangeList;
|
||||
class ScallopedRegion;
|
||||
|
||||
class Vec2 {
|
||||
public:
|
||||
Vec2() {};
|
||||
Vec2(float _x, float _y) : x(_x), y(_y) {};
|
||||
|
||||
float x,y;
|
||||
|
||||
float length() { return sqrt(x*x + y*y); }
|
||||
|
||||
bool operator ==(const Vec2 &b) const { return x==b.x && y==b.y; }
|
||||
Vec2 operator +(Vec2 b) { return Vec2(x+b.x, y+b.y); }
|
||||
Vec2 operator -(Vec2 b) { return Vec2(x-b.x, y-b.y); }
|
||||
Vec2 operator *(Vec2 b) { return Vec2(x*b.x, y*b.y); }
|
||||
Vec2 operator /(Vec2 b) { return Vec2(x/b.x, y*b.y); }
|
||||
|
||||
Vec2 operator +(float n) { return Vec2(x+n, y+n); }
|
||||
Vec2 operator -(float n) { return Vec2(x-n, y-n); }
|
||||
Vec2 operator *(float n) { return Vec2(x*n, y*n); }
|
||||
Vec2 operator /(float n) { return Vec2(x/n, y*n); }
|
||||
|
||||
Vec2 &operator +=(Vec2 b) { x+=b.x; y+=b.y; return *this; }
|
||||
Vec2 &operator -=(Vec2 b) { x-=b.x; y-=b.y; return *this; }
|
||||
Vec2 &operator *=(Vec2 b) { x*=b.x; y*=b.y; return *this; }
|
||||
Vec2 &operator /=(Vec2 b) { x/=b.x; y/=b.y; return *this; }
|
||||
|
||||
Vec2 &operator +=(float n) { x+=n; y+=n; return *this; }
|
||||
Vec2 &operator -=(float n) { x-=n; y-=n; return *this; }
|
||||
Vec2 &operator *=(float n) { x*=n; y*=n; return *this; }
|
||||
Vec2 &operator /=(float n) { x/=n; y/=n; return *this; }
|
||||
};
|
||||
|
||||
/// \brief Daniel Dunbar's blue noise generator
|
||||
///
|
||||
/// The original code has been modified so that the 'boundary sampling'
|
||||
/// method is the only one available.
|
||||
class BLUE_NOISE {
|
||||
protected:
|
||||
RNG m_rng;
|
||||
std::vector<int> m_neighbors;
|
||||
|
||||
int (*m_grid)[kMaxPointsPerCell];
|
||||
int m_gridSize;
|
||||
float m_gridCellSize;
|
||||
|
||||
public:
|
||||
std::vector<Vec2> points;
|
||||
float radius;
|
||||
bool isTiled;
|
||||
|
||||
public:
|
||||
BLUE_NOISE(float radius, bool isTiled=true, bool usesGrid=true);
|
||||
virtual ~BLUE_NOISE() { };
|
||||
|
||||
//
|
||||
|
||||
bool pointInDomain(Vec2 &a);
|
||||
|
||||
// return shortest distance between _a_
|
||||
// and _b_ (accounting for tiling)
|
||||
float getDistanceSquared(Vec2 &a, Vec2 &b) { Vec2 v = getTiled(b-a); return v.x*v.x + v.y*v.y; }
|
||||
float getDistance(Vec2 &a, Vec2 &b) { return sqrt(getDistanceSquared(a, b)); }
|
||||
|
||||
// generate a random point in square
|
||||
Vec2 randomPoint();
|
||||
|
||||
// return tiled coordinates of _v_
|
||||
Vec2 getTiled(Vec2 v);
|
||||
|
||||
// return grid x,y for point
|
||||
void getGridXY(Vec2 &v, int *gx_out, int *gy_out);
|
||||
|
||||
// add _pt_ to point list and grid
|
||||
void addPoint(Vec2 pt);
|
||||
|
||||
// populate m_neighbors with list of
|
||||
// all points within _radius_ of _pt_
|
||||
// and return number of such points
|
||||
int findNeighbors(Vec2 &pt, float radius);
|
||||
|
||||
// return distance to closest neighbor within _radius_
|
||||
float findClosestNeighbor(Vec2 &pt, float radius);
|
||||
|
||||
// find available angle ranges on boundary for candidate
|
||||
// by subtracting occluded neighbor ranges from _rl_
|
||||
void findNeighborRanges(int index, RangeList &rl);
|
||||
|
||||
// extend point set by boundary sampling until domain is
|
||||
// full
|
||||
void maximize();
|
||||
|
||||
// apply one step of Lloyd relaxation
|
||||
void relax();
|
||||
|
||||
void complete();
|
||||
|
||||
void writeToBool(bool* noise, int size);
|
||||
};
|
||||
7
BlueNoise/LICENSE.txt
Normal file
7
BlueNoise/LICENSE.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
This code is released into the public domain. You can do whatever
|
||||
you want with it.
|
||||
|
||||
I do ask that you respect the authorship and credit myself (Daniel
|
||||
Dunbar) when referencing the code. Additionally, if you use the
|
||||
code in an interesting or integral manner I would like to hear
|
||||
about it.
|
||||
101
BlueNoise/README.txt
Normal file
101
BlueNoise/README.txt
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
PDSample - Poisson-Disk sample set generation
|
||||
Daniel Dunbar, daniel@zuster.org
|
||||
----
|
||||
|
||||
|
||||
Overview
|
||||
--
|
||||
PDSample generates Poisson-disk sampling sets in the domain [-1,1]^2
|
||||
using a variety of methods. See "A Spatial Data Structure for Fast
|
||||
Poisson-Disk Sample Generation", in Proc' of SIGGRAPH 2006 for more
|
||||
information.
|
||||
|
||||
|
||||
Building
|
||||
---
|
||||
The code should be portable to any platform with 32-bit float's and int's.
|
||||
|
||||
Windows: There is an included PDSample.sln for MSVS version 7.
|
||||
Unix: Type 'make' and hope for the best.
|
||||
|
||||
|
||||
Usage
|
||||
---
|
||||
PDSample [-m] [-t] [-r <relax count=0>] [-M <multiplier=1>]
|
||||
[-N <minMaxThrows=1000>] <method> <radius> <output>
|
||||
|
||||
Options
|
||||
--
|
||||
o -t
|
||||
Uses tiled (toroidal) domain for supporting samplers. The resulting point
|
||||
set will be suitable for tiling repeatedly in the x and y directions.
|
||||
|
||||
o -m
|
||||
Maximize the resulting point set. For samplers which do not already produce
|
||||
a maximal point set then this will use the Boundary sampling method to
|
||||
ensure the resulting point set is maximal.
|
||||
|
||||
o -r <relax count>
|
||||
Apply the specified number of relaxations to the resulting point set. This
|
||||
requires that qvoronoi be in the path.
|
||||
|
||||
o -M <multiplier>
|
||||
For DartThrowing and BestCandidate methods this determines the factor to
|
||||
multiply the current number of points by to determine how many samples to
|
||||
take before exiting (DartThrowing) or accepting the best candidate
|
||||
(BestCandidate).
|
||||
|
||||
o -N <minMaxThrows>
|
||||
This specifies a minimum number of samples that will be taken for the
|
||||
DartThrowing sampler. See below.
|
||||
|
||||
|
||||
Available Samplers (for method argument)
|
||||
--
|
||||
o DartThrowing
|
||||
Standard dart throwing. On each iteration the DartThrowing sampler will
|
||||
try min(N*multiplier,minMaxThrows) samples before termination. Note that
|
||||
for regular dart throwing where simply a maximum number of throws is used
|
||||
to determine the termination point, the multiplier should be set to 0.
|
||||
|
||||
o BestCandidate
|
||||
Mitchell's Best Candidate algorithm. Uses the multiplier argument.
|
||||
|
||||
o Boundary
|
||||
Dart throwing by maximizing boundaries.
|
||||
|
||||
o Pure
|
||||
Dart throwing using scalloped sectors.
|
||||
o LinearPure
|
||||
Dart throwing using scalloped sectors but without sampling regions according
|
||||
to their probability of being hit.
|
||||
|
||||
o Penrose
|
||||
Ostromoukhov et al.'s sampling method using their quasisampler_prototype.h
|
||||
|
||||
o Uniform
|
||||
Random point generation. The number of samples to take is calculated as
|
||||
.75/radius^2 to approximately match the density of Poisson-disk sampling.
|
||||
|
||||
|
||||
Output format
|
||||
--
|
||||
Point sets are output in a trivial binary format. The format is not intended
|
||||
for distribution and does not encode the endianness of the generating platform.
|
||||
|
||||
The format matches the pseudo-C struct below:
|
||||
struct {
|
||||
int N; // number of points
|
||||
float t; // generation time
|
||||
float r; // radius used in generation
|
||||
int isTiled; // flag for if the set is tileable
|
||||
float points[N][2];
|
||||
};
|
||||
|
||||
|
||||
Acknowledgments
|
||||
--
|
||||
Thanks to Ares Lagae for comments on a preliminary release of the code,
|
||||
Ostromoukhov et al. for making available their quasisampler implementation,
|
||||
as well as Takuji Nishimura and Makoto Matsumoto for their Mersenne
|
||||
Twister random number generator.
|
||||
174
BlueNoise/RNG.cpp
Normal file
174
BlueNoise/RNG.cpp
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
A C-program for MT19937, with initialization improved 2002/1/26.
|
||||
Coded by Takuji Nishimura and Makoto Matsumoto.
|
||||
Modified to be a C++ class by Daniel Dunbar.
|
||||
|
||||
Before using, initialize the state by using init_genrand(seed)
|
||||
or init_by_array(init_key, key_length).
|
||||
|
||||
Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of its contributors may not be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
Any feedback is very welcome.
|
||||
http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
|
||||
email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
|
||||
*/
|
||||
|
||||
#include "RNG.h"
|
||||
|
||||
/* initializes mt[N] with a seed */
|
||||
RNG::RNG(unsigned long s)
|
||||
{
|
||||
seed(s);
|
||||
}
|
||||
|
||||
/* initialize by an array with array-length */
|
||||
/* init_key is the array for initializing keys */
|
||||
/* key_length is its length */
|
||||
/* slight change for C++, 2004/2/26 */
|
||||
RNG::RNG(unsigned long init_key[], int key_length)
|
||||
{
|
||||
int i, j, k;
|
||||
seed(19650218UL);
|
||||
i=1; j=0;
|
||||
k = (N>key_length ? N : key_length);
|
||||
for (; k; k--) {
|
||||
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL))
|
||||
+ init_key[j] + j; /* non linear */
|
||||
mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
|
||||
i++; j++;
|
||||
if (i>=N) { mt[0] = mt[N-1]; i=1; }
|
||||
if (j>=key_length) j=0;
|
||||
}
|
||||
for (k=N-1; k; k--) {
|
||||
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
|
||||
- i; /* non linear */
|
||||
mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
|
||||
i++;
|
||||
if (i>=N) { mt[0] = mt[N-1]; i=1; }
|
||||
}
|
||||
|
||||
mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
|
||||
}
|
||||
|
||||
void RNG::seed(unsigned long s)
|
||||
{
|
||||
mt[0]= s & 0xffffffffUL;
|
||||
for (mti=1; mti<N; mti++) {
|
||||
mt[mti] =
|
||||
(1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti);
|
||||
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
|
||||
/* In the previous versions, MSBs of the seed affect */
|
||||
/* only MSBs of the array mt[]. */
|
||||
/* 2002/01/09 modified by Makoto Matsumoto */
|
||||
mt[mti] &= 0xffffffffUL;
|
||||
/* for >32 bit machines */
|
||||
}
|
||||
}
|
||||
|
||||
/* generates a random number on [0,0xffffffff]-interval */
|
||||
unsigned long RNG::getInt32()
|
||||
{
|
||||
unsigned long y;
|
||||
static unsigned long mag01[2]={0x0UL, _MATRIX_A};
|
||||
/* mag01[x] = x * _MATRIX_A for x=0,1 */
|
||||
|
||||
if (mti >= N) { /* generate N words at one time */
|
||||
int kk;
|
||||
|
||||
for (kk=0;kk<N-_M;kk++) {
|
||||
y = (mt[kk]&_UPPER_MASK)|(mt[kk+1]&_LOWER_MASK);
|
||||
mt[kk] = mt[kk+_M] ^ (y >> 1) ^ mag01[y & 0x1UL];
|
||||
}
|
||||
for (;kk<N-1;kk++) {
|
||||
y = (mt[kk]&_UPPER_MASK)|(mt[kk+1]&_LOWER_MASK);
|
||||
mt[kk] = mt[kk+(_M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
|
||||
}
|
||||
y = (mt[N-1]&_UPPER_MASK)|(mt[0]&_LOWER_MASK);
|
||||
mt[N-1] = mt[_M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
|
||||
|
||||
mti = 0;
|
||||
}
|
||||
|
||||
y = mt[mti++];
|
||||
|
||||
/* Tempering */
|
||||
y ^= (y >> 11);
|
||||
y ^= (y << 7) & 0x9d2c5680UL;
|
||||
y ^= (y << 15) & 0xefc60000UL;
|
||||
y ^= (y >> 18);
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
/* generates a random number on [0,0x7fffffff]-interval */
|
||||
long RNG::getInt31()
|
||||
{
|
||||
return (long)(getInt32()>>1);
|
||||
}
|
||||
|
||||
/* generates a random number on [0,1]-real-interval */
|
||||
double RNG::getDoubleLR()
|
||||
{
|
||||
return getInt32()*(1.0/4294967295.0);
|
||||
/* divided by 2^32-1 */
|
||||
}
|
||||
|
||||
/* generates a random number on [0,1)-real-interval */
|
||||
double RNG::getDoubleL()
|
||||
{
|
||||
return getInt32()*(1.0/4294967296.0);
|
||||
/* divided by 2^32 */
|
||||
}
|
||||
|
||||
/* generates a random number on (0,1)-real-interval */
|
||||
double RNG::getDouble()
|
||||
{
|
||||
return (((double)getInt32()) + 0.5)*(1.0/4294967296.0);
|
||||
/* divided by 2^32 */
|
||||
}
|
||||
|
||||
float RNG::getFloatLR()
|
||||
{
|
||||
return getInt32()*(1.0f/4294967295.0f);
|
||||
/* divided by 2^32-1 */
|
||||
}
|
||||
float RNG::getFloatL()
|
||||
{
|
||||
return getInt32()*(1.0f/4294967296.0f);
|
||||
/* divided by 2^32 */
|
||||
}
|
||||
float RNG::getFloat()
|
||||
{
|
||||
return (getInt32() + 0.5f)*(1.0f/4294967296.0f);
|
||||
/* divided by 2^32 */
|
||||
}
|
||||
|
||||
37
BlueNoise/RNG.h
Normal file
37
BlueNoise/RNG.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class RNG {
|
||||
private:
|
||||
/* Period parameters */
|
||||
static const long N = 624;
|
||||
static const long _M = 397;
|
||||
static const unsigned long _MATRIX_A = 0x9908b0dfUL; /* constant vector a */
|
||||
static const unsigned long _UPPER_MASK = 0x80000000UL; /* most significant w-r bits */
|
||||
static const unsigned long _LOWER_MASK = 0x7fffffffUL; /* least significant r bits */
|
||||
|
||||
private:
|
||||
unsigned long mt[N]; /* the array for the state vector */
|
||||
int mti;
|
||||
|
||||
public:
|
||||
RNG(unsigned long seed=5489UL);
|
||||
RNG(unsigned long *init_key, int key_length);
|
||||
|
||||
void seed(unsigned long seed);
|
||||
|
||||
/* generates a random number on [0,0xffffffff]-interval */
|
||||
unsigned long getInt32();
|
||||
/* generates a random number on [0,0x7fffffff]-interval */
|
||||
long getInt31();
|
||||
/* generates a random number on [0,1]-real-interval */
|
||||
double getDoubleLR();
|
||||
float getFloatLR();
|
||||
/* generates a random number on [0,1)-real-interval */
|
||||
double getDoubleL();
|
||||
float getFloatL();
|
||||
/* generates a random number on (0,1)-real-interval */
|
||||
double getDouble();
|
||||
float getFloat();
|
||||
};
|
||||
139
BlueNoise/RangeList.cpp
Normal file
139
BlueNoise/RangeList.cpp
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
// $Id: RangeList.cpp,v 1.4 2006/01/24 03:22:14 zr Exp $
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <cstring>
|
||||
|
||||
#include "RangeList.h"
|
||||
|
||||
///
|
||||
|
||||
static const float kSmallestRange = .000001f;
|
||||
|
||||
RangeList::RangeList(float min, float max)
|
||||
{
|
||||
numRanges = 0;
|
||||
rangesSize = 8;
|
||||
ranges = new RangeEntry[rangesSize];
|
||||
reset(min, max);
|
||||
}
|
||||
|
||||
RangeList::~RangeList()
|
||||
{
|
||||
delete[] ranges;
|
||||
}
|
||||
|
||||
void RangeList::reset(float min, float max)
|
||||
{
|
||||
numRanges = 1;
|
||||
ranges[0].min = min;
|
||||
ranges[0].max = max;
|
||||
}
|
||||
|
||||
void RangeList::deleteRange(int pos)
|
||||
{
|
||||
if (pos<numRanges-1) {
|
||||
memmove(&ranges[pos], &ranges[pos+1], sizeof(*ranges)*(numRanges-(pos+1)));
|
||||
}
|
||||
numRanges--;
|
||||
}
|
||||
|
||||
void RangeList::insertRange(int pos, float min, float max)
|
||||
{
|
||||
if (numRanges==rangesSize) {
|
||||
RangeEntry *tmp = new RangeEntry[rangesSize];
|
||||
memcpy(tmp, ranges, numRanges*sizeof(*tmp));
|
||||
delete[] ranges;
|
||||
ranges = tmp;
|
||||
}
|
||||
|
||||
if (pos<numRanges) {
|
||||
memmove(&ranges[pos+1], &ranges[pos], sizeof(*ranges)*(numRanges-pos));
|
||||
}
|
||||
|
||||
ranges[pos].min = min;
|
||||
ranges[pos].max = max;
|
||||
numRanges++;
|
||||
}
|
||||
|
||||
void RangeList::subtract(float a, float b)
|
||||
{
|
||||
static const float twoPi = (float) (M_PI*2);
|
||||
|
||||
if (a>twoPi) {
|
||||
subtract(a-twoPi, b-twoPi);
|
||||
} else if (b<0) {
|
||||
subtract(a+twoPi, b+twoPi);
|
||||
} else if (a<0) {
|
||||
subtract(0, b);
|
||||
subtract(a+twoPi,twoPi);
|
||||
} else if (b>twoPi) {
|
||||
subtract(a, twoPi);
|
||||
subtract(0, b-twoPi);
|
||||
} else if (numRanges==0) {
|
||||
;
|
||||
} else {
|
||||
int pos;
|
||||
|
||||
if (a<ranges[0].min) {
|
||||
pos = -1;
|
||||
} else {
|
||||
int lo=0, mid=0, hi=numRanges;
|
||||
|
||||
while (lo<hi-1) {
|
||||
mid = (lo+hi)>>1;
|
||||
if (ranges[mid].min<a) {
|
||||
lo = mid;
|
||||
} else {
|
||||
hi = mid;
|
||||
}
|
||||
}
|
||||
|
||||
pos = lo;
|
||||
}
|
||||
|
||||
if (pos==-1) {
|
||||
pos = 0;
|
||||
} else if (a<ranges[pos].max) {
|
||||
float c = ranges[pos].min;
|
||||
float d = ranges[pos].max;
|
||||
if (a-c<kSmallestRange) {
|
||||
if (b<d) {
|
||||
ranges[pos].min = b;
|
||||
} else {
|
||||
deleteRange(pos);
|
||||
}
|
||||
} else {
|
||||
ranges[pos].max = a;
|
||||
if (b<d) {
|
||||
insertRange(pos+1, b, d);
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
} else {
|
||||
if (pos<numRanges-1 && b>ranges[pos+1].min) {
|
||||
pos++;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (pos<numRanges && b>ranges[pos].min) {
|
||||
if (ranges[pos].max-b<kSmallestRange) {
|
||||
deleteRange(pos);
|
||||
} else {
|
||||
ranges[pos].min = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RangeList::print()
|
||||
{
|
||||
printf("[");
|
||||
for (int i=0; i<numRanges; i++) {
|
||||
printf("(%f,%f)%s", ranges[i].min, ranges[i].max, (i==numRanges-1)?"":", ");
|
||||
}
|
||||
printf("]\n");
|
||||
}
|
||||
27
BlueNoise/RangeList.h
Normal file
27
BlueNoise/RangeList.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// $Id: RangeList.h,v 1.3 2006/01/12 16:39:19 zr Exp $
|
||||
|
||||
#include <vector>
|
||||
|
||||
typedef struct _RangeEntry {
|
||||
float min, max;
|
||||
} RangeEntry;
|
||||
|
||||
class RangeList {
|
||||
public:
|
||||
RangeEntry *ranges;
|
||||
int numRanges, rangesSize;
|
||||
|
||||
public:
|
||||
RangeList(float min, float max);
|
||||
~RangeList();
|
||||
|
||||
void reset(float min, float max);
|
||||
|
||||
void print();
|
||||
|
||||
void subtract(float min, float max);
|
||||
|
||||
private:
|
||||
void deleteRange(int pos);
|
||||
void insertRange(int pos, float min, float max);
|
||||
};
|
||||
289
BlueNoise/ScallopedSector.cpp
Normal file
289
BlueNoise/ScallopedSector.cpp
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "BLUE_NOISE.h"
|
||||
#include "ScallopedSector.h"
|
||||
|
||||
static const float kTwoPi = (float) (M_PI*2);
|
||||
|
||||
static float integralOfDistToCircle(float x, float d, float r, float k)
|
||||
{
|
||||
if (r<FLT_EPSILON)
|
||||
return 0.0;
|
||||
|
||||
float sin_x = sin(x);
|
||||
float d_sin_x = d*sin_x;
|
||||
float y = sin_x*d/r;
|
||||
if (y<-1) y = -1;
|
||||
else if (y>1) y = 1;
|
||||
|
||||
float theta = asin(y);
|
||||
|
||||
return (r*(r*(x +
|
||||
k*theta) +
|
||||
k*cos(theta)*d_sin_x) +
|
||||
d*cos(x)*d_sin_x)*.5f;
|
||||
}
|
||||
|
||||
ScallopedSector::ScallopedSector(Vec2 &_Pt, float _a1, float _a2, Vec2 &P1, float r1, float sign1, Vec2 &P2, float r2, float sign2)
|
||||
{
|
||||
Vec2 v1 = Vec2(P1.x - _Pt.x, P1.y - _Pt.y);
|
||||
Vec2 v2 = Vec2(P2.x - _Pt.x, P2.y - _Pt.y);
|
||||
|
||||
P = _Pt;
|
||||
a1 = _a1;
|
||||
a2 = _a2;
|
||||
|
||||
arcs[0].P = P1;
|
||||
arcs[0].r = r1;
|
||||
arcs[0].sign = sign1;
|
||||
arcs[0].d = sqrt(v1.x*v1.x + v1.y*v1.y);
|
||||
arcs[0].rSqrd = arcs[0].r*arcs[0].r;
|
||||
arcs[0].dSqrd = arcs[0].d*arcs[0].d;
|
||||
arcs[0].theta = atan2(v1.y,v1.x);
|
||||
arcs[0].integralAtStart = integralOfDistToCircle(a1 - arcs[0].theta, arcs[0].d, arcs[0].r, arcs[0].sign);
|
||||
|
||||
arcs[1].P = P2;
|
||||
arcs[1].r = r2;
|
||||
arcs[1].sign = sign2;
|
||||
arcs[1].d = sqrt(v2.x*v2.x + v2.y*v2.y);
|
||||
arcs[1].rSqrd = arcs[1].r*arcs[1].r;
|
||||
arcs[1].dSqrd = arcs[1].d*arcs[1].d;
|
||||
arcs[1].theta = atan2(v2.y,v2.x);
|
||||
arcs[1].integralAtStart = integralOfDistToCircle(a1 - arcs[1].theta, arcs[1].d, arcs[1].r, arcs[1].sign);
|
||||
|
||||
area = calcAreaToAngle(a2);
|
||||
}
|
||||
|
||||
float ScallopedSector::calcAreaToAngle(float angle)
|
||||
{
|
||||
float underInner = integralOfDistToCircle(angle - arcs[0].theta, arcs[0].d, arcs[0].r, arcs[0].sign) - arcs[0].integralAtStart;
|
||||
float underOuter = integralOfDistToCircle(angle - arcs[1].theta, arcs[1].d, arcs[1].r, arcs[1].sign) - arcs[1].integralAtStart;
|
||||
|
||||
return underOuter-underInner;
|
||||
}
|
||||
|
||||
float ScallopedSector::calcAngleForArea(float area, RNG &rng)
|
||||
{
|
||||
float lo = a1, hi = a2, cur = lo + (hi-lo)*rng.getFloat();
|
||||
|
||||
for (int i=0; i<10; i++) {
|
||||
if (calcAreaToAngle(cur)<area) {
|
||||
lo = cur;
|
||||
cur = (cur + hi)*.5f;
|
||||
} else {
|
||||
hi = cur;
|
||||
cur = (lo + cur)*.5f;
|
||||
}
|
||||
}
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
||||
float ScallopedSector::distToCurve(float angle, int index)
|
||||
{
|
||||
float alpha = angle - arcs[index].theta;
|
||||
float sin_alpha = sin(alpha);
|
||||
float t0 = arcs[index].rSqrd - arcs[index].dSqrd*sin_alpha*sin_alpha;
|
||||
if (t0<0) {
|
||||
return arcs[index].d*cos(alpha);
|
||||
} else {
|
||||
return arcs[index].d*cos(alpha) + arcs[index].sign*sqrt(t0);
|
||||
}
|
||||
}
|
||||
|
||||
Vec2 ScallopedSector::sample(RNG &rng)
|
||||
{
|
||||
float angle = calcAngleForArea(area*rng.getFloatL(), rng);
|
||||
float d1 = distToCurve(angle, 0);
|
||||
float d2 = distToCurve(angle, 1);
|
||||
float d = sqrt(d1*d1 + (d2*d2 - d1*d1)*rng.getFloat());
|
||||
|
||||
return Vec2(P.x + cos(angle)*d, P.y + sin(angle)*d);
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
float ScallopedSector::canonizeAngle(float angle)
|
||||
{
|
||||
float delta = fmod(angle - a1, kTwoPi);
|
||||
if (delta<0) delta += kTwoPi;
|
||||
return a1 + delta;
|
||||
}
|
||||
|
||||
void ScallopedSector::distToCircle(float angle, Vec2 &C, float r, float *d1_out, float *d2_out)
|
||||
{
|
||||
Vec2 v(C.x - P.x, C.y - P.y);
|
||||
float dSqrd = v.x*v.x + v.y*v.y;
|
||||
float theta = atan2(v.y, v.x);
|
||||
float alpha = angle - theta;
|
||||
float sin_alpha = sin(alpha);
|
||||
float xSqrd = r*r - dSqrd*sin_alpha*sin_alpha;
|
||||
|
||||
if (xSqrd<0) {
|
||||
*d1_out = *d2_out = -10000000;
|
||||
} else {
|
||||
float a = sqrt(dSqrd)*cos(alpha);
|
||||
float x = sqrt(xSqrd);
|
||||
*d1_out = a-x;
|
||||
*d2_out = a+x;
|
||||
}
|
||||
}
|
||||
|
||||
void ScallopedSector::subtractDisk(Vec2 &C, float r, std::vector<ScallopedSector> *regions)
|
||||
{
|
||||
std::vector<float> angles;
|
||||
|
||||
Vec2 v(C.x - P.x, C.y-P.y);
|
||||
float d = sqrt(v.x*v.x + v.y*v.y);
|
||||
|
||||
if (r<d) {
|
||||
float theta = atan2(v.y, v.x);
|
||||
float x = sqrt(d*d-r*r);
|
||||
float angle, alpha = asin(r/d);
|
||||
|
||||
angle = canonizeAngle(theta+alpha);
|
||||
if (a1<angle && angle<a2) {
|
||||
if (distToCurve(angle,0)<x && x<distToCurve(angle,1))
|
||||
angles.push_back(angle);
|
||||
}
|
||||
|
||||
angle = canonizeAngle(theta-alpha);
|
||||
if (a1<angle && angle<a2) {
|
||||
if (distToCurve(angle,0)<x && x<distToCurve(angle,1))
|
||||
angles.push_back(angle);
|
||||
}
|
||||
}
|
||||
|
||||
for (int arcIndex=0; arcIndex<2; arcIndex++) {
|
||||
Vec2 &C2 = arcs[arcIndex].P;
|
||||
float R = arcs[arcIndex].r;
|
||||
Vec2 v(C.x - C2.x, C.y - C2.y);
|
||||
float d = sqrt(v.x*v.x + v.y*v.y);
|
||||
|
||||
if (d>FLT_EPSILON) {
|
||||
float invD = 1.0f/d;
|
||||
float x = (d*d - r*r + R*R)*invD*.5f;
|
||||
float k = R*R - x*x;
|
||||
|
||||
if (k>0) {
|
||||
float y = sqrt(k);
|
||||
float vx = v.x*invD;
|
||||
float vy = v.y*invD;
|
||||
float vx_x = vx*x, vy_x = vy*x;
|
||||
float vx_y = vx*y, vy_y = vy*y;
|
||||
float angle;
|
||||
|
||||
angle = canonizeAngle(atan2(C2.y + vy_x + vx_y - P.y,
|
||||
C2.x + vx_x - vy_y - P.x));
|
||||
if (a1<angle && angle<a2) angles.push_back(angle);
|
||||
|
||||
angle = canonizeAngle(atan2(C2.y + vy_x - vx_y - P.y,
|
||||
C2.x + vx_x + vy_y - P.x));
|
||||
if (a1<angle && angle<a2) angles.push_back(angle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort(angles.begin(), angles.end());
|
||||
angles.insert(angles.begin(), a1);
|
||||
angles.push_back(a2);
|
||||
|
||||
for (unsigned int i=1; i<angles.size(); i++) {
|
||||
float a1 = angles[i-1], a2 = angles[i];
|
||||
float midA = (a1+a2)*.5f;
|
||||
float inner = distToCurve(midA,0);
|
||||
float outer = distToCurve(midA,1);
|
||||
float d1, d2;
|
||||
|
||||
distToCircle(midA, C, r, &d1, &d2); // d1<=d2
|
||||
|
||||
if (d2<inner || d1>outer) {
|
||||
regions->push_back(ScallopedSector(P, a1, a2, arcs[0].P, arcs[0].r, arcs[0].sign, arcs[1].P, arcs[1].r, arcs[1].sign));
|
||||
} else {
|
||||
if (inner<d1) {
|
||||
regions->push_back(ScallopedSector(P, a1, a2, arcs[0].P, arcs[0].r, arcs[0].sign, C, r, -1));
|
||||
}
|
||||
if (d2<outer) {
|
||||
regions->push_back(ScallopedSector(P, a1, a2, C, r, 1, arcs[1].P, arcs[1].r, arcs[1].sign));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
ScallopedRegion::ScallopedRegion(Vec2 &P, float r1, float r2, float _minArea) :
|
||||
minArea(_minArea)
|
||||
{
|
||||
regions = new std::vector<ScallopedSector>;
|
||||
regions->push_back(ScallopedSector(P, 0, kTwoPi, P, r1, 1, P, r2, 1));
|
||||
area = (*regions)[0].area;
|
||||
}
|
||||
|
||||
ScallopedRegion::~ScallopedRegion()
|
||||
{
|
||||
delete regions;
|
||||
}
|
||||
|
||||
void ScallopedRegion::subtractDisk(Vec2 C, float r)
|
||||
{
|
||||
std::vector<ScallopedSector> *newRegions = new std::vector<ScallopedSector>;
|
||||
|
||||
area = 0;
|
||||
for (unsigned int i=0; i<regions->size(); i++) {
|
||||
ScallopedSector &ss = (*regions)[i];
|
||||
std::vector<ScallopedSector> *tmp = new std::vector<ScallopedSector>;
|
||||
|
||||
ss.subtractDisk(C, r, tmp);
|
||||
|
||||
for (unsigned int j=0; j<tmp->size(); j++) {
|
||||
ScallopedSector &nss = (*tmp)[j];
|
||||
|
||||
if (nss.area>minArea) {
|
||||
area += nss.area;
|
||||
|
||||
if (newRegions->size()) {
|
||||
ScallopedSector &last = (*newRegions)[newRegions->size()-1];
|
||||
if (last.a2==nss.a1 && (last.arcs[0].P==nss.arcs[0].P && last.arcs[0].r==nss.arcs[0].r && last.arcs[0].sign==nss.arcs[0].sign) &&
|
||||
(last.arcs[1].P==nss.arcs[1].P && last.arcs[1].r==nss.arcs[1].r && last.arcs[1].sign==nss.arcs[1].sign)) {
|
||||
last.a2 = nss.a2;
|
||||
last.area = last.calcAreaToAngle(last.a2);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
newRegions->push_back(nss);
|
||||
}
|
||||
}
|
||||
|
||||
delete tmp;
|
||||
}
|
||||
|
||||
delete regions;
|
||||
regions = newRegions;
|
||||
}
|
||||
|
||||
Vec2 ScallopedRegion::sample(RNG &rng)
|
||||
{
|
||||
if (!regions->size()) {
|
||||
printf("Fatal error, sampled from empty region.");
|
||||
exit(1);
|
||||
return Vec2(0,0);
|
||||
} else {
|
||||
float a = area*rng.getFloatL();
|
||||
ScallopedSector &ss = (*regions)[0];
|
||||
|
||||
for (unsigned int i=0; i<regions->size(); i++) {
|
||||
ss = (*regions)[i];
|
||||
if (a<ss.area)
|
||||
break;
|
||||
a -= ss.area;
|
||||
}
|
||||
|
||||
return ss.sample(rng);
|
||||
}
|
||||
}
|
||||
51
BlueNoise/ScallopedSector.h
Normal file
51
BlueNoise/ScallopedSector.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// $Id: ScallopedSector.h,v 1.3 2006/07/06 23:30:24 zr Exp $
|
||||
|
||||
#include <vector>
|
||||
|
||||
typedef struct {
|
||||
Vec2 P;
|
||||
float r, sign, d, theta, integralAtStart;
|
||||
float rSqrd, dSqrd;
|
||||
} ArcData;
|
||||
|
||||
class ScallopedSector
|
||||
{
|
||||
public:
|
||||
Vec2 P;
|
||||
float a1, a2, area;
|
||||
|
||||
ArcData arcs[2];
|
||||
|
||||
public:
|
||||
ScallopedSector(Vec2 &_Pt, float _a1, float _a2, Vec2 &P1, float r1, float sign1, Vec2 &P2, float r2, float sign2);
|
||||
|
||||
float calcAreaToAngle(float angle);
|
||||
float calcAngleForArea(float area, RNG &rng);
|
||||
Vec2 sample(RNG &rng);
|
||||
|
||||
float distToCurve(float angle, int index);
|
||||
|
||||
void subtractDisk(Vec2 &C, float r, std::vector<ScallopedSector> *regions);
|
||||
|
||||
private:
|
||||
float canonizeAngle(float angle);
|
||||
|
||||
void distToCircle(float angle, Vec2 &C, float r, float *d1_out, float *d2_out);
|
||||
};
|
||||
|
||||
class ScallopedRegion
|
||||
{
|
||||
public:
|
||||
std::vector<ScallopedSector> *regions;
|
||||
float minArea;
|
||||
float area;
|
||||
|
||||
public:
|
||||
ScallopedRegion(Vec2 &P, float r1, float r2, float minArea=.00000001);
|
||||
~ScallopedRegion();
|
||||
|
||||
bool isEmpty() { return regions->size()==0; }
|
||||
void subtractDisk(Vec2 C, float r);
|
||||
|
||||
Vec2 sample(RNG &rng);
|
||||
};
|
||||
313
BlueNoise/WeightedDiscretePDF.cpp
Normal file
313
BlueNoise/WeightedDiscretePDF.cpp
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
// $Id: WeightedDiscretePDF.cpp,v 1.4 2006/07/07 05:54:31 zr Exp $
|
||||
|
||||
#include <stdexcept>
|
||||
#include "WeightedDiscretePDF.h"
|
||||
|
||||
//
|
||||
|
||||
template <class T>
|
||||
WDPDF_Node<T>::WDPDF_Node(T key_, float weight_, WDPDF_Node<T> *parent_)
|
||||
{
|
||||
m_mark = false;
|
||||
|
||||
key = key_;
|
||||
weight = weight_;
|
||||
sumWeights = 0;
|
||||
left = right = 0;
|
||||
parent = parent_;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
WDPDF_Node<T>::~WDPDF_Node()
|
||||
{
|
||||
if (left) delete left;
|
||||
if (right) delete right;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
template <class T>
|
||||
WeightedDiscretePDF<T>::WeightedDiscretePDF()
|
||||
{
|
||||
m_root = 0;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
WeightedDiscretePDF<T>::~WeightedDiscretePDF()
|
||||
{
|
||||
if (m_root) delete m_root;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void WeightedDiscretePDF<T>::insert(T item, float weight)
|
||||
{
|
||||
WDPDF_Node<T> *p=0, *n=m_root;
|
||||
|
||||
while (n) {
|
||||
if (n->leftIsRed() && n->rightIsRed())
|
||||
split(n);
|
||||
|
||||
p = n;
|
||||
if (n->key==item) {
|
||||
throw std::domain_error("insert: argument(item) already in tree");
|
||||
} else {
|
||||
n = (item<n->key)?n->left:n->right;
|
||||
}
|
||||
}
|
||||
|
||||
n = new WDPDF_Node<T>(item, weight, p);
|
||||
|
||||
if (!p) {
|
||||
m_root = n;
|
||||
} else {
|
||||
if (item<p->key) {
|
||||
p->left = n;
|
||||
} else {
|
||||
p->right = n;
|
||||
}
|
||||
|
||||
split(n);
|
||||
}
|
||||
|
||||
propogateSumsUp(n);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void WeightedDiscretePDF<T>::remove(T item)
|
||||
{
|
||||
WDPDF_Node<T> **np = lookup(item, 0);
|
||||
WDPDF_Node<T> *child, *n = *np;
|
||||
|
||||
if (!n) {
|
||||
throw std::domain_error("remove: argument(item) not in tree");
|
||||
} else {
|
||||
if (n->left) {
|
||||
WDPDF_Node<T> **leftMaxp = &n->left;
|
||||
|
||||
while ((*leftMaxp)->right)
|
||||
leftMaxp = &(*leftMaxp)->right;
|
||||
|
||||
n->key = (*leftMaxp)->key;
|
||||
n->weight = (*leftMaxp)->weight;
|
||||
|
||||
np = leftMaxp;
|
||||
n = *np;
|
||||
}
|
||||
|
||||
// node now has at most one child
|
||||
|
||||
child = n->left?n->left:n->right;
|
||||
*np = child;
|
||||
|
||||
if (child) {
|
||||
child->parent = n->parent;
|
||||
|
||||
if (n->isBlack()) {
|
||||
lengthen(child);
|
||||
}
|
||||
}
|
||||
|
||||
propogateSumsUp(n->parent);
|
||||
|
||||
n->left = n->right = 0;
|
||||
delete n;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void WeightedDiscretePDF<T>::update(T item, float weight)
|
||||
{
|
||||
WDPDF_Node<T> *n = *lookup(item, 0);
|
||||
|
||||
if (!n) {
|
||||
throw std::domain_error("update: argument(item) not in tree");
|
||||
} else {
|
||||
float delta = weight - n->weight;
|
||||
n->weight = weight;
|
||||
|
||||
for (; n; n=n->parent) {
|
||||
n->sumWeights += delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T WeightedDiscretePDF<T>::choose(float p)
|
||||
{
|
||||
if (p<0.0 || p>=1.0) {
|
||||
throw std::domain_error("choose: argument(p) outside valid range");
|
||||
} else if (!m_root) {
|
||||
throw std::logic_error("choose: choose() called on empty tree");
|
||||
} else {
|
||||
float w = m_root->sumWeights * p;
|
||||
WDPDF_Node<T> *n = m_root;
|
||||
|
||||
while (1) {
|
||||
if (n->left) {
|
||||
if (w<n->left->sumWeights) {
|
||||
n = n->left;
|
||||
continue;
|
||||
} else {
|
||||
w -= n->left->sumWeights;
|
||||
}
|
||||
}
|
||||
if (w<n->weight || !n->right) {
|
||||
break; // !n->right condition shouldn't be necessary, just sanity check
|
||||
}
|
||||
w -= n->weight;
|
||||
n = n->right;
|
||||
}
|
||||
|
||||
return n->key;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool WeightedDiscretePDF<T>::inTree(T item)
|
||||
{
|
||||
WDPDF_Node<T> *n = *lookup(item, 0);
|
||||
|
||||
return !!n;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
template <class T>
|
||||
WDPDF_Node<T> **WeightedDiscretePDF<T>::lookup(T item, WDPDF_Node<T> **parent_out)
|
||||
{
|
||||
WDPDF_Node<T> *n, *p=0, **np=&m_root;
|
||||
|
||||
while ((n = *np)) {
|
||||
if (n->key==item) {
|
||||
break;
|
||||
} else {
|
||||
p = n;
|
||||
if (item<n->key) {
|
||||
np = &n->left;
|
||||
} else {
|
||||
np = &n->right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parent_out)
|
||||
*parent_out = p;
|
||||
return np;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void WeightedDiscretePDF<T>::split(WDPDF_Node<T> *n)
|
||||
{
|
||||
if (n->left) n->left->markBlack();
|
||||
if (n->right) n->right->markBlack();
|
||||
|
||||
if (n->parent) {
|
||||
WDPDF_Node<T> *p = n->parent;
|
||||
|
||||
n->markRed();
|
||||
|
||||
if (p->isRed()) {
|
||||
p->parent->markRed();
|
||||
|
||||
// not same direction
|
||||
if (!( (n==p->left && p==p->parent->left) ||
|
||||
(n==p->right && p==p->parent->right))) {
|
||||
rotate(n);
|
||||
p = n;
|
||||
}
|
||||
|
||||
rotate(p);
|
||||
p->markBlack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void WeightedDiscretePDF<T>::rotate(WDPDF_Node<T> *n)
|
||||
{
|
||||
WDPDF_Node<T> *p=n->parent, *pp=p->parent;
|
||||
|
||||
n->parent = pp;
|
||||
p->parent = n;
|
||||
|
||||
if (n==p->left) {
|
||||
p->left = n->right;
|
||||
n->right = p;
|
||||
if (p->left) p->left->parent = p;
|
||||
} else {
|
||||
p->right = n->left;
|
||||
n->left = p;
|
||||
if (p->right) p->right->parent = p;
|
||||
}
|
||||
|
||||
n->setSum();
|
||||
p->setSum();
|
||||
|
||||
if (!pp) {
|
||||
m_root = n;
|
||||
} else {
|
||||
if (p==pp->left) {
|
||||
pp->left = n;
|
||||
} else {
|
||||
pp->right = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void WeightedDiscretePDF<T>::lengthen(WDPDF_Node<T> *n)
|
||||
{
|
||||
if (n->isRed()) {
|
||||
n->markBlack();
|
||||
} else if (n->parent) {
|
||||
WDPDF_Node<T> *sibling = n->sibling();
|
||||
|
||||
if (sibling && sibling->isRed()) {
|
||||
n->parent->markRed();
|
||||
sibling->markBlack();
|
||||
|
||||
rotate(sibling); // node sibling is now old sibling child, must be black
|
||||
sibling = n->sibling();
|
||||
}
|
||||
|
||||
// sibling is black
|
||||
|
||||
if (!sibling) {
|
||||
lengthen(n->parent);
|
||||
} else if (sibling->leftIsBlack() && sibling->rightIsBlack()) {
|
||||
if (n->parent->isBlack()) {
|
||||
sibling->markRed();
|
||||
lengthen(n->parent);
|
||||
} else {
|
||||
sibling->markRed();
|
||||
n->parent->markBlack();
|
||||
}
|
||||
} else {
|
||||
if (n==n->parent->left && sibling->rightIsBlack()) {
|
||||
rotate(sibling->left); // sibling->left must be red
|
||||
sibling->markRed();
|
||||
sibling->parent->markBlack();
|
||||
sibling = sibling->parent;
|
||||
} else if (n==n->parent->right && sibling->leftIsBlack()) {
|
||||
rotate(sibling->right); // sibling->right must be red
|
||||
sibling->markRed();
|
||||
sibling->parent->markBlack();
|
||||
sibling = sibling->parent;
|
||||
}
|
||||
|
||||
// sibling is black, and sibling's far child is red
|
||||
|
||||
rotate(sibling);
|
||||
if (n->parent->isRed()) sibling->markRed();
|
||||
sibling->left->markBlack();
|
||||
sibling->right->markBlack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void WeightedDiscretePDF<T>::propogateSumsUp(WDPDF_Node<T> *n)
|
||||
{
|
||||
for (; n; n=n->parent)
|
||||
n->setSum();
|
||||
}
|
||||
57
BlueNoise/WeightedDiscretePDF.h
Normal file
57
BlueNoise/WeightedDiscretePDF.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
// $Id: WeightedDiscretePDF.h,v 1.4 2006/07/07 05:54:31 zr Exp $
|
||||
|
||||
template <class T>
|
||||
class WDPDF_Node
|
||||
{
|
||||
private:
|
||||
bool m_mark;
|
||||
|
||||
public:
|
||||
WDPDF_Node<T> *parent, *left, *right;
|
||||
T key;
|
||||
float weight, sumWeights;
|
||||
|
||||
public:
|
||||
WDPDF_Node(T key_, float weight_, WDPDF_Node<T> *parent_);
|
||||
~WDPDF_Node();
|
||||
|
||||
WDPDF_Node<T> *sibling() { return this==parent->left?parent->right:parent->left; }
|
||||
|
||||
void markRed() { m_mark = true; }
|
||||
void markBlack() { m_mark = false; }
|
||||
bool isRed() { return m_mark; }
|
||||
bool isBlack() { return !m_mark; }
|
||||
bool leftIsBlack() { return !left || left->isBlack(); }
|
||||
bool rightIsBlack() { return !right || right->isBlack(); }
|
||||
bool leftIsRed() { return !leftIsBlack(); }
|
||||
bool rightIsRed() { return !rightIsBlack(); }
|
||||
void setSum() { sumWeights = weight + (left?left->sumWeights:0) + (right?right->sumWeights:0); }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class WeightedDiscretePDF
|
||||
{
|
||||
private:
|
||||
WDPDF_Node<T> *m_root;
|
||||
|
||||
public:
|
||||
WeightedDiscretePDF();
|
||||
~WeightedDiscretePDF();
|
||||
|
||||
void insert(T item, float weight);
|
||||
void update(T item, float newWeight);
|
||||
void remove(T item);
|
||||
bool inTree(T item);
|
||||
|
||||
/* pick a tree element according to its
|
||||
* weight. p should be in [0,1).
|
||||
*/
|
||||
T choose(float p);
|
||||
|
||||
private:
|
||||
WDPDF_Node<T> **lookup(T item, WDPDF_Node<T> **parent_out);
|
||||
void split(WDPDF_Node<T> *node);
|
||||
void rotate(WDPDF_Node<T> *node);
|
||||
void lengthen(WDPDF_Node<T> *node);
|
||||
void propogateSumsUp(WDPDF_Node<T> *n);
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue