TheSwamp
Code Red => VB(A) => Topic started by: Bryco on July 26, 2006, 10:34:28 PM
-
ThisDrawing.Utility.GetSubEntity ent, varPick, TransMatrix, ContextData, vbCrLf & "p"
I haven't messed alot with transformations so I'm not sure what this means.
Aim: I have a function that adds a leader and note to a block with the mtext showing either tha name of the block or the xdata attached to the block. It works very well and is handy. But I would like to pick a point on the block (This point is really just the center of the pick box) and have that point change to the nearest point on the block for my leader arrowhead.
I have a nearestpointtoEnt function worked out nicely but the ent passes by the GetSubEntity function is not the entity in the blockref but the entity in the block at 0,0.
The TransMatrix is presumably the translation matrix the blockref underwent, but picking an object and applying the same TransMatrix to it makes it go all curly wobbler.
Any ideas?
-
Sorry Bryco,
I do not follow what you are doing. From what I can tell, you just need to get each entity inside of the block, then for each type of ent, ie line or arc, find the end points, midpoints, etc., add all you find to an array, find the shortest distance from where you picked to the closest ent and use that point for your leader. It seems pretty simple and I know you are a much better programmer then me, so I must not understand why you would need a matrix at all.
With that said, I do have code that goes inside block entities, but I only yank xdata from them, so you may need that matrix after all because I cannot say for sure I would be able to get that geometry info correct because I have never actually tried it.
-
Well I would like to translate the picked point to its equivalent position next to the block entity (not the blockref),
then I can find the nearest point on the entity, then translate that back to the blockref.
I suspect that the transmatrix has the right info in it but I don't know how to use it.
-
so, you are picking a sub ent of the inserted block ref which gives you a point which after calculating a nearest point on sub ent is given back to you in relation to the block table record (as if inserted at 0,0,0) and you can't xform your leader to the inserted ref using the block xform matrix, is that what you are saying?
-
Not quite Mick. The ent given is part of the blockrecord not part of the blockref. (I can copy it using copyobjects,using modelspace as the new owner, but it will be as if inserted at 0,0,0). In fact I cant tell which blockref I picked. The picked point is the actual point I picked .
So I have an odd mix. The tie is definately in the transmatrix
-
Ok, that's sort of what I meant/thought :)
Try inverting the matrix from the picked block ref, transform your picked point, do your calc's then xform back. I wouldn't have a clue if vba can do this but if you have to you would simply negate each value in the matrix given which should do the trick.
-
Thanks Mick. My mate at work knows a bit of maths and after transposing the matrix it works backwards. So now I think if I can figure out the inverting I may have something.(There is no native function). The bummer is that I would rather just translate the pickedpoint but I dont know how to feed that into a transforation matrix. The vba one only accepts objects.
I guess I'm going to have to read up on linear algebra, ruin a good beer that stuff.
-
Made a bit of progress.
Still this InverseMatrix stuff is bizarre.
The function I made seems to be ok but throws a wobbler.
It relies on adding an identity matrix to the matrix then
doing some pivot stuff and checking to see if the first part tuned into an identity matrix.
Well I don't know for sure makes them work or not.
Doesn't really sound like proress does it?
-
Can't you just create a matrix out of the info from the inserted block ref (normalised normal, x and y directions with the vector to inseted point) and negate each element to invert the matrix??
-
Ok, I see you have the normal and rotation prop's available in vba.
lets try this -
create a point (inspntX + 1, inspntY, inspntZ) '// one unit along the xaxis:
rotate this new point by the given rot angle
get the cross product of the vector from the inspnt to new point and the block's normal, this will give you a Y axis.
now cross product the new Yaxis with the normal to orthoganlise (is that a word) the axis.
You should now have enough info to construct a matrix given your new 3 axes and the translation vector to the insert point. Negate them all to reverse the directions and transform your point.
Does that sound doable??
-
Might end up that way Mick.
A blockref has those proprties but i don't have a blockref. (Point and block item)
I can get the blockref with a selectatpoint selectionset but I would like to learn how to use what I have first.
Here is a debug.print of the matricies given for 2 blocks
TransMatrix for insertpt 4,4 0 rotation
1 0 0 0
0 1 0 0
0 0 1 0
4 4 0 1
TransposedMatrix
1 0 0 4
0 1 0 4
0 0 1 0
0 0 0 1
TransMatrix insertpt 4,4 0 rotation
0.707106781186547 0.707106781186548 0 0
-0.707106781186548 0.707106781186547 0 0
0 0 1 0
4 2 0 1
TransposedMatrix
0.707106781186547 -0.707106781186548 0 4
0.707106781186548 0.707106781186547 0 2
0 0 1 0
0 0 0 1
The transposed look kosher and works perfectly to bring an ent copied from the blockrecord to the blockref.
I just got to work on the inverse.
I might have a mix up between integers and doubles
-
Something doesn't look right, the bottom row below the vectors should be all 0's, these are there only to 'square' up the matrix or maybe for projection calc's, your translation vector should be down the right. I attached an image from the help doc's that may help. They definitely shouldn't be swapping from bottom to right side anyway I wouldn't think.
-
I think all you need is to 'invert' it not transpose, try negating the values instead of swapping them around.
i.e. you can move something by a vector (3,2,1) and to move it the other way would be the vector negated (-3,-2,-1) which should point the other direction. Same should apply for rotations.
hth,
Mick.
-
Well Mick, you cracked it for a win.
Invert then transpose seems to work.
Thanks for the tip.
Now I'll have to tackle the multiplying formulae.
-
These seem to work so far, they up for discussion and I,m sure they could be better
Function Transpose(Matrix As Variant) As Variant
Dim transMat(0 To 3, 0 To 3) As Double
Dim i As Integer, j As Integer
For i = 0 To 3
For j = 0 To 3
transMat(i, j) = Matrix(j, i)
Next j
Next i
Transpose = transMat
End Function
Function InverseMatrix(M As Variant) As Variant
Dim Matrix() As Double
Dim RowCt As Integer, ColCt As Integer
Dim NewColCt As Integer
Dim NoInverse As Boolean
Dim i As Integer, j As Integer
RowCt = UBound(M, 1)
ColCt = UBound(M, 2)
NewColCt = ColCt + RowCt + 1
ReDim Matrix(RowCt, NewColCt)
For i = 0 To RowCt
'add the given matrix
For j = 0 To ColCt
If Rd(M(i, j), 0) Then
Matrix(i, j) = 0
Else
Matrix(i, j) = M(i, j)
End If
Next j
'add an identity crisis
For j = ColCt + 1 To NewColCt
If j - (ColCt + 1) = i Then
Matrix(i, j) = 1
Else
Matrix(i, j) = 0
End If
Next
Next i
Matrix = MPivot(Matrix)
'now see if it worked
For i = 0 To RowCt
For j = 0 To ColCt
If j = i Then
If Not Rd(Matrix(i, j), 1) Then
NoInverse = True
Debug.Print Matrix(i, j)
'Exit Function
End If
Else
If Not Rd(Matrix(i, j), 0) Then
NoInverse = True
'Exit Function
End If
End If
Next
Next i
If NoInverse Then
'MsgBox "nope"
' Debug.Print
' For i = 0 To 3
' Debug.Print Matrix(i, 0), Matrix(i, 1), Matrix(i, 2), Matrix(i, 3), Matrix(i, 4), Matrix(i, 5), Matrix(i, 6), Matrix(i, 7)
' Next
Matrix = OrderMatrix(Matrix)
End If
'If Not NoInverse Then
ReDim InVMatrix(RowCt, ColCt)
For i = 0 To RowCt
For j = ColCt + 1 To NewColCt
InVMatrix(i, j - (ColCt + 1)) = Matrix(i, j)
Next j
Next i
'End If
InverseMatrix = InVMatrix
End Function
Function MPivot(Matrix) As Variant
Dim j As Integer, i As Integer, k As Integer
Dim iP As Integer
Dim Pivot As Double
Dim ColCt As Integer
Dim RowCt As Integer
Dim PC As Double
Dim Sign As Integer
Dim Den As Integer
Dim dTemp As Double
RowCt = UBound(Matrix, 1)
ColCt = UBound(Matrix, 2)
'ij is row,column
For i = 0 To RowCt
For j = 0 To ColCt
If Matrix(i, j) <> 0 Then
Pivot = Matrix(i, j)
iP = j
Exit For
End If
Next j
For k = 0 To RowCt
If Not k = i Then
PC = Matrix(k, iP)
If PC = 0 Then GoTo Skip
Sign = 1
If Pivot < 0 Then
If PC < 0 Then
Sign = -1
End If
Else
If PC > 0 Then
Sign = -1
End If
End If
Dim N1 As Double, N2 As Double
N1 = Abs(Pivot): N2 = Abs(PC)
Den = LCD(N1, N2)
For j = 0 To ColCt
dTemp = Matrix(k, j) * N1 / Den + (Matrix(i, j) * N2 / Den * Sign)
If Rd(dTemp, 0) Then
Matrix(k, j) = 0
Else
Matrix(k, j) = dTemp
End If
Next j
End If
Skip:
Next k
Next i
For i = 0 To RowCt
For j = 0 To ColCt
If Matrix(i, j) <> 0 Then
Pivot = 1 / Matrix(i, j)
Exit For
End If
Next j
For j = 0 To ColCt
Matrix(i, j) = Matrix(i, j) * Pivot
Next j
Next i
MPivot = Matrix
End Function
Function OrderMatrix(Matrix As Variant) As Variant
Dim i As Integer, j As Integer
Dim k As Integer, l As Integer
Dim RowCt As Integer, ColCt As Integer
RowCt = UBound(Matrix, 1)
ColCt = UBound(Matrix, 2)
ReDim tempRow(ColCt) As Double
'ij is row,column
For i = 0 To RowCt
For j = 0 To ColCt
If j = i Then
If Not Rd(Matrix(i, j), 1) Then
For k = 0 To RowCt
If Not k = i Then
If Rd(Matrix(k, j), 1) Then
For l = 0 To ColCt
tempRow(l) = Matrix(k, l)
Matrix(k, l) = Matrix(i, l)
Matrix(i, l) = tempRow(l)
Next l
End If
End If
Next k
End If
End If
Next j
Next i
OrderMatrix = Matrix
End Function
Function LCD(N1, N2) As Integer
'LowestCommonDenominator
Dim iCt As Integer, i As Integer
Dim Ans As Integer
Ans = 1
If N1 < N2 Then
iCt = N1
Else
iCt = N2
End If
If iCt > 1 Then
For i = 1 To iCt
If (N1 Mod i = 0 And N2 Mod i = 0) Then Ans = i
Next i
End If
LCD = Ans
End Function
Function TransformPt(M As Variant, P1 As Variant) As Variant
Dim i As Integer
Dim x As Double, y As Double, z As Double, D As Double
Dim P(3) As Double
For i = 0 To 2
P(i) = P1(i)
Next
P(3) = 1
For i = 0 To 3
x = x + P(i) * M(i, 0)
y = y + P(i) * M(i, 1)
z = z + P(i) * M(i, 2)
D = D + P(i) * M(i, 3)
Next
P1(0) = x: P1(1) = y: P1(2) = z
TransformPt = P1
End Function
And this is the reason I wanted to use a matrix
Sub TestMultiplyMatrix()
Dim P, Ent As AcadEntity
Dim i As Integer
Dim M, ContextData, TransMatrix
ThisDrawing.Utility.GetSubEntity Ent, P, TransMatrix, ContextData
ThisDrawing.ModelSpace.AddPoint P
If VarType(ContextData) = vbEmpty Then
MsgBox "This is not a block"
Exit Sub
End If
M = InverseMatrix(TransMatrix)
P = TransformPt(M, P)
P = NearestPtOnObject(Ent, P)
P = TransformPt(TransMatrix, P)
ThisDrawing.ModelSpace.AddPoint P
End Sub
The NearestPtOnObject(Ent, P) function is not included as I'm still working on it.
I thought getting this far was going to give me a little break from math but instead I just seemed to have opened Pandora's box.
The nearest point to an ellipse is insane. I dont even know if the article is offering a proof or a question. I'm inclined to go with the one that mentions "best guess",
-
Somebody will correct me if my recollection on this is incorrect, but from memory, the tranformation matrix returned from this function (like the LISP nentsel) is a compound matrix. This means that if you picked an entity inside a block reference, which in turn was inside another block reference, it's the resultant matrix of the 2 block ref transforms.
Bryco, can you give me more detail about what you are trying to do and the results you have thus far and I will try and look at this tomorrow.
Cheers,
Glenn.
-
I think I've got it Glenn.
The matrix part is working so far, although I should investigate nested blocks a little more.
So I can transform the picked point relative to the blockrecords picked entity postion.
Here I find the nearest point to the picked ent.
Actually the first step is to put the picked point on the ents plane or elevation so
unless I can also find a matrix for the view this whole thing may have to be rethought.
Now I can get the nearest point.
The math for this is fairly simple for some entities.
Circle, arc, pline, Line no worries.
The spline is too difficult for me so I gave up and use the Vlax curves class.
I'll probably use that for an ellipse as well as following an ellipse normal (the one going in from the tangent)
is a bit above my math.
So I'm close but still have a way to go.
I forgot that Set Owner = ThisDrawing.ObjectIdToObject(ContextData(i))
will give me the blockreference from getsubentity function
so I may have to go that way.