admin管理员组

文章数量:1604636

前言

  
  谈到国产自研CAD软件,有一个绕不开的词——DWG格式,毕竟在相对漫长的CAD软件历史中,大多数的专业图纸几乎都是用AutoCAD绘制的,保存格式为 .dwg格式。 而 .dwg格式又非开源文件格式,在没有AutoDesk公司帮助下,别的软件想要读取或修改dwg格式有着难以想象的难度,即使你有能力破解了dwg格式的读写,也会面临着AutoDesk公司的诉讼,毕竟人家有专利保护,实际上这也是一种变相的垄断,天下苦欧特克AUTOCAD久已!。下面以国产CAD龙头中望软件为例简单介绍ITC组织和Open Design联盟(ODA)。

1、中望官司风波

  2020年4月9日,广州中望龙腾软件股份有限公司(下文简称“中望”)向科创板提交招股书,拟募资6.01亿元。在中望官网,自我介绍是“国内唯一同时拥有完全自主知识产权二维中望CAD、高端三维CAD/CAM软件中望3D的国际化软件企业。”但这个被中望一再强调的“完全自主知识产权”,究竟有多少含金量?

  2013年,《中国新闻出版报》发布文章《ITC禁售引发国产CAD软件生存危机》,称ITC组织将于禁止对华销售InterlliCAD7以下版本的CAD产品,这对国产CAD软件而言是巨大的挑战,很可能导致许多软件无法再对用户销售。当时为了应对这一政策,中望CAD放弃了基于 ITC 的产品,转向自己开发 ZWCAD+产品,本来以为中望软件研发出了自主内核的CAD平台软件。但好景不长,就在这个宣称不受ITC影响的中望CAD+发布一年多后,竞争对手AutoCAD却找上了门来。

  2014年,开发AutoCAD软件的母公司欧特克公司在荷兰、美国两地起诉中望,称“AutoCAD源代码被盗用并被不当使用于ZWCAD+的开发。 在2017年中望挂牌新三板的《转让说明书》中,这一诉讼案的背后故事被公之于众。中望被要求赔偿欧特克总计180万美元,分3年共4次支付,单次分别支付40万、45万、45万、50万美元。此次案件最终以中望软件禁售ZWCAD+系列产品,并向Autodesk(欧特克)赔款而告终。兜兜转转一大圈,中望最终还是回归了ITC的怀抱,并租用至今。

  在ITC组织的官网上,无论是在其官网首页,还是组织会员列表中,中望CAD都在其列,并清晰的展示了基于IntelliCAD的最新产品中望CAD2020(ZWCAD2020)。在ITC组织发布的新闻稿中更多次强调“ZWCAD是运行在IntelliCAD引擎之上”,ITC官网还明确展示了中望成为会员获得的技术收益,包括熟悉的CAD用户界面、DWG格式的原生兼容、获得关键CAD引擎的源代码等等。

  ODA官网的价格信息显示,中望购买了ODA的会员才获得其技术使用权,且每年要支付年费。最贵的企业会员年费在3万美元以上。以ODA提供的dwg技术为例,dwg是中望最大的竞争对手AutoCAD软件的专有文件格式,其他CAD软件厂商想与之竞争,就必须有dwg文件的读写和转换能力,以对AutoCAD兼容,使用户能够在不同软件平台间切换。查阅ODA官网,中望CAD作为会员代表出现,并提到ODA提供快速、稳定及可靠的.dwg技术来帮助其构建ZWCAD内核。

2、ODA技术联盟和ITC组织

  ODA(Open Design Alliance)是一个非盈利技术联盟,由1200个成员公司组成,申请成为付费会员后能够使用其图形技术库。ODA致力于促进开放的、工业标准的CAD数据和遗留的CAD数据的格式交换。 ODA开发用于技术图形应用程序的核心平台Teigha™, Teigha支持dwg、dgn、stl、pdf之间的数据交换。Teigha支持的多个平台:Windows、Mac、Unix、Linux等。 ODA 会员可以用 C++, .NET, 和 ActiveX 接口开发自己的应用程序。ODA的宗旨是开发核心的图形技术库,让软件开发商专注与应用开发。 和ITC一样也是面向会员的。

  ITC(IntelliCAD Technology Consortium)是由CAD开发公司组成的一个非盈利组织,拥有版权并负责开发和管理IntelliCAD。ITC会员在缴纳会费后,可享有使用美国IntelliCAD源代码的权利,并发布OEM品牌的CAD软件。IntelliCAD 最初的创建是为了阻止 Autodesk 完全垄断 CAD,让数百万用户能够在 Autodesk 产品之外编辑数十亿个工程数据文件。

  IntelliCAD 是一个专业级的商业 2D/3D CAD 软件程序和解决方案开发平台。IntelliCAD 的核心数据库依赖于 ODA 平台来打开、可视化、编辑和保存 .dwg 和 .dgn 文件。IntelliCAD是一个CAD编辑器和开发平台,具有由IntelliCAD 技术联盟(“ITC”)通过共享开发发布的应用程序编程接口API 。IntelliCAD 模拟了AutoCAD的基本界面和功能,但是,它特别能够在各种文件类型(即 dwg.、BIM、TIFF 等)之间自由合并和交换。ITC IntelliCAD 不直接出售给最终用户,而是授权给联盟成员,他们通过支付年费来支持共享开发,类似于合作安排,以换取在全球范围内分发基于 IntelliCAD 的解决方案的许可。

3、Teigha SDK常用API

  在CAD(计算机辅助设计)领域,由Open Design Alliance (ODA) 开发的Teigha SDK无疑是一个强大的工具,它为开发者提供了一套全面的工具包,用于处理各种CAD文件。本仓库不仅包含了Teigha SDK 4.2的官方文档,还提供了丰富的使用示例,帮助开发者快速上手并集成该SDK到他们的项目中。https://gitcode/open-source-toolkit/a2de8/overview?utm_source=tools_gitcode&index=top&type=card&webUrl

3.1 ODA实体

3.1.1 ODA实体分类

std::wstring GetOdDbEntityClassName(const OdDbObjectId& entId)
{
	std::wstring sClassName = L"";

	OdDbEntityPtr pOdEnt = entId.safeOpenObject();
	if (!pOdEnt.isNull())
	{
		if (pOdEnt->isKindOf(OdDbPoint::desc()))
		{
			sClassName = L"点";
		}
		else if (pOdEnt->isKindOf(OdDbLine::desc()))
		{
			sClassName = L"线段";
		}
		else if (pOdEnt->isKindOf(OdDbMline::desc()))
		{
			sClassName = L"多线";
		}
		else if (pOdEnt->isKindOf(OdDbPolyline::desc()))
		{
			sClassName = L"多段线";
		}
		else if (pOdEnt->isKindOf(OdDb2dPolyline::desc()))
		{
			sClassName = L"二维多段线";
		}
		else if (pOdEnt->isKindOf(OdDb3dPolyline::desc()))
		{
			sClassName = L"三维多段线";
		}
		else if (pOdEnt->isKindOf(OdDbXline::desc()))
		{
			sClassName = L"构造线";
		}
		else if (pOdEnt->isKindOf(OdDbRay::desc()))
		{
			sClassName = L"射线";
		}
		else if (pOdEnt->isKindOf(OdDbArc::desc()))
		{
			sClassName = L"圆弧";
		}
		else if (pOdEnt->isKindOf(OdDbCircle::desc()))
		{
			sClassName = L"圆";
		}
		else if (pOdEnt->isKindOf(OdDbEllipse::desc()))
		{
			sClassName = L"椭圆";
		}
		else if (pOdEnt->isKindOf(OdDbHatch::desc()))
		{
			sClassName = L"填充";
		}
		else if (pOdEnt->isKindOf(OdDbSpline::desc()))
		{
			sClassName = L"样条线";
		}
		else if (pOdEnt->isKindOf(OdDbText::desc()))
		{
			sClassName = L"文字";
		}
		else if (pOdEnt->isKindOf(OdDbMText::desc()))
		{
			sClassName = L"多行文字";
		}
		else if (pOdEnt->isKindOf(OdDbSolid::desc()))
		{
			sClassName = L"体";
		}
		else if (pOdEnt->isKindOf(OdDbRegion::desc()))
		{
			sClassName = L"面域";
		}
		else if (pOdEnt->isKindOf(OdDbShape::desc()))
		{
			sClassName = L"形状";
		}
		else if (pOdEnt->isKindOf(OdDbSurface::desc()))
		{
			sClassName = L"曲面";
		}
		else if (pOdEnt->isKindOf(OdDbBlockReference::desc()))
		{
			sClassName = L"块参照";
		}
		else if (pOdEnt->isKindOf(OdDbRadialDimension::desc()))
		{
			sClassName = L"半径标注";
		}
		else if (pOdEnt->isKindOf(OdDbDiametricDimension::desc()))
		{
			sClassName = L"直径标注";
		}
		else if (pOdEnt->isKindOf(OdDb2LineAngularDimension::desc()))
		{
			sClassName = L"角度标注";
		}
		else if (pOdEnt->isKindOf(OdDb3PointAngularDimension::desc()))
		{
			sClassName = L"3d角度标注";
		}
		else if (pOdEnt->isKindOf(OdDbAlignedDimension::desc()))
		{
			sClassName = L"对齐标注";
		}
		else if (pOdEnt->isKindOf(OdDbArcDimension::desc()))
		{
			sClassName = L"弧长标注";
		}
		else if (pOdEnt->isKindOf(OdDbOrdinateDimension::desc()))
		{
			sClassName = L"坐标标注";
		}
		else if (pOdEnt->isKindOf(OdDbRadialDimensionLarge::desc()))
		{
			sClassName = L"折弯标注";
		}
		else if (pOdEnt->isKindOf(OdDbLeader::desc()))
		{
			sClassName = L"引线标注";
		}
		else if (pOdEnt->isKindOf(OdDbMLeader::desc()))
		{
			sClassName = L"多引线标注";
		}
		else if (pOdEnt->isKindOf(OdDbRotatedDimension::desc()))
		{
			sClassName = L"线性标注";
		}
		else if (pOdEnt->isKindOf(OdDbOle2Frame::desc()))
		{
			sClassName = L"OLE";
		}
		else if (pOdEnt->isKindOf(OdDbRasterImage::desc()))
		{
			sClassName = L"图片";
		}
	}

	return sClassName;
}

3.1.2 绘制直线

void DrawLines()
{
	OdGePoint3d startPoint;
	OdGePoint3d endPoint;

	OdDbLinePtr pLineOd = OdDbLine::createObject();
	pLineOd->setStartPoint(startPoint);
	pLineOd->setEndPoint(endPoint);

	OdDbObjectId idNew = appendOdDbEntity(pLineOd);
}

3.1.3 添加实体到数据库

OdDbHostAppServices* m_pSystemService = new OdDbHostAppServices();
OdDbDatabasePtr  m_pDb = m_pSystemService->createDatabase();
OdDbObjectId appendOdDbEntity(OdDbEntityPtr pEnt)
{
	OdSmartPtr<OdDbBlockTableRecord> pModelSpace = m_pDb->getModelSpaceId().safeOpenObject(OdDb::kForWrite);
	std::wstring sLayerName = pEnt->layer();
	if (sLayerName.empty())
	{
		std::wstring sLayerDefault = OdDbSymUtil::getSymbolName(m_pDb->getCLAYER());
		if (sLayerDefault != L"0")
		{
			pEnt->setLayer(m_pDb->getCLAYER());
		}
	}
	OdDbObjectId id = pModelSpace->appendOdDbEntity(pEnt);

	return id;
}

3.1.4 读取直线起点

GraphPoint3d startPoint(const OdDbObjectId& entId)
{
	OdGePoint3d ptStart = OdGePoint3d::kOrigin;
	OdDbEntityPtr pOdEnt = entId.safeOpenObject();
	if (!pOdEnt.isNull())
	{
		if (pOdEnt->isKindOf(OdDbLine::desc()))
		{
			OdDbLine* pLine = dynamic_cast<OdDbLine*> (pOdEnt.get());
			ptStart = pLine->startPoint();
		}
	}

	return ptStart;
}

3.1.5 修改直线起点

void setStartPoint(const OdDbObjectId& entId,const OdGePoint3d& ptStart)
{
	OdDbEntityPtr pOdEnt = entId.safeOpenObject(OdDb::kForWrite);
	if (!pOdEnt.isNull())
	{
		if (pOdEnt->isKindOf(OdDbLine::desc()))
		{
			OdDbLine* pLine = dynamic_cast<OdDbLine*> (pOdEnt.get());
			pLine->setStartPoint(ptStart);
		}
	}
}

3.1.6 依据块名称获取块Id

OdDbObjectId getBolckIdFromDB(OdDbDatabasePtr pDb, std::wstring sBlockName)
{
	OdDbObjectId id = OdDbObjectId(0);

	if (!pDb || sBlockName.empty())
	{
		return id;
	}

	OdDbBlockTablePtr pBlockTable = pDb->getBlockTableId().safeOpenObject(OdDb::kForRead);
	if (!pBlockTable)
	{
		return id;
	}

	OdDbObjectId pBlockRecordId = pBlockTable->getAt(OdString(sBlockName.c_str()));
	if (pBlockRecordId.isNull())
	{
		return id;
	}

	return pBlockRecordId;
}

3.2 ODA图层

3.2.1 获取所有图层名称列表

int getAllLayerNames(OdDbDatabasePtr pDb, std::vector<std::wstring>& layerNames)
{
	if (!pDb)
	{
		return 0;
	}

	OdDbLayerTablePtr pLayers = pDb->getLayerTableId().safeOpenObject();
	if (!pLayers)
	{
		return 0;
	}

	for (OdDbSymbolTableIteratorPtr pIter = pLayers->newIterator(); !pIter->done(); pIter->step())
	{
		OdDbLayerTableRecordPtr pLayer = pIter->getRecord();
		if (!pLayer)
		{
			continue;
		}
		std::wstring sName = pLayer->getName();
		layerNames.emplace_back(sName);
	}

	return (int)layerNames.size();
}

3.2.2 依据图层名称获取图层ID

OdDbObjectId getLayerOdIdFromDB(OdDbDatabasePtr pDb, std::wstring sLayerName)
{
	OdDbObjectId id = OdDbObjectId(0);

	if (!pDb || sLayerName.empty())
	{
		return id;
	}

	OdDbLayerTablePtr pLayerTable = pDb->getLayerTableId().safeOpenObject(OdDb::kForRead);
	if (!pLayerTable)
	{
		return id;
	}

	OdDbObjectId pLayerRecordId = pLayerTable->getAt(OdString(sLayerName.c_str()));
	if (pLayerRecordId.isNull())
	{
		return id;
	}

	return pLayerRecordId;
}

3.3 ODA线型

3.3.1 获取线型列表

std::vector<std::wstring> getLineTypeNames(OdDbDatabasePtr pDb)
{
	std::vector<std::wstring> vecLineType;
	OdDbLinetypeTablePtr pLineTypeTable = pDb->getLinetypeTableId().safeOpenObject();
	if (!pLineTypeTable.isNull())
	{
		OdDbSymbolTableIteratorPtr pIt = pLineTypeTable->newIterator();
		if (!pIt.isNull())
		{
			for (pIt->start(); !pIt->done(); pIt->step())
			{
				OdDbObjectId odLineTypeId = pIt->getRecordId();
				wstring sName = getLineTypeIdByName(odTextStyleId);
				if (!sName.empty())
				{
					vecLineType.push_back(sName);
				}
			}
		}
	}

	return vecL

ineType;
}

3.3.2 依据线型名称获取线型ID

OdDbObjectId getLineTypeIdByName(OdDbDatabasePtr pDb, std::wstring sLineTypeName)
{
	OdDbObjectId pLineTypeId = OdDbObjectId(0);

	if (!pDb || sLineTypeName.empty())
	{
		return pLineTypeId;
	}

	OdDbLinetypeTablePtr pLineTypeTable = pDb->getLinetypeTableId().safeOpenObject(OdDb::kForRead);
	if (!pLineTypeTable)
	{
		return pLineTypeId;
	}

	OdDbObjectId pLineTypeRecordId = pLineTypeTable->getAt(OdString(sLineTypeName.c_str()));
	if (pLineTypeRecordId.isNull())
	{
		return pLineTypeId;
	}

	return pLineTypeRecordId;
}

3.3.3 依据线型ID获取线型名称

std::wstring getLineTypeIdByName(OdDbDatabasePtr pDb, OdDbObjectId pLineTypeId )
{
	 std::wstring sLineTypeName;

	if (!pDb || pLineTypeId.isNull())
	{
		return sLineTypeName;
	}
	OdDbLinetypeTablePtr pLineTypes = pDb->getLinetypeTableId().safeOpenObject();
	if (pLineTypes.isNull())
	{
		return sLineTypeName;
	}

	OdDbSymbolTableIteratorPtr pIt = pLineTypes->newIterator();
	if (pIt.isNull())
	{
		return sLineTypeName;
	}

	for (pIt->start(); !pIt->done(); pIt->step())
	{
		OdDbObjectId odLineTpyeId = pIt->getRecordId();

		OdDbLinetypeTableRecordPtr pLineType = odLineTpyeId.safeOpenObject();
		sLineTypeName = pLineType->getName();

		if (odLineTpyeId == pLineTypeId)
		{
			return sLineTypeName;
		}
	}

	return sLineTypeName;
}

3.4 ODA文字样式

3.4.1 依据文字样式名称获取文字样式ID

OdDbObjectId getTextStyleIdFromDB(OdDbDatabasePtr pDb, std::wstring sTextStyleName)
{
	OdDbObjectId id = OdDbObjectId(0);

	if (!pDb || sTextStyleName.empty())
	{
		return id;
	}

	OdDbTextStyleTablePtr pTextStyleTable = pDb->getTextStyleTableId().safeOpenObject(OdDb::kForRead);
	if (!pTextStyleTable)
	{
		return id;
	}

	OdDbObjectId pTextStyleRecordId = pTextStyleTable->getAt(OdString(sTextStyleName.c_str()));
	if (pTextStyleRecordId.isNull())
	{
		return id;
	}

	return pTextStyleRecordId;
}

3.4.2 依据文字样式ID称获取文字样式名称

std::wstring getTextStyleName(const OdDbObjectId& styleId)
{
	if (!styleId.isValid())
		return L"";

	std::wstring sName;
	OdDbTextStyleTableRecordPtr pRec = styleId.safeOpenObject(OdDb::kForRead);
	if (pRec)
	{
		sName = pRec->getName();
	}

	return 

sName;
}

3.4.3 获取文字样式列表

std::vector<std::wstring> getTextStyleNames(OdDbDatabasePtr pDb)
{
	std::vector<std::wstring> vecAllCadTextStyle;
	OdDbTextStyleTablePtr pTextStyles = pDb->getTextStyleTableId().safeOpenObject();
	if (!pTextStyles.isNull())
	{
		OdDbSymbolTableIteratorPtr pIt = pTextStyles->newIterator();
		if (!pIt.isNull())
		{
			for (pIt->start(); !pIt->done(); pIt->step())
			{
				OdDbObjectId odTextStyleId = pIt->getRecordId();
				wstring sName = getTextStyleName(odTextStyleId);
				if (!sName.empty())
				{
					vecAllCadTextStyle.push_back(sName);
				}
			}
		}
	}

	return vecAllCadTextStyle;
}

3.5 ODA扩展数据和数据字典

3.5.1 设定扩展数据XData

void setXData(OdDbObjectId& objId) 
{
	double unit = 1.0;
	OdDbRayPtr pEnt(objId.safeOpenObject(OdDb::kForWrite));
	if (pEnt)
	{
		OdResBufPtr pXd = pEnt->xData(OD_T("RAY_END_POINT_LEN"));
		if (pXd) 
		{
			pXd->setNext(OdResBuf::newRb(OdResBuf::kDxfXdReal, unit));
			pEnt->setXData(pXd);
		}
		else 
		{
			pXd = OdResBuf::newRb(1001, OD_T("RAY_END_POINT_LEN"));
			pXd->setNext(OdResBuf::newRb(OdResBuf::kDxfXdReal, unit));
			pEnt->setXData(pXd);
		}
	}
}

3.5.2 设置数据字典中字段值

bool SaveOdDbDictionary(OdDbDatabasePtr pDb, wstring sDictName, wstring sKey, wstring sValue)
{
	if (pDb.isNull())
		return false;

	wstring sKeyValue = sValue;
	if (sDictName.empty() || sKey.empty() || sKeyValue.empty())
		return false;

	OdDbDictionaryPtr spNameDict = pDb->getNamedObjectsDictionaryId().safeOpenObject();
	OdDbObjectId dictId = spNameDict->getAt(OdString(sDictName.c_str()));
	if (!dictId.isValid())
	{
		spNameDict->upgradeOpen();
		OdDbDictionaryPtr pNewDict = OdDbDictionary::createObject();
		dictId = spNameDict->setAt(OdString(sDictName.c_str()), pNewDict);
		if (!dictId.isValid())
			return false;
	}

	OdDbDictionaryPtr spDict = dictId.safeOpenObject(OdDb::kForWrite);
	if (spDict == nullptr)
		return false;

	wstring sKeyName = sKey;
	OdDbObjectId xRecId = spDict->getAt(sKeyName.c_str());
	if (!xRecId.isValid())
	{
		OdDbXrecordPtr pXrecord = OdDbXrecord::createObject();
		xRecId = spDict->setAt(sKeyName.c_str(), pXrecord);
		if (!xRecId.isValid())
			return false;
	}

	OdDbXrecordPtr pXrecord = xRecId.safeOpenObject(OdDb::kForWrite);
	if (pXrecord == nullptr)
		return false;

	OdResBufPtr pRb, temp;
	temp = pRb = OdResBuf::newRb(OdResBuf::kDxfXdAsciiString);
	temp->setString(sKeyValue.c_str());
	pXrecord->setFromRbChain(temp);

	return true;
}

3.5.3 获取数据字典中字段值

bool GetOdDbDictionary(OdDbDatabasePtr pDb, wstring sDictName, wstring sKey, wstring& sValue)
{
	if (pDb.isNull())
		return false;

	OdDbDictionaryPtr spNameDict = pDb->getNamedObjectsDictionaryId().safeOpenObject();
	OdDbObjectId dictId = spNameDict->getAt(OdString(sDictName.c_str()));
	if (!dictId.isValid())
		return false;

	OdDbDictionaryPtr spDict = dictId.safeOpenObject(OdDb::kForWrite);
	if (spDict == nullptr)
		return false;

	wstring sKeyName = sKey;
	OdDbObjectId xRecId = spDict->getAt(sKeyName.c_str());
	if (!xRecId.isValid())
		return false;

	OdDbXrecordPtr pXrecord = xRecId.safeOpenObject(OdDb::kForWrite);
	if (pXrecord == nullptr)
		return false;

	OdResBufPtr pResBuf = pXrecord->rbChain();

	wstring sGetString;
	while (!pResBuf.isNull())
	{
		if (OdResBuf::kDxfXdAsciiString == pResBuf->restype())
		{
			sGetString = pResBuf->getString();
		}
		pResBuf = pResBuf->next();
	}

	if (sGetString.empty())
		return false;

	sValue = sGetString;

	return true;
}

本文标签: 核心组织联盟TeighaODA