LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/dgn - dgnstroke.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 21 111 18.9 %
Date: 2024-11-25 13:07:18 Functions: 2 3 66.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Microstation DGN Access Library
       4             :  * Purpose:  Code to stroke Arcs/Ellipses into polylines.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2001, Avenza Systems Inc, http://www.avenza.com/
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "dgnlibp.h"
      14             : #include <cmath>
      15             : 
      16             : constexpr double DEG_TO_RAD = M_PI / 180.0;
      17             : 
      18             : /************************************************************************/
      19             : /*                         ComputePointOnArc()                          */
      20             : /************************************************************************/
      21             : 
      22         438 : static void ComputePointOnArc2D(double dfPrimary, double dfSecondary,
      23             :                                 double dfAxisRotation, double dfAngle,
      24             :                                 double *pdfX, double *pdfY)
      25             : 
      26             : {
      27             :     // dfAxisRotation and dfAngle are supposed to be in Radians
      28         438 :     const double dfCosRotation = cos(dfAxisRotation);
      29         438 :     const double dfSinRotation = sin(dfAxisRotation);
      30         438 :     const double dfEllipseX = dfPrimary * cos(dfAngle);
      31         438 :     const double dfEllipseY = dfSecondary * sin(dfAngle);
      32             : 
      33         438 :     *pdfX = dfEllipseX * dfCosRotation - dfEllipseY * dfSinRotation;
      34         438 :     *pdfY = dfEllipseX * dfSinRotation + dfEllipseY * dfCosRotation;
      35         438 : }
      36             : 
      37             : /************************************************************************/
      38             : /*                            DGNStrokeArc()                            */
      39             : /************************************************************************/
      40             : 
      41             : /**
      42             :  * Generate a polyline approximation of an arc.
      43             :  *
      44             :  * Produce a series of equidistant (actually equi-angle) points along
      45             :  * an arc.  Currently this only works for 2D arcs (and ellipses).
      46             :  *
      47             :  * @param hFile the DGN file to which the arc belongs (currently not used).
      48             :  * @param psArc the arc to be approximated.
      49             :  * @param nPoints the number of points to use to approximate the arc.
      50             :  * @param pasPoints the array of points into which to put the results.
      51             :  * There must be room for at least nPoints points.
      52             :  *
      53             :  * @return TRUE on success or FALSE on failure.
      54             :  */
      55             : 
      56           6 : int DGNStrokeArc(CPL_UNUSED DGNHandle hFile, DGNElemArc *psArc, int nPoints,
      57             :                  DGNPoint *pasPoints)
      58             : {
      59           6 :     if (nPoints < 2)
      60           0 :         return FALSE;
      61             : 
      62           6 :     if (psArc->primary_axis == 0.0 || psArc->secondary_axis == 0.0)
      63             :     {
      64           0 :         CPLError(CE_Warning, CPLE_AppDefined,
      65             :                  "Zero primary or secondary axis in DGNStrokeArc().");
      66           0 :         return FALSE;
      67             :     }
      68             : 
      69           6 :     const double dfAngleStep = psArc->sweepang / (nPoints - 1);
      70         444 :     for (int i = 0; i < nPoints; i++)
      71             :     {
      72         438 :         const double dfAngle = (psArc->startang + dfAngleStep * i) * DEG_TO_RAD;
      73             : 
      74         438 :         ComputePointOnArc2D(psArc->primary_axis, psArc->secondary_axis,
      75         438 :                             psArc->rotation * DEG_TO_RAD, dfAngle,
      76         438 :                             &(pasPoints[i].x), &(pasPoints[i].y));
      77         438 :         pasPoints[i].x += psArc->origin.x;
      78         438 :         pasPoints[i].y += psArc->origin.y;
      79         438 :         pasPoints[i].z = psArc->origin.z;
      80             :     }
      81             : 
      82           6 :     return TRUE;
      83             : }
      84             : 
      85             : /************************************************************************/
      86             : /*                           DGNStrokeCurve()                           */
      87             : /************************************************************************/
      88             : 
      89             : /**
      90             :  * Generate a polyline approximation of an curve.
      91             :  *
      92             :  * Produce a series of equidistant points along a microstation curve element.
      93             :  * Currently this only works for 2D.
      94             :  *
      95             :  * @param hFile the DGN file to which the arc belongs (currently not used).
      96             :  * @param psCurve the curve to be approximated.
      97             :  * @param nPoints the number of points to use to approximate the curve.
      98             :  * @param pasPoints the array of points into which to put the results.
      99             :  * There must be room for at least nPoints points.
     100             :  *
     101             :  * @return TRUE on success or FALSE on failure.
     102             :  */
     103             : 
     104           0 : int DGNStrokeCurve(CPL_UNUSED DGNHandle hFile, DGNElemMultiPoint *psCurve,
     105             :                    int nPoints, DGNPoint *pasPoints)
     106             : {
     107           0 :     const int nDGNPoints = psCurve->num_vertices;
     108             : 
     109           0 :     if (nDGNPoints < 6)
     110           0 :         return FALSE;
     111             : 
     112           0 :     if (nPoints < nDGNPoints - 4)
     113           0 :         return FALSE;
     114             : 
     115             :     /* -------------------------------------------------------------------- */
     116             :     /*      Compute the Compute the slopes/distances of the segments.       */
     117             :     /* -------------------------------------------------------------------- */
     118             :     double *padfMx =
     119           0 :         static_cast<double *>(CPLMalloc(sizeof(double) * nDGNPoints));
     120             :     double *padfMy =
     121           0 :         static_cast<double *>(CPLMalloc(sizeof(double) * nDGNPoints));
     122             :     double *padfD =
     123           0 :         static_cast<double *>(CPLMalloc(sizeof(double) * nDGNPoints));
     124             :     double *padfTx =
     125           0 :         static_cast<double *>(CPLMalloc(sizeof(double) * nDGNPoints));
     126             :     double *padfTy =
     127           0 :         static_cast<double *>(CPLMalloc(sizeof(double) * nDGNPoints));
     128             : 
     129           0 :     double dfTotalD = 0.0;
     130             : 
     131           0 :     DGNPoint *pasDGNPoints = psCurve->vertices;
     132             : 
     133           0 :     for (int k = 0; k < nDGNPoints - 1; k++)
     134             :     {
     135             :         /* coverity[overrun-local] */
     136           0 :         padfD[k] = sqrt((pasDGNPoints[k + 1].x - pasDGNPoints[k].x) *
     137           0 :                             (pasDGNPoints[k + 1].x - pasDGNPoints[k].x) +
     138           0 :                         (pasDGNPoints[k + 1].y - pasDGNPoints[k].y) *
     139           0 :                             (pasDGNPoints[k + 1].y - pasDGNPoints[k].y));
     140           0 :         if (padfD[k] == 0.0)
     141             :         {
     142           0 :             padfD[k] = 0.0001;
     143           0 :             padfMx[k] = 0.0;
     144           0 :             padfMy[k] = 0.0;
     145             :         }
     146             :         else
     147             :         {
     148           0 :             padfMx[k] = (pasDGNPoints[k + 1].x - pasDGNPoints[k].x) / padfD[k];
     149           0 :             padfMy[k] = (pasDGNPoints[k + 1].y - pasDGNPoints[k].y) / padfD[k];
     150             :         }
     151             : 
     152           0 :         if (k > 1 && k < nDGNPoints - 3)
     153           0 :             dfTotalD += padfD[k];
     154             :     }
     155             : 
     156             :     /* -------------------------------------------------------------------- */
     157             :     /*      Compute the Tx, and Ty coefficients for each segment.           */
     158             :     /* -------------------------------------------------------------------- */
     159           0 :     for (int k = 2; k < nDGNPoints - 2; k++)
     160             :     {
     161           0 :         if (fabs(padfMx[k + 1] - padfMx[k]) == 0.0 &&
     162           0 :             fabs(padfMx[k - 1] - padfMx[k - 2]) == 0.0)
     163             :         {
     164           0 :             padfTx[k] = (padfMx[k] + padfMx[k - 1]) / 2;
     165             :         }
     166             :         else
     167             :         {
     168           0 :             padfTx[k] = (padfMx[k - 1] * fabs(padfMx[k + 1] - padfMx[k]) +
     169           0 :                          padfMx[k] * fabs(padfMx[k - 1] - padfMx[k - 2])) /
     170           0 :                         (std::abs(padfMx[k + 1] - padfMx[k]) +
     171           0 :                          std::abs(padfMx[k - 1] - padfMx[k - 2]));
     172             :         }
     173             : 
     174           0 :         if (fabs(padfMy[k + 1] - padfMy[k]) == 0.0 &&
     175           0 :             fabs(padfMy[k - 1] - padfMy[k - 2]) == 0.0)
     176             :         {
     177           0 :             padfTy[k] = (padfMy[k] + padfMy[k - 1]) / 2;
     178             :         }
     179             :         else
     180             :         {
     181           0 :             padfTy[k] = (padfMy[k - 1] * fabs(padfMy[k + 1] - padfMy[k]) +
     182           0 :                          padfMy[k] * fabs(padfMy[k - 1] - padfMy[k - 2])) /
     183           0 :                         (std::abs(padfMy[k + 1] - padfMy[k]) +
     184           0 :                          std::abs(padfMy[k - 1] - padfMy[k - 2]));
     185             :         }
     186             :     }
     187             : 
     188             :     /* -------------------------------------------------------------------- */
     189             :     /*      Determine a step size in D.  We scale things so that we have    */
     190             :     /*      roughly equidistant steps in D, but assume we also want to      */
     191             :     /*      include every node along the way.                               */
     192             :     /* -------------------------------------------------------------------- */
     193           0 :     double dfStepSize = dfTotalD / (nPoints - (nDGNPoints - 4) - 1);
     194             : 
     195             :     /* ==================================================================== */
     196             :     /*      Process each of the segments.                                   */
     197             :     /* ==================================================================== */
     198           0 :     double dfD = dfStepSize;
     199           0 :     int iOutPoint = 0;
     200             : 
     201           0 :     for (int k = 2; k < nDGNPoints - 3; k++)
     202             :     {
     203             :         /* --------------------------------------------------------------------
     204             :          */
     205             :         /*      Compute the "x" coefficients for this segment. */
     206             :         /* --------------------------------------------------------------------
     207             :          */
     208           0 :         const double dfCx = padfTx[k];
     209           0 :         const double dfBx =
     210           0 :             (3.0 * (pasDGNPoints[k + 1].x - pasDGNPoints[k].x) / padfD[k] -
     211           0 :              2.0 * padfTx[k] - padfTx[k + 1]) /
     212           0 :             padfD[k];
     213           0 :         const double dfAx =
     214           0 :             (padfTx[k] + padfTx[k + 1] -
     215           0 :              2 * (pasDGNPoints[k + 1].x - pasDGNPoints[k].x) / padfD[k]) /
     216           0 :             (padfD[k] * padfD[k]);
     217             : 
     218             :         /* --------------------------------------------------------------------
     219             :          */
     220             :         /*      Compute the Y coefficients for this segment. */
     221             :         /* --------------------------------------------------------------------
     222             :          */
     223           0 :         const double dfCy = padfTy[k];
     224           0 :         const double dfBy =
     225           0 :             (3.0 * (pasDGNPoints[k + 1].y - pasDGNPoints[k].y) / padfD[k] -
     226           0 :              2.0 * padfTy[k] - padfTy[k + 1]) /
     227           0 :             padfD[k];
     228           0 :         const double dfAy =
     229           0 :             (padfTy[k] + padfTy[k + 1] -
     230           0 :              2 * (pasDGNPoints[k + 1].y - pasDGNPoints[k].y) / padfD[k]) /
     231           0 :             (padfD[k] * padfD[k]);
     232             : 
     233             :         /* --------------------------------------------------------------------
     234             :          */
     235             :         /*      Add the start point for this segment. */
     236             :         /* --------------------------------------------------------------------
     237             :          */
     238           0 :         pasPoints[iOutPoint].x = pasDGNPoints[k].x;
     239           0 :         pasPoints[iOutPoint].y = pasDGNPoints[k].y;
     240           0 :         pasPoints[iOutPoint].z = 0.0;
     241           0 :         iOutPoint++;
     242             : 
     243             :         /* --------------------------------------------------------------------
     244             :          */
     245             :         /*      Step along, adding intermediate points. */
     246             :         /* --------------------------------------------------------------------
     247             :          */
     248           0 :         while (dfD < padfD[k] && iOutPoint < nPoints - (nDGNPoints - k - 1))
     249             :         {
     250           0 :             pasPoints[iOutPoint].x = dfAx * dfD * dfD * dfD + dfBx * dfD * dfD +
     251           0 :                                      dfCx * dfD + pasDGNPoints[k].x;
     252           0 :             pasPoints[iOutPoint].y = dfAy * dfD * dfD * dfD + dfBy * dfD * dfD +
     253           0 :                                      dfCy * dfD + pasDGNPoints[k].y;
     254           0 :             pasPoints[iOutPoint].z = 0.0;
     255           0 :             iOutPoint++;
     256             : 
     257           0 :             dfD += dfStepSize;
     258             :         }
     259             : 
     260           0 :         dfD -= padfD[k];
     261             :     }
     262             : 
     263             :     /* -------------------------------------------------------------------- */
     264             :     /*      Add the start point for this segment.                           */
     265             :     /* -------------------------------------------------------------------- */
     266           0 :     while (iOutPoint < nPoints)
     267             :     {
     268           0 :         pasPoints[iOutPoint].x = pasDGNPoints[nDGNPoints - 3].x;
     269           0 :         pasPoints[iOutPoint].y = pasDGNPoints[nDGNPoints - 3].y;
     270           0 :         pasPoints[iOutPoint].z = 0.0;
     271           0 :         iOutPoint++;
     272             :     }
     273             : 
     274             :     /* -------------------------------------------------------------------- */
     275             :     /*      Cleanup.                                                        */
     276             :     /* -------------------------------------------------------------------- */
     277           0 :     CPLFree(padfMx);
     278           0 :     CPLFree(padfMy);
     279           0 :     CPLFree(padfD);
     280           0 :     CPLFree(padfTx);
     281           0 :     CPLFree(padfTy);
     282             : 
     283           0 :     return TRUE;
     284             : }
     285             : 
     286             : /************************************************************************/
     287             : /*                                main()                                */
     288             : /*                                                                      */
     289             : /*      test mainline                                                   */
     290             : /************************************************************************/
     291             : #ifdef notdef
     292             : int main(int argc, char **argv)
     293             : 
     294             : {
     295             :     if (argc != 5)
     296             :     {
     297             :         printf(  // ok
     298             :             "Usage: stroke primary_axis secondary_axis axis_rotation angle\n");
     299             :         exit(1);
     300             :     }
     301             : 
     302             :     const double dfPrimary = CPLAtof(argv[1]);
     303             :     const double dfSecondary = CPLAtof(argv[2]);
     304             :     const double dfAxisRotation = CPLAtof(argv[3]) / 180 * M_PI;
     305             :     const double dfAngle = CPLAtof(argv[4]) / 180 * M_PI;
     306             : 
     307             :     double dfX = 0.0;
     308             :     double dfY = 0.0;
     309             :     ComputePointOnArc2D(dfPrimary, dfSecondary, dfAxisRotation, dfAngle, &dfX,
     310             :                         &dfY);
     311             : 
     312             :     printf("X=%.2f, Y=%.2f\n", dfX, dfY);  // ok
     313             : 
     314             :     return 0;
     315             : }
     316             : 
     317             : #endif

Generated by: LCOV version 1.14