/* * ShootOFF - Software for Laser Dry Fire Training * Copyright (C) 2016 phrack * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.shootoff.gui.pane; import java.io.IOException; import java.util.Map; import java.util.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.shootoff.camera.perspective.PerspectiveManager; import com.shootoff.config.Configuration; import com.shootoff.config.ConfigurationException; import com.shootoff.targets.Target; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.geometry.Dimension2D; import javafx.geometry.Pos; import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; import javafx.scene.control.TextField; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.Pane; public class TargetDistancePane extends Pane { private final static Logger logger = LoggerFactory.getLogger(TargetDistancePane.class); private final Target target; private final PerspectiveManager perspectiveManager; private final Configuration config; private final String cameraName; private final String originalTargetDistance; private final String originalCameraDistance; private final TextField shooterDistance; private final TextField targetDistance; private final TextField cameraDistance; private final TextField targetWidth; private final TextField targetHeight; private boolean defaultsSet = false; public TargetDistancePane(Target target, PerspectiveManager perspectiveManager, Configuration config) { this.target = target; this.perspectiveManager = perspectiveManager; this.config = config; cameraName = perspectiveManager.getCalibratedCameraName(); final Image backgroundImage = new Image( TargetDistancePane.class.getResourceAsStream("/images/perspective_settings.png")); getChildren().add(new ImageView(backgroundImage)); shooterDistance = createDistanceTextField(234, 68); targetDistance = createDistanceTextField(534, 126); cameraDistance = createDistanceTextField(329, 252); targetWidth = createDistanceTextField(745, 48); targetHeight = createDistanceTextField(863, 191); getChildren().add(shooterDistance); getChildren().add(targetDistance); getChildren().add(cameraDistance); getChildren().add(targetWidth); getChildren().add(targetHeight); setDefaults(); originalCameraDistance = cameraDistance.getText(); originalTargetDistance = targetDistance.getText(); } private TextField createDistanceTextField(double x, double y) { final TextField distanceTextField = new TextField(); distanceTextField.setPromptText("(mm)"); distanceTextField.setLayoutX(x); distanceTextField.setLayoutY(y); distanceTextField.setPrefWidth(75); distanceTextField.setAlignment(Pos.CENTER); distanceTextField.textProperty().addListener(new NumberOnlyChangeListener(distanceTextField)); return distanceTextField; } private class NumberOnlyChangeListener implements ChangeListener<String> { private final TextField observedTextField; public NumberOnlyChangeListener(TextField observedTextField) { this.observedTextField = observedTextField; } @Override public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) { if (!defaultsSet) return; if (!newValue.matches("\\d*")) { observedTextField.setText(oldValue); observedTextField.positionCaret(observedTextField.getLength()); } else { setDistance(); } } } private void setDefaults() { if (target.tagExists(Target.TAG_DEFAULT_PERCEIVED_WIDTH)) { targetWidth.setText(target.getTag(Target.TAG_DEFAULT_PERCEIVED_WIDTH)); } if (target.tagExists(Target.TAG_DEFAULT_PERCEIVED_HEIGHT)) { targetHeight.setText(target.getTag(Target.TAG_DEFAULT_PERCEIVED_HEIGHT)); } if (target.tagExists(Target.TAG_CURRENT_PERCEIVED_DISTANCE)) { targetDistance.setText(target.getTag(Target.TAG_CURRENT_PERCEIVED_DISTANCE)); } else if (target.tagExists(Target.TAG_DEFAULT_PERCEIVED_DISTANCE)) { targetDistance.setText(target.getTag(Target.TAG_DEFAULT_PERCEIVED_DISTANCE)); } if (perspectiveManager.getCameraDistance() > 0) { cameraDistance.setText(String.valueOf(perspectiveManager.getCameraDistance())); } else if (config.getCameraDistance(cameraName).isPresent()) { cameraDistance.setText(String.valueOf(config.getCameraDistance(cameraName).get())); } if (!cameraDistance.getText().isEmpty() && !perspectiveManager.isInitialized()) { throw new AssertionError( "The perspective manager is not initialized. We should not have been " + "able to get here."); } if (target.tagExists(Target.TAG_SHOOTER_DISTANCE)) { shooterDistance.setText(target.getTag(Target.TAG_SHOOTER_DISTANCE)); } else if (!cameraDistance.getText().isEmpty()) { shooterDistance.setText(cameraDistance.getText()); } defaultsSet = true; } private void setDistance() { if (!validateDistanceData()) return; persistSettings(); final int width = Integer.parseInt(targetWidth.getText()); final int height = Integer.parseInt(targetHeight.getText()); final int distance = Integer.parseInt(targetDistance.getText()); if (logger.isTraceEnabled()) { logger.trace( "New target settings from distance settings pane: current width = {}, " + "default height = {}, default distance = {}, new distance = {}", width, height, originalTargetDistance, distance); } final Optional<Dimension2D> targetDimensions = perspectiveManager.calculateObjectSize(width, height, distance); if (targetDimensions.isPresent()) { final Dimension2D d = targetDimensions.get(); target.setDimensions(d.getWidth(), d.getHeight()); } } private boolean validateDistanceData() { boolean isValid = validateDistanceField(shooterDistance); isValid = validateDistanceField(targetDistance) && isValid; isValid = validateDistanceField(cameraDistance) && isValid; isValid = validateDistanceField(targetWidth) && isValid; isValid = validateDistanceField(targetHeight) && isValid; if (!isValid) return isValid; if ("0".equals(targetDistance.getText())) { final Alert invalidDataAlert = new Alert(AlertType.ERROR); final String message = "Target Distance cannot be 0, please set a value greater than 0."; invalidDataAlert.setTitle("Invalid Target Distance"); invalidDataAlert.setHeaderText("Target Distance Cannot Be Zero"); invalidDataAlert.setResizable(true); invalidDataAlert.setContentText(message); invalidDataAlert.initOwner(getScene().getWindow()); invalidDataAlert.showAndWait(); isValid = false; } return isValid; } private boolean validateDistanceField(TextField field) { boolean isValid = true; if (field.getText().isEmpty()) { isValid = false; field.setStyle("-fx-text-box-border: red; -fx-focus-color: red;"); } else { field.setStyle(""); } return isValid; } private void persistSettings() { final Map<String, String> tags = target.getAllTags(); if (!targetWidth.getText().isEmpty()) { tags.put(Target.TAG_DEFAULT_PERCEIVED_WIDTH, targetWidth.getText()); } if (!targetHeight.getText().isEmpty()) { tags.put(Target.TAG_DEFAULT_PERCEIVED_HEIGHT, targetHeight.getText()); } tags.put(Target.TAG_CURRENT_PERCEIVED_DISTANCE, targetDistance.getText()); tags.put(Target.TAG_SHOOTER_DISTANCE, shooterDistance.getText()); if (!originalCameraDistance.equals(cameraDistance.getText())) { config.setCameraDistance(cameraName, Integer.parseInt(cameraDistance.getText())); try { config.writeConfigurationFile(); } catch (ConfigurationException | IOException e) { logger.error("Failed to persist user-defined camera distance", e); } } perspectiveManager.setShooterDistance(Integer.parseInt(shooterDistance.getText())); perspectiveManager.setCameraDistance(Integer.parseInt(cameraDistance.getText())); } }