三次元空間上の円と球
(geometry-3d/sphere.hpp)
- View this file on GitHub
- Last update: 2026-07-25 02:01:37+09:00
- Include:
#include "geometry-3d/sphere.hpp"
三次元空間上の円と球に関する包含判定,交差,面積,体積を扱う.
空間円を中心 p,法線 n,半径 r を用いた Circle3D で表し,球を中心 p と半径 r を用いた Sphere3D で表す.
-
area(c):空間円cの面積を返す. -
circumference(c):空間円cの円周を返す. -
surface_area(s):球sの表面積を返す. -
volume(s):球sの体積を返す. -
contains_sphere(s, p):球sと点pの位置関係を返す.外部,球面上,内部ならそれぞれ $0,1,2$. -
intersect(s, t):二球の位置関係を返す.内包,内接,交差円をもつ,外接,離れている場合にそれぞれ $0,1,2,3,4$. -
cross_point_ls(l, s):直線lと球sの交点対を返す.接する場合は同じ点を二つ返し,交わらない場合はnullopt. -
cross_circle_ps(pl, s):平面plと球sの交差円を返す.接する場合は半径 $0$ の円,交わらない場合はnullopt. -
cross_circle_ss(s, t):二球の交差円を返す.接する場合は半径 $0$ の円,交差円が存在しない場合はnullopt.
Depends on
三次元幾何の基本要素
(geometry-3d/geometry-base.hpp)
三次元空間上の直線
(geometry-3d/line.hpp)
三次元空間上の平面
(geometry-3d/plane.hpp)
二次元幾何の基本要素
(geometry/geometry-base.hpp)
Verified with
Code
#pragma once
#include "geometry-3d/plane.hpp"
struct Circle3D {
Point3D p, n;
Real r;
Circle3D() = default;
Circle3D(const Point3D& _p, const Point3D& _n, Real _r)
: p(_p), n(_n), r(_r) {
assert(abs(n) > EPS && r >= 0);
}
};
struct Sphere3D {
Point3D p;
Real r;
Sphere3D() = default;
Sphere3D(const Point3D& _p, Real _r) : p(_p), r(_r) { assert(r >= 0); }
};
using Circles3D = vector<Circle3D>;
using Spheres3D = vector<Sphere3D>;
Real area(const Circle3D& c) { return PI * c.r * c.r; }
Real circumference(const Circle3D& c) { return 2 * PI * c.r; }
Real surface_area(const Sphere3D& s) { return 4 * PI * s.r * s.r; }
Real volume(const Sphere3D& s) { return 4 * PI * s.r * s.r * s.r / 3; }
int contains_sphere(const Sphere3D& s, const Point3D& p) {
int z = sign(abs(p - s.p) - s.r);
return z > 0 ? 0 : z == 0 ? 1 : 2;
}
// 0: contained, 1: internally tangent, 2: intersecting,
// 3: externally tangent, 4: separate.
int intersect(Sphere3D s, Sphere3D t) {
if (s.r < t.r) swap(s, t);
Real d = abs(s.p - t.p);
if (s.r + t.r < d - EPS) return 4;
if (equals(s.r + t.r, d)) return 3;
if (s.r - t.r < d - EPS) return 2;
if (equals(s.r - t.r, d)) return 1;
return 0;
}
optional<pair<Point3D, Point3D>> cross_point_ls(const Line3D& l,
const Sphere3D& s) {
Point3D q = projection(l, s.p);
Real h2 = s.r * s.r - norm(q - s.p);
if (h2 < -EPS) return nullopt;
h2 = max<Real>(0, h2);
Point3D d = normalize(l.b - l.a) * sqrt(h2);
return pair<Point3D, Point3D>{q - d, q + d};
}
optional<Circle3D> cross_circle_ps(const Plane3D& pl, const Sphere3D& s) {
Point3D q = projection(pl, s.p);
Real r2 = s.r * s.r - norm(q - s.p);
if (r2 < -EPS) return nullopt;
return Circle3D{q, normalize(pl.n), sqrt(max<Real>(0, r2))};
}
optional<Circle3D> cross_circle_ss(const Sphere3D& s, const Sphere3D& t) {
int relation = intersect(s, t);
if (relation == 0 || relation == 4) return nullopt;
Point3D d = t.p - s.p;
Real len = abs(d);
if (len < EPS) return nullopt;
Real x = (s.r * s.r - t.r * t.r + len * len) / (2 * len);
Real r2 = s.r * s.r - x * x;
Point3D n = d / len;
return Circle3D{s.p + n * x, n, sqrt(max<Real>(0, r2))};
}
/**
* @brief 三次元空間上の円と球
* @docs docs/geometry-3d/sphere.md
*/#line 2 "geometry-3d/sphere.hpp"
#line 2 "geometry-3d/plane.hpp"
#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
*/
#line 4 "geometry-3d/plane.hpp"
struct Plane3D {
Point3D p, n;
Plane3D() = default;
Plane3D(const Point3D& _p, const Point3D& _n) : p(_p), n(_n) {
assert(abs(n) > EPS);
}
Plane3D(const Point3D& a, const Point3D& b, const Point3D& c)
: p(a), n(cross(b - a, c - a)) {
assert(abs(n) > EPS);
}
};
using Planes3D = vector<Plane3D>;
Real plane_value(const Plane3D& pl, const Point3D& p) {
return dot(pl.n, p - pl.p);
}
bool is_intersect_pp(const Plane3D& pl, const Point3D& p) {
return abs(plane_value(pl, p)) < EPS * abs(pl.n);
}
bool is_parallel(const Plane3D& a, const Plane3D& b) {
return abs(cross(a.n, b.n)) < EPS * abs(a.n) * abs(b.n);
}
bool is_orthogonal(const Plane3D& a, const Plane3D& b) {
return abs(dot(a.n, b.n)) < EPS * abs(a.n) * abs(b.n);
}
Point3D projection(const Plane3D& pl, const Point3D& p) {
assert(norm(pl.n) > EPS * EPS);
return p - pl.n * (plane_value(pl, p) / norm(pl.n));
}
Point3D reflection(const Plane3D& pl, const Point3D& p) {
return projection(pl, p) * 2 - p;
}
Real signed_distance_pp(const Plane3D& pl, const Point3D& p) {
return plane_value(pl, p) / abs(pl.n);
}
Real distance_pp(const Plane3D& pl, const Point3D& p) {
return abs(signed_distance_pp(pl, p));
}
bool is_intersect_lp(const Line3D& l, const Plane3D& pl) {
Point3D d = l.b - l.a;
bool parallel = abs(dot(pl.n, d)) < EPS * abs(pl.n) * abs(d);
return !parallel || is_intersect_pp(pl, l.a);
}
optional<Point3D> cross_point_lp(const Line3D& l, const Plane3D& pl) {
Point3D v = l.b - l.a;
Real d = dot(pl.n, v);
if (abs(d) < EPS * abs(pl.n) * abs(v)) return nullopt;
Real t = dot(pl.n, pl.p - l.a) / d;
return l.a + v * t;
}
Real distance_lp(const Line3D& l, const Plane3D& pl) {
return is_intersect_lp(l, pl) ? 0 : distance_pp(pl, l.a);
}
optional<Line3D> cross_line_pp(const Plane3D& a, const Plane3D& b) {
Point3D d = cross(a.n, b.n);
Real d2 = norm(d);
if (d2 < EPS * EPS * norm(a.n) * norm(b.n)) return nullopt;
Real da = dot(a.n, a.p), db = dot(b.n, b.p);
Point3D p = cross(da * b.n - db * a.n, d) / d2;
return Line3D{p, p + d};
}
Real distance_pp(const Plane3D& a, const Plane3D& b) {
return is_parallel(a, b) ? distance_pp(a, b.p) : 0;
}
/**
* @brief 三次元空間上の平面
* @docs docs/geometry-3d/plane.md
*/
#line 4 "geometry-3d/sphere.hpp"
struct Circle3D {
Point3D p, n;
Real r;
Circle3D() = default;
Circle3D(const Point3D& _p, const Point3D& _n, Real _r)
: p(_p), n(_n), r(_r) {
assert(abs(n) > EPS && r >= 0);
}
};
struct Sphere3D {
Point3D p;
Real r;
Sphere3D() = default;
Sphere3D(const Point3D& _p, Real _r) : p(_p), r(_r) { assert(r >= 0); }
};
using Circles3D = vector<Circle3D>;
using Spheres3D = vector<Sphere3D>;
Real area(const Circle3D& c) { return PI * c.r * c.r; }
Real circumference(const Circle3D& c) { return 2 * PI * c.r; }
Real surface_area(const Sphere3D& s) { return 4 * PI * s.r * s.r; }
Real volume(const Sphere3D& s) { return 4 * PI * s.r * s.r * s.r / 3; }
int contains_sphere(const Sphere3D& s, const Point3D& p) {
int z = sign(abs(p - s.p) - s.r);
return z > 0 ? 0 : z == 0 ? 1 : 2;
}
// 0: contained, 1: internally tangent, 2: intersecting,
// 3: externally tangent, 4: separate.
int intersect(Sphere3D s, Sphere3D t) {
if (s.r < t.r) swap(s, t);
Real d = abs(s.p - t.p);
if (s.r + t.r < d - EPS) return 4;
if (equals(s.r + t.r, d)) return 3;
if (s.r - t.r < d - EPS) return 2;
if (equals(s.r - t.r, d)) return 1;
return 0;
}
optional<pair<Point3D, Point3D>> cross_point_ls(const Line3D& l,
const Sphere3D& s) {
Point3D q = projection(l, s.p);
Real h2 = s.r * s.r - norm(q - s.p);
if (h2 < -EPS) return nullopt;
h2 = max<Real>(0, h2);
Point3D d = normalize(l.b - l.a) * sqrt(h2);
return pair<Point3D, Point3D>{q - d, q + d};
}
optional<Circle3D> cross_circle_ps(const Plane3D& pl, const Sphere3D& s) {
Point3D q = projection(pl, s.p);
Real r2 = s.r * s.r - norm(q - s.p);
if (r2 < -EPS) return nullopt;
return Circle3D{q, normalize(pl.n), sqrt(max<Real>(0, r2))};
}
optional<Circle3D> cross_circle_ss(const Sphere3D& s, const Sphere3D& t) {
int relation = intersect(s, t);
if (relation == 0 || relation == 4) return nullopt;
Point3D d = t.p - s.p;
Real len = abs(d);
if (len < EPS) return nullopt;
Real x = (s.r * s.r - t.r * t.r + len * len) / (2 * len);
Real r2 = s.r * s.r - x * x;
Point3D n = d / len;
return Circle3D{s.p + n * x, n, sqrt(max<Real>(0, r2))};
}
/**
* @brief 三次元空間上の円と球
* @docs docs/geometry-3d/sphere.md
*/