有个项目的需求是需要把多个圆环的区域合并。得到包络线和内部空洞的线。
查了查google。类似的实现比较少见。简单的说说我的实现方法。
基本想法就是通过判断一段圆弧和圆环组的关系,来判断这段圆弧是否是属于合并后的包络线的一部分
如图,左边的圆盒右边的圆相交。则这两个圆就被分成了两段。
对外包络线,如上图。将所有圆环的外圆(粗线)相交。把每个圆都切成一段段的圆弧,如左边的黑色圆,则切成了黑色段和黑色+红色的两段。对每一段,我们测试这一段弧是否在其它圆环的大圆内部,如果这段圆弧不在其它的环的大圆内部(不包括内部小圆),则这段圆弧属于外包络线的一部分。否则不是。
内部透空的小圆的合并稍微麻烦一些。因为透空的部分,不光是小圆的弧,还会有大圆弧的参与。
考察小圆的圆弧,同样的,我们将小圆 和其它圆环的求交点,把小圆切成一段段的弧线(注意小圆和其它环求交的时候,就要跟这个环的大小圆都交一遍,而不光是小圆和小圆了)。如果这一段弧线不在这组圆环内(指的是环的内部,而不是大圆的内部),则表示这段弧是属于内包络线的一部分。
考察大圆的弧,大圆产生内包络线的原因主要是大圆会抵消一部分小圆的透空作用。所以同样的我们将大圆 和其它圆环的求交点,把小圆切成一段段的弧线--跟外包络不同,这次求交的时候,该大圆要和其它圆环的大小圆都求交,而不光是大圆对大圆。 对每一段圆弧,如果弧线不在环的内部,而且该段弧应该还需要位于某个环的小圆内部(看图就明白为什么了)。则该大圆弧也是内包络的一部分。
这里求圆弧在环的内部还是在大小圆内,有一个偷懒的方法。因为我们已经保证这段圆弧除了两个端点以外不和其它任何弧相交,所以只要用这段弧的中点来代替弧来作判断就好了。使得复杂性大大降低了。
以上是效果图,三个环合并成一个区域。
以下是主要代码
class CRingGroup
{
public:
bool load(const wchar_t* _ringGroupsFile)
{
xXmlDocument doc;
if(doc.load(_ringGroupsFile) == false )
return false;
xXmlNode::XmlNodes ringNodes;
doc.findAllNode(L"ring" , ringNodes);
if(ringNodes.size() == 0)
return false;
for(size_t i = 0 ; i < ringNodes.size() ; i ++)
{
xXmlNode* pNode = ringNodes[i];
xRing _ring;
_ring.m_InnerR = pNode->float_value(L"r1");
_ring.m_OutR = pNode->float_value(L"r2");
_ring.m_Center = pNode->get_value<float2>(L"center");
m_vRings.push_back(_ring);
}
}
void toSplineDrawer(CSplineDraw& drawer)
{
drawer.m_vArcs.clear();
for(int i = 0 ; i < m_vRings.size() ; i ++)
{
std::vector<float> CutPoints;
xRing& ring = m_vRings[i];
ring.toArcs(CutPoints , true , drawer.m_vArcs);
}
for(int i = 0 ; i < m_vRings.size() ; i ++)
{
std::vector<float> CutPoints;
xRing& ring = m_vRings[i];
ring.toArcs(CutPoints , false , drawer.m_vArcs);
}
}
void toInnerLine(std::vector<xArc>& vOut)
{
for(int i = 0 ; i < m_vRings.size() ; i ++)
{
std::vector<float> vCutPoints;
std::vector<xArc> vArcs;
//先来切内圆
xRing& _ring = m_vRings[i];
for(int j = 0 ; j < m_vRings.size() ; j ++)
{
if(j == i) continue;
xRing& _ring2 = m_vRings[j];
_ring.CutByCircle(_ring2.m_Center , _ring2.m_OutR , vCutPoints , false );
_ring.CutByCircle(_ring2.m_Center , _ring2.m_InnerR , vCutPoints , false);
}
_ring.toArcs(vCutPoints , false , vArcs);
//内圆的弧,必须在所有的外圆的外部。
for(int iArc = 0 ; iArc < vArcs.size() ; iArc ++)
{
xArc& _arc = vArcs[iArc];
bool bInRing = false;
for(int k = 0 ; k < m_vRings.size() ; k ++)
{
if( k == i ) continue;
xRing& _ring2 = m_vRings[k];
if( true == _ring2.IsInRing( _arc ) )
{
bInRing = true;
}
}
//////////////////////////////////////////////////////////////////////////
if(bInRing == false)
{
vOut.push_back(_arc);
}
}
//内圆切完了。来切外圆套在其它内圆中的部分。
vCutPoints.clear();
vArcs.clear();
for(int j = 0 ; j < m_vRings.size() ; j ++)
{
if(j == i) continue;
xRing& _ring2 = m_vRings[j];
_ring.CutByCircle(_ring2.m_Center , _ring2.m_OutR , vCutPoints , true );
_ring.CutByCircle(_ring2.m_Center , _ring2.m_InnerR , vCutPoints , true);
}
_ring.toArcs(vCutPoints , true , vArcs);
//内圆的弧,必须在所有的外圆的外部。
for(int iArc = 0 ; iArc < vArcs.size() ; iArc ++)
{
xArc& _arc = vArcs[iArc];
bool bInRing = false;
bool bInSmallRing = false;
for(int k = 0 ; k < m_vRings.size() ; k ++)
{
if( k == i ) continue;
xRing& _ring2 = m_vRings[k];
if( true == _ring2.IsInRing( _arc ) )
{
bInRing = true;
}
if( true == _ring2.IsInSmallRing( _arc) )
{
bInSmallRing = true;
}
}
//////////////////////////////////////////////////////////////////////////
if(bInRing == false && bInSmallRing == true)
{
vOut.push_back(_arc);
}
}
}
return ;
}
void toOutLine(std::vector<xArc>& vOut)
{
//return ;
for(int i = 0 ; i < m_vRings.size() ; i ++)
{
std::vector<float> vCutPoints;
xRing& _ring = m_vRings[i];
for(int j = 0 ; j < m_vRings.size() ; j ++)
{
if(j == i) continue;
xRing& _ring2 = m_vRings[j];
_ring.CutByCircle(_ring2.m_Center , _ring2.m_OutR , vCutPoints , true);
}
std::vector<xArc> vArcs;
_ring.toArcs(vCutPoints , true , vArcs);
//判断生成的圆弧是不是在内部。
for(int iArc = 0 ; iArc < vArcs.size() ; iArc ++)
{
xArc& _arc = vArcs[iArc];
bool bInRing = false;
for(int k = 0 ; k < m_vRings.size() ; k ++)
{
if( k == i ) continue;
xRing& _ring2 = m_vRings[k];
if( true == _ring2.IsInOutRing(_arc ) )
{
bInRing = true;
}
}
//////////////////////////////////////////////////////////////////////////
if(bInRing == false)
{
vOut.push_back(_arc);
}
}
}
return ;
}
public:
std::vector<xRing> m_vRings;
};