Related
I am trying to sort a vector of pointers to a class A using std::sort, but am having some difficulties.
Imagine class A beings just a point, containing x and y coordinates. I want to sort the vector by y coordinates from biggest to lowest using some fixed offset value. And on top of this I want to sort it by x coordinatex, from lowest to biggest. I've had something like following in mind but as you can imagine its not working as wanted:
bool A::sortByCoordinates(const A *a, const A *b)
{
if ((b->y < a->y - offset) || (b->y > a->y + offset)) {
return false;
} else if (b->x < a->x) {
return true;
}
return false;
}
void A::recalculate(std::vector<A *> &test)
{
std::sort(test.begin(), test.end(), sortByCoordinates);
}
In short, if b->y < a->y - offset or b->y > a->y + offset treat it as b->y == a->y and then sort by their x coordinates from lowest to biggest. If the above is not true sort it as b->y < a->y.
How can I achieve this?
EDIT:
Imagine a xy plane such as this:
where black dots represent class A with x and y coordinates. I want to split this plane into finitely many sections which are represented by the red lines and are wide as offset. Now I want to treat points in these sections as tho they had the same y coordinate and sort them only by their x coordinate.
(b->y < a->y - offset) || (b->y > a->y + offset)
These are two different cases, which should have different results. I suppose that b is "less" then a in first case, and "greater" in the other case, but your code returns false for both cases. See #Jonathan's answer on how to fix this.
But also note a that your approach (considering all y's that differ by less than offset equal) has a major flaw: it will not generate a strict total order. Assume you have offset=3 and three points A(x=0,y=0), B(x=-2,y=2) and C(x=-4,y=4).
You will have A and B falling within offset by y coordinate so you will compare A.x and B.x and get A<B. Similarly you get B<C, but A and C do not fall within offset distance, so you get A>C, that is A<B<C<A, which should never be. (Tweak the coordinates if I get your ordering wrong). Therefore you first need to reconsider your ordering concept.
If (as you specify in the edit) you want to divide into horizontal stripes, and sort points within a stripe by x, then you should not check whether the ys differ by no more than offset, because two points can have their ys differ by less then offset, but still be located in different stripes. I would suggest to explicitly calculate the stripe number and compare them, something like
bool A::sortByCoordinates(const A& a, const A& b)
{
// get stripe numbers, assuming A::y to be positive int
int stripeA = a.y / offset; // be careful if a.y can be negative!
int stripeB = b.y / offset;
if (stripeA != stripeB)
return stripeA < stripeB;
return a.x < b.x;
}
I was doing a practice question and it was something like this,We are given N pair of coordinates (x,y) and we are given a central point too which is (x0,y0).We were asked to find maximum number of points lying on a line passing from (x0,y0).
My approach:- I tried to maintain a hash map having slope as the key and I thought to get the maximum second value to get maximum number of points on the same line.Something like this
mp[(yi-y0)/(xi-x0))]++; //i from 0 to n
And iterating map and doing something line this
if(it->second >max) //it is the iterator
max=it->second;
and printing max at last;
Problem With my approach- Whenever I get (xi-x0) as 0 I get runtime error.I also tried atan(slope) so that i would get degrees instead of some not defined value but still its not working.
What i expect->How to remove this runtime error and is my approach correct for finding maximum points on a line passing from a point(x0,y0).
P.S -My native language is not english so please ignore if something goes wrong.I tried my best to make everything clear If i am not clear enough please tell me
I'm assuming no other points have the same coordinates as your "origin".
If all your coordinates happen to be integers, you can keep a rational number (i.e. a pair of integers, i.e. a numerator and a denominator) as the slope, instead of a single real number.
The slope is DeltaY / DeltaX, so all you have to do is keep the pair of numbers separate. You just need to take care to divide the pair by their greatest common divisor, and handle the case where DeltaX is zero. For example:
pair<int, int> CalcSlope (int x0, int y0, int x1, int y1)
{
int dx = abs(x1 - x0), dy = abs(y1 - y0);
int g = GCD(dx, dy);
return {dy / g, dx / g};
}
Now just use the return value of CalcSlope() as your map key.
In case you need it, here's one way to calculate the GCD:
int GCD (int a, int b)
{
if (0 == b) return a;
else return gcd(b, a % b);
}
You have already accepted an answer, but I would like to share my approach anyway. This method uses the fact that three points a, b, and c are covariant if and only if
(a.first-c.first)*(b.second-c.second) - (a.second-c.second)*(b.first-c.first) == 0
You can use this property to create a custom comparison object like this
struct comparePoints {
comparePoints(int x0 = 0, int y0 = 0) : _x0(x0), _y0(y0) {}
bool operator()(const point& a, const point& b) {
return (a.first-_x0)*(b.second-_y0) - (b.first-_x0)*(a.second-_y0) < 0;
}
private:
int _x0, _y0;
};
which you can then use as a comparison object of a map according to
comparePoints comparator(x0, y0);
map<pair<int, int>, int, comparePoints> counter(comparator);
You can then add points to this map similar to what you did before:
if (!(x == x0 && y == y0))
counter[{x,y}]++;
By using comparitor as a comparison object, two keys a, b in the map are considered equal if !comparator(a, b) && !comparator(b,a), which is true if and only if a, b and {x0,y0} are collinear.
The advantage of this method is that you don't need to divide the coordinates which avoids rounding errors and problems with dividing by zero, or calculate the atan which is a costly operation.
Move everything so that the zero point is at the origin:
(xi, yi) -= (x0, y0)
Then for each point (xi, yi), find the greatest common divisor of xi and yi and divide both numbers by it:
k = GCD(xi, yi)
(xi', yi`) = (yi/k, yi/k)
Now points that are on the same ray will map to equal points. If (xi, yi) is on the same ray as (xj, yj) then (xi', yi') = (xj', yj').
Now find the largest set of equal points (don't forget any (xi, yi) = (0, 0)) and you have your answer.
You've a very original approach here !
Nevertheless, a vertical line has a infinite slope and this is the problem here: dividing by 0 is not allowed.
Alternative built on your solution (slope):
...
int mpvertical=0; // a separate couner for verticals
if (xi-x0)
mp[(yi-y0)/(xi-x0))]++;
else if (yi-y0)
mpvertical++;
// else the point (xi,yi) is the point (x0,y0): it shall not be counted)
This is cumbersome, because you have everything in the map plus this extra counter. But it will be exact. A workaround could be to count such points in mp[std::numeric_limits<double>::max()], but this would be an approximation.
Remark: the case were xi==x0 AND yi==y0 corresponds to your origin point. These points have to be discarded as they belong to every line line.
Trigonomic alternative (angle):
This uses the general atan2 formula used to converting cartesian coordinates into polar coordinates, to get the angle:
if (xi!=x0 && yi!=y0) // the other case can be ignored
mp[ 2*atan((yi-y0)/((xi-x0)+sqrt(pow(xi-x0,2)+pow(yi-y0,2)))) ]++;
so your key for mp will be an angle between -pi and +pi. No more extra case, but slightly more calculations.
You can hide these extra details and use the slighltly more optimized build in function:
if (xi!=x0 && yi!=y0) // the other case can be ignored
mp[ atan2(yi-y0, xi-x0) ]++;
you can give this approach a try
struct vec2
{
vec2(float a,float b):x(a),y(b){}
float x,y;
};
bool isColinear(vec2 a, vec2 b, vec2 c)
{
return fabs((a.y-b.y)*(a.x-c.x) - (a.y-c.y)*(a.x-b.x)) <= 0.000001 ;
}
This is the question:
Write the definition of a function minMax that has five parameters. The first three parameters are integers. The last two are set by the function to the largest and smallest of the values of the first three parameters. The function does not return a value.
The function can be used as follows:
int a = 31, b = 5, c = 19, big, small;
minMax(a, b, c, &big, &small); /* big is now 31; small is now 5 */
This is my code:
void minMax(int x, int y, int z, int* big, int* small)
{
if (x < y && x < z)
*small = x;
else if (y < x && y < z)
*small = y;
else if (z < x && z < y)
*small = z;
if (x > y && x > z)
*big = x;
else if (y > x && y > z)
*big = y;
else if (z > x && z > y)
*big = z;
}
This is the error I'm getting:
Your function did not change the value of small. Make sure you are dereferencing it in your function.
Not sure what's wrong?
Thanks.
I see one immediate problem.
What do you think will happen when you pass the numbers 1, 1 and 7?
Perhaps you may want to consider the use of <= and >= rather than just < and >.
Since that error message looks nothing like any compiler error I've seen before (and the code is valid syntactically), I'd suggest the message is coming from a test harness which probably:
sets the big/small values to numbers other than those being passed in (eg, -9999).
calls the function with test data (eg, 1,1,7).
checks the output varibales to ensure they've been changed to the correct values.
In addition, it's not the most readable code in the world (no offence intended). If you can structure your code in such a way that its intent is clear from a glance (including comments where appropriate), you'll have hordes of future programmers singing your praises and worshiping your name :-)
Something like this shows the intent a little more clearly (IMNSHO) than lots of those else if constructs:
// Populate big/small based on max/min of x, y and z.
void minMax (int x, int y, int z, int *big, int *small) {
// Set by default to x, only change if others are bigger.
*big = x;
if (y > *big) *big = y;
if (z > *big) *big = z;
// Same for small but with reversed comparisons.
*small = x;
if (y < *small) *small = y;
if (z < *small) *small = z;
}
I'm not sure what isn't working. It seems like that would basically work but could be better structured.
Maybe something like this:
void minMax(int x, int y, int z, int* big, int* small)
{
*big = *small = x;
if (y > *big)
*big = y;
if (y < *small)
*small = y;
if (z > *big)
*big = z;
if (z < *small)
*small = z;
}
The error message
Your function did not change the value of small. Make sure you are dereferencing it in your function.
… appears to come from a test harness provided to you by your teacher.
Anyway, it's correct: there are values that you can choose where your function will not assign anything to *small.
For example, with a, b and c the same value, your function will do nothing at all.
Anyway,
for future questions, please provide a complete example program that demonstrates the issue.
So that people will not have to guess and use unreliable telepathy.
Also, the assignment calls for you to implement a function with an ungood signature.
It teaches a Bad Way™ to design functions.
Here is a possible ordinary C++ function signature:
void getMinAndMax( int& smallest, int& largest, int a, int b, int c )
Here is an even better signature with modern C++ technology:
std::pair<int, int> minAndMax( int a, int b, int c )
The absence of a get prefix for the latter function's name is because it is an expression-oriented function, like sin and cos (you wouldn't write getSin or getCos, would you?), while the presence of that prefix for the first function is merely to make the name imperative, to reflect that it's not an expression-oriented function but instead an action-oriended function.
Of course, with C++11 one would let the function accept any number of arguments. Except that as I'm writing this, Visual C++ does not yet support that properly. For example, here is the signature of std::min from the C++11 standard library:
template<class T, class Compare>
T min(initializer_list<T> t, Compare comp);
With C++03 one can do that to some degree by accepting a single container argument, of templated type.
I'm trying to implement fBm onto a sphere for a planet. To create my sphere, I convert it to such from a cube.
Unfortunately, the fBm that gets generated appears as mirrored patches. In addition, it only does it on 2 faces (wrapping the values for the other faces).
This leads to a similarly stretched look when rendered as a sphere
The noise function is the improved noise as described by Ken Perlin,
I adapted this for HLSL:
float fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); }
float lerp(float t, float a, float b) { return a + t * (b - a); }
float grad(int hash, float x, float y, float z) {
int h = hash & 15; // CONVERT LO 4 BITS OF HASH CODE
float u = h<8 ? x : y, // INTO 12 GRADIENT DIRECTIONS.
v = h<4 ? y : h==12||h==14 ? x : z;
return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
}
int p[512] = { 151,...180 }; //0-255 twice
float noise(float x, float y, float z) {
int X = (int)floor(x) & 255; // FIND UNIT CUBE THAT
int Y = (int)floor(y) & 255; // CONTAINS POINT.
int Z = (int)floor(z) & 255;
x -= floor(x); // FIND RELATIVE X,Y,Z
y -= floor(y); // OF POINT IN CUBE.
z -= floor(z);
float u = fade(x), // COMPUTE FADE CURVES
v = fade(y), // FOR EACH OF X,Y,Z.
w = fade(z);
int A = p[X ]+Y, AA = p[A]+Z, AB = p[A+1]+Z, // HASH COORDINATES OF
B = p[X+1]+Y, BA = p[B]+Z, BB = p[B+1]+Z; // THE 8 CUBE CORNERS,
return lerp(w, lerp(v, lerp(u, grad(p[AA ], x , y , z ), // AND ADD
grad(p[BA ], x-1, y , z )), // BLENDED
lerp(u, grad(p[AB ], x , y-1, z ), // RESULTS
grad(p[BB ], x-1, y-1, z ))),// FROM 8
lerp(v, lerp(u, grad(p[AA+1], x , y , z-1 ), // CORNERS
grad(p[BA+1], x-1, y , z-1 )), // OF CUBE
lerp(u, grad(p[AB+1], x , y-1, z-1 ),
grad(p[BB+1], x-1, y-1, z-1 ))));
}
This implementation has worked as expected in a previous project, however for this project it appears to instead create a smoothed out grid when I use the vertex position as the input.
It's a unit cube, so the values aren't integers but I can't figure out why it's not creating the typical Perlin noise texture.
Any help would be greatly appreciated, I'll provide more information if it's needed.
The array of ints p can't be accessed by the function so I'm assuming the values in it are undefined.
A quick fix is to make the array static, but this is really slow.
So now I need to pass in the array. But I'm having trouble with that.
I use the noise function below in a Dx11 planet rendering project. I've included an fBm function too. I found it (written in GLSL) on the WebGL shader programming website ShaderToy.
It was written by the godlike inigo quilez, who authored the site.
Give it a try, I hope it's of some help. All credit should go to inigo quilez for his work. Porting it to HLSL is trivial. I've only tested in in shader model 5, but I'm sure it'll work under 4 at least.
// hash based 3d value noise
// function taken from https://www.shadertoy.com/view/XslGRr
// Created by inigo quilez - iq/2013
// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
// ported from GLSL to HLSL
cbuffer cbNoiseParameters
{
float _rOctaves;
float _rLacunarity;
float _rFrequency;
float _rAmplitude;
float _rGain;
float _rOffset;
};
float hash( float n )
{
return frac(sin(n)*43758.5453);
}
float noise( float3 x )
{
// The noise function returns a value in the range -1.0f -> 1.0f
float3 p = floor(x);
float3 f = frac(x);
f = f*f*(3.0-2.0*f);
float n = p.x + p.y*57.0 + 113.0*p.z;
return lerp(lerp(lerp( hash(n+0.0), hash(n+1.0),f.x),
lerp( hash(n+57.0), hash(n+58.0),f.x),f.y),
lerp(lerp( hash(n+113.0), hash(n+114.0),f.x),
lerp( hash(n+170.0), hash(n+171.0),f.x),f.y),f.z);
}
float fBm( float3 vPt )
{
float octaves = _rOctaves;
float lacunarity = _rLacunarity;
float frequency = _rFrequency;
float amplitude = _rAmplitude;
float gain = _rGain;
float offset = _rOffset;
float value = 0.f;
for( int i = 0; i < octaves; ++ i )
{
value += noise( vPt * frequency ) * amplitude;
amplitude *= gain;
frequency *= lacunarity;
}
return value;
}
I have an algorithm which can find if a point is in a given polygon:
int CGlEngineFunctions::PointInPoly(int npts, float *xp, float *yp, float x, float y)
{
int i, j, c = 0;
for (i = 0, j = npts-1; i < npts; j = i++) {
if ((((yp[i] <= y) && (y < yp[j])) ||
((yp[j] <= y) && (y < yp[i]))) &&
(x < (xp[j] - xp[i]) * (y - yp[i]) / (yp[j] - yp[i]) + xp[i]))
c = !c;
}
return c;
}
given this, how could I make it check if its within a rectangle defind by Ptopleft and Pbottomright instead of a single point?
Thanks
Basically you know how in Adobe Illustrator you can drag to select all objects that fall within the selection rectangle? well I mean that. –
Can't you just find the minimum and maximum x and y values among the points of the polygon and check to see if any of the values are outside the rectangle's dimensions?
EDIT: duh, I misinterpreted the question. If you want to ensure that the polygon is encosed by a rectangle, do a check for each polygon point. You can do that more cheaply with the minimum/maximum x and y coordinates and checking if that rectangle is within the query rectangle.
EDIT2: Oops, meant horizontal, not vertical edges.
EDIT3: Oops #2, it does handle horizontal edges by avoiding checking edges that are horizontal. If you cross multiply however, you can avoid the special casing as well.
int isPointInRect( Point point, Point ptopleft, Point pbottomright) {
float xp[2] ;
xp[0] = ptopleft.x,
xp[1] = pbottomright.x;
float yp[2] ;
yp[0] = ptopleft.y ;
yp[1] = pbottomright.y ;
return CGlEngineFunctions::PointInPoly(2, xp, yp, point.x, point.y);
}
As mentioned before, for that specific problem, this function is an overkill. However, if you are required to use it, note that:
1. It works only for convex polygons,
2. The arrays holding the polygon's vertices must be sorted such that consecutive points in the array relate to adjacent vertices of your polygon.
3. To work properly, the vertices must be ordered in the "right hand rule" order. That means that when you start "walking" along the edges, you only make left turns.
That said, I think there is an error in the implementation. Instead of:
// c initialized to 0 (false), then...
c = !c;
you should have something like:
// c initialized to 1 (true), then...
// negate your condition:
if ( ! (....))
c = 0;