揭开"Overrule“的神秘面纱
本帖最后由 作者 于 2009-9-21 18:18:30 编辑一、从箭头开始
如果一直在关注Autodesk的动作,你会发现Autodesk在2010版引入的Overrule-规则重定义是一件多么让人激动的技术!
从置顶帖Autodesk提供的温度计的例子,你会发现什么?
对,这其中有自定义实体的影子,虽然只是子集(Autodesk据说在NetApi只会支持到这一步),但是真的很Cool
我们从一个简单的例子看看Overrule的实现流程吧
一)需求:
希望实现一个特殊的直线,在中点带有箭头,而且直线长度变化时箭头的尺寸不变
二)分析
简单的块定义是无法实现的,块缩放时,子实体同样会缩放,那么重定义一下显示规则
三)步骤
1、继承DrawableOverrule类就可以实现显示重定义,同样也可以实现其他形式的重定义,比如夹点、炸开等等
2、重载函数WorldDraw控制显示的行为
3、设置重定义过滤器,可以有多种方式,XData是使用起来最简单的方式
4、在加入实体时附着重定义过滤器对应的数据,这里就是相应程序名的XData
5、如果要在重定义时定义属性,比如箭头长度、颜色,同样可以把数据一并放在XData中
6、使Overrule生效
基本的步骤就是这样,下面是代码:using System;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.GraphicsInterface;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
namespace ArrowOverrule
{
//管理重定义
class TlsApplication : IExtensionApplication
{
//初始例程,重定义生效
void IExtensionApplication.Initialize()
{
Helper.OverruleStart();
Overrule.Overruling = true;
}
//终止例程,重定义失效
void IExtensionApplication.Terminate()
{
Helper.OverruleEnd();
Overrule.Overruling = false;
}
}
//静态类,存放常用的参数
static class Helper
{
//XData的应用程序注册名
public readonly static string RegAppName = "TlsCad.Arrow";
//箭头长度,暂时无法更改,可扩展功能
public static double ArrowLen = 5;
//重定义生效
//注意这里的实体类型和Overrule子类中必须严格对应
public static void OverruleStart()
{
Overrule.AddOverrule(RXObject.GetClass(typeof(Line)), LArrowDrawOverrule.TheOverrule, false);
}
//重定义失效
public static void OverruleEnd()
{
Overrule.RemoveOverrule(RXObject.GetClass(typeof(Line)), LArrowDrawOverrule.TheOverrule);
}
//让特定的实体附着XData,以便重定义重载可以过滤到该实体
public static void SetTo(Line line)
{
ResultBuffer rb =
new ResultBuffer(
new TypedValue[] {
new TypedValue((int)DxfCode.ExtendedDataRegAppName, RegAppName),
new TypedValue((int)DxfCode.ExtendedDataReal, ArrowLen) });
line.XData = rb;
}
/*Overrule的实现机理
* 1、继承相应的Overrule基类,并在子类中定义规则(即函数重载),并设置过滤条件
* 2、对你想重定义的实体按过滤条件附着数据
* 3、AutoCad在遇到这类特殊实体时按子类重载的函数去操控它
*/
public static void LArrow()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
PromptPointResult res1 = ed.GetPoint("\n请输入起点:");
if (res1.Status == PromptStatus.OK)
{
PromptPointOptions opts = new PromptPointOptions("\n请输入终点:");
opts.BasePoint = res1.Value;
opts.UseBasePoint = true;
PromptPointResult res2 = ed.GetPoint(opts);
if (res2.Status == PromptStatus.OK)
{
Database db = doc.Database;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTableRecord btr =
(BlockTableRecord)tr.GetObject(
db.CurrentSpaceId,
OpenMode.ForWrite,
false);
Line line = new Line(res1.Value, res2.Value);
btr.AppendEntity(line);
tr.AddNewlyCreatedDBObject(line, true);
RegAppTable rat =
(RegAppTable)tr.GetObject(
db.RegAppTableId,
OpenMode.ForRead,
false);
if (!rat.Has(RegAppName))
{
rat.UpgradeOpen();
RegAppTableRecord regapp = new RegAppTableRecord();
regapp.Name = RegAppName;
rat.Add(regapp);
tr.AddNewlyCreatedDBObject(regapp, true);
}
SetTo(line);
tr.Commit();
}
}
}
}
}
//显示重定义
public class LArrowDrawOverrule : DrawableOverrule
{
public static LArrowDrawOverrule TheOverrule = new LArrowDrawOverrule();
//设置重定义的过滤条件
public LArrowDrawOverrule()
{
SetXDataFilter(Helper.RegAppName);
}
//显示重载
public override bool WorldDraw(Drawable drawable, WorldDraw wd)
{
Line line = (Line)drawable;
Vector3d vec;
Point3d pnt;
if (line.Length == 0)
{
vec = Vector3d.XAxis;
pnt = line.StartPoint;
}
else
{
double cenpar = (line.StartParam + line.EndParam) / 2;
vec = line.GetFirstDerivative(cenpar).GetNormal();
pnt = line.GetPointAtParameter(cenpar);
}
Vector3d v1 = vec.TransformBy(Matrix3d.Rotation(Math.PI / 6, Vector3d.ZAxis, Point3d.Origin));
Vector3d v2 = vec.TransformBy(Matrix3d.Rotation(-Math.PI / 6, Vector3d.ZAxis, Point3d.Origin));
wd.Geometry.Draw(new Line(pnt, pnt + v1 * Helper.ArrowLen));
wd.Geometry.Draw(new Line(pnt, pnt + v2 * Helper.ArrowLen));
return base.WorldDraw(drawable, wd);
}
}
}
EdgeA.Color = Color.FromColorIndex(ColorMethod.ByAci, 1); // 使用 ACI 颜色索引设置
EdgeA.WorldDraw(wd);
为何我想让他显示为红色,却始终是默认的颜色呢? 我发现夹点重定义里:
移动箭头的时候,鼠标移动范围不能再那条直线附近很近的地方,不然移动不起。
收藏一下,慢慢研究,感谢 沙长.先收藏.以后再来看. 太棒了,这样就实现我想了很久的想法. 二、实现属性的附着
上一个代码中我们实现了带有一个固定箭头长度的直线,这里再提升一下功能
如果要实现不同的直线附加的箭头大小不同,这样可以么?如何实现?
其实仔细看下代码会发现这一行:
//箭头长度,暂时无法更改,可扩展功能
public static double ArrowLen = 5;
ArrowLen是我定义的存储箭头长度的静态变量,在上一个代码中并没有更改它,这一次就要用到它了:)
要实现这个功能,就必须把箭头长度附着在XData中
同时在重载函数WorldDraw中读取该数据,按照该值去确定对应实体的箭头长度
最后还要注意一点,XData是不能只附加应用程序名的
下面是实现的代码using System;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.GraphicsInterface;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
namespace ArrowOverrule
{
//管理重定义
class TlsApplication : IExtensionApplication
{
//初始例程,重定义生效
void IExtensionApplication.Initialize()
{
Helper.OverruleStart();
Overrule.Overruling = true;
}
//终止例程,重定义失效
void IExtensionApplication.Terminate()
{
Helper.OverruleEnd();
Overrule.Overruling = false;
}
}
//静态类,存放常用的参数
static class Helper
{
//XData的应用程序注册名
public readonly static string RegAppName = "TlsCad.Arrow";
//默认箭头长度
public static double ArrowLength = 5;
//重定义生效
//注意这里的实体类型和Overrule子类中必须严格对应
public static void OverruleStart()
{
Overrule.AddOverrule(RXObject.GetClass(typeof(Line)), LArrowDrawOverrule.TheOverrule, false);
}
//重定义失效
public static void OverruleEnd()
{
Overrule.RemoveOverrule(RXObject.GetClass(typeof(Line)), LArrowDrawOverrule.TheOverrule);
}
//让特定的实体附着XData,以便重定义重载可以过滤到该实体
public static void SetTo(Line line)
{
ResultBuffer rb =
new ResultBuffer(
new TypedValue[] {
new TypedValue((int)DxfCode.ExtendedDataRegAppName, RegAppName),
new TypedValue((int)DxfCode.ExtendedDataReal, ArrowLength) });
line.XData = rb;
}
/*Overrule的实现机理
* 1、继承相应的Overrule基类,并在子类中定义规则(即函数重载),并设置过滤条件
* 2、对你想重定义的实体按过滤条件附着数据
* 3、AutoCad在遇到这类特殊实体时按子类重载的函数去操控它
*/
public static void LArrow()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
PromptDoubleOptions optDouble = new PromptDoubleOptions("\n请输入箭头长度:");
optDouble.AllowNone = true;
optDouble.AllowZero = false;
optDouble.DefaultValue = ArrowLength;
optDouble.UseDefaultValue = true;
PromptDoubleResult resDouble = ed.GetDouble(optDouble);
if (resDouble.Status == PromptStatus.OK)
{
//改变箭头长度设定值
ArrowLength = resDouble.Value;
PromptPointResult resStartPoint = ed.GetPoint("\n请输入起点:");
if (resStartPoint.Status == PromptStatus.OK)
{
PromptPointOptions optEndPoint = new PromptPointOptions("\n请输入终点:");
optEndPoint.BasePoint = resStartPoint.Value;
optEndPoint.UseBasePoint = true;
PromptPointResult resEndPoint = ed.GetPoint(optEndPoint);
if (resEndPoint.Status == PromptStatus.OK)
{
Database db = doc.Database;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTableRecord btr =
(BlockTableRecord)tr.GetObject(
db.CurrentSpaceId,
OpenMode.ForWrite,
false);
Line line = new Line(resStartPoint.Value, resEndPoint.Value);
btr.AppendEntity(line);
tr.AddNewlyCreatedDBObject(line, true);
RegAppTable rat =
(RegAppTable)tr.GetObject(
db.RegAppTableId,
OpenMode.ForRead,
false);
if (!rat.Has(RegAppName))
{
rat.UpgradeOpen();
RegAppTableRecord regapp = new RegAppTableRecord();
regapp.Name = RegAppName;
rat.Add(regapp);
tr.AddNewlyCreatedDBObject(regapp, true);
}
//附着当前设定的箭头长度
SetTo(line);
tr.Commit();
}
}
}
}
}
}
//显示重定义
public class LArrowDrawOverrule : DrawableOverrule
{
public static LArrowDrawOverrule TheOverrule = new LArrowDrawOverrule();
//设置重定义的过滤条件
public LArrowDrawOverrule()
{
SetXDataFilter(Helper.RegAppName);
}
//显示重载
public override bool WorldDraw(Drawable drawable, WorldDraw wd)
{
Line line = (Line)drawable;
//读取箭头的长度
double len = (double)line.XData.AsArray().Value;
Vector3d vec;
Point3d pnt;
if (line.Length == 0)
{
vec = Vector3d.XAxis;
pnt = line.StartPoint;
}
else
{
double cenpar = (line.StartParam + line.EndParam) / 2;
vec = line.GetFirstDerivative(cenpar).GetNormal();
pnt = line.GetPointAtParameter(cenpar);
}
Vector3d v1 = vec.TransformBy(Matrix3d.Rotation(Math.PI / 6, Vector3d.ZAxis, Point3d.Origin));
Vector3d v2 = vec.TransformBy(Matrix3d.Rotation(-Math.PI / 6, Vector3d.ZAxis, Point3d.Origin));
//按实体附着的数据显示箭头的实际长度
wd.Geometry.Draw(new Line(pnt, pnt + v1 * len));
wd.Geometry.Draw(new Line(pnt, pnt + v2 * len));
return base.WorldDraw(drawable, wd);
}
}
}
在命令行输入:
命令: larr
请输入箭头长度 <5>:
请输入起点:
请输入终点:
命令:LARR
请输入箭头长度 <5>: 10
请输入起点:
请输入终点:
命令:LARR
请输入箭头长度 <10>: 15
请输入起点:
请输入终点:
谢谢分享,学习学习 三、夹点重定义
在前面的例子里,我们实现了一个中点附着箭头的直线
可能有这样的需求,希望箭头的位置可以在直线上任意移动,就像起点和终点一样可以让用户使用夹点去改变它的位置
这样的效果可以通过夹点重定义实现
夹点重定义主要的重载函数是:
GetGripPoints
定义夹点获取时的夹点位置
有两种形式的重载:
public override void GetGripPoints(Entity entity, GripDataCollection grips, double curViewUnitSize, int gripSize, Vector3d curViewDir, GetGripPointsFlags bitFlags)
部分复杂实体无法重载该函数(如MText),可以获取较复杂的控制,必须继承GripData
public override void GetGripPoints(Entity entity, Point3dCollection gripPoints, IntegerCollection snapModes, IntegerCollection geometryIds)
通用重载函数,使用方法较简单
MoveGripPointsAt
移动夹点时应相应的更新实体的属性或扩展数据
同样有两种形式的重载,并且应与GetGripPoints的相应重载相符
下面的例子,我更改了程序的构架,实现了一个新的箭头类(LArrow)以方便管理,在其中再附加一个数据_scale,标识箭头与起点距离占全长的百分比
并在GetGripPoints重载函数中去除直线中点处的夹点,并加入一个新夹点去控制箭头的位置(与_scale相应)
代码如下:
using System;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.GraphicsInterface;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
namespace TlsCad
{
class TlsApplication : IExtensionApplication
{
void IExtensionApplication.Initialize()
{
LArrow.OverruleStart();
Overrule.Overruling = true;
}
void IExtensionApplication.Terminate()
{
LArrow.OverruleEnd();
Overrule.Overruling = false;
}
}
class LArrow
{
//默认箭头长度
public static double CurrArrowLength = 5;
//XData的应用程序注册名
public readonly static string RegAppName = "TlsCad.Line.Arrow";
private readonly static Matrix3d _matLeft =
Matrix3d.Rotation(Math.PI / 6, Vector3d.ZAxis, Point3d.Origin);
private readonly static Matrix3d _matRight =
Matrix3d.Rotation(-Math.PI / 6, Vector3d.ZAxis, Point3d.Origin);
private readonly static TypedValue _tvHead =
new TypedValue((int)DxfCode.ExtendedDataRegAppName, RegAppName);
private Line _line;
private double _arrowLength;
private double _scale;
//构造函数,获取直线实体附着的数据
public LArrow(Line line)
{
_line = line;
ResultBuffer rb = _line.GetXDataForApplication(RegAppName);
if (rb != null)
{
TypedValue[] vals = rb.AsArray();
_arrowLength = (double)vals.Value;
_scale = (double)vals.Value;
}
}
public LArrow(Drawable d)
: this((Line)d)
{ }
public LArrow(Entity ent)
: this((Line)ent)
{ }
//重定义生效
//注意这里的实体类型和Overrule子类中必须严格对应
public static void OverruleStart()
{
Overrule.AddOverrule(RXObject.GetClass(typeof(Line)), LArrowDrawOverrule.TheOverrule, false);
Overrule.AddOverrule(RXObject.GetClass(typeof(Line)), LArrowGripOverrule.TheOverrule, false);
}
//重定义失效
public static void OverruleEnd()
{
Overrule.RemoveOverrule(RXObject.GetClass(typeof(Line)), LArrowDrawOverrule.TheOverrule);
Overrule.RemoveOverrule(RXObject.GetClass(typeof(Line)), LArrowGripOverrule.TheOverrule);
}
//绘制箭头
public void WorldDraw(WorldDraw wd)
{
if (_arrowLength > 0)
{
Vector3d vec;
Point3d pnt;
if (_line.Length == 0)
{
vec = Vector3d.XAxis;
pnt = _line.StartPoint;
}
else
{
vec = (_line.EndPoint - _line.StartPoint).GetNormal();
pnt = _line.GetPointAtParameter(_scale * _line.EndParam);
}
Vector3d v1 = vec.TransformBy(_matLeft);
Vector3d v2 = vec.TransformBy(_matRight);
//按实体附着的数据显示箭头的实际长度
wd.Geometry.WorldLine(pnt, pnt + v1 * _arrowLength);
wd.Geometry.WorldLine(pnt, pnt + v2 * _arrowLength);
}
}
//保存扩展数据
public void SaveExtendedData()
{
ResultBuffer rb = new ResultBuffer();
rb.Add(_tvHead);
rb.Add(new TypedValue((int)DxfCode.ExtendedDataReal, _arrowLength));
rb.Add(new TypedValue((int)DxfCode.ExtendedDataReal, _scale));
_line.XData = rb;
}
public Line Line
{
get { return _line; }
}
public double Scale
{
get { return _scale; }
set { _scale = value; }
}
public Point3d Position
{
get
{
return _line.GetPointAtParameter(_scale * _line.EndParam);
}
set
{
_scale = _line.GetParameterAtPoint(_line.GetClosestPointTo(value, false)) / _line.EndParam;
}
}
public double ArrowLength
{
get { return _arrowLength; }
set { _arrowLength = value; }
}
/*
* Overrule的实现机理
* 1、继承相应的Overrule基类,并在子类中定义规则(即函数重载),并设置过滤条件
* 2、对你想重定义的实体按过滤条件附着数据
* 3、AutoCad在遇到这类特殊实体时按子类重载的函数去操控它
*/
public static void DoIt()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
PromptDoubleOptions optDouble = new PromptDoubleOptions("\n请输入箭头长度:");
optDouble.AllowNone = true;
optDouble.AllowZero = false;
optDouble.DefaultValue = CurrArrowLength;
optDouble.UseDefaultValue = true;
PromptDoubleResult resDouble = ed.GetDouble(optDouble);
if (resDouble.Status == PromptStatus.OK)
{
//改变箭头长度设定值
CurrArrowLength = resDouble.Value;
PromptPointResult resStartPoint = ed.GetPoint("\n请输入起点:");
if (resStartPoint.Status == PromptStatus.OK)
{
PromptPointOptions optEndPoint = new PromptPointOptions("\n请输入终点:");
optEndPoint.BasePoint = resStartPoint.Value;
optEndPoint.UseBasePoint = true;
PromptPointResult resEndPoint = ed.GetPoint(optEndPoint);
if (resEndPoint.Status == PromptStatus.OK)
{
Point3d startpnt = resStartPoint.Value;
Point3d endpnt = resEndPoint.Value;
Database db = doc.Database;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTableRecord btr =
(BlockTableRecord)tr.GetObject(
db.CurrentSpaceId,
OpenMode.ForWrite,
false);
Line line = new Line(startpnt, endpnt);
btr.AppendEntity(line);
tr.AddNewlyCreatedDBObject(line, true);
RegAppTable rat =
(RegAppTable)tr.GetObject(
db.RegAppTableId,
OpenMode.ForRead,
false);
if (!rat.Has(RegAppName))
{
rat.UpgradeOpen();
RegAppTableRecord regapp = new RegAppTableRecord();
regapp.Name = RegAppName;
rat.Add(regapp);
tr.AddNewlyCreatedDBObject(regapp, true);
}
Point3d midpnt = (startpnt + endpnt.GetAsVector()) / 2;
LArrow la = new LArrow(line);
//附着当前设定的箭头长度
la.ArrowLength = CurrArrowLength;
la.Scale = 0.5;
la.SaveExtendedData();
tr.Commit();
}
}
}
}
}
}
//实现特殊的夹点
class LArrowGripData : GripData
{
public LArrowGripData(Point3d position)
{
GripPoint = position;
}
public void Move(LArrow la, Vector3d vec)
{
la.Position += vec;
la.SaveExtendedData();
}
}
//夹点重定义
public class LArrowGripOverrule : GripOverrule
{
public static LArrowGripOverrule TheOverrule = new LArrowGripOverrule();
public LArrowGripOverrule()
{
SetXDataFilter(LArrow.RegAppName);
}
//获取夹点,简单实体应重载该函数以获取更灵活的控制
public override void GetGripPoints(Entity entity, GripDataCollection grips, double curViewUnitSize, int gripSize, Vector3d curViewDir, GetGripPointsFlags bitFlags)
{
LArrow la = new LArrow(entity);
base.GetGripPoints(entity, grips, curViewUnitSize, gripSize, curViewDir, bitFlags);
grips.Remove(grips);
grips.Add(new LArrowGripData(la.Position));
}
//移动夹点
public override void MoveGripPointsAt(Entity entity, GripDataCollection grips, Vector3d offset, MoveGripPointsFlags bitFlags)
{
LArrow la = new LArrow(entity);
foreach(GripData gd in grips)
{
if (gd is LArrowGripData)
{
LArrowGripData lagd = (LArrowGripData)gd;
lagd.Move(la, offset);
}
}
for(int i = grips.Count - 1; i >= 0; i--)
{
if (grips is LArrowGripData)
{
grips.Remove(grips);
}
}
if (grips.Count > 0)
{
base.MoveGripPointsAt(entity, grips, offset, bitFlags);
}
}
}
//显示重定义
public class LArrowDrawOverrule : DrawableOverrule
{
public static LArrowDrawOverrule TheOverrule = new LArrowDrawOverrule();
//设置重定义的过滤条件
public LArrowDrawOverrule()
{
SetXDataFilter(LArrow.RegAppName);
}
//显示重载
public override bool WorldDraw(Drawable drawable, WorldDraw wd)
{
LArrow la = new LArrow(drawable);
la.WorldDraw(wd);
return base.WorldDraw(drawable, wd);
}
}
}
继续关注学习中。。。。 <p>顶一下.</p><p></p><p>飞狐兄.问一下.重定义是不是把一个对象(直线,圆...)的显示.夹点.给定义成别的.</p><p>选择这个对象是时.在面板属性里还是一条线?</p><p>只是给一条线加了扩展数据.在显示的时候处理一下.</p> 这么好的帖子,怎么没人顶呢?另问一下,是必须用CAD2010+VS2008吗?用CAD2008+VS2005,可以吗? 先收藏.以后再来看...