Skip to content

Commit

Permalink
Refactor bounding shape drawing (#103)
Browse files Browse the repository at this point in the history
* Refactor bounding shape drawing.

* Add license headers.
  • Loading branch information
mfl28 committed Nov 10, 2023
1 parent 10520bc commit ea7d111
Show file tree
Hide file tree
Showing 9 changed files with 454 additions and 216 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -201,15 +201,8 @@ public Controller(final Stage mainStage, final MainView view, final HostServices
this.view = view;
this.hostServices = hostServices;

stage.setTitle(PROGRAM_IDENTIFIER);
stage.getIcons().add(MainView.APPLICATION_ICON);
stage.setOnCloseRequest(event -> {
onRegisterExitAction();
event.consume();
});

setupStage();
loadPreferences();

view.connectToController(this);
setUpModelListeners();
setUpServices();
Expand Down Expand Up @@ -463,6 +456,8 @@ public void onRegisterAddObjectCategoryAction() {
* Handles the event of the user requesting to exit the application.
*/
public void onRegisterExitAction() {
view.getEditorImagePane().finalizeBoundingShapeDrawing();

updateModelFromView();

if(!model.isSaved()) {
Expand Down Expand Up @@ -608,14 +603,10 @@ public void onRegisterImageViewMouseReleasedEvent(MouseEvent event) {
view.getEditorImageView().setCursor(Cursor.OPEN_HAND);
}

if(view.getObjectCategoryTable().isCategorySelected()) {
if(imagePane.getDrawingMode() == EditorImagePaneView.DrawingMode.BOX
&& imagePane.isBoundingBoxDrawingInProgress()) {
imagePane.finalizeBoundingBox();
} else if(imagePane.getDrawingMode() == EditorImagePaneView.DrawingMode.FREEHAND
&& imagePane.isFreehandDrawingInProgress()) {
imagePane.finalizeFreehandShape();
}
if(view.getObjectCategoryTable().isCategorySelected() &&
(Objects.equals(imagePane.getCurrentBoundingShapeDrawingMode(), EditorImagePaneView.DrawingMode.BOX) ||
Objects.equals(imagePane.getCurrentBoundingShapeDrawingMode(), EditorImagePaneView.DrawingMode.FREEHAND))) {
imagePane.finalizeBoundingShapeDrawing();
}
}
}
Expand All @@ -632,16 +623,15 @@ public void onRegisterImageViewMousePressedEvent(MouseEvent event) {
&& !event.isShortcutDown()
&& imagePaneView.isCategorySelected()) {
if(event.getButton().equals(MouseButton.PRIMARY)) {
if(imagePaneView.getDrawingMode() == EditorImagePaneView.DrawingMode.BOX) {
imagePaneView.initializeBoundingRectangle(event);
} else if(imagePaneView.getDrawingMode() == EditorImagePaneView.DrawingMode.POLYGON) {
imagePaneView.initializeBoundingPolygon(event);
} else if(imagePaneView.getDrawingMode() == EditorImagePaneView.DrawingMode.FREEHAND) {
imagePaneView.initializeBoundingFreehandShape(event);
if(!imagePaneView.isDrawingInProgress()) {
imagePaneView.initializeBoundingShapeDrawing(event);
} else {
imagePaneView.updateBoundingShapeDrawing(event);
}
} else if(event.getButton().equals(MouseButton.SECONDARY)
&& imagePaneView.getDrawingMode() == EditorImagePaneView.DrawingMode.POLYGON) {
imagePaneView.setBoundingPolygonsEditingAndConstructing(false);
&& Objects.equals(imagePaneView.getCurrentBoundingShapeDrawingMode(),
EditorImagePaneView.DrawingMode.POLYGON)) {
imagePaneView.finalizeBoundingShapeDrawing();
}
}
}
Expand Down Expand Up @@ -1598,6 +1588,21 @@ private void setCurrentAnnotationLoadingDirectory(File source) {
}
}

private void setupStage() {
stage.setTitle(PROGRAM_IDENTIFIER);
stage.getIcons().add(MainView.APPLICATION_ICON);
stage.setOnCloseRequest(event -> {
onRegisterExitAction();
event.consume();
});

stage.addEventFilter(MouseEvent.MOUSE_PRESSED, event -> {
if (event.getTarget() != view.getEditorImageView()) {
view.getEditorImagePane().finalizeBoundingShapeDrawing();
}
});
}

/**
* Class containing possible key-combinations.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright (C) 2023 Markus Fleischhacker <markus.fleischhacker28@gmail.com>
*
* This file is part of Bounding Box Editor
*
* Bounding Box Editor 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.
*
* Bounding Box Editor 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 Bounding Box Editor. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.mfl28.boundingboxeditor.ui;

import com.github.mfl28.boundingboxeditor.model.data.ObjectCategory;
import com.github.mfl28.boundingboxeditor.utils.MathUtils;
import javafx.geometry.Point2D;
import javafx.scene.control.ToggleGroup;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;

import java.util.List;


public class BoundingBoxDrawer implements BoundingShapeDrawer {
private final ImageView imageView;
private final ToggleGroup toggleGroup;
private final List<BoundingShapeViewable> boundingShapes;

private BoundingBoxView boundingBoxView;

private boolean drawingInProgress = false;

public BoundingBoxDrawer(ImageView imageView, ToggleGroup toggleGroup, List<BoundingShapeViewable> boundingShapes) {
this.imageView = imageView;
this.toggleGroup = toggleGroup;
this.boundingShapes = boundingShapes;
}

@Override
public void initializeShape(MouseEvent event, ObjectCategory objectCategory) {
if (event.getEventType().equals(MouseEvent.MOUSE_PRESSED) && event.getButton().equals(MouseButton.PRIMARY)) {
Point2D parentCoordinates = imageView.localToParent(event.getX(), event.getY());

boundingBoxView = new BoundingBoxView(objectCategory);
boundingBoxView.getConstructionAnchorLocal().setFromMouseEvent(event);
boundingBoxView.setToggleGroup(toggleGroup);

boundingBoxView.setX(parentCoordinates.getX());
boundingBoxView.setY(parentCoordinates.getY());
boundingBoxView.setWidth(0);
boundingBoxView.setHeight(0);

boundingShapes.add(boundingBoxView);

boundingBoxView.autoScaleWithBounds(imageView.boundsInParentProperty());
toggleGroup.selectToggle(boundingBoxView);

drawingInProgress = true;
}
}

@Override
public void updateShape(MouseEvent event) {
if (event.getEventType().equals(MouseEvent.MOUSE_DRAGGED) && event.getButton().equals(MouseButton.PRIMARY)) {
final Point2D clampedEventXY =
MathUtils.clampWithinBounds(event.getX(), event.getY(), imageView.getBoundsInLocal());

DragAnchor constructionAnchor = boundingBoxView.getConstructionAnchorLocal();
Point2D parentCoordinates =
imageView.localToParent(Math.min(clampedEventXY.getX(), constructionAnchor.getX()),
Math.min(clampedEventXY.getY(), constructionAnchor.getY()));

boundingBoxView.setX(parentCoordinates.getX());
boundingBoxView.setY(parentCoordinates.getY());
boundingBoxView.setWidth(Math.abs(clampedEventXY.getX() - constructionAnchor.getX()));
boundingBoxView.setHeight(Math.abs(clampedEventXY.getY() - constructionAnchor.getY()));
}
}

@Override
public void finalizeShape() {
drawingInProgress = false;
}

@Override
public boolean isDrawingInProgress() {
return drawingInProgress;
}

@Override
public EditorImagePaneView.DrawingMode getDrawingMode() {
return EditorImagePaneView.DrawingMode.BOX;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* Copyright (C) 2023 Markus Fleischhacker <markus.fleischhacker28@gmail.com>
*
* This file is part of Bounding Box Editor
*
* Bounding Box Editor 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.
*
* Bounding Box Editor 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 Bounding Box Editor. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.mfl28.boundingboxeditor.ui;

import com.github.mfl28.boundingboxeditor.model.data.ObjectCategory;
import com.github.mfl28.boundingboxeditor.utils.MathUtils;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.geometry.Point2D;
import javafx.scene.control.ToggleGroup;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.shape.ClosePath;

import java.util.List;

public class BoundingFreeHandShapeDrawer implements BoundingShapeDrawer {

private final ImageView imageView;
private final ToggleGroup toggleGroup;
private final List<BoundingShapeViewable> boundingShapes;
private final BooleanProperty autoSimplify;
private final DoubleProperty simplifyRelativeDistanceTolerance;
private boolean drawingInProgress = false;
private BoundingFreehandShapeView boundingFreehandShapeView;

public BoundingFreeHandShapeDrawer(ImageView imageView, ToggleGroup toggleGroup, List<BoundingShapeViewable> boundingShapes,
BooleanProperty autoSimplify, DoubleProperty simplifyRelativeDistanceTolerance) {
this.imageView = imageView;
this.toggleGroup = toggleGroup;
this.boundingShapes = boundingShapes;
this.autoSimplify = autoSimplify;
this.simplifyRelativeDistanceTolerance = simplifyRelativeDistanceTolerance;
}

@Override
public void initializeShape(MouseEvent event, ObjectCategory objectCategory) {
if (event.getEventType().equals(MouseEvent.MOUSE_PRESSED) && event.getButton().equals(MouseButton.PRIMARY)) {
Point2D parentCoordinates = imageView.localToParent(event.getX(), event.getY());

boundingFreehandShapeView = new BoundingFreehandShapeView(objectCategory);
boundingFreehandShapeView.setToggleGroup(toggleGroup);

boundingShapes.add(boundingFreehandShapeView);

boundingFreehandShapeView.autoScaleWithBounds(imageView.boundsInParentProperty());

boundingFreehandShapeView.setVisible(true);
toggleGroup.selectToggle(boundingFreehandShapeView);
boundingFreehandShapeView.addMoveTo(parentCoordinates.getX(), parentCoordinates.getY());
drawingInProgress = true;
}
}

@Override
public void updateShape(MouseEvent event) {
if (event.getEventType().equals(MouseEvent.MOUSE_DRAGGED) && event.getButton().equals(MouseButton.PRIMARY)) {
final Point2D clampedEventXY =
MathUtils.clampWithinBounds(event.getX(), event.getY(), imageView.getBoundsInLocal());

Point2D parentCoordinates =
imageView.localToParent(clampedEventXY.getX(), clampedEventXY.getY());

boundingFreehandShapeView.addLineTo(parentCoordinates.getX(), parentCoordinates.getY());
}
}

@Override
public void finalizeShape() {
boundingFreehandShapeView.getElements().add(new ClosePath());

BoundingPolygonView boundingPolygonView = new BoundingPolygonView(
boundingFreehandShapeView.getViewData().getObjectCategory());

final List<Double> pointsInImage = boundingFreehandShapeView.getPointsInImage();

boundingPolygonView.setEditing(true);

for(int i = 0; i < pointsInImage.size(); i += 2) {
boundingPolygonView.appendNode(pointsInImage.get(i), pointsInImage.get(i + 1));
}

if(autoSimplify.get()) {
boundingPolygonView.simplify(simplifyRelativeDistanceTolerance.get(),
boundingFreehandShapeView.getViewData().autoScaleBounds().getValue());
}

boundingPolygonView.setToggleGroup(toggleGroup);

boundingShapes.remove(boundingFreehandShapeView);

ObjectCategoryTreeItem parentTreeItem = (ObjectCategoryTreeItem) boundingFreehandShapeView.getViewData()
.getTreeItem().getParent();
parentTreeItem.detachBoundingShapeTreeItemChild(boundingFreehandShapeView.getViewData().getTreeItem());

if(parentTreeItem.getChildren().isEmpty()) {
parentTreeItem.getParent().getChildren().remove(parentTreeItem);
}

boundingShapes.add(boundingPolygonView);

boundingPolygonView.autoScaleWithBounds(imageView.boundsInParentProperty());
boundingPolygonView.setVisible(true);
toggleGroup.selectToggle(boundingPolygonView);

boundingPolygonView.setConstructing(false);
boundingPolygonView.setEditing(false);

drawingInProgress = false;
}

@Override
public boolean isDrawingInProgress() {
return drawingInProgress;
}

@Override
public EditorImagePaneView.DrawingMode getDrawingMode() {
return EditorImagePaneView.DrawingMode.FREEHAND;
}
}
Loading

0 comments on commit ea7d111

Please sign in to comment.