October 2014

Sun Mon Tue Wed Thu Fri Sat
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  










« Update to the AutoCAD .NET entity jig framework | Main | Purging unwanted DGN linestyle data from an AutoCAD drawing using .NET »

December 10, 2012

Creating a square AutoCAD box via the jig entity framework and .NET

After creating a frustum-shaped jig “manually”, refactoring the code while introducing the idea of an “Entity Jig Framework and then updating the framework and providing a number of usage examples, today’s post looks at a slightly more complex use-case: defining a jig to create a square (in X & Y) box by selecting opposing corners.

It may sound simple, but it was actually harder that it sounds – especially when supporting the use of an arbitrary User Coordinate System. It also makes use of a “phase” type that we previously hadn’t needed, as we require point input for our second corner.

Here’s the C# code implementing our BOJ command, which reuses some code previously borrowed from Philippe Leefsma (and we’ll see some other handy code snippets from Philippe, later in the week):

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.Geometry;

using System.Collections.Generic;

using System;

using JigFrameworks;

 

namespace EntityJigs

{

  public class Commands

  {

    [CommandMethod("BOJ")]

    public void BoxJig()

    {

      var doc =

        Application.DocumentManager.MdiActiveDocument;

      var db = doc.Database;

      var ed = doc.Editor;

 

      // Let's get the initial corner of the box

 

      var ppr = ed.GetPoint("\nSpecify first corner: ");

 

      if (ppr.Status == PromptStatus.OK)

      {

        // In order for the visual style to be respected,

        // we'll add the to-be-jigged solid to the database

 

        var tr = doc.TransactionManager.StartTransaction();

        using (tr)

        {

          var btr =

            (BlockTableRecord)tr.GetObject(

              db.CurrentSpaceId, OpenMode.ForWrite

            );

 

          var sol = new Solid3d();

          btr.AppendEntity(sol);

          tr.AddNewlyCreatedDBObject(sol, true);

 

          // Create our jig object passing in the selected point

 

          var jf =

            new EntityJigFramework(

              ed.CurrentUserCoordinateSystem, sol, ppr.Value,

              new List<Phase>()

              {

                // Two phases, the second of which has a custom

                // offset for the base point

 

                new PointPhase("\nSpecify opposite corner: "),

                new SolidDistancePhase(

                  "\nSpecify height: ",

                  1e-05,

                  (vals, pt) =>

                  {

                    // Get the diagonal line between the corners

 

                    var pt2 = (Point3d)vals[0].Value;

                    var diag = pt2 - pt;

                    var dlen = diag.Length;

 

                    // Use Pythagoras' theorem to get the side

                    // length

 

                    var side = Math.Sqrt(dlen * dlen / 2);

                    var halfSide = side / 2;

 

                    // Start by getting the displacement from

                    // the start point, adjusting for the fact

                    // we're jigging from a corner, not the center

 

                    var mat =

                      Matrix3d.Displacement(

                        pt.GetAsVector() +

                        new Vector3d(

                          halfSide,

                          halfSide,

                          (double)vals[1].Value / 2

                        )

                      );

 

                    // Calculate the angle between the diagonal

                    // and the X axis

 

                    var ang =

                      ComputeAngle(

                        pt,

                        pt2,

                        Vector3d.XAxis,

                        Matrix3d.Identity

                      );

 

                    // Add a rotation component to the

                    // transformation, adjusted by 45 degrees

                    // to jig the box diagonally

 

                    mat =

                      mat.PostMultiplyBy(

                        Matrix3d.Rotation(

                          ang + (-45 * Math.PI / 180),

                          Vector3d.ZAxis,

                          new Point3d(-halfSide, -halfSide, 0)

                        )

                      );

 

                    return

                      new Vector3d(halfSide, halfSide, 0).

                        TransformBy(mat);

                  }

                )

              },

              (e, vals, pt, ucs) =>

              {

                // Our entity update function

 

                // Get the diagonal line between the corners

 

                var pt2 = (Point3d)vals[0].Value;

                var diag = pt2 - pt;

                var dlen = diag.Length;

 

                // Use Pythagoras' theorem to get the side

                // length

 

                var side = Math.Sqrt(dlen * dlen / 2);

                var halfSide = side / 2;

 

                // Create our box with square sides and

                // the chosen height

 

                var s = (Solid3d)e;

                s.CreateBox(side, side, (double)vals[1].Value);

 

                // Start by getting the displacement from

                // the start point, adjusting for the fact

                // we're jigging from a corner, not the center

                // (need to adjust for the current UCS)

 

                var mat =

                  Matrix3d.Displacement(

                    pt.GetAsVector() +

                    new Vector3d(

                      halfSide,

                      halfSide,

                      (double)vals[1].Value / 2

                    )

                  ).PreMultiplyBy(ucs);

 

                // Calculate the angle between the diagonal

                // and the X axis

 

                var ang =

                  ComputeAngle(

                    pt,

                    pt2,

                    Vector3d.XAxis,

                    Matrix3d.Identity

                  );

 

                // Add a rotation component to the

                // transformation, adjusted by 45 degrees

                // to jig the box diagonally

 

                mat =

                  mat.PostMultiplyBy(

                    Matrix3d.Rotation(

                      ang + (-45 * Math.PI / 180),

                      Vector3d.ZAxis,

                      new Point3d(-halfSide, -halfSide, 0)

                    )

                  );

 

                // Transform our solid

 

                s.TransformBy(mat);

 

                return true;

              }

            );

          jf.RunTillComplete(ed, tr);

        }

      }

    }

 

    // Custom ArcTangent method, as the Math.Atan

    // doesn't handle specific cases

 

    public static double Atan(double y, double x)

    {

      if (x > 0)

        return Math.Atan(y / x);

      else if (x < 0)

        return Math.Atan(y / x) - Math.PI;

      else  // x == 0

      {

        if (y > 0)

          return Math.PI;

        else if (y < 0)

          return -Math.PI;

        else // if (y == 0) theta is undefined

          return 0.0;

      }

    }

 

    // Computes Angle between a provided vector and that

    // defined by the vector between two points

 

    public static double ComputeAngle(

      Point3d startPoint, Point3d endPoint,

      Vector3d xdir, Matrix3d ucs

    )

    {

      var v =

        new Vector3d(

          (endPoint.X - startPoint.X) / 2,

          (endPoint.Y - startPoint.Y) / 2,

          (endPoint.Z - startPoint.Z) / 2

        );

 

      var cos = v.DotProduct(xdir);

      var sin =

        v.DotProduct(

          Vector3d.ZAxis.TransformBy(ucs).CrossProduct(xdir)

        );

 

      return Atan(sin, cos);

    }

  }

}

Here’s the command in action:

Jigging boxes

Unless anyone has further suggestions for the EntityJigFramework – or requests for additional usage examples – then I think I’ll move on from this topic, for now.

Later this week we’ll see some code that purges unused, complex DGN linetypes from a DWG and I’ll hopefully also show some fun I’ve been having with my ZX Spectrum.

blog comments powered by Disqus

Feed/Share

10 Random Posts