Advertisement
fedoseevtimofey

Geometry

Nov 5th, 2017
134
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 13.51 KB | None | 0 0
  1. double sqr(double a) // квадрат числа
  2. {
  3.         return a * a;
  4. }
  5.  
  6.  
  7. bool doubleEqual(double a, double b) // сравниваем на равенство с eps, константой 1е-9 используется только здесь
  8. {
  9.         return fabs(a - b) < 1e-9;
  10. }
  11.  
  12.  
  13. bool doubleLessOrEqual(double a, double b) // <= с eps
  14. {
  15.         return a < b || doubleEqual(a, b);
  16. }
  17.  
  18.  
  19. bool doubleLess(double a, double b) // < с eps
  20. {
  21.         return a < b && !doubleEqual(a, b);
  22. }
  23.  
  24.  
  25. bool doubleGreaterOrEqual(double a, double b) // >= с eps
  26. {
  27.         return a > b || doubleEqual(a, b);
  28. }
  29.  
  30.  
  31. bool doubleGreater(double a, double b) // > с eps
  32. {
  33.         return a > b && !doubleEqual(a, b);
  34. }
  35.  
  36.  
  37. double mySqrt(double a) // mySqrt с проверкой, что корректен аргумент
  38. {
  39.         if(doubleLess(a, 0) ) //некорректный вызов
  40.         {
  41.                 throw "sqrt(-1)";
  42.         }
  43.         if(a < 0) return 0; //отрицательное ввиду небольшой погрешности
  44.         return sqrt(a);
  45. }
  46.  
  47.  
  48. struct Point{ // класс точки или вектора, далее мы эти понятия разделять не будем
  49.                                 // но условимся (для удобного чтения) большими буквами обозначать точки (A, B, C, D, ...)
  50.                                 // маленькими - вектора(v, u, w,...)
  51. private: double x, y; // 2 приватных поля, других не будет
  52. public:
  53.         Point(): x(0), y(0) {} // конструктор по умолчанию
  54.  
  55.         Point(double x, double y): x(x), y(y) {} // намеренно сделаем два конструктора вместо Point(x = 0...)
  56.                                                                                         //Это избавит нас от ошибок вида Point A = 2;
  57.  
  58.         void scan() // читаем координаты точки
  59.         {
  60.                 scanf("%lf %lf", &x, &y);
  61.         }
  62.  
  63.         void print() const // выводим координаты точки
  64.         {
  65.                 printf("%.10lf %.10lf\n", x, y);
  66.         }
  67.  
  68.         Point operator+(const Point & p) const // сложение 2х векторов
  69.         {
  70.                 return Point(x + p.x, y + p.y);
  71.         }
  72.  
  73.         Point operator-(const Point & p) const // вычитание 2х векторов
  74.         {
  75.                 return Point(x - p.x, y - p.y);
  76.         }
  77.  
  78.         Point operator-() const // обратный вектор
  79.         {
  80.                 return Point(-x, -y);
  81.         }
  82.  
  83.         Point operator*(double k) const // умножение вектора на скаляр
  84.         {
  85.                 return Point(x * k, y * k);
  86.         }
  87.  
  88.         Point operator/(double k) const // деление вектора на скаляр
  89.         {
  90.                 return Point(x / k, y / k);
  91.         }
  92.  
  93.         double operator%(const Point & p) const // скалярное произведение
  94.         {
  95.                 return x * p.x + y * p.y;
  96.         }
  97.  
  98.         double operator*(const Point & p) const // векторное произведение
  99.         {
  100.                 return x * p.y - y * p.x;
  101.         }
  102.  
  103.         double length() const // длина вектора по определению из ан.гема (корень из скалярного квадрата)
  104.         {
  105.                 return mySqrt(*this % *this);
  106.         }
  107.  
  108.         double distTo(const Point & p) const //расстояние между 2мя точками - модуль вектора между ними
  109.         {
  110.                 return (*this - p).length();
  111.         }
  112.  
  113.         double distTo(const Point & A, const Point & B) const // расстояние от точки до прямой (всегда >= 0)
  114.         {
  115.                 double d = A.distTo(B);
  116.                 if(doubleEqual(d, 0) ) // прямая должна задаваться двумя! точками
  117.                 {
  118.                         throw "A = B";
  119.                 }
  120.                 double s = (*this - A) * (*this - B); // удвоенная площадь треугольника
  121.                 return fabs(s) / d; // метод площадей
  122.         }
  123.  
  124.         Point normalize(double k = 1) const // выставляет длину вектора в k
  125.         {
  126.                 double len = length(); // Текущая длина
  127.                 if(doubleEqual(len, 0) ) // если длина ноль
  128.                 {
  129.                         if(doubleEqual(k, 0) )
  130.                         {
  131.                                 return Point();
  132.                         }
  133.                         throw "zero-size vector"; //кроме нулевой никакую получить не можем
  134.                 }
  135.                 return *this * (k / len);
  136.         }
  137.  
  138.         Point getH(const Point & A, const Point & B) const // опускаем высоту из точки на прямую (A, B)
  139.         {
  140.                 Point C = *this;
  141.                 Point v = B - A; // направляющий вектор прямой
  142.                 Point u = C - A; // вектор, проекция которого нам нужна
  143.                 double k = v % u / v.length(); // нашли длину проекции
  144.                 v = v.normalize(k); // нашли проекцию u на v
  145.                 Point H = A + v; // подвинули точку A в проекцию конца вектора u, отложенного из A
  146.                 return H;
  147.         }
  148.  
  149.         Point rotate() const // поворот на 90 градусов против часовой стрелки (положительное направление)
  150.         {
  151.                 return Point(-y, x);
  152.         }
  153.  
  154.         Point rotate(double alpha) const // поворот на угол alpha против часовой стрелки
  155.                                                                         // (по часовой стрелке, если alpha < 0)
  156.         {
  157.                 return rotate(cos(alpha), sin(alpha) ); // делегируем задачу другому экземпляру функции
  158.         }
  159.  
  160.         Point rotate(double cosa, double sina) const // поворот с заданными косинусом и синусом
  161.         {
  162.                 Point v = *this;
  163.                 Point u = v.rotate(); // вектор, перпендикулярный v, теперь (v, u) - базис, в котором мы знаем ответ
  164.                 Point w = v * cosa + u * sina; // зная координаты в базисе (v, u), нашли w
  165.                 return w;
  166.         }
  167.  
  168.         bool isZero() const // проверка на то, что точка нулевая без сложных операций и погрешности
  169.         {
  170.                 return doubleEqual(x, 0) && doubleEqual(y, 0);
  171.         }
  172.  
  173.         bool isOnLine(const Point & A, const Point & B) const // точка на прямой?
  174.         {
  175.                 return doubleEqual( (A - *this) * (B - *this), 0);
  176.         }
  177.  
  178.         bool isInSegment(const Point & A, const Point & B) const // точка внутри отрезка?
  179.         {
  180.                 return isOnLine(A, B) && doubleLessOrEqual( (A - *this) % (B - *this), 0 );
  181.         }
  182.  
  183.         bool isInSegmentStrictly(const Point & A, const Point & B) const // точка внутри отрезка строго?
  184.         {
  185.                 return isOnLine(A, B) && doubleLess( (A - *this) % (B - *this), 0 );
  186.         }
  187.  
  188.         double getAngle() const
  189.         {
  190.                 return atan2(y, x); // угол между вектором и осью ОХ
  191.         }
  192.  
  193.         double getAngle(Point u) const
  194.         {
  195.                 Point v = *this;
  196.                 return atan2(v * u, v % u); // ориентированный угол между векторами
  197.         }
  198.  
  199. };
  200.  
  201.  
  202. int getIntersection // ищем пересечение прямой (A, B) и прямой (C, D)
  203.         (
  204.                 const Point & A,
  205.                 const Point & B,
  206.                 const Point & C,
  207.                 const Point & D,
  208.                 Point & O
  209.         )
  210. {
  211.         Point v = B - A; // направляющий вектор прямой (A, B)
  212.         double s1 = (C - A) * (D - A); // площадь треугольника A, C, D
  213.         double s2 = (D - B) * (C - B); // площадь треугольника B, D, C
  214.         double s = s1 + s2; // площадь четурёхугольника
  215.         if(doubleEqual(s, 0) ) // прямые параллельны (или совпадают)
  216.         {
  217.                 if(!A.isOnLine(C, D) ) // прямые параллельны
  218.                 {
  219.                         return 0; // пересечение пусто
  220.                 }
  221.                 return 2; // больше 1ой точки в пересечении
  222.         }
  223.         v = v / s;
  224.         v = v * s1; // вектора AO и AB пропорциональны площадям s1 и s
  225.         O = A + v; // нашли точку пересечения
  226.         return 1; // 1 точка в пересечении
  227. }
  228.  
  229.  
  230. int getIntersection // ищем пересечение 2х окружностей с центрами в A, B, радиусами rA, rB
  231.         (
  232.                 const Point & A,
  233.                 double rA,
  234.                 const Point & B,
  235.                 double rB,
  236.                 Point & M,
  237.                 Point & N
  238.         )
  239. {
  240.         double d = A.distTo(B); // расстояние между точками
  241.         if(doubleLess(rA + rB, d) || doubleLess(d, fabs(rA - rB)) ) // если окружности не касаются, то точек 0
  242.         {
  243.                 return 0;
  244.         }
  245.         double a = (sqr(rA) - sqr(rB) + sqr(d) ) / 2 / d; // длину проекции отрезка (от A до пересечения окружностей) на прямую AB
  246.         double h = mySqrt(sqr(rA) - sqr(a)); //расстояние о точки пересечения окружностей до прямой AB
  247.         Point v = B - A; //направляющий вектор прямой AB
  248.         Point u = v.rotate(); // нормальный вектор к прямой AB
  249.         v = v.normalize(a);
  250.         u = u.normalize(h); //нормируем до нужных длин оба вектора
  251.         Point H = A + v;
  252.         M = H + u;
  253.         N = H - u;
  254.         if(u.isZero() ) return 1; //если u = 0, то окружности касаются
  255.         return 2;
  256. }
  257.  
  258.  
  259.  
  260. int getIntersection // ищем пересечение прямой (A, B) и окружности (O, r)
  261.         (
  262.                 const Point & A,
  263.                 const Point & B,
  264.                 const Point & O,
  265.                 double r,
  266.                 Point & M,
  267.                 Point & N
  268.         )
  269. {
  270.         double h = O.distTo(A, B);
  271.         if(doubleLess(r, h) ) //если окружность далеко от прямой
  272.         {
  273.                 return 0;
  274.         }
  275.         Point H = O.getH(A, B); //уронили высоту на AB
  276.         Point v = B - A; //взяли направляющий вектор прямой
  277.         double k = mySqrt(sqr(r) - sqr(h) ); // величина, на которую надо подвинуть H до точки пересечения окружности и прямой
  278.         v = v.normalize(k);
  279.         M = H + v;
  280.         N = H - v;
  281.         if(v.isZero() ) return 1;
  282.         return 2;
  283. }
  284.  
  285.  
  286. int getTangent  // получаем касательные из точки A к оркужности (O, r)
  287.         (
  288.                 const Point & A,
  289.                 const Point & O,
  290.                 double r,
  291.                 Point & M,
  292.                 Point & N
  293.         )
  294. {
  295.         Point v = O - A;
  296.         double d = v.length();
  297.         if(doubleLess(d, r) ) return 0; // точка внутри, не существует касательной
  298.         double alpha = asin(r / d); // нашли угол между касательной и вектором v
  299.         double L = mySqrt(sqr(d) - sqr(r)); // длина касательной
  300.         v = v.normalize(L); // теперь v имеет длину касательной
  301.         M = A + v.rotate(alpha);  
  302.         N = A - v.rotate(alpha); // повернули вектор v в обе стороны и нашли концы обоих касательных
  303.         if(doubleEqual(r, d) ) return 1;
  304.         return 2;
  305. }
  306.  
  307.  
  308. void getOutTangent  // получаем внешние касательные к паре оркужностей
  309.         (
  310.                 Point A,
  311.                 double rA,
  312.                 Point B,
  313.                 double rB,
  314.                 pair<Point, Point> & P,
  315.                 pair<Point, Point> & Q
  316.         )
  317. {
  318.         if(rA > rB)
  319.         {
  320.                 swap(rA, rB);
  321.                 swap(A, B);
  322.         }
  323.         Point u = (A - B).rotate(asin(r / d)).rotate().normalize(rA);
  324.         P.first = A + u;
  325.         Q.first = A - u;
  326.         Point T1, T2;
  327.         getTangent(A, B, rB - rA, T1, T2); //сжали окружности на rA , получили ответ для неё, потом раздвинули прямые на rA в стороны
  328.         P.second = T1 + u;
  329.         Q.second = T2 - u;
  330. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement