Архив за месяц: Май 2014

1с 8: Установка сервера 1с 8.3. Ошибка загрузки компоненты testbase

Если инсталлировать сервер 1с 8.3 без платформы, возникает ошибка загрузки компоненты testbase
После инсталляции скопировать в папку с сервером 1С файл testbase.dll и перезагрузиться. Перезапуск служб сервера почему-то не помогает...

Нарезка областей (regions) на прямоугольные куски заданного размера

Задача стояла следующая: необходимо для указанных областей (regions) создать их копию, нарезанную либо на прямоугольники заданного размера, либо на заданное количество строк и столбцов. При этом исходные области (regions), подлежащие разрезке могут быть абсолютно любой, произвольной формы: например, созданными на основании сплайнов и содержащими в себе отверстия.
AutoCAD вообще очень медленно работает с объектами Region, примерно так же медленно, как и с телами (объектами Solid3d). Причём AutoCAD 2014 и 2015 работают с областями и телами даже в два раза медленней, чем более старые версии. Это обусловлено тем, что в обозначенных версиях AutoCAD компанией Autodesk был переписан код по работе с Region и Solid3d. но при этом не было произведено надлежащего тестирования обновлённого функционала.

В данной заметке приведён исходный код разрезки областей различными способами, а так же предоставлен дополнительный программный функционал, который может быть полезен при работе с областями в ходе решения иных задач.

В процессе тестирования производилась разрезка различных областей, в т.ч. и созданных при помощи сплайнов (на скрине выделены коричневым цветом):



Код определяет ряд пользовательских команд:

1. Команда AcadBoundaryRegion создаёт жёлтую рамку вокруг регионов. Эта рамка показывает визуальные границы BoundaryBox для этих примитивов/ Габариты берутся именно те, которые AutoCAD API показывает программистам в свойствах примитивов:


Команду AcadBoundaryRegion я использую при написании программ, чтобы визуально увидеть границы, которые AutoCAD возвращает для запрошенного примитива, и тем самым понять, почему в той или иной ситуации мой код делает не совсем то, что я от него хотел. Обратите внимание на то, что области, выполненные из сплайнов, не чётко вписываются в эти границы. Однако в процессе программирования нам порой нужно точно знать габариты нашего примитива. Для этого была написана следующая команда.

2. Команда BoundaryRegion показывает корректные визуальные границы наших областей.


Как видим, созданные с помощью BoundaryRegion границы имеют зелёный цвет, для их визуального отличия от границ, созданных при помощи команды AcadBoundaryRegion.
Эту команду я использую для того, чтобы визуально убедиться в том, что вычисленные мною габариты верны.

3. Команда MeshRegionThroughCellSize нарезает копии наших исходных областей на ячейки, заданного размера:



Можно задавать произвольное значение высоты и ширины ячеек.

4. Команда MeshRegionThroughRowsAndColumnsCount режет копии наших областей на указанное количество строк и столбцов:



Используя команду MeshRegionThroughRowsAndColumnsCount можно нарезать области произвольным образом, например так:



Скорость работы команды MeshRegionThroughCellSize и MeshRegionThroughRowsAndColumnsCount зависит от количества областей, которые должны получиться на выходе, а так же от значения предельно допустимой абсолютной погрешности, указанной пользователем.

Исходный код программы разбит на несколько файлов. Далее приведено их полное содержимое.

Файл PluginEnvironment.cs

/* PluginEnvironment.cs
 * © Андрей Бушман, 2014 
 * Централизованное хранение общей информации, используемой в коде различных
 * методов. Т.о. при необходимости достаточно будет поменять её в одном месте
 * и все изменения автоматически подхватятся.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Bushman.CAD {
  public static class PluginEnvironment {
    /// <summary>
    /// Наименование группы команд AutoCAD, определённых в составе данной 
    /// сборки.
    /// </summary>
    public const String DefaultCommandGroup = "Bushman";
  }
}

Файл ExtensionApplication.cs

/* ExtensionApplication.cs
 * © Андрей Бушман, 2014 
 * Информация, отправляемая в консоль ActiveDocument при загрузке данной 
 * сборки. Если в текущий момент ActiveDocument равен null, то информация будет
 * выведена в консоль первого открытого в дальнейшем документа.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;

#if AUTOCAD
using cad = Autodesk.AutoCAD.ApplicationServices.Application;
using Ap = Autodesk.AutoCAD.ApplicationServices;
using Db = Autodesk.AutoCAD.DatabaseServices;
using Ed = Autodesk.AutoCAD.EditorInput;
using Rt = Autodesk.AutoCAD.Runtime;
using Gm = Autodesk.AutoCAD.Geometry;
using Wn = Autodesk.AutoCAD.Windows;
using Hs = Autodesk.AutoCAD.DatabaseServices.HostApplicationServices;
using Us = Autodesk.AutoCAD.DatabaseServices.SymbolUtilityServices;
#endif

namespace Bushman.CAD {

  public sealed class ExtensionApplication : Rt.IExtensionApplication {
    #region IExtensionApplication Members

    public void Initialize() {
      Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
      if(doc == null) {
        cad.DocumentManager.DocumentActivated += DocMng_DocActivated;
        return;
      }
      Db.Database db = doc.Database;
      Ed.Editor ed = doc.Editor;
      WriteAboutInfo(ed);
    }

    public void Terminate() {
    }
    #endregion

    internal static void WriteAboutInfo(Ed.Editor ed) {
      try {
        String title = GetAssemblyAttribute<AssemblyTitleAttribute>(
          a => a.Title);
        String copyright = GetAssemblyAttribute<AssemblyCopyrightAttribute>(
          a => a.Copyright);
        // String version = GetAssemblyAttribute<AssemblyVersionAttribute>(
        //  a => a.Version);
        String description = GetAssemblyAttribute<AssemblyDescriptionAttribute>(
          a => a.Description);

        ed.WriteMessage("n{0}n{1}. {2}n{3}n",
         Assembly.GetExecutingAssembly().Location, title, copyright,
         description);
      }
      catch(Exception ex) {
        ed.WriteMessage("{0}n", ex.Message);
      }
    }

    void DocMng_DocActivated(object sender, Ap.DocumentCollectionEventArgs e) {
      cad.DocumentManager.DocumentActivated -= DocMng_DocActivated;
      WriteAboutInfo(e.Document.Editor);
    }

    internal static String GetAssemblyAttribute<T>(Func<T, String> value)
      where T : Attribute {
      T attribute = (T)Attribute.GetCustomAttribute(
        Assembly.GetExecutingAssembly(), typeof(T));
      return value.Invoke(attribute);
    }
  }
}

Файл About.cs

/* About.cs
 * © Андрей Бушман, 2014 
 * Команда, предоставляющая пользователю общую информацию о библиотеке.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BUc = Bushman.CAD;

#if AUTOCAD
using cad = Autodesk.AutoCAD.ApplicationServices.Application;
using Ap = Autodesk.AutoCAD.ApplicationServices;
using Db = Autodesk.AutoCAD.DatabaseServices;
using Ed = Autodesk.AutoCAD.EditorInput;
using Rt = Autodesk.AutoCAD.Runtime;
using Gm = Autodesk.AutoCAD.Geometry;
using Wn = Autodesk.AutoCAD.Windows;
using Hs = Autodesk.AutoCAD.DatabaseServices.HostApplicationServices;
using Us = Autodesk.AutoCAD.DatabaseServices.SymbolUtilityServices;
#endif

[assembly: Rt.CommandClass(typeof(Bushman.CAD.Commands.About))]

namespace Bushman.CAD.Commands {
  public class About {
    [Rt.CommandMethod(BUc.PluginEnvironment.DefaultCommandGroup, "About",
      Rt.CommandFlags.Modal)]
    public void AboutCommand() {
      Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
      if(doc == null || doc.IsDisposed)
        return;
      BUc.ExtensionApplication.WriteAboutInfo(doc.Editor);
    }
  }
}

Файл RegionTools.cs

/* RegionTools.cs
 * © Андрей Бушман, 2014 
 * Набор методов, предназначенных для создания разрезанной на части копии 
 * исходной области (region).
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

#if AUTOCAD
using cad = Autodesk.AutoCAD.ApplicationServices.Application;
using Ap = Autodesk.AutoCAD.ApplicationServices;
using Db = Autodesk.AutoCAD.DatabaseServices;
using Ed = Autodesk.AutoCAD.EditorInput;
using Rt = Autodesk.AutoCAD.Runtime;
using Gm = Autodesk.AutoCAD.Geometry;
using Wn = Autodesk.AutoCAD.Windows;
using Hs = Autodesk.AutoCAD.DatabaseServices.HostApplicationServices;
using Us = Autodesk.AutoCAD.DatabaseServices.SymbolUtilityServices;
using Br = Autodesk.AutoCAD.BoundaryRepresentation;
#endif

namespace Bushman.CAD.Extensions {

  /// <summary>
  /// Статический класс, предоставляющий дополнительный набор методов для 
  /// работы с объектами Region.
  /// </summary>
  public static class RegionTools {

    static Double dx = 10.0;
    static Double dy = 10.0;

    /// <summary>
    /// Предельно допустимое значение ширины ячейки. Свойство влияет на работу
    /// команды MeshRegionThroughCellSize.
    /// </summary>
    public static Double CellWidth {
      get { return dx; }
      set {
        if(dx <= 0)
          throw new ArgumentException(value.ToString());
        dx = value;
      }
    }

    /// <summary>
    /// Предельно допустимое значение высоты ячейки. Свойство влияет на работу
    /// команды MeshRegionThroughCellSize.
    /// </summary>
    public static Double CellHeight {
      get { return dy; }
      set {
        if(dy <= 0)
          throw new ArgumentException(value.ToString());
        dy = value;
      }
    }

    static Int32 rowsCount = 1;
    static Int32 columnsCount = 1;

    /// <summary>
    /// Количество строк в нарезаемой сетке. Свойство влияет на работу
    /// команды MeshRegionThroughRowsAndColumnsCount.
    /// </summary>
    public static Int32 RowsCount {
      get { return rowsCount; }
      set {
        if(rowsCount <= 0)
          throw new ArgumentException(value.ToString());
        rowsCount = value;
      }
    }

    /// <summary>
    /// Количество колонок в нарезаемой сетке. Свойство влияет на работу
    /// команды MeshRegionThroughRowsAndColumnsCount.
    /// </summary>
    public static Int32 ColumnsCount {
      get { return columnsCount; }
      set {
        if(columnsCount <= 0)
          throw new ArgumentException(value.ToString());
        columnsCount = value;
      }
    }

    // Предельная абсолютная погрешность точности измерения визуальных границ
    // примитива.
    static Double delta = 0.1;

    /// <summary>
    /// Предельная абсолютная погрешность точности измерения визуальных границ
    /// "GeometricExtents" региона. Чем меньше погрешность, тем точнее граница,
    /// но тем дольше она высчитывается. Свойство влияет на работу команды
    /// BoundaryRegion.
    /// </summary>
    public static Double Delta {
      get { return delta; }
      set {
        if(delta <= 0)
          throw new ArgumentException(value.ToString());
        delta = value;
      }
    }

    /// <summary>
    /// Настройка базы данных чертежа: установка нужных единиц измерения, 
    /// системы кооринат и т.п.
    /// </summary>
    /// <param name="db">Целевая база данных</param>
    internal static void SetDatabaseDefaultSettings(Db.Database db) {
      if(db == null)
        throw new ArgumentNullException("db");
      if(db.IsDisposed)
        throw new ArgumentException("db.IsDisposed == true");

      // Устанавливаем текущей метрическую систему измерения и миллиметры в
      // качестве используемых единиц измерения
      db.Measurement = Db.MeasurementValue.Metric;
      db.Insunits = Db.UnitsValue.Millimeters;
      // Устанавливаем текущей мировую систему координат
      db.WorldUcsBaseOrigin(Db.OrthographicView.TopView);
    }

    /// <summary>
    /// Создать прямоугольную область (Region), представляющую собой "ячейку"
    /// нарезаемой сетки.
    /// </summary>
    /// <param name="topLeftCorner">Координата верхнего левого угла "ячейки".
    /// </param>
    /// <param name="dx">Длина "ячейки".</param>
    /// <param name="dy">Высота "ячейки".</param>
    /// <returns>Возвращается новый объект Region. Программист должен либо 
    /// добавить полученный объект в Database, либо по завершению работы с ним
    /// не забыть его уничтожить, вызвав метод Dispose (дабы избежать утечки 
    /// памяти).</returns>
    public static Db.Region GetRegionCell(Gm.Point2d topLeftCorner, Double dx,
      Double dy) {

      #region Проверка входных данных
      if(dx <= 0)
        throw new ArgumentException("dx <= 0");
      if(dy <= 0)
        throw new ArgumentException("dy <= 0");
      #endregion

      Db.Region regCell = null;
      using(Db.Polyline pline = new Db.Polyline()) {
        pline.SetDatabaseDefaults();
        pline.AddVertexAt(0, new Gm.Point2d(topLeftCorner.X, topLeftCorner.Y),
          0, 0, 0);
        pline.AddVertexAt(1, new Gm.Point2d(topLeftCorner.X + dx,
          topLeftCorner.Y), 0, 0, 0);
        pline.AddVertexAt(2, new Gm.Point2d(topLeftCorner.X + dx,
          topLeftCorner.Y - dy), 0, 0, 0);
        pline.AddVertexAt(3, new Gm.Point2d(topLeftCorner.X, topLeftCorner.Y -
          dy), 0, 0, 0);
        pline.Closed = true;

        regCell = Db.Region.CreateFromCurves(new Db.DBObjectCollection() { 
          pline }).Cast<Db.Region>().First();
      }
      return regCell;
    }

    /// <summary>
    /// Получить массив областей (region) путём разреки копии указанной 
    /// области на отдельные прямоугольные (по возможности) части.
    /// </summary>
    /// <param name="region">Область, копия которой будет создана и затем 
    /// разрезана на части.</param>
    /// <param name="delta">Предельная абсолютная погрешность точности 
    /// измерения визуальных границ региона. Чем меньше погрешность, тем точнее
    /// граница, но тем дольше она высчитывается.</param>
    /// <param name="dx">Максимальная длина получаемых сегментов.</param>
    /// <param name="dy">Максимальная высота получаемых сегментов.</param>
    /// <returns>Возвращается массив объектов Region. Программист должен либо 
    /// добавить их в Database, либо по завершению своей работы с ними не 
    /// забыть уничтожить, вызвав для каждого элемента его метод Dispose (дабы
    /// избежать утечки памяти).</returns>
    public static Db.Region[] CloneAndMesh(this Db.Region region, Double delta,
      Double dx, Double dy) {

      #region Проверка входных данных
      if(region == null)
        throw new ArgumentNullException("region");
      if(region.IsDisposed)
        throw new ArgumentException("region.IsDisposed == true");
      if(dx < 0)
        throw new ArgumentException("dx < 0");
      if(dy < 0)
        throw new ArgumentException("dy < 0");
      #endregion

      List<Db.Region> result = new List<Db.Region>();

      Gm.Point2d minPoint = Gm.Point2d.Origin;
      Gm.Point2d maxPoint = Gm.Point2d.Origin;

      region.GetVisualBoundary(delta, ref minPoint, ref maxPoint);

      #region Если исходный регион не превышает ячейку, то резать нечего
      Double length = maxPoint.X - minPoint.X;
      Double height = maxPoint.Y - minPoint.Y;

      // Если исходный регион меньше ячейки нарезаемой сетки, то возвращаем
      // пустой массив, т.к. разрезать нечего
      if(dx >= length && dy >= height)
        return result.ToArray();
      #endregion

      for(Double offsetY = height; offsetY > -dy; offsetY -= dy) {

        Db.Region regionClone = region.Clone() as Db.Region;
        Gm.Point2d topLeftCorner = new Gm.Point2d(minPoint.X,
          minPoint.Y + offsetY);

        Db.Region regionRow = GetRegionCell(topLeftCorner, length, dy);

        regionClone.BooleanOperation(Db.BooleanOperationType.BoolIntersect,
          regionRow);

        // Если значения обоих свойств regionClone.IsNull и regionRow.IsNull
        // равны true, значит пересечение отсутствует

        // Если логическая операция выполнена успешно, то её результат 
        // сохраняем, т.к. его ещё предстоит резать вертикально
        if(!regionClone.IsNull && regionRow.IsNull) {

          // Теперь полученную горионтальную часть режем вертикально
          if(length > dx) {
            Db.Region row = regionClone;
            Double rowLength = maxPoint.X - minPoint.X;

            for(Double offsetX = rowLength - dx; offsetX > -dx;
              offsetX -= dx) {
              Db.Region rowClone = row.Clone() as Db.Region;
              topLeftCorner = new Gm.Point2d(minPoint.X + offsetX,
                minPoint.Y + offsetY);

              Db.Region regionCell = GetRegionCell(topLeftCorner, dx, dy);
              regionCell.LayerId = region.LayerId;

              rowClone.BooleanOperation(
                Db.BooleanOperationType.BoolIntersect, regionCell);

              // Если логическая операция выполнена успешно, то её результат 
              // сохраняем, т.к. его ещё предстоит резать вертикально
              if(!rowClone.IsNull && regionCell.IsNull) {
                result.AddRange(ExplodeIfUnitedRegions(rowClone));

                if(!regionCell.IsDisposed)
                  regionCell.Dispose();
              }
              // Предотвращаем утечку памяти
              else {
                if(!regionCell.IsDisposed)
                  regionCell.Dispose();
                if(!rowClone.IsDisposed)
                  rowClone.Dispose();
              }
            }
            row.Dispose();
          }
          else {
            result.AddRange(ExplodeIfUnitedRegions(regionClone));
          }

          if(!regionRow.IsDisposed)
            regionRow.Dispose();
        }
        // Предотвращаем утечку памяти
        else {
          if(!regionRow.IsDisposed)
            regionRow.Dispose();
          if(!regionClone.IsDisposed)
            regionClone.Dispose();
        }
      }
      return result.ToArray();
    }

    /// <summary>
    /// Метод получает на входе объект Region и проверяет, состоит ли он из
    /// нескольких объединённых областей. Если состоит, то эти области 
    /// извлекаются и записываются в массив объектов Region, после чего объект
    /// исходной области, переданной методу в качестве параметра, уничтожается.
    /// 
    /// Если исходная область не состоит из нескольких объединённых частей, то
    /// объект этой области добавляется в массив объектов Region.
    /// </summary>
    /// <param name="region">Исходная область, подлежащая обработке.</param>
    /// <returns>Возвращается массив областей. Как минимум, в массиве будет 
    /// присутствовать хотя бы один объект.</returns>
    public static Db.Region[] ExplodeIfUnitedRegions(Db.Region region) {

      if(region == null)
        throw new ArgumentNullException("region");
      if(region.IsDisposed)
        throw new ArgumentException("region.IsDisposed == true");

      List<Db.Region> result = new List<Db.Region>();
      using(Br.Brep brep = new Br.Brep(region)) {
        if(brep.Complexes != null && brep.Complexes.Count() > 1) {
          using(Db.DBObjectCollection explodeResult =
            new Db.DBObjectCollection()) {
            region.Explode(explodeResult);
            foreach(Db.DBObject item in explodeResult) {
              Db.Region n = (Db.Region)item;
              n.LayerId = region.LayerId;
              result.Add(n);
            }
          }

          if(!region.IsDisposed)
            region.Dispose();
        }
        else
          result.Add(region);
      }
      return result.ToArray();
    }

    // Код метода GetVisualBoundary был написан Александром Ривилисом здесь:
    // http://adn-cis.org/forum/index.php?topic=495.msg3043#msg3043

    /// <summary>
    /// Получить координаты границ левого нижнего и правого верхнего углов для
    /// визуального "GeometricExtents" региона.
    /// </summary>
    /// <param name="region">Регион, для которого следует получить координаты 
    /// границ визуального "GeometricExtents".</param>
    /// <param name="delta">Предельная арифметическая погрешность вычислений.
    /// </param>
    /// <param name="minPoint">Ссылка на переменную Point2d, в которой следует
    /// сохранить координаты левого нижнего угла визуального 
    /// "GeometricExtents".</param>
    /// <param name="maxPoint">Ссылка на переменную Point2d, в которой следует
    /// сохранить координаты правого верхнего угла визуального 
    /// "GeometricExtents".</param>
    public static void GetVisualBoundary(this Db.Region region, Double delta,
  ref Gm.Point2d minPoint, ref Gm.Point2d maxPoint) {
      using(Gm.BoundBlock3d boundBlk = new Gm.BoundBlock3d()) {
        using(Br.Brep brep = new Br.Brep(region)) {
          foreach(Br.Edge edge in brep.Edges) {
            using(Gm.Curve3d curve = edge.Curve) {
              Gm.ExternalCurve3d curve3d = curve as Gm.ExternalCurve3d;
              // Делать точный расчет нужно только если образующая - сплайн
              // в противном случае достаточно получить BoundBlock
              if(curve3d != null && curve3d.IsNurbCurve) {
                using(Gm.NurbCurve3d nurbCurve = curve3d.NativeCurve
                  as Gm.NurbCurve3d) {
                  Gm.Interval interval = nurbCurve.GetInterval();
                  for(double par = interval.LowerBound;
                    par <= interval.UpperBound; par += (delta * 2.0)) {
                    Gm.Point3d p = nurbCurve.EvaluatePoint(par);
                    if(!boundBlk.IsBox)
                      boundBlk.Set(p, p);
                    else
                      boundBlk.Extend(p);
                  }
                }
              }
              else {
                if(!boundBlk.IsBox) {
                  boundBlk.Set(edge.BoundBlock.GetMinimumPoint(),
                    edge.BoundBlock.GetMaximumPoint());
                }
                else {
                  boundBlk.Extend(edge.BoundBlock.GetMinimumPoint());
                  boundBlk.Extend(edge.BoundBlock.GetMaximumPoint());
                }
              }
            }
          }
        }
        // Возвращаем вычисленный результат
        minPoint = new Gm.Point2d(boundBlk.GetMinimumPoint().X,
          boundBlk.GetMinimumPoint().Y);
        maxPoint = new Gm.Point2d(boundBlk.GetMaximumPoint().X,
          boundBlk.GetMaximumPoint().Y);
      }
    }
  }
}

Файл RegionCommands.cs

/* RegionCommands.cs
 * © Андрей Бушман, 2014 
 * Дополнительные команды CAD для работы с объектами областей (region).
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using BUc = Bushman.CAD;
using BUx = Bushman.CAD.Extensions;
// для возможности использования методов расширений
using Bushman.CAD.Extensions;

#if AUTOCAD
using cad = Autodesk.AutoCAD.ApplicationServices.Application;
using Ap = Autodesk.AutoCAD.ApplicationServices;
using Db = Autodesk.AutoCAD.DatabaseServices;
using Ed = Autodesk.AutoCAD.EditorInput;
using Rt = Autodesk.AutoCAD.Runtime;
using Gm = Autodesk.AutoCAD.Geometry;
using Wn = Autodesk.AutoCAD.Windows;
using Hs = Autodesk.AutoCAD.DatabaseServices.HostApplicationServices;
using Us = Autodesk.AutoCAD.DatabaseServices.SymbolUtilityServices;
using Br = Autodesk.AutoCAD.BoundaryRepresentation;
#endif

[assembly: Rt.CommandClass(typeof(Bushman.CAD.Commands.RegionCommands))]

namespace Bushman.CAD.Commands {

  public sealed class RegionCommands {

    /// <summary>
    /// Команда CAD, при помощи которой формируется "нарезка" копий указанных
    /// областей (Regions) на прямоугольные (по возможности) части, не 
    /// превышающие размеров dx и dy, заданных пользователем.
    /// </summary>
    [Rt.CommandMethod(Bushman.CAD.PluginEnvironment.DefaultCommandGroup,
      "MeshRegionThroughCellSize", Rt.CommandFlags.Modal)]
    public void MeshRegionThroughCellSize() {
      Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
      if(doc == null)
        return;

      Db.Database db = doc.Database;
      BUx.RegionTools.SetDatabaseDefaultSettings(db);
      Ed.Editor ed = doc.Editor;

      Db.TypedValue[] tv = new Db.TypedValue[1];
      tv[0] = new Db.TypedValue((Int32)Db.DxfCode.Start, "REGION");
      Ed.SelectionFilter filter = new Ed.SelectionFilter(tv);
      Ed.PromptSelectionOptions pso = new Ed.PromptSelectionOptions();
      pso.MessageForAdding = "Выберите области (Regions) для добавления " +
        "их в набор";
      pso.MessageForRemoval =
        "Выберите области (Regions), подлежащие удалению из набора";
      pso.SingleOnly = false;
      pso.RejectObjectsFromNonCurrentSpace = true;
      pso.AllowDuplicates = false;

      Ed.PromptSelectionResult psr = ed.GetSelection(pso, filter);

      if(psr.Status != Ed.PromptStatus.OK) {
        ed.WriteMessage("Ничего не выбрано. Выполнение команды прерваноn");
        return;
      }
      else {
        ed.WriteMessage("nВсего выбрано областей (Regions): {0}n",
          psr.Value.Count);
      }
      Db.ObjectId[] ids = psr.Value.GetObjectIds();
      Ed.PromptDoubleOptions pdo = new Ed.PromptDoubleOptions("dx");
      pdo.AllowNegative = false;
      pdo.AllowZero = false;
      pdo.AllowNone = false;
      pdo.DefaultValue = RegionTools.CellWidth;
      pdo.UseDefaultValue = true;

      Ed.PromptDoubleResult pdr = ed.GetDouble(pdo);
      if(pdr.Status != Ed.PromptStatus.OK) {
        ed.WriteMessage("Выполнение команды прерваноn");
        return;
      }
      RegionTools.CellWidth = pdr.Value;

      ed.WriteMessage(Environment.NewLine);

      pdo.Message = "dy";
      pdo.DefaultValue = RegionTools.CellHeight;
      pdr = ed.GetDouble(pdo);
      if(pdr.Status != Ed.PromptStatus.OK) {
        ed.WriteMessage("Выполнение команды прерваноn");
        return;
      }
      RegionTools.CellHeight = pdr.Value;

      pdo = new Ed.PromptDoubleOptions(
        "Предельная абсолютная погрешность");
      pdo.AllowNegative = false;
      pdo.AllowZero = false;
      pdo.AllowNone = false;
      pdo.DefaultValue = RegionTools.Delta;
      pdo.UseDefaultValue = true;

      pdr = ed.GetDouble(pdo);

      if(pdr.Status != Ed.PromptStatus.OK) {
        ed.WriteMessage("Выполнение команды прерваноn");
        return;
      }
      RegionTools.Delta = pdr.Value;

      ed.WriteMessage(Environment.NewLine);

      using(Db.Transaction tr = db.TransactionManager.StartTransaction()) {

        Db.BlockTable bt = (Db.BlockTable)tr.GetObject(db.BlockTableId,
         Db.OpenMode.ForRead);
        Db.BlockTableRecord btr = (Db.BlockTableRecord)tr.GetObject(bt[
         Db.BlockTableRecord.ModelSpace], Db.OpenMode.ForWrite);

        DateTime start = DateTime.Now;
        Int32 count = 0;
        foreach(Db.ObjectId id in ids) {
          Db.Region region = tr.GetObject(id, Db.OpenMode.ForRead)
            as Db.Region;
          Db.Region[] regions = region.CloneAndMesh(RegionTools.Delta,
            RegionTools.CellWidth, RegionTools.CellHeight);
          count += regions.Length;
          foreach(Db.Region item in regions) {
            btr.AppendEntity(item);
            tr.AddNewlyCreatedDBObject(item, true);
          }
        }
        tr.Commit();

        DateTime end = DateTime.Now;
        ed.WriteMessage(
          "nСоздано областей (region): {0}nЗатраченное время: {1}n",
         count, end - start);
      }
    }

    /// <summary>
    /// Команда CAD, при помощи которой формируется "нарезка" копий указанных
    /// областей (Regions) на прямоугольные (по возможности) части согласно 
    /// указанному количеству строк и столбцов.
    /// </summary>
    [Rt.CommandMethod(Bushman.CAD.PluginEnvironment.DefaultCommandGroup,
      "MeshRegionThroughRowsAndColumnsCount", Rt.CommandFlags.Modal)]
    public void MeshRegionThroughRowsAndColumnsCount() {
      Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
      if(doc == null)
        return;

      Db.Database db = doc.Database;
      BUx.RegionTools.SetDatabaseDefaultSettings(db);
      Ed.Editor ed = doc.Editor;

      Db.TypedValue[] tv = new Db.TypedValue[1];
      tv[0] = new Db.TypedValue((Int32)Db.DxfCode.Start, "REGION");
      Ed.SelectionFilter filter = new Ed.SelectionFilter(tv);
      Ed.PromptSelectionOptions pso = new Ed.PromptSelectionOptions();
      pso.MessageForAdding = "Выберите области (Regions) для добавления " +
        "их в набор";
      pso.MessageForRemoval =
        "Выберите области (Regions), подлежащие удалению из набора";
      pso.SingleOnly = false;
      pso.RejectObjectsFromNonCurrentSpace = true;
      pso.AllowDuplicates = false;

      Ed.PromptSelectionResult psr = ed.GetSelection(pso, filter);

      if(psr.Status != Ed.PromptStatus.OK) {
        ed.WriteMessage("Ничего не выбрано. Выполнение команды прерваноn");
        return;
      }
      else {
        ed.WriteMessage("nВсего выбрано областей (Regions): {0}n",
          psr.Value.Count);
      }
      Db.ObjectId[] ids = psr.Value.GetObjectIds();
      Ed.PromptIntegerOptions pio = new Ed.PromptIntegerOptions(
        "Количество столбцов");
      pio.AllowNegative = false;
      pio.AllowZero = false;
      pio.AllowNone = false;
      pio.DefaultValue = RegionTools.ColumnsCount;
      pio.UseDefaultValue = true;

      Ed.PromptIntegerResult pir = ed.GetInteger(pio);
      if(pir.Status != Ed.PromptStatus.OK) {
        ed.WriteMessage("Выполнение команды прерваноn");
        return;
      }
      RegionTools.ColumnsCount = pir.Value;

      ed.WriteMessage(Environment.NewLine);

      pio.Message = "Количество строк";
      pio.DefaultValue = RegionTools.RowsCount;
      pir = ed.GetInteger(pio);
      if(pir.Status != Ed.PromptStatus.OK) {
        ed.WriteMessage("Выполнение команды прерваноn");
        return;
      }
      RegionTools.RowsCount = pir.Value;

      if(RegionTools.ColumnsCount != 1 || RegionTools.RowsCount != 1) {
        Ed.PromptDoubleOptions pdo = new Ed.PromptDoubleOptions(
  "Предельная абсолютная погрешность");
        pdo.AllowNegative = false;
        pdo.AllowZero = false;
        pdo.AllowNone = false;
        pdo.DefaultValue = RegionTools.Delta;
        pdo.UseDefaultValue = true;

        Ed.PromptDoubleResult pdr = ed.GetDouble(pdo);

        if(pdr.Status != Ed.PromptStatus.OK) {
          ed.WriteMessage("Выполнение команды прерваноn");
          return;
        }
        RegionTools.Delta = pdr.Value;

        ed.WriteMessage(Environment.NewLine);

        using(Db.Transaction tr = db.TransactionManager.StartTransaction()) {

          Db.BlockTable bt = (Db.BlockTable)tr.GetObject(db.BlockTableId,
           Db.OpenMode.ForRead);
          Db.BlockTableRecord btr = (Db.BlockTableRecord)tr.GetObject(bt[
           Db.BlockTableRecord.ModelSpace], Db.OpenMode.ForWrite);

          DateTime start = DateTime.Now;
          Int32 count = 0;
          foreach(Db.ObjectId id in ids) {
            Db.Region region = tr.GetObject(id, Db.OpenMode.ForRead)
              as Db.Region;

            Gm.Point2d minPoint = Gm.Point2d.Origin;
            Gm.Point2d maxPoint = Gm.Point2d.Origin;

            region.GetVisualBoundary(RegionTools.Delta, ref minPoint,
              ref maxPoint);

            Double width = (maxPoint.X - minPoint.X) / RegionTools.ColumnsCount;
            Double height = (maxPoint.Y - minPoint.Y) / RegionTools.RowsCount;

            Db.Region[] regions = region.CloneAndMesh(RegionTools.Delta, width,
              height);
            count += regions.Length;
            foreach(Db.Region item in regions) {
              btr.AppendEntity(item);
              tr.AddNewlyCreatedDBObject(item, true);
            }
          }
          tr.Commit();

          DateTime end = DateTime.Now;
          ed.WriteMessage(
            "nСоздано областей (region): {0}nЗатраченное время: {1}n",
           count, end - start);
        }
      }
      else {
        ed.WriteMessage("nЕсли количество строк и столбцов одновременно " +
          "равны 1, то создавать нечего.n");
      }
    }

    /// <summary>
    /// Получение значения визуальных границ регионов (аналог реализации от
    /// AutoCAD по умолчанию). В случаях, когда контуры областей выполнены
    /// сплайнами, команда BoundaryRegion покажет более точные границы, чем 
    /// команда AcadBoundaryRegion. Назначение AcadBoundaryRegion в том, чтобы
    /// визуально показать программисту границы контейнера области. Это может
    /// помочь понять, почему в некоторых случаях программный код даёт не те 
    /// результаты, которые ожидались на выходе.
    /// </summary>
    [Rt.CommandMethod(Bushman.CAD.PluginEnvironment.DefaultCommandGroup,
      "AcadBoundaryRegion", Rt.CommandFlags.Modal)]
    public void AcadBoundaryRegion() {
      Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
      if(doc == null)
        return;

      Db.Database db = doc.Database;
      BUx.RegionTools.SetDatabaseDefaultSettings(db);
      Ed.Editor ed = doc.Editor;

      Db.TypedValue[] tv = new Db.TypedValue[1];
      tv[0] = new Db.TypedValue((Int32)Db.DxfCode.Start, "REGION");
      Ed.SelectionFilter filter = new Ed.SelectionFilter(tv);
      Ed.PromptSelectionOptions pso = new Ed.PromptSelectionOptions();
      pso.MessageForAdding = "Выберите области (Regions) для добавления " +
        "их в набор";
      pso.MessageForRemoval =
        "Выберите области (Regions), подлежащие удалению из набора";
      pso.SingleOnly = false;
      pso.RejectObjectsFromNonCurrentSpace = true;
      pso.AllowDuplicates = false;

      Ed.PromptSelectionResult psr = ed.GetSelection(pso, filter);

      if(psr.Status != Ed.PromptStatus.OK) {
        ed.WriteMessage("Ничего не выбрано. Выполнение команды прерваноn");
        return;
      }
      else {
        ed.WriteMessage("nВсего выбрано областей (Regions): {0}n",
          psr.Value.Count);
      }
      Db.ObjectId[] ids = psr.Value.GetObjectIds();

      ed.WriteMessage(Environment.NewLine);

      using(Db.Transaction tr = db.TransactionManager.StartTransaction()) {

        Db.BlockTable bt = (Db.BlockTable)tr.GetObject(db.BlockTableId,
         Db.OpenMode.ForRead);
        Db.BlockTableRecord btr = (Db.BlockTableRecord)tr.GetObject(bt[
         Db.BlockTableRecord.ModelSpace], Db.OpenMode.ForWrite);

        foreach(Db.ObjectId id in ids) {
          Db.Region region = tr.GetObject(id, Db.OpenMode.ForRead)
            as Db.Region;

          using(Br.Brep brep = new Br.Brep(region)) {
            Gm.Point3d origin = new Gm.Point3d(0, 0, 0);
            Gm.Vector3d normal = new Gm.Vector3d(0, 0, 1);
            Gm.Plane plane = new Gm.Plane(origin, normal);

            Gm.BoundBlock3d bb = brep.BoundBlock;

            Gm.Point2d minPoint = Gm.Point2d.Origin;
            Gm.Point2d maxPoint = Gm.Point2d.Origin;

            Double minX = Double.MaxValue;
            Double minY = Double.MaxValue;

            Double maxX = Double.MinValue;
            Double maxY = Double.MinValue;

            if(brep.Edges != null) {
              foreach(Br.Edge edge in brep.Edges) {

                Gm.Point3d min = bb.GetMinimumPoint();
                Gm.Point3d max = bb.GetMaximumPoint();

                if(min.X < minX)
                  minX = min.X;

                if(min.Y < minY)
                  minY = min.Y;

                if(max.X > maxX)
                  maxX = max.X;

                if(max.Y > maxY)
                  maxY = max.Y;
              }
              minPoint = new Gm.Point2d(minX, minY);
              maxPoint = new Gm.Point2d(maxX, maxY);
            }
            else {
              minPoint = bb.GetMinimumPoint().Convert2d(plane);
              maxPoint = bb.GetMaximumPoint().Convert2d(plane);
            }


            Db.Polyline pline = new Db.Polyline(4);
            pline.SetDatabaseDefaults();

            Gm.Point2d[] points = new Gm.Point2d[]{
              new Gm.Point2d(minPoint.X,maxPoint.Y),
              new Gm.Point2d(maxPoint.X,maxPoint.Y),
              new Gm.Point2d(maxPoint.X,minPoint.Y),
              new Gm.Point2d(minPoint.X, minPoint.Y)};

            for(Int32 i = 0; i < points.Length; i++)
              pline.AddVertexAt(i, points[i], 0, 0, 0);

            pline.Closed = true;
            pline.ColorIndex = 50;

            btr.AppendEntity(pline);
            tr.AddNewlyCreatedDBObject(pline, true);
          }
        }
        tr.Commit();
      }
    }

    /// <summary>
    /// Получение значения визуальных границ регионов. В случаях, когда контуры
    /// областей выполнены сплайнами, данная команда покажет более точные 
    /// границы, чем команда AcadBoundaryRegion. Данная команда предназначена
    /// для демонстрирования работы статического метода 
    /// RegionTools.GetVisualBoundary(...).
    /// </summary>
    [Rt.CommandMethod(Bushman.CAD.PluginEnvironment.DefaultCommandGroup,
  "BoundaryRegion", Rt.CommandFlags.Modal)]
    public void BoundaryRegion() {
      Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
      if(doc == null)
        return;

      Db.Database db = doc.Database;
      BUx.RegionTools.SetDatabaseDefaultSettings(db);
      Ed.Editor ed = doc.Editor;

      Db.TypedValue[] tv = new Db.TypedValue[1];
      tv[0] = new Db.TypedValue((Int32)Db.DxfCode.Start, "REGION");
      Ed.SelectionFilter filter = new Ed.SelectionFilter(tv);
      Ed.PromptSelectionOptions pso = new Ed.PromptSelectionOptions();
      pso.MessageForAdding = "Выберите области (Regions) для добавления " +
        "их в набор";
      pso.MessageForRemoval =
        "Выберите области (Regions), подлежащие удалению из набора";
      pso.SingleOnly = false;
      pso.RejectObjectsFromNonCurrentSpace = true;
      pso.AllowDuplicates = false;

      Ed.PromptSelectionResult psr = ed.GetSelection(pso, filter);

      if(psr.Status != Ed.PromptStatus.OK) {
        ed.WriteMessage("Ничего не выбрано. Выполнение команды прерваноn");
        return;
      }
      else {
        ed.WriteMessage("nВсего выбрано областей (Regions): {0}n",
          psr.Value.Count);
      }
      Db.ObjectId[] ids = psr.Value.GetObjectIds();

      ed.WriteMessage(Environment.NewLine);

      Ed.PromptDoubleOptions pdo = new Ed.PromptDoubleOptions(
        "Предельная абсолютная погрешность");
      pdo.AllowNegative = false;
      pdo.AllowZero = false;
      pdo.AllowNone = false;
      pdo.DefaultValue = RegionTools.Delta;
      pdo.UseDefaultValue = true;

      Ed.PromptDoubleResult pdr = ed.GetDouble(pdo);
      if(pdr.Status != Ed.PromptStatus.OK) {
        ed.WriteMessage("Выполнение команды прерваноn");
        return;
      }
      RegionTools.Delta = pdr.Value;

      using(Db.Transaction tr = db.TransactionManager.StartTransaction()) {

        Db.BlockTable bt = (Db.BlockTable)tr.GetObject(db.BlockTableId,
         Db.OpenMode.ForRead);
        Db.BlockTableRecord btr = (Db.BlockTableRecord)tr.GetObject(bt[
         Db.BlockTableRecord.ModelSpace], Db.OpenMode.ForWrite);

        Int32 count = 0;
        DateTime start = DateTime.Now;

        foreach(Db.ObjectId id in ids) {
          Db.Region region = tr.GetObject(id, Db.OpenMode.ForRead)
            as Db.Region;
          using(Br.Brep brep = new Br.Brep(region)) {
            Gm.Point3d origin = new Gm.Point3d(0, 0, 0);
            Gm.Vector3d normal = new Gm.Vector3d(0, 0, 1);
            Gm.Plane plane = new Gm.Plane(origin, normal);

            Gm.Point2d minPoint = Gm.Point2d.Origin;
            Gm.Point2d maxPoint = Gm.Point2d.Origin;

            region.GetVisualBoundary(RegionTools.Delta, ref minPoint,
              ref maxPoint);

            Db.Polyline pline = new Db.Polyline(4);
            pline.SetDatabaseDefaults();

            Gm.Point2d[] points = new Gm.Point2d[]{
              new Gm.Point2d(minPoint.X,maxPoint.Y),
              new Gm.Point2d(maxPoint.X,maxPoint.Y),
              new Gm.Point2d(maxPoint.X,minPoint.Y),
              new Gm.Point2d(minPoint.X, minPoint.Y)};

            for(Int32 i = 0; i < points.Length; i++)
              pline.AddVertexAt(i, points[i], 0, 0, 0);

            pline.Closed = true;
            pline.ColorIndex = 80;

            btr.AppendEntity(pline);
            tr.AddNewlyCreatedDBObject(pline, true);
            ++count;
          }
        }
        tr.Commit();
        DateTime end = DateTime.Now;
        ed.WriteMessage("nСоздано границ: {0}nЗатраченное время: {1}n",
         count, end - start);
      }
    }
  }
}

Выполнение логических операций с областями (regions)

В приведённом ниже коде сначала создаётся две области (красная и жёлтая), затем к их копиям применяются различные логические операции. Результат размещается рядом и подсвечивается зелёным цветом. Результат работы кода показан на скрине.

Комментировать тут особо не чего, поэтому сразу код:

   1:  /* RegionBooleanOperationsSample.cs
   2:   * © Andrey Bushman, 2014 
   3:   */
   4:  using System;
   5:  using System.Collections.Generic;
   6:  using System.Linq;
   7:   
   8:  #if AUTOCAD
   9:  using cad = Autodesk.AutoCAD.ApplicationServices.Application;
  10:  using Ap = Autodesk.AutoCAD.ApplicationServices;
  11:  using Db = Autodesk.AutoCAD.DatabaseServices;
  12:  using Ed = Autodesk.AutoCAD.EditorInput;
  13:  using Rt = Autodesk.AutoCAD.Runtime;
  14:  using Gm = Autodesk.AutoCAD.Geometry;
  15:  using Wn = Autodesk.AutoCAD.Windows;
  16:  using Hs = Autodesk.AutoCAD.DatabaseServices.HostApplicationServices;
  17:  using Us = Autodesk.AutoCAD.DatabaseServices.SymbolUtilityServices;
  18:  #endif
  19:   
  20:  [assembly: Rt.CommandClass(typeof(Bushman.CAD.Experiences
  21:    .RegionBooleanOperationsSample))]
  22:   
  23:  namespace Bushman.CAD.Experiences {
  24:    public class RegionBooleanOperationsSample {
  25:      const String cmdGroup = "Bushman";
  26:      [Rt.CommandMethod(cmdGroup, "CreateRegions", Rt.CommandFlags.Modal)]
  27:      public void CreateRegions() {
  28:        Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
  29:        if(doc == null || doc.IsDisposed)
  30:          return;
  31:        using(doc.LockDocument()) {
  32:          Ed.Editor ed = doc.Editor;
  33:          Db.Database db = doc.Database;
  34:   
  35:          Gm.Point2d[] points_1 = new Gm.Point2d[]{
  36:            new Gm.Point2d(0,0),
  37:            new Gm.Point2d(0,70),
  38:            new Gm.Point2d(10,70),
  39:            new Gm.Point2d(10,10),
  40:            new Gm.Point2d(40,10),
  41:            new Gm.Point2d(40,70),
  42:            new Gm.Point2d(50,70),
  43:            new Gm.Point2d(50,10),
  44:            new Gm.Point2d(80,10),
  45:            new Gm.Point2d(80,70),
  46:            new Gm.Point2d(90,70),
  47:            new Gm.Point2d(90,0)
  48:          };
  49:          Db.Region region_1 = CreateRegion(points_1, 10);
  50:   
  51:          Gm.Point2d[] points_2 = new Gm.Point2d[]{          
  52:            new Gm.Point2d(0,70),
  53:            new Gm.Point2d(90,70),
  54:            new Gm.Point2d(90,60),
  55:            new Gm.Point2d(0,60)
  56:          };
  57:          Db.Region region_2 = CreateRegion(points_2, 50);
  58:   
  59:          using(Db.Transaction tr = db.TransactionManager.StartTransaction()) {
  60:            Db.BlockTable bt = tr.GetObject(db.BlockTableId, Db.OpenMode.ForRead)
  61:              as Db.BlockTable;
  62:            Db.BlockTableRecord ms = tr.GetObject(bt[Db.BlockTableRecord
  63:              .ModelSpace], Db.OpenMode.ForWrite) as Db.BlockTableRecord;
  64:            ms.AppendEntity(region_1);
  65:            tr.AddNewlyCreatedDBObject(region_1, true);
  66:            ms.AppendEntity(region_2);
  67:            tr.AddNewlyCreatedDBObject(region_2, true);
  68:   
  69:            Db.DBText text = new Db.DBText();
  70:            text.SetDatabaseDefaults();
  71:            text.Height = 5;
  72:            text.TextString = "Base regions";
  73:            text.Position = new Gm.Point3d(0, 80, 0);
  74:            ms.AppendEntity(text);
  75:            tr.AddNewlyCreatedDBObject(text, true);
  76:   
  77:            Int32 dx = 120;
  78:            Int32 x = dx;
  79:   
  80:            foreach(String name in Enum.GetNames(typeof(Db.BooleanOperationType)
  81:              )) {
  82:              Db.Region region_1Clone = region_1.Clone() as Db.Region;
  83:              Db.Region region_2Clone = region_2.Clone() as Db.Region;
  84:              Db.DBText textClone = text.Clone() as Db.DBText;
  85:              textClone.TextString = String.Format("The {0} operation", name);
  86:   
  87:              Gm.Point3d acPt3d = new Gm.Point3d(0, 0, 0);
  88:              Gm.Vector3d acVec3d = acPt3d.GetVectorTo(new Gm.Point3d(x, 0,
  89:                0));
  90:   
  91:              region_1Clone.TransformBy(Gm.Matrix3d.Displacement(acVec3d));
  92:              region_2Clone.TransformBy(Gm.Matrix3d.Displacement(acVec3d));
  93:              textClone.TransformBy(Gm.Matrix3d.Displacement(acVec3d));
  94:   
  95:              region_2Clone.BooleanOperation((Db.BooleanOperationType)Enum
  96:                .Parse(typeof(Db.BooleanOperationType), name), region_1Clone);
  97:   
  98:              if(!region_2Clone.IsNull && region_1Clone.IsNull)
  99:                region_2Clone.ColorIndex = 80;
 100:   
 101:              ms.AppendEntity(region_1Clone);
 102:              tr.AddNewlyCreatedDBObject(region_1Clone, true);
 103:              ms.AppendEntity(region_2Clone);
 104:              tr.AddNewlyCreatedDBObject(region_2Clone, true);
 105:              ms.AppendEntity(textClone);
 106:              tr.AddNewlyCreatedDBObject(textClone, true);
 107:   
 108:              x += dx;
 109:            }
 110:            tr.Commit();
 111:          }
 112:        }
 113:        doc.SendStringToExecute("_ZOOM _E ", true, false, false);
 114:      }
 115:   
 116:      internal Db.Region CreateRegion(Gm.Point2d[] points, Int32 colorIndex) {
 117:        if(points == null)
 118:          throw new ArgumentNullException("points");
 119:        if(points.Length == 0)
 120:          throw new ArgumentException("points.Length == 0");
 121:        using(Db.Polyline pline = new Db.Polyline(12)) {
 122:          pline.SetDatabaseDefaults();
 123:          for(int i = 0; i < points.Length; i++)
 124:            pline.AddVertexAt(i, points[i], 0, 0, 0);
 125:          pline.Closed = true;
 126:          Db.Region region = Db.Region.CreateFromCurves(
 127:            new Db.DBObjectCollection { pline }).Cast<Db.Region>().First();
 128:          region.SetDatabaseDefaults();
 129:          region.ColorIndex = colorIndex;
 130:          return region;
 131:        }
 132:      }
 133:    }
 134:  }

Результат выглядит следующим образом:



InternalsVisibleTo и Intellysience

Порой наши библиотеки могут содержать некоторый функционал, который мы активно используем в составе сборки, но который мы бы не хотели делать общедоступным. Такие методы и классы, как правило, объявляются с модификатором доступа internal. Если программист желает разрешить доступ к этому функционалу из внешних сборок, то в целевой сборке он помечает их как дружественные, при помощи атрибута сборки InternalsVisibleTo. Однако может случиться так, что в дружественной сборке Intellysience откажется показывать то, что в целевой было объявлено с модификатором internal. При этом компиляция проектов по прежнему будет проходить благополучно.

Например, программист может писать тесты для своего плагина AutoCAD, разместив код этих тестов в отдельном проекте. В этом случае, дабы не объявлять весь тестируемый функционал как public, он может быть объявлен как internal, после чего в коде плагина добавляется соответствующий атрибут сборки:

   1:  #if DEBUG
   2:  // Объявляем сборку acad_plugin_tests дружественной
   3:  [assembly: InternalsVisibleTo("acad_plugin_tests")] 
   4:  #endif

Т.о. проект acad_plugin_tests содержащий тесты, объявляется дружественным для отладочного варианта нашей сборки, что даёт нам возможность добраться до функционала, подлежащего тестированию. При компиляции Release версии сборка acad_plugin_tests уже не будет объявлена дружественной.

Однако, если вы вдруг обнаружите, что в процессе написания кода в дружественной сборке не отображаются элементы, помеченные в целевой как internal:


то для решения этой проблемы следует удалить *.suo файлы, находящиеся в каталоге нашего решения, рядом с *.sln файлом:


Как видим, теперь в коде дружественной сборки Intellysience отображает в т.ч. и internal элементы, дополнительно помечая их "сердечком".