Архитектору и проектировщику|Тепло- и звукоизоляция URSA.RU
| Правила | Регистрация | Пользователи | Поиск | Сообщения за день | Все разделы прочитаны |  Справка по форуму | Файлообменник |

Вернуться   Форум DWG.RU > Программное обеспечение > Программирование > .NET > C#.NET. Переопределение ручек (Grip Overrule).

C#.NET. Переопределение ручек (Grip Overrule).

Ответ
Поиск в этой теме
Непрочитано 17.05.2013, 15:33 #1
C#.NET. Переопределение ручек (Grip Overrule).
Do$
 
AutoCAD/Civil3D LISP/C#
 
Санкт-Петербург
Регистрация: 15.08.2008
Сообщений: 1,683

Разбираюсь с кодом отсюда: http://through-the-interface.typepad...ace/overrules/
Обнаружил весьма неприятный момент - обработчик события на открытие/закрытие документа добавляется в DocumentManager дважды.
Получается это потому, что при запуске команды "GOO" сперва вызывается конструктор без параметров класса GripVectorOverrule, в котором прописано добавление обработчика, потом создается экземпляр класса theOverrule, снова вызывается конструктор без параметров, снова добавляется обработчик. Понятно, что тут ошибка программиста, но вот как это лучше исправить? Может надо по-другому класс переделать? Что это вообще за рекурсивная конструкция по созданию объекта внутри объекта (и вызов команды тут же)?

Вот код, описывающий кусок класса GripVectorOverrule:

Код:
[Выделить все]
  // This class is instantiated by AutoCAD for each document when
    // a command is called by the user the first time in the context
    // of a given document. In other words, non static data in this
    // class is implicitly per-document!

    // Этот класс имеет экземпляры в Автокаде в каждом документе когда
    // команда запускается пользователем в первый раз в контексте
    // текущего документа. Другими словами, нестатические данные в этом
    // классе неявно за документ! (???)

    public class GripVectorOverrule : GripOverrule
    {
        // A static pointer to our overrule instance
        static public GripVectorOverrule theOverrule = new GripVectorOverrule();

        // A flag to indicate whether we're overruling
        // Флаг индикации включенного переопределения
        // Начальное значение - ложь
        static bool overruling = false;

        // gripdata for each selected polyline
        // Данные о ручках выбранных полилиний
        // Начальное значение - новый пустой словарь
        static Dictionary<ObjectId, GripDataCollection> _ents_handled = new Dictionary<ObjectId, GripDataCollection>();
         
        // Конструктор по умолчанию запускается 2 раза!
        public GripVectorOverrule()
        {
            // Set event handlers on documents to get access to
            // OnImpliedSelectionChanged

            DocumentCollection dm = Application.DocumentManager;
            // Обработка создания документа (открытия?)
            dm.DocumentCreated += new DocumentCollectionEventHandler(dm_DocumentCreated);            
            // Обработка уничтожения документа (закрытия?)
            dm.DocumentToBeDestroyed += new DocumentCollectionEventHandler(dm_DocumentToBeDestroyed);

            // Attach handler to currently loaded documents
            foreach (Document doc in dm)
                doc.ImpliedSelectionChanged += new EventHandler(doc_ImpliedSelectionChanged);
        }

        void dm_DocumentCreated(object sender, DocumentCollectionEventArgs e)
        {
            e.Document.ImpliedSelectionChanged += new EventHandler(doc_ImpliedSelectionChanged);
            // Добавил сигнализацию о том, что сработал обработчик
            Application.ShowAlertDialog("Create: " + e.Document.Name);
        }

        void dm_DocumentToBeDestroyed(object sender, DocumentCollectionEventArgs e)
        {
            e.Document.ImpliedSelectionChanged -= new EventHandler(doc_ImpliedSelectionChanged);
            // Добавил сигнализацию о том, что сработал обработчик
            Application.ShowAlertDialog("Destroyed: " + e.Document.Name);
        }

        void doc_ImpliedSelectionChanged(object sender, EventArgs e)
        {
            // Check for empty selection on current document
            Document doc = Application.DocumentManager.MdiActiveDocument;
            PromptSelectionResult res = doc.Editor.SelectImplied();

            // If nothing selected, it's a good time to reset GripData
            // dictionary
            if (res != null)
                if (res.Value == null) GripVectorOverrule.ResetAllGrips();
        }

        //<Тут много кода>

        [CommandMethod("GOO")]
        public void GripOverruleOnOff()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            //Если включено переопределение
            if (overruling)
                //Удаление переопределения, созданного в этом приложении
                ObjectOverrule.RemoveOverrule(RXClass.GetClass(typeof(Autodesk.AutoCAD.DatabaseServices.Polyline)), GripVectorOverrule.theOverrule);
            else
                //Иначе - добавление переопределения, созданного в этом приложении
                ObjectOverrule.AddOverrule(RXClass.GetClass(typeof(Autodesk.AutoCAD.DatabaseServices.Polyline)), GripVectorOverrule.theOverrule, true);

            //Изменение флага на противоположный
            overruling = !overruling;

            //Изменение статического свойства Overruling на противоположное
            GripOverrule.Overruling = overruling;

            //Вывод статусного сообщения в комстроку
            ed.WriteMessage("\nGrip overruling turned {0}.", overruling ? "on" : "off");
        }
    }


Схематично, это выглядит так:
Код:
[Выделить все]
 public class Class
{
   public static theClass = new Class();

   [CommandMethod("Command")]
   public void Method()
   {
      <Код, который использует Class.theClass>
   }
}
У меня, честно говоря, мозг заворачивается от такой конструкции

P.S. Название темы изменил на более подходящее.

Последний раз редактировалось Do$, 20.05.2013 в 15:14.
Просмотров: 3763
 
Непрочитано 17.05.2013, 19:37
1 | #2
bargool


 
Регистрация: 16.08.2006
Санкт-Петербург
Сообщений: 501
<phrase 1=


Ага. Вначале инстанцируется для открытого документа, затем при обращении к GripVectorOverrule.theOverrule
Быстрое предположение, но что если заменить GripVectorOverrule.theOverrule на this? Непонятно, зачем здесь используется статическое свойство
И ещё, для пущей удобочитаемости оборачивайте код в [ CODE ][ CPP ][ /CPP ][ /CODE ] (кнопка "C++"), пожалуйста
Вообще, лучше вынести Overrule-зависимый код в отдельный класс, определить для него private поле в классе, где команда, ну а дальше уже поступать исходя из того, что требуется. Всё ИМХО
__________________
Алексей

Последний раз редактировалось bargool, 17.05.2013 в 19:44.
bargool вне форума  
 
Автор темы   Непрочитано 18.05.2013, 00:47
#3
Do$

AutoCAD/Civil3D LISP/C#
 
Регистрация: 15.08.2008
Санкт-Петербург
Сообщений: 1,683
Отправить сообщение для Do$ с помощью Skype™


Вот и я предположил, что кто-то решил "почесать правое ухо левой пяткой". Уж очень какая-то замудреная конструкция получилась. Просто думал, мало ли, может в этом есть какой-то смысл, который мне пока неведом.
Попробую переделать по-человечески, спасибо!
Do$ вне форума  
 
Автор темы   Непрочитано 19.05.2013, 21:35
#4
Do$

AutoCAD/Civil3D LISP/C#
 
Регистрация: 15.08.2008
Санкт-Петербург
Сообщений: 1,683
Отправить сообщение для Do$ с помощью Skype™


Получилось!
Некоторые выводы:
1. При запуске команды, создается объект - экземпляр класса, содержащего команду, путем вызова конструктора этого класса без параметров. В этот конструктор можно поместить методы, которые будут выполняться перед запуском команды.
2. Все статические данные этого класса будут общими для всего приложения, нестатические - только для документа, из которого запущена команда.
А еще, похоже, что оверрулинг плохо дружит с вертикальными приложениями Отбой. Код определения геометрии ручек оказался косячный.

Последний раз редактировалось Do$, 20.05.2013 в 02:18.
Do$ вне форума  
 
Непрочитано 20.05.2013, 12:57
#5
bargool


 
Регистрация: 16.08.2006
Санкт-Петербург
Сообщений: 501
<phrase 1=


Делитесь кодом с общественностью, если это возможно
1 и 2 подтверждаю
__________________
Алексей
bargool вне форума  
 
Непрочитано 20.05.2013, 14:11
#6
Дима_

Продуман
 
Регистрация: 22.02.2007
Питер
Сообщений: 2,843


Цитата:
Сообщение от Do$ Посмотреть сообщение
2. Все статические данные этого класса будут общими для всего приложения, нестатические - только для документа, из которого запущена команда.
То ли я не понял, то-ли это немного не так - скорее у тебя перезаписывается класс при вызове команды, если создание объекта задать ДО CommandMethodAttribute, а из команды только использовать его методы (неважно статические или нет), то да - создастся он на момент 1 вызова (естественно если не включен в интерфес инициализации при загрузке), но будут они в любом документе (сессии). У меня, в большинстве случаев, все окна создаются 1 раз, а по закрытию прячутся до следующего вызова - и рабатают естественно до "конца" автокада.
__________________
Когда в руках молоток все вокруг кажется гвоздями.
Дима_ вне форума  
 
Автор темы   Непрочитано 20.05.2013, 15:03
#7
Do$

AutoCAD/Civil3D LISP/C#
 
Регистрация: 15.08.2008
Санкт-Петербург
Сообщений: 1,683
Отправить сообщение для Do$ с помощью Skype™


Цитата:
Сообщение от bargool Посмотреть сообщение
Делитесь кодом с общественностью, если это возможно
Да не вопрос

Код:
[Выделить все]
 using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.GraphicsInterface;
using Autodesk.AutoCAD.EditorInput;
using System.Collections.Generic;
using System;



namespace comsPLine
{
    // Derived class for grip handling 

    public class myGripData : GripData
    {

        // Object id of the original entity
        public ObjectId m_key = ObjectId.Null;

        // Progressive grip number
        public int m_index = 0;

        // Previous radius at this grip vertex
        public Double m_radius = 0.0;

        // Original grip location
        public Point3d m_original_point = Point3d.Origin;

        public override void OnGripStatusChanged(ObjectId entityId, GripData.Status newStatus)
        {
            if (newStatus == Status.GripAbort)
            {
                // Revert grips to original location
                GripVectorOverrule.ResetGrips(entityId);
            }
        }

        public override bool ViewportDraw(ViewportDraw worldDraw, ObjectId entityId, GripData.DrawType type, Point3d? imageGripPoint, int gripSizeInPixels)
        {
            // Calculate the size of the glyph in WCS
            Point2d glyphSize = worldDraw.Viewport.GetNumPixelsInUnitSquare(this.GripPoint);
            Double glyphHeight = (gripSizeInPixels / glyphSize.Y);
            Double glyphWeight = (gripSizeInPixels / glyphSize.X);

            // Transform to viewport
            //Matrix3d e2w = worldDraw.Viewport.EyeToWorldTransform;
            //Point3d pt = this.GripPoint.TransformBy(e2w);

            Matrix3d mx3d = worldDraw.Viewport.ModelToEyeTransform;
            Point3d pt = this.GripPoint.TransformBy(mx3d);            

            // Draw a glyph
            Point3dCollection pnts = new Point3dCollection();

            // triangle arrow down
            //pnts.Add(new Point3d(pt.X - glyphHeight, pt.Y + glyphHeight, pt.Z));
            //pnts.Add(new Point3d(pt.X, pt.Y - glyphHeight, pt.Z));
            //pnts.Add(new Point3d(pt.X + glyphHeight, pt.Y + glyphHeight, pt.Z));
            //pnts.Add(new Point3d(pt.X - glyphHeight, pt.Y + glyphHeight, pt.Z));

            // rectangle
            pnts.Add(new Point3d(pt.X - glyphWeight, pt.Y - glyphHeight, pt.Z));
            pnts.Add(new Point3d(pt.X + glyphWeight, pt.Y - glyphHeight, pt.Z));
            pnts.Add(new Point3d(pt.X + glyphWeight, pt.Y + glyphHeight, pt.Z));
            pnts.Add(new Point3d(pt.X - glyphWeight, pt.Y + glyphHeight, pt.Z));
            pnts.Add(new Point3d(pt.X - glyphWeight, pt.Y - glyphHeight, pt.Z));

            

            worldDraw.Geometry.DeviceContextPolygon(pnts);
            
            return base.ViewportDraw(worldDraw, entityId, type, imageGripPoint, gripSizeInPixels);
        }
    }

    

    public class GripVectorOverrule : GripOverrule
    { 
        // gripdata for each selected polyline
        public static Dictionary<ObjectId, GripDataCollection> _ents_handled = new Dictionary<ObjectId, GripDataCollection>();

        /// <summary>
        /// Called when entity is first selected.
        /// Analyze it and return alternative grips data collection if
        /// we must handle it.
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="grips"></param>
        /// <param name="curViewUnitSize"></param>
        /// <param name="gripSize"></param>
        /// <param name="curViewDir"></param>
        /// <param name="bitFlags"></param>
        public override void GetGripPoints
            (Entity entity, GripDataCollection grips, double curViewUnitSize, int gripSize, Vector3d curViewDir, GetGripPointsFlags bitFlags)
        {
            bool bValid = false;

            // get polyline object
            Autodesk.AutoCAD.DatabaseServices.Polyline p = entity as Autodesk.AutoCAD.DatabaseServices.Polyline;

            // check if the polyline is of the type we search for.
            // In the final version it may contain some xdata, maybe
            // holding a fixed radius value, to allow other plines to
            // work as expected.
            // However, we must always check if the geometry is still
            // valid. We should also check if the linear segments are
            // tangent to the arcs, but for the moment, we'll check if
            // we have a pattern of alternating line and arc segments,
            // starting with a line

            bValid = csMath.IsPolylineRoundedEdge(p);

            if (bValid)
            {
                // seems right, gather line segments
                LineSegment3d[] lsegs = new LineSegment3d[p.NumberOfVertices];
                int nSegs = 0;

                for (int i = 0; i < p.NumberOfVertices; i++)
                {
                    if (p.GetSegmentType(i) == SegmentType.Line) lsegs[nSegs++] = p.GetLineSegmentAt(i);
                }

                // Gather start, end and intersection points
                Point3d[] pnts = new Point3d[p.NumberOfVertices];

                // Current radius for inner intersection
                Double[] rads = new Double[p.NumberOfVertices];
                int nPnts = 0;

                // At least two linear segments are needed
                if (nSegs > 1)
                {
                    // Working plane for intersecting segments
                    Plane pPlane = p.GetPlane();

                    // Temporary intersecting point
                    Point3d pnt = Point3d.Origin;

                    // First point: if the polyline id closed, the first pt
                    // is the intersection of the first and last segments
                    if (p.Closed)
                    {
                        if ( csMath.CheckIntersect(lsegs[0], lsegs[nSegs - 1], pPlane, ref pnt))
                        {
                            pnts[nPnts] = pnt;
                            rads[nPnts] = csMath.GetFilletRadius(lsegs[0], lsegs[nSegs - 1], pPlane);
                            nPnts++;
                        }
                        else
                        {
                            // Polyline with overlapping segments? Not good for us
                            bValid = false;
                        }
                    }
                    else pnts[nPnts++] = lsegs[0].StartPoint;

                    // Add intersection points for internal segments
                    for (int i = 0; i < (nSegs - 1); i++)
                    {
                        if (csMath.CheckIntersect(lsegs[i], lsegs[i + 1], pPlane, ref pnt))
                        {
                            pnts[nPnts] = pnt;
                            rads[nPnts] = csMath.GetFilletRadius(lsegs[i], lsegs[i + 1], pPlane);
                            nPnts++;
                        }
                        else
                        {
                            // No intersection, overlapping or co-linear segments?
                            bValid = false;
                        }
                    }

                    // Last point: add if not a closed pline
                    if (!p.Closed) pnts[nPnts++] = lsegs[nSegs - 1].EndPoint;

                    if (bValid) // Still valid?
                    {
                        // Everything seems ok, add grip points
                        // Use also a private GripDataCollection, don't mess
                        // with AutoCAD's
                        GripDataCollection myGrips = new GripDataCollection();

                        for (int i = 0; i < nPnts; i++)
                        {
                            myGripData gd = new myGripData();
                            gd.m_index = i;
                            gd.m_key = entity.ObjectId;
                            gd.GripPoint = gd.m_original_point = pnts[i];
                            gd.m_radius = rads[i];
                            gd.GizmosEnabled = true;
                            grips.Add(gd);
                            myGrips.Add(gd);
                        }

                        // Check for same entity already in list. If so,
                        // remove it
                        _ents_handled.Remove(entity.ObjectId);

                        // Add to our managed list
                        _ents_handled.Add(entity.ObjectId, myGrips);
                    }
                }
                else bValid = false;
            }

            if (!bValid)
            {
                // Polyline not good for us, let it be treated as usual
                base.GetGripPoints(entity, grips, curViewUnitSize, gripSize, curViewDir, bitFlags);
            }
        }

        /// <summary>
        /// Handler called during grip streching.
        /// Rebuild polyline on the fly given modified grips
        /// </summary>
        /// <param name="entity">Clone of the original entity to manage</param>
        /// <param name="grips">Altered grips</param>
        /// <param name="offset">Vector of displacement from original grip position</param>
        /// <param name="bitFlags"></param>
        public override void MoveGripPointsAt(Entity entity, GripDataCollection grips, Vector3d offset, MoveGripPointsFlags bitFlags)
        {
            // Retrieve from the streched grips the ObjectId/dictionary
            // key
            // shouldn't happen. Programmer's paranoia
            if (grips.Count == 0) return;
            myGripData gda = grips[0] as myGripData;
            if (gda != null) // It's one of our grips
            {
                if (_ents_handled.ContainsKey(gda.m_key))
                {
                    // Retrieve our original grip collection
                    GripDataCollection original_grips = _ents_handled[gda.m_key];

                    // Correct grips with offset information
                    foreach (myGripData gdo in grips)
                    {
                        // Retrieve original grip and set current
                        // dragged location
                        myGripData gd = original_grips[gdo.m_index] as myGripData;
                        gd.GripPoint = gd.m_original_point + offset;
                    }
                    
                    // Recalc polyline from new sets of grips
                    csMath.RebuildPolyline(entity as Autodesk.AutoCAD.DatabaseServices.Polyline, original_grips);

                    // Done, don't fall into standard handling
                    return;
                }
            }

            // Revert to standard handling
            base.MoveGripPointsAt(entity, grips, offset, bitFlags);
        }

        /// <summary>
        /// Reset grip position for the selected entity.
        /// Revert position to initial location, useful when aborting
        /// a grip handling.
        /// </summary>
        /// <param name="entity_id">objectid/key of the selected entity</param>
        public static void ResetGrips(ObjectId entity_id)
        {
            // Reset grips to their original point
            if (_ents_handled.ContainsKey(entity_id))
            {
                GripDataCollection grips = _ents_handled[entity_id];
                foreach (myGripData gdo in grips) gdo.GripPoint = gdo.m_original_point;
            }
        }

        public static void ResetAllGrips()
        {
            // Clear handled list, to be called when selection is cleared
            _ents_handled.Clear();
        }                
    }

    // This class is instantiated by AutoCAD for each document when
    // a command is called by the user the first time in the context
    // of a given document. In other words, non static data in this
    // class is implicitly per-document!
    public class Command
    {
        // A static pointer to our overrule instance
        static GripVectorOverrule overRule = new GripVectorOverrule();

        // A flag to indicate whether we're overruling
        static bool overruling = false;

        public Command()
        {
            // Set event handlers on documents to get access to
            // OnImpliedSelectionChanged
            DocumentCollection dm = Application.DocumentManager;
            dm.DocumentCreated += new DocumentCollectionEventHandler(dm_DocumentCreated);
            dm.DocumentToBeDestroyed += new DocumentCollectionEventHandler(dm_DocumentToBeDestroyed);

            // Attach handler to currently loaded documents
            foreach (Document doc in dm)
                doc.ImpliedSelectionChanged += new EventHandler(doc_ImpliedSelectionChanged);
        }

        [CommandMethod("GOO")]
        public void GripOverruleOnOff()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            //bool overruling = GripVectorOverrule.Overruling;
            
            if (overruling)
                Overrule.RemoveOverrule(RXClass.GetClass(typeof(Autodesk.AutoCAD.DatabaseServices.Polyline)), overRule);

            else
                Overrule.AddOverrule(RXClass.GetClass(typeof(Autodesk.AutoCAD.DatabaseServices.Polyline)), overRule, true);

            
            overruling = !overruling;
            GripVectorOverrule.Overruling = overruling;

            ed.WriteMessage("\nGrip overruling turned {0}.", overruling ? "on" : "off");
        }       

        void dm_DocumentCreated(object sender, DocumentCollectionEventArgs e)
        {
            e.Document.ImpliedSelectionChanged += new EventHandler(doc_ImpliedSelectionChanged);
        }

        void dm_DocumentToBeDestroyed(object sender, DocumentCollectionEventArgs e)
        {
            e.Document.ImpliedSelectionChanged -= new EventHandler(doc_ImpliedSelectionChanged);
        }

        void doc_ImpliedSelectionChanged(object sender, EventArgs e)
        {
            // Check for empty selection on current document
            Document doc = Application.DocumentManager.MdiActiveDocument;
            PromptSelectionResult res = doc.Editor.SelectImplied();

            // If nothing selected, it's a good time to reset GripData
            // dictionary
            if (res != null)
                if (res.Value == null) GripVectorOverrule.ResetAllGrips();
        }
    }

}

//The second contains mathematical support functions – that Massimo has developed over many years, so it’s especially nice of him to share these (he even translated the bulk of the comments from Italian! :-).

// (C) Copyright 2008-2012 by COMSAL Srl - RSM
//
// COMSAL PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
// COMSAL SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE.
// COMSAL SRL DOES NOT WARRANT THAT THE OPERATION OF THE
// PROGRAM WILL BE UNINTERRUPTED OR ERROR FREE.
//
// Description:
// Classe di supporto per funzioni geometriche comuni
//
// History:
// 02.10.2008 [MC] Prima stesura
//
//

Код:
[Выделить все]
 using System;
using System.Collections.Generic;
using System.Text;

using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.GraphicsSystem;

namespace comsPLine
{
    // Support class for non-trigonometric math
    public class csCosDir
    {
        public Double cx;
        public Double cy;
        public Double cz;
        public csCosDir(){ cx = cy = cz = 0.0; }

        public csCosDir(Point2d p1, Point2d p2)
        {
            Double dx = p2.X - p1.X;
            Double dy = p2.Y - p1.Y;
            Double dd = Math.Sqrt(dx * dx + dy * dy);

            if (dd > csMath.dEpsilon)
            {
                cx = dx / dd;
                cy = dy / dd;
            }
        }



        public csCosDir(Point3d p1, Point3d p2)
        {
            Double dx = p2.X - p1.X;
            Double dy = p2.Y - p1.Y;
            Double dz = p2.Z - p1.Z;
            Double dd = Math.Sqrt(dx * dx + dy * dy + dz * dz);

            if (dd > csMath.dEpsilon)
            {
                cx = dx / dd;
                cy = dy / dd;
                cz = dz / dd;
            }
        }
    }

    // Generic info holder for polyline vertex with fillet
    public class myVertex
    {
        public Double radius; // Fillet radius (requested or real)
        public Point3d pOrg;  // Origin of the fillet arc
        public Point3d p1;    // Arc start point (end of 1st segment)
        public Point3d p2;    // Arc end point (start of 2nd segment)
        public Point3d pc;    // Arc mid point laying on the bisectrix
        public Double bulge;  // Bulge parameter as usual: B=2*H/D

        public myVertex()
        {
            pOrg = p1 = p2 = pc = Point3d.Origin;
            radius = bulge = 0.0;
        }

        public myVertex(Point3d pInit)
        {
            pOrg = p1 = p2 = pc = pInit;
            radius = bulge = 0.0;
        }
    }

    // Common math functions
    public static class csMath
    {
        public const Double PI = 3.141592653589793; // More decimal places than Math.PI

        public const Double dEpsilon = 0.001; // General precision allowed for identity

        /// <summary>
        /// Check if the polyline topology is compatible with a
        /// sequence of segments with internal rounded edges.
        /// Used to activate an alternate grip handling where
        /// user can control segment vertexes through apparent
        /// intersections.
        /// </summary>
        /// <param name="p">Polyline to check</param>
        /// <returns>Compatibility with alternate grip handling</returns>
        public static bool IsPolylineRoundedEdge(Polyline p)
        {
            bool result = false;
            if (p != null)
            {
                // Quick check if it contains at least one arc and two
                // segments, that means at least 4 points also check
                // for planar entity
                if (p.IsPlanar && p.HasBulges && (p.NumberOfVertices > 3))
                {
                    SegmentType prevType = p.GetSegmentType(0);

                    // First segment must be a line
                    if (prevType == SegmentType.Line)
                    {
                        result = true;
                        for (int i = 1; i < p.NumberOfVertices; i++)
                        {
                            SegmentType currType = p.GetSegmentType(i);
                            if (currType == SegmentType.Line || currType == SegmentType.Arc)
                            {
                                if (currType == prevType)
                                {
                                    result = false;
                                    break;
                                }
                                prevType = currType;
                            }
                        }

                        // Check if also the last segment is a line, or if
                        // it's an arc, the polyline must be closed, like
                        // a rounded box
                        if (prevType == SegmentType.Arc && !p.Closed) result = false;
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// Rebuild polyline with new grip information
        /// </summary>
        /// <param name="polyline">Polyline to be rebuilt</param>
        /// <param name="original_grips">Grips collection</param>

        public static void RebuildPolyline(Polyline p, GripDataCollection grips)
        {
            try
            {
                int nPnt = grips.Count;

                // Create vertex info from gripdata collection
                myVertex[] vFill = new myVertex[nPnt];

                // Initializa first and last vertex with first and
                // last grip points
                vFill[0] = new myVertex(grips[0].GripPoint);
                vFill[nPnt - 1] = new myVertex(grips[grips.Count - 1].GripPoint);

                // Retrieve adjacent segments (three consecutive points)
                for (int i = 1; i < (nPnt - 1); i++)
                {
                    myGripData gd_prev = grips[i - 1] as myGripData;
                    myGripData gd_center = grips[i] as myGripData;
                    myGripData gd_next = grips[i + 1] as myGripData;

                    // New vertex info
                    vFill[i] = new myVertex();
                    vFill[i].radius = gd_center.m_radius;

                    // Calc fillet information, if possible with current
                    // radius and segments length
                    if (!csMath.LinesFillet(gd_center.GripPoint, gd_prev.GripPoint, gd_next.GripPoint, ref vFill[i], true))
                    {
                        // Unable to find a solution, remove fillet information
                        vFill[i].p1 = vFill[i].p2 = vFill[i].pc = vFill[i].pOrg = gd_center.GripPoint;
                        vFill[i].radius = 0.0;
                    }
                }

                if (p.Closed)
                {
                    // Add vertex information on last grip
                    myGripData gd_prev = grips[grips.Count - 2] as myGripData;
                    myGripData gd_center = grips[grips.Count - 1] as myGripData;
                    myGripData gd_next = grips[0] as myGripData;

                    // Last point may coincident with first or not
                    if (gd_center.GripPoint.DistanceTo(gd_prev.GripPoint) < csMath.dEpsilon)
                        gd_prev = grips[grips.Count - 3] as myGripData;

                    vFill[nPnt - 1] = new myVertex();
                    vFill[nPnt - 1].radius = gd_center.m_radius;

                    // Calc fillet information, if possible with current
                    // radius and segments length
                    if (!csMath.LinesFillet(gd_center.GripPoint, gd_prev.GripPoint, gd_next.GripPoint, ref vFill[nPnt - 1], false))
                    {
                        // Unable to find a solution, remove fillet information
                        vFill[nPnt - 1].p1 = vFill[nPnt - 1].p2 = vFill[nPnt - 1].pc = vFill[nPnt - 1].pOrg = gd_center.GripPoint;
                        vFill[nPnt - 1].radius = 0.0;
                    }

                    // Recalc vertex information on first grip
                    gd_prev = grips[grips.Count - 1] as myGripData;
                    gd_center = grips[0] as myGripData;
                    gd_next = grips[1] as myGripData;

                    // Last point may coincident with first or not
                    if (gd_center.GripPoint.DistanceTo(gd_prev.GripPoint) < csMath.dEpsilon) gd_prev = grips[grips.Count - 2] as myGripData;
                    vFill[0] = new myVertex();
                    vFill[0].radius = gd_center.m_radius;

                    // Calc fillet information, if possible with
                    // current radius and segments length
                    if (!csMath.LinesFillet(gd_center.GripPoint, gd_prev.GripPoint, gd_next.GripPoint, ref vFill[0], false))
                    {
                        // Unable to find a solution, remove fillet information
                        vFill[0].p1 = vFill[0].p2 = vFill[0].pc = vFill[0].pOrg = gd_center.GripPoint;
                        vFill[0].radius = 0.0;
                    }
                }

                // Everything seem ok, rebuild polyline
                bool bIsClosed = p.Closed; // remember if original was closed

                // Clear current points definitions
                p.Closed = false;
                while (p.NumberOfVertices > 1)
                    p.RemoveVertexAt(0); // Cannot completely clear points

                // Add new points and segments definition
                for (int i = 0; i < (nPnt - 1); i++)
                {
                    // Add linesegment only if lenght is not null
                    if (vFill[i].p2.DistanceTo(vFill[i + 1].p1) > csMath.dEpsilon)
                        p.AddVertexAt(p.NumberOfVertices, new Point2d(vFill[i].p2.X, vFill[i].p2.Y), 0, 0, 0);

                    // End point always valid with bulge information if needed
                    p.AddVertexAt(p.NumberOfVertices, new Point2d(vFill[i + 1].p1.X, vFill[i + 1].p1.Y), vFill[i + 1].bulge, 0, 0);
                }
                p.RemoveVertexAt(0); // Remove last of old points

                // If closed re-add first point with bulge
                if (bIsClosed)
                {
                    // Add linesegment only if length is not null
                    if (vFill[nPnt - 1].p2.DistanceTo(vFill[0].p1) > csMath.dEpsilon)
                        p.AddVertexAt(p.NumberOfVertices, new Point2d(vFill[nPnt - 1].p2.X, vFill[nPnt - 1].p2.Y), 0, 0, 0);

                    // End point always valid with bulge information if needed
                    p.AddVertexAt(p.NumberOfVertices, new Point2d(vFill[0].p1.X, vFill[0].p1.Y), vFill[0].bulge, 0, 0);

                    p.Closed = true; // Restore closed status
                }
            }
            catch { }
        }

        /// <summary>
        /// Compute fillet information for two converging segments.
        /// The fillet information is returned through a myVertex class
        /// that will hold information about the arc start point on the
        /// first segment, the end point on the second segment, the arc
        /// origin and the arc midpoint on the bisetrix
        /// </summary>
        /// <param name="pOrg">Intersection point of the segments</param>
        /// <param name="p1">Start point of the first segment</param>
        /// <param name="p2">End point of the second segment</param>
        /// <param name="v">Info class with requested radius to be
        /// filled with fillet info</param>
        /// <returns>True if a fillet is possible, false if no solution
        /// can be found</returns>
        public static bool LinesFillet(Point3d pOrg, Point3d p1, Point3d p2, ref myVertex v, bool bAllowRadiusReduction)
        {
            try
            {
                bool bInversionOccured = false;

                // Check point validity
                Double d1 = pOrg.DistanceTo(p1);
                Double d2 = pOrg.DistanceTo(p2);
                Double dd = p1.DistanceTo(p2);

                if (d1 < csMath.dEpsilon) return false; // Segment p1-porg null
                if (d2 < csMath.dEpsilon) return false; // Segment p2-porg null
                if (dd < csMath.dEpsilon) return false; // Overlapping segments
                if (Math.Abs(dd - d1 - d2) < csMath.dEpsilon) return false; // Co-linear segments
                if (v.radius < csMath.dEpsilon) return false; // Radius too small

                // Sort points to keep the smaller angle always as p1-pOrg-p2
                if (csMath.DistFromLine(pOrg, p1, p2) < 0)
                {
                    // Point p2 is on the left side of vector pOrg->p1
                    // should be on the right: switch points
                    Point3d pp = p2;
                    p2 = p1;
                    p1 = pp;
                    bInversionOccured = true;  // Mark that inversion occurred
                }

                // Get sine/cosine coefficients
                csCosDir r1 = new csCosDir(pOrg, p1);
                csCosDir r2 = new csCosDir(pOrg, p2);

                // Get bisetrix where arc origin and midpoint lay
                csCosDir rb = new csCosDir();
                
                // Shouldn't happen, co-linear segments already checked
                if (!csMath.FindBisect(r1, r2, ref rb)) return false;

                // The bisetrix and the projection of the arc origin on
                // either segment forms a rect triangle. Align segment
                // coefficient to X axis, so the radius would be aligned
                // to Y axis
                Double cxr = rb.cx * r1.cx + rb.cy * r1.cy;
                Double cyr = -rb.cx * r1.cy + rb.cy * r1.cx;

                // Get hypotenuse given the radius and sine coefficient
                Double ipo = v.radius / cyr;

                // Get other catete: distance from pOrg and the fillet
                // points on segments
                Double l1 = ipo * cxr;


                // If allowed, check if fillet point lays outside the
                // smallest segment lenght
                if (bAllowRadiusReduction)
                {
                    Double dmin = (d1 < d2) ? d1 : d2;
                    if (l1 > dmin)
                    {
                        // Reduce radius to keep fillet inside segment
                        v.radius = dmin * cyr / cxr;

                        // Use new radius for computation
                        ipo = v.radius / cyr;
                        l1 = ipo * cxr;
                    }
                }

                // Given the length, get arc start and end points on
                // each segment. Beware of segment switch, if occurred
                if (bInversionOccured)
                {
                    v.p2 = new Point3d(pOrg.X + l1 * r1.cx, pOrg.Y + l1 * r1.cy, 0.0);
                    v.p1 = new Point3d(pOrg.X + l1 * r2.cx, pOrg.Y + l1 * r2.cy, 0.0);
                }

                else
                {
                    v.p1 = new Point3d(pOrg.X + l1 * r1.cx, pOrg.Y + l1 * r1.cy, 0.0);
                    v.p2 = new Point3d(pOrg.X + l1 * r2.cx, pOrg.Y + l1 * r2.cy, 0.0);
                }

                // Get arc midpoint on bisetrix
                v.pc = new Point3d(pOrg.X + (ipo - v.radius) * rb.cx, pOrg.Y + (ipo - v.radius) * rb.cy, 0.0);

                // Get arc origin
                v.pOrg = new Point3d(pOrg.X + ipo * rb.cx, pOrg.Y + ipo * rb.cy, 0.0);

                // Compute bulge using the formula B = 2*H/D, where D is
                // the chord and H the distance of the chord midpoint
                // and the arc midpoint
                Double D = v.p1.DistanceTo(v.p2);
                Double H = Math.Abs(csMath.DistFromLine(v.p1, v.p2, v.pc));
                if (D > csMath.dEpsilon) v.bulge = 2 * H / D;

                // Bulge should be positive for counterclockwise arcs and
                // negative for clockwise. Adjust sign according with
                // segment order switch, if occurred
                if (!bInversionOccured) v.bulge = -v.bulge;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("[LinesFillet]" + ex.Message);
                return false;
            }
            return true;
        }

        /// <summary>
        /// Get plausible fillet radius from two converging linesegments
        /// </summary>
        /// <param name="l1">First segment</param>
        /// <param name="l2">Second segment</param>
        /// <param name="pPlane">Working plane</param>
        /// <param name="pOut">Resulting point, if available</param>
        /// <returns></returns>
        public static Double GetFilletRadius(LineSegment3d l1, LineSegment3d l2, Plane pPlane)
        {
            Double result = 0.0;

            // Get 2d points on working plane
            Point2d p1 = l1.StartPoint.Convert2d(pPlane);
            Point2d p2 = l1.EndPoint.Convert2d(pPlane);
            Point2d q1 = l2.StartPoint.Convert2d(pPlane);
            Point2d q2 = l2.EndPoint.Convert2d(pPlane);
            Point2d pInt = Point2d.Origin;

            // Get intersection point
            IntersectLinesState res = IntersectLines(p1, p2, q1, q2, out pInt);
            if (res == IntersectLinesState.ApparentIntersect || res == IntersectLinesState.RealIntersect)
            {
                // Get the endpoints closest to the intersection
                Point2d l1_end = p1.GetDistanceTo(pInt) < p2.GetDistanceTo(pInt) ? p1 : p2;
                Point2d l2_end = q1.GetDistanceTo(pInt) < q2.GetDistanceTo(pInt) ? q1 : q2;

                // Throw a perpendicular vector on the endpoints closest
                // to the intersection
                Line2d lp1 = new LineSegment2d(p1, p2).GetPerpendicularLine(l1_end);
                Line2d lp2 = new LineSegment2d(q1, q2).GetPerpendicularLine(l2_end);

                // Get intersection of projection lines (center of fillet,
                // if segments already have fillet)
                Point2d[] pInts = lp1.IntersectWith(lp2);

                if (pInts != null)
                {
                    // Get distance of center of fillet from endpoints
                    Double r1 = l1_end.GetDistanceTo(pInts[0]);
                    Double r2 = l2_end.GetDistanceTo(pInts[0]);

                    // If the two segments were already rounded, the two
                    // distances should be the same and equals to the fillet
                    // radius. If the segments have been moved or stretched,
                    // the distances may differ. We take the lower radius
                    // as the minimum fillet radius available
                    result = Math.Min(r1, r2);
                }
            }
            return result;
        }

        /// <summary>
        /// Check intersection for two segments on plane pPlane
        /// </summary>
        /// <param name="l1">First segment</param>
        /// <param name="l2">Second segment</param>
        /// <param name="pPlane">Working plane</param>
        /// <param name="pOut">Resulting point, if available</param>
        /// <returns></returns>
        public static bool CheckIntersect(LineSegment3d l1, LineSegment3d l2, Plane pPlane, ref Point3d pOut)
        {
            bool result = false;

            // Get 2d points on working plane
            Point2d p1 = l1.StartPoint.Convert2d(pPlane);
            Point2d p2 = l1.EndPoint.Convert2d(pPlane);
            Point2d q1 = l2.StartPoint.Convert2d(pPlane);
            Point2d q2 = l2.EndPoint.Convert2d(pPlane);
            Point2d pInt = Point2d.Origin;
            IntersectLinesState res = IntersectLines(p1, p2, q1, q2, out pInt);

            if (res == IntersectLinesState.ApparentIntersect || res == IntersectLinesState.RealIntersect)
            {
                pOut = new Point3d(pPlane, pInt);
                result = true;
            }
            return result;
        }

        /// <summary>
        /// Possible results for IntersectLines() function
        /// </summary>
        public enum IntersectLinesState
        {
            InvalidPoints = -1,    // Invalid points (coincident?)
            RealIntersect = 0,     // Real intersection found, pInt valid
            ApparentIntersect = 1, // Apparent inters. found, pInt valid
            NoIntersection = 2,    // Segments are parallel
            OverLapping = 3,       // Segments are overlapping
            Colinear = 4           // Segments are co-linear
        }

        /// <summary>
        /// Try to get intersection point of two segment.
        /// The intersection may be real or apparent.
        /// The resulting point is
        /// </summary>
        /// <param name="p1">Start point of first segment</param>
        /// <param name="p2">End point of first segment</param>
        /// <param name="q1">Start point of second segment</param>
        /// <param name="q2">End point of second segment</param>
        /// <param name="pInt">[out] Resulting intersection point</param>
        /// <returns>Result validity state</returns>
        public static IntersectLinesState IntersectLines
            (
          Point2d p1, Point2d p2, // First segment
          Point2d q1, Point2d q2, // Second segment
          out Point2d pInt        // Intersecting point
        )
        {
            IntersectLinesState result = IntersectLinesState.NoIntersection;
            pInt = Point2d.Origin;

            // Get sine/cosine coefficients for the two segments
            csCosDir r1 = new csCosDir(p1, p2);
            csCosDir r2 = new csCosDir(q1, q2);

            // Check coefficients, if points are coincident,
            // segments are null
            if ((r1.cx == 0.0 && r1.cy == 0.0) || (r2.cx == 0.0 && r2.cy == 0.0))
            {
                // Coincident points? Invalid segments data
                return IntersectLinesState.InvalidPoints;
            }
            
            // Intersection coefficients
            Double a1 = r1.cx, a2 = r1.cy;
            Double b1 = -r2.cx, b2 = -r2.cy;
            Double c1 = q1.X - p1.X, c2 = q1.Y - p1.Y;

            // Get denominator, if null, segments have same direction
            Double dden = a1 * b2 - a2 * b1;

            if (Math.Abs(dden) > csMath.dEpsilon)
            {
                // Valid denominator, lines are not parallels or co-linear
                // now, intersection linear parameter may be get either
                // from first or second segment. Do both to check for
                // real or apparent intersection.

                // Linear parameter for second segment
                Double tt = (c1 * b2 - c2 * b1) / dden;

                // Linear parameter for first segment
                Double vv = (c2 * a1 - c1 * a2) / dden;

                // Intersection point from first segment parameter
                pInt = new Point2d(q1.X + r2.cx * vv, q1.Y + r2.cy * vv);

                // To be 'real', intersection point must lay on both
                // segments (parameter from 0 to 1). Otherwise, we set
                // it as 'apparent'. Get normalized linear parameter
                // (at this stage denominator are intrinsicly valid,
                // no need to check for DIVBYZERO)
                tt = tt / p1.GetDistanceTo(p2);
                vv = vv / q1.GetDistanceTo(q2);

                // Check if both coefficients lay within 0 and 1
                if (tt > -csMath.dEpsilon && tt < (1 + csMath.dEpsilon) && vv > -csMath.dEpsilon && vv < (1 + csMath.dEpsilon))
                    result = IntersectLinesState.RealIntersect;
                else result = IntersectLinesState.ApparentIntersect;                
            }

            else
            {
                // Segments are parallel or co-linear (have same direction).
                // Check coefficients for a new connecting segment with
                // points taken from both original segment. If coefficients
                // are the same, segments are co-linear, otherwise are
                // parallel
                csCosDir rx;

                // Avoid coincident points
                if (p1.GetDistanceTo(q1) > csMath.dEpsilon) rx = new csCosDir(p1, q1);
                else rx = new csCosDir(p1, q2);

                if (Math.Abs(rx.cx - r1.cx) < csMath.dEpsilon && Math.Abs(rx.cy - r1.cy) < csMath.dEpsilon)
                {
                    // Same coefficient, segments lay on the same vector.
                    // Check if there is any overlapping by checking distances
                    // from the two ends of a segments and another end.
                    // Sum of distances must be equal or higher than segment
                    // length, otherwise they are partially overlapping
                    Double ll = p2.GetDistanceTo(p1);
                    bool bOver1 = q1.GetDistanceTo(p1) + q1.GetDistanceTo(p2) > ll + csMath.dEpsilon;
                    bool bOver2 = q2.GetDistanceTo(p1) + q2.GetDistanceTo(p2) > ll + csMath.dEpsilon;
                    result = bOver1 && bOver2 ? IntersectLinesState.Colinear : IntersectLinesState.OverLapping;
                }

                else
                {
                    // Parallel segments, no intersection
                    result = IntersectLinesState.NoIntersection;
                }

            }
            return result;
        }

        /// <summary>
        /// Given a vector going from L1 to L2, get the projected
        /// distance of point P from the vector.
        /// If distance is positive, the point is on the right side
        /// of the vector, if distance is negative, the point is on
        /// the left side of the vector.
        /// Function assume all points lay on the same plane
        /// </summary>
        /// <param name="l1">Vector origin</param>
        /// <param name="l2">Vector direction</param>
        /// <param name="p">Point to get distance from</param>
        /// <returns>Projected distance of P from vector L1->L2</returns>
        public static Double DistFromLine(Point3d l1, Point3d l2, Point3d p)
        {
            csCosDir c = new csCosDir(l1, l2);
            return (((p.Y - l1.Y) * c.cx) - ((p.X - l1.X) * c.cy));
        }

        /// <summary>
        /// Get bisetrix coefficients of two vectors.
        /// Vectors are provided as coefficients and the order must
        /// be given in counterclockwise order.
        /// This is a 2d computation, vectors must lay on the same plane
        /// </summary>
        /// <param name="r1">First vector</param>
        /// <param name="r2">Second vector</param>
        /// <param name="rb">[out]Resulting coefficients</param>
        /// <returns>True if bisetrix has been found, false if not (parallel vectors?)</returns>
        public static bool FindBisect(csCosDir r1, csCosDir r2, ref csCosDir rb)
        {
            bool result = true;
            // Coefficients may be considered as points of a limited
            // space that goes from -1 to 1
            // Origin 0,0 is the intersection of the two vectors and
            // origin of the bisetrix
            // Quick check for vectors (almost) co-linear
            Double diste =
              Math.Sqrt(
                (r2.cx - r1.cx) * (r2.cx - r1.cx) +
                (r2.cy - r1.cy) * (r2.cy - r1.cy)
              );

            if (diste < csMath.dEpsilon)
            {
                // Vectors have very similar coefficients, use alternate
                // algorithm for a borderline situation
                Double dcx = (r2.cx + r1.cx) * 0.5;
                Double dcy = (r2.cy + r1.cy) * 0.5;
                Double dd = Math.Sqrt(dcx * dcx + dcy * dcy);

                if (dd < csMath.dEpsilon)
                {
                    // Really same coefficients, could be co-linear or
                    // parallel but cannot say because we don't have
                    // points on segments, just direction
                    result = false;
                }

                else
                {
                    // Denominator valid, use point distance from vector
                    // to get position of the first vector
                    Double distc = r1.cx * (r2.cy - r1.cy) - r1.cy * (r2.cx - r1.cx);
                    Double dSign = (distc < 0) ? -1.0 : 1.0;
                    rb.cx = dSign * dcx / dd;
                    rb.cy = dSign * dcy / dd;
                }
            }
            else
            {
                // Vectors normally spaced, use simpler formula
                Double dcx = (r2.cx - r1.cx);
                Double dcy = (r2.cy - r1.cy);
                Double dd = Math.Sqrt(dcx * dcx + dcy * dcy);
                rb.cx = dcy / dd;
                rb.cy = -dcx / dd;
            }

            return result;
        }
    }
}


Дима_, ничего не понял, если честно
Do$ вне форума  
 
Непрочитано 20.05.2013, 15:22
#8
Дима_

Продуман
 
Регистрация: 22.02.2007
Питер
Сообщений: 2,843


Цитата:
Сообщение от Do$ Посмотреть сообщение
Дима_, ничего не понял, если честно
Offtop: тогда забей
__________________
Когда в руках молоток все вокруг кажется гвоздями.
Дима_ вне форума  
Ответ
Вернуться   Форум DWG.RU > Программное обеспечение > Программирование > .NET > C#.NET. Переопределение ручек (Grip Overrule).

Размещение рекламы
Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск