繁体   English   中英

如何在 CGAL 中沿 x_monotone_curve_2 获得点(沿弧的点)?

[英]How can I get points along an x_monotone_curve_2 in CGAL (points along an arc)?

我在 CGAL 中有一个x_monotone_curve_2圆形曲线,我想沿着所述曲线找到插值点。 下面是演示我当前方法的 MWE,但它包含许多不精确的数学和角度计算。 感觉好像有一种更优雅的方式可以用 CGAL 来做到这一点。

沿着x_monotone_curve_2圆形曲线查找插值点的好方法是什么?

MWE

// Compile with: clang++ -DBOOST_ALL_NO_LIB -DCGAL_USE_GMPXX=1 -O2 -g -DNDEBUG -Wall -Wextra -pedantic -march=native -frounding-math main.cpp -lgmpxx -lmpfr -lgmp

#include <CGAL/Arr_circle_segment_traits_2.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Gps_circle_segment_traits_2.h>

using K = CGAL::Exact_predicates_exact_constructions_kernel;
using APoint_2 = CGAL::Arr_circle_segment_traits_2<K>::Point_2;
using Point_2 = K::Point_2;
using Circle_2 = K::Circle_2;
using Vector_2 = K::Vector_2;
using Traits_2 = CGAL::Gps_circle_segment_traits_2<K>;
using X_monotone_curve_2 = Traits_2::X_monotone_curve_2;

// Seems to be the only way to convert points between trait and non-trait systems.
Point_2 pc(const APoint_2 &pt){
  return Point_2(CGAL::to_double(pt.x()), CGAL::to_double(pt.y()));
}


class CircularArcInterpolator {
 public:
  CircularArcInterpolator(const X_monotone_curve_2 &curve) : curve(curve) {
    if(!curve.is_circular()){
      throw std::runtime_error("CircularArcInterpolator requires a circular curve!");
    }
  }

  // t is in the range [0,1] where 0 is the source of the curve and 1 is the
  // target
  Point_2 operator()(const double t) const {
    std::cout<<"\n\n";
    // Supporting circle
    const auto& circle = curve.supporting_circle();
    // Starting point of the circular arc
    const auto start = pc(curve.source());
    // Finishing point (counter-clockwise) of the circular arc
    const auto end = pc(curve.target());
    // If source==target this is a complete circle

    // Current strategy: find the angle between start/end point and x-axis
    // Get difference between angles and walk along this
    const Vector_2 x_axis(1,0);
    const auto start_angle = CGAL::angle(start - circle.center(), x_axis);
    const auto end_angle = CGAL::angle(end - circle.center(), x_axis);
    double ang_diff = end_angle - start_angle;
    // Special case if the start and end point describe a full circle
    if(ang_diff == 0){
      ang_diff = 2*M_PI;
    }
    std::cout<<"ang_diff = "<<ang_diff<<std::endl;
    const auto cx = circle.center().x();
    const auto cy = circle.center().y();
    const auto radius = std::sqrt(CGAL::to_double(circle.squared_radius()));
    std::cout<<"cx = "<<cx<<std::endl;
    std::cout<<"cy = "<<cy<<std::endl;
    std::cout<<"radius = "<<radius<<std::endl;
    std::cout<<"t = "<<t<<std::endl;
    std::cout<<"inc angle = "<<(start_angle + ang_diff*t)<<std::endl;

    return {
      cx + radius * std::cos(start_angle + ang_diff*t),
      cy + radius * std::sin(start_angle + ang_diff*t)
    };
  }

 private:
  const X_monotone_curve_2 curve;
};

int main(){
  Point_2 center(0,0);
  Circle_2 supporting_circle(center, 1);
  APoint_2 start_end(1, 0);
  X_monotone_curve_2 curve(supporting_circle, start_end, start_end, CGAL::COUNTERCLOCKWISE);
  CircularArcInterpolator cai(curve);
  const auto right = cai(0.00); // Should be Point_2(1,  0)
  const auto up    = cai(0.25); // Should be Point_2(0,  1)
  const auto left  = cai(0.50); // Should be Point_2(-1, 0)
  const auto down  = cai(0.75); // Should be Point_2(0, -1)
  const auto end   = cai(1.00); // Should be Point_2(1,  0)

  std::cout<<right<<std::endl;
  std::cout<<up   <<std::endl;
  std::cout<<left <<std::endl;
  std::cout<<down <<std::endl;
  std::cout<<end  <<std::endl;

  return 0;
}

CGAL 的未来版本将在 'CGAL::Arr_circle_segment_traits_2' 特征中包含一个新结构,即 Approximate_2,它计算近似给定弧的点序列。 代码附在下面。 您可以简单地剪切并粘贴到特征中(直到发布官方代码)。

  typedef double                                        Approximate_number_type;
  typedef CGAL::Cartesian<Approximate_number_type>      Approximate_kernel;
  typedef Approximate_kernel::Point_2                   Approximate_point_2;

  class Approximate_2 {
  protected:
    using Traits = Arr_circle_segment_traits_2<Kernel, Filter>;

    /*! The traits (in case it has state) */
    const Traits& m_traits;

    /*! Constructor
     * \param traits the traits.
     */
    Approximate_2(const Traits& traits) : m_traits(traits) {}

    friend class Arr_circle_segment_traits_2<Kernel, Filter>;

  public:
    /*! Obtain an approximation of a point coordinate.
     * \param p the exact point.
     * \param i the coordinate index (either 0 or 1).
     * \pre i is either 0 or 1.
     * \return An approximation of p's x-coordinate (if i == 0), or an
     *         approximation of p's y-coordinate (if i == 1).
     */
    Approximate_number_type operator()(const Point_2& p, int i) const {
      CGAL_precondition((i == 0) || (i == 1));
      return (i == 0) ? (CGAL::to_double(p.x())) : (CGAL::to_double(p.y()));
    }

    /*! Obtain an approximation of a point.
     */
    Approximate_point_2 operator()(const Point_2& p) const
    { return Approximate_point_2(operator()(p, 0), operator()(p, 1)); }

    /*! Obtain an approximation of an \f$x\f$-monotone curve.
     */
    template <typename OutputIterator>
    OutputIterator operator()(const X_monotone_curve_2& xcv, double error,
                              OutputIterator oi, bool l2r = true) const {
      if (xcv.is_linear()) return approximate_segment(xcv, oi, l2r);
      return approximate_arc(xcv, error, oi, l2r);;
    }

  private:
    /*! Handle segments.
     */
    template <typename OutputIterator>
    OutputIterator approximate_segment(const X_monotone_curve_2& xcv,
                                       OutputIterator oi,
                                       bool l2r = true) const {
      // std::cout << "SEGMENT\n";
      auto min_vertex = m_traits.construct_min_vertex_2_object();
      auto max_vertex = m_traits.construct_max_vertex_2_object();
      const auto& src = (l2r) ? min_vertex(xcv) : max_vertex(xcv);
      const auto& trg = (l2r) ? max_vertex(xcv) : min_vertex(xcv);
      auto xs = CGAL::to_double(src.x());
      auto ys = CGAL::to_double(src.y());
      auto xt = CGAL::to_double(trg.x());
      auto yt = CGAL::to_double(trg.y());
      *oi++ = Approximate_point_2(xs, ys);
      *oi++ = Approximate_point_2(xt, yt);
      return oi;
    }

    template <typename OutputIterator, typename Op, typename Transform>
    OutputIterator add_points(double x1, double y1, double t1,
                              double x2, double y2, double t2,
                              double error, OutputIterator oi,
                              Op op, Transform transform) const {
      auto tm = (t1 + t2)*0.5;

      // Compute the canocal point where the error is maximal.
      double xm, ym;
      op(tm, xm, ym);

      auto dx = x2 - x1;
      auto dy = y2 - y1;

      // Compute the error; abort if it is below the threshold
      auto l = std::sqrt(dx*dx + dy*dy);
      auto e = std::abs((xm*dy - ym*dx + x2*y1 - x1*y2) / l);
      if (e < error) return oi;

      double x, y;
      transform(xm, ym, x, y);
      add_points(x1, y1, t1, xm, ym, tm, error, oi, op, transform);
      *oi++ = Approximate_point_2(x, y);
      add_points(xm, ym, tm, x2, y2, t2, error, oi, op, transform);
      return oi;
    }

    /*! Compute the circular point given the parameter t and the transform
     * data, that is, the center (translation) and the sin and cos of the
     * rotation angle.
     */
    void circular_point(double r, double t, double& x, double& y) const {
      x = r * std::cos(t);
      y = r * std::sin(t);
    }

    /*! Transform a point. In particular, rotate the canonical point
     * (`xc`,`yc`) by an angle, the sine and cosine of which are `sint` and
     * `cost`, respectively, and translate by (`cx`,`cy`).
     */
    void transform_point(double xc, double yc, double cx, double cy,
                         double& x, double& y) const {
      x = xc + cx;
      y = yc + cy;
    }

    /*! Handle circular arcs.
     */
    template <typename OutputIterator>
    OutputIterator approximate_arc(const X_monotone_curve_2& xcv,
                                   double error, OutputIterator oi,
                                   bool l2r = true) const {
      auto min_vertex = m_traits.construct_min_vertex_2_object();
      auto max_vertex = m_traits.construct_max_vertex_2_object();
      const auto& src = (l2r) ? min_vertex(xcv) : max_vertex(xcv);
      const auto& trg = (l2r) ? max_vertex(xcv) : min_vertex(xcv);
      auto xs = CGAL::to_double(src.x());
      auto ys = CGAL::to_double(src.y());
      auto xt = CGAL::to_double(trg.x());
      auto yt = CGAL::to_double(trg.y());

      const typename Kernel::Circle_2& circ = xcv.supporting_circle();
      auto r_sqr = circ.squared_radius();
      auto r = std::sqrt(CGAL::to_double(r_sqr));

      // Obtain the center:
      auto cx = CGAL::to_double(circ.center().x());
      auto cy = CGAL::to_double(circ.center().y());

      // Inverse transform the source and target
      auto xs_t = xs - cx;
      auto ys_t = ys - cy;
      auto xt_t = xt - cx;
      auto yt_t = yt - cy;

      // Compute the parameters ts and tt such that
      // source == (x(ts),y(ts)), and
      // target == (x(tt),y(tt))
      auto ts = std::atan2(r*ys_t, r*xs_t);
      if (ts < 0) ts += 2*M_PI;
      auto tt = std::atan2(r*yt_t, r*xt_t);
      if (tt < 0) tt += 2*M_PI;
      auto orient(xcv.orientation());
      if (xcv.source() != src) orient = CGAL::opposite(orient);
      if (orient == COUNTERCLOCKWISE) {
        if (tt < ts) tt += 2*M_PI;
      }
      else {
        if (ts < tt) ts += 2*M_PI;
      }

      *oi++ = Approximate_point_2(xs, ys);
      add_points(xs_t, ys_t, ts, xt_t, yt_t, tt, error, oi,
                 [&](double tm, double& xm, double& ym) {
                   circular_point(r, tm, xm, ym);
                 },
                 [&](double xc, double& yc, double& x, double& y) {
                   transform_point(xc, yc, cx, cy, x, y);
                 });
      *oi++ = Approximate_point_2(xt, yt);
      return oi;
    }
  };

  /*! Obtain an Approximate_2 functor object. */
  Approximate_2 approximate_2_object() const { return Approximate_2(*this); }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM