diff --git a/.github/workflows/RELEASE.md b/.github/workflows/RELEASE.md new file mode 100644 index 0000000..4a2bc3f --- /dev/null +++ b/.github/workflows/RELEASE.md @@ -0,0 +1,3 @@ +See the [changelog](https://github.com/jameshball/osci-render/blob/master/CHANGELOG.md) for changes to this version. + +GPU VERSION - This is potentially unstable on Mac/Linux!! Please report any issues. If you hear nothing, but you see the program interface, then please let me know. \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1a39c63..0397868 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,17 +22,18 @@ jobs: mvn -B jpackage:jpackage@win $version= mvn help:evaluate -Dexpression="project.version" -q -DforceStdout echo "VERSION=$version" >> $env:GITHUB_ENV - echo "See [/CHANGELOG.md](https://github.com/jameshball/osci-render/blob/master/CHANGELOG.md) for changes to this version.\nGPU VERSION - This is potentially unstable on Mac/Linux!! Please report any issues. If you hear nothing, but you see the program interface, then please let me know." > CHANGELOG mv "target/lib/osci-render-$version.jar" "target/lib/osci-render-win-$version.jar" + Compress-Archive -Path "blender/osci_render" -DestinationPath "target/lib/osci-render-blender-addon.zip" ls target/lib/ - name: Release uses: softprops/action-gh-release@v1 with: - body_path: CHANGELOG + body_path: .github/workflows/RELEASE.md tag_name: v${{ env.VERSION }} files: | target/lib/osci-render-${{ env.VERSION }}.exe target/lib/osci-render-win-${{ env.VERSION }}.jar + target/lib/osci-render-blender-addon.zip env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_REPOSITORY: jameshball/osci-render diff --git a/src/main/java/sh/ball/engine/WorldObject.java b/src/main/java/sh/ball/engine/WorldObject.java index 1116292..0cc91c8 100644 --- a/src/main/java/sh/ball/engine/WorldObject.java +++ b/src/main/java/sh/ball/engine/WorldObject.java @@ -5,13 +5,14 @@ import com.mokiat.data.front.parser.*; import java.io.IOException; import java.io.InputStream; import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; 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; +import org.jgrapht.alg.shortestpath.DijkstraShortestPath; +import org.jgrapht.graph.*; public class WorldObject { @@ -60,33 +61,89 @@ public class WorldObject { return triangles; } - public void getDrawPath(Set edges) { - Graph graph = new DefaultUndirectedWeightedGraph<>( - DefaultWeightedEdge.class); + private Vector3 addEdgeToPath(Vector3 realSource, Graph graph, DefaultEdge edge, Set visited, Set notVisited, List path) { + visited.add(edge); + notVisited.remove(edge); - // 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 (Line3D edge : edges) { - graph.addVertex(edge.getStart()); - graph.addVertex(edge.getEnd()); + Vector3 supposedSource = graph.getEdgeSource(edge); + Vector3 supposedSink = graph.getEdgeTarget(edge); + // no guarantee on order since the graph is undirected + Vector3 source = supposedSource == realSource ? supposedSource : supposedSink; + Vector3 sink = supposedSink == realSource ? supposedSource : supposedSink; - DefaultWeightedEdge weightedEdge = new DefaultWeightedEdge(); - graph.addEdge(edge.getStart(), edge.getEnd(), weightedEdge); - graph.addEdge(edge.getStart(), edge.getEnd()); - graph.setEdgeWeight(weightedEdge, edge.getStart().distance(edge.getEnd())); + path.add(source); + path.add(sink); + + return sink; + } + + // Unused but faster than chinese postman - not good at handling with lots of vertices + private void fasterPath(Graph graph, Set vertices) { + Set visited = new HashSet<>(); + List path = new ArrayList<>(); + AsSubgraph subgraph = new AsSubgraph<>(graph, vertices); + Set notVisited = new HashSet<>(subgraph.edgeSet()); + + DefaultEdge outgoingEdge = notVisited.stream().findFirst().orElseThrow(); + Vector3 currentVertex = subgraph.getEdgeSource(outgoingEdge); + Vector3 startVertex = currentVertex; + visited.add(outgoingEdge); + notVisited.remove(outgoingEdge); + path.add(currentVertex); + path.add(subgraph.getEdgeTarget(outgoingEdge)); + + while (!notVisited.isEmpty()) { + Set outgoing = subgraph.outgoingEdgesOf(currentVertex); + DefaultEdge newEdge = null; + for (DefaultEdge edge : outgoing) { + if (!visited.contains(edge)) { + newEdge = edge; + currentVertex = addEdgeToPath(currentVertex, subgraph, edge, visited, notVisited, path); + break; + } + } + + if (newEdge == null) { + newEdge = notVisited.stream().findFirst().orElseThrow(); + Vector3 dest = subgraph.getEdgeSource(newEdge); + + List shortestPath = DijkstraShortestPath.findPathBetween(subgraph, currentVertex, dest).getEdgeList(); + for (DefaultEdge edge : shortestPath) { + addEdgeToPath(currentVertex, subgraph, edge, visited, notVisited, path); + } + + currentVertex = dest; + } } - ConnectivityInspector inspector = new ConnectivityInspector<>( + // return back to start vertex + path.addAll(DijkstraShortestPath.findPathBetween(subgraph, currentVertex, startVertex).getVertexList()); + vertexPath.add(path); + } + + public void getDrawPath(Set edges) { + Graph graph = new DefaultUndirectedGraph<>(DefaultEdge.class); + + // Add all lines in frame to graph as vertices and edges. + Stream.concat( + edges.stream().map(Line3D::getStart), + edges.stream().map(Line3D::getEnd) + ).distinct().forEach(graph::addVertex); + + for (Line3D edge : edges) { + graph.addEdge(edge.getStart(), edge.getEnd()); + } + + ConnectivityInspector inspector = new ConnectivityInspector<>( graph); // Chinese Postman can only be performed on connected graphs, so iterate over all connected // sub-graphs. - // TODO: parallelize? - for (Set vertices : inspector.connectedSets()) { - AsSubgraph subgraph = new AsSubgraph<>(graph, vertices); - ChinesePostman cp = new ChinesePostman<>(); - vertexPath.add(cp.getCPPSolution(subgraph).getVertexList()); - } + vertexPath = inspector.connectedSets().parallelStream().map(vertices -> { + ChinesePostman cp = new ChinesePostman<>(); + AsSubgraph subgraph = new AsSubgraph<>(graph, vertices); + return cp.getCPPSolution(subgraph).getVertexList(); + }).toList(); } public Vector3 getRotation() {