Help with connected outlines - c++

I'm making a vector drawing application. I use this algorithm to generate outlines.
This algorthm works well, except it does not close the outline as seen here:
alt text http://img716.imageshack.us/img716/2633/noclosure.png
I'm not sure what I should do to ensure that it always closes the outline. I tried inserting the last vertex at position[0] in the std::vector but this did not help.
DOUBLEPOINT looks like:
struct DOUBLEPOINT {
double point[2];
};
How can I make it so it always properly closes the shape even on sharp corners?
Thanks

Try appending a copy of the first and second points to the end of the vector before plotting it. Your first and last line segment will overlap, but it should ensure that all the points are connected and all corners are rounded similarly.

I usually just use modulus:
nxt = input[(i+1) % input.size()];

How about:
for( size_t i = 0; i < input.size(); ++i )
{
POINTFLOAT cur;
POINTFLOAT nxt;
if( i == input.size() - 1 )
{
cur.x = input[i].point[0];
cur.y = input[i].point[1];
nxt.x = input[0].point[0];
nxt.y = input[0].point[1];
}
else
{
cur.x = input[i].point[0];
cur.y = input[i].point[1];
nxt.x = input[i+1].point[0];
nxt.y = input[i+1].point[1];
}

Related

How to get the left chain of points of a polygon?

I am trying to get the left polygonal chain given a set of consecutive points. (NOTE: edges are non-intersecting.)
Image 1. Sample polygon and its bound.
What I did was:
Get the minY, maxY and minX. (Bound.)
Find the point that contains minY (or maxY) then save it as the first point.
Save any points until point with minY or maxY is found while checking for point with minX.
If the same Y is found first, save it as the new first point and repeat from #3.
If other Y is found first and the saved points has minX, this is the chain. Otherwise, save as the new first point and repeat from #3.
Image 2. The left chain of points.
But using this steps might give wrong result for some polygon, like this:
Since one point is (minX, maxY), either of the side will be returned.
EDIT:
With the idea of the left-bottom- and left-top-most points, here is the current code that I am using:
Get the min (left-bottom-most) and max (left-top-most) point.
std::vector<Coord> ret;
size_t i = 0;
Coord minCoord = poly[i];
Coord maxCoord = poly[i];
size_t minIdx = -1;
size_t maxIdx = -1;
size_t cnt = poly.size();
i++;
for (; i < cnt; i++)
{
Coord c = poly[i];
if (c.y < minCoord.y) // new bottom
{
minCoord = c;
minIdx = i;
}
else if (c.y == minCoord.y) // same bottom
{
if (c.x < minCoord.x) // left most
{
minCoord = c;
minIdx = i;
}
}
if (c.y > maxCoord.y) // new top
{
maxCoord = c;
maxIdx = i;
}
else if (c.y == maxCoord.y) // same top
{
if (c.x < maxCoord.x) // left most
{
maxCoord = c;
maxIdx = i;
}
}
}
Get the points connected to the max point.
i = maxIdx;
Coord mid = poly[i];
Coord ray1 = poly[(i + cnt - 1) % cnt];
Coord ray2 = poly[(i + 1) % cnt];
Get which has smallest angle. This will be the path we will follow.
double rad1 = Pts2Rad(mid, ray1);
double rad2 = Pts2Rad(mid, ray2);
int step = 1;
if (rad1 < rad2)
step = cnt - 1;
Save the points.
while (i != minIdx)
{
ret.push_back(poly[i]);
i = (i + step) % cnt;
}
ret.push_back(poly[minIdx]);
To be specific, I am assuming that no vertex is duplicated and define the "left chain" as the sequence of vertices from the original polygon loop that goes from the leftmost vertex in the top side of the bounding box, to the leftmost vertex in the bottom side of the bounding box. [In case the top and bottom sides coincide, these two vertices also coincide; I leave it to you what to return in this case.]
To obtain these, you can scan all vertices and keep the left-topmost so far and left-bottommost so far. Then compare to the next vertex. If above the left-topmost, becomes the new lef-topmost. If at the same level and to the left, becomes the new left-topmost. Similarly for the left-bottommost.

Intersection in box2d not working right

I'm trying to use the code here
http://www.iforce2d.net/b2dtut/raycasting
So I can know whether a line cross a box2d object or not. It half works, in that when the line crosses the object it does show as an intersection, however, if you click before the object, it still shows as an intersection, as if it doesn't know the line stops before the object. From reading about this code, it should not do this.
Here's a screen shot of the issue.
And here's the method I'm using for the check
-(b2Vec2)rayCheckWithInput:(b2Vec2)p1 andX:(b2Vec2)p2
{
b2RayCastInput input;
input.p1 = p1;
input.p2 = p2;
input.maxFraction = 1;
//check every fixture of every body to find closest
float closestFraction = 1; //start with end of line as p2
b2Vec2 intersectionNormal(0,0);
//for (b2Body* b = self.world.world->GetBodyList(); b; b = b->GetNext()) {
for (b2Fixture* f = box.body->GetFixtureList(); f; f = f->GetNext()) {
b2RayCastOutput output;
if ( ! f->RayCast( &output, input, 0 ) )
{
NSLog(#"Not Intersected");
continue;
}
if ( output.fraction < closestFraction ) {
closestFraction = output.fraction;
intersectionNormal = output.normal;
NSLog(#"Intersected");
NSLog(#"%f %f,", output.normal.x, output.normal.y);
NSLog(#"%f", output.fraction);
}
else{
NSLog(#"Intersected2");
NSLog(#"%f %f,", output.normal.x, output.normal.y);
NSLog(#"%f", output.fraction);
}
}
//}
b2Vec2 intersectionPoint = p1 + closestFraction * (p2 - p1);
NSLog(#"I point %f, %f", intersectionPoint.x, intersectionPoint.y);
return intersectionPoint;
}
I can't see how in that check I can tell if the click point is before (no intersection) or beyond (intersection) the bird, it seems to give the same result regardless of either of those possibilities.
Any ideas?
It's a problem of unit of measure: you should convert p1 and p2 coordinates (pixel or points) to box2d coordinates (meters).

Changing the SourceRect to count forward and backwards through the sprite sheet

Here is my update method that currently cycles through my 6 sprites for a walking character, it counts 0,1,2,3,4,5 and then resets back to 0.
The task is to have it to cycle forwards and then backwards 0,1,2,3,4,5,4,3,2,1,0,1,2... etc
I've tried to implement several counting methods to count up and count down given certain conditions, but they appear to fight and loop between frame 4/5.
Is there a quick solution? or would anyone be able to point me in the direction of a solution please :)
void SpriteGame::Update(int tickTotal, int tickDelta){
if ( tickTotal >= this->playerLastFrameChange + (TICKS_PER_SECOND / playerSheetLength) )
{
this->playerFrame = this->playerFrame + 1;
this->playerLastFrameChange = tickTotal;
if (this->playerFrame >= this->playerSheetLength)
{
this->playerFrame = 0;
}
this->playerSourceRect->left = this->playerFrame * widthOfSprite;
this->playerSourceRect->top = 0;
this->playerSourceRect->right = (this->playerFrame + 1) * widthOfSprite;
this->playerSourceRect->bottom = widthOfSprite;
}
}
implementing the (abs()) method worked counting 0,1,2,3,4,5,4,3,2,1,2.. etc
//initializing playerFrame = -4; at the top of the .cpp
this->playerFrame = this->playerFrame +1; //keep counting for as long as its <= 5 [sheet length]
if (this->playerFrame >= this->playerSheetLength)
{
this->playerFrame = -4;
}
this->playerSourceRect->left = (abs(playerFrame)) * widthOfSprite;
this->playerSourceRect->top = 0;
this->playerSourceRect->right = (abs(playerFrame)+1) * widthOfSprite;
this->playerSourceRect->bottom = widthOfSprite
A fairly simple way to get it to count up and down continuously would be to run your count from -4 up to 5 (always incrementing). When it goes past 5, set it back to -4.
To get the actual sprite index from that count, just take the absolute of it (abs()). That will give you the positive equivalent of any negative values, but leave the positive values unchanged.
How about sth like this:
char direction=1; //initialize forward
this->playerFrame+=direction;
if(this->playerFrame >= this->playerSheetLength || this->playerFrame <=0)
direction*=-1;
To stick with the arithmetic approach you've started with you need something like this pseudocode
if( /*it's time to update frame*/ )
{
if( currentFrame >= maxFrame ||
currentFrame <= minFrame )
{
incrementer *= -1;
}
currentFrame += incrementer;
// Then calculate the next frame boundary based on the frame number
}
Or if you kept a collection of rectangles in a vector, you could simply iterate to end, then reverse iterate to the beginning, and so on.
Google std::vector, std::vector::begin(), std::vector::end(), std::vector::rbegin(), std::vector::rend().
Quick example:
vector<rect> vec;
.
.
.
for(vector<rect>::iterator iter = vec.begin(); iter != vec.end(); ++iter)
{
....
}
for(vector<rect>::reverse_iterator iter = vec.rbegin(); iter != vec.rend(); ++iter)
{
....
}

Implementing De Boors algorithm for finding points on a B-spline

I've been working on this for several weeks but have been unable to get my algorithm working properly and i'm at my wits end. Here's an illustration of what i have achieved:
If everything was working i would expect a perfect circle/oval at the end.
My sample points (in white) are recalculated every time a new control point (in yellow) is added. At 4 control points everything looks perfect, again as i add a 5th on top of the 1st things look alright, but then on the 6th it starts to go off too the side and on the 7th it jumps up to the origin!
Below I'll post my code, where calculateWeightForPointI contains the actual algorithm. And for reference- here is the information i'm trying to follow. I'd be so greatful if someone could take a look for me.
void updateCurve(const std::vector<glm::vec3>& controls, std::vector<glm::vec3>& samples)
{
int subCurveOrder = 4; // = k = I want to break my curve into to cubics
// De boor 1st attempt
if(controls.size() >= subCurveOrder)
{
createKnotVector(subCurveOrder, controls.size());
samples.clear();
for(int steps=0; steps<=20; steps++)
{
// use steps to get a 0-1 range value for progression along the curve
// then get that value into the range [k-1, n+1]
// k-1 = subCurveOrder-1
// n+1 = always the number of total control points
float t = ( steps / 20.0f ) * ( controls.size() - (subCurveOrder-1) ) + subCurveOrder-1;
glm::vec3 newPoint(0,0,0);
for(int i=1; i <= controls.size(); i++)
{
float weightForControl = calculateWeightForPointI(i, subCurveOrder, controls.size(), t);
newPoint += weightForControl * controls.at(i-1);
}
samples.push_back(newPoint);
}
}
}
//i = the weight we're looking for, i should go from 1 to n+1, where n+1 is equal to the total number of control points.
//k = curve order = power/degree +1. eg, to break whole curve into cubics use a curve order of 4
//cps = number of total control points
//t = current step/interp value
float calculateWeightForPointI( int i, int k, int cps, float t )
{
//test if we've reached the bottom of the recursive call
if( k == 1 )
{
if( t >= knot(i) && t < knot(i+1) )
return 1;
else
return 0;
}
float numeratorA = ( t - knot(i) );
float denominatorA = ( knot(i + k-1) - knot(i) );
float numeratorB = ( knot(i + k) - t );
float denominatorB = ( knot(i + k) - knot(i + 1) );
float subweightA = 0;
float subweightB = 0;
if( denominatorA != 0 )
subweightA = numeratorA / denominatorA * calculateWeightForPointI(i, k-1, cps, t);
if( denominatorB != 0 )
subweightB = numeratorB / denominatorB * calculateWeightForPointI(i+1, k-1, cps, t);
return subweightA + subweightB;
}
//returns the knot value at the passed in index
//if i = 1 and we want Xi then we have to remember to index with i-1
float knot(int indexForKnot)
{
// When getting the index for the knot function i remember to subtract 1 from i because of the difference caused by us counting from i=1 to n+1 and indexing a vector from 0
return knotVector.at(indexForKnot-1);
}
//calculate the whole knot vector
void createKnotVector(int curveOrderK, int numControlPoints)
{
int knotSize = curveOrderK + numControlPoints;
for(int count = 0; count < knotSize; count++)
{
knotVector.push_back(count);
}
}
Your algorithm seems to work for any inputs I tried it on. Your problem might be a that a control point is not where it is supposed to be, or that they haven't been initialized properly. It looks like there are two control-points, half the height below the bottom left corner.

Stack Overflow with Pathfinding Algorithm

I have been working on a project that will, in short, generate a 2D matrix of numbers, with "empty" spaces are represented by 0's. Each number is connected by a list of nodes. The nodes contain the number value, the number's X and Y position, and a list of all spaces adjacent to it (its "neighbors"), with the exception of spaces diagonally adjacent to the point, due to the algorithm only allowing movements of up, down, left, and right. The issue that I am having is that, as the title would suggest, I am experiencing some stack overflow issues. I will post my code below, if anyone could help, I would be most appreciative.
CoordList* Puzzle::GeneratePath(CoordList* Path, int GoalX, int GoalY)
{
int CurrX;
int CurrY;
CurrX = Path->NeighborX;
CurrY = Path->NeighborY;
if(CurrX == GoalX && CurrY == GoalY)
{
return(Path);
}
else
{
int NewX;
int NewY;
double NewDistance;
int OldX;
int OldY;
double OldDistance;
CoordList* PointNeighbors = NULL;
CoordList* BestChoice = NULL;
for(int i = 0; i < NumDirections; i++)
{
CoordList* NewNeighbor = new CoordList;
NewX = CurrX + DirectsX[i];
NewY = CurrY + DirectsY[i];
if(IsPossible(NewX, NewY))
{
NewNeighbor->NeighborX = NewX;
NewNeighbor->NeighborY = NewY;
if(PointNeighbors == NULL)
{
NewNeighbor->next = NULL;
PointNeighbors = NewNeighbor;
}
else
{
NewNeighbor->next = PointNeighbors;
PointNeighbors = NewNeighbor;
}
}
//delete NewNeighbor;
}
while(PointNeighbors != NULL)
{
if(BestChoice == NULL)
{
CoordList* AChoice = new CoordList;
AChoice->next = NULL;
NewX = PointNeighbors->NeighborX;
NewY = PointNeighbors->NeighborY;
AChoice->NeighborX = NewX;
AChoice->NeighborY = NewY;
BestChoice = AChoice;
PointNeighbors = PointNeighbors->next;
//delete AChoice;
}
else
{
NewX = PointNeighbors->NeighborX;
NewY = PointNeighbors->NeighborY;
NewDistance = DetermineDistance(NewX, NewY, GoalX, GoalY);
OldX = BestChoice->NeighborX;
OldY = BestChoice->NeighborY;
OldDistance = DetermineDistance(OldX, OldY, GoalX, GoalY);
if(NewDistance < OldDistance)
{
BestChoice->NeighborX = NewX;
BestChoice->NeighborY = NewY;
}
PointNeighbors = PointNeighbors->next;
}
}
BestChoice->next = Path;
Path = BestChoice;
return(GeneratePath(Path, GoalX, GoalY));
}
}
I was asked to provide my determine distance function. This is just a simple implementation of the traditional Point Distance formula. Provided below.
double Puzzle::DetermineDistance(int OneX, int OneY, int TwoX, int TwoY)
{
int DifX;
int DifY;
double PointSum;
DifX = (TwoX - OneX);
DifY = (TwoY - OneY);
DifX = (DifX * DifX);
DifY = (DifY * DifY);
PointSum = (DifX + DifY);
return (sqrt(PointSum));
}
The following is the IsPossible function, which determines if an X and Y value lies within the possible grid space.
bool Puzzle::IsPossible(int x, int y)
{
if(x + 1 > Size - 1 || x - 1 < 0
|| y + 1 > Size - 1 || y - 1 < 0)
{
return false;
}
return true;
}
You might have a infinite recursion loop that causes the stackoverflow, as you make new local variables every recursion, especially with your observered oscillation behaviour. I assume you dont have that problem with small matrices. Its just a shot in the dark :-)
The oscillation problem indicates that you dont check whether you have already been on one place already?
Anyways, maybe you want to reconsider using another pathfinding algorithm. I would suggest a agent based solution. I used to use the following solution to solve a maze of similar structure: I started an agent with a "PositionsList" of spots where it have been, so in the beginning only with the starting point. Then it copied itself to every reachable position not being in his own PositionList, adding the new position to that list and destroying itself then. Repeat that pattern with all new agents until the first agent reaches the goal. That way you are guaranteed to find the optimal path. But it might get pretty memory heavy for big matrices, especially when there are a lot different ways to get to the goal and a lot of possible directions per position! But there are plenty of other very good pathfinding algorithms out there. Maybe one of them suits you well :-)
Good Luck!