/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2025, Open Design Alliance (the "Alliance"). // All rights reserved. // // This software and its documentation and related materials are owned by // the Alliance. The software may only be incorporated into application // programs owned by members of the Alliance, subject to a signed // Membership Agreement and Supplemental Software License Agreement with the // Alliance. The structure and organization of this software are the valuable // trade secrets of the Alliance and its suppliers. The software is also // protected by copyright law and international treaty provisions. Application // programs incorporating this software must include the following statement // with their copyright notices: // // This application incorporates Open Design Alliance software pursuant to a license // agreement with Open Design Alliance. // Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance. // All rights reserved. // // By use of this software, its documentation or related materials, you // acknowledge and accept the above terms. /////////////////////////////////////////////////////////////////////////////// #include "OdaCommon.h" #include #include #include #include #include #include "GiSrGeometry.h" #include "GsSrVectorizeView.h" #include "GsSrVectorizeDevice.h" #include "data/SrDataBuffer.h" #include "rendering/SrFrameContext.h" #include "utils/SrGeometryHelpers.h" #define OD_OPAQUE(x) ((x) | 0xFF000000) OdGiSrGeometry::OdGiSrGeometry() : m_drawClipBuffer(false), m_color(0), m_lineweight(OdDb::kLnWtByLwDefault), m_lineweightPixels(0), m_fillType(kOdGiFillNever), bLineweightOverrideInitialized(false) { m_pPackager = new OdSrMetafilePackager(m_vecView); } OdGiSrGeometry::~OdGiSrGeometry() { delete m_pPackager; } void OdGiSrGeometry::onTraitsModified() { //OdGsSrVectorizerDevice* pDevice = m_vecView->device(); const OdGiSubEntityTraitsData& entTraits = m_vecView->effectiveTraits(); //const OdCmEntityColor& color = entTraits.trueColor(); m_color = m_vecView->getColorRef(entTraits.trueColor(), entTraits.transparency()); m_fillType = entTraits.fillType(); const auto lwOverride = m_vecView->currentLineweightOverride(); if (lwOverride) { if (!bLineweightOverrideInitialized) { if (m_vecView->gsWriter().gsModel()) { m_pPackager->addRecord(OdSrLineweightOverrideRecord::createObject(*lwOverride)); m_vecView->pushLineweightOverride(lwOverride); } else { m_vecView->pushLineweightOverride(lwOverride); } bLineweightOverrideInitialized = true; } else if (!m_vecView->gsWriter().gsModel()) { // the content of OdSrLineweightOverrideRecord::play const bool bPushOverride = lwOverride->hasOverrides(); const bool bPrevOverride = m_vecView->hasLineweightOverride() || m_vecView->hasLinestyleOverride(); if (bPrevOverride) { m_vecView->popLineweightOverride(); } if (bPushOverride) { m_vecView->pushLineweightOverride(lwOverride); } } } m_lineweight = entTraits.lineWeight(); } // //void OdGiSrGeometry::circularArcProc( // const OdGePoint3d& center, // double radius, // const OdGeVector3d& normal, // const OdGeVector3d& startVector, // double sweepAngle, // OdGiArcType arcType, // const OdGeVector3d* pExtrusion) //{ // if (m_pPackager->isPacking()) // { // auto* pRec = OdSrArcRecord::createObject(center, radius, normal, circleArcFillMode(), sweepAngle, arcType, startVector, m_color); // m_pPackager->addRecord(pRec); // } //} struct EllipseData { OdGePoint2d center; OdGeVector2d majorAxis; // Direction and length of major axis OdGeVector2d minorAxis; // Direction and length of minor axis double majorRadius; double minorRadius; bool isRotated(double tolerance = 1e-6) { // Check if major axis is along X or Y axis bool majorAlongX = fabs(majorAxis.y) < tolerance; bool majorAlongY = fabs(majorAxis.x) < tolerance; bool minorAlongX = fabs(minorAxis.y) < tolerance; bool minorAlongY = fabs(minorAxis.x) < tolerance; return !(majorAlongX || majorAlongY) || !(minorAlongX || minorAlongY); } }; EllipseData projectCircleToScreen(const OdGePoint3d& center, double radius, const OdGeVector3d& normal) { EllipseData ellipse; // Project center to screen (drop Z coordinate) ellipse.center.set(center.x, center.y); OdGeVector3d circleNormal = normal.normal(); // Calculate projection factor (cosine of angle between circle normal and screen normal) double projectionFactor = fabs(circleNormal.dotProduct(OdGeVector3d::kZAxis)); // Major axis stays full radius, minor axis gets compressed ellipse.majorRadius = radius; ellipse.minorRadius = radius * projectionFactor; // Find direction of compression on screen // Project circle normal onto XY plane to get compression direction OdGeVector2d projectedNormal(circleNormal.x, circleNormal.y); if (projectedNormal.isZeroLength()) { // Circle normal is parallel to screen - circle becomes a line ellipse.minorRadius = 0.0; ellipse.majorAxis.set(radius, 0.0); ellipse.minorAxis.set(0.0, 0.0); } else { // Normalize the projection direction (this becomes minor axis direction) projectedNormal.normalize(); // Major axis is perpendicular to compression direction in 2D OdGeVector2d majorDir(-projectedNormal.y, projectedNormal.x); ellipse.majorAxis = majorDir * ellipse.majorRadius; ellipse.minorAxis = projectedNormal * ellipse.minorRadius; } ellipse.majorAxis.normalize(); ellipse.minorAxis.normalize(); return ellipse; } void OdGiSrGeometry::circleProc(const OdGePoint3d& center, double radius, const OdGeVector3d& normal, const OdGeVector3d* /*pExtrusion*/) { if (m_lineweight != OdDb::LineWeight::kLnWt000) { OdGiGeometrySimplifier::circleProc(center, radius, normal); return; } if (m_pPackager->isPacking()) { OdSrRecord* pRec; if (normal.isEqualTo(OdGeVector3d::kZAxis)) pRec = OdSrCircleRecord::createObject(center, radius, normal, circleArcFillMode(), m_color); else { EllipseData ellipse = projectCircleToScreen(center, radius, normal); if (ellipse.isRotated()) { OdGePoint2d startPoint = ellipse.center + ellipse.majorAxis; OdGePoint2d endPoint = startPoint; // Same point for full ellipse pRec = OdSrEllipticArcRecord::createObject( ellipse.center, ellipse.majorRadius, ellipse.minorRadius, ellipse.majorAxis, ellipse.minorAxis, startPoint, endPoint, 0.0, // startAngle - full ellipse starts at 0 Oda2PI, // endAngle - full ellipse ends at 2*PI circleArcFillMode(), circleArcFillMode() ? kOdGiArcSector : OdGiArcType::kOdGiArcSimple, m_color); } else pRec = OdSrFullEllipseRecord::createObject( ellipse.center, ellipse.majorRadius, ellipse.minorRadius, circleArcFillMode() ? kOdGiArcSector : OdGiArcType::kOdGiArcSimple, m_color); } m_pPackager->addRecord(pRec); } } //void OdGiSrGeometry::ellipArcProc( // const OdGeEllipArc3d& ellipArc, // const OdGePoint3d* endPointOverrides, // OdGiArcType arcType, // const OdGeVector3d* pExtrusion) //{ // if (m_pPackager->isPacking()) // { // auto pRec = OdSrEllipticArcRecord::createObject( // ellipArc.center().convert2d(), // ellipArc.majorRadius(), // ellipArc.minorRadius(), // ellipArc.majorAxis().convert2d(), // ellipArc.minorAxis().convert2d(), // ellipArc.startPoint().convert2d(), // ellipArc.endPoint().convert2d(), // ellipArc.startAng(), // ellipArc.endAng(), // circleArcFillMode(), // arcType, // m_color); // // m_pPackager->addRecord(pRec); // } //} void OdGiSrGeometry::polygonProc(OdInt32 numPoints, const OdGePoint3d* vertexList, const OdGeVector3d* pNormal, const OdGeVector3d* pExtrusion) { if (m_pPackager->isPacking()) { switch (m_fillType) { case kOdGiFillAlways: { /*auto pRec = OdSrPolygonRecord::createObject(numPoints, vertexList, m_color); m_pPackager->addRecord(pRec); break;*/ OdGiGeometrySimplifier::polygonProc(numPoints, vertexList, pNormal, pExtrusion); return; } case kOdGiFillNever: { auto pRec = OdSrPolylineRecord::createObject(numPoints, vertexList, m_lineweight, m_color, true /*closed*/); m_pPackager->addRecord(pRec); break; } } } } void OdGiSrGeometry::triangleOut(const OdInt32* vertices, const OdGeVector3d* /*pNormal*/) { OdGePoint3d threeVertices[3] = { vertexDataList()[vertices[0]], vertexDataList()[vertices[1]], vertexDataList()[vertices[2]] }; if (m_pPackager->isPacking()) { if (vertexData() && vertexData()->trueColors()) { ODCOLORREF* threeColors = new ODCOLORREF[3]; threeColors[0] = OD_OPAQUE(ODTOCOLORREF(vertexData()->trueColors()[vertices[0]])); threeColors[1] = OD_OPAQUE(ODTOCOLORREF(vertexData()->trueColors()[vertices[1]])); threeColors[2] = OD_OPAQUE(ODTOCOLORREF(vertexData()->trueColors()[vertices[2]])); auto pRec = OdSrSetColorsRecord::createObject(threeColors, 3); m_pPackager->addRecord(pRec); auto pRec2 = OdSrMultiColorTriangleRecord::createObject(threeVertices); m_pPackager->addRecord(pRec2); } else if (m_vecView->device()->hasRasterImage()) { auto pRec = OdSrTexturedTriangleRecord::createObject(threeVertices, 255); m_pPackager->addRecord(pRec); } else { auto pRecCol = OdSrSetColorsRecord::createObject(m_color); m_pPackager->addRecord(pRecCol); auto pRec = OdSrTexturedTriangleRecord::createObject(threeVertices, 255); m_pPackager->addRecord(pRec); } } else if (m_vecView && m_vecView->m_pContext && m_drawClipBuffer) { // OdSrSingleColorTriangleRecord is to be removed OdSrSingleColorTriangleRecord::play(*m_vecView->m_pContext, m_color, threeVertices, true); } } OdSrTextureTransform OdGiSrGeometry::getRasterImageTransform(OdInt32 imgWidth, OdInt32 imgHeight, const OdGePoint3d& origin, const OdGeVector3d& u, const OdGeVector3d& v) { //OdGeMatrix3d xWorld2UV; //OdUInt32 imgWidth(rasterWidth), imgHeight(rasterHeight); //OdGeVector3d imgNormal = rasterU.crossProduct(rasterV); //if (OdNonZero(imgNormal.normalizeGetLength(), 1.e-300)) // xWorld2UV.setCoordSystem(rasterOrigin, rasterU, rasterV, imgNormal).invert(); //xWorld2UV[0][0] /= imgWidth, xWorld2UV[0][1] /= imgWidth, // xWorld2UV[0][2] /= imgWidth, xWorld2UV[0][3] /= imgWidth; //xWorld2UV[1][0] /= imgHeight, xWorld2UV[1][1] /= imgHeight, // xWorld2UV[1][2] /= imgHeight, xWorld2UV[1][3] /= imgHeight; //return xWorld2UV; //OdGeMatrix3d world2uvInvU; //OdGeVector3d normal = rasterU.crossProduct(rasterV); //world2uvInvU.setCoordSystem(rasterOrigin, rasterU, rasterV, normal); //if (!normal.isZeroLength(OdGeTol(1.e-300))) // world2uvInvU.invert(); //OdGeMatrix3d uv2uvNorm; //uv2uvNorm.setToScaling(OdGeScale3d(1.0 / rasterWidth, 1.0 / rasterHeight, 1.0)); //return uv2uvNorm * world2uvInvU; // Compute transform OdGeMatrix3d xWorld2UV; OdGeVector3d imgNormal = u.crossProduct(v); if (OdNonZero(imgNormal.normalizeGetLength(), 1.e-300)) xWorld2UV.setCoordSystem(origin, u, v, imgNormal).invert(); OdSrTextureTransform transform; transform.m_sPlane.set(xWorld2UV[0][0] / imgWidth, xWorld2UV[0][1] / imgWidth, xWorld2UV[0][2] / imgWidth, xWorld2UV[0][3] / imgWidth); transform.m_tPlane.set(xWorld2UV[1][0] / imgHeight, xWorld2UV[1][1] / imgHeight, xWorld2UV[1][2] / imgHeight, xWorld2UV[1][3] / imgHeight); return transform; } void OdGiSrGeometry::setModelToEye(const OdGeMatrix3d* model2Eye) { model2EyeWhenVectorizing = model2Eye; } void OdGiSrGeometry::initTexture(const OdGePoint3d& origin, const OdGeVector3d& u, const OdGeVector3d& v, const OdGiRasterImage* pImage, bool transparency, double brightness, double contrast, double fade) { ODA_ASSERT(pImage != 0); bool bTransparentBitonal = false; bool bTransparent32 = false; OdGiRasterImagePtr pBitonalImg; if (pImage->colorDepth() == 1) { if (transparency) bTransparentBitonal = true; OdSmartPtr pBitRast = OdRxObjectImpl::createObject(); const OdGiSubEntityTraitsData& effTraits = m_vecView->effectiveTraits(); if (bTransparentBitonal || !GETBIT(effTraits.drawFlags(), OdGiSubEntityTraits::kDrawContourFill)) { pBitRast->setOriginal(pImage, m_color, m_vecView->device()->getPalette()[0], bTransparentBitonal); } else { // .dgn files specific ODCOLORREF secColor; if (effTraits.secondaryTrueColor().isByACI() || effTraits.secondaryTrueColor().isByDgnIndex()) secColor = OD_OPAQUE(m_vecView->paletteColor(effTraits.secondaryTrueColor().colorIndex())); else secColor = OD_OPAQUE(ODTOCOLORREF(effTraits.secondaryTrueColor())); pBitRast->setOriginal(pImage, m_color, secColor, bTransparentBitonal); } pBitonalImg = pBitRast; pImage = pBitonalImg; } else if (pImage->colorDepth() <= 8 && transparency && pImage->transparentColor() >= 0) { bTransparentBitonal = true; } else if (pImage->colorDepth() == 32 && transparency) { bTransparent32 = true; } if (m_vecView->device()->isRasterImageProcessible(pImage) && OdEqual(brightness, 50.0) && OdEqual(contrast, 50.0) && OdZero(fade)) { m_vecView->device()->setRasterImage(pImage); OdSrTextureTransform transform = getRasterImageTransform(pImage->pixelWidth(), pImage->pixelHeight(), origin, u, v); auto* pRecord = OdSrSetTextureRecord::createObject(pImage, transform, m_vecView->device()->textureParams()); m_pPackager->addRecord(pRecord); } else { OdSmartPtr pDesc = OdRxObjectImpl::createObject(); pDesc->setPixelWidth(pImage->pixelWidth()); pDesc->setPixelHeight(pImage->pixelHeight()); if (!bTransparentBitonal && !bTransparent32) { pDesc->setColorDepth(24); pDesc->pixelFormat().setBGR(); } else { pDesc->setColorDepth(32); pDesc->pixelFormat().setBGRA(); } OdGiRasterImagePtr pCopyImg; pCopyImg = pImage->convert(true, brightness, contrast, fade, m_vecView->device()->getPalette()[0], false, false, false, pDesc); m_vecView->device()->setRasterImage(pCopyImg); auto transform = getRasterImageTransform(pCopyImg->pixelWidth(), pCopyImg->pixelHeight(), origin, u, v); auto* pRecord = OdSrSetTextureRecord::createObject(pCopyImg, transform, m_vecView->device()->textureParams()); m_pPackager->addRecord(pRecord); } } void OdGiSrGeometry::uninitTexture() { m_vecView->device()->clearRasterImage(); auto* pRecord = OdSrResetTextureRecord::createObject(); m_pPackager->addRecord(pRecord); } void OdGiSrGeometry::polylineProc(OdInt32 numPoints, const OdGePoint3d* vertexList, const OdGeVector3d* /*pNormal*/, const OdGeVector3d* /*pExtrusion*/, OdGsMarker /*baseSubEntMarker*/) { if (numPoints == 2 && vertexList[0].isEqualTo(vertexList[1])) numPoints = 1; if (numPoints == 1) { bool isFatPoints = m_vecView->giContext().lineWeightConfiguration(OdGiContext::kPointLineWeight) > 0; OdDb::LineWeight lineWeight = !isFatPoints ? OdDb::LineWeight::kLnWt000 : m_lineweight; processRecord(m_pPackager, m_vecView->m_pContext, m_vecView, vertexList[0], lineWeight, m_color); } else processRecord(m_pPackager, m_vecView->m_pContext, m_vecView, numPoints, vertexList, m_lineweight, m_color, false /*not closed*/); } void OdGiSrGeometry::shellFaceOut(OdInt32 faceListSize, const OdInt32* pFaceList, const OdGeVector3d* pNormal) { OdInt32 loopSize = *pFaceList; const bool isSingleLoop = loopSize + 1 == faceListSize; if (isSingleLoop) { OdGiShellFaceIterator itVertex(vertexDataList(), pFaceList + 1); OdGePoint3dArray points; points.reserve(loopSize); for (int i = 0; i < loopSize; ++i, ++itVertex) points.append(*itVertex); const double tolerance = OdSrGeometryHelpers::acadPointTol; for (auto i = points.begin(); i != points.end(); ++i) for (auto j = i + 1; j != points.end();) { if (i->distanceTo(*j) < tolerance) j = points.erase(j); else ++j; } // Handle degenerate cases switch (points.size()) { case 0: return; // Nothing to render case 1: // Single point case 2: // Line segment polylineOut(points.size(), points.getPtr()); return; default: if (OdSrGeometryHelpers::arePointsCollinear(points)) { auto extremes = OdSrGeometryHelpers::findExtremePoints(points); OdGePoint3dArray linePoints; linePoints.resize(2); linePoints[0] = extremes.first; linePoints[1] = extremes.second; polylineOut(2, linePoints.getPtr()); return; } break; } OdGiGeometrySimplifier::shellFaceOut(faceListSize, pFaceList, pNormal); return; } OdGiGeometrySimplifier::shellFaceOut(faceListSize, pFaceList, pNormal); } void OdGiSrGeometry::addRecord(OdSrRecord* pRec) { m_pPackager->addRecord(pRec); } void OdGiSrGeometry::polylineOut(OdInt32 nPts, const OdGePoint3d* points) { polylineProc(nPts, points, &OdGeVector3d::kZAxis); // TODO: to figure out what normal we should use here } void OdGiSrGeometry::beginMetafile(OdRxObject* pMetafile, OdGsBaseVectorizer* pVectorizer) { m_vecView = static_cast(pVectorizer); m_pMetafile = pMetafile; m_pMetafile->begin(); startPacking(); } void OdGiSrGeometry::endMetafile(OdRxObject* /*pMetafile*/) { m_pMetafile->end(); stopPacking(); } void OdGiSrGeometry::startPacking() { OdGsSrVectorizerDevice* device = static_cast(m_vecView->device()); m_pPackager->startPacking(m_vecView, device, m_pMetafile); } void OdGiSrGeometry::stopPacking() { m_pPackager->stopPacking(); } void OdGiSrGeometry::setRenderMode(OdGsView::RenderMode mode) { m_renderMode = mode; }