kopia lustrzana https://github.com/jameshball/osci-render
Solve chinese postman in WorldObject class
rodzic
ad36f99ea0
commit
c46afe24c9
|
@ -42,6 +42,9 @@ public class AudioClient {
|
||||||
AudioPlayer player = new AudioPlayer(SAMPLE_RATE, frames, ROTATE_SPEED, TRANSLATION_SPEED,
|
AudioPlayer player = new AudioPlayer(SAMPLE_RATE, frames, ROTATE_SPEED, TRANSLATION_SPEED,
|
||||||
TRANSLATION, SCALE, WEIGHT);
|
TRANSLATION, SCALE, WEIGHT);
|
||||||
System.out.println("Starting audio stream");
|
System.out.println("Starting audio stream");
|
||||||
player.play();
|
new Thread(player).start();
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,7 +14,7 @@ import shapes.Vector2;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class AudioPlayer {
|
public class AudioPlayer implements Runnable {
|
||||||
|
|
||||||
private final XtFormat FORMAT;
|
private final XtFormat FORMAT;
|
||||||
|
|
||||||
|
@ -141,7 +141,12 @@ public class AudioPlayer {
|
||||||
return frames.get(currentFrame).get(currentShape);
|
return frames.get(currentFrame).get(currentShape);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void play() {
|
public void addFrame(List<Shape> frame) {
|
||||||
|
frames.add(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
try (XtAudio audio = new XtAudio(null, null, null, null)) {
|
try (XtAudio audio = new XtAudio(null, null, null, null)) {
|
||||||
XtService service = XtAudio.getServiceBySetup(XtSetup.CONSUMER_AUDIO);
|
XtService service = XtAudio.getServiceBySetup(XtSetup.CONSUMER_AUDIO);
|
||||||
try (XtDevice device = service.openDefaultDevice(true)) {
|
try (XtDevice device = service.openDefaultDevice(true)) {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package engine;
|
package engine;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import shapes.Line;
|
import shapes.Line;
|
||||||
import shapes.Vector2;
|
import shapes.Vector2;
|
||||||
|
|
||||||
|
@ -40,17 +42,17 @@ public class Camera {
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Line> draw(WorldObject worldObject) {
|
public List<Line> draw(WorldObject worldObject) {
|
||||||
return getFrame(getProjectedVertices(worldObject), worldObject.getEdgeData());
|
return getFrame(getProjectedVertices(worldObject), worldObject.getVertexPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Vector2> getProjectedVertices(WorldObject worldObject) {
|
public Map<Vector3, Vector2> getProjectedVertices(WorldObject worldObject) {
|
||||||
List<Vector2> vertices = new ArrayList<>();
|
Map<Vector3, Vector2> projectionMap = new HashMap<>();
|
||||||
|
|
||||||
for (Vector3 vertex : worldObject.getVertices()) {
|
for (Vector3 vertex : worldObject.getVertices()) {
|
||||||
vertices.add(project(vertex));
|
projectionMap.put(vertex, project(vertex));
|
||||||
}
|
}
|
||||||
|
|
||||||
return vertices;
|
return projectionMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Automatically finds the correct Z position to use to view the world object properly.
|
// Automatically finds the correct Z position to use to view the world object properly.
|
||||||
|
@ -73,7 +75,7 @@ public class Camera {
|
||||||
List<Vector2> vertices = new ArrayList<>();
|
List<Vector2> vertices = new ArrayList<>();
|
||||||
|
|
||||||
for (int i = 0; i < SAMPLE_RENDER_SAMPLES - 1; i++) {
|
for (int i = 0; i < SAMPLE_RENDER_SAMPLES - 1; i++) {
|
||||||
vertices.addAll(getProjectedVertices(clone));
|
vertices.addAll(getProjectedVertices(clone).values());
|
||||||
clone.rotate(rotation);
|
clone.rotate(rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,18 +113,14 @@ public class Camera {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Line> getFrame(List<Vector2> vertices, List<Integer> connections) {
|
public List<Line> getFrame(Map<Vector3, Vector2> projectionMap, List<Vector3> vertexPath) {
|
||||||
List<Line> lines = new ArrayList<>();
|
List<Line> lines = new ArrayList<>();
|
||||||
|
|
||||||
for (int i = 0; i < connections.size(); i += 2) {
|
for (int i = 0; i < vertexPath.size(); i += 2) {
|
||||||
Vector2 start = vertices.get(Math.abs(connections.get(i)));
|
lines.add(new Line(
|
||||||
Vector2 end = vertices.get(Math.abs(connections.get(i + 1)));
|
projectionMap.get(vertexPath.get(i)),
|
||||||
double x1 = start.getX();
|
projectionMap.get(vertexPath.get(i + 1))
|
||||||
double y1 = start.getY();
|
));
|
||||||
double x2 = end.getX();
|
|
||||||
double y2 = end.getY();
|
|
||||||
|
|
||||||
lines.add(new Line(x1, y1, x2, y2));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return lines;
|
return lines;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package engine;
|
package engine;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import shapes.Vector2;
|
||||||
|
|
||||||
public final class Vector3 {
|
public final class Vector3 {
|
||||||
|
|
||||||
|
@ -78,6 +80,10 @@ public final class Vector3 {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double distance(Vector3 vector) {
|
||||||
|
return Math.sqrt(Math.pow(vector.x, 2) + Math.pow(vector.y, 2) + Math.pow(vector.z, 2));
|
||||||
|
}
|
||||||
|
|
||||||
public static Vector3 meanPoint(List<Vector3> points) {
|
public static Vector3 meanPoint(List<Vector3> points) {
|
||||||
Vector3 mean = new Vector3();
|
Vector3 mean = new Vector3();
|
||||||
|
|
||||||
|
@ -88,6 +94,34 @@ public final class Vector3 {
|
||||||
return mean.scale(1f / (points.size()));
|
return mean.scale(1f / (points.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null || getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Vector3 point = (Vector3) obj;
|
||||||
|
return x == point.x && y == point.y && z== point.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double round(double value, int places) {
|
||||||
|
if (places < 0) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
long factor = (long) Math.pow(10, places);
|
||||||
|
value *= factor;
|
||||||
|
|
||||||
|
return (double) Math.round(value) / factor;
|
||||||
|
}
|
||||||
|
|
||||||
public Vector3 clone() {
|
public Vector3 clone() {
|
||||||
return new Vector3(x, y, z);
|
return new Vector3(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,36 +3,102 @@ package engine;
|
||||||
import com.mokiat.data.front.parser.*;
|
import com.mokiat.data.front.parser.*;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.jgrapht.Graph;
|
||||||
|
import org.jgrapht.alg.connectivity.ConnectivityInspector;
|
||||||
|
import org.jgrapht.alg.cycle.ChinesePostman;
|
||||||
|
import org.jgrapht.graph.AsSubgraph;
|
||||||
|
import org.jgrapht.graph.DefaultUndirectedWeightedGraph;
|
||||||
|
import org.jgrapht.graph.DefaultWeightedEdge;
|
||||||
|
|
||||||
public class WorldObject {
|
public class WorldObject {
|
||||||
|
|
||||||
private final List<Vector3> vertices;
|
private final List<Vector3> vertices;
|
||||||
private final List<Integer> edgeData;
|
|
||||||
|
// These should be a path of vertices from the above vertex list.
|
||||||
|
private List<Vector3> vertexPath;
|
||||||
private Vector3 position;
|
private Vector3 position;
|
||||||
private Vector3 rotation;
|
private Vector3 rotation;
|
||||||
|
|
||||||
public WorldObject(List<Vector3> vertices, List<Integer> edgeData, Vector3 position,
|
private WorldObject(List<Vector3> vertices, List<Vector3> vertexPath, Vector3 position, Vector3 rotation) {
|
||||||
Vector3 rotation) {
|
|
||||||
this.vertices = vertices;
|
this.vertices = vertices;
|
||||||
this.edgeData = edgeData;
|
|
||||||
this.position = position;
|
this.position = position;
|
||||||
this.rotation = rotation;
|
this.rotation = rotation;
|
||||||
|
this.vertexPath = vertexPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private WorldObject(List<Vector3> vertices, Vector3 position, Vector3 rotation) {
|
||||||
|
this(vertices, new ArrayList<>(), position, rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WorldObject(String filename, Vector3 position, Vector3 rotation) throws IOException {
|
public WorldObject(String filename, Vector3 position, Vector3 rotation) throws IOException {
|
||||||
this(new ArrayList<>(), new ArrayList<>(), position, rotation);
|
this(new ArrayList<>(), position, rotation);
|
||||||
loadFromFile(filename);
|
this.vertexPath = getDrawPath(loadFromFile(filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
public WorldObject(String filename) throws IOException {
|
public WorldObject(String filename) throws IOException {
|
||||||
this(filename, new Vector3(), new Vector3());
|
this(filename, new Vector3(), new Vector3());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Vector3> getVertexPath() {
|
||||||
|
List<Vector3> newVertices = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Vector3 vertex : vertexPath) {
|
||||||
|
newVertices.add(vertex.rotate(rotation).add(position));
|
||||||
|
}
|
||||||
|
|
||||||
|
return newVertices;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Vector3> getDrawPath(List<Integer> edgeData) {
|
||||||
|
Graph<Vector3, DefaultWeightedEdge> graph = new DefaultUndirectedWeightedGraph<>(
|
||||||
|
DefaultWeightedEdge.class);
|
||||||
|
|
||||||
|
List<Vector3> vertexPath = new ArrayList<>();
|
||||||
|
|
||||||
|
// Add all lines in frame to graph as vertices and edges. Edge weight is determined by the
|
||||||
|
// length of the line as this is directly proportional to draw time.
|
||||||
|
for (int i = 0; i < edgeData.size(); i += 2) {
|
||||||
|
Vector3 start = vertices.get(edgeData.get(i));
|
||||||
|
Vector3 end = vertices.get(edgeData.get(i + 1));
|
||||||
|
graph.addVertex(start);
|
||||||
|
graph.addVertex(end);
|
||||||
|
|
||||||
|
DefaultWeightedEdge edge = new DefaultWeightedEdge();
|
||||||
|
graph.addEdge(start, end, edge);
|
||||||
|
graph.setEdgeWeight(edge, start.distance(end));
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectivityInspector<Vector3, DefaultWeightedEdge> inspector = new ConnectivityInspector<>(
|
||||||
|
graph);
|
||||||
|
|
||||||
|
// Chinese Postman can only be performed on connected graphs, so iterate over all connected
|
||||||
|
// sub-graphs.
|
||||||
|
for (Set<Vector3> vertices : inspector.connectedSets()) {
|
||||||
|
AsSubgraph<Vector3, DefaultWeightedEdge> subgraph = new AsSubgraph<>(graph, vertices);
|
||||||
|
ChinesePostman<Vector3, DefaultWeightedEdge> cp = new ChinesePostman<>();
|
||||||
|
Collection<DefaultWeightedEdge> path;
|
||||||
|
|
||||||
|
try {
|
||||||
|
path = cp.getCPPSolution(subgraph).getEdgeList();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Safety in case getCPPSolution fails.
|
||||||
|
path = subgraph.edgeSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (DefaultWeightedEdge edge : path) {
|
||||||
|
vertexPath.add(subgraph.getEdgeSource(edge));
|
||||||
|
vertexPath.add(subgraph.getEdgeTarget(edge));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return vertexPath;
|
||||||
|
}
|
||||||
|
|
||||||
public void rotate(Vector3 theta) {
|
public void rotate(Vector3 theta) {
|
||||||
rotation = rotation.add(theta);
|
rotation = rotation.add(theta);
|
||||||
|
@ -60,15 +126,13 @@ public class WorldObject {
|
||||||
return newVertices;
|
return newVertices;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Integer> getEdgeData() {
|
private List<Integer> loadFromFile(String filename) throws IOException {
|
||||||
return edgeData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadFromFile(String filename) throws IOException {
|
|
||||||
InputStream in = new FileInputStream(filename);
|
InputStream in = new FileInputStream(filename);
|
||||||
final IOBJParser parser = new OBJParser();
|
final IOBJParser parser = new OBJParser();
|
||||||
final OBJModel model = parser.parse(in);
|
final OBJModel model = parser.parse(in);
|
||||||
|
|
||||||
|
List<Integer> edgeData = new ArrayList<>();
|
||||||
|
|
||||||
for (OBJVertex vertex : model.getVertices()) {
|
for (OBJVertex vertex : model.getVertices()) {
|
||||||
vertices.add(new Vector3(vertex.x, vertex.y, vertex.z));
|
vertices.add(new Vector3(vertex.x, vertex.y, vertex.z));
|
||||||
}
|
}
|
||||||
|
@ -85,10 +149,11 @@ public class WorldObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return edgeData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WorldObject clone() {
|
public WorldObject clone() {
|
||||||
return new WorldObject(new ArrayList<>(vertices), new ArrayList<>(edgeData), position,
|
return new WorldObject(new ArrayList<>(vertices), new ArrayList<>(vertexPath), position, rotation);
|
||||||
rotation);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue