实时预览技术Jig

“实时预览”是指用户在没点击鼠标确认输入点之前,CAD能根据鼠标位置绘制此刻的图元。当用户点击鼠标确认时,最终图形被确定,Jig过程停止;当用户取消Jig操作时,没有任何图元被最终绘制。采用Jig技术实现图元实时预览,可以将图元绘制过程中任一时刻的形状反馈给用户,供用户做下一步移位决策。

Jig技术基本是自定义实体的标配,也象征着自定义实体具备较强的交互性。下文讲解Jig的具体实现。

什么是Jig

在AutoCAD中,几乎所有的内置实体都支持Jig实时预览,下面是CAD内置的直线和圆的Jig效果。

创建Jig封装类

我们可以针对CAD原生实体或自定义实体实现特定的Jig效果,这时需要创建一个派生自AcEdJig类的子类,并在该子类中实现Jig的主要逻辑。

  • 可以借助ObjectARX的项模板进行创建,非常方便

  • 需要指定Jig子类名、该Jig类作用的目标实体类名、Jig的输入数

  • 指定完成之后,IDE为我们生成了CustomEntity1Jig.hCustomEntity1Jig.cpp两个文件。

代码逻辑

有几个重点函数需要关注:

  • startJig(CustomEntity1* pEntityToJig):启动Jig的主函数
  • 重写三个关键虚函数:sampler()update()Entity()
  • 还有若干与标注相关的函数,暂不细究
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#pragma once
#include "../MyCustomEntity/CustomEntity1.h"

class CustomEntity1Jig : public AcEdJig {

private:
int mCurrentInputLevel ;
AcDbDimDataPtrArray mDimData ;

public:
AcGePoint3dArray mInputPoints ;
CustomEntity1 *mpEntity ;

public:
CustomEntity1Jig () ;
~CustomEntity1Jig () ;

AcEdJig::DragStatus startJig (CustomEntity1*pEntityToJig) ;

protected:
virtual DragStatus sampler () ;
virtual Adesk::Boolean update () ;
virtual AcDbEntity *entity () const ;
virtual AcDbDimDataPtrArray *dimData (const double dimScale) ;
virtual Acad::ErrorStatus setDimValue (const AcDbDimData *pDimData, const double dimValue) ;
virtual Adesk::Boolean updateDimData () ;

AcEdJig::DragStatus GetStartPoint () ;
AcEdJig::DragStatus GetNextPoint () ;
} ;

下面针对上述关键函数的作用做简要说明。

  • startJig()启动函数:
    • 在每个输入中设置提示语,调用drag()函数,这里处理鼠标移动逻辑,内部调用三大关键虚函数
    • 根据drag()函数的返回结果确定下一步操作,返回kNormal时将实体添加到模型空间
  • 采用了模板方法的设计模式:
    • drag()依赖三大关键函数,该函数的实现逻辑相对固定,在父类中实现
    • 三大关键函数的具体逻辑与子类Jig的逻辑相关,在子类中实现
    • sampler()是采样函数:其中mCurrentInputLevel变量指示了当前采样针对哪个Jig输入,通过GetStartPoint()函数将采样点保存到成员变量中
    • update()函数用于实时更新目标实体的状态,其数据来自上一步的采样结果
    • entity()函数返回实时预览需要绘制的实体
  • 一般情况下,IDE生成Jig类后无需用户修改太多代码,主要修改三大关键虚函数即可,以update()为最
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#include "StdAfx.h"
#include "CustomEntity1Jig.h"

CustomEntity1Jig::CustomEntity1Jig() : AcEdJig(),
mCurrentInputLevel(0), mpEntity(NULL){}
CustomEntity1Jig::~CustomEntity1Jig() {}

// Jig的主逻辑
AcEdJig::DragStatus CustomEntity1Jig::startJig(CustomEntity1* pEntity) {
mpEntity = pEntity;
AcString inputPrompts[2] = {
L"\nPick point",
L"\nPick point"
};
AcString kwords[2] = {
L"",
L""
};

bool appendOk = true;
AcEdJig::DragStatus status = AcEdJig::kNull;
for (mCurrentInputLevel = 0; mCurrentInputLevel < 2; mCurrentInputLevel++) {
//- Add a new input point to the list of input points
mInputPoints.append(AcGePoint3d());
//- Set the input prompt
setDispPrompt(inputPrompts[mCurrentInputLevel]);
//- Setup the keywords required
setKeywordList(kwords[mCurrentInputLevel]);

bool quit = false;
//- Lets now do the input
status = drag();
if (status != kNormal) {
//- If it's a keyword
switch (status) {
case kCancel:
case kNull:
quit = true;
break;
case kKW1:
case kKW2:
case kKW3:
case kKW4:
case kKW5:
case kKW6:
case kKW7:
case kKW8:
case kKW9:
//- Do something
break;
}
}
else {
appendOk = true;
}
//- If to finish
if (quit)
break;
}

//- If the input went well
if (appendOk)
//- Append to the database
append();
else
//- Clean up
delete mpEntity;
return (status);
}

#pragma region Jig需要实现的三个虚函数
AcEdJig::DragStatus CustomEntity1Jig::sampler() {
//- Setup the user input controls for each input
AcEdJig::UserInputControls userInputControls[2] = {
/*AcEdJig::UserInputControls::*/(AcEdJig::UserInputControls)0,
/*AcEdJig::UserInputControls::*/(AcEdJig::UserInputControls)0
};
//- Setup the cursor type for each input
AcEdJig::CursorType cursorType[2] = {
/*AcEdJig::CursorType::*/(AcEdJig::CursorType)0,
/*AcEdJig::CursorType::*/(AcEdJig::CursorType)0
};
//- Setup the user input controls for each sample
setUserInputControls(userInputControls[mCurrentInputLevel]);
setSpecialCursorType(cursorType[mCurrentInputLevel]);

AcEdJig::DragStatus status = AcEdJig::kCancel;
//- Check the current input number to see which input to do
switch (mCurrentInputLevel + 1) {
case 1:
// TODO : get an input here
status = GetStartPoint();
break;
case 2:
// TODO : get an input here
status = GetNextPoint();
break;

default:
break;
}
return (status);
}

Adesk::Boolean CustomEntity1Jig::update() {
//- Check the current input number to see which update to do
AcGePoint3d p;
switch (mCurrentInputLevel + 1) {
double deltaX, deltaY, r1;
case 1:
// TODO : update your entity for this input
mpEntity->setCenter(mInputPoints[mCurrentInputLevel]);
break;
case 2:
// TODO : update your entity for this input
deltaX = mInputPoints[mCurrentInputLevel].x - mInputPoints[mCurrentInputLevel - 1].x;
deltaY = mInputPoints[mCurrentInputLevel].y - mInputPoints[mCurrentInputLevel - 1].y;
r1 = sqrt(deltaX * deltaX + deltaY * deltaY);
mpEntity->setR1(r1);
mpEntity->setR2(0.15 * r1);
break;

default:
break;
}

return (updateDimData());
}

AcDbEntity* CustomEntity1Jig::entity() const {
return ((AcDbEntity*)mpEntity);
}
#pragma endregion


AcDbDimDataPtrArray* CustomEntity1Jig::dimData(const double dimScale) {
return (NULL);
}

Acad::ErrorStatus CustomEntity1Jig::setDimValue(const AcDbDimData* pDimData, const double dimValue) {
Acad::ErrorStatus es = Acad::eOk;
return (es);
}

Adesk::Boolean CustomEntity1Jig::updateDimData() {
if (mDimData.length() <= 0)
return (true);
return (true);
}

AcEdJig::DragStatus CustomEntity1Jig::GetStartPoint() {
AcGePoint3d newPnt;
//- Get the point
AcEdJig::DragStatus status = acquirePoint(newPnt);
//- If valid input
if (status == AcEdJig::kNormal) {
//- If there is no difference
if (newPnt.isEqualTo(mInputPoints[mCurrentInputLevel]))
return (AcEdJig::kNoChange);
//- Otherwise update the point
mInputPoints[mCurrentInputLevel] = newPnt;
}
return (status);
}

AcEdJig::DragStatus CustomEntity1Jig::GetNextPoint() {
AcGePoint3d oldPnt = mInputPoints[mCurrentInputLevel - 1];
AcGePoint3d newPnt;
//- Get the point
AcEdJig::DragStatus status = acquirePoint(newPnt, oldPnt);
//- If valid input
if (status == AcEdJig::kNormal) {
//- If there is no difference
if (newPnt.isEqualTo(mInputPoints[mCurrentInputLevel]))
return (AcEdJig::kNoChange);
//- Otherwise update the point
mInputPoints[mCurrentInputLevel] = newPnt;
}
return (status);
}

测试Jig

添加一个ARX命令,在其中采用Jig方式动态添加CustomEntity1自定义实体(前面几个自定义实体相关的文章都基于CustomEntity1开展)。

  • 先创建一个自定义实体对象,设置大小圆的初始半径为0
  • 创建一个Jig对象,调用startJig()启动函数,向其传递自定义实体对象指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static void LHMyGroupCustomEntityJig() {
CustomEntity1* pEnt = new CustomEntity1();
pEnt->setR1(0);
pEnt->setR2(0);
pEnt->setN(10);

if (pEnt == nullptr)
return;

CustomEntity1Jig* jig = new CustomEntity1Jig();
if (jig == nullptr) {
delete pEnt;
return;
}

if (jig->startJig(pEnt) == AcEdJig::kNormal)
acutPrintf(L"\n创建Jig自定义实体成功!");
else
acutPrintf(L"\n创建Jig自定义实体成功!");

delete jig;
}

效果如下:

评论