设为首页收藏本站

中国膜结构网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

膜结构车棚
膜结构车棚膜结构资质国产膜材 膜结构网中国膜结构协会
查看: 217|回复: 1

objectARX将直线,圆弧,多段线转化为(合并)多段线

[复制链接]
  • TA的每日心情
    开心
    2021-6-19 14:40
  • 签到天数: 1539 天

    [LV.Master]伴坛终老

    发表于 2020-12-29 16:12 | 显示全部楼层 |阅读模式
    1. 将头文件和实现文分别复制后,可直接使用:


    2. 头文件xxxx.h:


    3. #pragma once
    4. #include <vector>
    5. using namespace std;


    6. //给所有的点编号,多次出现的点只编一个号,后面用编号来代替点,简化过程
    7. struct PointNum
    8. {
    9. AcGePoint3d pt; //点
    10. int nNum;  //编号
    11. };


    12. //相连的点,用编号与编号连表示
    13. struct NumMap
    14. {
    15. int nNum1;  //编号
    16. int nNum2;  //编号
    17. double dBulge; //凸度
    18. };


    19. //绘制点
    20. struct DrawPoint
    21. {
    22. int nNum;  //编号
    23. double dBulge; //到下一点的凸度
    24. };


    25. class UserHelp
    26. {
    27. public:
    28. UserHelp(void);
    29. public:
    30. ~UserHelp(void);
    31. private:
    32. //-----------------------------------------------------------------------------
    33. //Summary:找出与这一点相连的下一点,以及与之相连的凸度
    34. //Par:
    35. // nm   相连信息数组
    36. // nThisNum 点编号
    37. // dThisBulge 凸度
    38. // nNextNum 点编号(查找不成功时,为-1)
    39. //Return:
    40. // 查找成功为true,查找不成功为false
    41. //-----------------------------------------------------------------------------
    42. static bool FindNextPointNum(vector<NumMap>& nm, int nThisNum, double& dThisBulge, int& nNextNum);


    43. //-----------------------------------------------------------------------------
    44. //Summary:通过编号查找相应的点
    45. //Par:
    46. // vecPt2Num 点编号数组
    47. // dThisBulge 凸度
    48. // ptThis  对应的点
    49. //Return:
    50. // 查找成功为true,查找不成功为false
    51. //-----------------------------------------------------------------------------
    52. static bool NumToPoint(vector<PointNum>& vecPt2Num, int nThisNum, AcGePoint3d& ptThis);


    53. //-----------------------------------------------------------------------------
    54. //Summary:将实体添加到数据库中
    55. //Par:
    56. // pEnt 实体指针  
    57. //Return:
    58. // 返回添加实体ID号
    59. //-----------------------------------------------------------------------------
    60. static AcDbObjectId Append(AcDbEntity* pEnt);


    61. //-----------------------------------------------------------------------------
    62. //Summary:计算圆弧的凸度
    63. //Par:
    64. // pArc 圆弧指针  
    65. //Return:
    66. // 返回计算的结果
    67. //-----------------------------------------------------------------------------
    68. static double GetBulge(AcDbArc*& pArc);


    69. //-----------------------------------------------------------------------------
    70. //Summary:得到某两点间的绘制信息,包括点编号,连接信息
    71. //Par:
    72. // vecPt2Num 点编号数组
    73. // vecNumMap 连接信息数组
    74. // nCodeNum 需要编号的第几个点
    75. // ptStart  起点
    76. // ptEnd  终点
    77. // dBulge  ptStart到ptEnd的凸度
    78. //Return:
    79. // 无。
    80. //-----------------------------------------------------------------------------
    81. static void GetDrawInfo(vector<PointNum>& vecPt2Num, vector<NumMap>& vecNumMap, int& nCodeNum,
    82. const AcGePoint3d& ptStart, const AcGePoint3d ptEnd, double dBulge);


    83. //-----------------------------------------------------------------------------
    84. //Summary:将直线,圆弧,多段线转化为(合并)多段线
    85. //Par:
    86. // idObjArr 直线,圆弧,多段线的id
    87. // idPolyline 转化后多段线的ID
    88. //Return:
    89. // 转换成功为true,不成功为false。
    90. //-----------------------------------------------------------------------------
    91. public:
    92. static  bool ChangeToPolyLine(AcDbObjectIdArray& idObjArr, AcDbObjectId& idPolyline);
    93. };








    94. 实现文件xxxx.cpp:





    95. #include "StdAfx.h"
    96. #include "UserHelp.h"
    97. #include <algorithm>


    98. UserHelp::UserHelp(void)
    99. {
    100. }


    101. UserHelp::~UserHelp(void)
    102. {
    103. }


    104. AcDbObjectId UserHelp::Append(AcDbEntity* pEnt)
    105. {
    106. AcDbBlockTable *pBlockTable;
    107. acdbHostApplicationServices()->workingDatabase()
    108.   ->getSymbolTable(pBlockTable, AcDb::kForRead);


    109. AcDbBlockTableRecord *pBlockTableRecord;
    110. pBlockTable->getAt(ACDB_MODEL_SPACE, pBlockTableRecord,
    111.   AcDb::kForWrite);
    112. pBlockTable->close();


    113. AcDbObjectId EntId;
    114. pBlockTableRecord->appendAcDbEntity(EntId, pEnt);


    115. pBlockTableRecord->close();
    116. return EntId;
    117. }


    118. double UserHelp::GetBulge(AcDbArc*& pArc)
    119. {
    120. double dStartAngle = pArc->startAngle();
    121. double dEndAngle = pArc->endAngle();


    122. double dAlfa = dEndAngle - dStartAngle;
    123. if (dAlfa < 0.0)//如果终点角度小于起点角度,取补角
    124. {
    125.   dAlfa = 2 * PI + dAlfa;
    126. }


    127. double dBulge = 0.0;


    128. dBulge = tan((dAlfa) / 4.0);
    129. return dBulge;
    130. }




    131. bool UserHelp::FindNextPointNum(vector<NumMap>& nm, int nThisNum, double& dThisBulge, int& nNextNum)
    132. {
    133. bool bFind = false;


    134. //以第一个编号给为起点查找
    135. vector<NumMap>::iterator itr;
    136. for (itr = nm.begin(); itr != nm.end(); itr++)
    137. {
    138.   if ((*itr).nNum1 == nThisNum)
    139.   {
    140.    nNextNum = (*itr).nNum2;
    141.    dThisBulge = (*itr).dBulge;
    142.    bFind = true;
    143.    nm.erase(itr);
    144.    break;
    145.   }
    146. }


    147. if (!bFind)//如果以第一个编号给为起点未找到,就以第二个编号给为起点查找
    148. {
    149.   for (itr = nm.begin(); itr != nm.end(); itr++)
    150.   {
    151.    if ((*itr).nNum2 == nThisNum)
    152.    {
    153.     nNextNum = (*itr).nNum1;
    154.     dThisBulge = -(*itr).dBulge;//凸度取反
    155.     bFind = true;
    156.     nm.erase(itr);
    157.     break;
    158.    }
    159.   }
    160. }


    161. if (bFind)
    162. {
    163.   return true;
    164. }
    165. else
    166. {
    167.   nNextNum = -1;
    168.   dThisBulge = 0.0;
    169. }

    170. return false;
    171. }


    172. bool UserHelp::NumToPoint(vector<PointNum>& vecPt2Num, int nThisNum, AcGePoint3d& ptThis)
    173. {
    174. bool bFind = false;
    175. vector<PointNum>::iterator itr;
    176. for (itr = vecPt2Num.begin(); itr != vecPt2Num.end(); itr++)
    177. {
    178.   if ((*itr).nNum == nThisNum)
    179.   {
    180.    ptThis = (*itr).pt;
    181.    bFind = true;
    182.    break;
    183.   }
    184. }


    185. return bFind;
    186. }


    187. void UserHelp::GetDrawInfo(vector<PointNum>& vecPt2Num, vector<NumMap>& vecNumMap, int& nCodeNum,
    188.      const AcGePoint3d& ptStart, const AcGePoint3d ptEnd, double dBulge)
    189. {
    190. bool bStartNew = true; //起点是否编号
    191. bool bEndNew = true; //终点是否编号


    192. NumMap nummap;//两点之间的联系信息
    193. nummap.dBulge = dBulge;


    194. //处理起点
    195. vector<PointNum>::iterator itr;
    196. for (itr = vecPt2Num.begin(); itr != vecPt2Num.end(); itr++)
    197. {
    198.   if ((*itr).pt == ptStart)
    199.   {
    200.    bStartNew = false;
    201.    nummap.nNum1 = (*itr).nNum;
    202.    break;
    203.   }
    204. }


    205. if (bStartNew)
    206. {
    207.   PointNum pn;
    208.   pn.nNum = nCodeNum++;
    209.   pn.pt = ptStart;
    210.   vecPt2Num.push_back(pn);


    211.   nummap.nNum1 = pn.nNum;
    212. }


    213. //处理终点
    214. for (itr = vecPt2Num.begin(); itr != vecPt2Num.end(); itr++)
    215. {
    216.   if ((*itr).pt == ptEnd)
    217.   {
    218.    bEndNew = false;
    219.    nummap.nNum2 = (*itr).nNum;
    220.    break;
    221.   }
    222. }


    223. if (bEndNew)
    224. {
    225.   PointNum pn;
    226.   pn.nNum = nCodeNum++;
    227.   pn.pt = ptEnd;
    228.   vecPt2Num.push_back(pn);
    229.   nummap.nNum2 = pn.nNum;
    230. }


    231. //添加联系信息
    232. vecNumMap.push_back(nummap);
    233. }


    234. bool UserHelp::ChangeToPolyLine(AcDbObjectIdArray& idObjArr, AcDbObjectId& idPolyline)
    235. {
    236. vector<PointNum> vecPt2Num;//所有点编号
    237. vector<NumMap> vecNumMap;//所有连接信息
    238. int nCodeNum = 0;


    239. int length = idObjArr.length();
    240. for (int i = 0; i < length; i++)
    241. {
    242.   // 获得指向当前元素的指针
    243.   AcDbEntity *pEnt;
    244.   Acad::ErrorStatus es = acdbOpenAcDbEntity(pEnt, idObjArr.at(i),
    245.    AcDb::kForRead);


    246.   // 选择到作为边界的多段线了,直接跳过该次循环
    247.   if (es != Acad::eOk)   
    248.   {
    249.    continue;
    250.   }   


    251.   if(pEnt->isKindOf(AcDbArc::desc()))//圆弧
    252.   {
    253.    AcDbArc* pArc = AcDbArc::cast(pEnt);
    254.    if (pArc == NULL)
    255.    {
    256.     continue;
    257.    }


    258.    AcGePoint3d ptStart, ptEnd;
    259.    pArc->getStartPoint(ptStart);
    260.    pArc->getEndPoint(ptEnd);


    261.    double dBulge = 0.0;
    262.    dBulge = UserHelp::GetBulge(pArc);


    263.    UserHelp::GetDrawInfo(vecPt2Num, vecNumMap, nCodeNum, ptStart, ptEnd, dBulge);
    264.    pArc->close();   
    265.   }
    266.   else if (pEnt->isKindOf(AcDbLine::desc()))//直线
    267.   {
    268.    AcDbLine* pLine = AcDbLine::cast(pEnt);
    269.    if (pLine == NULL)
    270.    {
    271.     continue;
    272.    }   


    273.    AcGePoint3d ptStart, ptEnd;
    274.    pLine->getStartPoint(ptStart);
    275.    pLine->getEndPoint(ptEnd);


    276.    double dBulge = 0.0;


    277.    UserHelp::GetDrawInfo(vecPt2Num, vecNumMap, nCodeNum, ptStart, ptEnd, dBulge);


    278.    pLine->close();
    279.   }


    280.   else if (pEnt->isKindOf(AcDbPolyline::desc()))
    281.   {
    282.    AcDbPolyline* pPolyLine = AcDbPolyline::cast(pEnt);
    283.    if (pPolyLine == NULL)
    284.    {
    285.     continue;
    286.    }


    287.    int nVerCount = 0;
    288.    nVerCount = pPolyLine->numVerts();
    289.    if (nVerCount <= 1)
    290.    {
    291.     continue;
    292.    }
    293.    AcGePoint3d ptFirst;
    294.    double dBugle = -1000.0;
    295.    pPolyLine->getPointAt(0, ptFirst);
    296.    pPolyLine->getBulgeAt(0,dBugle);


    297.    for (int i = 1; i < nVerCount; i++)
    298.    {
    299.     AcGePoint3d ptVer;
    300.     pPolyLine->getPointAt(i, ptVer);
    301.     UserHelp::GetDrawInfo(vecPt2Num, vecNumMap, nCodeNum, ptFirst, ptVer, dBugle);


    302.     ptFirst = ptVer;
    303.     pPolyLine->getBulgeAt(i, dBugle);
    304.    }
    305.    pPolyLine->close();   
    306.   }


    307.   pEnt->close();
    308. }   


    309. AcGePoint3d pt1, pt2;
    310. double dBulge = 0.0;


    311. vector<NumMap>::iterator itr1;// vecNumMap;
    312. vector<NumMap>::iterator itr2;// vecNumMap;
    313. vector< vector<DrawPoint> > vecLines;


    314. if (vecNumMap.size() > 0)
    315. {
    316.   itr1 = vecNumMap.begin();
    317. }


    318. while(itr1 != vecNumMap.end())
    319. {
    320.   //从某点出发向一个方向搜索
    321.   vector<DrawPoint> vecOneLine;


    322.   int nBegin = (*itr1).nNum1;
    323.   int nThisNum = nBegin;   
    324.   int nLastNum = nThisNum;


    325.   DrawPoint drawpoint;   


    326.   while(vecNumMap.size() > 0)  
    327.   {
    328.    bool bSuc = false;
    329.    double dThisBulge = 0.0;
    330.    int nNextNum = 0;


    331.    bSuc = UserHelp::FindNextPointNum(vecNumMap, nThisNum, dThisBulge, nNextNum);
    332.    if (bSuc)
    333.    {
    334.     drawpoint.nNum = nThisNum;
    335.     drawpoint.dBulge =dThisBulge;
    336.     vecOneLine.push_back(drawpoint);
    337.     nThisNum = nNextNum;
    338.     nLastNum = nThisNum;
    339.    }   
    340.    else
    341.    {
    342.     break;
    343.    }
    344.   }


    345.   //处理最近一个点
    346.   drawpoint.nNum = nLastNum;
    347.   drawpoint.dBulge = 0.0;
    348.   vecOneLine.push_back(drawpoint);




    349.   //从某点出发向另一个方向搜索
    350.   vector<DrawPoint> vecOtherLine;
    351.   nThisNum = nBegin;
    352.   nLastNum = nThisNum;
    353.   while(vecNumMap.size() > 0)  
    354.   {
    355.    bool bSuc = false;
    356.    double dThisBulge = 0.0;
    357.    int nNextNum = 0;


    358.    bSuc = UserHelp::FindNextPointNum(vecNumMap, nThisNum, dThisBulge, nNextNum);
    359.    if (bSuc)
    360.    {
    361.     drawpoint.nNum = nNextNum;
    362.     drawpoint.dBulge =dThisBulge;
    363.     vecOtherLine.push_back(drawpoint);
    364.     nThisNum = nNextNum;
    365.    }   
    366.    else
    367.    {
    368.     break;
    369.    }
    370.   }


    371.   //两个方向拼接
    372.   reverse(vecOtherLine.begin(), vecOtherLine.end());
    373.   vector<DrawPoint>::iterator itrTmp;
    374.   for (itrTmp = vecOtherLine.begin(); itrTmp != vecOtherLine.end(); itrTmp++)
    375.   {
    376.    (*itrTmp).dBulge *= -1.0;
    377.   }


    378.   vecOtherLine.insert(vecOtherLine.end(), vecOneLine.begin(), vecOneLine.end());


    379.   vecLines.push_back(vecOtherLine);
    380.   itr1 = vecNumMap.begin();
    381. }


    382. //绘制多段线
    383. vector< vector<DrawPoint> >::iterator itr_1;
    384. vector<DrawPoint>::iterator itr_2;
    385. for (itr_1 = vecLines.begin(); itr_1 != vecLines.end(); itr_1++)
    386. {
    387.   AcDbPolyline* pPolyLine = new AcDbPolyline;
    388.   int i = 0;
    389.   vector<DrawPoint>& vecPoints = (*itr_1);
    390.   for (itr_2 = vecPoints.begin(); itr_2 != vecPoints.end(); itr_2++)
    391.   {
    392.    int nNum = (*itr_2).nNum;
    393.    AcGePoint3d ptVer;
    394.    if (UserHelp::NumToPoint(vecPt2Num, nNum, ptVer))
    395.    {
    396.     pPolyLine->addVertexAt(i++, ptVer.convert2d(AcGePlane::kXYPlane),(*itr_2).dBulge);
    397.    }
    398.   }


    399.   idPolyline = UserHelp::Append(pPolyLine);
    400.   pPolyLine->close();
    401. }
    402. return true;
    403. }
    复制代码
    回复


    http://www.mjgw.org/ 专业从事膜结构设计、制作加工、施工安装的膜结构工程服务,能够为客户提供专业的膜结构整体解决方案。做中国最好的膜结构综合服务平台。欢迎大家联系电话:198-7840-1958,QQ:463017170.
    相关关键词:膜结构车棚,膜结构车棚覆盖,膜结构车棚公司,膜结构车棚多少钱,膜结构车棚厂家,膜结构车棚价格,社区膜结构车棚,膜结构车棚膜布厂家 ,膜结构车棚哪家好,膜结构车棚多少钱一米,膜结构车棚报价,膜结构车棚哪里有,膜结构车棚定制,膜结构车棚安装,膜结构车棚设计,膜结构车棚电话,膜结构车棚加工,膜结构车棚膜布价格,膜结构车棚批发,膜结构车棚制造商,膜结构车棚生产厂家,膜结构车棚设计,膜结构车棚施工,膜结构车棚多少钱一平米,膜结构车棚订制,张拉膜车棚,张拉膜车棚覆盖,张拉膜车棚公司,张拉膜车棚多少钱,张拉膜车棚厂家,张拉膜车棚价格,社区张拉膜车棚,张拉膜车棚膜布厂家 ,张拉膜车棚哪家好,张拉膜车棚多少钱一米,张拉膜车棚报价,张拉膜车棚哪里有,张拉膜车棚定制,张拉膜车棚安装,张拉膜车棚设计,张拉膜车棚电话,张拉膜车棚加工,张拉膜车棚膜布价格,张拉膜车棚批发,张拉膜车棚制造商,张拉膜车棚生产厂家,张拉膜车棚设计,张拉膜车棚施工,张拉膜车棚多少钱一平米,张拉膜车棚订制,常用膜材品牌:德国杜肯、法国法拉利、德国海德斯、德国米乐、日本平岗、韩国秀博、比利时希运、美国赫虏伯、中国科宝、上海慧遥。

    使用道具 举报

  • TA的每日心情
    开心
    2021-6-19 14:40
  • 签到天数: 1539 天

    [LV.Master]伴坛终老

     楼主| 发表于 2020-12-29 16:12 | 显示全部楼层
    1. void CCommonModel::AppendPoly( const AcDbObjectIdArray& objArray,AcDbPolyline *pPolyNew )
    2. {
    3.         AcDbObjectIdArray objIdArray = objArray;
    4.         std::multimap<AcGePoint3d,int> mapPoints;                        // AcGePoint3d不能排序,要重载符号“<”;
    5.         std::multimap<AcGePoint3d,int>::iterator ite1;

    6.         std::multimap<AcGePoint3d,int>::iterator ite2;
    7.         for (int i = 0; i < objIdArray.length(); i++)
    8.         {
    9.                 AcDbObjectId objId;
    10.                 objId = objIdArray.at(i);
    11.                 AcGePoint3d ptStart;
    12.                 AcGePoint3d ptEnd;
    13.                 AcDbObjectPointer<AcDbPolyline> pPoly(objId,AcDb::kForRead);
    14.                 pPoly->getStartPoint(ptStart);
    15.                 pPoly->getEndPoint(ptEnd);
    16.                 if (i == 0)
    17.                 {
    18.                         mapPoints.insert(std::make_pair(ptStart,1));
    19.                         mapPoints.insert(std::make_pair(ptEnd,1));
    20.                 }
    21.                 else
    22.                 {
    23.                         ite1 = mapPoints.find(ptStart);
    24.                         ite2 = mapPoints.find(ptEnd);
    25.                         if (ite1 != mapPoints.end())
    26.                         {
    27.                                 (ite1->second)++;
    28.                         }
    29.                         else
    30.                         {
    31.                                 mapPoints.insert(std::make_pair(ptStart,1));
    32.                         }
    33.                         ite2 = mapPoints.find(ptEnd);
    34.                         if (ite2 != mapPoints.end())
    35.                         {
    36.                                 (ite2->second)++;
    37.                         }
    38.                         else
    39.                         {
    40.                                 mapPoints.insert(std::make_pair(ptEnd,1));
    41.                         }
    42.                 }
    43.         }

    44.         AcGePoint3d ptStartOk;
    45.         for (ite1 = mapPoints.begin(); ite1 != mapPoints.end(); ite1++)
    46.         {
    47.                 if (ite1->second == 1)
    48.                 {
    49.                         ptStartOk = ite1->first;
    50.                         break;
    51.                 }
    52.         }

    53.         int plIndex = 0;
    54.         while(objIdArray.length()>0)                                                               
    55.         {
    56.                 for (int i = 0; i < objIdArray.length(); i++)                // 问题,如果几条多线段不是首尾相接的,那么死循环。
    57.                 {
    58.                         AcDbObjectId objId;
    59.                         objId = objIdArray.at(i);
    60.                         AcGePoint3d ptStart;
    61.                         AcGePoint3d ptEnd;
    62.                         AcDbObjectPointer<AcDbPolyline> pPoly(objId,AcDb::kForRead);
    63.                         pPoly->getStartPoint(ptStart);
    64.                         pPoly->getEndPoint(ptEnd);

    65.                         AcGePoint2d pt ;
    66.                         double bulge = 0.0;
    67.                         if (ptStartOk == ptStart)                                                        // 判断点相等,用自带的函数或者用点距离
    68.                         {
    69.                                 for (int j= 0; j < pPoly->numVerts(); j++ )                // 问题:最后点会被下一个加入点给覆盖掉,即有重合点
    70.                                 {
    71.                                         pPoly->getPointAt(j,pt);
    72.                                         pPoly->getBulgeAt(j,bulge);
    73.                                         pPolyNew->addVertexAt(plIndex,pt,bulge);
    74.                                         plIndex++;
    75.                                 }
    76.                                 ptStartOk = ptEnd;
    77.                                 objIdArray.removeAt(i);
    78.                         }
    79.                         else if(ptStartOk == ptEnd)                                                        // 判断点相等,用自带的函数或者用点距离
    80.                         {
    81.                                 for(int k = pPoly->numVerts() - 1;k > 0; k--)
    82.                                 {
    83.                                         pPoly->getPointAt(k,pt);
    84.                                         if(k > 0)
    85.                                         {
    86.                                                 pPoly->getBulgeAt(k - 1,bulge);
    87.                                         }
    88.                                         else
    89.                                         {
    90.                                                 pPoly->getBulgeAt(0,bulge);
    91.                                         }
    92.                                         pPolyNew->addVertexAt(plIndex,pt,-bulge);
    93.                                         plIndex++;
    94.                                 }
    95.                                 ptStartOk = ptStart;
    96.                                 objIdArray.removeAt(i);
    97.                         }
    98.                         else
    99.                         {

    100.                         }
    101.                 }

    102.         }
    103. }

    104. bool operator < (AcGePoint3d pt3dS,AcGePoint3d pt3dE)
    105. {
    106.         if (pt3dS.x < pt3dE.x)
    107.         {
    108.                 return true;
    109.         }
    110.         else if (pt3dS.x == pt3dE.x)
    111.         {
    112.                 if (pt3dS.y < pt3dE.y)
    113.                 {
    114.                         return true;
    115.                 }
    116.                 else if (pt3dS.y == pt3dE.y)
    117.                         {
    118.                                 return false;
    119.                         }
    120.                 else
    121.                 {
    122.                         return false;
    123.                 }
    124.         }
    125.         else
    126.         {
    127.                 return false;
    128.         }
    129. }
    复制代码
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    关闭

    推荐膜材品牌上一条 /6 下一条

    进口膜材 国产膜材 pvdf膜材ptfe膜材ETFE膜材
    最好的膜结构公司 一级膜结构资质 膜结构一级资质
    膜结构设计-膜结构十大品牌-etfe设计-充气膜结构
    诺科膜结构
    遨都膜结构设计
    中国膜结构网
    中国空间膜结构

    QQ|申请友链|手机版|中国膜结构论坛