LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/dxf - ogrdxf_dimension.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 184 191 96.3 %
Date: 2024-11-21 22:18:42 Functions: 3 3 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  DXF Translator
       4             :  * Purpose:  Implements translation support for DIMENSION elements as a part
       5             :  *           of the OGRDXFLayer class.
       6             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.com>
      10             :  * Copyright (c) 2010, Even Rouault <even dot rouault at spatialys.com>
      11             :  * Copyright (c) 2017, Alan Thomas <alant@outlook.com.au>
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  ****************************************************************************/
      15             : 
      16             : #include "ogr_dxf.h"
      17             : #include "cpl_conv.h"
      18             : 
      19             : #include <stdexcept>
      20             : 
      21             : /************************************************************************/
      22             : /*                             PointDist()                              */
      23             : /************************************************************************/
      24             : 
      25             : #ifndef PointDist_defined
      26             : #define PointDist_defined
      27             : 
      28          28 : inline static double PointDist(double x1, double y1, double x2, double y2)
      29             : {
      30          28 :     return sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
      31             : }
      32             : #endif
      33             : 
      34             : /************************************************************************/
      35             : /*                         TranslateDIMENSION()                         */
      36             : /************************************************************************/
      37             : 
      38          30 : OGRDXFFeature *OGRDXFLayer::TranslateDIMENSION()
      39             : 
      40             : {
      41             :     char szLineBuf[257];
      42          30 :     int nCode = 0;
      43             :     // int  nDimType = 0;
      44          30 :     OGRDXFFeature *poFeature = new OGRDXFFeature(poFeatureDefn);
      45          30 :     double dfArrowX1 = 0.0;
      46          30 :     double dfArrowY1 = 0.0;
      47             :     // double dfArrowZ1 = 0.0;
      48          30 :     double dfTargetX1 = 0.0;
      49          30 :     double dfTargetY1 = 0.0;
      50             :     // double dfTargetZ1 = 0.0;
      51          30 :     double dfTargetX2 = 0.0;
      52          30 :     double dfTargetY2 = 0.0;
      53             :     // double dfTargetZ2 = 0.0;
      54          30 :     double dfTextX = 0.0;
      55          30 :     double dfTextY = 0.0;
      56             :     // double dfTextZ = 0.0;
      57             : 
      58          30 :     bool bReadyForDimstyleOverride = false;
      59             : 
      60          30 :     bool bHaveBlock = false;
      61          60 :     CPLString osBlockName;
      62          60 :     CPLString osText;
      63             : 
      64          60 :     std::map<CPLString, CPLString> oDimStyleProperties;
      65          30 :     poDS->PopulateDefaultDimStyleProperties(oDimStyleProperties);
      66             : 
      67        1105 :     while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0)
      68             :     {
      69        1075 :         switch (nCode)
      70             :         {
      71          19 :             case 2:
      72          19 :                 bHaveBlock = true;
      73          19 :                 osBlockName = szLineBuf;
      74          19 :                 break;
      75             : 
      76          28 :             case 3:
      77             :                 // 3 is the dimension style name. We don't need to store it,
      78             :                 // let's just fetch the dimension style properties
      79          28 :                 poDS->LookupDimStyle(szLineBuf, oDimStyleProperties);
      80          28 :                 break;
      81             : 
      82          28 :             case 10:
      83          28 :                 dfArrowX1 = CPLAtof(szLineBuf);
      84          28 :                 break;
      85             : 
      86          28 :             case 20:
      87          28 :                 dfArrowY1 = CPLAtof(szLineBuf);
      88          28 :                 break;
      89             : 
      90          28 :             case 30:
      91             :                 /* dfArrowZ1 = CPLAtof(szLineBuf); */
      92          28 :                 break;
      93             : 
      94          28 :             case 11:
      95          28 :                 dfTextX = CPLAtof(szLineBuf);
      96          28 :                 break;
      97             : 
      98          28 :             case 21:
      99          28 :                 dfTextY = CPLAtof(szLineBuf);
     100          28 :                 break;
     101             : 
     102          28 :             case 31:
     103             :                 /* dfTextZ = CPLAtof(szLineBuf); */
     104          28 :                 break;
     105             : 
     106          28 :             case 13:
     107          28 :                 dfTargetX2 = CPLAtof(szLineBuf);
     108          28 :                 break;
     109             : 
     110          28 :             case 23:
     111          28 :                 dfTargetY2 = CPLAtof(szLineBuf);
     112          28 :                 break;
     113             : 
     114          28 :             case 33:
     115             :                 /* dfTargetZ2 = CPLAtof(szLineBuf); */
     116          28 :                 break;
     117             : 
     118          28 :             case 14:
     119          28 :                 dfTargetX1 = CPLAtof(szLineBuf);
     120          28 :                 break;
     121             : 
     122          28 :             case 24:
     123          28 :                 dfTargetY1 = CPLAtof(szLineBuf);
     124          28 :                 break;
     125             : 
     126          28 :             case 34:
     127             :                 /* dfTargetZ1 = CPLAtof(szLineBuf); */
     128          28 :                 break;
     129             : 
     130          26 :             case 70:
     131             :                 /* nDimType = atoi(szLineBuf); */
     132          26 :                 break;
     133             : 
     134          25 :             case 1:
     135          25 :                 osText = szLineBuf;
     136          25 :                 break;
     137             : 
     138          15 :             case 1001:
     139          15 :                 bReadyForDimstyleOverride = EQUAL(szLineBuf, "ACAD");
     140          15 :                 break;
     141             : 
     142         160 :             case 1070:
     143         160 :                 if (bReadyForDimstyleOverride)
     144             :                 {
     145             :                     // Store DIMSTYLE override values in the dimension
     146             :                     // style property map. The nInnerCode values match the
     147             :                     // group codes used in the DIMSTYLE table.
     148         160 :                     const int nInnerCode = atoi(szLineBuf);
     149             :                     const char *pszProperty =
     150         160 :                         ACGetDimStylePropertyName(nInnerCode);
     151         160 :                     if (pszProperty)
     152             :                     {
     153          74 :                         nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf));
     154          74 :                         if (nCode == 1005 || nCode == 1040 || nCode == 1070)
     155          74 :                             oDimStyleProperties[pszProperty] = szLineBuf;
     156             :                     }
     157             :                 }
     158         160 :                 break;
     159             : 
     160         466 :             default:
     161         466 :                 TranslateGenericProperty(poFeature, nCode, szLineBuf);
     162         466 :                 break;
     163             :         }
     164             :     }
     165          30 :     if (nCode < 0)
     166             :     {
     167           0 :         DXF_LAYER_READER_ERROR();
     168           0 :         delete poFeature;
     169           0 :         return nullptr;
     170             :     }
     171          30 :     if (nCode == 0)
     172          30 :         poDS->UnreadValue();
     173             : 
     174             :     // If osBlockName (group code 2) refers to a valid block, we can just insert
     175             :     // that block - that should give us the correctly exploded geometry of this
     176             :     // dimension. If this value is missing, or doesn't refer to a valid block,
     177             :     // we will need to use our own logic to generate the dimension lines.
     178          30 :     if (bHaveBlock && osBlockName.length() > 0)
     179             :     {
     180             :         // Always inline the block, because this is an anonymous block that the
     181             :         // user likely doesn't know or care about
     182             :         try
     183             :         {
     184          19 :             OGRDXFFeature *poBlockFeature = InsertBlockInline(
     185          22 :                 CPLGetErrorCounter(), osBlockName, OGRDXFInsertTransformer(),
     186          19 :                 poFeature, apoPendingFeatures, true, false);
     187             : 
     188          16 :             return poBlockFeature;  // may be NULL but that is OK
     189             :         }
     190           3 :         catch (const std::invalid_argument &)
     191             :         {
     192             :         }
     193             :     }
     194             : 
     195             :     // Unpack the dimension style
     196          14 :     const double dfScale = CPLAtof(oDimStyleProperties["DIMSCALE"]);
     197          14 :     const double dfArrowheadSize = CPLAtof(oDimStyleProperties["DIMASZ"]);
     198          14 :     const double dfExtLineExtendLength = CPLAtof(oDimStyleProperties["DIMEXE"]);
     199          14 :     const double dfExtLineOffset = CPLAtof(oDimStyleProperties["DIMEXO"]);
     200          14 :     const bool bWantExtLine1 = atoi(oDimStyleProperties["DIMSE1"]) == 0;
     201          14 :     const bool bWantExtLine2 = atoi(oDimStyleProperties["DIMSE2"]) == 0;
     202          14 :     const double dfTextHeight = CPLAtof(oDimStyleProperties["DIMTXT"]);
     203          14 :     const int nUnitsPrecision = atoi(oDimStyleProperties["DIMDEC"]);
     204             :     const bool bTextSupposedlyCentered =
     205          14 :         atoi(oDimStyleProperties["DIMTAD"]) == 0;
     206          28 :     const CPLString osTextColor = oDimStyleProperties["DIMCLRT"];
     207             : 
     208             :     /*************************************************************************
     209             : 
     210             :        DIMENSION geometry layout
     211             : 
     212             :                       (11,21)(text center point)
     213             :             |          DimText                  |
     214             :     (10,20) X<--------------------------------->X (Arrow2 - computed)
     215             :     (Arrow1)|                                   |
     216             :             |                                   |
     217             :             |                                   X (13,23) (Target2)
     218             :             |
     219             :             X (14,24) (Target1)
     220             : 
     221             :     Given:
     222             :       Locations Arrow1, Target1, and Target2 we need to compute Arrow2.
     223             : 
     224             :     Steps:
     225             :      1) Compute direction vector from Target1 to Arrow1 (Vec1).
     226             :      2) Compute direction vector for arrow as perpendicular to Vec1 (call Vec2).
     227             :      3) Compute Arrow2 location as intersection between line defined by
     228             :         Vec2 and Arrow1 and line defined by Target2 and direction Vec1 (call
     229             :     Arrow2)
     230             : 
     231             :     Then we can draw lines for the various components.
     232             : 
     233             :     Note that Vec1 and Vec2 may be horizontal, vertical or on an angle but
     234             :     the approach is as above in all these cases.
     235             : 
     236             :     *************************************************************************/
     237             : 
     238             :     /* -------------------------------------------------------------------- */
     239             :     /*      Step 1, compute direction vector between Target1 and Arrow1.    */
     240             :     /* -------------------------------------------------------------------- */
     241          14 :     double dfVec1X = dfArrowX1 - dfTargetX1;
     242          14 :     double dfVec1Y = dfArrowY1 - dfTargetY1;
     243             : 
     244             :     // make Vec1 a unit vector
     245          14 :     double dfVec1Length = PointDist(0, 0, dfVec1X, dfVec1Y);
     246          14 :     if (dfVec1Length > 0.0)
     247             :     {
     248          12 :         dfVec1X /= dfVec1Length;
     249          12 :         dfVec1Y /= dfVec1Length;
     250             :     }
     251             : 
     252             :     /* -------------------------------------------------------------------- */
     253             :     /*      Step 2, compute the direction vector from Arrow1 to Arrow2      */
     254             :     /*      as a perpendicular to Vec1.                                     */
     255             :     /* -------------------------------------------------------------------- */
     256          14 :     double dfVec2X = dfVec1Y;
     257          14 :     double dfVec2Y = -dfVec1X;
     258             : 
     259             :     /* -------------------------------------------------------------------- */
     260             :     /*      Step 3, compute intersection of line from target2 along         */
     261             :     /*      direction vector 1, with the line through Arrow1 and            */
     262             :     /*      direction vector 2.                                             */
     263             :     /* -------------------------------------------------------------------- */
     264          14 :     double dfArrowX2 = 0.0;
     265          14 :     double dfArrowY2 = 0.0;
     266             : 
     267             :     // special case if vec1 is zero, which means the arrow and target
     268             :     // points coincide.
     269          14 :     if (dfVec1X == 0.0 && dfVec1Y == 0.0)
     270             :     {
     271           2 :         dfArrowX2 = dfTargetX2;
     272           2 :         dfArrowY2 = dfTargetY2;
     273             :     }
     274             : 
     275             :     // special case if vec1 is vertical.
     276          12 :     else if (dfVec1X == 0.0)
     277             :     {
     278           5 :         dfArrowX2 = dfTargetX2;
     279           5 :         dfArrowY2 = dfArrowY1;
     280             :     }
     281             : 
     282             :     // special case if vec1 is horizontal.
     283           7 :     else if (dfVec1Y == 0.0)
     284             :     {
     285           3 :         dfArrowX2 = dfArrowX1;
     286           3 :         dfArrowY2 = dfTargetY2;
     287             :     }
     288             : 
     289             :     else  // General case for diagonal vectors.
     290             :     {
     291             :         // first convert vec1 + target2 into y = mx + b format: call this L1
     292             : 
     293           4 :         const double dfL1M = dfVec1Y / dfVec1X;
     294           4 :         const double dfL1B = dfTargetY2 - dfL1M * dfTargetX2;
     295             : 
     296             :         // convert vec2 + Arrow1 into y = mx + b format, call this L2
     297             : 
     298           4 :         const double dfL2M = dfVec2Y / dfVec2X;
     299           4 :         const double dfL2B = dfArrowY1 - dfL2M * dfArrowX1;
     300             : 
     301             :         // Compute intersection x = (b2-b1) / (m1-m2)
     302             : 
     303           4 :         dfArrowX2 = (dfL2B - dfL1B) / (dfL1M - dfL2M);
     304           4 :         dfArrowY2 = dfL2M * dfArrowX2 + dfL2B;
     305             :     }
     306             : 
     307             :     /* -------------------------------------------------------------------- */
     308             :     /*      Create geometries for the different components of the           */
     309             :     /*      dimension object.                                               */
     310             :     /* -------------------------------------------------------------------- */
     311          14 :     OGRMultiLineString *poMLS = new OGRMultiLineString();
     312          28 :     OGRLineString oLine;
     313             : 
     314             :     // Main arrow line between Arrow1 and Arrow2.
     315          14 :     oLine.setPoint(0, dfArrowX1, dfArrowY1);
     316          14 :     oLine.setPoint(1, dfArrowX2, dfArrowY2);
     317          14 :     poMLS->addGeometry(&oLine);
     318             : 
     319             :     // Insert default arrowheads.
     320          14 :     InsertArrowhead(poFeature, "", &oLine, dfArrowheadSize * dfScale);
     321          14 :     InsertArrowhead(poFeature, "", &oLine, dfArrowheadSize * dfScale, true);
     322             : 
     323             :     // Dimension line from Target1 to Arrow1 with a small extension.
     324          14 :     oLine.setPoint(0, dfTargetX1 + dfVec1X * dfExtLineOffset,
     325          14 :                    dfTargetY1 + dfVec1Y * dfExtLineOffset);
     326          14 :     oLine.setPoint(1, dfArrowX1 + dfVec1X * dfExtLineExtendLength,
     327          14 :                    dfArrowY1 + dfVec1Y * dfExtLineExtendLength);
     328          14 :     if (bWantExtLine1 && oLine.get_Length() > 0.0)
     329             :     {
     330          12 :         poMLS->addGeometry(&oLine);
     331             :     }
     332             : 
     333             :     // Dimension line from Target2 to Arrow2 with a small extension.
     334          14 :     oLine.setPoint(0, dfTargetX2 + dfVec1X * dfExtLineOffset,
     335          14 :                    dfTargetY2 + dfVec1Y * dfExtLineOffset);
     336          14 :     oLine.setPoint(1, dfArrowX2 + dfVec1X * dfExtLineExtendLength,
     337          14 :                    dfArrowY2 + dfVec1Y * dfExtLineExtendLength);
     338          14 :     if (bWantExtLine2 && oLine.get_Length() > 0.0)
     339             :     {
     340          12 :         poMLS->addGeometry(&oLine);
     341             :     }
     342             : 
     343          14 :     poFeature->SetGeometryDirectly(poMLS);
     344             : 
     345          14 :     PrepareLineStyle(poFeature);
     346             : 
     347             :     /* -------------------------------------------------------------------- */
     348             :     /*      Prepare a new feature to serve as the dimension text label      */
     349             :     /*      feature.  We will push it onto the layer as a pending           */
     350             :     /*      feature for the next feature read.                              */
     351             :     /*                                                                      */
     352             :     /*      The DXF format supports a myriad of options for dimension       */
     353             :     /*      text placement, some of which involve the drawing of            */
     354             :     /*      additional lines and the like.  For now we ignore most of       */
     355             :     /*      those properties and place the text alongside the dimension     */
     356             :     /*      line.                                                           */
     357             :     /* -------------------------------------------------------------------- */
     358             : 
     359             :     // a single space suppresses labeling.
     360          14 :     if (osText == " ")
     361           0 :         return poFeature;
     362             : 
     363          14 :     OGRDXFFeature *poLabelFeature = poFeature->CloneDXFFeature();
     364             : 
     365          14 :     poLabelFeature->SetGeometryDirectly(new OGRPoint(dfTextX, dfTextY));
     366             : 
     367          14 :     if (osText.empty())
     368          13 :         osText = "<>";
     369             : 
     370             :     // Do we need to compute the dimension value?
     371          14 :     size_t nDimensionPos = osText.find("<>");
     372          14 :     if (nDimensionPos == std::string::npos)
     373             :     {
     374           0 :         poLabelFeature->SetField("Text", TextUnescape(osText, true));
     375             :     }
     376             :     else
     377             :     {
     378             :         // Replace the first occurrence of <> with the dimension
     379          14 :         CPLString osDimensionText;
     380          14 :         FormatDimension(osDimensionText,
     381             :                         PointDist(dfArrowX1, dfArrowY1, dfArrowX2, dfArrowY2),
     382             :                         nUnitsPrecision);
     383          14 :         osText.replace(nDimensionPos, 2, osDimensionText);
     384          14 :         poLabelFeature->SetField("Text", TextUnescape(osText, true));
     385             :     }
     386             : 
     387          14 :     CPLString osStyle;
     388             :     char szBuffer[64];
     389             : 
     390             :     osStyle.Printf("LABEL(f:\"Arial\",t:\"%s\"",
     391          14 :                    TextUnescape(osText.c_str(), true).c_str());
     392             : 
     393             :     // If the text is supposed to be centered on the line, we align
     394             :     // it above the line. Drawing it properly would require us to
     395             :     // work out the width of the text, which seems like too much
     396             :     // effort for what is just a fallback renderer.
     397          14 :     if (bTextSupposedlyCentered)
     398           4 :         osStyle += ",p:11";
     399             :     else
     400          10 :         osStyle += ",p:5";
     401             : 
     402             :     // Compute the text angle. Use atan to avoid upside-down text
     403          14 :     const double dfTextAngle =
     404             :         (dfArrowX1 == dfArrowX2)
     405          14 :             ? -90.0
     406          10 :             : atan((dfArrowY1 - dfArrowY2) / (dfArrowX1 - dfArrowX2)) * 180.0 /
     407             :                   M_PI;
     408             : 
     409          14 :     if (dfTextAngle != 0.0)
     410             :     {
     411           9 :         CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfTextAngle);
     412           9 :         osStyle += CPLString().Printf(",a:%s", szBuffer);
     413             :     }
     414             : 
     415          14 :     if (dfTextHeight != 0.0)
     416             :     {
     417          14 :         CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfTextHeight * dfScale);
     418          14 :         osStyle += CPLString().Printf(",s:%sg", szBuffer);
     419             :     }
     420             : 
     421          14 :     poLabelFeature->oStyleProperties["Color"] = osTextColor;
     422          14 :     osStyle += ",c:";
     423          14 :     osStyle += poLabelFeature->GetColor(poDS, poFeature);
     424             : 
     425          14 :     osStyle += ")";
     426             : 
     427          14 :     poLabelFeature->SetStyleString(osStyle);
     428             : 
     429          14 :     apoPendingFeatures.push(poLabelFeature);
     430             : 
     431          14 :     return poFeature;
     432             : }
     433             : 
     434             : /************************************************************************/
     435             : /*                          FormatDimension()                           */
     436             : /*                                                                      */
     437             : /*      Format a dimension number according to the current files        */
     438             : /*      formatting conventions.                                         */
     439             : /************************************************************************/
     440             : 
     441          14 : void OGRDXFLayer::FormatDimension(CPLString &osText, const double dfValue,
     442             :                                   int nPrecision)
     443             : 
     444             : {
     445          14 :     if (nPrecision < 0)
     446           0 :         nPrecision = 0;
     447          14 :     else if (nPrecision > 20)
     448           0 :         nPrecision = 20;
     449             : 
     450             :     // We could do a significantly more precise formatting if we want
     451             :     // to spend the effort.  See QCAD's rs_dimlinear.cpp and related files
     452             :     // for example.
     453             : 
     454             :     char szFormat[32];
     455          14 :     snprintf(szFormat, sizeof(szFormat), "%%.%df", nPrecision);
     456             : 
     457             :     char szBuffer[64];
     458          14 :     CPLsnprintf(szBuffer, sizeof(szBuffer), szFormat, dfValue);
     459             : 
     460          14 :     osText = szBuffer;
     461          14 : }

Generated by: LCOV version 1.14