Unnar Björnsson

Ranch Hand

Posts: 164

posted 11 years ago

I�ll start by displaying the variables:

I have tried few solutions, the most obvious one is the mathematical form, but the point misses it�s target because in math there are 1/2 X�s and 1/3 Y�s but there is no 1/2 or 1/3 pixel.

So I tried another solution. I stacked the x�s and y�s so the error were at worst 1 pixel. Then I made an array of all the points:

Every timeunit the painted point is repainted to misTrail[i] and i incremented. This almost works, the point still slightly misses it�s target. Why is that? Is there a better way to accomplish this?

I hope I didn�t make it too difficult to understand. In short I want a point or ball to shoot from a source to the point I clicked and I want the number of it�s appearances to reflect the distance it travels i.e it doesn�t go faster when close to 90� angle.

*xPool = 0;*

yPool = 0;

missileX = (selected.getX()+((selected.getIcon().getIconWidth())/2));

missileY = (selected.getY()+((selected.getIcon().getIconHeight())/2));

abX = e.getX()-(selected.getX()+((selected.getIcon().getIconWidth())/2));

abY = e.getY()-(selected.getY()+((selected.getIcon().getIconHeight())/2));

abXY = Math.sqrt(Math.pow(abX, 2) + Math.pow(abY, 2));yPool = 0;

missileX = (selected.getX()+((selected.getIcon().getIconWidth())/2));

missileY = (selected.getY()+((selected.getIcon().getIconHeight())/2));

abX = e.getX()-(selected.getX()+((selected.getIcon().getIconWidth())/2));

abY = e.getY()-(selected.getY()+((selected.getIcon().getIconHeight())/2));

abXY = Math.sqrt(Math.pow(abX, 2) + Math.pow(abY, 2));

**e**is a mouseEvent i.e where I clicked the mouse on the board.**selected**is the currently selected unit(a class I made for unit that can move, turn etc.)**xPool and yPool**are stacks of x and y values. Lets say if x=0.4 the first x would be +0 pixel the next x (x=0.8) would also be +0 pixel the third (x=1.2) would be +1 pixel. The stack is floored to the next integer so the error is never more than 1 pixel.**abX, abY and abXY**are the distances i.e abX is the x-distance from source to target, abY y-distance and abXY the 2-dimensional distance(a�+b�=c�).I have tried few solutions, the most obvious one is the mathematical form, but the point misses it�s target because in math there are 1/2 X�s and 1/3 Y�s but there is no 1/2 or 1/3 pixel.

So I tried another solution. I stacked the x�s and y�s so the error were at worst 1 pixel. Then I made an array of all the points:

*misTrail = new Point[((int)abXY/5)+1];*

for(int i=0; i<misTrail.length; i++)

{

xPool += abX/(abXY/5);

yPool += abY/(abXY/5);

misTrail[i] = new Point((int)(selected.getX()+xPool),

(int)(selected.getY()+yPool));

}for(int i=0; i<misTrail.length; i++)

{

xPool += abX/(abXY/5);

yPool += abY/(abXY/5);

misTrail[i] = new Point((int)(selected.getX()+xPool),

(int)(selected.getY()+yPool));

}

Every timeunit the painted point is repainted to misTrail[i] and i incremented. This almost works, the point still slightly misses it�s target. Why is that? Is there a better way to accomplish this?

I hope I didn�t make it too difficult to understand. In short I want a point or ball to shoot from a source to the point I clicked and I want the number of it�s appearances to reflect the distance it travels i.e it doesn�t go faster when close to 90� angle.

Layne Lund

Ranch Hand

Posts: 3061

posted 11 years ago

I think the problem might be from the calculations in your loop such as

You caim that your round off error is within 1 pixel, but I think you have failed to realize that it is off by at most 1 pixel EACH TIME through the loop. So if the loop iterates twenty times, you might be off by as much as 20 pixels in the end. I suggest that you calculate the xPool each time from the initial position, rather than incrementing from the "current position". Perhaps something like this will fix the problem:

I have only showed the calculation for xPool. You can do the same thing for yPool.

I hope this answers your question. I also have some additional comments on your code. If I understand your code correctly, you are calculating two values (i.e. abX/(abXY/5) and abY/(abXY/5)) over and over again in the for loop, but the result is always going to be the same each time. You can optimize this slightly by calculating this once before the loop starts:

I don't know how much of a difference this will make, but it becomes more obvious that you are using some physics (and even trigonometry here). If you study up on these two subjects, you will probably find some ways to use the mathematics involved to make your code much simpler. It will also make it easier for other programmers to read, at least if they are familiar with the same concepts.

Specificially, there are a couple of equations that are helpful from physics that you can use here (and you are in fact using them, although they are disguised:

x = x0 + vx*t

y = y0 + vy*t

vx = v * sin(theta)

vy = v * cos(theta)

where

t is the amount of time (usually measured from some determined start time)

(x, y) is the position of the particle at time t

v is the velocity of the particle

vx is the x-component of the velocity

vy is the y-component of the velocity

x0 is the initial position of the particle

y0 is the initial position of the particle

theta is the angle that describes the direction that the particle is moving

Interestingly, you are already using all of these. I don't know if that is on purpose or not. If not, I strongly encourage you to look into this further. I think it will help you understand a lot of the mathematics you use in writing games.

Regards,

Layne

[ July 14, 2005: Message edited by: Layne Lund ]

[ July 14, 2005: Message edited by: Layne Lund ]

You caim that your round off error is within 1 pixel, but I think you have failed to realize that it is off by at most 1 pixel EACH TIME through the loop. So if the loop iterates twenty times, you might be off by as much as 20 pixels in the end. I suggest that you calculate the xPool each time from the initial position, rather than incrementing from the "current position". Perhaps something like this will fix the problem:

I have only showed the calculation for xPool. You can do the same thing for yPool.

I hope this answers your question. I also have some additional comments on your code. If I understand your code correctly, you are calculating two values (i.e. abX/(abXY/5) and abY/(abXY/5)) over and over again in the for loop, but the result is always going to be the same each time. You can optimize this slightly by calculating this once before the loop starts:

I don't know how much of a difference this will make, but it becomes more obvious that you are using some physics (and even trigonometry here). If you study up on these two subjects, you will probably find some ways to use the mathematics involved to make your code much simpler. It will also make it easier for other programmers to read, at least if they are familiar with the same concepts.

Specificially, there are a couple of equations that are helpful from physics that you can use here (and you are in fact using them, although they are disguised:

x = x0 + vx*t

y = y0 + vy*t

vx = v * sin(theta)

vy = v * cos(theta)

where

t is the amount of time (usually measured from some determined start time)

(x, y) is the position of the particle at time t

v is the velocity of the particle

vx is the x-component of the velocity

vy is the y-component of the velocity

x0 is the initial position of the particle

y0 is the initial position of the particle

theta is the angle that describes the direction that the particle is moving

Interestingly, you are already using all of these. I don't know if that is on purpose or not. If not, I strongly encourage you to look into this further. I think it will help you understand a lot of the mathematics you use in writing games.

Regards,

Layne

[ July 14, 2005: Message edited by: Layne Lund ]

[ July 14, 2005: Message edited by: Layne Lund ]

Unnar Björnsson

Ranch Hand

Posts: 164

posted 11 years ago

Yeah! I solved it I did it like this:

If abX/abY was larger or equal to 1 Y would be forced to move 1 pixel at a time(though in this case 8 pixel to speed the travel time), if it was less then 1, X would be forced to move 1 pixel at a time, therefore the point moves exactly one pixel distance every time unit.

Thanks for your help anyway.

[ July 15, 2005: Message edited by: Unnar Bj�rnsson ]

missileX = (selected.getX()+((selected.getIcon().getIconWidth())/2));

missileY = (selected.getY()+((selected.getIcon().getIconHeight())/2));

xPool = missileX;

yPool = missileY;

abX = e.getX()-(selected.getX()+((selected.getIcon().getIconWidth())/2));

abY = e.getY()-(selected.getY()+((selected.getIcon().getIconHeight())/2));

abXY = Math.sqrt(Math.pow(abX, 2) + Math.pow(abY, 2));

if((Math.abs(abY)/Math.abs(abX)) >= 1)

{

y = 8*(abY/Math.abs(abY));

x = y/(abY/abX);

}

else

{

x = 8*(abX/Math.abs(abX));

y = (abY/abX)*x;

}missileX = (selected.getX()+((selected.getIcon().getIconWidth())/2));

missileY = (selected.getY()+((selected.getIcon().getIconHeight())/2));

xPool = missileX;

yPool = missileY;

abX = e.getX()-(selected.getX()+((selected.getIcon().getIconWidth())/2));

abY = e.getY()-(selected.getY()+((selected.getIcon().getIconHeight())/2));

abXY = Math.sqrt(Math.pow(abX, 2) + Math.pow(abY, 2));

if((Math.abs(abY)/Math.abs(abX)) >= 1)

{

y = 8*(abY/Math.abs(abY));

x = y/(abY/abX);

}

else

{

x = 8*(abX/Math.abs(abX));

y = (abY/abX)*x;

}

If abX/abY was larger or equal to 1 Y would be forced to move 1 pixel at a time(though in this case 8 pixel to speed the travel time), if it was less then 1, X would be forced to move 1 pixel at a time, therefore the point moves exactly one pixel distance every time unit.

Thanks for your help anyway.

[ July 15, 2005: Message edited by: Unnar Bj�rnsson ]

John Dove

Greenhorn

Posts: 11

posted 11 years ago

You could always use a parametric algorithm to "walk" through all the discrete pixel coordinates that exist in your line segment. The following sample code does this:

-------------------------

package simulation.math;

////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////

// top-level public class

////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////

/**

* an instance of this class lets a user "walk" through all the integer (x,y)

* points that comprise a line segment.

*

* @author John Dove scjp,scjd 2005

*/

public class ParametricLineStep

{

//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////

// fields

//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////

/**

* the target line whose coordinates must be "walked" through, and

* returned to the user.

*/

private Line m_line;

/**

* the last point that was parsed from the line.

*/

private Point m_lastPointReturned;

/**

* flag indicating if the entire line has been parsed.

*/

private boolean m_lineParsed;

/**

* the percentage to increment the "step percent increment" by, which dictates

* where on the line the parse is currently at.

*/

private double m_stepPercentIncrement;

/**

* dictates where on the line the parse is currently at.

*/

private double m_stepPercent;

/**

* the x-value of the starting point for parametrically parsing the line.

*/

private int m_startPointX;

/**

* the y-value of the starting point for parametrically parsing the line.

*/

private int m_startPointY;

/**

* the remaining x-axis length that the start point will need to grow by,

* in order to completely parse the line.

*/

private int m_remainingLengthX;

/**

* the remaining y-axis length that the start point will need to grow by,

* in order to completely parse the line.

*/

private int m_remainingLengthY;

//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////

// methods

//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////

/**

* creates an empty instance that is not parsing a line yet.

*/

public ParametricLineStep()

{

m_line = null;

m_lastPointReturned = new Point();

}

/**

* resets the line parse to the beginning of the line.

*/

public void rewind()

{

m_stepPercent = 0.0;

m_lineParsed = false;

}

/**

* sets the line to parse to the supplied line.

*

* @param line the supplied line to parse.

*/

public void setLine(Line line)

{

// save the line

m_line = line;

rewind();

// determine the line length

double lineLength = 0;

// if the line is parallel to the x-axis

if (m_line.m_p1.m_y == m_line.m_p2.m_y)

{

lineLength = Math.abs(m_line.m_p1.m_x - m_line.m_p2.m_x) + 1;

}

// else if the line is parallel to the y-axis

else if (m_line.m_p1.m_x == m_line.m_p2.m_x)

{

lineLength = Math.abs(m_line.m_p1.m_y - m_line.m_p2.m_y) + 1;

}

// else, if the line is NOT parallel to either axis, we the use pythagorean

// theorem to compute the line length

else

{

// first get the distance in points (i.e. discrete pixels) between the

// x and y ordinates. +1 is used to ensure that we get an accurate point

// count inclusive of the endpoints themselves. For example, 5-2 = 3,

// but between 2 and 5 *inclusive* there are actually 4 points.

int xDifference = Math.abs(m_line.m_p1.m_x-m_line.m_p2.m_x) + 1;

int yDifference = Math.abs(m_line.m_p1.m_y-m_line.m_p2.m_y) + 1;

lineLength = Math.sqrt(xDifference*xDifference + yDifference*yDifference);

}

// determine the step percent increment, which is equal to the "percentage"

// of the line that *half* a pixel is equal to

m_stepPercentIncrement = 0.5/lineLength;

// determine the starting point for parametrically parsing the line

m_startPointX = line.m_p1.m_x;

m_startPointY = line.m_p1.m_y;

// determine the remaining lengths in BOTH the x and y directions

// that the start point will need to grow by, in order to completely

// parse the line. +1 is NOT needed, as we have a startX/YPoint

m_remainingLengthX = line.m_p2.m_x - line.m_p1.m_x;

m_remainingLengthY = line.m_p2.m_y - line.m_p1.m_y;

}

/**

* returns the next point on the line, else null if no more points remain.

* This method will always return the SAME object reference, however, the

* values will be changed to be that of the next point (x,y).

*

* @return the next point on the line, else null if no more points remain.

*/

public Point getNextPoint()

{

// assume no more points exist, so set return value to null

Point returnValue = null;

// if the line has NOT been completely parsed yet

if (!m_lineParsed)

{

// while the next point has not been determined and the step percent is

// less than 1.0; i.e. this check does NOT cover the 1.0 step percent,

// instead, the 1.0 step percent is covered outside the while loop (below)

// as a special case

while(m_stepPercent <= 1.0)

{

// get the next point

int nextPointX = m_startPointX + (int)Math.round(m_stepPercent * m_remainingLengthX);

int nextPointY = m_startPointY + (int)Math.round(m_stepPercent * m_remainingLengthY);

// if this is the FIRST point to be parsed, return it

if (m_stepPercent == 0.0)

{

m_lastPointReturned.m_x = nextPointX;

m_lastPointReturned.m_y = nextPointY;

returnValue = m_lastPointReturned;

break;

}

// else, if this is the 2ND+ point to be parsed, such that it is NOT

// equal to the last point parsed, return it

else if (!m_lastPointReturned.equals(nextPointX,nextPointY))

{

m_lastPointReturned.m_x = nextPointX;

m_lastPointReturned.m_y = nextPointY;

returnValue = m_lastPointReturned;

break;

}

// increment the step percent and try again. If continually repeated,

// this will eventually cause the while loop to exit without ever

// finding a return point

m_stepPercent = m_stepPercent + m_stepPercentIncrement;

}

//----------------------------------------------------------------------

// SPECIAL CASE: step percent >= 1.0

//----------------------------------------------------------------------

// if the while loop (above) exited WITHOUT finding a return Point, it

// means the step percent must be >= 1.0. In this special case, we will

// attempt to return the other endpoint of the line, if and only if it

// has NOT already been returned in a prior call to this method

//----------------------------------------------------------------------

if (returnValue == null)

{

// in any case, the line will be considered fully parsed after this

// code block executes

m_lineParsed = true;

// get the next point

int nextPointX = m_line.m_p2.m_x;

int nextPointY = m_line.m_p2.m_y;

// if this final point differs from the last that was returned from this

// method, use it as the return value

if (!m_lastPointReturned.equals(nextPointX,nextPointY))

{

m_lastPointReturned.m_x = nextPointX;

m_lastPointReturned.m_y = nextPointY;

returnValue = m_lastPointReturned;

}

}

// else, the while loop exited because the next point was FOUND, which also

// means that the step percent is still LESS THAN 1.0. In this case we

// simply increment the step percent for the NEXT call to this method

else

{

m_stepPercent = m_stepPercent + m_stepPercentIncrement;

}

}

// returns the next point on the line, else null if no more points exist

return returnValue;

}

}

-------------------------

package simulation.math;

////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////

// top-level public class

////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////

/**

* an instance of this class lets a user "walk" through all the integer (x,y)

* points that comprise a line segment.

*

* @author John Dove scjp,scjd 2005

*/

public class ParametricLineStep

{

//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////

// fields

//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////

/**

* the target line whose coordinates must be "walked" through, and

* returned to the user.

*/

private Line m_line;

/**

* the last point that was parsed from the line.

*/

private Point m_lastPointReturned;

/**

* flag indicating if the entire line has been parsed.

*/

private boolean m_lineParsed;

/**

* the percentage to increment the "step percent increment" by, which dictates

* where on the line the parse is currently at.

*/

private double m_stepPercentIncrement;

/**

* dictates where on the line the parse is currently at.

*/

private double m_stepPercent;

/**

* the x-value of the starting point for parametrically parsing the line.

*/

private int m_startPointX;

/**

* the y-value of the starting point for parametrically parsing the line.

*/

private int m_startPointY;

/**

* the remaining x-axis length that the start point will need to grow by,

* in order to completely parse the line.

*/

private int m_remainingLengthX;

/**

* the remaining y-axis length that the start point will need to grow by,

* in order to completely parse the line.

*/

private int m_remainingLengthY;

//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////

// methods

//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////

/**

* creates an empty instance that is not parsing a line yet.

*/

public ParametricLineStep()

{

m_line = null;

m_lastPointReturned = new Point();

}

/**

* resets the line parse to the beginning of the line.

*/

public void rewind()

{

m_stepPercent = 0.0;

m_lineParsed = false;

}

/**

* sets the line to parse to the supplied line.

*

* @param line the supplied line to parse.

*/

public void setLine(Line line)

{

// save the line

m_line = line;

rewind();

// determine the line length

double lineLength = 0;

// if the line is parallel to the x-axis

if (m_line.m_p1.m_y == m_line.m_p2.m_y)

{

lineLength = Math.abs(m_line.m_p1.m_x - m_line.m_p2.m_x) + 1;

}

// else if the line is parallel to the y-axis

else if (m_line.m_p1.m_x == m_line.m_p2.m_x)

{

lineLength = Math.abs(m_line.m_p1.m_y - m_line.m_p2.m_y) + 1;

}

// else, if the line is NOT parallel to either axis, we the use pythagorean

// theorem to compute the line length

else

{

// first get the distance in points (i.e. discrete pixels) between the

// x and y ordinates. +1 is used to ensure that we get an accurate point

// count inclusive of the endpoints themselves. For example, 5-2 = 3,

// but between 2 and 5 *inclusive* there are actually 4 points.

int xDifference = Math.abs(m_line.m_p1.m_x-m_line.m_p2.m_x) + 1;

int yDifference = Math.abs(m_line.m_p1.m_y-m_line.m_p2.m_y) + 1;

lineLength = Math.sqrt(xDifference*xDifference + yDifference*yDifference);

}

// determine the step percent increment, which is equal to the "percentage"

// of the line that *half* a pixel is equal to

m_stepPercentIncrement = 0.5/lineLength;

// determine the starting point for parametrically parsing the line

m_startPointX = line.m_p1.m_x;

m_startPointY = line.m_p1.m_y;

// determine the remaining lengths in BOTH the x and y directions

// that the start point will need to grow by, in order to completely

// parse the line. +1 is NOT needed, as we have a startX/YPoint

m_remainingLengthX = line.m_p2.m_x - line.m_p1.m_x;

m_remainingLengthY = line.m_p2.m_y - line.m_p1.m_y;

}

/**

* returns the next point on the line, else null if no more points remain.

* This method will always return the SAME object reference, however, the

* values will be changed to be that of the next point (x,y).

*

* @return the next point on the line, else null if no more points remain.

*/

public Point getNextPoint()

{

// assume no more points exist, so set return value to null

Point returnValue = null;

// if the line has NOT been completely parsed yet

if (!m_lineParsed)

{

// while the next point has not been determined and the step percent is

// less than 1.0; i.e. this check does NOT cover the 1.0 step percent,

// instead, the 1.0 step percent is covered outside the while loop (below)

// as a special case

while(m_stepPercent <= 1.0)

{

// get the next point

int nextPointX = m_startPointX + (int)Math.round(m_stepPercent * m_remainingLengthX);

int nextPointY = m_startPointY + (int)Math.round(m_stepPercent * m_remainingLengthY);

// if this is the FIRST point to be parsed, return it

if (m_stepPercent == 0.0)

{

m_lastPointReturned.m_x = nextPointX;

m_lastPointReturned.m_y = nextPointY;

returnValue = m_lastPointReturned;

break;

}

// else, if this is the 2ND+ point to be parsed, such that it is NOT

// equal to the last point parsed, return it

else if (!m_lastPointReturned.equals(nextPointX,nextPointY))

{

m_lastPointReturned.m_x = nextPointX;

m_lastPointReturned.m_y = nextPointY;

returnValue = m_lastPointReturned;

break;

}

// increment the step percent and try again. If continually repeated,

// this will eventually cause the while loop to exit without ever

// finding a return point

m_stepPercent = m_stepPercent + m_stepPercentIncrement;

}

//----------------------------------------------------------------------

// SPECIAL CASE: step percent >= 1.0

//----------------------------------------------------------------------

// if the while loop (above) exited WITHOUT finding a return Point, it

// means the step percent must be >= 1.0. In this special case, we will

// attempt to return the other endpoint of the line, if and only if it

// has NOT already been returned in a prior call to this method

//----------------------------------------------------------------------

if (returnValue == null)

{

// in any case, the line will be considered fully parsed after this

// code block executes

m_lineParsed = true;

// get the next point

int nextPointX = m_line.m_p2.m_x;

int nextPointY = m_line.m_p2.m_y;

// if this final point differs from the last that was returned from this

// method, use it as the return value

if (!m_lastPointReturned.equals(nextPointX,nextPointY))

{

m_lastPointReturned.m_x = nextPointX;

m_lastPointReturned.m_y = nextPointY;

returnValue = m_lastPointReturned;

}

}

// else, the while loop exited because the next point was FOUND, which also

// means that the step percent is still LESS THAN 1.0. In this case we

// simply increment the step percent for the NEXT call to this method

else

{

m_stepPercent = m_stepPercent + m_stepPercentIncrement;

}

}

// returns the next point on the line, else null if no more points exist

return returnValue;

}

}

Regards,<br />John Dove<br />john_dove@mapinfo.com<br /><a href="http://home.nycap.rr.com/jdove" target="_blank" rel="nofollow">http://home.nycap.rr.com/jdove</a>

Layne Lund

Ranch Hand

Posts: 3061

posted 11 years ago

John,

Would you please go back and edit your above post by adding UBB CODE tags? UBB works a lot like HTML and the CODE tags will preserve your formatting so that we can read your code more easily.

Thanks

Layne

Would you please go back and edit your above post by adding UBB CODE tags? UBB works a lot like HTML and the CODE tags will preserve your formatting so that we can read your code more easily.

Thanks

Layne

John Dove

Greenhorn

Posts: 11

posted 11 years ago
Regards,<br />John Dove<br />john_dove@mapinfo.com<br /><a href="http://home.nycap.rr.com/jdove" target="_blank" rel="nofollow">http://home.nycap.rr.com/jdove</a>

>>> UBB works a lot like HTML and the CODE

>>> tags will preserve your formatting so that we

>>> can read your code more easily.

I did not know that UBB existed. :-) So it's just a variant of markup language that is used in forums then? Is it an industry standard, xor is it proprietary unto each forum that "offers" its usage?

>>> Would you please go back and edit your above

>>> post by adding UBB CODE tags?

Sure - will do. Gotta go to work right now, however, I will try and do this editing at work.

Regards,

John Dove

johndove_123@hotmail.com

john_dove@mapinfo.com

http://home.nycap.rr.com/jdove

Thanks

Layne

>>> tags will preserve your formatting so that we

>>> can read your code more easily.

I did not know that UBB existed. :-) So it's just a variant of markup language that is used in forums then? Is it an industry standard, xor is it proprietary unto each forum that "offers" its usage?

>>> Would you please go back and edit your above

>>> post by adding UBB CODE tags?

Sure - will do. Gotta go to work right now, however, I will try and do this editing at work.

Regards,

John Dove

johndove_123@hotmail.com

john_dove@mapinfo.com

http://home.nycap.rr.com/jdove

Thanks

Layne

Don't get me started about those stupid light bulbs. |