maspeir
September 11th, 2008, 08:30 PM
I figured that this has been dealt with so many time that there would be literally tons of resources all over the web. However, it would seems that it has been done so many times that they all dropped off of the edge of the web. I cannot find anything.
I'm in the VERY early design stages of making a PCB CAD program. I need lines of varying thickness and need precise control over them, so creating wide lines through geometric primitives is preferable to simply "thickening" pixels. I have a very good routine (posted below) that does just that and works very well. The problem is that I cannot figure out how to implement the end caps. I need the option of using either no end cap, square end caps or round end caps. Pretty standard. It *SEEMS* easy, but has proven very difficult. I guess I just cannot get my mind wrapped around the process.
I'd appreciate any help, whether math or code.
This is the code. It is still very early. As soon as I get the caps implemented, I'll switch it over to OpenGL for rendering.
void DrawLine(HDC hDC,Point start,Point end,double width)
{
Point left[2],right[2];
POINT sc_points[4];
double dx,dy,dx1,dy1,l,half_width;
half_width = width * .5;
if(width > 1)
{
dx = (end.x - start.x);
dy = (end.y - start.y);
l = sqrt(dx * dx + dy * dy);
if(l == 0)
return;
dx1 = (dx * half_width) / l;
dy1 = (dy * half_width) / l;
left[0].x = start.x - dy1;
left[0].y = start.y + dx1;
left[1].x = end.x - dy1;
left[1].y = end.y + dx1;
right[0].x = start.x + dy1;
right[0].y = start.y - dx1;
right[1].x = end.x + dy1;
right[1].y = end.y - dx1;
sc_points[0].x = left[0].x;
sc_points[0].y = left[0].y;
sc_points[1].x = left[1].x;
sc_points[1].y = left[1].y;
sc_points[2].x = right[1].x;
sc_points[2].y = right[1].y;
sc_points[3].x = right[0].x;
sc_points[3].y = right[0].y;
Polygon(hDC,sc_points,4);
}
}
maspeir
September 11th, 2008, 10:40 PM
I've figured out the circular end caps. Just had some signs flipped. The complete code follows. I plan on improving it and reducing/eliminating its reliance on cos/sin. All in good time.
The thing that bugs me is that I still cannot figure out how to do square end caps. It sounds simple, just push out the two points of the line by half the width and draw the line as normal. Problem is, how do I push out the ends of the line? Every solution I can think of requires the line to be at a certain angle or requires many different cases to maintain rotational invariance. Can't be that difficult, right?
//Found this routine somewhere online. If its yours, let me know.
double CalcTheta(Point Point1,Point Point2)
{
double t;
if(Point2.x - Point1.x == 0)
{
if(Point2.y > Point1.y)
t = 0;
else
t = PI;
}else{
t = atan((Point2.y - Point1.y) / (Point2.x - Point1.x));
if(Point2.x > Point1.x)
t = PI / 2.0f - t;
else
t = PI * 1.5f - t;
}
return(t);
}
void DrawLine(HDC hDC,Point start,Point end,double width)
{
Point left[2],right[2];
POINT sc_points[4];
double dx,dy,dx1,dy1,l,half_width;
double x,y,angle,step,s,e,temp;
double old_x,old_y;
half_width = width * .5;
if(half_width == 4)
step = 360 / width; // Make the angle between steps proportional to the line width. Take care of oddity when step = 90
else
step = 360 / half_width; // Make the angle between steps proportional to the line width
if(width > 1)
{
dx = (end.x - start.x);
dy = (end.y - start.y);
l = sqrt(dx * dx + dy * dy);
if(l == 0)
return;
dx1 = (dx * half_width) / l;
dy1 = (dy * half_width) / l;
left[0].x = start.x - dy1;
left[0].y = start.y + dx1;
left[1].x = end.x - dy1;
left[1].y = end.y + dx1;
right[0].x = start.x + dy1;
right[0].y = start.y - dx1;
right[1].x = end.x + dy1;
right[1].y = end.y - dx1;
sc_points[0].x = left[0].x;
sc_points[0].y = left[0].y;
sc_points[1].x = left[1].x;
sc_points[1].y = left[1].y;
sc_points[2].x = right[1].x;
sc_points[2].y = right[1].y;
sc_points[3].x = right[0].x;
sc_points[3].y = right[0].y;
Polygon(hDC,sc_points,4);
old_x = sc_points[0].x = left[1].x;
old_y = sc_points[0].y = left[1].y;
s = CalcTheta(end,right[1]) / PI * 180;
e = CalcTheta(end,left[1]) / PI * 180;
s -= 90;
e -= 90;
if(s > e)
{
temp = s;
s = e;
e = temp;
for(angle = s;angle < e;angle += step)
{
old_x = x = end.x + cos(angle * PI / 180) * half_width;
old_y = y = end.y - sin(angle * PI / 180) * half_width;
sc_points[1].x = x;
sc_points[1].y = y;
sc_points[2].x = end.x;
sc_points[2].y = end.y;
Polygon(hDC,sc_points,3);
sc_points[0].x = old_x;
sc_points[0].y = old_y;
}
sc_points[1].x = right[1].x;
sc_points[1].y = right[1].y;
sc_points[2].x = end.x;
sc_points[2].y = end.y;
Polygon(hDC,sc_points,3);
}else{
for(angle = s;angle < e;angle += step)
{
old_x = x = end.x - cos(angle * PI / 180) * half_width;
old_y = y = end.y + sin(angle * PI / 180) * half_width;
sc_points[1].x = x;
sc_points[1].y = y;
sc_points[2].x = end.x;
sc_points[2].y = end.y;
Polygon(hDC,sc_points,3);
sc_points[0].x = old_x;
sc_points[0].y = old_y;
}
sc_points[1].x = right[1].x;
sc_points[1].y = right[1].y;
sc_points[2].x = end.x;
sc_points[2].y = end.y;
Polygon(hDC,sc_points,3);
}
old_x = sc_points[0].x = left[0].x;
old_y = sc_points[0].y = left[0].y;
s = CalcTheta(start,left[0]) / PI * 180;
e = CalcTheta(start,right[0]) / PI * 180;
s -= 90;
e -= 90;
if(s > e)
{
temp = s;
s = e;
e = temp;
for(angle = s;angle < e;angle += step)
{
old_x = x = start.x + cos(angle * PI / 180) * half_width;
old_y = y = start.y - sin(angle * PI / 180) * half_width;
sc_points[1].x = x;
sc_points[1].y = y;
sc_points[2].x = start.x;
sc_points[2].y = start.y;
Polygon(hDC,sc_points,3);
sc_points[0].x = old_x;
sc_points[0].y = old_y;
}
sc_points[1].x = left[0].x;
sc_points[1].y = left[0].y;
sc_points[2].x = start.x;
sc_points[2].y = start.y;
Polygon(hDC,sc_points,3);
}else{
for(angle = s;angle < e;angle += step)
{
old_x = x = start.x - cos(angle * PI / 180) * half_width;
old_y = y = start.y + sin(angle * PI / 180) * half_width;
sc_points[1].x = x;
sc_points[1].y = y;
sc_points[2].x = start.x;
sc_points[2].y = start.y;
Polygon(hDC,sc_points,3);
sc_points[0].x = old_x;
sc_points[0].y = old_y;
}
sc_points[1].x = left[0].x;
sc_points[1].y = left[0].y;
sc_points[2].x = start.x;
sc_points[2].y = start.y;
Polygon(hDC,sc_points,3);
}
}
}
TheMelter
September 12th, 2008, 07:31 PM
Hi
A bit off topic , not for beeing a party killer ... How big plans do you have
for this pcb program , since there are so many of this kind of programs and for a very very low price??
Making the graphic is problerly the smallest part of a pcb program.
back to topic , if I understand ur problem correct .
if u want to extend ur line :
A start point
B end point
U unit vector //this vector will allways have the length 1
dx = B.X-A.X;
dy = B.Y-A.X;
U.X = dx / sqrt(dx^2+dy^2) ;
U.Y = dy / sqrt(dx^2+dy^2) ;
now u have a unit vector u can use to what u want .
if you want to make the line 1.2 mm longer in each end.
u just add the 1.2*U to your endpoints.
B.X=B.X+U.X*1.2 ;
B.Y=B.Y +U.Y*1.2 ;
A.X=A.X - U.X*1.2;
A.Y =A.Y -U.X*1.2;
so now your line is got 2.4 mm longer.
Hope I did understand you problem correct.
Best regards
TheMelter