DXF笔记:多义线线宽模拟实现

发布于:2023-01-20 ⋅ 阅读:(412) ⋅ 点赞:(0)

线宽模拟思路

参看之前的文章:DXF笔记:多义线线宽的绘制思路

示例代码

演示了实现思路,展示了核心代码,因为依赖了其他一些代码,所以不能直接运行。



//osg图元项 shanql 2022.8.12
struct tagOsgPrimitiveItem
{
	tagOsgPrimitiveItem() :m_bFillMode(false){}
	osg::ref_ptr<osg::Vec3dArray> m_pVertextArray; //图元顶点数组
	osg::PrimitiveSet::Mode m_enPrimitiveMode; //图元类型,如线,三角形等
	bool m_bFillMode; //是否为填充模式

	//如果是直线,这个保存着直线原始起末点
	AcGePoint3d m_ptLineStart;
	AcGePoint3d m_ptLineEnd;
};


/*
* @brief 获取GeomArc的散列点
* @author shanql
* @date 2022.8.11
*/
static osg::ref_ptr<osg::Vec3dArray> GetGeomArcSamplePoint(std::shared_ptr<GeomArc> pGeomArc)
{
	if (!pGeomArc)
	{
		UEASSERT(false, _T("object type is not symbol arc"));
		return nullptr;
	}

	double dStartAngle = pGeomArc->GetStartAngle() / PI * 180;
	double dEndAngle = pGeomArc->GetEndAngle() / PI * 180;
	double dRadius = pGeomArc->GetRadius();

	std::vector<osg::Vec3d> vlist;
	double end;
	double start;
	if (dStartAngle > dEndAngle)
	{
		start = dStartAngle;
		end = dEndAngle + 360;
	}
	else {
		start = dStartAngle;
		end = dEndAngle;
	}

	double theta = 5.0; // we generate polyline from "spokes" at theta degrees at arc's center

// 		if (_useAccuracy) {
// 			// we generate points on a polyline where each point lies on the arc, thus the maximum error occurs at the midpoint of each line segment where it lies furthest inside the arc
// 			// If we divide the segment in half and connect the bisection point to the arc's center, we have two rightangled triangles with
// 			// one side=r-maxError, hypotenuse=r, and internal angle at center is half the angle we will step with:
// 			double maxError=min(_maxError,_radius); // Avoid offending acos() in the edge case where allowable deviation is greater than radius.
// 			double newtheta=acos( (_radius-maxError) / _radius);
// 			newtheta=osg::RadiansToDegrees(newtheta)*2.0;
// 			//cout<<"r="<<_radius<<" _me="<<_maxError<<" (_radius-_maxError)="<<(_radius-_maxError)<<" newtheta="<<newtheta<<endl;
// 			// Option to only use the new accuracy code when it would improve on the accuracy of the old method
// 			if (_improveAccuracyOnly) {
// 				theta=min(newtheta,theta);
// 			} else {
// 				theta=newtheta;
// 			}
// 		}


	int numsteps = (int)((end - start) / theta);
	//cout<<"arc theta="<<osg::RadiansToDegrees(theta)<<" end="<<end<<" start="<<start<<" numsteps="<<numsteps<<" e-s/theta="<<((end-start)/theta)<<" end-start="<<(end-start)<<endl;
	if (numsteps * theta < (end - start)) numsteps++;
	numsteps = max(numsteps, 2); // Whatever else, minimum representation of an arc is a straightline

	double angle_step = (end - start) / 180 * PI;
	angle_step /= (double)numsteps;

	//拆分圆弧
	AcGePoint3d tmpCenter = pGeomArc->GetCenter();
	osg::Vec3d a(tmpCenter.x, tmpCenter.y, tmpCenter.z);
	osg::Vec3d b;
	AcGePoint3d tempPt;
	double angle1 = start / 180 * PI;
	AcGeVector3d mNormal = pGeomArc->GetNormal();

	pGeomArc->GetStartPoint(tempPt);
	vlist.push_back(osg::Vec3d(tempPt.x, tempPt.y, tempPt.z));
	for (int r = 0; r < numsteps; r++)
	{
		tempPt.rotateBy(angle_step, mNormal, tmpCenter);
		b = osg::Vec3d(tempPt.x, tempPt.y, tempPt.z);
		vlist.push_back(b);
	}

	//设置顶点数组
	osg::ref_ptr<osg::Vec3dArray> pVertextArray = new osg::Vec3dArray();
	for (auto it = vlist.begin(); it != vlist.end(); ++it)
	{
		pVertextArray->push_back(*it);
	}

	return pVertextArray;
}

/*
* @brief 模拟多义线的线宽
* @details 带线宽的直线拆分成三角形或四边形,带线宽且等宽的弧线拆分
*	成三角带(非等宽的弧线有些复杂,暂不处理宽度)
* @param pGeomPolyline 多义线指针
* @param[out] mRtOsgPrimitiveList osg的图元列表
*/
static void SimulationLineWidthForPolyline(
	const std::shared_ptr<GeomPolyline>& pGeomPolyline,
	SmartPtrVector(tagOsgPrimitiveItem)& mRtOsgPrimitiveList)
{
	if (!pGeomPolyline)
	{
		return;
	}

	AcGePoint3dArray& mGMPtArray = pGeomPolyline->GetPointArray();
	if (mGMPtArray.length() <= 1)//至少要两个顶点
	{
		return;
	}

	double dDefLineWidth = pGeomPolyline->GetDiameter(); //使用各个点的起点端点宽度

	//带线宽的直线拆分成三角形或四边形,带线宽且等宽的弧线拆分成三角带(非等宽的弧线不处理宽度)
	SmartPtrVector(tagOsgPrimitiveItem) mOsgPrimitiveList; //需要绘制的osg图元列表
	AcGePoint3d ptFirst = mGMPtArray.at(0);
	for (int i = 1; i < mGMPtArray.length(); ++i)
	{
		double dBugle = pGeomPolyline->GetPtBugleAt(i - 1);
		double dStartWidth = pGeomPolyline->GetStartWidthAt(i - 1);
		double dEndWidth = pGeomPolyline->GetEndWidthAt(i - 1);
		if (MY_DOUBLE_EQ(dStartWidth, 0.0)
			&& MY_DOUBLE_EQ(dEndWidth, 0.0))//仿照autocad, 这种情况就要使用固定宽度
		{
			dStartWidth = dDefLineWidth;
			dEndWidth = dDefLineWidth;
		}

		const AcGePoint3d& ptSecond = mGMPtArray.at(i);
		if (MY_DOUBLE_EQ(dBugle, 0.0)) //凸度为0,直线
		{
			if (MY_DOUBLE_GT(dStartWidth, 0.0) || MY_DOUBLE_GT(dEndWidth, 0.0)) //多边形模拟线宽
			{
				//与直线垂直的方向
				AcGeVector3d vecOffsetDir = (ptSecond - ptFirst).rotateBy(PI / 2.0, AcGeVector3d::kZAxis);
				vecOffsetDir.normalize();

				//点1
				osg::ref_ptr<osg::Vec3dArray> pVertextArray = new osg::Vec3dArray();
				if (MY_DOUBLE_EQ(dStartWidth, 0.0))
				{
					pVertextArray->push_back(osg::Vec3d(ptFirst.x, ptFirst.y, ptFirst.z));
				}
				else
				{
					AcGePoint3d ptTemp = ptFirst + vecOffsetDir * dStartWidth / 2.0;
					pVertextArray->push_back(osg::Vec3d(ptTemp.x, ptTemp.y, ptTemp.z));
				}

				//点2
				if (MY_DOUBLE_EQ(dEndWidth, 0.0))
				{
					pVertextArray->push_back(osg::Vec3d(ptSecond.x, ptSecond.y, ptSecond.z));
				}
				else
				{
					AcGePoint3d ptTemp = ptSecond + vecOffsetDir * dEndWidth / 2.0;
					pVertextArray->push_back(osg::Vec3d(ptTemp.x, ptTemp.y, ptTemp.z));
				}

				//均处理成4个点,后续处理拐角时好处理
				if (MY_DOUBLE_GT(dEndWidth, 0.0))
				{
					AcGePoint3d ptTemp = ptSecond + (-vecOffsetDir) * dEndWidth / 2.0;
					pVertextArray->push_back(osg::Vec3d(ptTemp.x, ptTemp.y, ptTemp.z));
				}
				else
				{
					//线宽为0时,此点会上一顶点相同
					pVertextArray->push_back(osg::Vec3d(ptSecond.x, ptSecond.y, ptSecond.z));
				}

				if (MY_DOUBLE_GT(dStartWidth, 0.0))
				{
					AcGePoint3d ptTemp = ptFirst + (-vecOffsetDir) * dStartWidth / 2.0;
					pVertextArray->push_back(osg::Vec3d(ptTemp.x, ptTemp.y, ptTemp.z));
				}
				else
				{
					//线宽为0时,此点会上一顶点相同
					pVertextArray->push_back(osg::Vec3d(ptFirst.x, ptFirst.y, ptFirst.z));
				}

				auto pOneOsgPrim = std::make_shared<tagOsgPrimitiveItem>();
				pOneOsgPrim->m_bFillMode = true;
//				if (pVertextArray->size() == 3)
//				{
//					pOneOsgPrim->m_enPrimitiveMode = osg::PrimitiveSet::TRIANGLES;
//				}
//				else
//				{
//					pOneOsgPrim->m_enPrimitiveMode = osg::PrimitiveSet::QUADS;
//				}
				pOneOsgPrim->m_enPrimitiveMode = osg::PrimitiveSet::QUADS;
				pOneOsgPrim->m_pVertextArray = pVertextArray;
				pOneOsgPrim->m_ptLineStart = ptFirst;
				pOneOsgPrim->m_ptLineEnd = ptSecond;
				mRtOsgPrimitiveList.push_back(pOneOsgPrim);
			}
			else
			{
				osg::ref_ptr<osg::Vec3dArray> pVertextArray = new osg::Vec3dArray();
				pVertextArray->push_back(osg::Vec3d(ptFirst.x, ptFirst.y, ptFirst.z));
				pVertextArray->push_back(osg::Vec3d(ptSecond.x, ptSecond.y, ptSecond.z));

				auto pOneOsgPrim = std::make_shared<tagOsgPrimitiveItem>();
				pOneOsgPrim->m_bFillMode = false;
				pOneOsgPrim->m_enPrimitiveMode = osg::PrimitiveSet::LINES;
				pOneOsgPrim->m_pVertextArray = pVertextArray;
				pOneOsgPrim->m_ptLineStart = ptFirst;
				pOneOsgPrim->m_ptLineEnd = ptSecond;
				mRtOsgPrimitiveList.push_back(pOneOsgPrim);
			}
		}
		else //弧线
		{
			double dRadian = atan(fabs(dBugle)) * 4.0; //圆弧的弧度值
			AcGeVector3d vecNormal = AcGeVector3d::kZAxis; //圆弧法向量
			AcGePoint3d ptMiddle = (ptFirst + ptSecond.asVector()) / 2.0;

			if (MY_DOUBLE_LT(dBugle, 0.0))//负的凸度值表示弧是以顺时针方向从选择的顶点至下一个顶点
			{
				vecNormal = -vecNormal;
			}

			AcGePoint3d ptCenter;
			AcGeVector3d vecRefDir;
			if (MY_DOUBLE_EQ(dRadian, PI))//==180度 半圆
			{
				//创建圆弧
				ptCenter = ptMiddle;
				vecRefDir = ptFirst - ptMiddle;
			}
			else
			{
				AcGeVector3d offsetDir = ptFirst - ptMiddle;
				offsetDir.normalize();
				double dOffsetValue = 0.0;
				if (MY_DOUBLE_GT(dRadian, PI)) //>180度,优弧
				{
					dOffsetValue = ptMiddle.distanceTo(ptFirst) / std::tan(PI - dRadian / 2.0);
					offsetDir.rotateBy(PI / 2.0, vecNormal);
				}
				else //< 180度
				{
					dOffsetValue = ptMiddle.distanceTo(ptFirst) / std::tan(dRadian / 2.0);
					offsetDir.rotateBy(-PI / 2.0, vecNormal);
				}

				//创建圆弧
				ptCenter = ptMiddle + dOffsetValue * offsetDir;
				vecRefDir = ptFirst - ptCenter;
			}

			double dRadius = ptCenter.distanceTo(ptFirst);
			if (MY_DOUBLE_GT(dStartWidth, 0.0) //暂只支持等宽的圆弧线
				&& MY_DOUBLE_EQ(dStartWidth, dEndWidth)
				&& MY_DOUBLE_GT(dRadius, dStartWidth / 2.0))
			{
				auto pSmallGeomArc = std::make_shared<GeomArc>(ptCenter,
					vecNormal,
					vecRefDir,
					dRadius - dStartWidth / 2.0,
					0.0,
					dRadian);

				auto pLargeGeomArc = std::make_shared<GeomArc>(ptCenter,
					vecNormal,
					vecRefDir,
					dRadius + dStartWidth / 2.0,
					0.0,
					dRadian);

				auto pSmallVec3Array = GetGeomArcSamplePoint(pSmallGeomArc);
				auto pLargeVec3Array = GetGeomArcSamplePoint(pLargeGeomArc);

				if (pSmallVec3Array
					&& pLargeVec3Array
					&& pSmallVec3Array->size() > 0
					&& pLargeVec3Array->size() > 0)
				{
					osg::ref_ptr<osg::Vec3dArray> pStripVec3Array = new osg::Vec3dArray();
					int nSmallArraySize = int(pSmallVec3Array->size());
					int nLargeArraySize = int(pLargeVec3Array->size());
					int sm = 0, la = 0;
					while (sm < nSmallArraySize)
					{
						pStripVec3Array->push_back(pSmallVec3Array->at(sm));
						if (la < nLargeArraySize)
						{
							pStripVec3Array->push_back(pLargeVec3Array->at(la));
						}
						else
						{
							break;
						}

						++sm;
						++la;
					}

					//构造三角带
					if (pStripVec3Array->size() >= 3)
					{
						auto pOneOsgPrim = std::make_shared<tagOsgPrimitiveItem>();
						pOneOsgPrim->m_bFillMode = true;
						pOneOsgPrim->m_enPrimitiveMode = osg::PrimitiveSet::TRIANGLE_STRIP;
						pOneOsgPrim->m_pVertextArray = pStripVec3Array;
						mRtOsgPrimitiveList.push_back(pOneOsgPrim);
					}


					//若有剩余顶点,构造扇形三角形
					osg::ref_ptr<osg::Vec3dArray> pFanVec3Array = new osg::Vec3dArray();
					if (sm < nSmallArraySize)
					{
						pFanVec3Array->push_back(pLargeVec3Array->at(nLargeArraySize - 1));
						while (sm < nSmallArraySize)
						{
							pFanVec3Array->push_back(pSmallVec3Array->at(sm));
						}
					}
					else if (la < nLargeArraySize)
					{
						pFanVec3Array->push_back(pSmallVec3Array->at(nSmallArraySize - 1));
						while (la < nLargeArraySize)
						{
							pFanVec3Array->push_back(pLargeVec3Array->at(la));
						}
					}
					if (pFanVec3Array->size() >= 3)
					{
						auto pOneOsgPrim = std::make_shared<tagOsgPrimitiveItem>();
						pOneOsgPrim->m_bFillMode = true;
						pOneOsgPrim->m_enPrimitiveMode = osg::PrimitiveSet::TRIANGLE_FAN;
						pOneOsgPrim->m_pVertextArray = pFanVec3Array;
						mRtOsgPrimitiveList.push_back(pOneOsgPrim);
					}

				}

			}
			else
			{
				auto pGeomArc = std::make_shared<GeomArc>(ptCenter,
					vecNormal,
					vecRefDir,
					ptCenter.distanceTo(ptFirst),
					0.0,
					dRadian);

				auto pVec3Array = GetGeomArcSamplePoint(pGeomArc);

				auto pOneOsgPrim = std::make_shared<tagOsgPrimitiveItem>();
				pOneOsgPrim->m_bFillMode = false;
				pOneOsgPrim->m_enPrimitiveMode = osg::PrimitiveSet::LINE_STRIP;
				pOneOsgPrim->m_pVertextArray = pVec3Array;
				mRtOsgPrimitiveList.push_back(pOneOsgPrim);
			}
		}

		ptFirst = ptSecond; //绘制下一根线
	}
}


void CreatePloyline(std::shared_ptr<GeomPolyline> pGeomPolyline, osg::ref_ptr<osg::Geode>& pRtOsgGeode)
{
	if (NULL == pRtOsgGeode)
		return;
	try
	{
		if (!pGeomPolyline)
		{
			return /*nullptr*/;
		}

		//模拟线宽处理
		SmartPtrVector(tagOsgPrimitiveItem) mOsgPrimitiveList; //需要绘制的osg图元列表
		SimulationLineWidthForPolyline(pGeomPolyline, mOsgPrimitiveList);
		if (mOsgPrimitiveList.size() <= 0)
		{
			return;
		}

		//处理下相邻直线(已用三角形或四边形模拟了线宽)之间的拐角
		std::shared_ptr<tagOsgPrimitiveItem> pPrevPrimitiveItem;
		for (auto it = mOsgPrimitiveList.begin(); it != mOsgPrimitiveList.end(); ++it)
		{
			auto pPrimitiveItem = *it;
			if (!pPrimitiveItem || !pPrimitiveItem->m_pVertextArray)
			{
				pPrevPrimitiveItem = nullptr;
				continue;
			}

			//无宽度或非直线(有线宽的直线会使用osg::PrimitiveSet::TRIANGLES或QUADS来模拟)
			if (pPrimitiveItem->m_pVertextArray->size() <= 2
				|| (pPrimitiveItem->m_enPrimitiveMode != osg::PrimitiveSet::TRIANGLES 
				&& pPrimitiveItem->m_enPrimitiveMode != osg::PrimitiveSet::QUADS))
			{
				pPrevPrimitiveItem = nullptr;
				continue;
			}

			if (!pPrevPrimitiveItem)
			{
				pPrevPrimitiveItem = pPrimitiveItem;
				continue;
			}
					
			std::shared_ptr<tagOsgPrimitiveItem> pCurPrimitiveItem = pPrimitiveItem;
			osg::ref_ptr<osg::Vec3dArray> pPrevLine = pPrevPrimitiveItem->m_pVertextArray;
			osg::ref_ptr<osg::Vec3dArray> pCurLine = pCurPrimitiveItem->m_pVertextArray;
			if (!pPrevLine || !pCurLine)
			{
				pPrevPrimitiveItem = pPrimitiveItem;
				continue;
			}

			//判断相连的直线是否拐角
			AcGeVector3d prevLineDir = pPrevPrimitiveItem->m_ptLineEnd - pPrevPrimitiveItem->m_ptLineStart;
			AcGeVector3d curLineDir = pCurPrimitiveItem->m_ptLineEnd - pCurPrimitiveItem->m_ptLineStart;
			if (prevLineDir.isCodirectionalTo(curLineDir)) //两直线平行,不用考虑方向
			{
				pPrevPrimitiveItem = pPrimitiveItem;
				continue;
			}


			//AB 与A1B1相交于O1,
			//CD 与C1D1相交于O2,
			osg::Vec3d& ptA = pPrevLine->at(0);
			osg::Vec3d& ptB = pPrevLine->at(1);
			osg::Vec3d& ptA1 = pCurLine->at(0);
			osg::Vec3d& ptB1 = pCurLine->at(1);
			AcGeVector3d vecLineABDir(ptB.x() - ptA.x(),
				ptB.y() - ptA.y(),
				ptB.z() - ptA.z());
			vecLineABDir.normalize();
			AcGeVector3d vecLineA1B1Dir(ptB1.x() - ptA1.x(),
				ptB1.y() - ptA1.y(),
				ptB1.z() - ptA1.z());
			vecLineA1B1Dir.normalize();
			AcGePoint3d ptCrossO1;
			BOOL bIsTrueCross = FALSE;
			if (!Calculator::Instance().GetLineIntersectionNParallel(
				AcGePoint3d(ptA.x(), ptA.y(), ptA.z()),
				vecLineABDir,
				AcGePoint3d(ptA1.x(), ptA1.y(), ptA1.z()),
				vecLineA1B1Dir,
				ptCrossO1, bIsTrueCross))
			{
				pPrevPrimitiveItem = pCurPrimitiveItem;
				continue;
			}

			osg::Vec3d& ptC = pPrevLine->at(pPrevLine->size()-2);
			osg::Vec3d& ptD = pPrevLine->at(pPrevLine->size()-1);
			osg::Vec3d& ptC1 = pCurLine->at(pCurLine->size()-2);
			osg::Vec3d& ptD1 = pCurLine->at(pCurLine->size()-1);
			AcGeVector3d vecLineCDDir(ptD.x() - ptC.x(),
				ptD.y() - ptC.y(),
				ptD.z() - ptC.z());
			vecLineCDDir.normalize();
			AcGeVector3d vecLineC1D1Dir(ptD1.x() - ptC1.x(),
				ptD1.y() - ptC1.y(),
				ptD1.z() - ptC1.z());
			vecLineC1D1Dir.normalize();
			AcGePoint3d ptCrossO2;
			if (!Calculator::Instance().GetLineIntersectionNParallel(
				AcGePoint3d(ptC.x(), ptC.y(), ptC.z()),
				vecLineCDDir,
				AcGePoint3d(ptC1.x(), ptC1.y(), ptC1.z()),
				vecLineC1D1Dir,
				ptCrossO2, bIsTrueCross))
			{
				pPrevPrimitiveItem = pCurPrimitiveItem;
				continue;
			}

			//使用斜角连接,将点B、点A1替换成交点O1; 点C、点D1替换成交点O2。
			ptB = osg::Vec3d(ptCrossO1.x, ptCrossO1.y, ptCrossO1.z);
			ptA1 = osg::Vec3d(ptCrossO1.x, ptCrossO1.y, ptCrossO1.z);
			ptC = osg::Vec3d(ptCrossO2.x, ptCrossO2.y, ptCrossO2.z);
			ptD1 = osg::Vec3d(ptCrossO2.x, ptCrossO2.y, ptCrossO2.z);

			pPrevPrimitiveItem = pCurPrimitiveItem;
		}
		

		//开始绘制
		for (auto it = mOsgPrimitiveList.begin(); it != mOsgPrimitiveList.end(); ++it)
		{
			auto pPrimitiveItem = *it;
			if (!pPrimitiveItem || !pPrimitiveItem->m_pVertextArray)
			{
				continue;
			}

			osg::ref_ptr<osg::Geometry> pGeom = new osg::Geometry();
			pGeom->setVertexArray(pPrimitiveItem->m_pVertextArray);
			pGeom->addPrimitiveSet(new osg::DrawArrays(pPrimitiveItem->m_enPrimitiveMode,
				0, pPrimitiveItem->m_pVertextArray->size()));
			
			//填充模式
			if (pPrimitiveItem->m_bFillMode)
			{
				osg::PolygonMode* pPolyMode = new osg::PolygonMode(osg::PolygonMode::Face::FRONT_AND_BACK,
					osg::PolygonMode::Mode::LINE);
				pGeom->getOrCreateStateSet()->setAttributeAndModes(pPolyMode,
					osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
			}
			
			pRtOsgGeode->addDrawable(pGeom);
		}
	}
	catch ( ... )
	{
	}

}


运行对比

  • 未处理拐角的情况
    在这里插入图片描述
  • 拐角改为斜角连接
    在这里插入图片描述
  • autocad与当前实现演示对比
    上图为autocad2007显示图,下图为当前实现的显示图。当前实现未处理不等宽的圆弧,其他的模拟的还可以。
    在这里插入图片描述

在这里插入图片描述

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到