Commit d20721ff authored by Nico Mack's avatar Nico Mack

Re-implemented Calibration functionality.

parent d7176a92
......@@ -3,16 +3,14 @@
<screen>
<x1>0</x1>
<y1>0</y1>
<!-- <x2>800</x2>
<y2>650</y2> -->
<x2>1280</x2>
<y2>800</y2>
</screen>
<camera>
<x1>0.06699997</x1>
<y1>-0.013999984</y1>
<x2>0.92900026</x2>
<y2>1.0049983</y2>
<x1>0</x1>
<y1>0</y1>
<x2>1</x2>
<y2>1</y2>
</camera>
<table>
<width>1000</width>
......@@ -27,8 +25,6 @@
<rotationThreshold>0.2</rotationThreshold>
</table>
<window>
<!-- <x>1550</x>
<y>10</y> -->
<x>0</x>
<y>0</y>
<width>1280</width>
......
......@@ -27,6 +27,7 @@ import lu.list.itis.dkd.tui.content.Stage;
import lu.list.itis.dkd.tui.exception.BuildException;
import lu.list.itis.dkd.tui.utility.About;
import lu.list.itis.dkd.tui.utility.Calibration;
import lu.list.itis.dkd.tui.utility.CalibrationScreen;
import lu.list.itis.dkd.tui.utility.Externalization;
import lu.list.itis.dkd.tui.utility.SplashScreen;
import lu.list.itis.dkd.tui.utility.TulipPackage;
......@@ -41,18 +42,12 @@ import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Area;
import java.awt.geom.Line2D;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
......@@ -109,17 +104,17 @@ public class TangibleInterfaceManager extends Stage {
private String title;
/** Field indicating whether the interface is in calibration mode. */
protected boolean calibrationMode = false;
protected boolean calibrationShown;
/** Index pointer used to switch between different configuration points. */
protected int configurationPoint = 0;
private Area clippingRegion;
private Area fullScreenArea;
private SplashScreen splashScreen;
private CalibrationScreen calibrationScreen;
private String aboutHtml;
// ***************************************************************************
......@@ -152,6 +147,7 @@ public class TangibleInterfaceManager extends Stage {
this.interfaceProperties = properties;
this.bootstrap = bootstrap;
this.splashShown = false;
this.calibrationShown = false;
title = interfaceProperties.getProperty("frameTitle", "TangibleApplication"); //$NON-NLS-1$ //$NON-NLS-2$
transparent = Boolean.parseBoolean(interfaceProperties.getProperty(Externalization.TRANSPARENT_NODE, "False"));
......@@ -306,6 +302,29 @@ public class TangibleInterfaceManager extends Stage {
return (this.renderTemplate(aboutTemplate, aboutFields));
}
// ---------------------------------------------------------------------------
private void handleCalibrationScreen(CalibrationScreen.Action action) {
switch (action) {
case REVERT:
Calibration.loadFromFile(calibrationFileURI);
this.hideCalibrationScreen();
break;
case SAVE:
Calibration.saveToFile(calibrationFileURI);
this.hideCalibrationScreen();
break;
case NONE://$FALL-THROUGH$
case KEEP:
this.hideCalibrationScreen();
break;
default:
LOGGER.error("Unkown Calibration Screen Action encountered!"); //$NON-NLS-1$
}
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Class Body *
......@@ -335,6 +354,10 @@ public class TangibleInterfaceManager extends Stage {
super.setupWindow();
splashScreen = new SplashScreen(this.bounds.width, this.bounds.height);
splashScreen.setAbout(this.aboutHtml);
calibrationScreen = new CalibrationScreen(this.bounds.width, this.bounds.height);
calibrationScreen.setVisible(false);
window.getContentPane().add(calibrationScreen);
}
// ---------------------------------------------------------------------------
......@@ -360,6 +383,14 @@ public class TangibleInterfaceManager extends Stage {
showSplashScreen();
}
break;
case KeyEvent.VK_C:
if (calibrationShown) {
hideCalibrationScreen();
} else {
showCalibrationScreen();
}
break;
default:
break;
}
......@@ -398,6 +429,21 @@ public class TangibleInterfaceManager extends Stage {
splashShown = false;
}
// ---------------------------------------------------------------------------
protected void showCalibrationScreen() {
calibrationScreen.setVisible(true);
calibrationScreen.requestFocus();
calibrationShown = true;
}
// ---------------------------------------------------------------------------
protected void hideCalibrationScreen() {
calibrationScreen.setVisible(false);
calibrationShown = false;
}
// ---------------------------------------------------------------------------
/** Method used to destroy the current frame. */
// ---------------------------------------------------------------------------
......@@ -428,60 +474,6 @@ public class TangibleInterfaceManager extends Stage {
this.initAll();
}
// ---------------------------------------------------------------------------
/**
* Method used to set up a {@link JFrame} and all corresponding components to display a TUI
* calibration window. The method also sets up specific {@link KeyListener}s for the following keys:
* <ul>
* <li>Escape: exit calibration mode without storing changes;</li>
* <li>Enter: exit calibration mode after storing changes;</li>>
* <li>Backspace: reset the calibration window discarding all current changes;</li>
* <li>Left arrow: increase screen or camera size vertically;</li>
* <li>Up arrow: increase screen or camera size horizontally;</li>
* <li>Down arrow: decrease screen or camera size horizontally;</li>
* <li>Right arrow: decrease screen or camera size vertically;</li>
* <li>N: next configuration point.</li>
* </ul>
*/
// ---------------------------------------------------------------------------
public void setupCalibrationWindow() {
window = new JFrame();
window.setLayout(null);
window.add(this);
window.setTitle("Calibrating..."); //$NON-NLS-1$
window.setResizable(false);
window.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(@Nullable WindowEvent evt) {
System.exit(0);
}
});
// window.addKeyListener(new TuiConfigurationKeyAdapter());
if (fullscreen) {
int width = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
int height = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
setBounds();
window.setSize(width, height);
window.setUndecorated(true);
window.getContentPane().setBackground(Color.RED);
device.setFullScreenWindow(window);
} else {
setBounds();
window.getContentPane().setBackground(Color.RED);
window.setSize(this.bounds.width, this.bounds.height);
window.setUndecorated(true);
window.setLocation(Calibration.getTopLeftXCoordinate(), Calibration.getTopLeftYCoordinate());
}
window.setVisible(true);
window.repaint();
}
// ---------------------------------------------------------------------------
/**
* Method used to adjust the bounds of the {@link JComponent}.
......@@ -502,251 +494,15 @@ public class TangibleInterfaceManager extends Stage {
@Override
public void paint(@Nullable Graphics canvas) {
if (calibrationMode) {
updateCalibration(canvas);
} else {
update(canvas);
}
}
update(canvas);
// ---------------------------------------------------------------------------
/**
* This method is called when the application is in calibration mode. In this case the grey overlay
* of widgets is drawn as well as two rectangles showing the current calibration values:
*
* <ul>
* <li>A white rectangle (i.e., white fill) on a red background shows the area of the screen that is
* projected on the tabletop. This can be used to crop areas of the projector that are larger than
* the tabletop surface.</li>
* <li>A rectangle with a black border shows the outlines of the area seen by the camera. This can
* be modified to align the grey overlays with the physical objects.</li>
* </ul>
*
* @param canvas
* The graphics objects to be drawn onto.
*/
// ---------------------------------------------------------------------------
if (calibrationShown) {
calibrationScreen.repaint();
CalibrationScreen.Action action = calibrationScreen.getAction();
public void updateCalibration(Graphics canvas) {
Graphics2D canvas2D = (Graphics2D) canvas;
canvas2D.setColor(Color.white);
canvas2D.fillRect(0, 0, Calibration.getScreenWidth(), Calibration.getScreenHeight());
canvas2D.setColor(Color.black);
// canvas2D.draw(new Rectangle(Calibration.cameraToScreenX(0),
// Calibration.cameraToScreenY(0), Calibration.cameraToScreenX(1) -
// Calibration.cameraToScreenX(0), Calibration.cameraToScreenY(1) -
// Calibration.cameraToScreenY(0)));
// draw vertical lines
for (int i = 0; i < 9; i++) {
canvas2D.draw(new Line2D.Float(Calibration.cameraToScreenX(0) + i * (Calibration.cameraToScreenX(1) - Calibration.cameraToScreenX(0)) / 8, Calibration.cameraToScreenY(0),
Calibration.cameraToScreenX(0) + i * (Calibration.cameraToScreenX(1) - Calibration.cameraToScreenX(0)) / 8, Calibration.cameraToScreenY(1)));
}
// draw horizontal lines
for (int i = 0; i < 6; i++) {
canvas2D.draw(new Line2D.Float(Calibration.cameraToScreenX(0), Calibration.cameraToScreenY(0) + i * (Calibration.cameraToScreenY(1) - Calibration.cameraToScreenY(0)) / 6,
Calibration.cameraToScreenX(1), Calibration.cameraToScreenY(0) + i * (Calibration.cameraToScreenY(1) - Calibration.cameraToScreenY(0)) / 6));
if (action != CalibrationScreen.Action.NONE) {
this.handleCalibrationScreen(action);
}
}
}
// ---------------------------------------------------------------------------
/**
* Class extending a {@link KeyAdapter} to set the behaviour of key presses during regular execution
* of the TUI in the main window.
*
* @author Eric Tobias [eric.tobias@list.lu]
* @since 1.0
* @version 2.3.1
*/
// ---------------------------------------------------------------------------
// private final class TuiKeyAdapter extends KeyAdapter {
// /** Constructor */
// protected TuiKeyAdapter() {}
//
// @Override
// public void keyPressed(@Nullable KeyEvent event) {
// Preconditions.checkArgument(event != null, "The event is null!"); //$NON-NLS-1$
// switch (event.getKeyCode()) {
// case KeyEvent.VK_ESCAPE:
// System.exit(0);
// break;
// case KeyEvent.VK_F1:
// destroyWindow();
// setupWindow();
// fullscreen = !fullscreen;
// show();
// break;
// // case KeyEvent.VK_V:
// // toggleVerbose();
// // break;
// case KeyEvent.VK_R:
// reset();
// break;
// case KeyEvent.VK_C:
// destroyWindow();
// setupCalibrationWindow();
// calibrationMode = true;
// break;
// case KeyEvent.VK_A:
// if (splashShown) {
// hideSplashScreen();
// } else {
// showSplashScreen();
// }
// break;
// default:
// LOGGER.info("The keypress could not be associated to a case!"); //$NON-NLS-1$
// break;
// }
// }
// }
// ---------------------------------------------------------------------------
/**
* Class extending a {@link KeyAdapter} to set the behaviour of key presses during the configuration
* of the TUI.
*
* @author Eric Tobias [eric.tobias@list.lu]
* @since 1.0
* @version 2.3.1
*/
// ---------------------------------------------------------------------------
// private final class TuiConfigurationKeyAdapter extends KeyAdapter {
// /** Constructor. */
// public TuiConfigurationKeyAdapter() {}
//
// @Override
// public void keyPressed(@Nullable KeyEvent event) {
// Preconditions.checkArgument(event != null, "The event is null!"); //$NON-NLS-1$
// switch (event.getKeyCode()) {
// /** Exit calibration mode without storing changes. */
// case KeyEvent.VK_ESCAPE:
// destroyWindow();
// setupWindow();
// showWindow();
// calibrationMode = false;
// reset();
// break;
//
// /** Exit calibration mode after storing changes. */
// case KeyEvent.VK_ENTER:
// destroyWindow();
// Calibration.saveToFile(calibrationFileURI);
// setupWindow();
// showWindow();
// calibrationMode = false;
// reset();
// break;
//
// /** Reset the calibration window discarding all current changes. */
// case KeyEvent.VK_BACK_SPACE:
// configurationPoint = 0;
// Calibration.loadFromFile(calibrationFileURI);
// destroyWindow();
// setupCalibrationWindow();
// break;
//
// /** Increase screen or camera size vertically. */
// case KeyEvent.VK_LEFT:
// switch (configurationPoint) {
// case 0:
// Calibration.decreaseScreenLeft(1);
// break;
// case 1:
// Calibration.decreaseScreenRight(1);
// break;
// case 2:
// Calibration.decreaseCameraLeft(0.001f);
// break;
// case 3:
// Calibration.decreaseCameraRight(0.001f);
// break;
// default:
// LOGGER.warn("Keypress could not be mapped with current configurationPoint value!"); //$NON-NLS-1$
// break;
// }
// setBounds();
// break;
//
// /** Increase screen or camera size horizontally. */
// case KeyEvent.VK_UP:
// switch (configurationPoint) {
// case 0:
// Calibration.decreaseScreenTop(1);
// break;
// case 1:
// Calibration.decreaseScreenBottom(1);
// break;
// case 2:
// Calibration.decreaseCameraTop(0.001f);
// break;
// case 3:
// Calibration.decreaseCameraBottom(0.001f);
// break;
// default:
// LOGGER.warn("Keypress could not be mapped!"); //$NON-NLS-1$
// break;
// }
// setBounds();
// break;
//
// /** Decrease screen or camera size horizontally. */
// case KeyEvent.VK_DOWN:
// switch (configurationPoint) {
// case 0:
// Calibration.increaseScreenTop(1);
// break;
// case 1:
// Calibration.increaseScreenBottom(1);
// break;
// case 2:
// Calibration.increaseCameraTop(0.001f);
// break;
// case 3:
// Calibration.increaseCameraBottom(0.001f);
// break;
// default:
// LOGGER.warn("Keypress could not be mapped!"); //$NON-NLS-1$
// break;
// }
// setBounds();
// break;
//
// /** Decrease screen or camera size vertically. */
// case KeyEvent.VK_RIGHT:
// switch (configurationPoint) {
// case 0:
// Calibration.increaseScreenLeft(1);
// break;
// case 1:
// Calibration.increaseScreenRight(1);
// break;
// case 2:
// Calibration.increaseCameraLeft(0.001f);
// break;
// case 3:
// Calibration.increaseCameraRight(0.001f);
// break;
// default:
// LOGGER.warn("Keypress could not be mapped with current configurationPoint value!"); //$NON-NLS-1$
// break;
// }
// setBounds();
// break;
//
// /** Switch to next calibration point. */
// case KeyEvent.VK_N:
// configurationPoint++;
// if (configurationPoint > 3)
// configurationPoint = 0;
// break;
//
// default:
// LOGGER.warn("Keypress" + event.getKeyChar() + "is not mapped!"); //$NON-NLS-1$ //$NON-NLS-2$
// break;
// }
// }
// }
}
\ No newline at end of file
......@@ -354,22 +354,6 @@ public class Stage extends JComponent {
this.objects.put(objectId, widget);
}
// ---------------------------------------------------------------------------
/**
* @param identifier
* @param content
*/
// ---------------------------------------------------------------------------
// public void addToStage(String identifier, Content content) {
// Preconditions.checkArgument(content != null, "The passed object cannot be null!");
// //$NON-NLS-1$
// Preconditions.checkArgument(content.getStageId() == this.stageId, "The passed content {} is
// not intended to be added to stage with ID {}!", identifier, stageId); //$NON-NLS-1$
//
// this.contents.put(identifier, content);
// }
// ---------------------------------------------------------------------------
/**
* @param handleId
......@@ -473,30 +457,15 @@ public class Stage extends JComponent {
canvas2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
canvas2D.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
// long elapsed = System.currentTimeMillis();
List<Drawable> drawable = getContent(Drawable.class);
if (drawable != null) {
drawable.forEach(content -> content.paint(canvas2D));
}
// elapsed = System.currentTimeMillis() - elapsed;
// if (logger.isTraceEnabled())
// logger.trace("Drawing Content for Stage {} took {} ms!", this.name, elapsed);
// //$NON-NLS-1$
// elapsed = System.currentTimeMillis();
if (this.objects != null) {
objects.values().forEach(widget -> widget.paint(canvas2D));
}
// elapsed = System.currentTimeMillis() - elapsed;
// if (logger.isTraceEnabled())
// logger.trace("Drawing Objects for Stage {} took {} ms!", this.name, elapsed);
// //$NON-NLS-1$
}
}
/**
* Copyright Luxembourg Institute of Science and Technology, 2018. All rights reserved.
*
* This file is part of TULIP.
*
* TULIP is free software: you can redistribute it and/or modify it under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation, version 3 of the
* License.
*
* TULIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with TULIP. If
* not, see <http://www.gnu.org/licenses/lgpl-3.0.html>.
*/
package lu.list.itis.dkd.tui.utility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Line2D;
import javax.swing.JPanel;
/**
* @author mack
* @since [major].[minor]
* @version [major].[minor].[micro]
*/
// ***************************************************************************
// * Class Definition and Members *
// ***************************************************************************
public class CalibrationScreen extends JPanel implements KeyListener {
private Corner corner;
private CoordinateSystem system;
private Action action;
// ***************************************************************************
// * Constants *
// ***************************************************************************
public enum Action {
NONE, KEEP, REVERT, SAVE
}
private static final long serialVersionUID = 1L;
private static final Color SCREEN_COLOUR = new Color(255, 255, 255, 80);
private static final Color GRID_COLOUR = new Color(80, 80, 80, 255);
private static final int GRID_COLUMNS = 9;
private static final int GRID_ROWS = 7;
private static final float CAMERA_UNIT = 1e-3f;
private static final int SCREEN_UNIT = 1;
@SuppressWarnings("javadoc")
private enum Corner {
NONE, TOP_LEFT, BOTTOM_RIGHT
}
@SuppressWarnings("javadoc")
private enum CoordinateSystem {
NONE, SCREEN, CAMERA
}
@SuppressWarnings("javadoc")
private enum Horizontal {
NONE, LEFT, RIGHT
}
@SuppressWarnings("javadoc")
private enum Vertical {
NONE, UP, DOWN
}
private static final Logger LOGGER = LoggerFactory.getLogger(CalibrationScreen.class.getSimpleName());
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s)
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* @param screenWidth
* @param screenHeight
*/
// ---------------------------------------------------------------------------
public CalibrationScreen(int screenWidth, int screenHeight) {
this.setSize(new Dimension(screenWidth, screenHeight));
this.setPreferredSize(new Dimension(screenWidth, screenHeight));
this.setOpaque(false);
this.addKeyListener(this);
this.corner = Corner.NONE;
this.system = CoordinateSystem.NONE;
this.action = Action.NONE;
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Primitives(s)
// ***************************************************************************
// ---------------------------------------------------------------------------
private void nudgeCameraCorner(Corner cameraCorner, Horizontal horizontal, Vertical vertical) {
float step = CAMERA_UNIT;
switch (cameraCorner) {
case NONE:
LOGGER.warn("No Corner selected!"); //$NON-NLS-1$
break;
case TOP_LEFT:
if (horizontal == Horizontal.LEFT) {
Calibration.decreaseCameraLeft(step);
} else if (horizontal == Horizontal.RIGHT) {
Calibration.increaseCameraLeft(step);
}
if (vertical == Vertical.UP) {
Calibration.decreaseCameraTop(step);
} else if (vertical == Vertical.DOWN) {
Calibration.increaseCameraTop(step);
}
break;
case BOTTOM_RIGHT:
if (horizontal == Horizontal.LEFT) {
Calibration.decreaseCameraRight(step);