|
[code]1. 说明
基本的 Jig 拖动支持在创建/编辑一个实体的过程中,移动光标的同时实体的外观同步变化,但是很多情况下我们希望能有多个实体同时变化.
本节介绍一个例子, 沿一个圆弧实体等间距放置若干个图块,用户拖动光标时圆弧的形状发生变化,同时插入的块参照的位置也会随之变化.
2. 思路
Jig 一拖多可以考虑两种实现思路:
(1) 在entity 函数中返回一个长度为0的直线(欺骗AutoCAD,让AutoCAD更新这条直线的行为不会影响到图形窗口中的显示),在 update 函数中,根据需要打开多个需要同步变化的实体,对其参数进行修改,然后关闭它。
(2) 创建一个自定义实体,entity 函数中返回这个自定义实体的指针,在update函数中,修改自定义实体的一些参数,引起自定义实体的重新操作,在自定义实体的 worldDraw 函数中调用子实体(子实体就是需要拖动的多个实体)完成拖动绘制。
3. 步骤
(1) 添加 CArcBlockJigEntity 新类, 父类设置为 AcDbEntity
MyWorldDraw函数 完成自定义实体的绘制
SetEndPoint函数 函数在拖动过程中动态修改圆弧的终点,它会引发自定义实体的绘 制,也就是 MyWorldDraw 函数被调用
PostToModelSpace函数 函数能将动态显示的子实体添加到模型空间
GetBlkRefIds函数 将PostToModelSpace函数中添加的块参照ID集合返回到外部调用函数
DrawOrAddSubEnts函数 将worldDraw和PostModelSpace中公用的代码封装起来便于重用
class CArcBlockJigEntity : public AcDbEntity
{
public:
// 参数:startPoint:起始点;endPoint:终止点;thirdPoint:第三点;
// blockId:块的id; count:插入块的个数
CArcBlockJigEntity(const AcGePoint3d &startPoint, const AcGePoint3d &thirdPoint, const AcGePoint3d &endPoint, AcDbObjectId blkDefId, int count);
virtual ~CArcBlockJigEntity();
// 自定义实体的绘制函数
virtual Adesk::Boolean MyWorldDraw(AcGiWorldDraw* mode);
// 设置圆弧终点的位置
void SetEntPoint(const AcGePoint3d &pt);
// 将圆弧和块添加到模型空间
void PostToModelSpace();
// 获得添加的块参照集合
AcDbObjectIdArray GetBlkRefIds();
private:
//绘制实体或添加到模型空间
void DrawOrAddSubEnts(AcGiWorldDraw* mode);
private:
AcGePoint3d m_startPoint, m_endPoint, m_thirdPoint;
// 圆弧的起点、终点和第三点(圆弧上位于起点和终点中间的一点)
AcDbObjectId m_blkDefId; // 块定义ID
int m_blockCount; // 要布置的块参照的数量
AcDbObjectIdArray m_blkRefIds; // 添加的块参照集合
};
(2) 构造函数初始化
CArcBlockJigEntity::CArcBlockJigEntity(const AcGePoint3d &startPoint, const AcGePoint3d &thirdPoint, const AcGePoint3d &endPoint, AcDbObjectId blkDefId, int count)
{
m_startPoint = startPoint;
m_thirdPoint = thirdPoint;
m_endPoint = endPoint;
m_blkDefId = blkDefId;
m_blockCount = count;
}
CArcBlockJigEntity::~CArcBlockJigEntity(void)
{
}
(3) 绘制
MyWorldDraw 函数会在图形窗口中显示圆弧和块参照子实体,负责拖动过程中的显示更新,
PostToModelSpace函数则会将这些子实体添加到模型空间,负责拖动完整之后将子实体添加到模型空间。
这两个函数的处理逻辑完全一致,不同的在于结果体现不一样,一个是在图形窗口中绘制,另外一个添加到模型空间.DrawOrAddSubEnts 函数封装了两个函数中公用的代码,这样就可以将相同的逻辑用一个函数来体现.
A . 判断给定的三点是否共线,如果共线,就要用直线段来代替圆弧
// 判断给定的三点是否共线
static bool ThreePointIsCollinear(const AcGePoint2d &pt1, const AcGePoint2d &pt2, const AcGePoint2d &pt3); // 判断给定的三点是否共线
// 判断给定的三点是否共线
bool CGeometryOper::ThreePointIsCollinear(const AcGePoint2d &pt1, const AcGePoint2d &pt2, const AcGePoint2d &pt3)
{
double xy = pt1.x * pt1.x + pt1.y * pt1.y;
double xyse = xy - pt3.x * pt3.x - pt3.y * pt3.y;
double xysm = xy - pt2.x * pt2.x - pt2.y * pt2.y;
xy = (pt1.x - pt2.x) * (pt1.y - pt3.y) - (pt1.x - pt3.x) * (pt1.y - pt2.y);
return (fabs(xy) < 1.0E-5);
}
B. 在图形窗口中直接绘制圆弧/直线段的方法:
mode->geometry().polyline(2, verts);
mode->geometry().circularArc(m_startPoint, m_thirdPoint, m_endPoint);
C. 为了计算圆弧或直线段的等分点, 我们使用AcDbCurve提供的一些成员函数,如果三点共线,创建一条直线段,否则就创建圆弧实体.
创建直线段的代码为:
pCurve = new AcDbLine(m_startPoint, m_endPoint);
D. 判断顺时针还是逆时针(AcDbArc对象构建时默认是逆时针)
判断方式: 终点在起点-中间点连线的左侧还是右侧
// 判断绘画圆弧时是顺时针还是逆时针
static int PtInLeftOfLine(const AcGePoint3d &ptStart, const AcGePoint3d &ptEnd, const AcGePoint3d &pt, double tol = 1.0E-7);
static int PtInLeftOfLine(const AcGePoint2d &ptStart, const AcGePoint2d &ptEnd, const AcGePoint2d &pt, double tol = 1.0E-7);
static int PtInLeftOfLine(double x1, double y1, double x2, double y2, double x3, double y3, double tol = 1.0E-7);
// 判断绘画圆弧时是顺时针还是逆时针
int CGeometryOper::PtInLeftOfLine(const AcGePoint3d &ptStart, const AcGePoint3d &ptEnd, const AcGePoint3d &pt, double tol)
{
return PtInLeftOfLine(ptStart.x, ptStart.y, ptEnd.x, ptEnd.y, pt.x, pt.y, tol);
}
int CGeometryOper::PtInLeftOfLine(const AcGePoint2d &ptStart, const AcGePoint2d &ptEnd, const AcGePoint2d &pt, double tol)
{
return PtInLeftOfLine(ptStart.x, ptStart.y, ptEnd.x, ptEnd.y, pt.x, pt.y, tol);
}
int CGeometryOper::PtInLeftOfLine(double x1, double y1, double x2, double y2, double x3, double y3, double tol)
{
// 两个矢量的叉乘结果是一个,矢量的行列式值是这两个矢量确定的平行四边形的面积
double a = (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1);
if (fabs(a) < tol)
{
return 0;
}
else if (a > 0)
{
return 1;
}
else
{
return -1;
}
}
E. 二维三维点转换
static AcGePoint2d ToPoint2d(const AcGePoint3d &point3d);
static AcGePoint3d ToPoint3d(const AcGePoint2d &point2d, double z = 0)
AcGePoint2d CGeometryOper::ToPoint2d(const AcGePoint3d &point3d)
{
return AcGePoint2d(point3d.x, point3d.y);
}
AcGePoint3d CGeometryOper::ToPoint3d(const AcGePoint2d &point2d, double z)
{
return AcGePoint3d(point2d.x, point2d.y, z);
}
F. 准备函数完成,
//绘制实体或添加到模型空间,封装
void CArcBlockJigEntity::DrawOrAddSubEnts(AcGiWorldDraw* mode)
{
//绘制圆弧
AcDbCurve *pCurve = NULL; //计算等分点的曲线
AcGePoint2d startPoint2d = CGeometryOper::ToPoint2d(m_startPoint);
AcGePoint2d thirdPoint2d = CGeometryOper::ToPoint2d(m_thirdPoint);
AcGePoint2d endPoint2d = CGeometryOper::ToPoint2d(m_endPoint);
if (CGeometryOper::ThreePointIsCollinear(startPoint2d, thirdPoint2d, endPoint2d))
{
AcGePoint3d verts[2];
verts[0] = m_startPoint;
verts[1] = m_endPoint;
if (mode != NULL)
{
mode->geometry().polyline(2, verts);
}
pCurve = new AcDbLine(m_startPoint, m_endPoint);//创建直线段
}
else
{
if (mode != NULL)
{
mode->geometry().circularArc(m_startPoint, m_thirdPoint, m_endPoint);
}
AcGeCircArc2d geArc(startPoint2d, thirdPoint2d, endPoint2d);
//geArc的起始角度始终是0,因此单独计算起始角度和终止角度
AcGeVector2d vecStart = startPoint2d - geArc.center();
AcGeVector2d vecEnd = endPoint2d - geArc.center();
//AcGeArc必须是逆时针,因此需要根据三点的旋转方向,确定正确的起始角度
double startAngle = 0;
if (CGeometryOper::PtInLeftOfLine(startPoint2d, thirdPoint2d, endPoint2d) > 0)
//逆时针
{
startAngle = vecStart.angle();
}
else
{
startAngle = vecEnd.angle();
}
double endAngle = startAngle + (geArc.endAng() - geArc.startAng());
pCurve = new AcDbArc(CGeometryOper::ToPoint3d(geArc.center()), geArc.radius(),
startAngle, endAngle);
//计算等分点,获得块参照插入的位置
double startParam = 0, endParam = 0; //曲线的起点和终点参数
pCurve->getStartParam(startParam);
pCurve->getEndParam(endParam);
int intervalCount = m_blockCount + 1; //等分间距份数比块参照数量大1
double paramInterval = (endParam - startParam) / intervalCount;
AcGePoint3dArray blkRefPoints; //块参照插入点的集合
for (int i = 1; i < intervalCount; i++) //曲线的起点和终点不需要放置图块
{
double param = startParam + i * paramInterval;
AcGePoint3d pt;
pCurve->getPointAtParam(param, pt);
blkRefPoints.append(pt);
}
if (mode != NULL) //显示子实体
{
delete pCurve; //动态分配的实体,不加入模型空间,使用完毕之后需要释放
}
else //添加子实体的方式
{
CCreateEnt::PostToModelSpace(pCurve);
}
//绘制几个图块
m_blkRefIds.setLogicalLength(0);
for (int i = 0; i < blkRefPoints.length(); i++)
{
AcDbBlockReference *pBlkRef = new AcDbBlockReference(blkRefPoints[i], m_blkDefId);
if (mode != NULL)
{
pBlkRef->worldDraw(mode);
delete pBlkRef;
}
else
{
m_blkRefIds.append(CCreateEnt::PostToModelSpace(pBlkRef));
}
}
}
}
(4) MyWorldDraw() 函数 PostToModelSpace() 函数
// 自定义实体的绘制函数
Adesk::Boolean CArcBlockJigEntity::MyWorldDraw(AcGiWorldDraw* mode)
{
DrawOrAddSubEnts(mode);
return Adesk::kTrue;
}
// 将圆弧和块添加到模型空间
void CArcBlockJigEntity::PostToModelSpace()
{
DrawOrAddSubEnts(NULL);
}
(5) 设置圆弧终点的位置 用于修改自定义实体中的圆弧终点
// 设置圆弧终点的位置 用于修改自定义实体中的圆弧终点
void CArcBlockJigEntity::SetEntPoint(const AcGePoint3d &pt)
{
//这句代码能引发MyWorldDraw函数的调用
assertWriteEnabled();
m_endPoint = pt;
}
(6) 将PostToModelSpace 中添加到模型空间的块参照集合返回到外部调用函数
获得添加的块参照集合
// 将PostToModelSpace 中添加到模型空间的块参照集合返回到外部调用函数
// 获得添加的块参照集合
AcDbObjectIdArray CArcBlockJigEntity::GetBlkRefIds()
{
return m_blkRefIds;
}
map的遍历方法 有几种? 帮你选择最好的遍历方式
text/x-java
5星
超过95%的资源
7KB
下载
(7) 添加新类 CArcBlockJig , 从 AcEdJig 类继承而来
#include "ArcBlockJigEntity.h"
class CArcBlockJig : public AcEdJig
{
public:
CArcBlockJig(void);
virtual ~CArcBlockJig(void);
//参数startPoint:起始点;endPoint:终止点;thirdPoint第三点;
//blkDefId块的Id; blockCount:插入块的个数
bool doIt(const AcGePoint3d &startPoint, AcGePoint3d &thirdPoint,
AcGePoint3d &endPoint, AcDbObjectId blkDefId, int blockCount);
//此函数将被drag函数调用以获得一个输入
virtual AcEdJig::DragStatus sampler();
virtual Adesk::Boolean update();
virtual AcDbEntity* entity() const;//制定了Jig所操作的对象
//获得Jig操作成功后插入的块的参照集合
AcDbObjectIdArray GetBlkRefIds();
private:
CArcBlockJigEntity* m_pJigEnt;
AcGePoint3d m_curPoint;
AcDbObjectIdArray m_blkRefIds;
};
(8) 构造函数中, 对自定义实体的指针进行初始化,
析构函数中, 销毁自定义实体,
CArcBlockJig::CArcBlockJig(void)
{
m_pJigEnt = NULL;
}
CArcBlockJig::~CArcBlockJig(void)
{
if (m_pJigEnt)
{
delete m_pJigEnt;
m_pJigEnt = NULL;
}
}
(9) doIt() 函数
// 处理拖动的整个流程
// 参数startPoint:起始点;endPoint:终止点;thirdPoint第三点;
// blkDefId块的Id; blockCount:插入块的个数
bool CArcBlockJig::doIt(const AcGePoint3d &startPoint,
AcGePoint3d &thirdPoint, AcGePoint3d &endPoint,
AcDbObjectId blkDefId, int blockCount)
{
//拖动之前:创建自定义实体
if (m_pJigEnt != NULL)
{
delete m_pJigEnt;
m_pJigEnt = NULL;
}
m_pJigEnt = new CArcBlockJigEntity(startPoint, thirdPoint, endPoint, blkDefId, blockCount);
//执行拖动绘制
CString prompt = _T("\n指定下一点:");
setDispPrompt(prompt);
AcEdJig::DragStatus stat = drag();
//执行之后:根据需要确定自己的处理方式
bool bRet = false;
if (stat == kNormal)
{
//添加子实体到模型空间
m_pJigEnt->PostToModelSpace();
bRet = true;
}
m_blkRefIds = m_pJigEnt->GetBlkRefIds();
delete m_pJigEnt;
m_pJigEnt = NULL;
return bRet;
}
(10) sampler() 函数
// 此函数将被drag函数调用以获得一个输入
AcEdJig::DragStatus CArcBlockJig::sampler()
{
setUserInputControls((UserInputControls)
( AcEdJig::kAccept3dCoordinates
| AcEdJig::kNoNegativeResponseAccepted
| AcEdJig::kNullResponseAccepted ));
// 一定要判断一下点是否发生了变化,否则update函数不停地被调用,实体反而不能被绘制出来
static AcGePoint3d pointTemp;
DragStatus stat = acquirePoint(m_curPoint);
if (pointTemp != m_curPoint)
{
pointTemp = m_curPoint;
}
else if (stat == AcEdJig::kNormal)
{
return AcEdJig::kNoChange;
}
return stat;
}
(11) update() 函数
// 更新自定义实体
Adesk::Boolean CArcBlockJig::update()
{
m_pJigEnt->SetEntPoint(m_curPoint);
return Adesk::kTrue;
}
(12) entity() 函数
// 返回 AutoCAD 需要动态更新的实体
AcDbEntity* CArcBlockJig::entity() const
{
return m_pJigEnt;
}
(13) GetBlkRefIds() 函数
// 将Jig过程中创建的块参照集合返回给外部调用函数
AcDbObjectIdArray CArcBlockJig::GetBlkRefIds()
{
return m_blkRefIds;
}
(14) 添加一个测试函数进行测试
static void ArcBlockJig(); //测试函数
// 测试
void CArcBlockJig::ArcBlockJig()
{
//选择一个块参照,用于沿圆弧插入
AcDbEntity *pEnt = NULL;
AcDbObjectId blkDefId;
AcGePoint3d pickPoint;
if (CArcBlockJig::PromptSelectEntity(_T("\n 选择一个块参照用于沿圆弧插入:"), AcDbBlockReference::desc(), pEnt, pickPoint))
{
AcDbBlockReference *pBlkRef = AcDbBlockReference::cast(pEnt);
blkDefId = pBlkRef->blockTableRecord();
pEnt->close();
}
if (blkDefId.isNull())
{
return;
}
//提示用户拾取第一点
AcGePoint3d startPoint;
if (!CArcBlockJig::GetPoint(_T("\n拾取第一点:"), startPoint))
{
return;
}
//提示用户拾取第二点
AcGePoint3d secondPoint;
if (!CArcBlockJig::GetPoint(startPoint, _T("\n拾取第二点:"), secondPoint))
{
return;
}
//开始拖动
CArcBlockJig jig;
int blockCount = 4;
jig.doIt(startPoint, secondPoint, secondPoint, blkDefId, blockCount);
}
(15) 需要的函数
static bool PromptSelectEntity(const TCHAR* prompt, AcRxClass* classDesc, AcDbEntity *&pEnt,
AcGePoint3d &pickPoint, bool bOpenForWrite = true);
static bool PromptSelectEntity(const TCHAR* prompt, const std::vector<AcRxClass*> &classDescs, AcDbEntity *&pEnt,
AcGePoint3d &pickPoint, bool bOpenForWrite = true);
// 提示用户选择一个点(无论当前是否在UCS中工作,直接返回该点的WCS坐标)
// basePoint: 基于WCS的点坐标
// 返回值:与acedGetPoint函数相同
static int GetPointReturnCode(const AcGePoint3d &basePoint, const TCHAR* prompt, AcGePoint3d &point);
static bool GetPoint(const AcGePoint3d &basePoint, const TCHAR* prompt, AcGePoint3d &point);
static int GetPointReturnCode(const TCHAR* prompt, AcGePoint3d &point);
static bool GetPoint(const TCHAR* prompt, AcGePoint3d &point);
// 将一个点从用户坐标系坐标转换到世界坐标系
static AcGePoint3d UcsToWcsPoint(const AcGePoint3d &point);
// 将一个点从世界坐标系坐标转换到显示坐标系
static AcGePoint3d WcsToUcsPoint(const AcGePoint3d &point);
bool CArcBlockJig::PromptSelectEntity(const TCHAR* prompt, AcRxClass* classDesc, AcDbEntity *&pEnt,
AcGePoint3d &pickPoint, bool bOpenForWrite /*= true*/)
{
std::vector<AcRxClass*> descs; //#include <vector>
descs.push_back(classDesc);
return PromptSelectEntity(prompt, descs, pEnt, pickPoint, bOpenForWrite);
}
bool CArcBlockJig::PromptSelectEntity(const TCHAR* prompt, const std::vector<AcRxClass*> &classDescs, AcDbEntity *&pEnt,
AcGePoint3d &pickPoint, bool bOpenForWrite /*= true*/)
{
ads_name ename;
RETRY:
if (acedEntSel(prompt, ename, asDblArray(pickPoint)) != RTNORM)
{
pEnt = NULL;
return false;
}
AcDbObjectId entId;
acdbGetObjectId(entId, ename);
// 判断选择的实体是否是指定类型的实体
Acad::ErrorStatus es;
if (bOpenForWrite)
{
es = acdbOpenObject(pEnt, entId, AcDb::kForWrite);
}
else
{
es = acdbOpenObject(pEnt, entId, AcDb::kForRead);
}
assert(es == Acad::eOk);
bool bRet = false;
for (int i = 0; i < (int)classDescs.size(); i++)
{
if (pEnt->isKindOf(classDescs[i]))
{
bRet = true;
break;
}
}
if (bRet)
{
return true;
}
else
{
pEnt->close();
acutPrintf(_T("\n选择的实体类型不合要求, 请再次选择..."));
goto RETRY;
}
}
int CArcBlockJig::GetPointReturnCode(const AcGePoint3d &basePoint, const TCHAR* prompt, AcGePoint3d &point)
{
// 将基点转换为UCS坐标
AcGePoint3d ucsBasePoint = CArcBlockJig::WcsToUcsPoint(basePoint);
int nReturn = acedGetPoint(asDblArray(ucsBasePoint), prompt, asDblArray(point));
if (nReturn == RTNORM)
{
// acedGetPoint得到UCS坐标,转换为WCS
point = CArcBlockJig::UcsToWcsPoint(point);
}
return nReturn;
}
int CArcBlockJig::GetPointReturnCode(const TCHAR* prompt, AcGePoint3d &point)
{
int nReturn = acedGetPoint(NULL, prompt, asDblArray(point));
if (nReturn == RTNORM)
{
point = CArcBlockJig::UcsToWcsPoint(point);
}
return nReturn;
}
bool CArcBlockJig::GetPoint(const AcGePoint3d &basePoint, const TCHAR* prompt, AcGePoint3d &point)
{
return (GetPointReturnCode(basePoint, prompt, point) == RTNORM);
}
bool CArcBlockJig::GetPoint(const TCHAR* prompt, AcGePoint3d &point)
{
return (GetPointReturnCode(prompt, point) == RTNORM);
}
AcGePoint3d CArcBlockJig::UcsToWcsPoint(const AcGePoint3d &point)
{
// 转换成世界坐标
AcGePoint3d pt;
struct resbuf rbFrom, rbTo;
rbFrom.restype = RTSHORT;
rbFrom.resval.rint = 1; // from UCS
rbTo.restype = RTSHORT;
rbTo.resval.rint = 0; // to WCS
acedTrans(asDblArray(point), &rbFrom, &rbTo, Adesk::kFalse, asDblArray(pt));
return pt;
}
AcGePoint3d CArcBlockJig::WcsToUcsPoint(const AcGePoint3d &point)
{
// 转换成世界坐标
AcGePoint3d pt;
struct resbuf rbFrom, rbTo;
rbFrom.restype = RTSHORT;
rbFrom.resval.rint = 0; // from WCS
rbTo.restype = RTSHORT;
rbTo.resval.rint = 1; // to UCS
acedTrans(asDblArray(point), &rbFrom, &rbTo, Adesk::kFalse, asDblArray(pt));
return pt;
}
java集合map取key使用示例 java遍历map
pdf
0星
超过10%的资源
19KB
下载
(16) 在acrxEntryPoint.cpp中
ACED_ARXCOMMAND_ENTRY_AUTO(CArxConfigApp, MidasMyGroup, MyArcBlockJig, MyArcBlockJig, ACRX_CMD_MODAL, NULL) //Jig圆弧上分段画选择的块
//当前项目中注册命令 ArcBlockJig (Jig圆弧上分段画选择的块)
static void MidasMyGroupMyArcBlockJig()
{
CDrawSquareJig::DrawSequareJig();
}
原文链接:https://blog.csdn.net/qq_42981953/article/details/121974605[/code] |
|