Author Topic: Trying to insert block in current UCS  (Read 9089 times)

0 Members and 1 Guest are viewing this topic.

Patch61

  • Guest
Trying to insert block in current UCS
« on: March 25, 2011, 11:13:28 AM »
I'm trying to write a routine that will insert a block (with attributes) in the current UCS. I have cobbled this together from a few different sources, but I can't quite get the orientation to work consistently. I've tried a couple different examples of TransformBy on the block, but neither works consistently correctly.

Any ideas?

Also... How do I exit cleanly? When I'm done with my routine, it leaves the command line in a state that is not ready (no prompt). I have to hit Esc or Enter to get back the command prompt.


Code: [Select]
      Public Sub InsertDrawingAsBlock(ByVal doc As Document, ByVal path As String, ByVal blockname As String, _
                                      ByVal iPt As Point3d, Optional ByVal Space As String = "Model", _
                                      Optional ByVal LayerName As String = "Misc", Optional ByVal Xplode As Boolean = False, _
                                      Optional ByVal bRotate As Double = 0.0, Optional ByVal bXScale As Single = 1.0, _
                                      Optional ByVal bYScale As Single = 1.0, Optional ByVal bZScale As Single = 1.0)
         Dim curdb As Database = doc.Database
         Dim ed As Editor = doc.Editor
         Dim loc As DocumentLock = doc.LockDocument()
         Dim ucsMat As Matrix3d = ed.CurrentUserCoordinateSystem

         Using loc
            Dim blkid As ObjectId = ObjectId.Null
            Dim db As New Database(False, True)
            Using db
               db.ReadDwgFile(path, System.IO.FileShare.Read, True, "")
               blkid = curdb.Insert(path, db, True)
               Using tr As Transaction = doc.TransactionManager.StartTransaction()
                  Dim btr As BlockTableRecord = DirectCast(curdb.CurrentSpaceId.GetObject(OpenMode.ForWrite), BlockTableRecord)
                  Dim bt As BlockTable = DirectCast(tr.GetObject(curdb.BlockTableId, OpenMode.ForRead), BlockTable)
                  If bt.Has(blockname) Then
                     Dim btrId As ObjectId = bt(blockname).GetObject(OpenMode.ForRead).ObjectId
                     btr.UpgradeOpen()
                     Dim bref As New BlockReference(iPt, btrId)
                     btr.AppendEntity(bref)
                     tr.AddNewlyCreatedDBObject(bref, True)
                  Else
                  bt.UpgradeOpen()
                  Dim btrec As BlockTableRecord = DirectCast(blkid.GetObject(OpenMode.ForRead), BlockTableRecord)
                  btrec.UpgradeOpen()
                  btrec.Name = blockname
                  btrec.DowngradeOpen()
                     Using btr
                        'iPt = iPt.TransformBy(ucsMat)
                        Using bref As New BlockReference(iPt, blkid)
                           Dim mat As Matrix3d = Matrix3d.Identity
                           bref.TransformBy(mat)
                           'bref.TransformBy(ucsMat)
                           'Rotate  
                           bref.Rotation = bRotate * (Math.PI / 180)
                           'Scale factor
                           bref.ScaleFactors = New Scale3d(bXScale, bYScale, bZScale)
                           btr.AppendEntity(bref)
                           tr.AddNewlyCreatedDBObject(bref, True)

                           Using btAttRec As BlockTableRecord = DirectCast(bref.BlockTableRecord.GetObject(OpenMode.ForRead), BlockTableRecord)
                              Dim atcoll As Autodesk.AutoCAD.DatabaseServices.AttributeCollection = bref.AttributeCollection
                              For Each subid As ObjectId In btAttRec
                                 Dim ent As Entity = DirectCast(subid.GetObject(OpenMode.ForRead), Entity)
                                 Dim attDef As AttributeDefinition = TryCast(ent, AttributeDefinition)
                                 If attDef IsNot Nothing Then
                                    'ed.WriteMessage(vbLf & "Value: " + attDef.TextString)
                                    Dim attRef As New AttributeReference()
                                    attRef.SetPropertiesFrom(attDef)
                                    attRef.Visible = attDef.Visible
                                    attRef.SetAttributeFromBlock(attDef, bref.BlockTransform)
                                    attRef.HorizontalMode = attDef.HorizontalMode
                                    attRef.VerticalMode = attDef.VerticalMode
                                    attRef.Rotation = attDef.Rotation
                                    attRef.TextStyleId = attDef.TextStyleId
                                    attRef.Position = attDef.Position + iPt.GetAsVector()
                                    attRef.Tag = attDef.Tag
                                    attRef.FieldLength = attDef.FieldLength
                                    attRef.TextString = attDef.TextString
                                    attRef.AdjustAlignment(curdb)
                                    atcoll.AppendAttribute(attRef)
                                    tr.AddNewlyCreatedDBObject(attRef, True)
                                 End If
                              Next
                           End Using
                           bref.DowngradeOpen()

                           'Does it need to be exploded?
                           If Xplode = True Then
                              bref.ExplodeToOwnerSpace()
                              bref.Erase()
                           End If

                        End Using
                     End Using
                     btrec.DowngradeOpen()
                     bt.DowngradeOpen()
                     ed.Regen()

                  End If
                  tr.Commit()
               End Using
            End Using
         End Using
      End Sub
« Last Edit: March 25, 2011, 11:20:47 AM by Patch61 »

fixo

  • Guest
Re: Trying to insert block in current UCS
« Reply #1 on: March 25, 2011, 12:04:33 PM »
Try use wcs insertion point, something like


Code: [Select]
       Dim doc As Document = Application.DocumentManager.MdiActiveDocument
            Dim pt As Point3d = New Point3d(100, 100, 100)
            Dim ed As Editor = doc.Editor
            Dim mat As Matrix3d = ed.CurrentUserCoordinateSystem
            Dim ucs As CoordinateSystem3d = mat.CoordinateSystem3d
            Dim wcsmat As Matrix3d = _
                  Matrix3d.AlignCoordinateSystem(ucs.Origin, ucs.Xaxis, ucs.Yaxis, ucs.Zaxis, _
                                          Point3d.Origin, Vector3d.XAxis, Vector3d.YAxis, Vector3d.ZAxis)
            Dim inspt As Point3d = pt.TransformBy(wcsmat)
            InsertDrawingAsBlock(doc, "C:\Test\blah.dwg", "newblock", inspt, , , , , , , )

Patch61

  • Guest
Re: Trying to insert block in current UCS
« Reply #2 on: March 25, 2011, 01:13:09 PM »
Thanks for the reply and the help. I appreciate the effort!

However, from what I can tell, all that you are transforming into the UCS is the insertion point, not the inserted block. I'm trying to adapt what you posted into my program to transform the block reference, but I'm not having much luck.

It is still baffling to me that AutoDesk has done all this effort with their .Net API, but have totally missed the boat on useable documentation. So extremely far from a professional job that one is inclined to believe they are using the 'many monkees pounding keys for many days' method of documentation. Or perhaps, the person in charge of documentation is just plain stupid. Either scenario seems about as likely after trying to use the crap they shat on us.


Steve
« Last Edit: March 25, 2011, 01:19:48 PM by Patch61 »

kaefer

  • Swamp Rat
  • Posts: 572
Re: Trying to insert block in current UCS
« Reply #3 on: March 25, 2011, 04:13:56 PM »
However, from what I can tell, all that you are transforming into the UCS is the insertion point, not the inserted block. I'm trying to adapt what you posted into my program to transform the block reference, but I'm not having much luck.

Why would that be? You're already utilizing the TransformBy() method, just pass it an adaquate Matrix3d. Besides, the concept of combining transformations by multiplication of their matrices shouldn't be alien too.

Your other issue may be solved missing-prompt-wise by Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt().

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: Trying to insert block in current UCS
« Reply #4 on: March 25, 2011, 07:54:15 PM »
Patch 61,
What does this represent
ByVal iPt As Point3d

??

Is the variable holding a point in the current UCS or in WORLD (note that the current UCS may BE World).

Points selected by getpoint will be in the current UCS.



Quote
However, from what I can tell, all that you are transforming into the UCS is the insertion point, not the inserted block.

Your understanding is incorrect.
Fixo's code transforms the UCS defined pt to the WCS defined insPt
Then inserts the block at  insPt.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

--> Donate to theSwamp<--

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: Trying to insert block in current UCS
« Reply #5 on: March 25, 2011, 08:18:14 PM »

addendum

Does this
Quote
Trying to insert block in current UCS.

actually mean you want to do this :

Insert a Block at a point defined in the current UCS ??
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

--> Donate to theSwamp<--

kaefer

  • Swamp Rat
  • Posts: 572
Re: Trying to insert block in current UCS
« Reply #6 on: March 26, 2011, 03:55:28 AM »
Does this
Quote
Trying to insert block in current UCS.

actually mean you want to do this :

Insert a Block at a point defined in the current UCS ??

I think he means:

Insert a block at a point defined in the current UCS and align it with the XY plane of the current UCS.

Discounting rotation and scaling, the transform could look like
Code: [Select]
    db.GetUcsMatrix * Matrix3d.Displacement(iPt - Point3d.Origin)

The database extension was discussed recently http://www.theswamp.org/index.php?topic=37409.msg425283#msg425283,
selected earlier mentions are http://www.theswamp.org/index.php?topic=19035.msg231983#msg231983, http://www.theswamp.org/index.php?topic=20656.msg251052#msg251052 and way back in 2005 http://www.theswamp.org/index.php?topic=7657.msg97523#msg97523.

Patch61

  • Guest
Re: Trying to insert block in current UCS
« Reply #7 on: March 28, 2011, 08:57:53 AM »
Does this
Quote
Trying to insert block in current UCS.

actually mean you want to do this :

Insert a Block at a point defined in the current UCS ??

I think he means:

Insert a block at a point defined in the current UCS and align it with the XY plane of the current UCS.

Discounting rotation and scaling, the transform could look like
Code: [Select]
   db.GetUcsMatrix * Matrix3d.Displacement(iPt - Point3d.Origin)

The database extension was discussed recently http://www.theswamp.org/index.php?topic=37409.msg425283#msg425283,
selected earlier mentions are http://www.theswamp.org/index.php?topic=19035.msg231983#msg231983, http://www.theswamp.org/index.php?topic=20656.msg251052#msg251052 and way back in 2005 http://www.theswamp.org/index.php?topic=7657.msg97523#msg97523.

Yes, I want to take a user-clicked location and insert a block at that location, with the block being aligned to the current UCS, just like how the Insert command works in AutoCAD.

You guys have given me something new to try, so I will integrate your ideas with my code and see what we end up with. I've never completely understood matrix math... it hurts my brain!

Thanks for the help!


Steve
« Last Edit: March 28, 2011, 09:12:53 AM by Patch61 »

Patch61

  • Guest
Re: Trying to insert block in current UCS
« Reply #8 on: March 28, 2011, 09:51:23 AM »
How would I use this in the context of my posted code? Is this returning a Matrix3D object?

Code: [Select]
   db.GetUcsMatrix * Matrix3d.Displacement(iPt - Point3d.Origin)

Again, matrix math is greek to me. And the poor documentation from AutoDesk leaves me scratching my head on these issues.  :|

Update:
When I try to use this as follows:
Code: [Select]
Dim UserMat As Matrix3d
UserMat = db.GetUcsMatrix * Matrix3d.Displacement(iPt - Point3d.Origin)

Visual Studio tells me that 'GetUcsMatrix' is not a member of 'AutoDesk.AutoCAD.DatabaseServices.Database'.

When I try to add the extension to my project, VS tells me 'Extension methods can be defined only in modules.' Huh? What modules? I'm working in VB, if that matters.

Ugh. I feel like a caveman trying to understand this. Inserting a block should be a simple thing. I hate this .Net API.  :realmad:


Thanks,
Steve
« Last Edit: March 28, 2011, 10:27:12 AM by Patch61 »

Patch61

  • Guest
Re: Trying to insert block in current UCS
« Reply #9 on: March 29, 2011, 08:36:30 AM »
Help! Please? Anybody? :cry:

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: Trying to insert block in current UCS
« Reply #10 on: March 29, 2011, 08:48:44 AM »

I'll have a play tomorrow for you
.. but I do think you may have a little reading to do :)
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

--> Donate to theSwamp<--

Patch61

  • Guest
Re: Trying to insert block in current UCS
« Reply #11 on: March 29, 2011, 09:03:33 AM »

I'll have a play tomorrow for you
.. but I do think you may have a little reading to do :)

Thanks, Kerry!

I don't mind reading and learning at all. Just, in this case, I have no clue what to read!

Steve

gile

  • Water Moccasin
  • Posts: 2261
  • Marseille, France
Re: Trying to insert block in current UCS
« Reply #12 on: March 29, 2011, 11:29:24 AM »
Hi,

Here's a little sample to insert a bloc in the current UCS.

EDIT there was something wrong in the precedent code.
This one is a little bit more explicit

Code: [Select]
        [CommandMethod("TEST")]
        public void InsertInCurrentUcs()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            PromptResult pr = ed.GetString("\nEnter the block name: ");
            if (pr.Status != PromptStatus.OK)
                return;
            string bName = pr.StringResult;
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                if (!bt.Has(bName))
                {
                    ed.WriteMessage("\nCan't find '{0}' block.", bName);
                    return;
                }

                PromptPointResult ppr = ed.GetPoint("\nSpecify insertion point: ");
                if (ppr.Status != PromptStatus.OK)
                    return;

                // Get the current UCS Z axis (extrusion direction)
                Matrix3d ucsMat = ed.CurrentUserCoordinateSystem;
                CoordinateSystem3d ucs = ucsMat.CoordinateSystem3d;
                Vector3d zdir = ucsMat.CoordinateSystem3d.Zaxis;

                // Get the OCS corresponding to UCS Z axis
                Matrix3d ocsMat = MakeOcs(zdir);

                // Transform the input point from UCS to OCS
                Point3d pt = ppr.Value.TransformBy(ucsMat.PreMultiplyBy(ocsMat));

                // Get the X axis of the OCS
                Vector3d ocsXdir = ocsMat.CoordinateSystem3d.Xaxis;

                // Get the UCS rotation (angle between the OCS X axis and the UCS X axis)
                double rot = ocsXdir.GetAngleTo(ucs.Xaxis, zdir);

                BlockTableRecord btr =
                    (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
                BlockReference br = new BlockReference(pt, bt[bName]);
                br.Position = pt;
                br.Rotation = rot;
                br.Normal = zdir;
                btr.AppendEntity(br);
                tr.AddNewlyCreatedDBObject(br, true);
                tr.Commit();
            }
        }

        // Return an OCS Matrix3d using the 'Arbitrary Axis Algoriythm'
        private Matrix3d MakeOcs(Vector3d zdir)
        {
            double d = 1.0 / 64.0;
            zdir = zdir.GetNormal();
            Vector3d xdir = Math.Abs(zdir.X) < d && Math.Abs(zdir.Y) < d ?
                Vector3d.YAxis.CrossProduct(zdir).GetNormal() :
                Vector3d.ZAxis.CrossProduct(zdir).GetNormal();
            Vector3d ydir = zdir.CrossProduct(xdir).GetNormal();
            return new Matrix3d(new double[16]{
                xdir.X, xdir.Y, xdir.Z, 0.0,
                ydir.X, ydir.Y, ydir.Z, 0.0,
                zdir.X, zdir.Y, zdir.Z, 0.0,
                0.0, 0.0, 0.0, 1.0});
        }
« Last Edit: March 29, 2011, 12:37:05 PM by gile »
Speaking English as a French Frog

kaefer

  • Swamp Rat
  • Posts: 572
Re: Trying to insert block in current UCS
« Reply #13 on: March 29, 2011, 12:11:53 PM »
Here's a little sample to insert a bloc in the current UCS.

Okay, I'll bite. What do you win by transforming the insertion point only and do the calculation of the normal direction manually? I concede that it's always useful to have the know how to do it. Besides, there's little trick in your code: Setting the Position property isn't the same thing as calling the constructor with the insertion point.

Let's compare:
Code: [Select]
type acApp = Autodesk.AutoCAD.ApplicationServices.Application

type Database with
    member db.IsPaperSpace =
        if db.TileMode then false
        else
            let ed = acApp.DocumentManager.MdiActiveDocument.Editor
            (db.PaperSpaceVportId = ed.CurrentViewportObjectId)

    member db.GetUcsMatrix =
        let (origin, xAxis, yAxis) =
            if db.IsPaperSpace then
                db.Pucsorg, db.Pucsxdir, db.Pucsydir
            else
                db.Ucsorg, db.Ucsxdir, db.Ucsydir
        Matrix3d.AlignCoordinateSystem(
            Point3d.Origin, Vector3d.XAxis, Vector3d.YAxis, Vector3d.ZAxis,
            origin, xAxis, yAxis, xAxis.CrossProduct yAxis )

let myInsertUCS (tr: Transaction) _ (db: Database) (btrOid: ObjectId) (iPt: Point3d) =
        let mat =
            db.GetUcsMatrix *
            Matrix3d.Displacement(iPt- Point3d.Origin)
        let btr = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) :?> BlockTableRecord
        let bref = new BlockReference(Point3d.Origin, btrOid)
        bref.TransformBy mat
        btr.AppendEntity bref |> ignore
        tr.AddNewlyCreatedDBObject(bref, true)

let gileInsertUCS (tr: Transaction) (ed: Editor) (db: Database) (btrOid: ObjectId) (iPt: Point3d) =
    // Transform the input point from UCS to WCS
    let ucsMat = ed.CurrentUserCoordinateSystem
    let pt = iPt.TransformBy ucsMat

    // Get the current UCS Z axis (extrusion direction)
    let ucs = ucsMat.CoordinateSystem3d
    let zdir = ucs.Zaxis

    // Get the X axis of the OCS from the current UCS Z axis
    // (using the 'Arbitrary Axis Algoriythm')
    let d = 1.0 / 64.0
    let ocsXdir =
        if abs zdir.X < d && abs zdir.Y < d then
            Vector3d.YAxis.CrossProduct zdir
        else
            Vector3d.ZAxis.CrossProduct zdir

    // Get the UCS rotation (angle between the OCS X axis and the UCS X axis)
    let rot = ocsXdir.GetAngleTo(ucs.Xaxis, zdir);

    let btr = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) :?> BlockTableRecord
    let bref =
        new BlockReference(
            Point3d.Origin, btrOid,
            Rotation = rot,
            Normal = zdir,
            Position = pt )
    btr.AppendEntity bref |> ignore
    tr.AddNewlyCreatedDBObject(bref, true)

let insertUCSCmd f =
    let doc = acApp.DocumentManager.MdiActiveDocument
    let db = doc.Database
    let ed = doc.Editor
    let pstr =
        new PromptStringOptions(
            "Block name",
            AllowSpaces = true )
        |> ed.GetString
    if pstr.Status = PromptStatus.OK then
        use tr = db.TransactionManager.StartTransaction()
        let bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) :?> BlockTable
        if not(bt.Has pstr.StringResult) then
            ed.WriteMessage("\n{0} not defined. ", pstr.StringResult)
        else
            let ppr = ed.GetPoint("Insertion point")
            if ppr.Status = PromptStatus.OK then
                f tr ed db bt.[pstr.StringResult] ppr.Value
        tr.Commit()

[<CommandMethod "myInsertUCS">]
let myInserUCSCmd() = insertUCSCmd myInsertUCS

[<CommandMethod "gileInsertUCS">]
let gileInserUCSCmd() = insertUCSCmd gileInsertUCS

gile

  • Water Moccasin
  • Posts: 2261
  • Marseille, France
Re: Trying to insert block in current UCS
« Reply #14 on: March 29, 2011, 12:58:28 PM »
You're right kaefer, I loose myself in the OCS route...
Speaking English as a French Frog