LCOV - code coverage report
Current view: top level - ftmesh - polygon.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 64 86 74.4 %
Date: 2022-01-29 21:33:29 Functions: 9 10 90.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2021  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/ftmesh
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software; you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation; either version 2 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License along
      17             : // with this program; if not, write to the Free Software Foundation, Inc.,
      18             : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      19             : 
      20             : /** \file
      21             :  * \brief Implementation of the ftpolygon class.
      22             :  *
      23             :  * The FreeType library gives us a list of tagged vertices which we need
      24             :  * to transform in a simple polygon. Then we can transform that polygon
      25             :  * to a mesh using the glut.
      26             :  *
      27             :  * \private
      28             :  */
      29             : 
      30             : // self
      31             : //
      32             : #include    <ftmesh/polygon.h>
      33             : 
      34             : 
      35             : // C++ lib
      36             : //
      37             : #include    <algorithm>
      38             : #include    <iostream>
      39             : 
      40             : 
      41             : // last include
      42             : //
      43             : #include    <snapdev/poison.h>
      44             : 
      45             : 
      46             : 
      47             : 
      48             : 
      49             : namespace ftmesh
      50             : {
      51             : 
      52             : 
      53             : constexpr unsigned int const        BEZIER_STEPS = 5;
      54             : 
      55             : 
      56             : 
      57             : 
      58             : 
      59          11 : polygon::polygon(
      60             :           FT_Vector * contour
      61             :         , char * tags
      62          11 :         , unsigned int n)
      63             : {
      64          11 :     if(n < 3)
      65             :     {
      66           0 :         throw std::logic_error("the polygon constructor expects at least 3 vectors in the contour");
      67             :     }
      68             : 
      69          11 :     point prev;
      70          11 :     point cur(contour[n - 1].x, contour[n - 1].y);  // we know n >= 3
      71          11 :     point next(contour[0].x, contour[0].y);
      72             : 
      73          11 :     double olddir(0.0);
      74          11 :     double dir((next - cur).angle());
      75          11 :     double angle(0.0);
      76             : 
      77             :     // see http://freetype.sourceforge.net/freetype2/docs/glyphs/glyphs-6.html
      78             :     // for a full description of FreeType tags
      79             :     //
      80         170 :     for(unsigned int i(0); i < n; ++i)
      81             :     {
      82         159 :         prev = cur;
      83         159 :         cur = next;
      84         159 :         int const next_pos(i + 1 == n ? 0 : i + 1);
      85         159 :         next = point(contour[next_pos].x, contour[next_pos].y);
      86         159 :         olddir = dir;
      87         159 :         dir = (next - cur).angle();
      88             : 
      89             :         // compute our path new direction.
      90             :         //
      91         159 :         double t(dir - olddir);
      92         159 :         if(t < -M_PI)
      93             :         {
      94           9 :             t += 2 * M_PI;
      95             :         }
      96         159 :         if(t > M_PI)
      97             :         {
      98          18 :             t -= 2 * M_PI;
      99             :         }
     100         159 :         angle += t;
     101             : 
     102             :         // only process point tags we know.
     103             :         //
     104         159 :         if(n < 2
     105         159 :         || FT_CURVE_TAG(tags[i]) == FT_CURVE_TAG_ON)
     106             :         {
     107         101 :             add_point(cur);
     108             :         }
     109          58 :         else if(FT_CURVE_TAG(tags[i]) == FT_CURVE_TAG_CONIC)
     110             :         {
     111          58 :             point prev2(prev);
     112          58 :             point next2(next);
     113             : 
     114             :             // previous point is either the real previous point (an "on"
     115             :             // point), or the midpoint between the current one and the
     116             :             // previous "conic off" point.
     117             :             //
     118          58 :             if(FT_CURVE_TAG(tags[(i - 1 + n) % n]) == FT_CURVE_TAG_CONIC)
     119             :             {
     120          29 :                 prev2 = (cur + prev) * 0.5;
     121          29 :                 add_point(prev2);
     122             :             }
     123             : 
     124             :             // next point is either the real next point or the midpoint.
     125             :             //
     126          58 :             if(FT_CURVE_TAG(tags[(i + 1) % n]) == FT_CURVE_TAG_CONIC)
     127             :             {
     128          29 :                 next2 = (cur + next) * 0.5;
     129             :             }
     130             : 
     131          58 :             evaluate_quadratic_curve(
     132             :                       prev2
     133             :                     , cur
     134             :                     , next2);
     135             :         }
     136           0 :         else if(FT_CURVE_TAG(tags[i          ]) == FT_CURVE_TAG_CUBIC
     137           0 :              && FT_CURVE_TAG(tags[(i + 1) % n]) == FT_CURVE_TAG_CUBIC)
     138             :         {
     139           0 :             int const after_next_pos((i + 2) % n);
     140           0 :             point const after_next(
     141           0 :                               contour[after_next_pos].x
     142           0 :                             , contour[after_next_pos].y);
     143           0 :             evaluate_cubic_curve(
     144             :                       prev
     145             :                     , cur
     146             :                     , next
     147             :                     , after_next);
     148             :         }
     149             :     }
     150             : 
     151             :     // if final angle is positive (+2PI), it's an anti-clockwise polygon,
     152             :     // otherwise (-2PI) it's clockwise.
     153             :     //
     154          11 :     f_clockwise = angle < 0.0;
     155          11 : }
     156             : 
     157             : 
     158        1475 : std::size_t polygon::size() const
     159             : {
     160        1475 :     return f_points.size();
     161             : }
     162             : 
     163             : 
     164         968 : point const & polygon::at(int idx) const
     165             : {
     166             :     // idx is often used with +1, +2 or -1
     167             :     //
     168         968 :     if(idx >= static_cast<int>(size()))
     169             :     {
     170           6 :         idx -= size();
     171             :     }
     172         962 :     else if(idx < 0)
     173             :     {
     174           0 :         idx += size();
     175             :     }
     176         968 :     return f_points.at(idx);
     177             : }
     178             : 
     179             : 
     180          58 : void polygon::evaluate_quadratic_curve(
     181             :           point const & a
     182             :         , point const & b
     183             :         , point const & c)
     184             : {
     185         290 :     for(unsigned int i(1); i < BEZIER_STEPS; i++)
     186             :     {
     187         232 :         double const t(static_cast<double>(i) / static_cast<double>(BEZIER_STEPS));
     188         232 :         double const nt(1.0 - t);
     189         232 :         point const u(a * nt + b * t);
     190         232 :         point const v(b * nt + c * t);
     191         232 :         add_point(u * nt + v * t);
     192             :     }
     193          58 : }
     194             : 
     195             : 
     196           0 : void polygon::evaluate_cubic_curve(
     197             :           point const & a
     198             :         , point const & b
     199             :         , point const & c
     200             :         , point const & d)
     201             : {
     202           0 :     for(unsigned int i = 0; i < BEZIER_STEPS; i++)
     203             :     {
     204           0 :         double const t(static_cast<double>(i) / static_cast<double>(BEZIER_STEPS));
     205           0 :         double const nt(1.0 - t);
     206           0 :         point const u(a * nt + b * t);
     207           0 :         point const v(b * nt + c * t);
     208           0 :         point const w(c * nt + d * t);
     209           0 :         point const m(u * nt + v * t);
     210           0 :         point const n(v * nt + w * t);
     211           0 :         add_point(m * nt + n * t);
     212             :     }
     213           0 : }
     214             : 
     215             : 
     216         362 : void polygon::add_point(point const & p)
     217             : {
     218             :     // try to avoid duplicates as it doesn't play well with glut tessellation
     219             :     //
     220             :     // HOWEVER, the current algorithm prevents points from going through
     221             :     // the starting point again, which I think is a mistake, that could
     222             :     // happen in the middle of the contour (but that's probably very rare)
     223             :     //
     224         724 :     if(f_points.empty()
     225         362 :     || (p != f_points.front() && p != f_points.back()))
     226             :     {
     227         362 :         f_points.push_back(p);
     228         362 :         if(p.is_left_of(f_leftmost))
     229             :         {
     230         115 :             f_leftmost = p;
     231             :         }
     232             :     }
     233         362 : }
     234             : 
     235             : 
     236          11 : point const & polygon::leftmost() const
     237             : {
     238          11 :     return f_leftmost;
     239             : }
     240             : 
     241             : 
     242          11 : void polygon::apply_parity(int parity)
     243             : {
     244             :     // contour orientation reversed?
     245             :     //
     246             :     // TBD: this would be important if we were to use the outset,
     247             :     //      I don't think it is for calculating the mesh
     248             :     //
     249          11 :     if((parity & 1) == 0 ^ f_clockwise)
     250             :     {
     251           0 :         std::reverse(f_points.begin(), f_points.end());
     252           0 :         f_clockwise = !f_clockwise;
     253             :     }
     254          11 : }
     255             : 
     256             : 
     257             : 
     258           6 : } // namespace ftmesh
     259             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13