Data update
This commit is contained in:
parent
29a5eea0d4
commit
5c1bb7bfa9
2011 changed files with 35081 additions and 3229 deletions
|
|
@ -0,0 +1,261 @@
|
|||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// A point of the current Catmull-Clark surface.
|
||||
class Point {
|
||||
public:
|
||||
Point() : x(0.0), y(0.0), z(0.0) { }
|
||||
Point(const double& aX, const double& aY, const double& aZ) : x(aX), y(aY), z(aZ) { }
|
||||
|
||||
Point add(const Point& other) const {
|
||||
return Point(x + other.x, y + other.y, z + other.z);
|
||||
}
|
||||
|
||||
Point multiply(const double& factor) const {
|
||||
return Point(x * factor, y * factor, z * factor);
|
||||
}
|
||||
|
||||
Point divide(const double& factor) const {
|
||||
return multiply(1.0 / factor);
|
||||
}
|
||||
|
||||
bool operator<(const Point& other) const {
|
||||
return ( x < other.x ) || ( ( x == other.x && y < other.y ) )
|
||||
|| ( x == other.x && y == other.y && z < other.z );
|
||||
}
|
||||
|
||||
bool operator==(const Point& other) const {
|
||||
return x == other.x && y == other.y && z == other.z;
|
||||
}
|
||||
|
||||
Point& operator=(const Point& other) {
|
||||
if ( *this != other ) {
|
||||
x = other.x; y = other.y; z = other.z;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string to_string() const {
|
||||
return "(" + format(x) + ", " + format(y) + ", " + format(z) + ")";
|
||||
}
|
||||
|
||||
private:
|
||||
std::string format(const double& value) const {
|
||||
std::stringstream stream;
|
||||
stream << std::fixed << std::setprecision(3) << value;
|
||||
return ( value >= 0 ) ? " " + stream.str() : stream.str();
|
||||
}
|
||||
|
||||
double x, y, z;
|
||||
};
|
||||
|
||||
// Return the centroid point of the given vector of points.
|
||||
Point centroid(const std::vector<Point>& points) {
|
||||
Point sum;
|
||||
for ( const Point& point : points ) {
|
||||
sum = sum.add(point);
|
||||
}
|
||||
return sum.divide(points.size());
|
||||
}
|
||||
|
||||
// An edge of the current Catmull-Clark surface.
|
||||
class Edge {
|
||||
public:
|
||||
Edge(Point aBegin, Point aEnd) {
|
||||
if ( aEnd < aBegin ) {
|
||||
std::swap(aBegin, aEnd);
|
||||
}
|
||||
begin = aBegin; end = aEnd;
|
||||
|
||||
mid_edge = centroid({ begin, end });
|
||||
hole_edge = false;
|
||||
}
|
||||
|
||||
bool contains(const Point& point) const {
|
||||
return point == begin || point == end;
|
||||
}
|
||||
|
||||
bool operator<(const Edge other) const {
|
||||
return ( begin < other.begin ) || ( begin == other.begin && end < other.end );
|
||||
}
|
||||
|
||||
bool operator==(const Edge& other) const {
|
||||
return contains(other.begin) && contains(other.end);
|
||||
}
|
||||
|
||||
bool hole_edge;
|
||||
Point begin, end, mid_edge, edge_point;
|
||||
};
|
||||
|
||||
// A face of the current Catmull-Clark surface.
|
||||
class Face {
|
||||
public:
|
||||
Face(std::vector<Point> aVertices) {
|
||||
vertices = aVertices;
|
||||
face_point = centroid(vertices);
|
||||
|
||||
for ( uint64_t i = 0; i < vertices.size() - 1; ++i ) {
|
||||
edges.emplace_back(Edge(vertices[i], vertices[i + 1]));
|
||||
}
|
||||
edges.emplace_back(Edge(vertices.back(), vertices.front()));
|
||||
}
|
||||
|
||||
bool contains(const Point& vertex) const {
|
||||
return std::find(vertices.begin(), vertices.end(), vertex) != vertices.end();
|
||||
}
|
||||
|
||||
bool contains(Edge edge) const {
|
||||
return contains(edge.begin) && contains(edge.end);
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
std::string result = "Face: ";
|
||||
for ( uint64_t i = 0; i < vertices.size() - 1; ++i ) {
|
||||
result += vertices[i].to_string() + ", ";
|
||||
}
|
||||
return result + vertices.back().to_string();
|
||||
}
|
||||
|
||||
std::vector<Point> vertices;
|
||||
std::vector<Edge> edges;
|
||||
Point face_point;
|
||||
};
|
||||
|
||||
// Return a map containing, for each vertex,
|
||||
// the new vertex created by the current iteration of the Catmull-Clark surface subdivision algorithm.
|
||||
std::map<Point, Point> next_vertices(const std::vector<Edge>& edges, const std::vector<Face>& faces) {
|
||||
std::map<Point, Point> next_vertices = { };
|
||||
std::set<Point> vertices = { };
|
||||
for ( const Face& face : faces ) {
|
||||
for ( const Point& vertex : face.vertices ) {
|
||||
vertices.emplace(vertex);
|
||||
}
|
||||
}
|
||||
|
||||
for ( const Point& vertex : vertices ) {
|
||||
std::vector<Face> faces_for_vertex = { };
|
||||
for ( const Face& face : faces ) {
|
||||
if ( face.contains(vertex) ) {
|
||||
faces_for_vertex.emplace_back(face);
|
||||
}
|
||||
}
|
||||
|
||||
std::set<Edge> edges_for_vertex = { };
|
||||
for ( const Edge& edge : edges ) {
|
||||
if ( edge.contains(vertex) ) {
|
||||
edges_for_vertex.emplace(edge);
|
||||
}
|
||||
}
|
||||
|
||||
if ( faces_for_vertex.size() != edges_for_vertex.size() ) {
|
||||
std::vector<Point> mid_edge_of_hole_edges = { };
|
||||
for ( const Edge& edge : edges_for_vertex ) {
|
||||
if ( edge.hole_edge ) {
|
||||
mid_edge_of_hole_edges.emplace_back(edge.mid_edge);
|
||||
}
|
||||
}
|
||||
mid_edge_of_hole_edges.emplace_back(vertex);
|
||||
next_vertices[vertex] = centroid(mid_edge_of_hole_edges);
|
||||
} else {
|
||||
const uint64_t face_count = faces_for_vertex.size();
|
||||
const double multiple_1 = static_cast<double>(( face_count - 3 ) / face_count);
|
||||
const double multiple_2 = 1.0 / face_count;
|
||||
const double multiple_3 = 2.0 / face_count;
|
||||
|
||||
Point next_vertex_1 = vertex.multiply(multiple_1);
|
||||
std::vector<Point> face_points = { };
|
||||
for ( const Face& face : faces_for_vertex ) {
|
||||
face_points.emplace_back(face.face_point);
|
||||
}
|
||||
Point next_vertex_2 = centroid(face_points).multiply(multiple_2);
|
||||
std::vector<Point> mid_edges = { };
|
||||
for ( const Edge& edge : edges_for_vertex ) {
|
||||
mid_edges.emplace_back(edge.mid_edge);
|
||||
}
|
||||
Point next_vertex_3 = centroid(mid_edges).multiply(multiple_3);
|
||||
Point next_vertex_4 = next_vertex_1.add(next_vertex_2);
|
||||
|
||||
next_vertices[vertex] = next_vertex_4.add(next_vertex_3);
|
||||
}
|
||||
}
|
||||
return next_vertices;
|
||||
}
|
||||
|
||||
// The Catmull-Clarke surface subdivision algorithm.
|
||||
std::vector<Face> catmull_clark_surface_subdivision(std::vector<Face>& faces) {
|
||||
// Determine, for each edge, whether or not it is an edge of a hole, and set its edge point accordingly.
|
||||
for ( Face& face : faces ) {
|
||||
for ( Edge& edge : face.edges ) {
|
||||
std::vector<Point> face_points_for_edge = { };
|
||||
for ( const Face& search_face : faces ) {
|
||||
if ( search_face.contains(edge) ) {
|
||||
face_points_for_edge.emplace_back(search_face.face_point);
|
||||
}
|
||||
}
|
||||
|
||||
if ( face_points_for_edge.size() == 2 ) {
|
||||
edge.hole_edge = false;
|
||||
edge.edge_point = centroid({ edge.mid_edge, centroid(face_points_for_edge) });
|
||||
} else {
|
||||
edge.hole_edge = true;
|
||||
edge.edge_point = edge.mid_edge;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Edge> edges = { };
|
||||
for ( const Face& face : faces ) {
|
||||
for ( const Edge& edge : face.edges ) {
|
||||
edges.emplace_back(edge);
|
||||
}
|
||||
}
|
||||
std::map<Point, Point> next_vertex_map = next_vertices(edges, faces);
|
||||
|
||||
std::vector<Face> next_faces = { };
|
||||
for ( Face face : faces ) { // The face may contain any number of points
|
||||
if ( face.vertices.size() >= 3 ) { // A face with 2 or fewer points does not contribute to the surface
|
||||
Point face_point = face.face_point;
|
||||
for ( uint64_t i = 0; i < face.vertices.size(); ++i ) {
|
||||
next_faces.emplace_back(Face({
|
||||
next_vertex_map[face.vertices[i]],
|
||||
face.edges[i].edge_point,
|
||||
face_point,
|
||||
face.edges[( i - 1 + face.vertices.size() ) % face.vertices.size()].edge_point}));
|
||||
}
|
||||
}
|
||||
}
|
||||
return next_faces;
|
||||
}
|
||||
|
||||
// Display the current Catmull-Clark surface on the console.
|
||||
void displaySurface(const std::vector<Face> faces) {
|
||||
std::cout << "Surface {" << std::endl;
|
||||
for ( const Face& face : faces ) {
|
||||
std::cout << face.toString() << std::endl;
|
||||
}
|
||||
std::cout << "}" << std::endl << std::endl;
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::vector<Face> faces = {
|
||||
Face({ Point(-1.0, 1.0, 1.0), Point(-1.0, -1.0, 1.0), Point( 1.0, -1.0, 1.0), Point( 1.0, 1.0, 1.0) }),
|
||||
Face({ Point( 1.0, 1.0, 1.0), Point( 1.0, -1.0, 1.0), Point( 1.0, -1.0, -1.0), Point( 1.0, 1.0, -1.0) }),
|
||||
Face({ Point( 1.0, 1.0, -1.0), Point( 1.0, -1.0, -1.0), Point(-1.0, -1.0, -1.0), Point(-1.0, 1.0, -1.0) }),
|
||||
Face({ Point(-1.0, 1.0, -1.0), Point(-1.0, 1.0, 1.0), Point( 1.0, 1.0, 1.0), Point( 1.0, 1.0, -1.0) }),
|
||||
Face({ Point(-1.0, 1.0, -1.0), Point(-1.0, -1.0, -1.0), Point(-1.0, -1.0, 1.0), Point(-1.0, 1.0, 1.0) }),
|
||||
Face({ Point(-1.0, -1.0, -1.0), Point(-1.0, -1.0, 1.0), Point( 1.0, -1.0, 1.0), Point( 1.0, -1.0, -1.0) })};
|
||||
|
||||
displaySurface(faces);
|
||||
const uint32_t iterations = 1;
|
||||
for ( uint32_t i = 0; i < iterations; ++i ) {
|
||||
faces = catmull_clark_surface_subdivision(faces);
|
||||
}
|
||||
displaySurface(faces);
|
||||
}
|
||||
|
|
@ -0,0 +1,257 @@
|
|||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class CatmullClarkSurfaceSubdivision {
|
||||
|
||||
public static void main(String[] args) {
|
||||
List<Face> faces = List.of( new Face(List.of( new Point(-1.0, 1.0, 1.0),
|
||||
new Point(-1.0, -1.0, 1.0),
|
||||
new Point( 1.0, -1.0, 1.0),
|
||||
new Point( 1.0, 1.0, 1.0) )),
|
||||
|
||||
new Face(List.of( new Point( 1.0, 1.0, 1.0),
|
||||
new Point( 1.0, -1.0, 1.0),
|
||||
new Point( 1.0, -1.0, -1.0),
|
||||
new Point( 1.0, 1.0, -1.0) )),
|
||||
|
||||
new Face(List.of( new Point( 1.0, 1.0, -1.0),
|
||||
new Point( 1.0, -1.0, -1.0),
|
||||
new Point(-1.0, -1.0, -1.0),
|
||||
new Point(-1.0, 1.0, -1.0) )),
|
||||
|
||||
new Face(List.of( new Point(-1.0, 1.0, -1.0),
|
||||
new Point(-1.0, 1.0, 1.0),
|
||||
new Point( 1.0, 1.0, 1.0),
|
||||
new Point( 1.0, 1.0, -1.0) )),
|
||||
|
||||
new Face(List.of( new Point(-1.0, 1.0, -1.0),
|
||||
new Point(-1.0, -1.0, -1.0),
|
||||
new Point(-1.0, -1.0, 1.0),
|
||||
new Point(-1.0, 1.0, 1.0) )),
|
||||
|
||||
new Face(List.of( new Point(-1.0, -1.0, -1.0),
|
||||
new Point(-1.0, -1.0, 1.0),
|
||||
new Point( 1.0, -1.0, 1.0),
|
||||
new Point( 1.0, -1.0, -1.0) )) );
|
||||
|
||||
displaySurface(faces);
|
||||
final int iterations = 1;
|
||||
for ( int i = 0; i < iterations; i++ ) {
|
||||
faces = catmullClarkSurfaceSubdivision(faces);
|
||||
}
|
||||
displaySurface(faces);
|
||||
}
|
||||
|
||||
// The Catmull-Clarke surface subdivision algorithm.
|
||||
private static List<Face> catmullClarkSurfaceSubdivision(List<Face> faces) {
|
||||
// Determine, for each edge, whether or not it is an edge of a hole, and set its edge point accordingly.
|
||||
List<Edge> edges = faces.stream().map( face -> face.edges.stream() ).flatMap(Function.identity()).toList();
|
||||
for ( Edge edge : edges ) {
|
||||
List<Point> facePointsForEdge =
|
||||
faces.stream().filter( face -> face.contains(edge) ).map( face -> face.facePoint ).toList();
|
||||
if ( facePointsForEdge.size() == 2 ) {
|
||||
edge.holeEdge = false;
|
||||
edge.edgePoint = centroid(List.of( edge.midEdge, centroid(facePointsForEdge) ));
|
||||
} else {
|
||||
edge.holeEdge = true;
|
||||
edge.edgePoint = edge.midEdge;
|
||||
}
|
||||
}
|
||||
|
||||
Map<Point, Point> nextVertices = nextVertices(edges, faces);
|
||||
|
||||
List<Face> nextFaces = new ArrayList<Face>();
|
||||
for ( Face face : faces ) { // The face may contain any number of points
|
||||
if ( face.vertices.size() >= 3 ) { // A face with 2 or fewer points does not contribute to the surface
|
||||
Point facePoint = face.facePoint;
|
||||
for ( int i = 0; i < face.vertices.size(); i++ ) {
|
||||
nextFaces.addLast( new Face(List.of(
|
||||
nextVertices.get(face.vertices.get(i)),
|
||||
face.edges.get(i).edgePoint,
|
||||
facePoint,
|
||||
face.edges.get(Math.floorMod(i - 1, face.vertices.size())).edgePoint )) );
|
||||
}
|
||||
}
|
||||
}
|
||||
return nextFaces;
|
||||
}
|
||||
|
||||
// Return a map containing, for each vertex,
|
||||
// the new vertex created by the current iteration of the Catmull-Clark surface subdivision algorithm.
|
||||
private static Map<Point, Point> nextVertices(List<Edge> edges, List<Face> faces) {
|
||||
Map<Point, Point> nextVertices = new HashMap<Point, Point>();
|
||||
List<Point> vertices =
|
||||
faces.stream().map( face -> face.vertices.stream() ).flatMap(Function.identity()).distinct().toList();
|
||||
|
||||
for ( Point vertex : vertices ) {
|
||||
List<Face> facesForVertex = faces.stream().filter( face -> face.contains(vertex) ).toList();
|
||||
List<Edge> edgesForVertex = edges.stream().filter( edge -> edge.contains(vertex) ).distinct().toList();
|
||||
|
||||
if ( facesForVertex.size() != edgesForVertex.size() ) {
|
||||
List<Point> midEdgeOfHoleEdges = edgesForVertex.stream().filter( edge -> edge.holeEdge )
|
||||
.map( edge -> edge.midEdge ).collect(Collectors.toList());
|
||||
midEdgeOfHoleEdges.add(vertex);
|
||||
nextVertices.put(vertex, centroid(midEdgeOfHoleEdges));
|
||||
} else {
|
||||
final int faceCount = facesForVertex.size();
|
||||
final double multipleOne = (double) ( faceCount - 3 ) / faceCount;
|
||||
final double multipleTwo = 1.0 / faceCount;
|
||||
final double multipleThree = 2.0 / faceCount;
|
||||
|
||||
Point nextVertexOne = vertex.multiply(multipleOne);
|
||||
List<Point> facePoints = facesForVertex.stream().map( face -> face.facePoint ).toList();
|
||||
Point nextVertexTwo = centroid(facePoints).multiply(multipleTwo);
|
||||
List<Point> midEdges = edgesForVertex.stream().map( edge -> edge.midEdge ).toList();
|
||||
Point nextVertexThree = centroid(midEdges).multiply(multipleThree);
|
||||
Point nextVertexFour = nextVertexOne.add(nextVertexTwo);
|
||||
|
||||
nextVertices.put(vertex, nextVertexFour.add(nextVertexThree));
|
||||
}
|
||||
}
|
||||
return nextVertices;
|
||||
}
|
||||
|
||||
// Return the centroid point of the given list of points.
|
||||
private static Point centroid(List<Point> points) {
|
||||
return points.stream().reduce(Point.ZERO, (left, right) -> left.add(right) ).divide(points.size());
|
||||
}
|
||||
|
||||
// Display the current Catmull-Clark surface on the console.
|
||||
private static void displaySurface(List<Face> faces) {
|
||||
System.out.println("Surface {");
|
||||
faces.stream().forEach(System.out::println);
|
||||
System.out.println("}" + System.lineSeparator());
|
||||
}
|
||||
|
||||
// A point of the current Catmull-Clark surface.
|
||||
private static final class Point implements Comparable<Point> {
|
||||
|
||||
public Point(double aX, double aY, double aZ) {
|
||||
x = aX; y = aY; z = aZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Point other) {
|
||||
if ( x == other.x ) {
|
||||
if ( y == other.y ) {
|
||||
return Double.compare(z, other.z);
|
||||
}
|
||||
return Double.compare(y, other.y);
|
||||
}
|
||||
return Double.compare(x, other.x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return switch ( other ) {
|
||||
case Point point -> x == point.x && y == point.y && z == point.z;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(x, y, z);
|
||||
}
|
||||
|
||||
public Point add(Point other) {
|
||||
return new Point(x + other.x, y + other.y, z + other.z);
|
||||
}
|
||||
|
||||
public Point multiply(double factor) {
|
||||
return new Point(x * factor, y * factor, z * factor);
|
||||
}
|
||||
|
||||
public Point divide(double factor) {
|
||||
return multiply(1.0 / factor);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "(" + format(x) + ", " + format(y) + ", " + format(z) + ")";
|
||||
}
|
||||
|
||||
public static Point ZERO = new Point(0.0, 0.0, 0.0);
|
||||
|
||||
private String format(double value) {
|
||||
return ( value >= 0 ) ? String.format(" %.3f", value) : String.format("%.3f", value);
|
||||
}
|
||||
|
||||
private final double x, y, z;
|
||||
|
||||
}
|
||||
|
||||
// An edge of the current Catmull-Clark surface.
|
||||
private static final class Edge {
|
||||
|
||||
public Edge(Point aBegin, Point aEnd) {
|
||||
if ( aBegin.compareTo(aEnd) <= 0 ) {
|
||||
begin = aBegin; end = aEnd;
|
||||
} else {
|
||||
begin = aEnd; end = aBegin;
|
||||
}
|
||||
|
||||
midEdge = centroid(List.of( begin, end ));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return switch ( other ) {
|
||||
case Edge edge -> begin.equals(edge.begin) && end.equals(edge.end);
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(begin, end);
|
||||
}
|
||||
|
||||
public boolean contains(Point point) {
|
||||
return point.equals(begin) || point.equals(end);
|
||||
}
|
||||
|
||||
public boolean holeEdge;
|
||||
public Point edgePoint;
|
||||
|
||||
public final Point begin, end, midEdge;
|
||||
|
||||
}
|
||||
|
||||
// A face of the current Catmull-Clark surface.
|
||||
private static final class Face {
|
||||
|
||||
public Face(List<Point> aVertices) {
|
||||
vertices = new ArrayList<Point>(aVertices);
|
||||
facePoint = centroid(vertices);
|
||||
|
||||
edges = new ArrayList<Edge>();
|
||||
for ( int i = 0; i < vertices.size() - 1; i++ ) {
|
||||
edges.addLast( new Edge(vertices.get(i), vertices.get(i + 1)) );
|
||||
}
|
||||
edges.addLast( new Edge(vertices.getLast(), vertices.getFirst()) );;
|
||||
}
|
||||
|
||||
public boolean contains(Point vertex) {
|
||||
return vertices.contains(vertex);
|
||||
}
|
||||
|
||||
public boolean contains(Edge edge) {
|
||||
return contains(edge.begin) && contains(edge.end);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Face: " + vertices.stream().map( point -> point.toString() ).collect(Collectors.joining("; "));
|
||||
}
|
||||
|
||||
public final List<Point> vertices;
|
||||
public final Point facePoint;
|
||||
public final List<Edge> edges;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue