Moving a series of points to be centered on a given point
I have the 5 points of a star calculated as follows:
Code:
// Calculate the outer points of the star
for(int i = 0; i < m_nPoints; i++)
{
m_dAngle += m_dTheta;
int X = (int)(m_dRadius * cos(m_dAngle));
int Y = (int)(m_dRadius * sin(m_dAngle));
m_ptsOuter[i].x = X;
m_ptsOuter[i].y = Y;
}
Now I want this star to be centered at a point given by m_ptCenter.
How can I move the points defined in m_ptsOuter to be centered on m_ptCenter?
Re: Moving a series of points to be centered on a given point
Originally Posted by AvDav
You need to determine the current center, then update all x,y pairs by delta values of new and old centers:
dx = new_center.x - old_center.x;
dy = new_center.y - old_center.y;
for each point in points do
point.x = point.x + dx;
point.y = point.y + dy;
Hmmm, well, I should have known this actually. Anyway, I used this method, but the polygon (not drawing star yet) isn't centering on the point.
I have attached an application that draws this polygon in a dialog.
In the dialogs OnPaint() I get the center of the dialog, set the radius of the star, set the center point, call InitStar() and then draw it. It isn't centering on the dialog.
Re: Moving a series of points to be centered on a given point
Originally Posted by MikeB
In the dialogs OnPaint() I get the center of the dialog, set the radius of the star, set the center point, call InitStar() and then draw it. It isn't centering on the dialog.
The flaw is in your CStar::GetContainingRect() method:
Problem is that you are using else-if for those four conditions - however, they are not mutually exclusive: For example, when ptTemp.x happens to be < rc.left, the y coordinates won't be tested any more, resulting in wrong values of your resulting rectangle - and since you are using it for determining the drawing center later on, your shape gets offset.
Besides that: I wonder why you need that step (calculating the containing rect, and then getting its center point) at all? The way you currently create your polygon, the center point is always (0,0) - and if you fix GetContainingRect(), you will see that the center point you get back is always (obviously) (0,0).
Problem is that you are using else-if for those four conditions - however, they are not mutually exclusive: For example, when ptTemp.x happens to be < rc.left, the y coordinates won't be tested any more, resulting in wrong values of your resulting rectangle - and since you are using it for determining the drawing center later on, your shape gets offset.
Besides that: I wonder why you need that step (calculating the containing rect, and then getting its center point) at all? The way you currently create your polygon, the center point is always (0,0) - and if you fix GetContainingRect(), you will see that the center point you get back is always (obviously) (0,0).
You are absolutely correct, but I want to ensure that if I ever use this class for anything other then my current project, that a star can in fact be moved from its current position. I know I create the orginal polygon at 0,0, but lets say I move it, then move it again?
Actually come to think of it, you are right, I should not need this step at all. If the user was to move it, all I would have to do is re-create the polygon and reposition its center.
Re: Moving a series of points to be centered on a given point
Now I am trying to draw the star by the points defining the perimeter of the star shape. To do this, I create 2 polygons, one for the inside points of the star and one for the outside of the star. EG, for a 5 point star, there are a total of 10 points along the perimeter.
I am getting all the points correctly, but when I try to combine the points into one array and call the CDC::Polygon(...) function, the star shape is not correct.. All the points are, but the shape seems inverted or something.
See attached program for all the code:
here is my InitStar() and DrawStar(...) functions for those that do not
want to download project:
Code:
void CStar::InitStar()
{
m_dTheta = (PI*2)/m_nPoints; // Angle to each point, outer circle
if(m_ptsOuter)
delete [] m_ptsOuter;
m_ptsOuter = new CPoint[m_nPoints];
if(m_ptsInner)
delete [] m_ptsInner;
m_ptsInner = new CPoint[m_nPoints];
// Calculate the outer points of the star
for(int i = 0; i < m_nPoints; i++)
{
m_dAngle += m_dTheta;
int X = (int)(m_dRadius * cos(m_dAngle));
int Y = (int)(m_dRadius * sin(m_dAngle));
m_ptsOuter[i].x = X;
m_ptsOuter[i].y = Y;
}
// Calculate the inner points of the star
double dRad = m_dRadius / 2;
double dAngle = m_dAngle + (m_dTheta / 2);
for(i = 0; i < m_nPoints; i++)
{
int X = (int)(dRad * cos(dAngle));
int Y = (int)(dRad * sin(dAngle));
m_ptsInner[i].x = X;
m_ptsInner[i].y = Y;
dAngle += m_dTheta;
}
// Center star points
for(i = 0; i < m_nPoints; i++)
{
m_ptsOuter[i].x += m_ptCenter.x;
m_ptsOuter[i].y += m_ptCenter.y;
m_ptsInner[i].x += m_ptCenter.x;
m_ptsInner[i].y += m_ptCenter.y;
}
}
void CStar::DrawStar(CDC* pDC)
{
CPoint* pts = new CPoint[m_nPoints*2];
int nPoint = 0;
for(int i = 0; i < m_nPoints * 2; i++)
{
if(i % 2 == 0)
{
pts[i] = m_ptsOuter[nPoint];
}
else
{
pts[i] = m_ptsInner[nPoint];
++nPoint;
}
}
for(i = 0; i < m_nPoints * 2; i++)
{
CPoint pt = pts[i];
}
pDC->Polygon(pts, m_nPoints * 2);
delete [] pts;
}
Re: Moving a series of points to be centered on a given point
Originally Posted by MikeB
I find if I reverse the for loop in the DrawStar(...) function, it is almost correct, but the top of the star goes bad somehow.
Looks like trial-and-error, if you ask me...
Again, your code is unnecessarily complicated (and hence error-prone). Why these two separate arrays for inner and outer points, the two separate loops, and later merging both arrays into one? It would be far easier to have one single array of (m_nPoints * 2) elements, and loop from 0 to < (m_nPoints * 2) - the only thing you need to do is to use the inner and outer radius in an alternating way for calculating the point, depending on the value of i % 2, and incrementing the angle by (m_dTheta / 2). This automatically gets you the polygon points in the right order, without having to deal with three arrays.
Re: Moving a series of points to be centered on a given point
Originally Posted by gstercken
Looks like trial-and-error, if you ask me...
Again, your code is unnecessarily complicated (and hence error-prone). Why these two separate arrays for inner and outer points, the two separate loops, and later merging both arrays into one? It would be far easier to have one single array of (m_nPoints * 2) elements, and loop from 0 to < (m_nPoints * 2) - the only thing you need to do is to use the inner and outer radius in an alternating way for calculating the point, depending on the value of i % 2, and incrementing the angle by (m_dTheta / 2). This automatically gets you the polygon points in the right order, without having to deal with three arrays.
LOL, easy enough, you are the man. 5 More stars for you, correctly centered and drawn!!!
Code:
void CStar::InitStar()
{
m_dTheta = (PI*2)/m_nPoints; // Angle to each point, outer circle
if(m_ptsOuter)
delete [] m_ptsOuter;
m_ptsOuter = new CPoint[m_nPoints*2];
// Calculate the outer points of the star
for(int i = 0; i < m_nPoints * 2; i++)
{
double dRad = i%2==0 ? m_dRadius : m_dRadius/2;
int X = (int)(dRad * cos(m_dAngle));
int Y = (int)(dRad * sin(m_dAngle));
m_ptsOuter[i].x = X;
m_ptsOuter[i].y = Y;
m_dAngle += m_dTheta/2;
}
// Center star points
for(i = 0; i < m_nPoints*2; i++)
{
m_ptsOuter[i].x += m_ptCenter.x;
m_ptsOuter[i].y += m_ptCenter.y;
}
}
void CStar::DrawStar(CDC* pDC)
{
pDC->Polygon(m_ptsOuter, m_nPoints * 2);
}
Re: Moving a series of points to be centered on a given point
Originally Posted by MikeB
5 More stars for you, correctly centered and drawn!!!
Thanks...
Btw, one more suggestion for further optimizing (and simplifying) your code: Instead of deleting and reallocating your point array each time you draw the shape, you could move this to the ctor (where the number of points is already known). The only other place where you would then need to delete and reallocate the array is in your setter for the number of points (SetPoints()).
Re: Moving a series of points to be centered on a given point
Here is the working app so far. I am only posting this in the case
that someone else is interested ever in doing the same type of thing.
The purpose of this "star" class is to be able to display customer ratings
based on a 5 star systems. I know I could have just displayed this numerically but my application is already lacking "fun" so I thought I would
implement a graphical display of the star system.
Anyway, now I need to come up with a way to paint these stars either in whole or partial. I know I can find the intersecting points of a of a dividing line, say at 25 percent to indicate a 1/4 star, and draw two polygons (the left and right) of the star, but does anyone have an easier method. As you can see by the previous posts, I am not one to take the easy route out.
Re: Moving a series of points to be centered on a given point
Originally Posted by gstercken
Thanks...
Btw, one more suggestion for further optimizing (and simplifying) your code: Instead of deleting and reallocating your point array each time you draw the shape, you could move this to the ctor (where the number of points is already known). The only other place where you would then need to delete and reallocate the array is in your setter for the number of points (SetPoints()).
I have moved the allocation to set points, which makes sense. But the points were not calculated each time it was drawn. Only when InitStar() was called which builds the star.
Should I call InitStar from inside all my properties set functions? Or let it be that it is called at the time of creation?
Re: Moving a series of points to be centered on a given point
Originally Posted by MikeB
The purpose of this "star" class is to be able to display customer ratings
based on a 5 star systems. I know I could have just displayed this numerically but my application is already lacking "fun" so I thought I would
implement a graphical display of the star system.
That's fine - but honestly, in a real-world app, I would do this with pre-drawn bitmaps for the stars... I assume that you need partially filled stars for displaying fractional ratings, right? And having only 4 differently filled star bitmaps (0%, 25%, 50% and 100%) already gives you 20 different levels with 5 stars - pretty much for any rating system.
But for the heck of it: The geometrical approach you mentioned (finding the intersecting line and drawing two separate filled polygons seems indeed to be the easiest - especially since you're already bothered with the math to dynamically calculate the polygon points. Any other approach would either use offscreen DCs and clipping regions, or temporary offscreen bitmaps and blitting with ternary ROP codes to combine certain bits of say, the polygon bitmap and a rectangle that determines the fill state. While they involve less geometry math, I estimate that the boilerplate code you need to make them work would be more, in the end, than the approach you mentioned yourself. In any case: There is no simple GDI approach for filling a polygon only partially.
* The Best Reasons to Target Windows 8
Learn some of the best reasons why you should seriously consider bringing your Android mobile development expertise to bear on the Windows 8 platform.