2008-11-20, 研究实现任意形状连接关系
一直以来,我画连接关系主要是处理电路之间的连接关系,还比较简单,原因在于以下几点:
1. 能画得横平竖直,交叉点能正确显示,母线馈线部分能规范表示就行
2. 各种编辑操作后,连接关系能够保持
最主要是横平竖直,使得处理起来方便得多,上述功能早就实现了。下面这张图就是一个典型的电路之间的连接关系的展现。
但需求越来越多,已有不少用户提出能否处理弯曲的连接关系,归纳起来,有以下几点:
1. 能弯曲显示连接关系
2. 能编辑形状
3. 各种相关对象编辑操作后,连接关系能够保持
这个问题,我放了一段时间,主要是没有时间来做,还有一个主要原因是没有考虑成熟。
今天,不用上班,就在家里,仔细琢磨了一下这个问题,初步设计出以下思路
1. 这种关系有两个外连接点
2. 可以和两个相关对象建立关系
3. 表现形式可以借用现有的线段、曲线、箭头等基本图元
4. 连接关系的形状编辑直接用基本图元的编辑
在以前软件的基础上,可以采用一个复合图元来实现,设计两个TPad表示两个外连接点,两个TCbwObject表示两个相关对象,再用一个TCbwObject表示关系的样子,先支持线段、圆弧、箭头、连接普通曲线、连续贝塞尔曲线等几个基本图元。
首先是设计界面,当然,通过代码是最快的。[code] AddItemToMenu(MenuItem_Relation, "线段", cctLine, 10, MenuItem_Relation_LineClick); // 线段
AddItemToMenu(MenuItem_Relation, "圆弧", cctArc, 11, MenuItem_Relation_LineClick); // 圆弧
AddItemToMenu(MenuItem_Relation, "箭头", cctArrow, 11, MenuItem_Relation_LineClick); // 箭头
AddItemToMenu(MenuItem_Relation, "连续普通曲线", cctCurve, 2, MenuItem_Relation_LineClick); // 连续普通曲线
AddItemToMenu(MenuItem_Relation, "连续贝塞尔曲线", cctBezier, 13, MenuItem_Relation_LineClick); // 连续贝塞尔曲线[/code]语言切换稍后完成。
运行后界面效果如下
其次,需要处理这些菜单的响应事件,即将这些菜单项的Tag值作为参数传递给画图过程[code]void __fastcall TCbwGraphBaseForm::MenuItem_Relation_LineClick(
TObject *Sender)
{ //
SpecialContent = AnsiString(GetTag(Sender));
OperateType = cotDraw;
cClassType = cctRelationShip;
}[/code]下来就该设计关系类了,这个简单[code]class TCbwRelationShip : public TCompoundMeta
{
typedef TCompoundMeta inherited;
TCbwObject * FDisplayObject; // 关系图元,动态创建
CBW_PUBLIC(TCbwObject *, BeginObject, FBeginObject) // 相关对象一
CBW_PUBLIC(TCbwObject *, EndObject, FEndObject) // 相关对象二
CBW_PUBLIC(TPad *, BeginPad, FBeginPad) // 外连接点一
CBW_PUBLIC(TPad *, EndPad, FEndPad) // 外连接点二
public:
CBW_DEFINE_OBJECT(TCbwRelationShip)
void __fastcall SetSpecialContent(AnsiString content);
void __fastcall SetAllDoneFlag(bool s);
};[/code]需根据特定内容动态创建关系显示图元[code]void __fastcall TCbwRelationShip::SetSpecialContent(AnsiString content)
{
int tag = content.ToInt();
FDisplayObject = (TCbwObject *)(*CbwBuildObjectMap)[tag]();
AddObject(FDisplayObject);
CloneUIProperties(this, FDisplayObject);
CloneUIProperties(this, FBeginPad);
CloneUIProperties(this, FEndPad);
}[/code]关系对象构造完成后,设置相关的对象与外连接点[code]void __fastcall TCbwRelationShip::SetAllDoneFlag(bool s)
{
if(s)
{
FBeginPad->AddPoint(FDisplayObject->BeginPoint);
FEndPad->AddPoint(FDisplayObject->EndPoint);
if(!AllDoneFlag)
{
PointNumber = FDisplayObject->PointNumber;
AllocatePoints();
for(int i = 0; i < PointNumber; ++i)
Points[i] = FDisplayObject->Points[i];
inherited::AllDoneFlag = s;
Compose();
if(FBeginObject)
{
TCbwFloatPoint distance = TCbwFloatPoint(FBeginPad->CentralPoint.x - FBeginObject->CentralPoint.x,
FBeginPad->CentralPoint.y - FBeginObject->CentralPoint.y);
FBeginObject->Relations[FBeginPad] = distance;
}
if(FEndObject)
{
TCbwFloatPoint distance = TCbwFloatPoint(FEndPad->CentralPoint.x - FEndObject->CentralPoint.x,
FEndPad->CentralPoint.y - FEndObject->CentralPoint.y);
FEndObject->Relations[FEndPad] = distance;
}
}
}
}[/code]剩下的就是在TCbwObject类中加上相应的支持,首先是变量
std::map<TPad *, TCbwFloatPoint> Relations; // 关系点集合,加上相对位置,便于后续调整
然后是需要在编辑操作之后的调整工作[code]void __fastcall TCbwObject::RefreshRelations(double xRatio, double yRatio, double angle)
{
CBW_ITERATOR_MAP(TPad *, TCbwFloatPoint, Relations)
{
TPad * pad = it->first;
TCbwRelationShip * relation = dynamic_cast<TCbwRelationShip *>(pad->;ParentObject);
if(!relation) continue;
relation->RefreshPosition(this, it->second, xRatio, yRatio, angle);
}
}[/code]其实交由关系类完成。[code]void __fastcall TCbwRelationShip::RefreshPosition(
TCbwObject * object,
TCbwFloatPoint distancePoint,
double xRatio, double yRatio, double angle)
{
if(Selected) return;
double dx = distancePoint.x * xRatio;
double dy = distancePoint.y * yRatio;
if(angle == 90)
{
double t = dx;
dx = dy;
dy = t;
}
dx += object->CentralPoint.x;
dy += object->CentralPoint.y;
if(FBeginObject == object)
BeginPoint = TCbwFloatPoint(dx, dy);
else
EndPoint = TCbwFloatPoint(dx, dy);
}[/code]最后分别针对各种编辑操作进行处理:[code]DragBy: RefreshRelations(1, 1, 0);
FlipHorzOnPoint: RefreshRelations(-1, 1, 0);
FlipVertOnPoint: RefreshRelations(1, -1, 0);
Rotate: RefreshRelations(1, 1, 90);
Zoom: RefreshRelations(horzRatio, vertRatio, 0);[/code]最后,在主窗口中处理一下关系对象的创建与点添加过程的相关操作,即鼠标点下的对象将绑定到关系对象中。
运行结果令我满意。
拖动控点以编辑形状
拖动相关对象,测试连接关系是否保持
也可以通过对象浏览器进行编辑