From arrays to vectors

I must be able to draw infinite number of circles, each must be draggable and droppable to a new location. When dropping it, redraw of the old circles on their position must be done. I have a working program for the X and Y coordinates when declared like X[4][40], but the maximum number of circles is n = 40 now. In order to enable the program to draw infinite number of circles, I am trying to use std::vector for initializing X and Y, and use push_back on LeftMouseButtonUp event. With this vector initializations, the drag and drop function is not redrawing all the circles. In fact, when numofinitn is set to 2, only the first circle is redrawn on drag and drop of the last circle. When numofinitn is set to 3, then the first two circles are redrawn. Etc. Now, how to 're-initialize' the vectors X and Y on drag and drop, so that numofinitn equals the number of circles n? I tried .resize(...), but this is setting all the values in the vector to default values. How to put some code in drag and drop function so all the old circles are redrawn on drag and drop of one of the circles?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int numofinitn = 3;//or 2
int numofinitm = 1;
//int  X[4][40];
//double Y[4][40];
std::vector<int> vx(numofinitn, default_int);
std::vector<std::vector<int>> X(numofinitm, vx);
std::vector<double> vy(numofinitn, default_double);
std::vector<std::vector<double>> Y(numofinitm, vy);

case WM_LBUTTONUP: {
n++;

	        		    		std::vector<int> myx;
	        		    		std::vector<double> myy;
	        		    		myx.push_back(x-6);
	        		    		myy.push_back((y-6)/6*6);
	        		    		X.push_back(myx);
	        		    		Y.push_back(myy);
}
You look to be a little overloaded.

Remember to keep stuff separated. It helps to keep a self-cheat-sheet of sorts to remember what is what.


(1)
You need to keep track of circles: you should have a circle type:

1
2
3
4
struct circle
{
  int x, y, r;  // center x, center y, radius
};


(2)
You need to keep track of more than one circle, and your code is not permitted to forget about any circles: you should have a global list of them:

 
std::vector <circle> circles;


(3)
You need to have a stacking order: some circles are on top (draw last, select first), other circles are on bottom (draw first, select last):

1
2
std::vector <circle> circles;
// stacking order is first → bottom-most to last → top-most 


(4)
Click event handler to add a circle needs to add a circle to the top of the list:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
case WM_LBUTTONDOWN:
  last_x = x;  // what kind of variable should last_x and last_y be?
  last_y = y;  // local or global?
  break;
  // (because we are done with them for now... but we need their values when
  //  the user finally lets go of the mouse button)

case WM_LBUTTONUP:
{
  // notice how r is a local variable -- it only exists inside of the enclosing { and } braces
  int r = std::sqrt( (x - last_x) * (x - last_x) + (y - last_y) * (y - last_y) );
  circles.push_back( { x, y, r } );
  // (we don't need r any more. rest in peace, r.)
}
break;


(5)
You need to draw the circles, bottom to top. (You do have a function specifically for painting, right?)

1
2
3
case WM_PAINT:
  PaintProc( ... );
  break;
1
2
3
4
5
6
7
8
9
10
void PaintProc( ... )
{
  // repaint the background here

  // repaint all the circles
  for (auto c : circles)
    // draw the circle c here

  //...
}


(6)
You need to be able to select a circle. A function will help:

1
2
3
4
5
bool is_point_in_circle( const point& p, const circle& c )
{
  int d = std::sqrt( (c.x - p.x) * (c.x - p.x) + (c.y - p.y) * (c.y - p.y) );
  return d <= c.r;
}

Hmm... I have written the same 'distance' code twice now...
This is a good candidate for a function! ;^)

Rewrite:

1
2
3
4
5
6
7
8
9
int distance( int x1, int y1, int x2, int y2 )
{
  return std::sqrt( ... );
}

bool is_point_in_circle( const point& p, const circle& c )
{
  return distance( c.x, c.y, p.x, p.y ) <= c.r;
}

Nice! Don't forget to fix the LMB up code to use the new distance() function too.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
case WM_RBUTTONUP:
{
  // Remember, top-most to bottom-most
  int n = circles.size();
  while (n--)
    if (is_point_in_circle( point{ x, y }, circles[n] ))
    {
      // invalidate the region where the circle is
      ...
      // erase the found circle
      circles.erase( std::next( circles.begin(), n ) );
      // done
      break;
    }
}
break;


Et cetera. Remember, one thing at a time.

What happens when you are asked to track other shapes besides circles?
You might have to change your vector to hold a different kind of structure than just a circle.

 
std::vector <shape> shapes;

For your assignment, a simple, hand-made discriminated union will do:

1
2
3
4
5
6
7
8
9
10
11
struct shape
{
  typedef enum { CIRCLE, RECTANGLE, ... } shape_type;
  shape_type type;
  union
  {
    circle    c;
    rectangle r;
    ...
  };
};


Hope this helps.
Last edited on
@bchinfosieeuw,

Since we can't see code that does actual drawing, most of the behavior you describe is outside of our view.

I can comment on what I see here in code, and I can infer your plan from your narrative above it, possibly deducing a direction you should consider.

First, the code above. It seems clear that the X and Y vector of vectors are always to be "in sync", which is to say they are really two parts of one concept. I submit there should be only one, not two, such vector of vectors, and the interior vector should not be an int or double, but a structure. That way there's one concept represented by one object, not a synchronized pair.

Something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct point
{
 int x;
 double y;
};

typedef std::vector< point >   pointvec;
typedef std::vector< pointvec >  vec_of_points;

vec_of_points P( numofinitn );

.....later you can then

pointvec pv;

pv.push_back( point( x-6, (y-6)/(6*6) ); // or however you like to make points

P.push_back( pv );



Now, I just prefer to typedef. That's up to you if you like long hand.

The point here is that there should be a single container of the concept of a point, not a separate container for every particle of that concept.

It may be that you need other members in point to help drawing logic, I can't say without seeing that.

The real issue then moves to what is your plan for drawing?

That said, what purpose have you for the nested vector? Will the interior vector always have only 1 point?
Making a simple “shape drawing” program is a very common homework assignment in second-year classes.

+1 on using typedefs.
@bchinfosieeuw,

@Duthomhas is more thought out on this, we posted about the same time it seems.
We must be in about the same timezone, methinks. You in Silicon Valley? Silicon Slopes?
:) Way on the other side. Only further east one can go from here is the ocean.
Topic archived. No new replies allowed.