Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - (gile)

Pages: [1] 2 3 ... 6
1
Polylines / Re: Offset curve toward centroid
« on: March 20, 2013, 07:11:04 PM »
According to Tony Tanzillo's advices at TheSwamp, here's a safer way because of the using of DisposableSet class to insure the newly created polylines to be diposed in case an exception is thrown.

Code: [Select]
using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.AutoCAD.DatabaseServices;

// With the help of Tony Tanzillo's advices
// http://www.theswamp.org/index.php?topic=31862.msg494503#msg494503

namespace OffsetPolylineSample
{
    /// <summary>
    /// Provides the Offset() extension method for the Polyline type
    /// </summary>
    public static class PolylineExtension
    {
        /// <summary>
        /// Enumeration of offset side options
        /// </summary>
        public enum OffsetSide
        {
            In, Out, Left, Right, Both
        }

        /// <summary>
        /// Offset the source polyline to specified side(s).
        /// </summary>
        /// <param name="source">The polyline to be offseted.</param>
        /// <param name="offsetDist">The offset distance.</param>
        /// <param name="side">The offset side(s).</param>
        /// <returns>A polyline sequence resulting from the offset of the source polyline.</returns>
        public static IEnumerable<Polyline> Offset(this Polyline source, double offsetDist, OffsetSide side)
        {
            offsetDist = Math.Abs(offsetDist);
            using (var plines = new DisposableSet<Polyline>())
            {
                IEnumerable<Polyline> offsetRight = source.GetOffsetCurves(offsetDist).Cast<Polyline>();
                plines.UnionWith(offsetRight);
                IEnumerable<Polyline> offsetLeft = source.GetOffsetCurves(-offsetDist).Cast<Polyline>();
                plines.UnionWith(offsetLeft);
                double areaRight = offsetRight.Select(pline => pline.Area).Sum();
                double areaLeft = offsetLeft.Select(pline => pline.Area).Sum();
                switch (side)
                {
                    case OffsetSide.In:
                        return plines.RemoveRange(
                           areaRight < areaLeft ? offsetRight : offsetLeft);
                    case OffsetSide.Out:
                        return plines.RemoveRange(
                           areaRight < areaLeft ? offsetLeft : offsetRight);
                    case OffsetSide.Left:
                        return plines.RemoveRange(offsetLeft);
                    case OffsetSide.Right:
                        return plines.RemoveRange(offsetRight);
                    case OffsetSide.Both:
                        plines.Clear();
                        return offsetRight.Concat(offsetLeft);
                    default:
                        return null;
                }
            }
        }
    }

    /// <summary>
    /// Represents a set of disposable values.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class DisposableSet<T> : HashSet<T>, IDisposable
        where T : IDisposable
    {
        /// <summary>
        /// Disposes all items of the current DisposableSet object.
        /// </summary>
        public void Dispose()
        {
            if (base.Count > 0)
            {
                System.Exception last = null;
                foreach (T item in this)
                {
                    if (item != null)
                    {
                        try
                        {
                            item.Dispose();
                        }
                        catch (System.Exception ex)
                        {
                            last = last ?? ex;
                        }
                    }
                }
                this.Clear();
                if (last != null)
                    throw last;
            }
        }

        /// <summary>
        /// Removes all elements in the specified collection from the current DisposableSet object.
        /// </summary>
        /// <param name="items">The collection of items to remove from the current DisposableSet object.</param>
        /// <returns>The collection of items to remove.</returns>
        public IEnumerable<T> RemoveRange(IEnumerable<T> items)
        {
            base.ExceptWith(items);
            return items;
        }
    }
}

2
Layouts and printing / Plot to multi sheets PDF
« on: March 17, 2013, 11:22:01 AM »
Hi,

Here's a little class to be used to plot layouts to a single multi sheets PDF file.

Code: [Select]
using System.Collections.Generic;
using System.IO;
using System.Text;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.PlottingServices;
using Autodesk.AutoCAD.Publishing;
using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;

namespace Plottings
{
    public class MultiSheetsPdf
    {
        private string dwgFile, pdfFile, dsdFile, outputDir;
        private int sheetNum;
        IEnumerable<Layout> layouts;

        private const string LOG = "publish.log";

        public MultiSheetsPdfPlot(string pdfFile, IEnumerable<Layout> layouts)
        {
            Database db = HostApplicationServices.WorkingDatabase;
            this.dwgFile = db.Filename;
            this.pdfFile = pdfFile;
            this.outputDir = Path.GetDirectoryName(this.pdfFile);
            this.dsdFile = Path.ChangeExtension(this.pdfFile, "dsd");
            this.layouts = layouts;
        }

        public void Publish()
        {
            if (TryCreateDSD())
            {
                Publisher publisher = AcAp.Publisher;
                PlotProgressDialog plotDlg = new PlotProgressDialog(false, this.sheetNum, true);
                publisher.PublishDsd(this.dsdFile, plotDlg);
                plotDlg.Destroy();
                File.Delete(this.dsdFile);
            }
        }

        private bool TryCreateDSD()
        {
            using (DsdData dsd = new DsdData())
            using (DsdEntryCollection dsdEntries = CreateDsdEntryCollection(this.layouts))
            {
                if (dsdEntries == null || dsdEntries.Count <= 0) return false;

                if (!Directory.Exists(this.outputDir))
                    Directory.CreateDirectory(this.outputDir);

                this.sheetNum = dsdEntries.Count;

                dsd.SetDsdEntryCollection(dsdEntries);

                dsd.SetUnrecognizedData("PwdProtectPublishedDWF", "FALSE");
                dsd.SetUnrecognizedData("PromptForPwd", "FALSE");
                dsd.SheetType = SheetType.MultiDwf;
                dsd.NoOfCopies = 1;
                dsd.DestinationName = this.pdfFile;
                dsd.IsHomogeneous = false;
                dsd.LogFilePath = Path.Combine(this.outputDir, LOG);

                PostProcessDSD(dsd);

                return true;
            }
        }

        private DsdEntryCollection CreateDsdEntryCollection(IEnumerable<Layout> layouts)
        {
            DsdEntryCollection entries = new DsdEntryCollection();

            foreach (Layout layout in layouts)
            {
                DsdEntry dsdEntry = new DsdEntry();
                dsdEntry.DwgName = this.dwgFile;
                dsdEntry.Layout = layout.LayoutName;
                dsdEntry.Title = Path.GetFileNameWithoutExtension(this.dwgFile) + "-" + layout.LayoutName;
                dsdEntry.Nps = layout.TabOrder.ToString();
                entries.Add(dsdEntry);
            }
            return entries;
        }

        private void PostProcessDSD(DsdData dsd)
        {
            string str, newStr;
            string tmpFile = Path.Combine(this.outputDir, "temp.dsd");

            dsd.WriteDsd(tmpFile);

            using (StreamReader reader = new StreamReader(tmpFile, Encoding.Default))
            using (StreamWriter writer = new StreamWriter(this.dsdFile, false, Encoding.Default))
            {
                while (!reader.EndOfStream)
                {
                    str = reader.ReadLine();
                    if (str.Contains("Has3DDWF"))
                    {
                        newStr = "Has3DDWF=0";
                    }
                    else if (str.Contains("OriginalSheetPath"))
                    {
                        newStr = "OriginalSheetPath=" + this.dwgFile;
                    }
                    else if (str.Contains("Type"))
                    {
                        newStr = "Type=6";
                    }
                    else if (str.Contains("OUT"))
                    {
                        newStr = "OUT=" + this.outputDir;
                    }
                    else if (str.Contains("IncludeLayer"))
                    {
                        newStr = "IncludeLayer=TRUE";
                    }
                    else if (str.Contains("PromptForDwfName"))
                    {
                        newStr = "PromptForDwfName=FALSE";
                    }
                    else if (str.Contains("LogFilePath"))
                    {
                        newStr = "LogFilePath=" + Path.Combine(this.outputDir, LOG);
                    }
                    else
                    {
                        newStr = str;
                    }
                    writer.WriteLine(newStr);
                }
            }
            File.Delete(tmpFile);
        }
    }
}

Using example which creates a new instance of MultiSheetsPdf passing to the constructor the current drawing file name with "pdf" extension and all layouts except "Model":
Code: [Select]
        [CommandMethod("PlotPdf")]
        public void PlotPdf()
        {
            Database db = HostApplicationServices.WorkingDatabase;
            short bgp = (short)AcAp.GetSystemVariable("BACKGROUNDPLOT");
            try
            {
                AcAp.SetSystemVariable("BACKGROUNDPLOT", 0);
                using (Transaction tr = db.TransactionManager.StartTransaction())
                {
                    List<Layout> layouts = new List<Layout>();
                    DBDictionary layoutDict =
                        (DBDictionary)db.LayoutDictionaryId.GetObject(OpenMode.ForRead);
                    foreach (DBDictionaryEntry entry in layoutDict)
                    {
                        if (entry.Key != "Model")
                        {
                            layouts.Add((Layout)tr.GetObject(entry.Value, OpenMode.ForRead));
                        }
                    }
                    layouts.Sort((l1, l2) => l1.TabOrder.CompareTo(l2.TabOrder));

                    string filename = Path.ChangeExtension(db.Filename, "pdf");

                    MultiSheetsPdf plotter = new MultiSheetsPdf(filename, layouts);
                    plotter.Publish();

                    tr.Commit();
                }
            }
            catch (System.Exception e)
            {
                Editor ed = AcAp.DocumentManager.MdiActiveDocument.Editor;
                ed.WriteMessage("\nError: {0}\n{1}", e.Message, e.StackTrace);
            }
            finally
            {
                AcAp.SetSystemVariable("BACKGROUNDPLOT", bgp);
            }
        }

3
Polylines / Re: Offset curve toward centroid
« on: March 17, 2013, 09:28:39 AM »
Hi,

To get a Polyline centroid, an easier (and faster) way is to add to your project the GeometryExtensions.dll and simply call the Polyline.Centroid() extension method.

To insure offseting inside a (closed) polyline, you can offset it on both sides and compare the resulting polylines areas.
Here's a little sample which uses this way.

In the PolylineExtension static class, the Polyline.Offset() extension method is defined. This method can be called as an instance method and requires an argument to specify the offset side. This argument is a member of the OffsetSide enum defined in the PolylineExtension class too.

The PolylineExtension class:
Code: [Select]
using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.AutoCAD.DatabaseServices;

namespace OffsetPolylineSample
{
    /// <summary>
    /// Provides a Offset() extension methode for the Polyline type
    /// </summary>
    public static class PolylineExtension
    {
        /// <summary>
        /// Enumeration of offset side options
        /// </summary>
        public enum OffsetSide { In, Out, Left, Right, Both }

        /// <summary>
        /// Offset the source polyline to specified side(s).
        /// </summary>
        /// <param name="source">The polyline to be offseted.</param>
        /// <param name="offsetDist">The offset distance.</param>
        /// <param name="side">The offset side(s).</param>
        /// <returns>A polyline sequence resulting from the offset of the source polyline.</returns>
        public static IEnumerable<Polyline> Offset(this Polyline source, double offsetDist, OffsetSide side)
        {
            offsetDist = Math.Abs(offsetDist);
            IEnumerable<Polyline> offsetRight = source.GetOffsetCurves(offsetDist).Cast<Polyline>();
            double areaRight = offsetRight.Select(pline => pline.Area).Sum();
            IEnumerable<Polyline> offsetLeft = source.GetOffsetCurves(-offsetDist).Cast<Polyline>();
            double areaLeft = offsetLeft.Select(pline => pline.Area).Sum();
            switch (side)
            {
                case OffsetSide.In:
                    if (areaRight < areaLeft)
                    {
                        offsetLeft.Dispose();
                        return offsetRight;
                    }
                    else
                    {
                        offsetRight.Dispose();
                        return offsetLeft;
                    }
                case OffsetSide.Out:
                    if (areaRight < areaLeft)
                    {
                        offsetRight.Dispose();
                        return offsetLeft;
                    }
                    else
                    {
                        offsetLeft.Dispose();
                        return offsetRight;
                    }
                case OffsetSide.Left:
                    offsetRight.Dispose();
                    return offsetLeft;
                case OffsetSide.Right:
                    offsetLeft.Dispose();
                    return offsetRight;
                case OffsetSide.Both:
                    return offsetRight.Concat(offsetLeft);
                default:
                    return null;
            }
        }

        private static void Dispose(this IEnumerable<Polyline> plines)
        {
            foreach (Polyline pline in plines)
            {
                pline.Dispose();
            }
        }
    }
}

A testing command:
Code: [Select]
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;

namespace OffsetPolylineSample
{
    public class CommandMethods
    {
        [CommandMethod("Test", CommandFlags.Modal)]
        public void Test()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;

            PromptDistanceOptions pdo =
                new PromptDistanceOptions("\nSpecify the offset distance: ");
            pdo.AllowZero = false;
            PromptDoubleResult pdr = ed.GetDistance(pdo);
            if (pdr.Status != PromptStatus.OK) return;
            double offsetDist = pdr.Value;

            PromptKeywordOptions pko =
                new PromptKeywordOptions("\nEnter the offset side [In/Out/Left/Right/Both]", "In Out Left Right Both");
            PromptResult pr = ed.GetKeywords(pko);
            if (pr.Status != PromptStatus.OK) return;
            PolylineExtension.OffsetSide side;
            switch (pr.StringResult)
            {
                case "In": side = PolylineExtension.OffsetSide.In; break;
                case "Out": side = PolylineExtension.OffsetSide.Out; break;
                case "Left": side = PolylineExtension.OffsetSide.Left; break;
                case "Right": side = PolylineExtension.OffsetSide.Right; break;
                default: side = PolylineExtension.OffsetSide.Both; break;
            }

            PromptEntityOptions peo = new PromptEntityOptions("\nSelect a polyline: ");
            peo.SetRejectMessage("Only a polyline !");
            peo.AddAllowedClass(typeof(Polyline), true);

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
                while (true)
                {
                    PromptEntityResult per = ed.GetEntity(peo);
                    if (per.Status != PromptStatus.OK) break;

                    Polyline pline = (Polyline)tr.GetObject(per.ObjectId, OpenMode.ForRead);
                    foreach (Polyline pl in pline.Offset(offsetDist, side))
                    {
                        btr.AppendEntity(pl);
                        tr.AddNewlyCreatedDBObject(pl, true);
                    }
                    db.TransactionManager.QueueForGraphicsFlush();
                }
                tr.Commit();
            }
        }
    }
}

4
DesignScript / Moebius strip
« on: February 24, 2013, 10:04:42 PM »
Code: [Select]
import("ProtoGeometry.dll");
import("Math.dll");

numPts = 60;
halfWid = 20;
angle = 0..360..#numPts;
radius = 100;
width = 2;

def pts(halfWid : double)
{
    x = (radius + halfWid * (Math.Cos(angle / 2))) * (Math.Cos(angle));
y = (radius + halfWid * (Math.Cos(angle / 2))) * (Math.Sin(angle));
z = halfWid * Math.Sin(angle / 2);
return = Point.ByCoordinates(x, y, z);
}

lines = Line.ByStartPointEndPoint(pts(halfWid), pts(-halfWid));
lines.SetVisibility(false);
surfs = Surface.LoftFromCrossSections({ lines[0..numPts / 2], lines[numPts / 2..numPts] });
surfs.Visible = false;
solids = surfs.Thicken(width, true);
solid = solids[0].Union(solids[1]);

5
AutoCAD talk / Re: Autoload dll from a personal folder
« on: February 13, 2013, 10:29:59 PM »
Hi,

As far as I know, you cannot use the autoloader from a different folder of those provided by Autodesk:
- %appdata%\Autodesk\ApplicationPlugins
- %programfiles%\Autodesk\ApplicationPlugins
- %programdata%\Autodesk\ApplicationPlugins

If you have rights to write in the registry, you can register your application:
http://through-the-interface.typepad.com/through_the_interface/2006/09/automatic_loadi.html

6
Selection sets / Re: Selection Ordinate Dimensions with Filter (dxf code)
« on: February 02, 2013, 03:36:48 PM »
The upper method isn't very strict, even it will work fine with ordinate dimensions because it uses a bitwise test on values wich aren't binary codes: it will work with 6, 5 (3 points angular dimensions), 3 (diameter dimension) but not with 4, 2, 1 or 0.

Another way, better in my opinion, is to combinate a classical selection filter and a Editor.SelectionAdded event handler to filter the selected objects.

C#
Code: [Select]
        [CommandMethod("Test")]
        public void test()
        {
            Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;

            TypedValue[] filter = { new TypedValue(0, "dimension"), new TypedValue(3, "ISO-25") };

            ed.SelectionAdded += ed_SelectionAdded;
            PromptSelectionResult psr = ed.GetSelection(new SelectionFilter(filter));
            ed.SelectionAdded -= ed_SelectionAdded;
            if (psr.Status != PromptStatus.OK) return;
            ed.SetImpliedSelection(psr.Value);
        }

        void ed_SelectionAdded(object sender, SelectionAddedEventArgs e)
        {
            for (int i = 0; i < e.AddedObjects.Count; i++)
            {
                if (e.AddedObjects[i].ObjectId.ObjectClass.Name != "AcDbOrdinateDimension")
                    e.Remove(i);
            }
        }

VB
Code: [Select]
        <CommandMethod("Test")> _
        Public Sub test()
            Dim ed As Editor = Application.DocumentManager.MdiActiveDocument.Editor

            Dim filter As TypedValue() = { New TypedValue(0, "dimension"), new TypedValue(3, "ISO-25") }

            AddHandler ed.SelectionAdded, AddressOf ed_SelectionAdded
            Dim psr As PromptSelectionResult = ed.GetSelection(New SelectionFilter(filter))
            RemoveHandler ed.SelectionAdded, AddressOf ed_SelectionAdded
            If psr.Status <> PromptStatus.OK Then
                Return
            End If
            ed.SetImpliedSelection(psr.Value)
        End Sub

        Private Sub ed_SelectionAdded(sender As Object, e As SelectionAddedEventArgs)
            For i As Integer = 0 To e.AddedObjects.Count - 1
                If e.AddedObjects(i).ObjectId.ObjectClass.Name <> "AcDbOrdinateDimension" Then
                    e.Remove(i)
                End If
            Next
        End Sub

7
Selection sets / Re: Selection Ordinate Dimensions with Filter (dxf code)
« on: February 02, 2013, 02:27:08 PM »
Hi,

Look at the 70 dxf group.

Code: [Select]
TypedValue[] tvs = {
    new TypedValue(0, "dimension"),
    new TypedValue(3, "ISO-25"),
    new TypedValue(-4, "&="),
    new TypedValue(70, 6)};

SelectionFilter filter = new SelectioFilter(tvs);

PromptSelectionResult psr = ed.GetSelection(filter);

8
Blocks / Drag (attributed) block reference
« on: January 26, 2013, 10:44:15 PM »
Dragging and rotating an (attributed) block.

BlockJig is to be used to drag (and possibly rotate) a simple block.
While dragging the block, it's possible to dynamically rotate the block by pressing Ctrl.
A click terminate the process keeping the current position and rotation for the block

BlockAttribJig inherits from BlockJig and handles the block attribute references.
In the constructor, each attribute definition informations (position, alignement and rotation) is stored in a structure.
The overrided Update() method sets the attributes geometry according to the attribute definition informations and the block reference position and rotation.

Code: (csharp) [Select]
        class BlockJig : EntityJig
        {
            protected BlockReference _br;
            protected Point3d _pos;
            protected double _rot, _ucsRot;

            public BlockJig(BlockReference br)
                : base(br)
            {
                _br = br;
                _pos = _br.Position;
                Editor ed = AcAp.DocumentManager.MdiActiveDocument.Editor;
                CoordinateSystem3d ucs = ed.CurrentUserCoordinateSystem.CoordinateSystem3d;
                Matrix3d ocsMat = Matrix3d.WorldToPlane(new Plane(Point3d.Origin, ucs.Zaxis));
                _ucsRot = Vector3d.XAxis.GetAngleTo(ucs.Xaxis.TransformBy(ocsMat), ucs.Zaxis);
                _rot = _br.Rotation - _ucsRot;
            }

            protected override SamplerStatus Sampler(JigPrompts prompts)
            {
                System.Windows.Forms.Keys mods = System.Windows.Forms.Control.ModifierKeys;
                if ((mods & System.Windows.Forms.Keys.Control) > 0)
                {
                    JigPromptAngleOptions jpao =
                        new JigPromptAngleOptions("\nSpecify the rotation: ");
                    jpao.UseBasePoint = true;
                    jpao.BasePoint = _br.Position;
                    jpao.Cursor = CursorType.RubberBand;
                    jpao.UserInputControls = (
                        UserInputControls.Accept3dCoordinates |
                        UserInputControls.UseBasePointElevation);
                    PromptDoubleResult pdr = prompts.AcquireAngle(jpao);

                    if (_rot == pdr.Value)
                    {
                        return SamplerStatus.NoChange;
                    }
                    else
                    {
                        _rot = pdr.Value;
                        return SamplerStatus.OK;
                    }
                }
                else
                {
                    JigPromptPointOptions jppo =
                        new JigPromptPointOptions("\nSpecify insertion point (or press Ctrl for rotation): ");
                    jppo.UserInputControls =
                      (UserInputControls.Accept3dCoordinates | UserInputControls.NullResponseAccepted);
                    PromptPointResult ppr = prompts.AcquirePoint(jppo);
                    if (_pos.DistanceTo(ppr.Value) < Tolerance.Global.EqualPoint)
                    {
                        return SamplerStatus.NoChange;
                    }
                    else
                    {
                        _pos = ppr.Value;
                    }
                    return SamplerStatus.OK;
                }
            }

            protected override bool Update()
            {
                _br.Position = _pos;
                _br.Rotation = _rot +_ucsRot;
                return true;
            }
        }

        class BlockAttribJig : BlockJig
        {
            struct TextInfo
            {
                public Point3d Position;
                public Point3d Alignment;
                public double Rotation;
                public bool IsAligned;
            }

            private Dictionary<string, TextInfo> _attInfos;

            public BlockAttribJig(BlockReference br)
                : base(br)
            {
                _attInfos = new Dictionary<string, TextInfo>();
                BlockTableRecord btr = (BlockTableRecord)br.BlockTableRecord.GetObject(OpenMode.ForRead);
                foreach (ObjectId id in btr)
                {
                    if (id.ObjectClass.Name == "AcDbAttributeDefinition")
                    {
                        AttributeDefinition attDef = (AttributeDefinition)id.GetObject(OpenMode.ForRead);
                        TextInfo ti = new TextInfo()
                        {
                            Position = attDef.Position,
                            Alignment = attDef.AlignmentPoint,
                            IsAligned = attDef.Justify != AttachmentPoint.BaseLeft,
                            Rotation = attDef.Rotation
                        };
                        _attInfos.Add(attDef.Tag.ToUpper(), ti);
                    }
                }
            }

            protected override bool Update()
            {
                base.Update();
                foreach (ObjectId id in _br.AttributeCollection)
                {
                    AttributeReference att = (AttributeReference)id.GetObject(OpenMode.ForWrite);
                    att.Rotation = _br.Rotation;
                    string tag = att.Tag.ToUpper();
                    if (_attInfos.ContainsKey(tag))
                    {
                        TextInfo ti = _attInfos[tag];
                        att.Position = ti.Position.TransformBy(_br.BlockTransform);
                        if (ti.IsAligned)
                        {
                            att.AlignmentPoint =
                                ti.Alignment.TransformBy(_br.BlockTransform);
                            att.AdjustAlignment(_br.Database);
                        }
                        if (att.IsMTextAttribute)
                        {
                            att.UpdateMTextAttribute();
                        }
                        att.Rotation = ti.Rotation + _br.Rotation;
                    }
                }
                return true;
            }
        }

A little using example to insert a block

Code: (csharp) [Select]
        [CommandMethod("TEST")]
        public void Test()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;

            PromptResult pr = ed.GetString("\nBlock name: ");
            if (pr.Status != PromptStatus.OK) return;
            string blockName = pr.StringResult;

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                if (!bt.Has(blockName))
                {
                    ed.WriteMessage("\nNone block '{0}' in the document block table.", blockName);
                    return;
                }
                BlockTableRecord curSpace =
                    (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);

                // Add the block reference to Database first
                BlockReference br = new BlockReference(Point3d.Origin, bt[blockName]);
                br.TransformBy(ed.CurrentUserCoordinateSystem);
                curSpace.AppendEntity(br);
                tr.AddNewlyCreatedDBObject(br, true);

                // Get the block definition
                BlockTableRecord btr =
                    (BlockTableRecord)tr.GetObject(bt[blockName], OpenMode.ForRead);
                BlockJig jig;
                if (btr.HasAttributeDefinitions)
                {
                    // Add attribute references to the block reference
                    foreach (ObjectId id in btr)
                    {
                        if (id.ObjectClass.Name == "AcDbAttributeDefinition")
                        {
                            AttributeDefinition attDef =
                                (AttributeDefinition)tr.GetObject(id, OpenMode.ForRead);
                            AttributeReference attRef = new AttributeReference();
                            attRef.SetAttributeFromBlock(attDef, br.BlockTransform);
                            ObjectId attId = br.AttributeCollection.AppendAttribute(attRef);
                            tr.AddNewlyCreatedDBObject(attRef, true);
                        }
                    }
                    // Create a BlockAttribJig instance
                    jig = new BlockAttribJig(br);
                }
                else
                {
                    // Create a BlockJig instance
                    jig = new BlockJig(br);
                }
                // Drag the block reference
                pr = ed.Drag(jig);
                if (pr.Status != PromptStatus.OK) br.Erase();
                tr.Commit();
            }
        }

9
Math and Geometry / Re: Geometry extensions
« on: January 21, 2013, 08:13:21 PM »
I updated the attachment and documentation in the first message.

10
Polylines / Fillet
« on: December 24, 2012, 05:05:41 PM »
Hi,

Here're two extension methods for the Polyline class to add an arc (fillet) at the specified vertex or at each vertex.

The main difference between C#, VB and F# is the way to loop for each vertex while adding some new ones: with C# a 'for' loop can be used because the condition is checked at each loop, this must be done with a 'while' loop in VB and the 'natural' way in F# is a tail recursive function.

C#
Code: [Select]
    public static class Extensions
    {
        // Adds an arc (fillet) at each vertex, if able.
        public static void FilletAll(this Polyline pline, double radius)
        {
            int i = pline.Closed ? 0 : 1;
            for (int j = 0; j < pline.NumberOfVertices - i; j += 1 + pline.FilletAt(j, radius))
            { }
        }

       // Adds an arc (fillet) at the specified vertex. Returns 1 if the operation succeeded, 0 if it failed.
        public static int FilletAt(this Polyline pline, int index, double radius)
        {
            int prev = index == 0 && pline.Closed ? pline.NumberOfVertices - 1 : index - 1;
            if (pline.GetSegmentType(prev) != SegmentType.Line ||
                pline.GetSegmentType(index) != SegmentType.Line)
                return 0;
            LineSegment2d seg1 = pline.GetLineSegment2dAt(prev);
            LineSegment2d seg2 = pline.GetLineSegment2dAt(index);
            Vector2d vec1 = seg1.StartPoint - seg1.EndPoint;
            Vector2d vec2 = seg2.EndPoint - seg2.StartPoint;
            double angle = (Math.PI - vec1.GetAngleTo(vec2)) / 2.0;
            double dist = radius * Math.Tan(angle);
            if (dist > seg1.Length || dist > seg2.Length)
                return 0;
            Point2d pt1 = seg1.EndPoint + vec1.GetNormal() * dist;
            Point2d pt2 = seg2.StartPoint + vec2.GetNormal() * dist;
            double bulge = Math.Tan(angle / 2.0);
            if (Clockwise(seg1.StartPoint, seg1.EndPoint, seg2.EndPoint))
                bulge = -bulge;
            pline.AddVertexAt(index, pt1, bulge, 0.0, 0.0);
            pline.SetPointAt(index + 1, pt2);
            return 1;
        }

        // Evaluates if the points are clockwise.
        private static bool Clockwise(Point2d p1, Point2d p2, Point2d p3)
        {
            return ((p2.X - p1.X) * (p3.Y - p1.Y) - (p2.Y - p1.Y) * (p3.X - p1.X)) < 1e-8;
        }
    }

VB
Code: [Select]
    Module Extensions

        ' Adds an arc (fillet) at each polyline vertex, if able.
        <System.Runtime.CompilerServices.Extension> _
        Public Sub FilletAll(pline As Polyline, radius As Double)
            Dim i As Integer = If(pline.Closed, 0, 1)
            While i < If(pline.Closed, pline.NumberOfVertices, pline.NumberOfVertices - 1)
                i += 1 + pline.FilletAt(i, radius)
            End While
        End Sub

        ' Adds an arc (fillet) at the specified vertex. Retuns 1 if the operation succeeded, 0 if it failed.
        <System.Runtime.CompilerServices.Extension> _
        Public Function FilletAt(pline As Polyline, index As Integer, radius As Double) As Integer
            Dim prev As Integer = If(index = 0 AndAlso pline.Closed, pline.NumberOfVertices - 1, index - 1)
            If pline.GetSegmentType(prev) <> SegmentType.Line OrElse _
                pline.GetSegmentType(index) <> SegmentType.Line Then
                Return 0
            End If
            Dim seg1 As LineSegment2d = pline.GetLineSegment2dAt(prev)
            Dim seg2 As LineSegment2d = pline.GetLineSegment2dAt(index)
            Dim vec1 As Vector2d = seg1.StartPoint - seg1.EndPoint
            Dim vec2 As Vector2d = seg2.EndPoint - seg2.StartPoint
            Dim angle As Double = (Math.PI - vec1.GetAngleTo(vec2)) / 2.0
            Dim dist As Double = radius * Math.Tan(angle)
            If dist > seg1.Length OrElse dist > seg2.Length Then
                Return 0
            End If
            Dim pt1 As Point2d = seg1.EndPoint + vec1.GetNormal() * dist
            Dim pt2 As Point2d = seg2.StartPoint + vec2.GetNormal() * dist
            Dim bulge As Double = Math.Tan(angle / 2.0)
            If Clockwise(seg1.StartPoint, seg1.EndPoint, seg2.EndPoint) Then
                bulge = -bulge
            End If
            pline.AddVertexAt(index, pt1, bulge, 0.0, 0.0)
            pline.SetPointAt(index + 1, pt2)
            Return 1
        End Function

        ' Evaluates if the points are clockwise.
        Private Function Clockwise(p1 As Point2d, p2 As Point2d, p3 As Point2d) As Boolean
            Return ((p2.X - p1.X) * (p3.Y - p1.Y) - (p2.Y - p1.Y) * (p3.X - p1.X)) < 0.00000001
        End Function

    End Module

F#
Code: [Select]
// Evaluates if the points are clockwise.
let clockwise (p1: Point2d) (p2: Point2d) (p3: Point2d) =
    ((p2.X - p1.X) * (p3.Y - p1.Y) - (p2.Y - p1.Y) * (p3.X - p1.X)) < 1e-8

type Polyline with
    // Adds an arc (fillet) at the specified vertex. Retuns 1 if the operation succeeded, 0 if it failed.
    member pl.FilletAt index radius =
        let prev = (if index = 0 && pl.Closed then pl.NumberOfVertices else index) - 1
        if pl.GetSegmentType(prev) <> SegmentType.Line ||
            pl.GetSegmentType(index) <> SegmentType.Line then
            0
        else
            let seg1 = pl.GetLineSegment2dAt(prev)
            let seg2 = pl.GetLineSegment2dAt(index)
            let vec1 = seg1.StartPoint - seg1.EndPoint
            let vec2 = seg2.EndPoint - seg2.StartPoint
            let angle = (Math.PI - vec1.GetAngleTo(vec2)) / 2.
            let dist = radius * tan(angle)
            if dist > seg1.Length || dist > seg2.Length then
                0
            else
                let pt1 = seg1.EndPoint + vec1.GetNormal() * dist
                let pt2 = seg2.StartPoint + vec2.GetNormal() * dist
                let mutable bulge = tan(angle / 2.)
                if clockwise pt1 seg1.EndPoint pt2 then bulge <- -bulge
                pl.AddVertexAt(index, pt1, bulge, 0., 0.)
                pl.SetPointAt(index + 1, pt2)
                1

    // Adds an arc (fillet) at each vertex, if able.
    member pl.FilletAll radius =
        let rec loop i =
            if i < pl.NumberOfVertices then
                loop (i + 1 + pl.FilletAt i radius)
        loop (if pl.Closed then 0 else 1)

11
Events / Re: EventHandler for Entity Selection
« on: November 25, 2012, 03:04:18 PM »
Hi,

I do not understand what you're trying to do, but you'll find an example of using the SelectionAdded event here.

12
Events / Re: EventHandler for Entity Selection
« on: November 24, 2012, 05:34:30 PM »
Hi,

The Editor.SelectionAdded event is fired every time entities are added to a selection set.

13
External databases / Re: Saving Custom Data
« on: November 04, 2012, 03:27:28 PM »
Hi,

Don't use new as variable name, it's a C# operator/keyword.

You'd rather test if the extension dictionary and the xrecord already exist.

While AddCustomData method is defined within the same class as the command, you don't need to declare it 'static' and it's a better practice to declare it 'private' (or 'internal' if it has to be used outside of the class within the same project).
If you want this method to be reusable, you can define an extension method for the DBObject type which ca be called on any DBObject instance  as an instance method.

Here's a first example with a private AddCustomData() method

Code: [Select]
    public class Commands
    {
        [CommandMethod("Test1")]
        public void Test1()
        {
            Document doc = AcAp.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            PromptEntityResult per = ed.GetEntity("\nSelect an entity: ");
            if (per.Status != PromptStatus.OK)
                return;
            AddCustomData(
                per.ObjectId,
                "CustomData",
                new TypedValue((int)DxfCode.Thickness, 5.0),
                new TypedValue((int)DxfCode.Real, 35.0));
        }

        private void AddCustomData(ObjectId id, string key, params TypedValue[] values)
        {
            Database db = id.Database;
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                DBObject obj = tr.GetObject(id, OpenMode.ForRead);
                ObjectId dictId = obj.ExtensionDictionary;
                if (dictId == ObjectId.Null)
                {
                    obj.UpgradeOpen();
                    obj.CreateExtensionDictionary();
                    dictId = obj.ExtensionDictionary;
                }
                DBDictionary xdict = (DBDictionary)tr.GetObject(dictId, OpenMode.ForRead);
                Xrecord xrec;
                if (xdict.Contains(key))
                {
                    xrec = (Xrecord)tr.GetObject((ObjectId)xdict[key], OpenMode.ForWrite);
                }
                else
                {
                    xdict.UpgradeOpen();
                    xrec = new Xrecord();
                    xdict.SetAt(key, xrec);
                    tr.AddNewlyCreatedDBObject(xrec, true);
                }
                xrec.Data = new ResultBuffer(values);
                tr.Commit();
            }
        }
    }

A second example using an extension method (this method have to be 'public static' and defined in a 'static' class):
Code: [Select]
    public class Commands
    {
        [CommandMethod("Test2")]
        public void Test2()
        {
            Document doc = AcAp.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            PromptEntityResult per = ed.GetEntity("\nSelect an entity: ");
            if (per.Status != PromptStatus.OK)
                return;
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                DBObject obj = tr.GetObject(per.ObjectId, OpenMode.ForRead);
                obj.SetXrecord(
                    "CustomData",
                    new TypedValue((int)DxfCode.Thickness, 5.0),
                    new TypedValue((int)DxfCode.Real, 35.0));
                tr.Commit();
            }
        }
    }

    public static class Extensions
    {
        public static void SetXrecord(this DBObject obj, string key, params TypedValue[] values)
        {
            Transaction tr = obj.Database.TransactionManager.TopTransaction;
            if (tr == null)
                throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.NotTopTransaction);
            ObjectId dictId = obj.ExtensionDictionary;
            if (dictId == ObjectId.Null)
            {
                obj.UpgradeOpen();
                obj.CreateExtensionDictionary();
                dictId = obj.ExtensionDictionary;
            }
            DBDictionary xdict = (DBDictionary)tr.GetObject(dictId, OpenMode.ForRead);
            Xrecord xrec;
            if (xdict.Contains(key))
            {
                xrec = (Xrecord)tr.GetObject((ObjectId)xdict[key], OpenMode.ForWrite);
            }
            else
            {
                xdict.UpgradeOpen();
                xrec = new Xrecord();
                xdict.SetAt(key, xrec);
                tr.AddNewlyCreatedDBObject(xrec, true);
            }
            xrec.Data = new ResultBuffer(values);
        }
    }

14
DesignScript / Re: Some more curves
« on: November 01, 2012, 07:42:45 PM »
One more: a cylindrical helix.

The constructor arguments are:
  • the radius
  • the total height
  • the total angle, if the input is negative the helix turns clockwise

Code: [Select]
import("ProtoGeometry.dll");
import("Math.dll");

class Helix
{
    Curve : Curve;
    constructor FromRadiusHeightAngle(radius : double, height : double, totalAngle : double)
    {
        pi = 3.141592653589793;
        numPts = Math.Abs(Math.Round(totalAngle / 15)) + 1;
        step = height / (numPts - 1);
        ccw = Math.Sign(totalAngle);
        tanZ = (180 * height) / (pi * totalAngle * radius);
        startTan = Vector.ByCoordinates(0, ccw, ccw * tanZ);
        endTan = Vector.ByCoordinates(ccw * -Math.Sin(totalAngle), ccw * Math.Cos(totalAngle), ccw * tanZ);
        x = radius * Math.Cos(0 .. totalAngle .. #numPts);
        y = radius * Math.Sin(0 .. totalAngle .. #numPts);
        z = step * (0 .. numPts);
        Curve = BSplineCurve.ByPoints(Point.ByCoordinates(x, y, z), startTan, endTan);
    }
}

15
DesignScript / Some more curves
« on: October 24, 2012, 02:50:13 PM »
Hi,

DesignScript is relatively poor in native curves (Line, Arc, Circle and BSplineCurve).
I thought it might be interesting to define some others.
I tried to do this as classes with constructors, these classes may be required to evolve with the addition of new features.
The generated curves are spline whose control points or smoothing are calculated from the equations of these curves.

The Catenary, Ellipse, EllpticalArc and Parabola classes:
Code: [Select]
import("ProtoGeometry.dll");
import("Math.dll");
   
class Catenary
{
    Curve : Curve;
    Chord : double;
    Param : double;
    Sagitta : double;
    Length : double;
   
    def argCosh(x : double)
    {
        return = Math.Log(x + Math.Sqrt(x * x - 1));
    }

    constructor FromChordParam(chord : double, param : double, numPts : int)
    {
        Chord = chord;
        Param = param;
        Sagitta = param * Math.Cosh(chord / (2 * param)) - param;
        Length = 2 * param * Math.Sinh(chord / (2 * param));
        x = chord / -2..chord / 2..#numPts;
        y = param * Math.Cosh(x / param);
        Curve = BSplineCurve.ByPoints(Point.ByCoordinates(x, y, 0));
    }
   
    constructor FromLenthSagitta(length : double, sagitta : double, numPts : int)
    {
        Length = length;
        Sagitta = sagitta;
        Param = (length * length - 4 * sagitta * sagitta) / (8 * sagitta);
        Chord = 2 * Param * argCosh((sagitta + Param) / Param);
        x = Chord / -2..Chord / 2..#numPts;
        y = Param * Math.Cosh(x / Param);
        Curve = BSplineCurve.ByPoints(Point.ByCoordinates(x, y, 0));
    }
}

class Ellipse
{
    Curve : Curve;
   
    constructor FromRadii(majRadius : double, minRadius : double)
    {
        Curve = Circle.ByCenterPointRadius(Point.ByCoordinates(0, 0, 0), 1)
        .Transform(CoordinateSystem.WCS,
            CoordinateSystem.Identity().Scale(majRadius, minRadius, 1));
    }
}

class EllipticalArc
{
    Curve : Curve;
   
    constructor FromRadii(majRadius : double, minRadius : double, startAngle : double, sweepAngle : double)
    {
        endAngle = startAngle + sweepAngle;
        startParam = Math.Atan(majRadius * Math.Tan(startAngle) / minRadius);
        endParam = Math.Atan(majRadius * Math.Tan(endAngle) / minRadius);
        [Imperative]
        {
            if (startAngle == 0 || startAngle == 90 || startAngle == 180 || startAngle == 270 || startAngle == 360)
                startParam = startAngle;
            else if (startAngle > 90 && startAngle < 270)
                startParam = startParam + 180;
            if (endAngle == 0 || endAngle == 90 || endAngle == 180 || endAngle == 270 || endAngle == 360)
                endParam = endAngle;
        else if (endAngle > 90 && endAngle < 270)
                endParam = endParam + 180;
            if (endParam <= startParam)
                endParam = endParam + 360;
        }
        Curve = Arc.ByCenterPointRadiusAngle(Point.ByCoordinates(0, 0, 0),
            1, startParam, endParam - startParam, Vector.ByCoordinates(0, 0, 1))
            .Transform(CoordinateSystem.WCS,
            CoordinateSystem.Identity().Scale(majRadius, minRadius, 1));
    }
}

class Parabola
{
    Curve : Curve;
    Focus : Point;
   
    constructor FromChordSagitta(chord : double, sagitta : double, numPts : int, focusVisible : bool)
    {
        a = 4 * sagitta / (chord * chord);
        Focus = Point.ByCoordinates(0, 1 / (4 * a), 0).SetVisibility(focusVisible);
        x = chord / - 2..chord / 2..#numPts;
        y = a * x * x;
        Curve = BSplineCurve.ByPoints(Point.ByCoordinates(x, y, 0));
    }
}


Using examples

Ellipses with incremented major axis.

Code: [Select]
el = Ellipse.FromRadii(30..80..5, 30);



Parabolas with decremented chords and incrmented sagittas

Code: [Select]
parabs = Parabola.FromChordSagitta(200..100..-10, 20..120..10, 19, false);


 
Catenaries with incremented params.
Each curve is moved so that start and end point lies ont the X axis. This is done using the Param and Sagitta properties of the Catenary instance.

Code: [Select]
def catenar(chord : double, param : double, numPts : int)
{
    cat = Catenary.FromChordParam(chord, param, numPts);
    return = cat.Curve.Translate(0, -cat.Param - cat.Sagitta, 0);
}

cats = catenar(160, 35..75..#10, 19);



 

Pages: [1] 2 3 ... 6