Code Red => VB(A) => Topic started by: DaveW on September 05, 2006, 03:12:21 PM

Title: New point planear to original points
Post by: DaveW on September 05, 2006, 03:12:21 PM
I have been trying to get around ACAD's collinear bug.

If I have line, I know I can get the Double values of X's,Y's and Z's of of the StartPoint, EndPoint and the Normal.

ACAD's help says, "You can add this normal vector to a point to obtain another point."

I can really use another point that is planear to the XY plane of the start and end points of a line based on it's current normal, but is not collinear. The new point should be about the same distance away from the start point as the end point is from the start point.

Does anyone have some sample code that does this? I have been pullingmy hair out all day.


Title: Re: New point planear to original points
Post by: DaveW on September 05, 2006, 04:24:52 PM
Here is an example of what I am dealing with:

Command: align
Select objects: all 13 found
Select objects:
Specify first source point: -13.073282044755,77.307053104084,105.082384708192
Specify first destination point: 0,0,0
Specify second source point: -13.073282044755,71.945120358405,109.122890845077
Specify second destination point: 1000,0,0
Specify third source point or <continue>:
The 3 source points are collinear.

The distances between the first two points and the last point are so far away from the third that ACAD screws up.

I even when ahead and added this function of ACAD newsgroup to check if I am seeing things, but it too says they are not collinear.
Code: [Select]
Public Function Colly(B As Variant, M As Variant, E As Variant, Optional Tolerance As Double) As Boolean
'Gives true if three points B(egin), M(iddle) and E(nd) are collinear
'if d(B,E)= d(B,M) + d(M,E)

Dim i As Integer, dBM As Double, dME As Double, dBE As Double

For i = 0 To UBound(B) 'Assuming all are 2D or all are 3D
dBM = dBM + (B(i) - M(i)) ^ 2
dME = dME + (M(i) - E(i)) ^ 2
dBE = dBE + (B(i) - E(i)) ^ 2
Next i
If Abs(Sqr(dBE) - Sqr(dBM) - Sqr(dME)) < Tolerance Then Colly = True _
Else Colly = False

End Function
Title: Re: New point planear to original points
Post by: DaveW on September 05, 2006, 05:11:10 PM
The only solution I have found is to get the midpoint of the line, measure it, then get the midpoint from the startpoint to the last midpoint, and repeat, until the vale is less then ten. Then use the startpoing and midpoint of the a line smaller then 10 and ALSO do this procesure to another line and use its mide point, once smaller then 10, for the third alin point.

Title: Re: New point planear to original points
Post by: MickD on September 05, 2006, 06:20:06 PM
you can set the ucs to the ocs of the line, ie -
xvec = line.normal
yvec = normalise(line.ep-line.sp)
zvec = xvec.crossproduct(yvec)

use these to set your new ucs then you can create a new point (0,linelength,0) in that ucs that will be the lines length along the xaxis in the plane of the line.
Title: Re: New point planear to original points
Post by: DaveW on September 05, 2006, 10:42:09 PM
Thanks Mick,

But I have no idea of how to do what you are saying.

I ended up doing this:
I have two lines that have been retrieved from the same planer region that has been exploded. I divide the first lines lenth until it is less then 10.
I do the same for the second line. I am already sure they are not collinear, as I make sure line ones handle is not line twos handle and I create a circle and rays and check for intersectwith.
I then use the start and end points of the first line for the first two align points and either the start or endpoint of the second line for the third align point.

It works! It is brute force and anything but eligant. But I had to ge it done and I had a feeling I was not going to endstand what you were going to say. :) I really dont.

You wrote this:
xvec = line.normal
yvec = normalise(line.ep-line.sp)
zvec = xvec.crossproduct(yvec)

xvec = line.normal <-- I beieve this is a three elemnt array of doubles for x, y and z.

yvec = normalise(line.ep-line.sp) <-- no idea at all.
zvec = xvec.crossproduct(yvec) <-- no idea at all.

I can't post my existing code as it way to big.


Title: Re: New point planear to original points
Post by: MickD on September 06, 2006, 12:15:48 AM
Ahh, you are trying to do an 'align' in code yes?

xvec etc are vectors which are an array of 3 doubles as you say.

you create other vectors by subtracting or adding other vectors. The piccy below explains it in 2d, 3d is just as easy.
once you have at least two vectors you can create a ucs by a bit of simple vector math.

If you are doing an align you can create a vector from your line, then create another vector from the lines normal which is multiplied by the lines length. Add the new vec to the vec created by the lines sp and you will have 2 lines perp to each other for use with alignment.
I'll try to draw another piccy.
Title: Re: New point planear to original points
Post by: MickD on September 06, 2006, 12:26:23 AM
a 2d demo of creating the new point piccies

The principals are the same with 3d and will work regardless of where the lines normal is pointing. a vector plus another vector produce a third vector. The values of this vector is actually the new point values.
This is the reason vectors can become confusing,  they can be confused with points very easily because two vectors can have exactly the same values but be located in 2 different places (ie. they were calculated differently using different sp and ep's say) :-o

The best way to understand is to do some very simple maths in 2d to 'proof' the outcome and it will become clear.
Title: Re: New point planear to original points
Post by: DaveW on September 06, 2006, 04:48:04 AM
I think I am beginning to understand.

First of all...

SP = StartPoint
EP = EndPoint
NP = NewPoint

By subtracting the startpoint from the endpoint of a line we produce vector

We can also produce a vector from the normal of the line, but we multiply it by the lines length. My guess is that the other vector retrieved by doing subtraction is based on the lines length and we want the second vector to share the same ratio.

Is this correct so far?

Thanks a bunch

Title: Re: New point planear to original points
Post by: MickD on September 06, 2006, 05:03:21 AM
sorry ep/sp etc. wasn't so clear Dave.

every point in space can be represented as a vector so the sp and ep can both be used as vectors to do some simple vector math. By adding/subtracting vectors you produce a new vector whose tail (sp) starts at the origin and whose head (ep) finishes at the new point.

Like I said, if you draw a 2d grid and keep the numbers simple it's easy ro visually see the calc's and the outcome.

Note that we didn't need to lengthen the lines normal, we could've just added it to the sp and produced another point albeit close to the sp (only 1 unit away). I lengthened the normal so the 3 points would be the same ratio apart which is what I think you are trying to acheive.
Title: Re: New point planear to original points
Post by: DaveW on September 06, 2006, 05:08:01 AM
Cool Mick.

It is 2am here. Let me chew on this for a day or so and then see what I can do then.

I have a feeling it is going to quite a bit more, but at least I will understand completely then.

Thank You.
Title: Re: New point planear to original points
Post by: MickD on September 06, 2006, 05:11:58 AM
no prob's. If I get time I'll see if I can punch out some code, it will be in C# though...I wouldn't know where to start with vb/a :)
Title: Re: New point planear to original points
Post by: DaveW on September 06, 2006, 05:13:34 AM
I will share whatever I come up with in VB.
Title: Re: New point planear to original points
Post by: DaveW on September 06, 2006, 10:22:38 AM
This will get things squared up to the WCS. It is not my final code, but the basics are here.

The reason this is not so important to everyone else is that even after the solid is square with the WCS, you still have to rotate it so that you align the proper x, y and z axis with their respective length, width and thickness counterparts. Being as no 3D entity in any cad engine in the world has complete orientation information, there is no way to finish the rotating or aligning the object. The rest of the needed data is not there. I can do it, but I have patented the only way possible around the problem. Licensing is cheap ;) Everyone else decided to drive the drawing with parametrics and formula. At that level, the graphical entites mean nothing, as they are just a result of the underlying database/parametric engine. That approach can work, but it is never really Design/Build. I have my share of different problems, this is for sure, but doing custom stuff is a good 100 times faster then the best parametric software out there.

Thanks again Mick. You are a gem.

Title: Re: New point planear to original points
Post by: Cathy on September 06, 2006, 03:52:54 PM
As a total aside, it does look like your 3 source points are pretty dang close to colinear.  Using the Colly function that you downloaded from the acad newsgroups, I had to set the tolerance to something like .000000001 to get it to report not colinear.  With a tolerance of .000001 they are colinear. 
Title: Re: New point planear to original points
Post by: DaveW on September 06, 2006, 05:34:43 PM
That is funny Cathy. They are nothing close. Even so, that utility I did not adjust. 0 is 0 and that is what I used in the function.

Here is the deal. The 3D solid that was exploded and the lines were retrieved from was, I believe, 2" x 3/4" x 5000" So I had a ratio of 5000 to 3/4, maybe. Acad will fail, at about 220 to 1. It was an exaggeration test part to reproduce the error easily. The exact number is somewhere in between  223.6 and 223.61 to 1 ratio.

Try this:
If you draw a line.....
Starting at the first specified source point, "10.3688159,-30.95757643,34.08858268"
Then the second specified source point, "-10.3688159,-30.95757643,81.91141732"
Then the third specified source point, "-10.56566629,-30.95757643,81.91141732"

you will get an inverted "L" in modelspace.

Now try to align it with:
First destination point: 0,0,0
Second destination point: 1000,0,0
Third destination point:  0,1000,0

Seriously, I think some programmer used an existing error message for a known bug, but never wrote the correct error message to hide fact he had not fixed his bug or actually forget to fix it.

All said and done, I forced the issue and it works. I have again found another way, thanks to Mick, and if successful in testing, this new way will much faster and more reliable.

Take care,


Title: Re: New point planear to original points
Post by: MickD on May 24, 2007, 02:12:54 AM
I've thought about this a bit further and found something in a previous post in this thread that makes things a bit clearer, I'll try to explain -

problem =
you want to rotate a 3d solid to wcs from any orientation in space, let's say that the edge you use is the length property (it doesn't matter though but if it's an odd shape, you would naturally use the longest 'direction' as the length in this case).
When rotated, let's say you need the L (length direction) to follow the z world axis, this will give you both the x and y axes to extract width and height.

so far so good??

What you will need -

Some vector modification functions such as dot product, cross product, normalisation, add/subtract etc, do a search on vb vector math or similar to find them, they will be done already I guarantee it ;)

A rudementary understanding of using matrices with acad, you will need to be able to set up a matix given 3 vectors(3 sets of doubles, they 'may' need to be normalised) and you need to know how to 'invert' it (reverse/negate it), if acad has a matrix class/method in vba, it will/should have an invert method.

Ok, given that, here's the solution -

1. Get your line/edge as a vector, to do this subtract the end point from the start point (not the other way around!)

2. Next you can use the line's normal, another point of the region or the regions' normal, I'll step out both methods. Either way they both 'should' be perpendicular to the plane of the region.

2a. Using the line or region's normal, get the cross product of the vector from the edge and the normal

zvec = edge.ep - edge.sp
yvec = edge/region.normal
xvec = yvec.cross(zvec)

that should give you three vectors along each x,y,z axis of that plane/region's face with the z vector being along the edge, a y vector normal/perp to plane (in the negative direction I think) and an x vector to the right and perp of z & y.

2b. Using another point of the plane - to do this you will need the 'previous' point before the start point of the edge that you have for L.
From this point, create another vec by subtracting the sp from this previous point, the difference here is we will need to do 2 cross products to make our axes orthoganal (square to each other).

zvex = edge.ep - edge.sp
xvec = prevpnt - edge.sp
yvec = zvec.cross(xvec)

now to square it up, yvec is already square to zvec from performing the cross product but xvec may not necessarilly be square to zvec, nor does it need to be yet but this will fix it.

xvec = yvec.cross(zvec)

this should give you the same result BUT - if the previous point is not to 'the right side' of the edge it may throw the resultant yvec in the other direction, no prob's, just 'negate' it (reverses the direction).
To do this you can get the first 2 vecs from 2b above and perform a dot product to see which side it's on, you may need to experiment but the result will either be +ive or -ive but it's easy to do once you know what you need.

Now, to rotate it to world, create a matrix loading in the numbers of your 3 vec axes into the appropriate matrix columns, get the object and transform it using this matrices 'to world' method or similar, get your data and rotate it back with the original matrix, it should put the object with the L direction along the z axis with the edge sp at 0,0,0 with the region plane along the x axis but you may need to experiment to get it how you like, easy peasy :D

Let's see what you can do with that ;)

Title: Re: New point planear to original points
Post by: DaveW on May 24, 2007, 02:45:36 PM
OMG Mick!!!

That will take me days to do start to get a handle on. I am not going to have any time for a few weeks, but I will give it shot and try to work it through, as I really want to understand. If I take it step by step I think it may help with a few things.

Thanks a bunch for such a detailed post.
Title: Re: New point planear to original points
Post by: DaveW on May 24, 2007, 02:47:41 PM
This is the basics of what i planning on using. It circumvents ACAD's colinear error.

Code: [Select]
Public Sub align()

Dim MyLine As AcadLine
Dim Ent As AcadEntity
Dim LineNormalHold(0 To 2) As Double
Dim FirstPoint(0 To 2) As Double
Dim SecondPoint(0 To 2) As Double
Dim ThirdPoint(0 To 2) As Double

For Each Ent In ThisDrawing.ModelSpace

    If Ent.ObjectName = "AcDbLine" Then
       Set MyLine = Ent
       LineNormalHold(0) = MyLine.Normal(0)
       LineNormalHold(1) = MyLine.Normal(1)
       LineNormalHold(2) = MyLine.Normal(2)
       LineNormalHold(0) = LineNormalHold(0) * MyLine.Length
       LineNormalHold(1) = LineNormalHold(1) * MyLine.Length
       LineNormalHold(2) = LineNormalHold(2) * MyLine.Length
       FirstPoint(0) = MyLine.StartPoint(0)
       FirstPoint(1) = MyLine.StartPoint(1)
       FirstPoint(2) = MyLine.StartPoint(2)
       SecondPoint(0) = MyLine.EndPoint(0)
       SecondPoint(1) = MyLine.EndPoint(1)
       SecondPoint(2) = MyLine.EndPoint(2)
       ThirdPoint(0) = LineNormalHold(0)
       ThirdPoint(1) = LineNormalHold(1)
       ThirdPoint(2) = LineNormalHold(2)
        ThisDrawing.SendCommand "align "
        ThisDrawing.SendCommand "all  "
        ThisDrawing.SendCommand Trim(Str(Round(FirstPoint(0), 12))) & "," & _
                                Trim(Str(Round(FirstPoint(1), 12))) & "," & _
                                Trim(Str(Round(FirstPoint(2), 12))) & " "
        ThisDrawing.SendCommand "0,0,0 "
        ThisDrawing.SendCommand Trim(Str(Round(SecondPoint(0), 12))) & "," & _
                                Trim(Str(Round(SecondPoint(1), 12))) & "," & _
                                Trim(Str(Round(SecondPoint(2), 12))) & " "
        ThisDrawing.SendCommand "1000,0,0 "
        ThisDrawing.SendCommand Trim(Str(Round(ThirdPoint(0), 12))) & "," & _
                                Trim(Str(Round(ThirdPoint(1), 12))) & "," & _
                                Trim(Str(Round(ThirdPoint(2), 12))) & " "
        ThisDrawing.SendCommand "0,10000,0 "
        ThisDrawing.SendCommand "z "
        ThisDrawing.SendCommand "e "
        Exit Sub
    End If
End Sub
Title: Re: New point planear to original points
Post by: MickD on May 24, 2007, 06:01:12 PM
I thought about an easier way to explain this and this may help also.

Ok, you have your ucs set to 'world' and you have an object at some place and odd angle in space. You want to convert the object form this place to the world ucs.
If you took a list of every point in the object in wcs they would be exactly as you would expect, now, if you placed your ucs on the object at some place for the origin with the x,y and z axes aligned as you would like them to be if you moved it to wcs, those points would now be different in relation to this new ucs yes?

The difference has everything to do with the coordinate system and the objects reference to it, what we're doing above is getting the objects preferred coordinate system(ucs) and transforming it to wcs but we are also taking all those points (the object) with it, you can move any object, no matter its shape like this and is exactly how a cad system does its MOVE/MIRROR/ROTATE/SCALE and other commands, it takes the current coordinate system and the objects vetices with it and multiply them by another coordinate system to produce a different result for all vertices.
MOVE is easier though as you only need a vector of the sp and end to multiply your vertices with and is much easier than pushing the vertices through a matrix but quite often you 'set' the matrix to translation which is optimised just for this.

Say you wanted to scale a box along the z axis by 2, you have your wcs (x=1,y=1,z=1 where 1 is a normalised length of the axis vector), to transform (scale), if you multiplied all vertices of the object by this ucs you would get the same values back as multiplying each vertex's xyz values by 1 would be the same.
Now, if you multiplied each vertex's xyz values by (x=1,y=1,z=2) respectively you would get the z vertex with a value twice the size as before and would therefore make your object longer along the z axis as a result.

As soon as that sinks in you'll find that rotations and transformations work exactly the same, it's just how you build your matrix.
Title: Re: New point planear to original points
Post by: Bryco on May 24, 2007, 09:57:54 PM
Mick what's your take on a line normal?
It appears to be meaningless and trustworthy.
You can give it any value and the line doesn't change.
Whereas circles, ellipses arcs plines etc behave.
I can imagine that a line shouldn't have a normal , but perhaps a normal plane.
I bring it up as 3d vectors are easier to figure out when you disregard the line.normal output.
Title: Re: New point planear to original points
Post by: MickD on May 24, 2007, 10:27:44 PM
That's why I gave 2 ways to produce the 'normal' using the region's normal or from 3 points (or a plane).
A line's normal from memory, if you draw a line in wcs in the xy plane the lines normal will be in the z direction, now, changing that normal won't change the line in any way which makes sense as a line doesn't have sides. Circles etc have their own plane and that is what you're changing when changing the normal.
Basically, changing a line's normal rotates the line around itself so there appears to be no change but a circle has many points around its insertion and they are rotated around this point so you do see a difference
Technically you could argue that a line, being 2d, shouldn't have a normal but it can prove invaluable in cad work. I think acad uses it primarily to distinguish which plane it was drawn in and it therefore gives it an ocs of sorts.

But, as you say, it can be changed and with some 3d stuff I was working on I used to do this intentionally.
An example -
I used to put in 'working' lines in a ucs to prepare the centre line geometry for a steel portal frame say, I would then add 3d solid steel sections to these lines using the lines normal to give the section its correct orientation and direction. If that line was drawn in say a ucs 90 deg different, my section would face the wrong way, I'd then change the normal of the line and reset the section only this time it faces the correct way.
Title: Re: New point planear to original points
Post by: Bryco on May 24, 2007, 10:39:53 PM
Good point.