/
shvidkuli
/
kkrjava
Обзор
Документация
Войти
/
shvidkuli
/
kkrjava
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/main/java/com/montecarlo/geometry/Arc.java
168 строк
6 KB
kroc400
first_commit
30 апр 2026, 22:44
30 апр 2026, 22:44
2347fdb
Код
Авторство
О чём код?
package com.montecarlo.geometry; /** * A filled semicircle (half-disk) defined by its center, radius, and an inward normal direction. * * <p>The region is: * <pre> * { p ∈ ℝ² | |p − center| ≤ radius AND (p − center) · normal ≥ 0 } * </pre> * where {@code normal} is the unit vector pointing into the "open" (filled) half-plane.</p> * * <p>The diameter chord lies along the hyperplane perpendicular to {@code normal} through * {@code center}. To construct an arc from two diameter endpoints {@code p1} and {@code p2}, * compute: * <pre> * center = midpoint(p1, p2) * radius = |p1 − p2| / 2 * refPoint = any point known to be on the filled side * Arc arc = new Arc(center, radius, refPoint); * </pre> * </p> * * <h3>Default figure geometry</h3> * <pre> * Rectangle ABCD = [0,4] × [0,4] * F = (2, 0) — midpoint of AB (diameter endpoint) * E = (4, 2) — midpoint of BC (diameter endpoint) * Semicircle: center = (3,1), radius = √2, D-side (towards D=(0,4)) * Area = π·r²/2 = π * </pre> */ public class Arc implements Shape { private final Point center; private final double radius; /** x-component of the unit inward normal (pointing into the filled half-plane). */ private final double nx; /** y-component of the unit inward normal. */ private final double ny; /** * Constructs a semicircle from center, radius, and a reference point that * lies strictly on the filled (inside) side of the diameter chord. * * <p>The inward normal direction is derived automatically as the unit vector * from {@code center} towards {@code refPoint}.</p> * * @param center midpoint of the diameter chord (center of the full circle) * @param radius radius of the circle (half the length of the diameter) * @param refPoint any point on the filled side; used only to determine orientation * @throws IllegalArgumentException if {@code refPoint} coincides with {@code center} */ public Arc(Point center, double radius, Point refPoint) { this.center = center; this.radius = radius; double dx = refPoint.getX() - center.getX(); double dy = refPoint.getY() - center.getY(); double len = Math.sqrt(dx * dx + dy * dy); if (len < 1e-12) { throw new IllegalArgumentException( "Reference point must not coincide with the center of the semicircle."); } this.nx = dx / len; this.ny = dy / len; } /** @return center of the full circle */ public Point getCenter() { return center; } /** @return radius of the circle */ public double getRadius() { return radius; } /** * Returns {@code true} if point {@code p} is inside or on the boundary of this semicircle. * * <p>A point belongs to the semicircle iff: * <ol> * <li>Its distance to {@code center} does not exceed {@code radius}.</li> * <li>The dot product of (p − center) and the inward normal is non-negative * (i.e., the point is on the correct side of the diameter chord).</li> * </ol> * </p> * * @param p the point to test * @return {@code true} if inside or on the boundary */ @Override public boolean contains(Point p) { double dx = p.getX() - center.getX(); double dy = p.getY() - center.getY(); // Condition 1: within the circle if (dx * dx + dy * dy > radius * radius + 1e-12) { return false; } // Condition 2: on the inward (filled) half-plane return (dx * nx + dy * ny) >= -1e-12; } /** * Returns the exact area of this semicircle: π·r²/2. * * @return π·r²/2 */ @Override public double analyticalArea() { return Math.PI * radius * radius / 2.0; } /** * Returns the tight axis-aligned bounding box of this semicircle. * * <p>The box is computed by: * <ul> * <li>Always including the two diameter endpoints (circle boundary on the chord).</li> * <li>Including each of the four cardinal extremes of the full circle only if * they lie on the filled side.</li> * </ul> * </p> * * @return tight bounding box */ @Override public BoundingBox getBoundingBox() { // Unit vector along the diameter chord = perpendicular to the normal // Rotating (nx, ny) by 90° gives (ny, -nx) double perpX = ny; double perpY = -nx; // Diameter endpoints: center ± radius * perp double ep1x = center.getX() + radius * perpX; double ep1y = center.getY() + radius * perpY; double ep2x = center.getX() - radius * perpX; double ep2y = center.getY() - radius * perpY; double minX = Math.min(ep1x, ep2x); double minY = Math.min(ep1y, ep2y); double maxX = Math.max(ep1x, ep2x); double maxY = Math.max(ep1y, ep2y); // Four cardinal extremes of the full circle double[][] cardinals = { {center.getX() - radius, center.getY()}, // left {center.getX() + radius, center.getY()}, // right {center.getX(), center.getY() - radius}, // bottom {center.getX(), center.getY() + radius} // top }; for (double[] c : cardinals) { double dx = c[0] - center.getX(); double dy = c[1] - center.getY(); // Include only if on the filled half-plane (dot product >= 0) if (dx * nx + dy * ny >= -1e-12) { minX = Math.min(minX, c[0]); minY = Math.min(minY, c[1]); maxX = Math.max(maxX, c[0]); maxY = Math.max(maxY, c[1]); } } return new BoundingBox(minX, minY, maxX, maxY); } @Override public String toString() { return String.format("Arc(center=%s, radius=%.4f, normal=(%.4f, %.4f))", center, radius, nx, ny); } }