private Extents3d GetExtents(Transaction tr, ObjectId[] ids)
        {
            var ext = new Extents3d();
            foreach (var id in ids)
            {
                var ent = tr.GetObject(id, OpenMode.ForRead) as Entity;
                if (ent != null)
                {
                    ext.AddExtents(ent.GeometricExtents);
                }
            }
            return ext;
        }
 
        private bool compareWithPatterns(string value, string[] patterns)
        {
            foreach (string p in patterns)
            {
                if (value.Contains(p))
                    return true;
            }
            return false;
        }
 
        private ObjectId FindTitle(Transaction tr, ObjectId[] ids, string[] patterns)
        {            
            foreach (ObjectId id in ids)
            {
                DBObject ent = tr.GetObject(id, OpenMode.ForRead);
                if (ent is DBText)
                {
                    DBText text = ent as DBText;
 
                    if (this.compareWithPatterns(text.TextString, patterns))
                        return id;                    
                }
                else if (ent is MText)
                {
                    MText mtext = ent as MText;
 
                    if (this.compareWithPatterns(mtext.Text, patterns))
                        return id;
                }
            }
 
            return ObjectId.Null;
        }
 
        private void FindDataContour(Editor ed, Transaction tr, ObjectId titleId, Extents3d selectionExtents, out Extents3d extents)
        {
            //get extents of title
            DBObject ent = tr.GetObject(titleId, OpenMode.ForRead);
            if (!ent.Bounds.HasValue)
                throw new System.Exception("Невозможно определить границы наименования");
 
            Extents3d titleExtents = ent.Bounds.Value;
 
            //get boundary of title
            DBObjectCollection objs = ed.TraceBoundary(titleExtents.MinPoint + (titleExtents.MaxPoint - titleExtents.MinPoint) / 2, true);
 
            if (objs.Count < 2)
                throw new System.Exception("Контур ячейки с наименованием не найден");
            
            Extents3d titleContourExtents = (objs[objs.Count - 1] as Entity).Bounds.Value;
 
            //calculate data extents
            extents = new Extents3d(
                titleContourExtents.MinPoint + (titleContourExtents.MaxPoint - titleContourExtents.MinPoint).OrthoProjectTo(new Vector3d(0, -1, 0)),
                titleContourExtents.MaxPoint + (selectionExtents.MaxPoint - titleContourExtents.MaxPoint).OrthoProjectTo(new Vector3d(0, -1, 0))
            );
        }
 
        private void AddAllProfile(Editor ed, ref AlxdProfileSource profileSource)
        {
            PromptSelectionOptions pso = new PromptSelectionOptions();
            pso.MessageForAdding = "Выберите один профиль целиком";
 
            PromptSelectionResult psres;
            psres = ed.GetSelection(pso);
 
            if (psres.Status != PromptStatus.OK)
            {
                ed.WriteMessage("\nНичего не выбрано.");
                return;
            }
            else
            {
                ObjectId[] objIds = psres.Value.GetObjectIds();
                if (objIds.Count() == 0)
                {
                    ed.WriteMessage("\nНичего не выбрано.");
                    return;
                }
 
                using (Transaction tr = ed.Document.TransactionManager.StartTransaction())
                {
                    Extents3d selectionExtents = GetExtents(tr, objIds);
 
                    profileSource.groundElevationsTitleObjectId = FindTitle(tr, objIds, this.groundElevationTitlePatterns);
                    profileSource.distanceBetweenElevationsTitleObjectId = FindTitle(tr, objIds, this.distanceBetweenElevationsTitlePatterns);
 
                    Extents3d groundElevationsDataExtents;
                    try
                    {
                        FindDataContour(ed, tr, profileSource.groundElevationsTitleObjectId, selectionExtents, out groundElevationsDataExtents);
 
                        TypedValue[] tvs = new TypedValue[] {
                                    new TypedValue((int)DxfCode.Start, "TEXT,MTEXT")
                                };
                        SelectionFilter filter = new SelectionFilter(tvs);
 
                        psres = ed.SelectCrossingWindow(groundElevationsDataExtents.MinPoint, groundElevationsDataExtents.MaxPoint);
 
                        if (psres.Status == PromptStatus.OK)
                        {
                            AlxdGroundElevations src = profileSource.groundElevations;
 
                            ObjectId[] tt = psres.Value.GetObjectIds().Where(item => !src.validGroundElevation.Keys.Contains(item) && !src.failedGroundElevation.Keys.Contains(item)).ToArray();
 
                            if (tt.Count() > 0)
                            {
                                AlxdGroundElevations tmp = ValidateGroundElevation(new ObjectIdCollection(tt));
                                if (tmp != null)
                                {
                                    profileSource.groundElevations.validGroundElevation = profileSource.groundElevations.validGroundElevation.Concat(tmp.validGroundElevation).ToDictionary(x => x.Key, x => x.Value);
                                    profileSource.groundElevations.failedGroundElevation = profileSource.groundElevations.failedGroundElevation.Concat(tmp.failedGroundElevation).ToDictionary(x => x.Key, x => x.Value);
                                }
                            }
                        }
                        else
                        {
                            ed.WriteMessage("Не удалось выбрать отметки земли в подвале профиля!");
                        }
                    }
                    catch (System.Exception ex)
                    {
                        ed.WriteMessage(ex.Message);
                    }
 
                    Extents3d distanceBetweenElevationsDataExtents;
                    try
                    {
                        FindDataContour(ed, tr, profileSource.distanceBetweenElevationsTitleObjectId, selectionExtents, out distanceBetweenElevationsDataExtents);
 
                        TypedValue[] tvs = new TypedValue[] {
                                    new TypedValue((int)DxfCode.Start, "TEXT,MTEXT,LINE,LWPOLYLINE")
                                };
                        SelectionFilter filter = new SelectionFilter(tvs);
 
                        psres = ed.SelectCrossingWindow(distanceBetweenElevationsDataExtents.MinPoint, distanceBetweenElevationsDataExtents.MaxPoint);
 
                        if (psres.Status == PromptStatus.OK)
                        {
                            AlxdDistanceBetweenElevations src = profileSource.distanceBetweenElevations;
 
                            ObjectId[] tt = psres.Value.GetObjectIds().Where(item =>
                                !src.validTextDistanceBetweenElevations.Keys.Contains(item) &&
                                !src.failedTextDistanceBetweenElevations.Keys.Contains(item) &&
                                !src.validLineDistanceBetweenElevations.Keys.Contains(item) &&
                                !src.failedLineDistanceBetweenElevations.Keys.Contains(item)).ToArray();
 
                            if (tt.Count() > 0)
                            {
                                AlxdDistanceBetweenElevations tmp = ValidateDistanceBetweenElevation(new ObjectIdCollection(tt));
                                if (tmp != null)
                                {
                                    profileSource.distanceBetweenElevations.validTextDistanceBetweenElevations = profileSource.distanceBetweenElevations.validTextDistanceBetweenElevations.Concat(tmp.validTextDistanceBetweenElevations).ToDictionary(x => x.Key, x => x.Value);
                                    profileSource.distanceBetweenElevations.failedTextDistanceBetweenElevations = profileSource.distanceBetweenElevations.failedTextDistanceBetweenElevations.Concat(tmp.failedTextDistanceBetweenElevations).ToDictionary(x => x.Key, x => x.Value);
                                    profileSource.distanceBetweenElevations.validLineDistanceBetweenElevations = profileSource.distanceBetweenElevations.validLineDistanceBetweenElevations.Concat(tmp.validLineDistanceBetweenElevations).ToDictionary(x => x.Key, x => x.Value);
                                    profileSource.distanceBetweenElevations.failedLineDistanceBetweenElevations = profileSource.distanceBetweenElevations.failedLineDistanceBetweenElevations.Concat(tmp.failedLineDistanceBetweenElevations).ToDictionary(x => x.Key, x => x.Value);
                                }
                            }
                        }
                        else
                        {
                            ed.WriteMessage("Не удалось выбрать расстояния между отметками в подвале профиля!");
                        }
                    }
                    catch (System.Exception ex)
                    {
                        ed.WriteMessage(ex.Message);
                    }
                    
                    tr.Commit();
                }
                
            }
        }