Сообщество программистов Autodesk в СНГ
ADN Club => AutoCAD .NET API => Тема начата: Alex от 28-01-2014, 21:07:42
-
Здравствуйте.
Ситуация следующая: на чертеже есть блок (BlockReference). Хотелось бы дать пользователю возможность этот блок вращать, но при этом не разрешать его перемещение (а в идеале - и удаление, и все остальное, кроме вращения).
Идеальным вариантом было бы вообще заблокировать слой - но тогда и вращать не получается.(
Можно, конечно, написать отдельную процедуру и сделать свою обвязку для вращения блока - но как-то не хочется, потому что в AutoCAD все и так здорово работает.
----
Первой моей мыслью было посмотреть события BlockReference. Навскидку нашлись Modified и OpenedForModify - но при попытке изменить кооринату блока внутри этих событий AutoCAD начинает ОЧЕНЬ меня не любить.(
На форуме AutoCAD я несколько раз (тема №1 (http://forums.autodesk.com/t5/NET/Modified-Event-of-BlockReference-Causes-Crash/td-p/3732554), тема №2 (http://forums.autodesk.com/t5/Autodesk-ObjectARX/openedForModify/td-p/1288307)) встретил хмурую точку зрения, гласящую, что отменить перемещение в событии вообще никак нельзя, а желающие вместо этого могут запоминать ID измененных объектов, чтобы потом вручную положить их на прежнее место - например, в событии Editor.EnteringQuiencentState.
----
Собственно вопросы:
1. Можно ли исходную задачу (запретить перемещение блока, разрешив при этом вращение) решить более простым способом?
2. Если нет - можно ли отменить перемещение объекта непосредственно в момент изменения (в рамках какого-либо события), а не после него?
Спасибо.
-
1. Можно ли исходную задачу (запретить перемещение блока, разрешив при этом вращение) решить более простым способом?
Простых способов нет.
2. Если нет - можно ли отменить перемещение объекта непосредственно в момент изменения (в рамках какого-либо события), а не после него?
Нет, если речь идет о событии.
НО!!! Можно попробовать пойти совсем другим путем, который называется Overrule (а точнее TransformOverrule). Я не нашел готового примера на .NET. Вот пример с использованием ObjectARX: Locking an Entity in AutoCAD using ObjectARX (http://adndevblog.typepad.com/autocad/2012/06/locking-an-entity-in-autocad-using-objectarx.html) (я его еще не успел перевести).
Тебе придется переопределить метод TransfomBy и анализировать матрицу Matrix3d передаваемую в этот метод на предмет того только ли операция поворота затребована.
-
Я не нашел готового примера на .NET. Вот пример с использованием ObjectARX: Locking an Entity in AutoCAD using ObjectARX (я его еще не успел перевести).
Полагаю, что ему не сильно поможет пример, написанный на native C++. Ежели вы всё же решите переводить обозначенную вами заметку, то наличие альтернативного кода на C# было бы не лишним (имхо).
Собственно вопросы...
Чертёж могут открыть на компьютере, где твой плагин отсутствует и спокойно выполнить все те операции, которые ты желаешь запретить (скорее всего именно так и будут поступать, когда возникнет желание обойти созданные тобою ограничения). Так что способ не надёжен (имхо).
-
Ежели вы всё же решите переводить обозначенную вами заметку, то наличие альтернативного кода на C# было бы не лишним (имхо).
Статью обязательно переведу и сделаю вариант с использованием AutoCAD .NET API. В первую очередь потому, что не нашел нигде готового примера использующего подмену TransfomBy в TransformOverrule.
Так что способ не надёжен (имхо).
Абсолютно с тобой согласен. Единственный надежный, но очень неудобный вариант - это Custom Entity (т.е. чистый ObjectARX). Но это приведет в последствии к таким сложностям, что лучше уж разрешить пользователю делать с этим блоком всё что угодно.
-
Спасибо за ответы и рекомендацию.
Я еще не пробовал применять этот пример, но если в .NET API есть реализации этих классов Object ARX, то я попробую все прикрутить.
Если получится - напишу, если не получится - напишу тем более.)
P. S. Я понимаю, что при желании с чертежом можно будет сделать все, что угодно. Но использовать этот чертеж без плагина будет бессмысленно.
Объекты на плане - это только отображение данных; изменение чертежа к изменениям данных приводить не должно.
Во всяком случае, пока.
Во всяком случае, я так думаю.)
-
Я еще не пробовал применять этот пример, но если в .NET API есть реализации этих классов Object ARX, то я попробую все прикрутить.
Есть. Класс TransformOverrule.
Во всяком случае, я так думаю.)
Я бы добавил: "Во всяком случае, я пока так думаю."
-
Я бы добавил: "Во всяком случае, я пока так думаю."
Именно такой смысл и предполагался.)
В конце концов, программирование - это же не точная наука! ;D
-
Да, с ходу взять ObjectARX не вышло.)
Но я нашел два примера у Kean Walmsley: TransformOverrule (http://through-the-interface.typepad.com/through_the_interface/2009/08/gluing-a-point-to-an-autocad-curve-using-overrules-from-net-part-1.html), GripOverrule (http://through-the-interface.typepad.com/through_the_interface/2009/08/knowing-when-an-autocad-object-is-grip-edited-using-overrules-in-net.html).
TransformOverrule отлично работает, обычное перемещение блоков блокируется.
А вот с GripOverrule получается печаль: при переопределении стандартной GripOverrule почему-то пропадает сам ползунок вращения. Просто пропадает, и все.
Есть интересный ответ (http://through-the-interface.typepad.com/through_the_interface/2009/08/knowing-when-an-autocad-object-is-grip-edited-using-overrules-in-net.html#comment-6a00d83452464869e20168e54f4956970c) Kean'а посетителю блога:
...if you overrule grips for blocks - even with an implementation of your methods that just supermessages to the base class - the existing grip move operation appears to stop working (although you could basically implement it yourself, if you needed to overrule grip move for blocks and have additional behaviour to want to implement).
Либо я где-то ошибся, либо для того, чтобы все заработало, нужно вручную переписать методы GripOverrule...
-
Там была какая-то ошибка в кодах по GripOverrule. Вот тут я разбирался с ним: http://forum.dwg.ru/showthread.php?t=99011&highlight=gripoverrule
Насколько я помню, первая ошибка была в построении командного класса, из-за которого лишние действия происходили. А вторая - неправильное вычисление положения переопределенных ручек, из-за чего они улетали "в космос". Возможно, что и в Вашем случае ползунок не пропадает, а находится где-то в стороне.
-
запретить перемещение блока, разрешив при этом вращение
Может можно средствами ArxDbg это сделать
-
Может можно средствами ArxDbg это сделать
Поясни как ты себе это представляешь? И кстати написана она на чистом C++ с использованием ObjectARX, и использует такие вещи, которых в AutoCAD .NET API нет.
-
Вот тут я разбирался с ним
Большое спасибо за ссылку, этот пример очень помог.
Как выяснилось, дело было в переопределении метода GetGripPoints().
В интерфейсе Autodesk.AutoCAD.DatabaseServices.GripOverrule присутствует два метода GetGripPoints:
public virtual void GetGripPoints(Entity entity, Point3dCollection gripPoints, IntegerCollection snapModes, IntegerCollection geometryIds);
public virtual void GetGripPoints(Entity entity, GripDataCollection grips, double curViewUnitSize, int gripSize, Vector3d curViewDir, GetGripPointsFlags bitFlags);
В примере (http://through-the-interface.typepad.com/through_the_interface/2009/08/knowing-when-an-autocad-object-is-grip-edited-using-overrules-in-net.html), который я нашел в блоге Kean'а, переопределялся только первый метод GetGripPoints(). При этом у блока остается ручка, отвечающая за перемещение, но пропадает ручка, отвечающая за поворот.
А в том примере (http://forum.dwg.ru/showthread.php?t=99011&highlight=gripoverrule), который предоставил Дмитрий, переопределялся второй метод GetGripPoints(). При этом у блока сохраняется как ручка, отвечающая за перемещение, так и ручка, отвечающая за поворот.
P.S. Только что проверял. Создаю реализацию интерфейса GripOverrule, которую потом активизирую:
GripOverrule.AddOverrule(RXClass.GetClass(typeof(BlockReference)), new GripVectorOverrule(), true);
GripVectorOverrule.Overruling = true;
В первом случае пишу реализацию класса GripVectorOverrule так:
public class GripVectorOverrule : GripOverrule
{
public override void GetGripPoints(Entity entity, Point3dCollection gripPoints, IntegerCollection snapModes, IntegerCollection geometryIds)
{
base.GetGripPoints(entity, gripPoints, snapModes, geometryIds);
}
}
При таком определении ручка вращения пропадает.
А можно попробовать такую реализацию GripVectorOverrule:
public class GripVectorOverrule : GripOverrule
{
public override void GetGripPoints (Entity entity, GripDataCollection grips, double curViewUnitSize, int gripSize, Vector3d curViewDir, GetGripPointsFlags bitFlags)
{
base.GetGripPoints(entity, grips, curViewUnitSize, gripSize, curViewDir, bitFlags);
}
}
При таком определении ручка вращения остается на месте.
Пока так... Сейчас еще один пост напишу, отчетный.)
-
Итого в сухом остатке сделал так:
1) в TransformOverrule решение о том, перемещать блок или нет, принимается на основе слоя, на котором расположен блок (других блоков на этом слое нет; но в принципе при необходимости можно проверять и имя блока);
2) в GripOverrule я не стал анализировать матрицы, а просто убрал ручку, отвечающую за перемещение блока (этот прием изложен здесь (http://adndevblog.typepad.com/autocad/2012/11/remove-insertion-grip-point-using-overrule.html)).
Теперь двигать блоки вроде бы нельзя. По крайней мере, случайно блок не сдвинешь - а мне как раз это и требовалось.)
Код:
public class Commands : IExtensionApplication
{
public void Initialize()
{
ObjectOverrule.AddOverrule(RXClass.GetClass(typeof(BlockReference)), new TransformVectorOverrule(), true);
GripOverrule.AddOverrule(RXClass.GetClass(typeof(BlockReference)), new GripVectorOverrule(), true);
TransformVectorOverrule.Overruling = true;
GripVectorOverrule.Overruling = true;
}
}
public class TransformVectorOverrule : TransformOverrule
{
public override void TransformBy(Entity entity, Matrix3d matrix)
{
if ((entity as BlockReference) != null && ((entity as BlockReference).Layer == myLayerName))
{
return;
}
base.TransformBy(entity, matrix);
return;
}
}
public class GripVectorOverrule : GripOverrule
{
public override void GetGripPoints (Entity entity, GripDataCollection grips, double curViewUnitSize, int gripSize, Vector3d curViewDir, GetGripPointsFlags bitFlags)
{
base.GetGripPoints(entity, grips, curViewUnitSize, gripSize, curViewDir, bitFlags);
if ((entity as BlockReference) != null && ((entity as BlockReference).Layer == myLayerName))
{
GripData toRemove = null;
foreach (GripData gd in grips)
{
if (gd.GripPoint == (entity as BlockReference).Position)
{
toRemove = gd;
break;
}
}
if (toRemove != null)
grips.Remove(toRemove);
}
}
}
P.S. Остались неясными два вопроса.
1. Вместо
TransformVectorOverrule.Overruling = true;
GripVectorOverrule.Overruling = true;
можно с тем же успехом написать
Overrule.Overruling = true;
Нужно ли было переопределять свойство Overruling для классов TransformVectorOverrule и GripVectorOverrule? У меня такое чувство, что да...
2. В статье про удаление ручек (http://adndevblog.typepad.com/autocad/2012/11/remove-insertion-grip-point-using-overrule.html) для удаления ручки из списка почему-то используется довольно вычурная конструкция
GripData toRemove = null;
foreach (GripData gd in grips)
{
if (gd.GripPoint == (entity as BlockReference).Position)
{
toRemove = gd;
break;
}
}
if (toRemove != null)
grips.Remove(toRemove);
Я попробовал переписать ее чуть короче:
foreach (GripData gd in grips)
{
if (gd.GripPoint == (entity as BlockReference).Position)
{
grips.Remove(gd);
break;
}
}
Это вроде тоже работает. Зачем авторы примера вводят дополнительную переменную toRemove?
-
TransformBy - это не только перемещение, но и поворот. Поэтому без анализа матрицы в TransformVectorOverrule (IMHO) ты запрещаешь для этого блока команду _ROTATE, что не есть правильно в твоей постановке задачи.
-
поворот
Здесь (http://www.theswamp.org/index.php?topic=40546.msg458445#msg458445) написано, что для матрицы преобразования
(
(a b c x)
(d e f y)
(g h j z)
(0 0 0 1)
)
угол поворота будет вычисляться по формуле
θ = atan(d/a)
Это соответствует действительности?
Я попробовал изменить условие:
if ((entity as BlockReference) != null && ((entity as BlockReference).Layer == XChange.surappDrillersLayerName) && (System.Math.Atan(matrix[1,0]/matrix[0,0]) == 0))
Теперь поворачивается в том числе и командой _ROTATE.
-
Всё несколько сложнее, как я понимаю. В матрице одновременно (!!!) может присутствовать три операции:
1) поворот;
2) масштабирование;
3) перенос.
Так что указанной тобой проверки недостаточно.
-
А не подскажете, где можно посмотреть спецификацию матрицы преобразования?
Она отличается от
(a b c x) (Sx·cosθ -Sy·sinθ 0 x)
(d e f y) = (Sx·sinθ Sy·cosθ 0 y)
(g h j z) ( 0 0 Sz z)
(0 0 0 1) ( 0 0 0 1)
(где (Sx, Sy, Sz) - коэффициенты масштабирования по осям, θ - угол поворота, (x y z) - координаты вектора параллельного переноса)?
Если отличается - тогда, конечно, такой проверки недостаточно.
-
UPD: хотя это, в принципе, не так критично. Вряд ли в моем случае понадобится команда _ROTATE.
Главная задача, в общем-то, уже решена.)
-
А не подскажете, где можно посмотреть спецификацию матрицы преобразования?
В документации ObjectARX:
ObjectARX Developer Guide > Basic Interaction with AutoCAD > Basic Interaction with AutoCAD > ObjectARX Global Utility Functions > Variables, Types, and Values Defined in ObjectARX > General Types and Definitions > Transformation Matrices
-
Если отличается - тогда, конечно, такой проверки недостаточно.
Даже если и не отличается, то всё-равно такой проверки недостаточно. Ты же не проверил, что x, y, z не равны 0 и a, e, j не равны 1.
-
Действительно, ошибка.
Просто я как-то и не подумал, как пользователь сможет одновременно применить к блоку не одну, а сразу несколько операций.
Это уже надо какую-то сложную команду писать... А я, наверное, не вполне корректно обрисовал свою задачу: мне нужно не вообще запретить изменения, а просто предотвратить случайные действия пользователя. Ну там, мышкой щелкнуть или еще чего...))
Описание матрицы попробую покурить...
-
Просто я как-то и не подумал, как пользователь сможет одновременно применить к блоку не одну, а сразу несколько операций.
Например, команда _ALIGN и он случайно этот блок выбрал.