I have template class for spatial indexing which by default should work for any 2d object which implements function void boundingBox( Rect2d * box ) using std::vector<OBJECT*> as container of objects inserted in particular grid tile.
template <class OBJECT, class TILE = std::vector<OBJECT*> >
class GridMap2D {
public:
double step, invStep;
int nx, ny, nxy;
TILE * tiles;
// ==== functions
inline int getIx( double x ){ return (int)( invStep * x ); };
inline int getIy( double y ){ return (int)( invStep * y ); };
inline double getX( int ix ){ return step * ix ; };
inline double getY( int iy ){ return step * iy ; };
inline int getIndex ( int ix, int iy ){ return nx*iy + ix; };
inline int getIndex ( double x, double y ){ return getIndex( getIx(x), getIy(y) ); };
inline TILE* getTile( double x, double y ){ return tiles + getIndex( x, y ); };
inline void insert( OBJECT* p, int i ){
tiles[ i ].push_back( p );
}
inline void insert( OBJECT* p, int ix, int iy ){ insert( p, getIndex( ix,iy ) ); };
// this is very general method to insert any object with bounding box
// but for many object it is not very efficient
// some objects suchb as Point2d does not even implement boundingBox()
inline void insert( OBJECT* p ){
Rect2d bbox;
p.boundingBox( &bbox );
int ix0 = getIx( bbox.x0 ); // TODO: bound check ?
int iy0 = getIy( bbox.y0 );
int ix1 = getIx( bbox.x1 );
int iy1 = getIy( bbox.y1 );
for( int iy=iy0; iy<=iy1; iy++ ){
for( int ix=ix0; ix<=ix1; ix++ ){
insert( p, ix, iy );
}
}
}
void init( int nx_, int ny_, double step_, int tile_n0 ){
step = step_;
invStep = 1/step;
nx = nx_; ny=ny_;
nxy = nx*ny;
tiles = new TILE[ nxy ];
for (int i=0; i<nxy; i++){
if ( tile_n0 != 0 ){
tiles[i].reserve( tile_n0 );
}
}
}
};
And its specialization for Segment2d which does not implement boundingBox() but has own insert algorithm based on line rasterization:
template<> class GridMap2D< Segment2d, std::vector<Segment2d*> >{
public:
inline void insert( Segment2d* l ){
Vec2d* a = l->a;
Vec2d* b = l->b;
double ax = a->x;
double ay = a->y;
double bx = b->x;
double by = b->y;
double dx = fabs( bx - ax );
double dy = fabs( by - ay );
int dix = ( ax < bx ) ? 1 : -1;
int diy = ( ay < by ) ? 1 : -1;
int ix = getIx( ax );
int iy = getIy( ay );
int ixb = getIx( bx );
int iyb = getIy( by );
double x=0, y=0;
int i=0;
insert( l, ix, iy );
insert( l, ixb, iyb );
while ( ( ix != ixb ) && ( iy != iyb ) ) {
if ( x < y ) {
x += dy;
ix += dix;
} else {
y += dx;
iy += diy;
}
insert( l, ix, iy );
}
}
};
which will insert line into grid tiles trough which it goes ... like this:
but I have several problems with how templates work:
In the specialization for Segment2d I got error: ‘getIx’ was not declared in this scope. Does it mean that the specialized template does not know functions defined in base template ? Or I probably do the specialization wrongly. I really do not want to rewrite the code several times, then the template approach would be pointless.
I'm not sure what happen when I instantiate or specialize the template by some parameter which does not implement some methods which the base template use. e.g.
consider I use different container type argument as TILE which does not implement .push_back()
my Segment2d does not implement boundingBox()
can does the specialization solve this problem ?
background:
I have two goals:
I want to create very fast spatial indexing for acceleration of ray-casting and collisions for any 2d shape.
Because it should be as fast as possible I want o use templates (resolved in compile-time ) rather than some class inherience hierarchy with virtual methods.
I want to learn how to use templates effectively
This question is related to this " Generic Quadtree ", where is recommendation to use templates for similar task. Tried to implement that ... but I perhaps my understanding of templates is not good enough.
NOTE: my GridMap2d is not a QuadTree, but I still added QuadTree as an keyword, because the question is relavant to it. QuadTree is very common spatial indexing data-structure, and implementing it using templates would have the same issue.
"I really do not want to rewrite the code several times, then the template approach would be pointless."
When you specialize a class, you do not "inherit" any of the member fields or methods. So you need some special tricks here to get what you want.
What you can do instead is to essentially move the behavior of your insert() method into a separate template class. That way, when you specialize that class's behavior, you don't end up clobbering your other fields and methods. This requires some clever restructuring of your code.
This code I believe should do the job:
struct GridMap2D_base {
double step, invStep;
int nx, ny, nxy;
int getIx( double x ) const { return (int)( invStep * x ); };
int getIy( double y ) const { return (int)( invStep * y ); };
double getX( int ix ) const { return step * ix ; };
double getY( int iy ) const { return step * iy ; };
int getIndex ( int ix, int iy ) const { return nx*iy + ix; };
int getIndex ( double x, double y ) const { return getIndex( getIx(x), getIy(y) ); };
};
struct PushBackHelper {
// add versions of this for std::list, etc., as needed
template <typename OBJECT>
static void push_back(std::vector<OBJECT*>& tile, OBJECT* p) {
tile.push_back(p);
}
};
template<typename OBJECT>
struct InsertAlgorithm {
int ix0, iy0, ix1, iy1, ix, iy;
InsertAlgorithm(const GridMap2D_base& base, OBJECT* p) {
Rect2d bbox;
p->boundingBox( &bbox );
ix0 = base.getIx( bbox.x0 ); // TODO: bound check ?
iy0 = base.getIy( bbox.y0 );
ix1 = base.getIx( bbox.x1 );
iy1 = base.getIy( bbox.y1 );
ix = 0;
iy = 0;
}
bool should_preinsert1(const GridMap2D_base& base, int& ix2, int& iy2) { return false; }
bool should_preinsert2(const GridMap2D_base& base, int& ix2, int& iy2) { return false; }
bool should_insert(const GridMap2D_base& base, int& ix2, int& iy2)
{
while (ix<=ix1) {
ix2 = ix;
iy2 = iy;
ix++;
return true;
}
iy++;
if (iy>iy1) return false;
ix = 0;
return should_insert(base, ix2, iy2);
}
};
template<> struct InsertAlgorithm<Segment2d> {
Vec2d* a;
Vec2d* b;
double ax, ay, bx, by, dx, dy, x, y;
int dix, diy, ix, iy, ixb, iyb;
InsertAlgorithm(const GridMap2D_base& base, Segment2d* l) {
a = l->a;
b = l->b;
ax = a->x;
ay = a->y;
bx = b->x;
by = b->y;
x = 0;
y = 0;
dx = fabs( bx - ax );
dy = fabs( by - ay );
dix = ( ax < bx ) ? 1 : -1;
diy = ( ay < by ) ? 1 : -1;
ix = base.getIx( ax );
iy = base.getIy( ay );
ixb = base.getIx( bx );
iyb = base.getIy( by );
}
bool should_preinsert1(const GridMap2D_base& base, int& ix2, int& iy2) {
ix2 = ix;
iy2 = iy;
return true;
}
bool should_preinsert2(const GridMap2D_base& base, int& ix2, int& iy2) {
ix2 = ixb;
iy2 = iyb;
return true;
}
bool should_insert(const GridMap2D_base& base, int& ix2, int& iy2)
{
if (ix==ixb && iy==iyb) return false;
if ( x < y ) {
x += dy;
ix += dix;
} else {
y += dx;
iy += diy;
}
ix2 = ix;
iy2 = iy;
return true;
}
};
template<class OBJECT, typename TILE=std::vector<OBJECT*> >
class GridMap2D : public GridMap2D_base {
public:
TILE* tiles;
TILE* getTile( double x, double y ){ return tiles + getIndex( x, y ); };
void insert( OBJECT* p ){
InsertAlgorithm<OBJECT> algo(*this, p);
int ix = 0;
int iy = 0;
if (algo.should_preinsert1(*this, ix, iy)) {
PushBackHelper::push_back(tiles[getIndex(ix, iy)], p);
}
if (algo.should_preinsert2(*this, ix, iy)) {
PushBackHelper::push_back(tiles[getIndex(ix, iy)], p);
}
while (algo.should_insert(*this, ix, iy)) {
PushBackHelper::push_back(tiles[getIndex(ix, iy)], p);
}
}
void init( int nx_, int ny_, double step_, int tile_n0 ){ ... }
};
Btw, the inline keyword has no effect when used within the class declaration.
Related
I have a problem, probably, with memory leaking in C++ threads. I receive a runtime error with code 11. I am writing an optimization algorithm, which aims to optimize parameters of 2D reactors. It generates instances of reforming function, which creates Reformer objects. The reformers have 2 different parameters, which can differ locally in a single reformer and are passed to the reforming function from the main function. To specify, each reformer is divided into a specified number of zones (same dimensions and locations in each reformer), and each zone can have different parameters. Therefore, size of each of 2 vectors is equal to [NUMBER OF REFORMERS] * [NUMBER OF ZONES]. Then, the reforming function creates Segment objects, which number is equal to the number of zones.
I assume that the issue here is that threads try to access the same vector simultaneously and I would really appreciate a solution for that matter.
Remarks:
If I change the main.cpp to substitute the threads with a usual loop, no error is returned.
If I comment out the setProp method in the set_segments functions, no error is returned (with threads).
Threads are highly recommended here, due to long computation time of a single Reformer, and I have an access to a multi-core computing units.
To clarify, I will explain everything with a minimal reproducible example:
input.h
#include <iostream>
#include <fstream>
#include <vector>
#include <thread>
int reactor_no = 2; // number of reformers
int zones_X = 5; // number of zones in a single reformer, X direction
int zones_Y = 2; // number of zones in a single reformer, Y direction
double dim_X = 0.5; // reactor's length
double dim_Y = 0.2; // reactor's height
double wall_t = 0.1; // thickness of the reactor wall
size_t zones = zones_X * zones_Y;
Reformer.h:
#include "input.h"
class Reformer {
public:
Reformer() {}
Reformer(const double& L, const double& Y, const double& wall_t,
const int& zones_X = 1, const int& zones_Y = 1) {
length_ = L;
height_ = Y;
zonesX_ = zones_X;
zonesY_ = zones_Y;
wall_thickness_ = wall_t;
dx_ = length_ / static_cast<double> (zonesX_);
dr_ = height_ / static_cast<double> (zonesY_);
}
private:
double wall_thickness_; // wall thickness (m)
double length_; // recactor length (m)
double height_; // reactor height (m) (excluding wall thickness)
int zonesX_; // number of segments in the X direction
int zonesY_; // number of segments in the Y direction
double dx_; // segment width (m)
double dr_; // segment height (m)
}
Segment.h:
#include "input.h"
class Segment{
public:
Segment() : Segment(0, 0) {}
Segment(int i, int j) {
i_ = i;
j_ = j;
}
void setXR(const double& dx, const double& dr, const int& SL, const int& SR) {
x0_ = i_ * dx;
x1_ = x0_ + dx;
r0_ = j_ * dr;
r1_ = r0_ + dr;
if (i_ == SL - 1) {
x1_ = length;
}
if (j_ == SR - 1) {
r1_ = radius;
}
}
void setWall() {
x0_ = 0;
x1_ = length;
r0_ = radius;
r1_ = radius + wall_t;
}
void setProp(const double& por, const double& por_s, const bool& cat) {
porosity_ = por;
catalyst_ = cat;
}
private:
size_t i_; //segment column no.
size_t j_; //segment row no.
double x0_; //beginning of segment - x coordinate (m)
double x1_; //ending of segment - x coordinate (m)
double r0_; //beginning of segment - r coordinate (m)
double r1_; //ending of segment - r coordinate (m)
int catalyst_; //1 - catalytic, 0 - non-catalytic
double porosity_; //porosity (-)
};
main.cpp:
#include "input.h"
int main() {
int zones = zones_X * zones_Y;
size_t pop_size = reactor_no * zones;
std::vector<int> cat;
cat.reserve(pop_size);
std::vector<double> porosity;
porosity.reserve(pop_size); // the values in the vectors are not important, therefore I will just fill them with 1s
for (int i = 0; i < pop_size; i++) {
cat[i] = 1;
porosity[i] = 1.0;
}
std::vector<std::thread> Ref;
Ref.reserve(reactor_no);
for (k = 0; k < reactor_no; k++) {
Ref.emplace_back(reforming, k, cat, porosity);
}
for (auto &X : Ref) { X.join(); }
}
reforming.cpp:
#include "input.h"
void reforming(const int m, const std::vector<int>& cat_check, const std::vector<double>& por) {
Reformer reactor(length, radius, wall_t, zonesX, zonesY);
std::vector<Segment> seg; // vector holding segment objects
seg.reserve(zones);
set_segments(seg, reactor, zones, m, por, por_s, cat_check);
}
set_segments function:
#include "input.h"
void set_segments(std::vector<Segment> &seg, Reformer &reac, const int m,
const std::vector<double> &por, const std::vector<int> &check) {
int i, j, k, n;
double dx = dim_X / static_cast<double> (zones_X);
double dy = dim_Y / static_cast<double> (zones_Y);
std::vector<Segment*> ptr_seg;
ptr_seg.reserve(zones);
k = 0;
for (i = 0; i < zones_X; i++) {
for (j = 0; j < zones_Y; j++) {
n = m * zones + (i * zones_Y + j);
seg.emplace_back(Segment(i, j));
seg[k].setProp(por[n], check[n]);
seg[k].setXR(dx, dy, zones_X, zones_Y);
k++;
}
}
}
Adding std::ref() to the reforming function call parameters solved the problem.
for (k = 0; k < spec_max; k++) {
Ref.emplace_back(reforming, k, std::ref(cat), std::ref(porosity));
}
I am using OpenGL version 4.2. I am calling a function inside another function in OpenGL fragment shader where the inner function (pix2loc) returns a bool variable as function argument and then the outer function (pix2ang) uses this bool variable to take a decision. More precisely, I have:
void pix2ang (int order_, bool is_nest, int pix, out double theta, out double phi)
{
double z, sth;
bool have_sth;
pix2loc (order_, is_nest, pix, z, phi, sth, have_sth);
if(have_sth)
theta = atan(float(sth),float(z));
else
theta = acos(float(z));
}
where the function pix2loc returns boolean variable have_sth as function argument as follows:
void pix2loc (int order_, bool is_nest, int pix, out double z, out double phi, out double sth, out bool have_sth)
{
.....
if <condition true>
have_sth = true;
else
have_sth = false;
}
I noticed that inside pix2ang have_sth does not work as expected, but if I inverse the if statements and change it to
if(!have_sth)
theta = acos(float(z));
else
theta = atan(float(sth),float(z));
then it works as expected. This makes the code un-reliable and I don't exactly understand what the problem is.
Ok. I decided to write the whole function of pix2loc such that it may help to understand the problem.
void pix2loc (int order_, bool is_nest, int pix, out double z, out double phi, out double sth, out bool have_sth)
{
int nside_ = int(1)<<order_;
int npface_ = nside_<<order_;
int ncap_ = (npface_-nside_)<<1;
int npix_ = 12*npface_;
double fact2_ = 4./npix_;
double fact1_ = (nside_<<1)*fact2_;
have_sth=false;
if (!is_nest)
{
if (pix<ncap_) // North Polar cap
{
int iring = (1+int(isqrt(1+2*pix)))>>1; // counted from North pole
int iphi = (pix+1) - 2*iring*(iring-1);
double tmp=(iring*iring)*fact2_;
z = 1.0 - tmp;
if (z>0.99) { sth=sqrt(tmp*(2.-tmp)); have_sth=true; }
phi = (iphi-0.5) * halfpi/iring;
}
else if (pix<(npix_-ncap_)) // Equatorial region
{
int nl4 = 4*nside_;
int ip = pix - ncap_;
int tmp = (order_>=0) ? ip>>(order_+2) : ip/nl4;
int iring = tmp + nside_;
int iphi = ip-nl4*tmp+1;
// 1 if iring+nside is odd, 1/2 otherwise
double fodd = bool((iring+nside_)&1) ? 1. : 0.5;
z = (2*nside_-iring)*fact1_;
phi = (iphi-fodd) * pi*0.75*fact1_;
}
else // South Polar cap
{
int ip = npix_ - pix;
int iring = (1+int(isqrt(2*ip-1)))>>1; // counted from South pole
int iphi = 4*iring + 1 - (ip - 2*iring*(iring-1));
double tmp=(iring*iring)*fact2_;
z = tmp - 1.0;
if (z<-0.99) { sth=sqrt(tmp*(2.-tmp)); have_sth=true; }
phi = (iphi-0.5) * halfpi/iring;
}
}
else
{
int face_num, ix, iy;
nest2xyf(order_, pix,ix,iy,face_num);
int jr = (int(jrll[face_num])<<order_) - ix - iy - 1;
int nr;
if (jr<nside_)
{
nr = jr;
double tmp=(nr*nr)*fact2_;
z = 1 - tmp;
if (z>0.99) { sth=sqrt(tmp*(2.-tmp)); have_sth=true; }
}
else if (jr > 3*nside_)
{
nr = nside_*4-jr;
double tmp=(nr*nr)*fact2_;
z = tmp - 1;
if (z<-0.99) { sth=sqrt(tmp*(2.-tmp)); have_sth=true; }
}
else
{
nr = nside_;
z = (2*nside_-jr)*fact1_;
}
int tmp=int(jpll[face_num])*nr+ix-iy;
if (tmp<0) tmp+=8*nr;
phi = (nr==nside_) ? 0.75*halfpi*tmp*fact1_ : (0.5*halfpi*tmp)/nr;
}
}
where pi, halfpi, jrll and jpll are constant variable defined in the begining of the fragment shader as
#version 420 core
const double pi = 3.141592653589793;
const double halfpi = 0.5 * pi;
const int jrll[] = { 2,2,2,2,3,3,3,3,4,4,4,4 };
const int jpll[] = { 1,3,5,7,0,2,4,6,1,3,5,7 };
Is there any way I can modify the poisson-disk points generator finding here.I need to generate new poisson points using the coordinates of points in the textfile.txt to improve the distribution. below the c++ code of poisson-disk sampling in a unit square.
poissonGenerator.h:
#include <vector>
#include <random>
#include <stdint.h>
#include <time.h>
namespace PoissoGenerator
{
class DefaultPRNG
{
public:
DefaultPRNG()
: m_Gen(std::random_device()())
, m_Dis(0.0f, 1.f)
{
// prepare PRNG
m_Gen.seed(time(nullptr));
}
explicit DefaultPRNG(unsigned short seed)
: m_Gen(seed)
, m_Dis(0.0f, 1.f)
{
}
double RandomDouble()
{
return static_cast <double>(m_Dis(m_Gen));
}
int RandomInt(int Max)
{
std::uniform_int_distribution<> DisInt(0, Max);
return DisInt(m_Gen);
}
private:
std::mt19937 m_Gen;
std::uniform_real_distribution<double> m_Dis;
};
struct sPoint
{
sPoint()
: x(0)
, y(0)
, m_valid(false){}
sPoint(double X, double Y)
: x(X)
, y(Y)
, m_valid(true){}
double x;
double y;
bool m_valid;
//
bool IsInRectangle() const
{
return x >= 0 && y >= 0 && x <= 1 && y <= 1;
}
//
bool IsInCircle() const
{
double fx = x - 0.5f;
double fy = y - 0.5f;
return (fx*fx + fy*fy) <= 0.25f;
}
};
struct sGridPoint
{
sGridPoint(int X, int Y)
: x(X)
, y(Y)
{}
int x;
int y;
};
double GetDistance(const sPoint& P1, const sPoint& P2)
{
return sqrt((P1.x - P2.x)*(P1.x - P2.x) + (P1.y - P2.y)*(P1.y - P2.y));
}
sGridPoint ImageToGrid(const sPoint& P, double CellSize)
{
return sGridPoint((int)(P.x / CellSize), (int)(P.y / CellSize));
}
struct sGrid
{
sGrid(int W, int H, double CellSize)
: m_W(W)
, m_H(H)
, m_CellSize(CellSize)
{
m_Grid.resize((m_H));
for (auto i = m_Grid.begin(); i != m_Grid.end(); i++){ i->resize(m_W); }
}
void Insert(const sPoint& P)
{
sGridPoint G = ImageToGrid(P, m_CellSize);
m_Grid[G.x][G.y] = P;
}
bool IsInNeighbourhood(sPoint Point, double MinDist, double CellSize)
{
sGridPoint G = ImageToGrid(Point, CellSize);
//number of adjacent cell to look for neighbour points
const int D = 5;
// Scan the neighbourhood of the Point in the grid
for (int i = G.x - D; i < G.x + D; i++)
{
for (int j = G.y - D; j < G.y + D; j++)
{
if (i >= 0 && i < m_W && j >= 0 && j < m_H)
{
sPoint P = m_Grid[i][j];
if (P.m_valid && GetDistance(P, Point) < MinDist){ return true; }
}
}
}
return false;
}
private:
int m_H;
int m_W;
double m_CellSize;
std::vector< std::vector< sPoint> > m_Grid;
};
template <typename PRNG>
sPoint PopRandom(std::vector<sPoint>& Points, PRNG& Generator)
{
const int Idx = Generator.RandomInt(Points.size() - 1);
const sPoint P = Points[Idx];
Points.erase(Points.begin() + Idx);
return P;
}
template <typename PRNG>
sPoint GenerateRandomPointAround(const sPoint& P, double MinDist, PRNG& Generator)
{
// Start with non-uniform distribution
double R1 = Generator.RandomDouble();
double R2 = Generator.RandomDouble();
// radius should be between MinDist and 2 * MinDist
double Radius = MinDist * (R1 + 1.0f);
//random angle
double Angle = 2 * 3.141592653589f * R2;
// the new point is generated around the point (x, y)
double X = P.x + Radius * cos(Angle);
double Y = P.y + Radius * sin(Angle);
return sPoint(X, Y);
}
// Return a vector of generated points
// NewPointsCount - refer to bridson-siggraph07-poissondisk.pdf
// for details (the value 'k')
// Circle - 'true' to fill a circle, 'false' to fill a rectangle
// MinDist - minimal distance estimator, use negative value for default
template <typename PRNG = DefaultPRNG>
std::vector<sPoint> GeneratePoissonPoints(rsize_t NumPoints, PRNG& Generator, int NewPointsCount = 30,
bool Circle = true, double MinDist = -1.0f)
{
if (MinDist < 0.0f)
{
MinDist = sqrt(double(NumPoints)) / double(NumPoints);
}
std::vector <sPoint> SamplePoints;
std::vector <sPoint> ProcessList;
// create the grid
double CellSize = MinDist / sqrt(2.0f);
int GridW = (int)(ceil)(1.0f / CellSize);
int GridH = (int)(ceil)(1.0f / CellSize);
sGrid Grid(GridW, GridH, CellSize);
sPoint FirstPoint;
do
{
FirstPoint = sPoint(Generator.RandomDouble(), Generator.RandomDouble());
} while (!(Circle ? FirstPoint.IsInCircle() : FirstPoint.IsInRectangle()));
//Update containers
ProcessList.push_back(FirstPoint);
SamplePoints.push_back(FirstPoint);
Grid.Insert(FirstPoint);
// generate new points for each point in the queue
while (!ProcessList.empty() && SamplePoints.size() < NumPoints)
{
#if POISSON_PROGRESS_INDICATOR
// a progress indicator, kind of
if (SamplePoints.size() % 100 == 0) std::cout << ".";
#endif // POISSON_PROGRESS_INDICATOR
sPoint Point = PopRandom<PRNG>(ProcessList, Generator);
for (int i = 0; i < NewPointsCount; i++)
{
sPoint NewPoint = GenerateRandomPointAround(Point, MinDist, Generator);
bool Fits = Circle ? NewPoint.IsInCircle() : NewPoint.IsInRectangle();
if (Fits && !Grid.IsInNeighbourhood(NewPoint, MinDist, CellSize))
{
ProcessList.push_back(NewPoint);
SamplePoints.push_back(NewPoint);
Grid.Insert(NewPoint);
continue;
}
}
}
#if POISSON_PROGRESS_INDICATOR
std::cout << std::endl << std::endl;
#endif // POISSON_PROGRESS_INDICATOR
return SamplePoints;
}
}
and the main program is:
poisson.cpp
#include "stdafx.h"
#include <vector>
#include <iostream>
#include <fstream>
#include <memory.h>
#define POISSON_PROGRESS_INDICATOR 1
#include "PoissonGenerator.h"
const int NumPoints = 20000; // minimal number of points to generate
int main()
{
PoissonGenerator::DefaultPRNG PRNG;
const auto Points =
PoissonGenerator::GeneratePoissonPoints(NumPoints,PRNG);
std::ofstream File("Poisson.txt", std::ios::out);
File << "NumPoints = " << Points.size() << std::endl;
for (const auto& p : Points)
{
File << " " << p.x << " " << p.y << std::endl;
}
system("PAUSE");
return 0;
}
Suppose you have a point in the space [0,1] x [0,1], in the form of a std::pair<double, double>, but desire points in the space [x,y] x [w,z].
The function object
struct ProjectTo {
double x, y, w, z;
std::pair<double, double> operator(std::pair<double, double> in)
{
return std::make_pair(in.first * (y - x) + x, in.second * (z - w) + w);
}
};
will transform such an input point into the desired output point.
Suppose further you have a std::vector<std::pair<double, double>> points, all drawn from the input distribution.
std::copy(points.begin(), points.end(), points.begin(), ProjectTo{ x, y, w, z });
Now you have a vector of points in the output space.
I have a template 2D image buffer class that can be used with many values types. The values are stored as a 1D dynamic array of T, accessed by a Row method to get a pointer to the correct row.
One of the methods of the class is used to sample a value in the image bilinearly.
The code generally works, but ever so rarely I get an access violation exception in this method in production which I can't seem to recreate, because the crash dump doesn't include the coordinates that were passed to the method.
These are the relevant parts of the code:
T* data;
int width, height;
T* Row(int y) const { return data + width * y; }
T GetValueBilinear(float x, float y) const
{
const float PIXEL_CENTER_OFFSET = 0.5F;
const float cx = clamp(0.0F, width - 1.0F, x - PIXEL_CENTER_OFFSET);
const float cy = clamp(0.0F, height - 1.0F, y - PIXEL_CENTER_OFFSET);
const float tx = fmod(cx, 1.0F);
const float ty = fmod(cy, 1.0F);
const int xInt = (int)cx;
const int yInt = (int)cy;
const T* r0 = Row(yInt);
const T* r1 = ty && yInt < (height - 1) ? Row(yInt + 1) : r0;
//interpolate on Y
const T& c00 = r0[xInt];
const T& c01 = r1[xInt];
T c0 = lerp(c00, c01, ty);
if (tx && xInt < (width - 1))
{
//interpolate on X
const T& c10 = r0[xInt + 1];
const T& c11 = r1[xInt + 1];
T c1 = lerp(c10, c11, ty);
return lerp(c0, c1, tx);
}
else
{
return c0;
}
}
The definitions for clamp, and lerp are:
template <typename T>
inline T clamp(T min, T max, T value) { return value < min ? min : value > max ? max : value; }
template <typename T>
inline T lerp(T a, T b, float t) { return a + (b - a) * t; } //i.e. a(1-t)+bt
Do you see any obvious errors which would cause an access violation for any values of x and y which are not NaN?
You can assume that width, height and data are valid and correct (i.e., positive dimensions - in this particular case 1280x720, data is not dangling pointer).
If it matters, then T is a float in this case.
The fact that this is non-reproducible and generally working 99.9% of the time, makes me feel like it could be an accuracy issue, though I can't see where it would come from.
Alternatively, what debugging techniques could I use to analyze the crash dumps more effectively?
I tested your GetValueBilinear with 1073741824 random values for the pair (x,y) on a 1280x720 data with no access violation.. so I would say it is working fine 99.999999%1 of the time :-) I suspect the problem is not in GetValueBilinear but elsewhere...
#include <cmath>
#include <algorithm>
template <typename T>
inline T clamp(T min, T max, T value) { return value < min ? min : value > max ? max : value; }
template <typename T>
inline T lerp(T a, T b, float t) { return a + (b - a) * t; } //i.e. a(1-t)+bt
template < typename T >
class C
{
public:
C(int w, int h) : height(h), width(w) {
float lower_bound = T(0);
float upper_bound = std::nextafter(T(255), std::numeric_limits<T>::max());
std::uniform_real_distribution<float> unif(lower_bound, upper_bound);
std::default_random_engine re;
data = new T[width*height];// I know... a leak! But... who cares?!
std::generate(data, data + (width*height), [&]() {return unif(re); });
}
T GetValueBilinear(float x, float y) const
{
const float PIXEL_CENTER_OFFSET = 0.5F;
const float cx = clamp(0.0F, width - 1.0F, x - PIXEL_CENTER_OFFSET);
const float cy = clamp(0.0F, height - 1.0F, y - PIXEL_CENTER_OFFSET);
const float tx = fmod(cx, 1.0F);
const float ty = fmod(cy, 1.0F);
const int xInt = (int)cx;
const int yInt = (int)cy;
const T* r0 = Row(yInt);
const T* r1 = ty && yInt < (height - 1) ? Row(yInt + 1) : r0;
//interpolate on Y
const T& c00 = r0[xInt];
const T& c01 = r1[xInt];
T c0 = lerp(c00, c01, ty);
if (tx && xInt < (width - 1))
{
//interpolate on X
const T& c10 = r0[xInt + 1];
const T& c11 = r1[xInt + 1];
T c1 = lerp(c10, c11, ty);
return lerp(c0, c1, tx);
}
else
{
return c0;
}
}
T* data;
int width, height;
T* Row(int y) const { return data + width * y; }
};
#include <random>
#include <iostream>
#include <Windows.h>
float x;
float y;
LONG WINAPI my_filter(_In_ struct _EXCEPTION_POINTERS *ExceptionInfo)
{
std::cout << x << " " << y << "\n";
return EXCEPTION_EXECUTE_HANDLER;
}
int main()
{
auto a = ::SetUnhandledExceptionFilter(my_filter);
float lower_bound = -(1 << 20);
float upper_bound = -lower_bound;
std::uniform_real_distribution<float> unif(lower_bound, upper_bound);
std::default_random_engine re;
float acc = 0;
C<float> img(1280, 720);
img.GetValueBilinear(1.863726958e-043, 1.5612089e-038);
for (size_t i = 0; i < (1 << 30); i++) {
x = unif(re);
y = unif(re);
acc += img.GetValueBilinear(x, y);
}
return static_cast<int>(acc);
}
1Even if no access violation was found I cannot say that the algorithm works well 100%, using a naïve model and this R code:
prop.test(0,1073741824)
I get a confidence interval for the true value of the proportion, the interval is (0.000000e+00, 4.460345e-09) and so the success percentage is (1-4.460345e-09)*100, but... do not trust me, I am not a statistician!
I have a set of bounded rectangles as Rect in a vector.
vector(Rect) boundRect( contours.size() );
I want to sort these rectangles like in the image below
image http://img42.com/liVFt
I have already tried using the method below, but I am not getting the order like in the image I have posted.
stable_sort( boundRect.begin(), boundRect.end(), compareX_rect );
stable_sort( boundRect.begin(), boundRect.end(), compareY_rect );
bool compareX_rect(const Rect & a, const Rect &b) {
return a.x >= b.x;
}
bool compareY_rect(const Rect & a, const Rect &b) {
return a.y >= b.y;
}
Can someone please help me with this? Thanks in advance.
combine into a single sort where the sort will compare y value first, then next on x value:
EDIT: Fixed the sort Tested on coding ground:
bool compareFn(Rectangle* l, Rectangle* r) {
if(l->y == r->y) return l->x < r->x;
return (l->y < r->y);
}
And to reduce noise (depending how much noise is involved) you can do a floor or round function, or calculate a 'cell' that the y value is a part of. Just increase the cell size until it overcomes the noise:
float cellSize = 20.0f;
bool compareFn(Rectangle* l, Rectangle* r) {
float lCell = floorf(l->y / cellSize);
float rCell = floorf(r->y / cellSize);
if(lCell == rCell) return l->x < r->x;
return (lCell < rCell);
}
And here's the program testing it (without noise reduction):
#include <iostream>
#include <vector>
#include <algorithm> // std::sort
using namespace std;
struct Rectangle {
float x;
float y;
float width;
float height;
Rectangle(float x_, float y_, float w_, float h_)
: x(x_)
, y(y_)
, width(w_)
, height(h_)
{}
};
bool compareFn(Rectangle* l, Rectangle* r) {
if(l->y == r->y) return l->x < r->x;
return (l->y < r->y);
}
int main()
{
vector<Rectangle*> rectangles;
for(int x=0; x<10; ++x) {
for(int y=0; y<10; ++y) {
Rectangle* rect = new Rectangle((9 - x) * 50, (9-y) * 50, 50, 50);
rectangles.push_back(rect);
}
}
printf("SORTING\n");
sort(rectangles.begin(), rectangles.end(), compareFn);
printf("RESULTS\n");
for(vector<Rectangle*>::iterator it=rectangles.begin(), end=rectangles.end(); it!=end; ++it) {
Rectangle* rect = *it;
printf("[%f, %f, %f, %f]\n", rect->x, rect->y, rect->width, rect->height);
}
return 0;
}