Alive neighbour cells not correctly counted - c++

I know my title isn't very specific but that's because I have no idea where the problem comes from. I'm stuck with this problem since 2 or 3 hours and in theory everything should be working, but it's not.
This piece of code:
for ( int x = -1; x <= 1; x++ ) { //Iterate through the 8 neighbour cells plus the one indicated
for ( int y = -1; y <= 1; y++ ) {
neighbour = coords(locX + x, locY + y, width); //Get the cell index in the array
if (existsInOrtho(ortho, neighbour)) { //If the index exists in the array
if (ortho[neighbour] == 0) { //Cell is dead
cnt--; //Remove one from the number of alive neighbour cells
}
} else { //Cell is not in the zone
cnt--; //Remove one from the number of alive neighbour cells
}
}
}
Iterates through all the neighbour cells to get their value in the array (1 for alive, 0 for dead). The "coords" function, shown here:
int coords(int locX, int locY, int width)
{
int res = -1;
locX = locX - 1; //Remove one from both coordinates, since an index starts at 0 (and the zone starts at (1;1) )
locY = locY - 1;
res = locX * width + locY; //Small calculation to get the index of the pixel in the array
return res;
}
Gets the index of the cell in the array. But when I run the code, it doesn't work, the number of neighbour cells is not correct (it's like a cell is not counted every time there's some alive in the neighborhood). I tried decomposing everything manually, and it works, so I don't know what ruins everything in the final code... Here is the complete code. Sorry if I made any English mistake, it's not my native language.

This code ...
for ( int x = -1; x <= 1; x++ ) { //Iterate through the 8 neighbour cells plus the one indicated
for ( int y = -1; y <= 1; y++ ) {
Actually checks 9 cells. Perhaps you forgot that it checks (x,y) = (0,0). That would include the cell itself as well as its neighbours.
A simple fix is:
for ( int x = -1; x <= 1; x++ ) { //Iterate through the 8 neighbour cells plus the one indicated
for ( int y = -1; y <= 1; y++ ) {
if (x || y) {
Also, the simulate function (from your link) makes the common mistake of updating the value of the cell in the same array before processing state changes required for the cells beside it. The easiest fix is to keep two arrays -- two complete copies of the grid (two ortho arrays, in your code). When reading from orthoA, update orthoB. And then on the next generation, flip. Read from orthoB and write to orthoA.

Related

Water in a falling sand simulation

I am currently working on a very simple 'Falling Sand' simulation game in C++ and SDL2, and am having problems with getting water to flow in a more realistic manner. I basically have a grid of cells that I iterate through bottom-to-top, left-to-right and if I find a water cell, I just check below, down to left, down to the right, left then right for empty cells and it moves into the first one its finds (it makes a random choice if both diagonal cells or both horizontal cells are free). I then mark the cell it moved into as processed so that it is not checked again for the rest of that loop.
My problem is a sort of 'left-bias' in how the particles move; if I spawn a square of water cells above a barrier, they will basically all shift to left without moving once the particles begin to reach the barrier, while the cells on the right will run down in the proper way. So instead of forming a nice triangular shape flowing out evenly to both sides, the whole shape will just move to the left. This effect is reversed whenever I iterate left-to-right, so I know it's something to do with that but so far I've been stumped trying to fix it. I initially thought it was a problem with how I marked the cells as processed but I've found no obvious bugs with that system in many hours of testing. Has anyone faced any similar challeneges in developing a simulation like this, or knows something that I'm missing? Any help would be very much appreciated.
EDIT:
Ok so I've made a little progress, however I've ran into another bug that seems to be unrelated to iteration, since now I save a copy of the old cells and read from that to decide an update, then update the original cells and display that. This already made the sand work better, however water, which checks horizontally for free cells, now 'disappears' when it does move horizontally. I've been testing it all morning and have yet to find a solution, I thought it might've been someting to do with how I was copying the arrays over, but it seems to work as far as I can tell.
New snippets:
Simulation.cpp
void Simulation::update()
{
copyStates(m_cells, m_oldCells); // so now oldcells is the last new state
for(int y = m_height - 1; y>= 0; y--)
for(int x = 0; x < m_width; x++)
{
Cell* c = getOldCell(x, y); // check in the old state for possible updates
switch(c->m_type)
{
case EMPTY:
break;
case SAND:
if(c->m_visited == false) update_sand(x, y);
break;
case WATER:
if(c->m_visited == false) update_water(x, y);
break;
default:
break;
}
}
}
void Simulation::update_water(int x, int y)
{
bool down = (getOldCell(x, y+1)->m_type == EMPTY) && checkBounds(x, y+1) && !getOldCell(x, y+1)->m_visited;
bool d_left = (getOldCell(x-1, y+1)->m_type == EMPTY) && checkBounds(x-1, y+1) && !getOldCell(x-1, y+1)->m_visited;
bool d_right = (getOldCell(x+1, y+1)->m_type == EMPTY) && checkBounds(x+1, y+1) && !getOldCell(x+1, y+1)->m_visited ;
bool left = (getOldCell(x-1, y)->m_type == EMPTY) && checkBounds(x-1, y) && !getOldCell(x-1, y)->m_visited ;
bool right = (getOldCell(x+1, y)->m_type == EMPTY) && checkBounds(x+1, y) && !getOldCell(x+1, y)->m_visited ;
// choose random dir if both are possible
if(d_left && d_right)
{
int r = rand() % 2;
if(r) d_right = false;
else d_left = false;
}
if(left && right)
{
int r = rand() % 2;
if(r) right = false;
else left = false;
}
if(down)
{
getCell(x, y+1)->m_type = WATER; // we now update the new state
getOldCell(x, y+1)->m_visited = true; // mark as visited so it will not be checked again in update()
} else if(d_left)
{
getCell(x-1, y+1)->m_type = WATER;
getOldCell(x-1, y+1)->m_visited = true;
} else if(d_right)
{
getCell(x+1, y+1)->m_type = WATER;
getOldCell(x+1, y+1)->m_visited = true;
} else if(left)
{
getCell(x-1, y)->m_type = WATER;
getOldCell(x-1, y)->m_visited = true;
} else if(right)
{
getCell(x+1, y)->m_type = WATER;
getOldCell(x+1, y)->m_visited = true;
}
if(down || d_right || d_left || left || right) // the original cell is now empty; update the new state
{
getCell(x, y)->m_type = EMPTY;
}
}
void Simulation::copyStates(Cell* from, Cell* to)
{
for(int x = 0; x < m_width; x++)
for(int y = 0; y < m_height; y++)
{
to[x + y * m_width].m_type = from[x + y * m_width].m_type;
to[x + y * m_width].m_visited = from[x + y * m_width].m_visited;
}
}
Main.cpp
sim.update();
Uint32 c_sand = 0xedec9a00;
for(int y = 0; y < sim.m_height; y++)
for(int x = 0; x < sim.m_width; x++)
{
sim.getCell(x, y)->m_visited = false;
if(sim.getCell(x, y)->m_type == 0) screen.setPixel(x, y, 0);
if(sim.getCell(x, y)->m_type == 1) screen.setPixel(x, y, c_sand);
if(sim.getCell(x, y)->m_type == 2) screen.setPixel(x, y, 0x0000cc00);
}
screen.render();
I've attached a gif showing the problem, hopefully this might help make it a little clearer. You can see the sand being placed normally, then the water and the strange patterns it makes after being placed (notice how it moves off to the left when it's spawned, unlike the sand)
You also have to mark the destination postion as visited to stop multiple cells moving in to the same place.

Collision detection in voxel world

I am kinda stuck with my basic voxel physics right now. It's very, very choppy and I am pretty sure my maths is broken somewhere, but let's see what you have to say:
// SOMEWHERE AT CLASS LEVEL (so not being reinstantiated every frame, but persisted instead!)
glm::vec3 oldPos;
// ACTUAL IMPL
glm::vec3 distanceToGravityCenter =
this->entity->getPosition() -
((this->entity->getPosition() - gravityCenter) * 0.005d); // TODO multiply by time
if (!entity->grounded) {
glm::vec3 entityPosition = entity->getPosition();
if (getBlock(floorf(entityPosition.x), floorf(entityPosition.y), floorf(entityPosition.z))) {
glm::vec3 dir = entityPosition - oldPos; // Actually no need to normalize as we check for lesser, bigger or equal to 0
std::cout << "falling dir: " << glm::to_string(dir) << std::endl;
// Calculate offset (where to put after hit)
int x = dir.x;
int y = dir.y;
int z = dir.z;
if (dir.x >= 0) {
x = -1;
} else if (dir.x < 0) {
x = 1;
}
if (dir.y >= 0) {
y = -1;
} else if (dir.y < 0) {
y = 1;
}
if (dir.z >= 0) {
z = -1;
} else if (dir.z < 0) {
z = 1;
}
glm::vec3 newPos = oldPos + glm::vec3(x, y, z);
this->entity->setPosition(newPos);
entity->grounded = true; // If some update happens, grounded needs to be changed
} else {
oldPos = entity->getPosition();
this->entity->setPosition(distanceToGravityCenter);
}
}
Basic idea was to determine from which direction entityt would hit the surface and then just position it one "unit" back into that direction. But obviously I am doing something wrong as that will always move entity back to the point where it came from, effectively holding it at the spawn point.
Also this could probably be much easier and I am overthinking it.
As #CompuChip already pointed out, your ifs could be further simplified.
But what is more important is one logical issue that would explain the "choppiness" you describe (Sadly you did not provide any footage, so this is my best guess)
From the code you posted:
First you check if entity is grounded. If so you continue with checking if there is a collision and lastly, if there is not, you set the position.
You have to invert that a bit.
Save old position
Check if grounded
Set the position already to the new one!
Do collision detection
Reset to old position IF you registered a collision!
So basically:
glm::vec3 distanceToGravityCenter =
this->entity->getPosition() -
((this->entity->getPosition() - gravityCenter) * 0.005d); // TODO multiply by time
oldPos = entity->getPosition(); // 1.
if (!entity->grounded) { // 2.
this->fallingStar->setPosition(distanceToGravityPoint); // 3
glm::vec3 entityPosition = entity->getPosition();
if (getBlock(floorf(entityPosition.x), floorf(entityPosition.y), floorf(entityPosition.z))) { // 4, 5
this->entity->setPosition(oldPos);
entity->grounded = true; // If some update happens, grounded needs to be changed
}
}
This should get you started :)
I want to elaborate a bit more:
If you check for collision first and then set position you create an "infinite loop" upon first collision/hit as you collide, then if there is a collision (which there is) you set back to the old position. Basically just mathematic inaccuracy will make you move, as on every check you are set back to the old position.
Consider the if-statements for one of your coordinates:
if (dir.x >= 0) {
x = -1;
}
if (dir.x < 0) {
x = 1;
}
Suppose that dir.x < 0. Then you will skip the first if, enter the second, and x will be set to 1.
If dir.x >= 0, you will enter the first if and x will be set to -1. Now x < 0 is true, so you will enter the second if as well, and x gets set to 1 again.
Probably what you want is to either set x to 1 or to -1, depending on dir.x. You should only execute the second if when the first one was not entered, so you need an else if:
if (dir.x >= 0) {
x = -1;
} else if (dir.x < 0) {
x = 1;
}
which can be condensed, if you so please, into
x = (dir.x >= 0) ? -1 : 1;

C++ Minesweeper checking adjacent squares and adding hints [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
So, I have a pretty good idea of how to implement the majority of the program. However, I am having a hard time coming up with an algorithm to add the hints of array locations adjacent to mines. The real trouble I am seeing is that the edge cases almost make it like you have two functions to deal with it (I have 20 line max on all functions). I know that from the position of the mine we want a loop to check row - 1 to row +1 and col -1 to col +1, but is it possible to do this in one function with the code I have for the game? If so, some advice would be great!
EDIT!
SO I think I have come up with the algorithm that works for all cases, but it is outputting bad info. I am pretty sure it is due to improper casting, but I am unable to see what's wrong.
Here are the two functions I wrote to add the hints:
void add_hints_chk(char ** game_board, int cur_row, int cur_col, int
rows, int cols)
{
int row_start = 0, row_end = 0, col_start = 0, col_end = 0;
if (cur_row - 1 < 0)
{
//Top edge case
row_start = 0;
}
else
{
row_start = cur_row - 1;
}
if (cur_row + 1 > rows - 1)
{
//bottom edge case
row_end = rows - 1;
}
else
{
row_end = cur_row + 1;
}
if (cur_col - 1 < 0)
{
//Left edge case
col_start = 0;
}
else
{
col_start = cur_col - 1;
}
if (cur_col - 1 > cols - 1)
{
//Right edge case
col_end = cols - 1;
}
else
{
col_end = cur_col + 1;
}
add_hints(game_board, row_start, row_end, col_start, col_end);
}
void add_hints(char **board, int row_start, int row_end, int col_start,
int col_end)
{
int tmp_int = 0;
for (int i = row_start; i <= row_end; i++)
{
for (int j = col_start; j <= col_end; j++)
{
if (board[i][j] != '*')
{
if (board[i][j] == ' ')
{
tmp_int = 1;
board[i][j] = (char)tmp_int;
}
else
{
tmp_int = (int)board[i][j];
tmp_int++;
board[i][j] += (char)tmp_int;
}
}
}
}
}
So, when I print the array, I get the little box with a q-mark in it. Am I converting tmp_int back to a char incorrectly?
There are different strategies to handle this. One simple strategy is creating a larger grid (add one line on each side) that is initialized with no bombs; make the board a view that hides the borders. With this strategy you know that you can step out of the game board without causing issues (since the data structure has an additional row).
Alternatively you can test whether the coordinates are within the valid range before calling the function that tests, or as the first step within that function.
Also you can consider precalculating the values for all of the map, whenever you add a bomb to the board during the pre-game phase, increment the counter of bombs in the vicinity for all of the surrounding positions. You can use either of the above approaches to handle the border conditions.
For any cell, C, there are 8 possible locations to check:
# # #
# C #
# # #
Before extracting data from the array, each outer location must be boundary checked.
You may be able to generalize, for example, if the value (column - 1) is out of bounds, you don't need to check 3 locations.
In your case, I would go with the brute force method and check each outer cell for boundary before accessing it. If profiling identifies this as the primary bottleneck, the come back and optimize it. Otherwise move on.
Edit 1: Being blunt
int C_left = C_column - 1;
int C_right = C_column + 1;
if (C_left >= 0)
{
// The left column can be accessed.
}
if (C_right < MAXIMUM_COLUMNS)
{
// The right columns can be accessed.
}
// Similarly for the rows.

Tallest tower with stacked boxes in the given order

Given N boxes. How can i find the tallest tower made with them in the given order ? (Given order means that the first box must be at the base of the tower and so on). All boxes must be used to make a valid tower.
It is possible to rotate the box on any axis in a way that any of its 6 faces gets parallel to the ground, however the perimeter of such face must be completely restrained inside the perimeter of the superior face of the box below it. In the case of the first box it is possible to choose any face, because the ground is big enough.
To solve this problem i've tried the following:
- Firstly the code generates the rotations for each rectangle (just a permutation of the dimensions)
- secondly constructing a dynamic programming solution for each box and each possible rotation
- finally search for the highest tower made (in the dp table)
But my algorithm is taking wrong answer in unknown test cases. What is wrong with it ? Dynamic programming is the best approach to solve this problem ?
Here is my code:
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <cstring>
struct rectangle{
int coords[3];
rectangle(){ coords[0] = coords[1] = coords[2] = 0; }
rectangle(int a, int b, int c){coords[0] = a; coords[1] = b; coords[2] = c; }
};
bool canStack(rectangle &current_rectangle, rectangle &last_rectangle){
for (int i = 0; i < 2; ++i)
if(current_rectangle.coords[i] > last_rectangle.coords[i])
return false;
return true;
}
//six is the number of rotations for each rectangle
int dp(std::vector< std::vector<rectangle> > &v){
int memoization[6][v.size()];
memset(memoization, -1, sizeof(memoization));
//all rotations of the first rectangle can be used
for (int i = 0; i < 6; ++i) {
memoization[i][0] = v[0][i].coords[2];
}
//for each rectangle
for (int i = 1; i < v.size(); ++i) {
//for each possible permutation of the current rectangle
for (int j = 0; j < 6; ++j) {
//for each permutation of the previous rectangle
for (int k = 0; k < 6; ++k) {
rectangle &prev = v[i - 1][k];
rectangle &curr = v[i][j];
//is possible to put the current rectangle with the previous rectangle ?
if( canStack(curr, prev) ) {
memoization[j][i] = std::max(memoization[j][i], curr.coords[2] + memoization[k][i-1]);
}
}
}
}
//what is the best solution ?
int ret = -1;
for (int i = 0; i < 6; ++i) {
ret = std::max(memoization[i][v.size()-1], ret);
}
return ret;
}
int main ( void ) {
int n;
scanf("%d", &n);
std::vector< std::vector<rectangle> > v(n);
for (int i = 0; i < n; ++i) {
rectangle r;
scanf("%d %d %d", &r.coords[0], &r.coords[1], &r.coords[2]);
//generate all rotations with the given rectangle (all combinations of the coordinates)
for (int j = 0; j < 3; ++j)
for (int k = 0; k < 3; ++k)
if(j != k) //micro optimization disease
for (int l = 0; l < 3; ++l)
if(l != j && l != k)
v[i].push_back( rectangle(r.coords[j], r.coords[k], r.coords[l]) );
}
printf("%d\n", dp(v));
}
Input Description
A test case starts with an integer N, representing the number of boxes (1 ≤ N ≤ 10^5).
Following there will be N rows, each containing three integers, A, B and C, representing the dimensions of the boxes (1 ≤ A, B, C ≤ 10^4).
Output Description
Print one row containing one integer, representing the maximum height of the stack if it’s possible to pile all the N boxes, or -1 otherwise.
Sample Input
2
5 2 2
1 3 4
Sample Output
6
Sample image for the given input and output.
Usually you're given the test case that made you fail. Otherwise, finding the problem is a lot harder.
You can always approach it from a different angle! I'm going to leave out the boring parts that are easily replicated.
struct Box { unsigned int dim[3]; };
Box will store the dimensions of each... box. When it comes time to read the dimensions, it needs to be sorted so that dim[0] >= dim[1] >= dim[2].
The idea is to loop and read the next box each iteration. It then compares the second largest dimension of the new box with the second largest dimension of the last box, and same with the third largest. If in either case the newer box is larger, it adjusts the older box to compare the first largest and third largest dimension. If that fails too, then the first and second largest. This way, it always prefers using a larger dimension as the vertical one.
If it had to rotate a box, it goes to the next box down and checks that the rotation doesn't need to be adjusted there too. It continues until there are no more boxes or it didn't need to rotate the next box. If at any time, all three rotations for a box failed to make it large enough, it stops because there is no solution.
Once all the boxes are in place, it just sums up each one's vertical dimension.
int main()
{
unsigned int size; //num boxes
std::cin >> size;
std::vector<Box> boxes(size); //all boxes
std::vector<unsigned char> pos(size, 0); //index of vertical dimension
//gets the index of dimension that isn't vertical
//largest indicates if it should pick the larger or smaller one
auto get = [](unsigned char x, bool largest) { if (largest) return x == 0 ? 1 : 0; return x == 2 ? 1 : 2; };
//check will compare the dimensions of two boxes and return true if the smaller one is under the larger one
auto check = [&boxes, &pos, &get](unsigned int x, bool largest) { return boxes[x - 1].dim[get(pos[x - 1], largest)] < boxes[x].dim[get(pos[x], largest)]; };
unsigned int x = 0, y; //indexing variables
unsigned char change; //detects box rotation change
bool fail = false; //if it cannot be solved
for (x = 0; x < size && !fail; ++x)
{
//read in the next three dimensions
//make sure dim[0] >= dim[1] >= dim[2]
//simple enough to write
//mine was too ugly and I didn't want to be embarrassed
y = x;
while (y && !fail) //when y == 0, no more boxes to check
{
change = pos[y - 1];
while (check(y, true) || check(y, false)) //while invalid rotation
{
if (++pos[y - 1] == 3) //rotate, when pos == 3, no solution
{
fail = true;
break;
}
}
if (change != pos[y - 1]) //if rotated box
--y;
else
break;
}
}
if (fail)
{
std::cout << -1;
}
else
{
unsigned long long max = 0;
for (x = 0; x < size; ++x)
max += boxes[x].dim[pos[x]];
std::cout << max;
}
return 0;
}
It works for the test cases I've written, but given that I don't know what caused yours to fail, I can't tell you what mine does differently (assuming it also doesn't fail your test conditions).
If you are allowed, this problem might benefit from a tree data structure.
First, define the three possible cases of block:
1) Cube - there is only one possible option for orientation, since every orientation results in the same height (applied toward total height) and the same footprint (applied to the restriction that the footprint of each block is completely contained by the block below it).
2) Square Rectangle - there are three possible orientations for this rectangle with two equal dimensions (for examples, a 4x4x1 or a 4x4x7 would both fit this).
3) All Different Dimensions - there are six possible orientations for this shape, where each side is different from the rest.
For the first box, choose how many orientations its shape allows, and create corresponding nodes at the first level (a root node with zero height will allow using simple binary trees, rather than requiring a more complicated type of tree that allows multiple elements within each node). Then, for each orientation, choose how many orientations the next box allows but only create nodes for those that are valid for the given orientation of the current box. If no orientations are possible given the orientation of the current box, remove that entire unique branch of orientations (the first parent node with multiple valid orientations will have one orientation removed by this pruning, but that parent node and all of its ancestors will be preserved otherwise).
By doing this, you can check for sets of boxes that have no solution by checking whether there are any elements below the root node, since an empty tree indicates that all possible orientations have been pruned away by invalid combinations.
If the tree is not empty, then just walk the tree to find the highest sum of heights within each branch of the tree, recursively up the tree to the root - the sum value is your maximum height, such as the following pseudocode:
std::size_t maximum_height() const{
if(leftnode == nullptr || rightnode == nullptr)
return this_node_box_height;
else{
auto leftheight = leftnode->maximum_height() + this_node_box_height;
auto rightheight = rightnode->maximum_height() + this_node_box_height;
if(leftheight >= rightheight)
return leftheight;
else
return rightheight;
}
}
The benefits of using a tree data structure are
1) You will greatly reduce the number of possible combinations you have to store and check, because in a tree, the invalid orientations will be eliminated at the earliest possible point - for example, using your 2x2x5 first box, with three possible orientations (as a Square Rectangle), only two orientations are possible because there is no possible way to orient it on its 2x2 end and still fit the 4x3x1 block on it. If on average only two orientations are possible for each block, you will need a much smaller number of nodes than if you compute every possible orientation and then filter them as a second step.
2) Detecting sets of blocks where there is no solution is much easier, because the data structure will only contain valid combinations.
3) Working with the finished tree will be much easier - for example, to find the sequence of orientations of the highest, rather than just the actual height, you could pass an empty std::vector to a modified highest() implementation, and let it append the actual orientation of each highest node as it walks the tree, in addition to returning the height.

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.