Skip to the content.

:heavy_check_mark: 三次元空間上の直線
(geometry-3d/line.hpp)

三次元空間上の直線に関する判定,射影,距離計算を扱う.

直線を異なる二点 a, b を用いた Line3D で表す.

Depends on

Required by

Verified with

Code

#pragma once

#include "geometry-3d/geometry-base.hpp"

struct Line3D {
  Point3D a, b;

  Line3D() = default;
  Line3D(const Point3D& _a, const Point3D& _b) : a(_a), b(_b) {
    assert(abs(b - a) > EPS);
  }

  friend istream& operator>>(istream& is, Line3D& l) { return is >> l.a >> l.b; }
  friend ostream& operator<<(ostream& os, const Line3D& l) {
    return os << l.a << " to " << l.b;
  }
};

using Lines3D = vector<Line3D>;

bool is_intersect_lp(const Line3D& l, const Point3D& p) {
  Point3D d = l.b - l.a;
  assert(abs(d) > EPS);
  return abs(cross(d, p - l.a)) < EPS * abs(d);
}

bool is_parallel(const Line3D& l, const Line3D& m) {
  Point3D u = l.b - l.a, v = m.b - m.a;
  assert(abs(u) > EPS && abs(v) > EPS);
  return abs(cross(u, v)) < EPS * abs(u) * abs(v);
}

bool is_orthogonal(const Line3D& l, const Line3D& m) {
  Point3D u = l.b - l.a, v = m.b - m.a;
  assert(abs(u) > EPS && abs(v) > EPS);
  return abs(dot(u, v)) < EPS * abs(u) * abs(v);
}

Point3D projection(const Line3D& l, const Point3D& p) {
  Point3D d = l.b - l.a;
  assert(norm(d) > EPS * EPS);
  return l.a + d * (dot(p - l.a, d) / norm(d));
}

Point3D reflection(const Line3D& l, const Point3D& p) {
  return projection(l, p) * 2 - p;
}

Real distance_lp(const Line3D& l, const Point3D& p) {
  return abs(p - projection(l, p));
}

pair<Point3D, Point3D> closest_points_ll(const Line3D& l, const Line3D& m) {
  Point3D u = l.b - l.a, v = m.b - m.a, w = l.a - m.a;
  Real a = norm(u), b = dot(u, v), c = norm(v);
  assert(a > EPS * EPS && c > EPS * EPS);
  Real d = dot(u, w), e = dot(v, w);
  Real det = a * c - b * b;
  if (abs(det) < EPS * EPS * a * c) {
    Point3D q = projection(m, l.a);
    return {l.a, q};
  }
  Real s = (b * e - c * d) / det;
  Real t = (a * e - b * d) / det;
  return {l.a + u * s, m.a + v * t};
}

Real distance_ll(const Line3D& l, const Line3D& m) {
  auto [p, q] = closest_points_ll(l, m);
  return abs(p - q);
}

bool is_intersect_ll(const Line3D& l, const Line3D& m) {
  return distance_ll(l, m) < EPS;
}

optional<Point3D> cross_point_ll(const Line3D& l, const Line3D& m) {
  if (is_parallel(l, m)) return nullopt;
  auto [p, q] = closest_points_ll(l, m);
  if (abs(p - q) >= EPS) return nullopt;
  return (p + q) / 2;
}

/**
 * @brief 三次元空間上の直線
 * @docs docs/geometry-3d/line.md
 */
#line 2 "geometry-3d/line.hpp"

#line 2 "geometry-3d/geometry-base.hpp"

#line 2 "geometry/geometry-base.hpp"

#include <bits/stdc++.h>

using Real = long double;
constexpr Real EPS = 1e-10;
constexpr Real PI = 3.141592653589793238462643383279L;
bool equals(Real x, Real y) { return fabs(x - y) < EPS; }
int sign(Real a) { return equals(a, 0) ? 0 : (a > 0 ? 1 : -1); }

template <class R>
struct PointBase {
  using P = PointBase;
  R x, y;
  PointBase() : x(0), y(0) {}
  PointBase(R _x, R _y) : x(_x), y(_y) {}
  template <typename T, typename U>
  PointBase(const pair<T, U>& p) : x(p.first), y(p.second) {}

  P operator+(const P& r) const { return P{x + r.x, y + r.y}; }
  P operator-(const P& r) const { return P{x - r.x, y - r.y}; }
  P operator-() const { return P{-x, -y}; }
  P operator*(R r) const { return P{x * r, y * r}; }
  P operator/(R r) const { return P{x / r, y / r}; }

  P& operator+=(const P& r) { return (*this) = (*this) + r; }
  P& operator-=(const P& r) { return (*this) = (*this) - r; }
  P& operator*=(R r) { return (*this) = (*this) * r; }
  P& operator/=(R r) { return (*this) = (*this) / r; }

  bool operator<(const P& r) const { return x != r.x ? x < r.x : y < r.y; }
  bool operator==(const P& r) const { return x == r.x and y == r.y; }
  bool operator!=(const P& r) const { return !((*this) == r); }

  P rotate(R rad) const {
    return {x * cos(rad) - y * sin(rad), x * sin(rad) + y * cos(rad)};
  }
  P rotate90() const { return {-y, x}; }

  R real() const { return x; }
  R imag() const { return y; }

  friend P operator*(R r, const P& p) { return p * r; }
  friend R real(const P& p) { return p.x; }
  friend R imag(const P& p) { return p.y; }
  friend R dot(const P& l, const P& r) { return l.x * r.x + l.y * r.y; }
  friend R cross(const P& l, const P& r) { return l.x * r.y - l.y * r.x; }
  friend R abs(const P& p) { return sqrt(p.x * p.x + p.y * p.y); }
  friend R norm(const P& p) { return p.x * p.x + p.y * p.y; }
  friend R arg(const P& p) { return atan2(p.y, p.x); }

  friend istream& operator>>(istream& is, P& p) {
    R a, b;
    is >> a >> b;
    p = P{a, b};
    return is;
  }
  friend ostream& operator<<(ostream& os, const P& p) {
    return os << p.x << " " << p.y;
  }
};
using Point = PointBase<Real>;
using Points = vector<Point>;

// relative position of c from a->b
int ccw(const Point& a, const Point& b, const Point& c) {
  Point x = b - a, y = c - a;
  if (cross(x, y) > EPS) return +1;        // counter-clockwise
  if (cross(x, y) < -EPS) return -1;       // clockwise
  if (dot(x, y) < -EPS) return +2;         // collinear in the order c-a-b
  if (norm(x) + EPS < norm(y)) return -2;  // collinear in the order a-b-c
  return 0;                                // collinear in the order a-c-b
}

/**
 * @brief 二次元幾何の基本要素
 * @docs docs/geometry/geometry-base.md
 */
#line 4 "geometry-3d/geometry-base.hpp"

template <class R>
struct Point3DBase {
  using P = Point3DBase;
  R x, y, z;

  Point3DBase() : x(0), y(0), z(0) {}
  Point3DBase(R _x, R _y, R _z) : x(_x), y(_y), z(_z) {}

  P operator+(const P& r) const { return {x + r.x, y + r.y, z + r.z}; }
  P operator-(const P& r) const { return {x - r.x, y - r.y, z - r.z}; }
  P operator-() const { return {-x, -y, -z}; }
  P operator*(R r) const { return {x * r, y * r, z * r}; }
  P operator/(R r) const { return {x / r, y / r, z / r}; }

  P& operator+=(const P& r) { return (*this) = (*this) + r; }
  P& operator-=(const P& r) { return (*this) = (*this) - r; }
  P& operator*=(R r) { return (*this) = (*this) * r; }
  P& operator/=(R r) { return (*this) = (*this) / r; }

  bool operator<(const P& r) const {
    if (x != r.x) return x < r.x;
    return y != r.y ? y < r.y : z < r.z;
  }
  bool operator==(const P& r) const {
    return x == r.x && y == r.y && z == r.z;
  }
  bool operator!=(const P& r) const { return !((*this) == r); }

  friend P operator*(R r, const P& p) { return p * r; }
  friend R dot(const P& l, const P& r) {
    return l.x * r.x + l.y * r.y + l.z * r.z;
  }
  friend P cross(const P& l, const P& r) {
    return {l.y * r.z - l.z * r.y, l.z * r.x - l.x * r.z,
            l.x * r.y - l.y * r.x};
  }
  friend R norm(const P& p) { return dot(p, p); }
  friend R abs(const P& p) { return sqrt(norm(p)); }

  friend istream& operator>>(istream& is, P& p) {
    return is >> p.x >> p.y >> p.z;
  }
  friend ostream& operator<<(ostream& os, const P& p) {
    return os << p.x << " " << p.y << " " << p.z;
  }
};

using Point3D = Point3DBase<Real>;
using Points3D = vector<Point3D>;

Real triple(const Point3D& a, const Point3D& b, const Point3D& c) {
  return dot(a, cross(b, c));
}

Point3D normalize(const Point3D& p) {
  Real len = abs(p);
  assert(len > EPS);
  return p / len;
}

Real angle(const Point3D& a, const Point3D& b) {
  Real d = abs(a) * abs(b);
  assert(d > EPS);
  return acos(clamp(dot(a, b) / d, (Real)-1, (Real)1));
}

bool equals(const Point3D& a, const Point3D& b) { return abs(a - b) < EPS; }

/**
 * @brief 三次元幾何の基本要素
 * @docs docs/geometry-3d/geometry-base.md
 */
#line 4 "geometry-3d/line.hpp"

struct Line3D {
  Point3D a, b;

  Line3D() = default;
  Line3D(const Point3D& _a, const Point3D& _b) : a(_a), b(_b) {
    assert(abs(b - a) > EPS);
  }

  friend istream& operator>>(istream& is, Line3D& l) { return is >> l.a >> l.b; }
  friend ostream& operator<<(ostream& os, const Line3D& l) {
    return os << l.a << " to " << l.b;
  }
};

using Lines3D = vector<Line3D>;

bool is_intersect_lp(const Line3D& l, const Point3D& p) {
  Point3D d = l.b - l.a;
  assert(abs(d) > EPS);
  return abs(cross(d, p - l.a)) < EPS * abs(d);
}

bool is_parallel(const Line3D& l, const Line3D& m) {
  Point3D u = l.b - l.a, v = m.b - m.a;
  assert(abs(u) > EPS && abs(v) > EPS);
  return abs(cross(u, v)) < EPS * abs(u) * abs(v);
}

bool is_orthogonal(const Line3D& l, const Line3D& m) {
  Point3D u = l.b - l.a, v = m.b - m.a;
  assert(abs(u) > EPS && abs(v) > EPS);
  return abs(dot(u, v)) < EPS * abs(u) * abs(v);
}

Point3D projection(const Line3D& l, const Point3D& p) {
  Point3D d = l.b - l.a;
  assert(norm(d) > EPS * EPS);
  return l.a + d * (dot(p - l.a, d) / norm(d));
}

Point3D reflection(const Line3D& l, const Point3D& p) {
  return projection(l, p) * 2 - p;
}

Real distance_lp(const Line3D& l, const Point3D& p) {
  return abs(p - projection(l, p));
}

pair<Point3D, Point3D> closest_points_ll(const Line3D& l, const Line3D& m) {
  Point3D u = l.b - l.a, v = m.b - m.a, w = l.a - m.a;
  Real a = norm(u), b = dot(u, v), c = norm(v);
  assert(a > EPS * EPS && c > EPS * EPS);
  Real d = dot(u, w), e = dot(v, w);
  Real det = a * c - b * b;
  if (abs(det) < EPS * EPS * a * c) {
    Point3D q = projection(m, l.a);
    return {l.a, q};
  }
  Real s = (b * e - c * d) / det;
  Real t = (a * e - b * d) / det;
  return {l.a + u * s, m.a + v * t};
}

Real distance_ll(const Line3D& l, const Line3D& m) {
  auto [p, q] = closest_points_ll(l, m);
  return abs(p - q);
}

bool is_intersect_ll(const Line3D& l, const Line3D& m) {
  return distance_ll(l, m) < EPS;
}

optional<Point3D> cross_point_ll(const Line3D& l, const Line3D& m) {
  if (is_parallel(l, m)) return nullopt;
  auto [p, q] = closest_points_ll(l, m);
  if (abs(p - q) >= EPS) return nullopt;
  return (p + q) / 2;
}

/**
 * @brief 三次元空間上の直線
 * @docs docs/geometry-3d/line.md
 */
Back to top page