Author Topic: Placing block correctly on UCS  (Read 4544 times)

0 Members and 1 Guest are viewing this topic.

TJK44

  • Guest
Placing block correctly on UCS
« on: December 07, 2011, 12:08:05 PM »
I have code that works fine if the UCS is set to TOP. The way my code is supposed to work is, you select entities that you want to make up the block, and it keeps the entities positions in the drawing. If the UCS is changed to anything but TOP, when the block is created in the drawing it is moved to another place in the drawing. Would it make sense to change the UCS to TOP when the user runs the command or is there a better way to do this? Here is the code I am working with. Thank you.

-Ted

Code: [Select]
    Function CreateABlock(ByVal pos As Point3d, ByVal blname As String) As ObjectId
        Dim ed As Editor = Application.DocumentManager.MdiActiveDocument.Editor
        Dim newBtrId As ObjectId
        Dim db As Database = HostApplicationServices.WorkingDatabase()
        Using trans As Transaction = db.TransactionManager.StartTransaction()
            Dim bt As BlockTable = trans.GetObject(db.BlockTableId, OpenMode.ForWrite)
                Dim res As PromptSelectionResult = ed.GetSelection
                Dim oid As New ObjectIdCollection(res.Value.GetObjectIds())
                Dim pt As Point3d = pos.TransformBy(ed.CurrentUserCoordinateSystem)

                If res.Status = PromptStatus.OK Then
                    Try
                        Dim newBtr As BlockTableRecord = New BlockTableRecord()
                        newBtr.Name = blname
                        newBtrId = bt.Add(newBtr)

                        trans.AddNewlyCreatedDBObject(newBtr, True)

                        For Each id In res.Value.GetObjectIds()
                            Dim ent = TryCast(trans.GetObject(id, OpenMode.ForRead), Entity)
                            If ent <> Nothing Then
                                Dim newent = TryCast(ent.Clone(), Entity)
                                If newent <> Nothing Then

                                End If
                            End If
                        Next

                        Dim disp As Vector3d = pt.GetVectorTo(Point3d.Origin)
                        For Each id As ObjectId In oid
                            Dim ent As Entity = DirectCast(trans.GetObject(id, OpenMode.ForWrite), Entity)
                            ent.TransformBy(Matrix3d.Displacement(disp))
                           
                        Next

                        newBtr.AssumeOwnershipOf(oid)

                   Catch ex As System.Exception
                        Autodesk.AutoCAD.ApplicationServices.Application.ShowAlertDialog(ex.Message & vbLf & ex.StackTrace)
                        trans.Dispose()
                    End Try
                End If

            End If

            trans.Commit()
        End Using
        Return newBtrId

    End Function

SGP2012

  • Guest
Re: Placing block correctly on UCS
« Reply #1 on: December 07, 2011, 01:27:44 PM »
Using Clone() can be very dangerous - it only performs a shallow clone. Best to avoid it whenever possible.

Have you considered using Database.DeepCloneObjects() instead. That method will deepclone your specified entities directly into your chosen container (your BlockTableRecord) - all in one function call.

TJK44

  • Guest
Re: Placing block correctly on UCS
« Reply #2 on: December 07, 2011, 02:30:14 PM »
What is the difference between a shallow and deep clone?

BillZndl

  • Guest
Re: Placing block correctly on UCS
« Reply #3 on: December 07, 2011, 02:37:50 PM »
I have code that works fine if the UCS is set to TOP. The way my code is supposed to work is, you select entities that you want to make up the block, and it keeps the entities positions in the drawing. If the UCS is changed to anything but TOP, when the block is created in the drawing it is moved to another place in the drawing. Would it make sense to change the UCS to TOP when the user runs the command or is there a better way to do this? Here is the code I am working with. Thank you.

-Ted


Just out of curiosity, have you every noticed that when you create a block manually in AutoCAD,
it asks you for a "base point"?
You could probably end a lot of your struggles inserting blocks by simply adding an "origin" to the table record.
you already have the point declared in your code
(assuming this is the point on the block that you want the block to be inserted at).

"Dim pt As Point3d = pos.TransformBy(ed.CurrentUserCoordinateSystem)"
Code: [Select]
                    If res.Status = PromptStatus.OK Then
                    Try
                        Dim newBtr As BlockTableRecord = New BlockTableRecord()
                        newBtr.Name = blname

                    >>>    newBtr.Origin = pt  <<<

                        newBtrId = bt.Add(newBtr)

                        trans.AddNewlyCreatedDBObject(newBtr, True)

                       

TJK44

  • Guest
Re: Placing block correctly on UCS
« Reply #4 on: December 07, 2011, 03:09:08 PM »
Thanks Bill but that still didn't solve my issue. When the block is created it is still getting shifted. Is there a way to set the UCS to TOP before adding the block reference?

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Placing block correctly on UCS
« Reply #5 on: December 07, 2011, 03:12:37 PM »
What is the difference between a shallow and deep clone?
You can read the docs which has good and a large amount of information.
For a simple generic definition is how it handles pointers of the copied object. If it just copies the 'memory address' and now the new and original object have a member that point to the same object or creates a new one, etc....
 

BillZndl

  • Guest
Re: Placing block correctly on UCS
« Reply #6 on: December 07, 2011, 03:18:09 PM »
Thanks Bill but that still didn't solve my issue. When the block is created it is still getting shifted.

If you are setting your base point correctly when you create the block,
there really shouldn't be an issue.

Inserting blocks on different ucs's is tricky because the block will come in on the same plane as it was created.



« Last Edit: December 07, 2011, 03:21:14 PM by BillZndl »

TJK44

  • Guest
Re: Placing block correctly on UCS
« Reply #7 on: December 07, 2011, 03:34:44 PM »
This is how I am getting the base point for pos value in the function.

Code: [Select]
                Dim blPos As PromptPointOptions = New PromptPointOptions("Enter Block Insertion Point: ")

                Dim blPosRes As PromptPointResult = Nothing
                blPosRes = ed.GetPoint(blPos)

BillZndl

  • Guest
Re: Placing block correctly on UCS
« Reply #8 on: December 07, 2011, 03:59:33 PM »
This is how I am getting the base point for pos value in the function.

Code: [Select]
                Dim blPos As PromptPointOptions = New PromptPointOptions("Enter Block Insertion Point: ")

                Dim blPosRes As PromptPointResult = Nothing
                blPosRes = ed.GetPoint(blPos)

This is how I have been doing it: C#, but very similar.
Code: [Select]
            PromptPointOptions opts = new PromptPointOptions("\nBase point: < pick > ");
            opts.AllowNone = false;
            PromptPointResult bpRes = editor.GetPoint(opts);

So I am literally picking a point some where on one of the block entities.

Then further down in the transaction:

Code: [Select]
          BlockTableRecord record = new BlockTableRecord();

          record.Name = blkName;

          record.Origin = bpRes.Value.TransformBy(editor.CurrentUserCoordinateSystem);

          table.Add(record);

           transaction.AddNewlyCreatedDBObject(record, true);

Now when you insert the block, you need to give it a destination point and your block base point should come in
right on that destination point.

I've never tried to just type in a coordinate as each ucs has it's own origin when it is set up so there may be some translation of the destination point required to get the desired result.



TJK44

  • Guest
Re: Placing block correctly on UCS
« Reply #9 on: December 08, 2011, 09:19:33 AM »
Thanks Bill. Looks like I will need to play around with translations I think. The block still gets moved if not in top ucs.

BillZndl

  • Guest
Re: Placing block correctly on UCS
« Reply #10 on: December 08, 2011, 09:45:21 AM »
Thanks Bill. Looks like I will need to play around with translations I think. The block still gets moved if not in top ucs.

You're welcome.
Anything you do in AutoCAD, (move, copy, etc.) you are going "from" somewhere "to" somewhere,
so once your basepoint is established in your block record, then placing it is just a matter of feeding it a point
that translates to the UCS that you are placing it on.

I'd write you some code but just don't have the time for that as I also have a job that I do for money that takes first presidence.  :lol:


TJK44

  • Guest
Re: Placing block correctly on UCS
« Reply #11 on: December 08, 2011, 01:14:37 PM »
Thanks for the extra input, I'll put it to use and see where I can get.

kaefer

  • Guest
Re: Placing block correctly on UCS
« Reply #12 on: December 08, 2011, 05:35:00 PM »
I've never tried to just type in a coordinate as each ucs has it's own origin when it is set up so there may be some translation of the destination point required to get the desired result.

Quite some transformation, and somewhat contrary to my assumption that this ground had been covered extensively before. In case I missed the relevant contributions, here's the reinvention of the wheel again.

Up front, there's the need to provide for a selection set and for the alternative of keeping or erasing the selected objects after the creation of a BlockTableRecord.

Code: [Select]
        [CommandMethod("BlockKeepObjects", CommandFlags.UsePickSet)]
        public void BlockKeepObjectsCommand()
        {
            BlockCommand(true);
        }
        [CommandMethod("BlockReplaceObjects", CommandFlags.UsePickSet)]
        public void BlockReplaceObjectsCommand()
        {
            BlockCommand(false);
        }

Now for the meaty parts. Let's select a valid block name and an insertion point. The base point is in UCS and stays that way. The selected objects are either deepcloned (keep objects) or their ownership gets transferred (replace objects with BlockReference), and after adding them to the BlockTableRecord they are in WCS and need transforming to UCS. The BlockReference on the other hand starts life in UCS at 0,0 and gets transformed to WCS combined with a translation to the insertion point. Note that only objects and no points are transformed.

The rest of the code is various house-keeping, like support for annotative blocks and for attributes. There's plenty of scope to screw up. It's totally possible that I'm missing something substantial, so please bear with me.

Code: [Select]
        void BlockCommand(bool keepObjects)
        {
            Database db = acadApp.DocumentManager.MdiActiveDocument.Database;
            Editor ed = acadApp.DocumentManager.MdiActiveDocument.Editor;

            PromptSelectionResult psr = ed.GetSelection();
            if (psr.Status != PromptStatus.OK) return;
            PromptStringOptions pso =
                new PromptStringOptions(
                    "Enter Block Name: ")
                    {
                        AllowSpaces = true
                    };
            PromptResult pr = ed.GetString(pso);
            if (pr.Status != PromptStatus.OK) return;
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                bool nameOk = false;
                try
                {
                    SymbolUtilityServices.ValidateSymbolName(pr.StringResult, false);
                    if (bt.Has(pr.StringResult))
                        ed.WriteMessage("\nBlock \"{0}\" already exists. ", pr.StringResult);
                    else
                        nameOk = true;
                }
                catch
                {
                    ed.WriteMessage("\nInvalid block name: \"{0}\". ", pr.StringResult);
                }
                if (!nameOk) return;
                PromptPointOptions ppo =
                    new PromptPointOptions(
                        "\nPick Insertion Point");
                PromptPointResult ppr = ed.GetPoint(ppo);
                if (ppr.Status != PromptStatus.OK) return;

                // Create BlockTableRecord
                Matrix3d ucs2wcs = ed.CurrentUserCoordinateSystem;
                Matrix3d wcs2ucs = ucs2wcs.Inverse();
                using (BlockTableRecord btr =
                            new BlockTableRecord()
                            {
                                Origin = ppr.Value,
                                Name = pr.StringResult,
                                Annotative = AnnotativeStates.True
                            })
                {
                    // Add BlockTableRecord to Database
                    bt.UpgradeOpen();
                    bt.Add(btr);
                    tr.AddNewlyCreatedDBObject(btr, true);

                    // Populate newly created BlockTableRecord with selected objects
                    ObjectIdCollection oidc = new ObjectIdCollection(psr.Value.GetObjectIds());
                    if (keepObjects)
                        db.DeepCloneObjects(oidc, btr.ObjectId, new IdMapping(), false);
                    else
                        btr.AssumeOwnershipOf(oidc);

                    // Transform objects now in block from WCS to UCS
                    // and collect attributes
                    System.Collections.Generic.List<AttributeDefinition> attdefs =
                        new System.Collections.Generic.List<AttributeDefinition>();
                    foreach (ObjectId oid in btr)
                    {
                        Entity ent = (Entity)tr.GetObject(oid, OpenMode.ForWrite);
                        ent.TransformBy(wcs2ucs);
                        AttributeDefinition ad = ent as AttributeDefinition;
                        if (ad != null && !ad.Constant) attdefs.Add(ad);
                    }
                    // Create BlockReference
                    using (BlockReference br = new BlockReference(Point3d.Origin, bt[pr.StringResult]))
                    {
                        br.TransformBy(ucs2wcs * Matrix3d.Displacement(ppr.Value.GetAsVector()));
                        var occ = db.ObjectContextManager.GetContextCollection("ACDB_ANNOTATIONSCALES");
                        if (occ.HasContext(db.Cannoscale.Name))
                            Autodesk.AutoCAD.Internal.ObjectContexts.AddContext(
                                br, occ.GetContext(db.Cannoscale.Name));

                        // Add BlockReference to current space
                        BlockTableRecord cspace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
                        cspace.AppendEntity(br);
                        tr.AddNewlyCreatedDBObject(br, true);

                        // Add attributes to BlockReference, if any
                        foreach(AttributeDefinition ad in attdefs)
                            using (AttributeReference attref = new AttributeReference())
                            {
                                attref.SetAttributeFromBlock(ad, br.BlockTransform);
                                if (ad.IsMTextAttributeDefinition)
                                    attref.UpdateMTextAttribute();
                                attref.AdjustAlignment(db);
                                br.AttributeCollection.AppendAttribute(attref);
                                tr.AddNewlyCreatedDBObject(attref, true);
                            }
                    }
                }
                tr.Commit();
            }
        }



« Last Edit: December 08, 2011, 06:06:09 PM by kaefer »

BillZndl

  • Guest
Re: Placing block correctly on UCS
« Reply #13 on: December 12, 2011, 07:13:47 AM »
I've never tried to just type in a coordinate as each ucs has it's own origin when it is set up so there may be some translation of the destination point required to get the desired result.
Quite some transformation, and somewhat contrary to my assumption that this ground had been covered extensively before.

Thanks kaefer for clearing that up.

I guess all I was trying to say was that if the OP was inserting on a UCS other than the WCS,
there would be a transformation necessary if the OP wanted the block to show up in the same position.

Sorry if I caused any confusion.  :oops: