diff --git a/README.md b/README.md index ea1a768..287f039 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Semviz -![Tag](https://img.shields.io/badge/tag-v1.0.0-blue.svg) [![Build Status](https://travis-ci.com/vmoglan/semviz.svg?branch=master)](https://travis-ci.org/vmoglan/semviz) +![Tag](https://img.shields.io/badge/tag-v1.0.1-blue.svg) [![Build Status](https://travis-ci.com/vmoglan/semviz.svg?branch=master)](https://travis-ci.org/vmoglan/semviz) ## Main objective @@ -24,7 +24,7 @@ The display of 3D objects must be done in three ways: ### Setup -You can download the Semviz all-in-one `.jar` at this [link](https://github.com/vmoglan/semviz/releases/download/v1.0.0/semviz-1.0.0.jar). There is no installation process, just make sure you have Java 8 installed on your marchine and then you should be able to run the file. +You can download the Semviz all-in-one `.jar` at this [link](https://github.com/vmoglan/semviz/releases/download/v1.0.1/semviz-1.0.1.jar). There is no installation process, just make sure you have Java 8 installed on your marchine and then you should be able to run the file. ### Initialization diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml index 8d4d295..5b68682 100644 --- a/dependency-reduced-pom.xml +++ b/dependency-reduced-pom.xml @@ -4,7 +4,7 @@ com.movlad semviz Semviz - 1.0.0 + 1.0.1 A viewer for semantically annotated three-dimensional point clouds. diff --git a/pom.xml b/pom.xml index 6d9a041..2c4df5b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.movlad semviz - 1.0.0 + 1.0.1 Semviz A viewer for semantically annotated three-dimensional point clouds. diff --git a/src/main/java/com/movlad/semviz/application/CommandNavigationController.java b/src/main/java/com/movlad/semviz/application/CommandNavigationController.java index 2e8e544..787a786 100644 --- a/src/main/java/com/movlad/semviz/application/CommandNavigationController.java +++ b/src/main/java/com/movlad/semviz/application/CommandNavigationController.java @@ -9,18 +9,14 @@ public final class CommandNavigationController extends Controller { private final List commands; - private int selectedIndex; + private int selectedCommandIndex; public CommandNavigationController() { commands = new ArrayList<>(); commands.add(""); - selectedIndex = commands.size() - 1; - } - - public String getSelection() { - return commands.get(selectedIndex); + selectedCommandIndex = commands.size() - 1; } /** @@ -30,18 +26,21 @@ public String getSelection() { */ public void enter(String command) { commands.add(commands.size() - 1, command); + + changeSupport.firePropertyChange("CommandLaunch", null, command); } /** * Selects previous command. */ public void up() { - if (selectedIndex - 1 >= 0) { - String prev = getSelection(); + if (selectedCommandIndex - 1 >= 0) { + String prev = commands.get(selectedCommandIndex); - selectedIndex--; + selectedCommandIndex--; - changeSupport.firePropertyChange("CommandNavigationUp", prev, getSelection()); + changeSupport.firePropertyChange("CommandNavigationUp", prev, + commands.get(selectedCommandIndex)); } } @@ -49,12 +48,13 @@ public void up() { * Selects a more recent command. */ public void down() { - if (selectedIndex + 1 < commands.size()) { - String prev = getSelection(); + if (selectedCommandIndex + 1 < commands.size()) { + String prev = commands.get(selectedCommandIndex); - selectedIndex++; + selectedCommandIndex++; - changeSupport.firePropertyChange("CommandNavigationDown", prev, getSelection()); + changeSupport.firePropertyChange("CommandNavigationDown", prev, + commands.get(selectedCommandIndex)); } } diff --git a/src/main/java/com/movlad/semviz/application/QueryManagerController.java b/src/main/java/com/movlad/semviz/application/QueryManagerController.java deleted file mode 100644 index 763f69e..0000000 --- a/src/main/java/com/movlad/semviz/application/QueryManagerController.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.movlad.semviz.application; - -import com.movlad.semviz.core.io.DirectoryLoader; -import com.movlad.semviz.core.io.InvalidDirectoryException; -import com.movlad.semviz.core.semantic.QueryManager; -import com.movlad.semviz.core.semantic.QueryResult; -import java.nio.file.NotDirectoryException; -import java.util.List; - -/** - * Controller that creates a bridge between the view and the query manager - * model; SPARQL queries can be executed through this controller. - */ -public final class QueryManagerController extends Controller { - - private QueryManager queryManager; - private List queryResults; - - public QueryManager getQueryManager() { - return queryManager; - } - - public List getQueryResults() { - return queryResults; - } - - /** - * Given a Semviz directory, this method loads a - * {@link com.movlad.semviz.core.semantic.QueryManager} from the ontology - * file within it and notifies the property change listeners of the success - * or failure. - * - * @param path is the path to the Semviz directory - */ - public void loadQueryManager(String path) { - QueryManager prev = queryManager; - - queryManager = null; - - if (queryResults != null) { - queryResults.clear(); - } - - try { - DirectoryLoader loader = new DirectoryLoader(path); - - loader.load(); - - queryManager = new QueryManager(loader); - - changeSupport.firePropertyChange("QueryManagerLoadSuccess", prev, queryManager); - } catch (InvalidDirectoryException | NotDirectoryException e) { - changeSupport.firePropertyChange("QueryManagerLoadError", null, e.getMessage()); - } - } - - /** - * Executes a query on the currently loaded - * {@link com.movlad.semviz.core.semantic.QueryManager}, generates a - * {@link com.movlad.semviz.core.semantic.SemanticCloud} formed by all the - * clouds retrieved using the query. - * - * @param queryString is the string of the query to be executed - */ - public void executeQuery(String queryString) { - List prev = queryResults; - - try { - queryResults = queryManager.query(queryString); - - changeSupport.firePropertyChange("QueryExecutionSuccess", prev, queryResults); - } catch (Exception e) { - changeSupport.firePropertyChange("QueryExecutionError", null, e.getMessage()); - } - } - -} diff --git a/src/main/java/com/movlad/semviz/application/SQMController.java b/src/main/java/com/movlad/semviz/application/SQMController.java new file mode 100644 index 0000000..a297a77 --- /dev/null +++ b/src/main/java/com/movlad/semviz/application/SQMController.java @@ -0,0 +1,99 @@ +package com.movlad.semviz.application; + +import com.movlad.semviz.core.io.InvalidDirectoryException; +import com.movlad.semviz.core.sqm.SQM; +import com.movlad.semviz.core.sqm.SemanticCloudDescription; +import java.io.FileNotFoundException; +import java.nio.file.NotDirectoryException; +import java.util.List; + +/** + * Controller for the Semviz Query Manager. + */ +public final class SQMController extends Controller { + + private static SQMController instance = null; + private final SQM sqm; // the unique instance of the Semviz Query Manager + private List descriptions; // the list of current query results + private int selectedDescriptionIndex; // the currently selected Description + + private SQMController() { + sqm = SQM.getInstance(); + selectedDescriptionIndex = -1; + } + + public static SQMController getInstance() { + if (instance == null) { + instance = new SQMController(); + } + + return instance; + } + + /** + * @return the currently selected description + */ + public SemanticCloudDescription getSelectedDescription() { + if (selectedDescriptionIndex > -1 + && selectedDescriptionIndex < descriptions.size()) { + return descriptions.get(selectedDescriptionIndex); + } + + return null; + } + + /** + * Given a Semviz directory, this method loads a Semviz directory into the + * Semviz query manager singleton. + * + * @param path is the path to the Semviz directory + */ + public void load(String path) { + if (descriptions != null) { + descriptions.clear(); + } + + try { + sqm.load(path); + + changeSupport.firePropertyChange("SQMLoadSuccess", null, sqm); + } catch (InvalidDirectoryException | NotDirectoryException | FileNotFoundException e) { + changeSupport.firePropertyChange("SQMLoadError", null, e.getMessage()); + } + } + + /** + * Executes a query on the currently loaded Semviz directory. + * + * @param queryString is the string of the query to be executed + */ + public void exec(String queryString) { + List prevDescriptions = descriptions; + + try { + changeSupport.firePropertyChange("SQMExecutionStarted", null, sqm); + + descriptions = sqm.exec(queryString); + + changeSupport.firePropertyChange("SQMFailCountChanged", null, sqm.getFailCount()); + changeSupport.firePropertyChange("SQMExecutionSuccess", prevDescriptions, descriptions); + } catch (Exception e) { + changeSupport.firePropertyChange("SQMExecutionError", null, e.getMessage()); + } + } + + /** + * Sets the selected semantic cloud description index. + * + * @param i is the index of the selected description + */ + public void setSelectedDescriptionIndex(int i) { + int prev = selectedDescriptionIndex; + + selectedDescriptionIndex = i; + + changeSupport.firePropertyChange("SQMDescriptionIndexChanged", prev, + selectedDescriptionIndex); + } + +} diff --git a/src/main/java/com/movlad/semviz/application/SceneController.java b/src/main/java/com/movlad/semviz/application/SceneController.java index 0297d21..eab8b33 100644 --- a/src/main/java/com/movlad/semviz/application/SceneController.java +++ b/src/main/java/com/movlad/semviz/application/SceneController.java @@ -1,14 +1,17 @@ package com.movlad.semviz.application; +import com.github.quickhull3d.Point3d; +import com.github.quickhull3d.Vector3d; import com.movlad.semviz.core.graphics.Geometry; import com.movlad.semviz.core.graphics.GeometryFactory; import com.movlad.semviz.core.graphics.Scene; import com.movlad.semviz.core.graphics.SceneObject; import com.movlad.semviz.core.math.geometry.BoundingBox; import com.movlad.semviz.core.math.geometry.PointCloud; -import com.movlad.semviz.core.semantic.SemanticCloud; +import com.movlad.semviz.core.sqm.SemanticCloudDescription; import java.util.ArrayList; import java.util.List; +import org.joml.Vector3f; /** * Controller for the modification of a {@code Scene} and view in general. @@ -38,16 +41,18 @@ public Scene getScene() { /** * Loads the base geometry for each cloud in a semantic cloud * - * @param cloud is the cloud containing the clusters + * @param descriptions */ - public void loadDisplayInformation(SemanticCloud cloud) { + public void load(List descriptions) { resetDisplayInformation(); + center(descriptions); + rotate(descriptions); - views = new SceneObject[cloud.size()][3]; - selectedViewIndices = new int[cloud.size()]; + views = new SceneObject[descriptions.size()][3]; + selectedViewIndices = new int[descriptions.size()]; - for (int i = 0; i < cloud.size(); i++) { - clusters.add(cloud.get(i)); + for (int i = 0; i < descriptions.size(); i++) { + clusters.add(descriptions.get(i).getCloud()); SceneObject object = new SceneObject(GeometryFactory.getInstance() .createHighResolutionCloudGeometry(clusters.get(i))); @@ -87,8 +92,6 @@ public int getSelectedViewIndex(int i) { * @param view is the view selection for the cluster at index {@code i} */ public void setSelectedViewIndex(int i, int view) { - int prev = selectedViewIndices[i]; - selectedViewIndices[i] = view; if (views[i][view] == null) { @@ -117,7 +120,7 @@ public void setSelectedViewIndex(int i, int view) { scene.replace(i, object); - changeSupport.firePropertyChange("SelectedViewIndexUpdate", prev, selectedViewIndices[i]); + changeSupport.firePropertyChange("SceneViewIndexUpdate", null, scene); } /** @@ -135,7 +138,56 @@ public void setDisplaySelection(int i) { scene.add(new SceneObject("selection", geometry)); } - changeSupport.firePropertyChange("SceneSelectionUpdate", null, i); + changeSupport.firePropertyChange("SceneSelectionUpdate", null, scene); + } + + /** + * Centers the cluster contained within each semantic description according + * to a common centroid. + * + * @param descriptions is a list of semantic cloud descriptions, each + * containing a cluster. + */ + private void center(List descriptions) { + Point3d centroid = new Point3d(0, 0, 0); + int size = 0; + + for (SemanticCloudDescription description : descriptions) { + PointCloud cluster = description.getCloud(); + + size += cluster.size(); + + cluster.forEach(point -> { + centroid.add(new Vector3d(point.x, point.y, point.z)); + }); + } + + centroid.set(centroid.x / size, centroid.y / size, centroid.z / size); + + descriptions.stream().map((description) -> description.getCloud()).forEachOrdered((cluster) -> { + cluster.forEach(point -> { + point.sub(centroid); + }); + }); + } + + /** + * Rotates the cluster of each semantic description so that the up vector of + * the whole corresponds to the world Z-axis. + * + * @param descriptions is a list of semantic cloud descriptions, each + * containing a cluster. + */ + private void rotate(List descriptions) { + descriptions.stream().map((description) -> description.getCloud()).forEachOrdered((cluster) -> { + cluster.forEach(point -> { + Vector3f v = new Vector3f((float) point.x, (float) point.y, (float) point.z); + + v.rotateAxis((float) (Math.PI / 2), 0.0f, 1.0f, 0.0f); + + point.set(v.x, v.y, v.z); + }); + }); } } diff --git a/src/main/java/com/movlad/semviz/application/SemanticCloudController.java b/src/main/java/com/movlad/semviz/application/SemanticCloudController.java deleted file mode 100644 index 561a0aa..0000000 --- a/src/main/java/com/movlad/semviz/application/SemanticCloudController.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.movlad.semviz.application; - -import com.movlad.semviz.core.semantic.QueryManager; -import com.movlad.semviz.core.semantic.QueryResult; -import com.movlad.semviz.core.semantic.SemanticCloud; -import java.util.List; - -/** - * Controller that notifies listeners about change concerning a loaded semantic - * cloud. - */ -public final class SemanticCloudController extends Controller { - - private SemanticCloud semanticCloud; - - /** - * Loads a semantic cloud from a list of results issued from a query manager - * and notifies listeners. - * - * @param queryManager contains the model from which the results are issued - * @param queryResults are the results issued from a SPARQL query - */ - public void loadSuperCloud(QueryManager queryManager, List queryResults) { - SemanticCloud prev = semanticCloud; - - semanticCloud = new SemanticCloud(queryManager, queryResults); - - semanticCloud.load(); - changeSupport.firePropertyChange("SemanticCloudChange", prev, semanticCloud); - } - -} diff --git a/src/main/java/com/movlad/semviz/core/graphics/BufferAttribute.java b/src/main/java/com/movlad/semviz/core/graphics/BufferAttribute.java index 58e039e..9e2c82c 100644 --- a/src/main/java/com/movlad/semviz/core/graphics/BufferAttribute.java +++ b/src/main/java/com/movlad/semviz/core/graphics/BufferAttribute.java @@ -6,7 +6,7 @@ * Class representing one attribute of a buffer layout, such as position, color * or texture coordinates. */ -class BufferAttribute { +public class BufferAttribute { private final String name; private final int size; diff --git a/src/main/java/com/movlad/semviz/core/graphics/BufferLayout.java b/src/main/java/com/movlad/semviz/core/graphics/BufferLayout.java index bc9896b..cfe8c83 100644 --- a/src/main/java/com/movlad/semviz/core/graphics/BufferLayout.java +++ b/src/main/java/com/movlad/semviz/core/graphics/BufferLayout.java @@ -9,7 +9,7 @@ * {@code VertexBufferObject}, such as position, color, texture coordinates etc. * . */ -class BufferLayout implements Iterable { +public class BufferLayout implements Iterable { private final List attributes; private int stride = 0; diff --git a/src/main/java/com/movlad/semviz/core/graphics/Renderer.java b/src/main/java/com/movlad/semviz/core/graphics/Renderer.java index b5739e9..8621f8d 100644 --- a/src/main/java/com/movlad/semviz/core/graphics/Renderer.java +++ b/src/main/java/com/movlad/semviz/core/graphics/Renderer.java @@ -15,8 +15,8 @@ */ public abstract class Renderer implements GLEventListener { - private final Scene scene; - private final Camera camera; + private volatile Scene scene; + private Camera camera; private ShaderProgram program; @@ -24,10 +24,16 @@ public abstract class Renderer implements GLEventListener { private IntBuffer vaos; private IntBuffer vbos; - public Renderer(Scene scene, Camera camera) { + public Renderer() { + this.VAO = IntBuffer.allocate(1); + } + + public final void setScene(Scene scene) { this.scene = scene; + } + + public final void setCamera(Camera camera) { this.camera = camera; - this.VAO = IntBuffer.allocate(1); } @Override diff --git a/src/main/java/com/movlad/semviz/core/io/CloudLoader.java b/src/main/java/com/movlad/semviz/core/io/CloudLoader.java index 3e56dad..06895e4 100644 --- a/src/main/java/com/movlad/semviz/core/io/CloudLoader.java +++ b/src/main/java/com/movlad/semviz/core/io/CloudLoader.java @@ -11,33 +11,24 @@ */ public class CloudLoader { + private final int POINT_ATTR_COUNT = 9; private String path; - private boolean normalsIncluded; - - private int numInvalidLines; - + private int failCount; private PointCloud pointCloud; /** * @param path is the path of the cloud {@code .txt} file - * @param normalsIncluded is true if the file also contains the normal - * vectors in each point */ - public CloudLoader(String path, boolean normalsIncluded) { + public CloudLoader(String path) { this.path = path; - this.normalsIncluded = normalsIncluded; } public void setPath(String path) { this.path = path; } - public void setNormalsIncluded(boolean normalsIncluded) { - this.normalsIncluded = normalsIncluded; - } - - public int getNumInvalidLines() { - return numInvalidLines; + public int getFailCount() { + return failCount; } /** @@ -54,15 +45,7 @@ public PointCloud getCloud() { * @throws IOException if the cloud file is not found */ public void load() throws IOException { - int numFields; - - numInvalidLines = 0; - - if (normalsIncluded) { - numFields = 9; - } else { - numFields = 6; - } + failCount = 0; try (BufferedReader reader = new BufferedReader(new FileReader(path))) { pointCloud = new PointCloud(); @@ -70,26 +53,28 @@ public void load() throws IOException { for (String line; (line = reader.readLine()) != null;) { String[] attributes = line.split("\\t"); - if (attributes.length == numFields) { - Point point = new Point(); + if (attributes.length == POINT_ATTR_COUNT) { + try { + Point point = new Point(); - point.x = Float.parseFloat(attributes[0]); - point.y = Float.parseFloat(attributes[1]); - point.z = Float.parseFloat(attributes[2]); + point.x = Float.parseFloat(attributes[0]); + point.y = Float.parseFloat(attributes[1]); + point.z = Float.parseFloat(attributes[2]); - point.r = Short.parseShort(attributes[3]); - point.g = Short.parseShort(attributes[4]); - point.b = Short.parseShort(attributes[5]); + point.r = Short.parseShort(attributes[3]); + point.g = Short.parseShort(attributes[4]); + point.b = Short.parseShort(attributes[5]); - if (numFields == 9) { point.normalX = Float.parseFloat(attributes[6]); point.normalY = Float.parseFloat(attributes[7]); point.normalZ = Float.parseFloat(attributes[8]); - } - pointCloud.add(point); + pointCloud.add(point); + } catch (NumberFormatException e) { + failCount++; + } } else { - numInvalidLines++; + failCount++; } } } diff --git a/src/main/java/com/movlad/semviz/core/io/DirectoryLoader.java b/src/main/java/com/movlad/semviz/core/io/DirectoryLoader.java deleted file mode 100644 index b65ed2a..0000000 --- a/src/main/java/com/movlad/semviz/core/io/DirectoryLoader.java +++ /dev/null @@ -1,147 +0,0 @@ -package com.movlad.semviz.core.io; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.nio.file.NotDirectoryException; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.rdf.model.ModelFactory; - -/** - * Used for validating and loading a Semviz directory. - */ -public class DirectoryLoader { - - private final String dirPath; - private OntModel model; - private File cloudsDir; - private boolean isValid; - - /** - * @param dirPath is the path to the directory - */ - public DirectoryLoader(String dirPath) { - this.dirPath = dirPath; - this.isValid = false; - } - - /** - * @throws InvalidDirectoryException if something goes wrong when loading a - * directory - * @throws java.nio.file.NotDirectoryException if the file at the given path - * is not a directory - */ - public void load() throws InvalidDirectoryException, NotDirectoryException { - this.model = null; - - File dir = new File(dirPath); - - if (!dir.exists()) { - throw new InvalidDirectoryException("Directory does not exist"); - } - - if (!dir.isDirectory()) { - throw new NotDirectoryException("File is not a directory."); - } - - model = null; - - if (!containsValidOntology(dir) || !containsCloudsDir(dir)) { - throw new InvalidDirectoryException("Invalid directory structure."); - } - - this.isValid = true; - } - - public boolean isValid() { - return isValid; - } - - public OntModel getModel() { - return model; - } - - public File getCloudsDir() { - return cloudsDir; - } - - /** - * Called within the constructor, this method checks whether the path in the - * parameter leads to a valid Semviz directory. - * - * @param dirPath is the path that leads to the directory - */ - private boolean validateDir(String dirPath) { - this.model = null; - - File dir = new File(dirPath); - - if (!dir.exists()) { - return false; - } - - if (!dir.isDirectory()) { - return false; - } - - model = null; - - return containsValidOntology(dir) && containsCloudsDir(dir); - } - - /** - * @param dir gives the array of files in the directory - * @returns true if the a Knowdip ontology is found in the directory and it - * is valid - */ - private boolean containsValidOntology(File dir) { - File[] files = dir.listFiles(); - - for (File file : files) { - if (file.getName().toLowerCase().endsWith(".owl")) { - model = ModelFactory.createOntologyModel(); - - FileInputStream is; - - try { - is = new FileInputStream(file.getAbsolutePath()); - } catch (FileNotFoundException e) { - return false; - } - - model.read(is, null); - - if (model.getNsPrefixURI("knowdip") == null) { - model = null; - - return false; - } - - return true; - } - } - - return false; - } - - /** - * @param dir gives the array of files in the directory - * @returns true if the directory contains a cloud folder - */ - private boolean containsCloudsDir(File dir) { - File[] files = dir.listFiles(); - - for (File file : files) { - if (file.getName().toLowerCase().equals("clouds")) { - if (file.isDirectory()) { - cloudsDir = file; - - return true; - } - } - } - - return false; - } - -} diff --git a/src/main/java/com/movlad/semviz/core/semantic/QueryExecutor.java b/src/main/java/com/movlad/semviz/core/semantic/QueryExecutor.java deleted file mode 100644 index 9fda5aa..0000000 --- a/src/main/java/com/movlad/semviz/core/semantic/QueryExecutor.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.movlad.semviz.core.semantic; - -import java.util.*; -import org.apache.jena.ontology.Individual; -import org.apache.jena.ontology.OntClass; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.query.*; -import org.apache.jena.rdf.model.Literal; -import org.apache.jena.rdf.model.RDFNode; -import org.apache.jena.rdf.model.Resource; - -class QueryExecutor { - - private final Query query; - private final OntModel model; - - public QueryExecutor(Query query, OntModel model) { - this.query = query; - this.model = model; - } - - public List exec() { - QueryExecution execution = QueryExecutionFactory.create(query, model); - ResultSet results = execution.execSelect(); - List solutions = ResultSetFormatter.toList(results); - List queryResults = new ArrayList<>(); - - solutions.forEach(solution -> { - Iterator varNameIt = solution.varNames(); - QueryResult result = new QueryResult(); - - while (varNameIt.hasNext()) { - String varName = varNameIt.next(); - RDFNode node = solution.get(varName); - Individual cloud = validateCloudResource(node); - - if (cloud != null) { - result.setIndividual(cloud); - } else { - if (node.isResource()) { - result.putAttribute(varName, ((Resource) node).getURI()); - } else { - result.putAttribute(varName, ((Literal) node).getLexicalForm()); - } - } - } - - if (result.getIndividual() != null) { - queryResults.add(result); - } - }); - - return queryResults; - } - - private Individual validateCloudResource(RDFNode node) { - if (node.isResource()) { - String uri = node.asResource().getURI(); - Individual individual = model.getIndividual(uri); - OntClass ontClass = model.getOntClass(model.getNsPrefixURI("knowdip") + "Point-Cloud"); - - if (individual.getOntClass().hasSuperClass(ontClass)) { - return individual; - } - } - - return null; - } - -} diff --git a/src/main/java/com/movlad/semviz/core/semantic/QueryManager.java b/src/main/java/com/movlad/semviz/core/semantic/QueryManager.java deleted file mode 100644 index d8a59eb..0000000 --- a/src/main/java/com/movlad/semviz/core/semantic/QueryManager.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package com.movlad.semviz.core.semantic; - -import com.movlad.semviz.core.io.CloudLoader; -import com.movlad.semviz.core.io.InvalidDirectoryException; -import com.movlad.semviz.core.io.DirectoryLoader; -import com.movlad.semviz.core.math.geometry.PointCloud; -import java.io.IOException; -import java.util.List; -import org.apache.jena.ontology.Individual; -import org.apache.jena.query.Query; -import org.apache.jena.query.QueryFactory; - -public class QueryManager { - - private static final String NS = "http://lab.ponciano.info/knowdip#"; - private static final String PREFIX = "PREFIX knowdip: <" + NS + ">"; - - DirectoryLoader dir; - - public QueryManager(DirectoryLoader dir) throws InvalidDirectoryException { - if (!dir.isValid()) { - throw new InvalidDirectoryException("Invalid parameter directory."); - } - - this.dir = dir; - } - - /** - * Queries the loaded model and notifies the observers of a change in - * results.Updates the {@code CanvasController}. - * - * @param queryString is the SPARQL query - * @return a list of query results - */ - public List query(String queryString) { - Query query = QueryFactory.create(PREFIX + System.lineSeparator() + queryString); - QueryExecutor queryExec = new QueryExecutor(query, dir.getModel()); - - return queryExec.exec(); - } - - /** - * @param cloudIndividual is the instance containing the semantic - * description of a cloud - * @return point cloud semantically described by the individual in parameter - */ - public PointCloud retrieve(Individual cloudIndividual) { - String pathToCloud = dir.getCloudsDir().getAbsolutePath() - + "/" + cloudIndividual.getLocalName() + ".txt"; - CloudLoader loader = new CloudLoader(pathToCloud, true); - - try { - loader.load(); - } catch (IOException e) { - return null; - } - - return loader.getCloud(); - } - -} diff --git a/src/main/java/com/movlad/semviz/core/semantic/QueryResult.java b/src/main/java/com/movlad/semviz/core/semantic/QueryResult.java deleted file mode 100644 index ec679aa..0000000 --- a/src/main/java/com/movlad/semviz/core/semantic/QueryResult.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.movlad.semviz.core.semantic; - -import org.apache.jena.ontology.Individual; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -public class QueryResult { - - private Individual individual; - private Map attributes; - - public QueryResult() { - individual = null; - attributes = new HashMap<>(); - } - - public Individual getIndividual() { - return individual; - } - - public void setIndividual(Individual cloud) { - this.individual = cloud; - } - - public void putAttribute(String key, String value) { - attributes.put(key, value); - } - - public String getAttribute(String key) { - return attributes.get(key); - } - - public Set getKeys() { - return attributes.keySet(); - } - - public Collection getValues() { - return attributes.values(); - } - -} diff --git a/src/main/java/com/movlad/semviz/core/semantic/SemanticCloud.java b/src/main/java/com/movlad/semviz/core/semantic/SemanticCloud.java deleted file mode 100644 index 3a5d90a..0000000 --- a/src/main/java/com/movlad/semviz/core/semantic/SemanticCloud.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.movlad.semviz.core.semantic; - -import com.github.quickhull3d.Point3d; -import com.github.quickhull3d.Vector3d; -import com.movlad.semviz.core.math.geometry.PointCloud; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import org.joml.Vector3f; - -/** - * When executing a SPARQL query via - * {@link com.movlad.semviz.core.semantic.QueryManager}, multiple cloud - * individuals are retrieved. These clusters that originate from the same base - * cloud are not centered by default, and in order to center them, the centroid - * of the base cloud needs to be subtracted from each of their points. This - * class groups all of the retrieved clusters, calculates the centroid of their - * base cloud and then centers them. - */ -public class SemanticCloud implements Iterable { - - private final List clusters; - private final QueryManager queryManager; - private final List queryResults; - - /** - * Constructor. - * - * @param queryManager holds the data of the currently loaded Semviz - * directory - * @param queryResults is the list of results obtained after the execution - * of a SPARQL query on the query manager - */ - public SemanticCloud(QueryManager queryManager, List queryResults) { - this.clusters = new ArrayList<>(); - this.queryManager = queryManager; - this.queryResults = queryResults; - } - - public PointCloud get(int i) { - return clusters.get(i); - } - - /** - * Retrieves all the clusters described by the query results, centers them - * and rotates them. - */ - public void load() { - queryResults.forEach(result -> { - clusters.add(queryManager.retrieve(result.getIndividual())); - }); - - center(); - rotate(); - } - - /** - * Subtracts the centroid of the base cloud from each point of each cluster. - */ - private void center() { - Point3d centroid = new Point3d(0, 0, 0); - int size = 0; - - for (PointCloud cluster : clusters) { - size += cluster.size(); - - cluster.forEach(point -> { - centroid.add(new Vector3d(point.x, point.y, point.z)); - }); - } - - centroid.set(centroid.x / size, centroid.y / size, centroid.z / size); - - clusters.forEach(cluster -> { - cluster.forEach(point -> { - point.sub(centroid); - }); - }); - } - - /** - * Rotates each point of each cloud 90 degrees around the X-Axis so that - * they are well aligned with the world up vector. - */ - private void rotate() { - for (PointCloud cluster : clusters) { - cluster.forEach(point -> { - Vector3f v = new Vector3f((float) point.x, (float) point.y, (float) point.z); - - v.rotateAxis((float) (Math.PI / 2), 0.0f, 1.0f, 0.0f); - - point.set(v.x, v.y, v.z); - }); - } - } - - public int size() { - return clusters.size(); - } - - @Override - public Iterator iterator() { - return clusters.iterator(); - } - -} diff --git a/src/main/java/com/movlad/semviz/core/sqm/SQM.java b/src/main/java/com/movlad/semviz/core/sqm/SQM.java new file mode 100644 index 0000000..a3c919e --- /dev/null +++ b/src/main/java/com/movlad/semviz/core/sqm/SQM.java @@ -0,0 +1,250 @@ +package com.movlad.semviz.core.sqm; + +import com.movlad.semviz.core.io.CloudLoader; +import com.movlad.semviz.core.io.InvalidDirectoryException; +import com.movlad.semviz.core.math.geometry.PointCloud; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.NotDirectoryException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import org.apache.jena.ontology.Individual; +import org.apache.jena.ontology.OntModel; +import org.apache.jena.query.Query; +import org.apache.jena.query.QueryExecution; +import org.apache.jena.query.QueryExecutionFactory; +import org.apache.jena.query.QueryFactory; +import org.apache.jena.query.QuerySolution; +import org.apache.jena.query.ResultSet; +import org.apache.jena.rdf.model.Literal; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.RDFNode; +import org.apache.jena.rdf.model.Resource; + +/** + * The Semviz Query Manager ({@code SQM}) is a singleton used for loading a + * Semviz directory and retrieving semantic descriptions of clouds using SPARQL + * queries. + */ +public class SQM { + + private static SQM instance = null; + private OntModel model; + private String path; + private int failCount; + + private SQM() { + failCount = 0; + } + + public static SQM getInstance() { + if (instance == null) { + instance = new SQM(); + } + + return instance; + } + + /** + * @return the active ontology model currently loaded within the query + * manager + */ + public OntModel getModel() { + return model; + } + + /** + * @return the path to the active Semviz directory + */ + public String getPath() { + return path; + } + + /** + * @return the number of {@code SemanticCloudDescription} instances that + * could not be retrieved on the last query + */ + public int getFailCount() { + return failCount; + } + + /** + * Attempts to load a Semviz directory. + * + * @param path is the absolute path to the Semviz directory to be loaded + * @throws FileNotFoundException if the file does not exist + * @throws NotDirectoryException if the file at the given path is not a + * directory + * @throws InvalidDirectoryException if the directory does not contain an + * ontology + */ + public void load(String path) throws InvalidDirectoryException, NotDirectoryException, + FileNotFoundException { + failCount = 0; + model = null; + + File directory = new File(path); + + if (!directory.exists()) { + throw new InvalidDirectoryException("Directory does not exist"); + } + + if (!directory.isDirectory()) { + throw new NotDirectoryException("File is not a directory."); + } + + File[] files = directory.listFiles(); + + for (File file : files) { + if (file.getName().toLowerCase().endsWith(".owl")) { + FileInputStream is = new FileInputStream(file.getAbsolutePath()); + + model = ModelFactory.createOntologyModel(); + + model.read(is, null); + } + } + + if (model == null) { + throw new InvalidDirectoryException("Not a valid directory."); + } + + this.path = path; + } + + /** + * Executes a SPARQL query on the active Semviz directory and returns the + * list of successfully retrieved instances of + * {@code SemanticCloudDescription}. + * + * @param queryString is the SPARQL query to be executed + * @return the list of successfully retrieved instances of + * {@code SemanticCloudDescription}. + */ + public List exec(String queryString) { + failCount = 0; + + List descriptions = new ArrayList<>(); + String prefixes = generatePrefixes(); + Query query = QueryFactory.create(prefixes + queryString); + QueryExecution execution = QueryExecutionFactory.create(query, model); + ResultSet results = execution.execSelect(); + + while (results.hasNext()) { + QuerySolution solution = results.next(); + + SemanticCloudDescription result = processQuerySolution(solution); + + if (result != null) { + descriptions.add(result); + } else { + failCount++; + } + } + + return descriptions; + } + + /** + * @return a String containing all the prefixes generated from the + * namespaces found in the active ontology + */ + private String generatePrefixes() { + Map namespaceMap = model.getNsPrefixMap(); + String prefixes = new String(); + + prefixes = namespaceMap.keySet().stream().map((key) -> "PREFIX " + key + + ": <" + namespaceMap.get(key) + ">" + + System.lineSeparator()).reduce(prefixes, String::concat); + + return prefixes; + } + + /** + * Maps an instance of {@code SemanticCloudDescription} to an instance of + * {@code QuerySolution}. + * + * @param solution is the instance of {@code QuerySolution} to be processed + * @return an instance of {@code SemanticCloudDescription} that corresponds + * to the parameter {@code QuerySolution} + */ + private SemanticCloudDescription processQuerySolution(QuerySolution solution) { + SemanticCloudDescription result; + Iterator varNameIt = solution.varNames(); + Individual individual = null; + Map attributes = new HashMap<>(); + PointCloud cloud = null; + + while (varNameIt.hasNext()) { + String varName = varNameIt.next(); + RDFNode node = solution.get(varName); + + individual = validateCloudResource(node); + + if (individual == null) { + if (node.isResource()) { + attributes.put(varName, ((Resource) node).getURI()); + } else { + attributes.put(varName, ((Literal) node).getLexicalForm()); + } + } else { + cloud = retrieveCloud(individual); + } + } + + if ((individual == null) || (cloud == null)) { + return null; + } + + result = new SemanticCloudDescription(individual, attributes, cloud); + + return result; + } + + /** + * Validates an RDF node as a point cloud resource by checking whether its + * local name contains {@code PointCloud}. + * + * @param node is the RDF node to be checked + * @return an individual if a node is a valid {@code PointCloud} resource, + * {@code null} otherwise + */ + private Individual validateCloudResource(RDFNode node) { + if (node.isResource()) { + String uri = node.asResource().getURI(); + Individual individual = model.getIndividual(uri); + + if (individual.getLocalName().contains("PointCloud")) { + return individual; + } + } + + return null; + } + + /** + * Retrieves a "physical" point cloud based on its semantic description. + * + * @param individual is an ontology individual that describes a point cloud + * @return the point cloud corresponding to a given ontology individual + */ + private PointCloud retrieveCloud(Individual individual) { + String localName = individual.getLocalName(); + String pathToCloud = path + "/clouds/" + localName + ".txt"; + CloudLoader loader = new CloudLoader(pathToCloud); + + try { + loader.load(); + } catch (IOException ex) { + return null; + } + + return loader.getCloud(); + } + +} diff --git a/src/main/java/com/movlad/semviz/core/sqm/SemanticCloudDescription.java b/src/main/java/com/movlad/semviz/core/sqm/SemanticCloudDescription.java new file mode 100644 index 0000000..1418ef9 --- /dev/null +++ b/src/main/java/com/movlad/semviz/core/sqm/SemanticCloudDescription.java @@ -0,0 +1,47 @@ +package com.movlad.semviz.core.sqm; + +import com.movlad.semviz.core.math.geometry.PointCloud; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import org.apache.jena.ontology.Individual; + +public class SemanticCloudDescription { + + private final Individual individual; + private final Map attributes; + private final PointCloud cloud; + + public SemanticCloudDescription(Individual individual, Map attributes, + PointCloud cloud) { + this.individual = individual; + this.attributes = attributes; + this.cloud = cloud; + } + + public Individual getIndividual() { + return individual; + } + + public String getAttribute(String key) { + return attributes.get(key); + } + + public Set attributeKeySet() { + return attributes.keySet(); + } + + public Collection attributeValues() { + return attributes.values(); + } + + public PointCloud getCloud() { + return cloud; + } + + @Override + public String toString() { + return individual.getLocalName(); + } + +} diff --git a/src/main/java/com/movlad/semviz/view/LoadingDialog.form b/src/main/java/com/movlad/semviz/view/LoadingDialog.form new file mode 100644 index 0000000..85abcc8 --- /dev/null +++ b/src/main/java/com/movlad/semviz/view/LoadingDialog.form @@ -0,0 +1,48 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/main/java/com/movlad/semviz/view/LoadingDialog.java b/src/main/java/com/movlad/semviz/view/LoadingDialog.java new file mode 100644 index 0000000..569052e --- /dev/null +++ b/src/main/java/com/movlad/semviz/view/LoadingDialog.java @@ -0,0 +1,54 @@ +package com.movlad.semviz.view; + +import javax.swing.JFrame; + +public final class LoadingDialog extends javax.swing.JDialog { + + /** + * @param parent is the parent frame + */ + public LoadingDialog(JFrame parent) { + super(parent); + + this.initComponents(); + this.setBounds(parent.getWidth() / 2, parent.getHeight() / 2, getWidth(), getHeight()); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + Label_Loading = new javax.swing.JLabel(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setUndecorated(true); + + Label_Loading.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); + Label_Loading.setText("Loading..."); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(Label_Loading, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 55, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(Label_Loading, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel Label_Loading; + // End of variables declaration//GEN-END:variables +} diff --git a/src/main/java/com/movlad/semviz/view/MainFrame.form b/src/main/java/com/movlad/semviz/view/MainFrame.form index 7efe0f4..f2086f9 100644 --- a/src/main/java/com/movlad/semviz/view/MainFrame.form +++ b/src/main/java/com/movlad/semviz/view/MainFrame.form @@ -126,26 +126,21 @@ - - - - - - - + + + + + - - - - + @@ -158,22 +153,14 @@ - - - - - - - - - - + - + + @@ -215,7 +202,7 @@ - + @@ -264,7 +251,7 @@ - + @@ -287,7 +274,7 @@ - + @@ -323,9 +310,9 @@ - + - + @@ -349,7 +336,7 @@ - + @@ -388,7 +375,7 @@ - + diff --git a/src/main/java/com/movlad/semviz/view/MainFrame.java b/src/main/java/com/movlad/semviz/view/MainFrame.java index af7d5aa..c547722 100644 --- a/src/main/java/com/movlad/semviz/view/MainFrame.java +++ b/src/main/java/com/movlad/semviz/view/MainFrame.java @@ -7,13 +7,10 @@ import com.jogamp.opengl.awt.GLJPanel; import com.movlad.semviz.application.CommandNavigationController; -import com.movlad.semviz.application.QueryManagerController; +import com.movlad.semviz.application.SQMController; import com.movlad.semviz.application.SceneController; -import com.movlad.semviz.application.SemanticCloudController; -import com.movlad.semviz.core.semantic.QueryManager; -import com.movlad.semviz.core.semantic.QueryResult; -import com.movlad.semviz.core.semantic.SemanticCloud; -import java.awt.Color; +import com.movlad.semviz.core.sqm.SQM; +import com.movlad.semviz.core.sqm.SemanticCloudDescription; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.KeyEvent; @@ -21,28 +18,29 @@ import java.awt.event.WindowEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.nio.file.Paths; import java.util.List; import javax.swing.DefaultListModel; import javax.swing.DefaultListSelectionModel; +import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JOptionPane; +import javax.swing.SwingWorker; import javax.swing.UIManager; import javax.swing.WindowConstants; import javax.swing.table.DefaultTableModel; public class MainFrame extends javax.swing.JFrame implements PropertyChangeListener { - private QueryManagerController queryManagerController; - private SemanticCloudController semanticCloudController; - private CommandNavigationController commandNavigationController; + private SQMController sqmController; + private CommandNavigationController navigationController; private SceneController sceneController; // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JComboBox ComboBox_GeometrySelection; private javax.swing.JLabel Label_GeometrySelection; private java.awt.Label Label_Individuals; - private javax.swing.JLabel Label_StatusLED; private javax.swing.JLabel Label_StatusText; private javax.swing.JLabel Label_VarInfo; private javax.swing.JList List_Individuals; @@ -86,10 +84,12 @@ public static void main(String args[]) { * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html */ - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (Exception e) { - JOptionPane.showMessageDialog(null, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); + if (System.getProperty("os.name").contains("Windows")) { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) { + JOptionPane.showMessageDialog(null, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); + } } // @@ -122,7 +122,6 @@ private void initComponents() { TextField_Command = new javax.swing.JTextField(); Panel_Control = new javax.swing.JPanel(); Panel_Status = new javax.swing.JPanel(); - Label_StatusLED = new javax.swing.JLabel(); Label_StatusText = new javax.swing.JLabel(); Panel_Individuals = new javax.swing.JPanel(); Label_Individuals = new java.awt.Label(); @@ -152,17 +151,14 @@ public void keyPressed(java.awt.event.KeyEvent evt) { Panel_Control.setBackground(new java.awt.Color(204, 204, 204)); - Label_StatusLED.setFont(new java.awt.Font("Arial", 0, 20)); // NOI18N - Label_StatusLED.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); - Label_StatusLED.setText("•"); - - Label_StatusText.setFont(new java.awt.Font("Arial", 0, 20)); // NOI18N - Label_StatusText.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + Label_StatusText.setFont(new java.awt.Font("Arial", 0, 14)); // NOI18N + Label_StatusText.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); Label_StatusText.setText("Ontology Model Loaded"); + Label_StatusText.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); Panel_Individuals.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); - Label_Individuals.setFont(new java.awt.Font("Dialog", 1, 12)); // NOI18N + Label_Individuals.setFont(new java.awt.Font("Arial", 1, 13)); // NOI18N Label_Individuals.setText("Cloud List"); List_Individuals.setModel(new javax.swing.AbstractListModel() { @@ -201,7 +197,7 @@ public void valueChanged(javax.swing.event.ListSelectionEvent evt) { .addContainerGap()) ); - Label_VarInfo.setFont(new java.awt.Font("Tahoma", 1, 13)); // NOI18N + Label_VarInfo.setFont(new java.awt.Font("Arial", 1, 13)); // NOI18N Label_VarInfo.setText("Queried Info"); Table_VarInfo.setModel(new javax.swing.table.DefaultTableModel( @@ -227,7 +223,7 @@ public void valueChanged(javax.swing.event.ListSelectionEvent evt) { .addComponent(Scroll_VarInfo, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) .addGroup(Panel_VarInfoLayout.createSequentialGroup() .addComponent(Label_VarInfo) - .addGap(0, 0, Short.MAX_VALUE))) + .addGap(0, 178, Short.MAX_VALUE))) .addContainerGap()) ); Panel_VarInfoLayout.setVerticalGroup( @@ -240,7 +236,7 @@ public void valueChanged(javax.swing.event.ListSelectionEvent evt) { .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); - Label_GeometrySelection.setFont(new java.awt.Font("Tahoma", 1, 13)); // NOI18N + Label_GeometrySelection.setFont(new java.awt.Font("Arial", 1, 13)); // NOI18N Label_GeometrySelection.setText("View"); ComboBox_GeometrySelection.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); @@ -257,9 +253,9 @@ public void itemStateChanged(java.awt.event.ItemEvent evt) { .addGroup(Panel_GeometrySelectionLayout.createSequentialGroup() .addContainerGap() .addGroup(Panel_GeometrySelectionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(ComboBox_GeometrySelection, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(ComboBox_GeometrySelection, 0, 252, Short.MAX_VALUE) .addGroup(Panel_GeometrySelectionLayout.createSequentialGroup() - .addComponent(Label_GeometrySelection) + .addComponent(Label_GeometrySelection, javax.swing.GroupLayout.PREFERRED_SIZE, 40, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 0, Short.MAX_VALUE))) .addContainerGap()) ); @@ -277,23 +273,19 @@ public void itemStateChanged(java.awt.event.ItemEvent evt) { Panel_Status.setLayout(Panel_StatusLayout); Panel_StatusLayout.setHorizontalGroup( Panel_StatusLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(Panel_StatusLayout.createSequentialGroup() - .addContainerGap() - .addComponent(Label_StatusLED, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(Label_StatusText, javax.swing.GroupLayout.PREFERRED_SIZE, 208, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(34, Short.MAX_VALUE)) .addComponent(Panel_Individuals, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(Panel_VarInfo, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(Panel_GeometrySelection, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, Panel_StatusLayout.createSequentialGroup() + .addContainerGap() + .addComponent(Label_StatusText, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addContainerGap()) ); Panel_StatusLayout.setVerticalGroup( Panel_StatusLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, Panel_StatusLayout.createSequentialGroup() .addGap(27, 27, 27) - .addGroup(Panel_StatusLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(Label_StatusLED, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(Label_StatusText, javax.swing.GroupLayout.PREFERRED_SIZE, 29, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(Label_StatusText, javax.swing.GroupLayout.PREFERRED_SIZE, 29, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(Panel_Individuals, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) @@ -320,7 +312,7 @@ public void itemStateChanged(java.awt.event.ItemEvent evt) { Panel_GLContainer.setLayout(Panel_GLContainerLayout); Panel_GLContainerLayout.setHorizontalGroup( Panel_GLContainerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 698, Short.MAX_VALUE) + .addGap(0, 706, Short.MAX_VALUE) ); Panel_GLContainerLayout.setVerticalGroup( Panel_GLContainerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -389,7 +381,7 @@ private void menuItem_OpenActionPerformed(java.awt.event.ActionEvent evt) {//GEN if (returnVal == JFileChooser.APPROVE_OPTION) { String path = fc.getSelectedFile().getAbsolutePath(); - queryManagerController.loadQueryManager(path); + sqmController.load(path); } }//GEN-LAST:event_menuItem_OpenActionPerformed @@ -401,17 +393,15 @@ private void TextField_CommandKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIR switch (evt.getKeyCode()) { case KeyEvent.VK_ENTER: String queryString = TextField_Command.getText(); - - commandNavigationController.enter(queryString); - queryManagerController.executeQuery(queryString); + navigationController.enter(queryString); break; case KeyEvent.VK_UP: - commandNavigationController.up(); + navigationController.up(); break; case KeyEvent.VK_DOWN: - commandNavigationController.down(); + navigationController.down(); break; } @@ -429,49 +419,22 @@ private void ComboBox_GeometrySelectionItemStateChanged(java.awt.event.ItemEvent private void List_IndividualsValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_List_IndividualsValueChanged int index = List_Individuals.getSelectedIndex(); - resetVarInfoTable(); - - DefaultTableModel model = (DefaultTableModel) Table_VarInfo.getModel(); - - if (index != -1) { - // An individual is selected - - /* - Filling in variable information table with the semantic attributes - decribing the selected individual. - */ - List queryResults = queryManagerController.getQueryResults(); - - queryResults.get(index).getKeys().forEach(key -> { - String[] row = new String[2]; - - row[0] = key; - row[1] = queryResults.get(index).getAttribute(key); - - model.addRow(row); - }); - - sceneController.setDisplaySelection(index); - ComboBox_GeometrySelection.setEnabled(true); - ComboBox_GeometrySelection.setSelectedIndex(sceneController.getSelectedViewIndex(index)); - } + sqmController.setSelectedDescriptionIndex(index); }//GEN-LAST:event_List_IndividualsValueChanged private void initControllers() { - queryManagerController = new QueryManagerController(); - semanticCloudController = new SemanticCloudController(); - commandNavigationController = new CommandNavigationController(); + sqmController = SQMController.getInstance(); + navigationController = new CommandNavigationController(); sceneController = new SceneController(); - queryManagerController.register(this); - semanticCloudController.register(this); - commandNavigationController.register(this); + sqmController.register(this); + navigationController.register(this); sceneController.register(this); } private void initStatusBar() { - Label_StatusLED.setForeground(Color.RED); - Label_StatusText.setText("Inactive"); + Label_StatusText.setText("No directory loaded."); + Label_StatusText.setToolTipText(""); } private void initIndividualsList() { @@ -554,53 +517,68 @@ public void propertyChange(PropertyChangeEvent evt) { JOptionPane.ERROR_MESSAGE); } - if (evt.getPropertyName().contains("CommandNavigation")) { - TextField_Command.setText((String) evt.getNewValue()); - } + pollSQMEvents(evt); + pollNavigationEvents(evt); + } - if (evt.getPropertyName().contains("QueryManagerLoad")) { + /** + * Checks whether an event comes from the {@code SQMController} and takes + * action accordingly. + * + * @param evt is a property change event + */ + private void pollSQMEvents(PropertyChangeEvent evt) { + if (evt.getPropertyName().contains("SQMLoad")) { resetInterface(); } - if (evt.getPropertyName().contains("QueryExecution")) { - onQueryExecution(evt); + if (evt.getPropertyName().contains("SQMExecution")) { + onSQMExecution(evt); } switch (evt.getPropertyName()) { - case "QueryManagerLoadSuccess": - onQueryManagerLoadSuccess(evt); + case "SQMLoadSuccess": + onSQMLoadSuccess(evt); + + break; + + case "SQMExecutionSuccess": + onSQMExecutionSuccess(evt); break; - case "QueryExecutionSuccess": - onQueryExecutionSuccess(evt); + case "SQMFailCountChanged": + onSQMFailCountChanged(evt); break; - case "SemanticCloudChange": - sceneController.loadDisplayInformation((SemanticCloud) evt.getNewValue()); + case "SQMDescriptionIndexChanged": + onSQMDescriptionIndexChanged(evt); break; } } /** - * Called when the query manager is successfully loaded. + * Checks whether an event is related to command navigation and takes action + * accordingly. + * + * @param evt is a property change event */ - private void onQueryManagerLoadSuccess(PropertyChangeEvent evt) { - Label_StatusLED.setForeground(Color.GREEN); - Label_StatusText.setText("Active"); - TextField_Command.setEnabled(true); - sceneController.resetDisplayInformation(); + private void pollNavigationEvents(PropertyChangeEvent evt) { + if (evt.getPropertyName().contains("CommandNavigation")) { + TextField_Command.setText((String) evt.getNewValue()); + } - JOptionPane.showMessageDialog(this, "Load complete.", "Info", - JOptionPane.INFORMATION_MESSAGE); + if (evt.getPropertyName().equals("CommandLaunch")) { + onCommandLaunch(evt); + } } /** - * Called whenever a query is executed. + * Called whenever a exec is executed. */ - private void onQueryExecution(PropertyChangeEvent evt) { + private void onSQMExecution(PropertyChangeEvent evt) { resetIndividualsList(); resetVarInfoTable(); resetGeometrySelectionComboBox(); @@ -608,20 +586,116 @@ private void onQueryExecution(PropertyChangeEvent evt) { } /** - * Called upon successful execution of a query. + * Called when the exec manager is successfully loaded. */ - private void onQueryExecutionSuccess(PropertyChangeEvent evt) { + private void onSQMLoadSuccess(PropertyChangeEvent evt) { + SQM sqm = (SQM) evt.getNewValue(); + String activeDirectoryPath = sqm.getPath(); + + Label_StatusText.setText("Active directory: " + + Paths.get(activeDirectoryPath).getFileName().toString()); + Label_StatusText.setToolTipText(activeDirectoryPath); + TextField_Command.setEnabled(true); + sceneController.resetDisplayInformation(); + + JOptionPane.showMessageDialog(this, "Load complete.", "Info", + JOptionPane.INFORMATION_MESSAGE); + } + + /** + * Called upon successful execution of a exec. + */ + private void onSQMExecutionSuccess(PropertyChangeEvent evt) { + List descriptions + = (List) evt.getNewValue(); + + sceneController.load(descriptions); + DefaultListModel listModel = (DefaultListModel) List_Individuals.getModel(); - List queryResults = (List) evt.getNewValue(); + descriptions.forEach(description -> { + listModel.addElement(description.toString()); + }); + } + + /** + * Called whenever the fail count of the Semviz Query Manager changes. + * + * @param evt is a property change event + */ + private void onSQMFailCountChanged(PropertyChangeEvent evt) { + int failCount = (int) evt.getNewValue(); + + if (failCount > 0) { + JOptionPane.showMessageDialog(this, "A number of " + failCount + " cloud " + + "individuals could not be retreived.", "Error", JOptionPane.ERROR_MESSAGE); + } + } + + /** + * Called whenever the selected description index is changed within the + * Semviz Query Manager controller. + * + * @param evt is a property change event + */ + private void onSQMDescriptionIndexChanged(PropertyChangeEvent evt) { + resetVarInfoTable(); + + int index = (int) evt.getNewValue(); + DefaultTableModel model = (DefaultTableModel) Table_VarInfo.getModel(); + + if (index != -1) { + // An individual is selected + + /* + Filling in variable information table with the semantic attributes + decribing the selected individual. + */ + sqmController.getSelectedDescription().attributeKeySet().forEach(key -> { + String[] row = new String[2]; + + row[0] = key; + row[1] = sqmController.getSelectedDescription().getAttribute(key); + + model.addRow(row); + }); + + sceneController.setDisplaySelection(index); + ComboBox_GeometrySelection.setEnabled(true); + ComboBox_GeometrySelection.setSelectedIndex(sceneController.getSelectedViewIndex(index)); + } + } - queryResults.forEach(result -> { - listModel.addElement(result.getIndividual().getLocalName()); + /** + * Called whenever a SPARQL query is launched. + * + * @param evt is a property change event + */ + private void onCommandLaunch(PropertyChangeEvent evt) { + String queryString = (String) evt.getNewValue(); + + SwingWorker swingWorker = new SwingWorker() { + @Override + protected Void doInBackground() throws Exception { + sqmController.exec(queryString); + + return null; + } + }; + + final JDialog dialog = new LoadingDialog(this); + + swingWorker.addPropertyChangeListener((PropertyChangeEvent evt1) -> { + if (evt1.getPropertyName().equals("state")) { + if (evt1.getNewValue() == SwingWorker.StateValue.DONE) { + dialog.dispose(); + } + } }); - QueryManager queryManager = queryManagerController.getQueryManager(); + swingWorker.execute(); - semanticCloudController.loadSuperCloud(queryManager, queryResults); + dialog.setVisible(true); } public void exit() { diff --git a/src/main/java/com/movlad/semviz/view/ViewerPanel.java b/src/main/java/com/movlad/semviz/view/ViewerPanel.java index c2601fe..3a5ffdd 100644 --- a/src/main/java/com/movlad/semviz/view/ViewerPanel.java +++ b/src/main/java/com/movlad/semviz/view/ViewerPanel.java @@ -5,18 +5,22 @@ import com.jogamp.opengl.GLProfile; import com.jogamp.opengl.awt.GLJPanel; import com.jogamp.opengl.util.Animator; +import com.movlad.semviz.application.SQMController; import com.movlad.semviz.application.SceneController; import com.movlad.semviz.core.graphics.Controls; import com.movlad.semviz.core.graphics.OrbitControls; import com.movlad.semviz.core.graphics.OrthographicCamera; import com.movlad.semviz.core.graphics.Renderer; +import com.movlad.semviz.core.graphics.Scene; import java.awt.Dimension; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import org.joml.Vector3f; /** * Interface component on which a scene is drawn. */ -public final class ViewerPanel extends GLJPanel { +public final class ViewerPanel extends GLJPanel implements PropertyChangeListener { private final OrthographicCamera camera; private final Controls controls; @@ -34,6 +38,11 @@ public ViewerPanel(Dimension d, SceneController sceneController) { super(new GLCapabilities(GLProfile.get(GLProfile.GL3))); setSize(d); + sceneController.register(this); + SQMController.getInstance().register(this); + + Scene scene = new Scene(); + camera = new OrthographicCamera(-getWidth() / 2, getWidth() / 2, -getHeight() / 2, getHeight() / 2, 0.1f, 1000.0f); @@ -49,7 +58,7 @@ public ViewerPanel(Dimension d, SceneController sceneController) { addMouseMotionListener(controls); addMouseWheelListener(controls); - renderer = new Renderer(sceneController.getScene(), camera) { + renderer = new Renderer() { @Override public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { @@ -60,6 +69,8 @@ public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height }; + renderer.setCamera(camera); + renderer.setScene(scene); addGLEventListener(renderer); animator = new Animator(this); @@ -67,4 +78,16 @@ public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height animator.start(); } + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals("SQMExecutionStarted")) { + animator.stop(); + } + + if (evt.getPropertyName().contains("Scene")) { + renderer.setScene((Scene) evt.getNewValue()); + animator.start(); + } + } + } diff --git a/src/main/resources/icons/icons8-reboot-100.png b/src/main/resources/icons/icons8-reboot-100.png new file mode 100644 index 0000000..7778b81 Binary files /dev/null and b/src/main/resources/icons/icons8-reboot-100.png differ