Skip to content

Commit

Permalink
Add compass
Browse files Browse the repository at this point in the history
  • Loading branch information
rolandlo committed Oct 15, 2022
1 parent f3da0e4 commit bb51258
Show file tree
Hide file tree
Showing 17 changed files with 899 additions and 9 deletions.
80 changes: 80 additions & 0 deletions src/core/control/CompassController.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#include "CompassController.h"

#include "control/Control.h"
#include "control/layer/LayerController.h"
#include "gui/XournalView.h"
#include "model/Compass.h"
#include "model/GeometryTool.h"
#include "model/Stroke.h"
#include "model/XojPage.h"

CompassController::CompassController(XojPageView* view, Compass* s): GeometryToolController(view, s) {}

CompassController::~CompassController() = default;

auto CompassController::getType() const -> std::string { return "compass"; }

auto CompassController::posRelToSide(double x, double y) const -> utl::Point<double> {
cairo_matrix_t inv = s->getMatrix();
cairo_matrix_invert(&inv);
cairo_matrix_transform_point(&inv, &x, &y);
return utl::Point<double>(x, -y);
}

auto CompassController::isInsideGeometryTool(double x, double y, double border) const -> bool {
const auto p = posRelToSide(x, y);
return std::hypot(p.x, p.y) <= s->getHeight() + border;
}

auto CompassController::getPointForPos(double a) const -> utl::Point<double> {
cairo_matrix_t matrix = s->getMatrix();
double x = s->getHeight() * std::cos(a);
double y = s->getHeight() * std::sin(a);
cairo_matrix_transform_point(&matrix, &x, &y);

return utl::Point<double>(x, y);
}

void CompassController::createStroke(double a) {
if (!std::isnan(a)) {
angleMax = a;
angleMin = a;

const auto p = this->getPointForPos(a);
initializeStroke();
stroke->addPoint(Point(p.x, p.y));
stroke->addPoint(Point(p.x, p.y)); // doubled point
s->notify();
} else {
g_warning("No valid stroke from compass!");
}
}

void CompassController::updateStroke(double x) {
angleMax = std::max(this->angleMax, x);
angleMin = std::min(this->angleMin, x);
stroke->deletePointsFrom(0);
const auto h = view->getXournal()->getControl()->getToolHandler();
const auto filled = (h->getFill() != -1);
const auto c = utl::Point{s->getTranslationX(), s->getTranslationY()};

if (filled && angleMax < angleMin + 2 * M_PI) {
stroke->addPoint(Point(c.x, c.y));
}
for (auto i = 0; i <= 100; i++) {
auto p = getPointForPos(angleMin + static_cast<double>(i) / 100.0 * (angleMax - angleMin));
stroke->addPoint(Point(p.x, p.y));
}
if (filled && angleMax < angleMin + 2 * M_PI) {
stroke->addPoint(Point(c.x, c.y));
}
s->notify();
}

void CompassController::finalizeStroke() {
angleMax = NAN;
angleMin = NAN;
addStrokeToLayer();
}

auto CompassController::existsStroke() -> bool { return !std::isnan(angleMax) && !std::isnan(angleMin); }
97 changes: 97 additions & 0 deletions src/core/control/CompassController.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Xournal++
*
* A Compass controller
*
* @author Xournal++ Team
* https://github.com/xournalpp/xournalpp
*
* @license GNU GPLv2 or later
*/

#pragma once

#include <gtk/gtk.h>

#include "gui/PageView.h"
#include "util/Point.h"

#include "GeometryToolController.h"

class Compass;
class Stroke;

/**
* @brief A class that controls a compass
* The compass can be moved, rotated and scaled
* There are methods for translating coordinates
* and methods to deal with the temporary stroke
* that is displayed near the the outline of the
* compass or a radius.
*/

class CompassController: public GeometryToolController {
public:
CompassController(XojPageView* view, Compass* s);
~CompassController();

public:
std::string getType() const override;

/**
* @brief returns the position of a point relative to a coordinate system, in which the
* positive x-axis coincides with the distinguished compass axis and the origin lies
* in the center of the compass
* @param x the x-coordinate of the point (in document coordinates)
* @param y the y-coordinate of the point (in document coordinates)
*/
utl::Point<double> posRelToSide(double x, double y) const;

/**
* @brief checks whether a point with given coordinates lies in the geometry tool with an additional
* border enlarging (or shrinked) it
* @param x the x-coordinate of the given point (in document coordinates)
* @param y the y-coordinate of the given point (in document coordinates)
* @param border the size of the border (if negative, the geometry tool is shrinked via the border)
*/
bool isInsideGeometryTool(double x, double y, double border = 0.0) const override;

/**
* @brief the point (in document coordinates) for a given angle on the outline of the setsquare
* @param a the angle with respect to the distinguished compass axis of the point
*/
utl::Point<double> getPointForPos(double a) const;

/**
* @brief creates a stroke starting at the given angle of the outline of the compass
* @param a the angle of the point on the outline of the compass (when unrotated and untranslated)
*/
void createStroke(double a);

/**
* @brief updates the stroke aligned to the outline of the compass
* @param a the angle of the point on the outline of the compass (when unrotated and untranslated)
* updating the stroke
*/
void updateStroke(double a);

/**
* @brief finishes the stroke aligned to the outline of the compass
*/
void finalizeStroke();

/**
* checks whether a stroke already exists
*/
bool existsStroke();

private:
/**
* @brief when a stroke near the outline of the compass is drawn, the minimal and maximal
* angles of the point to be drawn (with respect to an unrotated, and untranslated coordinate system)
* are saved in the variables angleMin and angleMax
*/
double angleMax = NAN;
double angleMin = NAN;
};
13 changes: 13 additions & 0 deletions src/core/control/Control.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
#include "gui/toolbarMenubar/ToolMenuHandler.h" // for Tool...
#include "gui/toolbarMenubar/model/ToolbarData.h" // for Tool...
#include "gui/toolbarMenubar/model/ToolbarModel.h" // for Tool...
#include "model/Compass.h" // for Sets...
#include "model/Document.h" // for Docu...
#include "model/DocumentChangeType.h" // for DOCU...
#include "model/Element.h" // for Element
Expand Down Expand Up @@ -85,6 +86,7 @@
#include "util/i18n.h" // for _, FS
#include "util/serializing/InputStreamException.h" // for Inpu...
#include "util/serializing/ObjectInputStream.h" // for Obje...
#include "view/CompassView.h" // for Sets...
#include "view/SetsquareView.h" // for Sets...
#include "view/overlays/OverlayView.h"

Expand Down Expand Up @@ -643,6 +645,17 @@ void Control::actionPerformed(ActionType type, ActionGroup group, GdkEvent* even
xournal->getViewFor(getCurrentPageNo())->rerenderPage();
}
break;
case ACTION_COMPASS:
if (auto xournal = this->win->getXournal();
!xournal->getGeometryToolController() || xournal->getGeometryToolController()->getType() != "compass") {
xournal->resetGeometryTool();
xournal->makeGeometryTool("compass");
xournal->getViewFor(getCurrentPageNo())->rerenderPage();
} else {
xournal->resetGeometryTool();
xournal->getViewFor(getCurrentPageNo())->rerenderPage();
}
break;
case ACTION_TOOL_FLOATING_TOOLBOX:
if (enabled) {
selectTool(TOOL_FLOATING_TOOLBOX);
Expand Down
1 change: 1 addition & 0 deletions src/core/enums/ActionType.enum.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ enum ActionType {
ACTION_GRID_SNAPPING,
ACTION_HIGHLIGHT_POSITION,
ACTION_SETSQUARE,
ACTION_COMPASS,

// Used for all colors
ACTION_SELECT_COLOR,
Expand Down
8 changes: 8 additions & 0 deletions src/core/enums/generated/ActionType.generated.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,10 @@ auto ActionType_fromString(const string& value) -> ActionType {
return ACTION_SETSQUARE;
}

if (value == "ACTION_COMPASS") {
return ACTION_COMPASS;
}

if (value == "ACTION_SELECT_COLOR") {
return ACTION_SELECT_COLOR;
}
Expand Down Expand Up @@ -1058,6 +1062,10 @@ auto ActionType_toString(ActionType value) -> string {
return "ACTION_SETSQUARE";
}

if (value == ACTION_COMPASS) {
return "ACTION_COMPASS";
}

if (value == ACTION_SELECT_COLOR) {
return "ACTION_SELECT_COLOR";
}
Expand Down
19 changes: 15 additions & 4 deletions src/core/gui/XournalView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
#include <gdk/gdkkeysyms.h> // for GDK_KEY_Page_Down
#include <glib-object.h> // for g_object_ref_sink

#include "control/Control.h" // for Control
#include "control/PdfCache.h" // for PdfCache
#include "control/ScrollHandler.h" // for ScrollHandler
#include "control/SetsquareController.h"
#include "control/CompassController.h" // for CompassController
#include "control/Control.h" // for Control
#include "control/PdfCache.h" // for PdfCache
#include "control/ScrollHandler.h" // for ScrollHandler
#include "control/SetsquareController.h" // for SetsquareController
#include "control/ToolHandler.h" // for ToolHandler
#include "control/jobs/XournalScheduler.h" // for XournalScheduler
#include "control/settings/MetadataManager.h" // for MetadataManager
Expand All @@ -25,6 +26,7 @@
#include "enums/ActionType.enum.h" // for ACTION_NONE
#include "gui/MainWindow.h" // for MainWindow
#include "gui/PdfFloatingToolbox.h" // for PdfFloatingToolbox
#include "gui/inputdevices/CompassInputHandler.h" // for CompassInputHandler
#include "gui/inputdevices/HandRecognition.h" // for HandRecognition
#include "gui/inputdevices/InputContext.h" // for InputContext
#include "gui/inputdevices/SetsquareInputHandler.h" // for SetsquareInputHandler
Expand All @@ -39,6 +41,7 @@
#include "util/Point.h" // for Point
#include "util/Rectangle.h" // for Rectangle
#include "util/Util.h" // for npos
#include "view/CompassView.h" // for CompassView
#include "view/SetsquareView.h" // for SetsquareView

#include "Layout.h" // for Layout
Expand Down Expand Up @@ -698,6 +701,14 @@ void XournalView::makeGeometryTool(std::string tool) {
this->geometryToolHandler = new SetsquareInputHandler(this, this->geometryToolController);
geometryToolHandler->registerToPool(setsquare->getHandlerPool());
control->fireActionSelected(GROUP_GEOMETRY_TOOL, ACTION_SETSQUARE);
} else if (tool == "compass") {
auto compass = new Compass();
view->addOverlayView(std::make_unique<xoj::view::CompassView>(compass, view));
this->geometryTool = compass;
this->geometryToolController = new CompassController(view, compass);
this->geometryToolHandler = new CompassInputHandler(this, geometryToolController);
geometryToolHandler->registerToPool(compass->getHandlerPool());
control->fireActionSelected(GROUP_GEOMETRY_TOOL, ACTION_COMPASS);
}
}

Expand Down
80 changes: 80 additions & 0 deletions src/core/gui/inputdevices/CompassInputHandler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#include "CompassInputHandler.h"

#include <memory> // for __shared_ptr_access, shar...

#include "control/CompassController.h" // for CompassController, HYPOTENUSE
#include "control/Control.h"
#include "control/ToolEnums.h" // for TOOL_HAND, TOOL_HIGHLIGHTER
#include "control/ToolHandler.h" // for ToolHandler
#include "gui/XournalView.h" // for XournalView
#include "gui/inputdevices/InputEvents.h"
#include "model/Compass.h"

constexpr double MIN_HEIGHT = 1.0;
constexpr double MAX_HEIGHT = 10.0;

CompassInputHandler::CompassInputHandler(XournalView* xournal, GeometryToolController* controller):
GeometryToolHandler(xournal, controller, Compass::INITIAL_HEIGHT, Compass::INITIAL_X, Compass::INITIAL_Y) {}

CompassInputHandler::~CompassInputHandler() noexcept { this->unregisterFromPool(); }

auto CompassInputHandler::handlePointer(InputEvent const& event) -> bool {
const auto coords = getCoords(event);
CompassController* compassController = static_cast<CompassController*>(controller);

const auto toolHandler = xournal->getControl()->getToolHandler();
switch (toolHandler->getToolType()) {
case TOOL_HIGHLIGHTER:
case TOOL_PEN:
if (event.type == BUTTON_PRESS_EVENT) {
if (controller->isInsideGeometryTool(coords.x, coords.y, 0) &&
!controller->isInsideGeometryTool(coords.x, coords.y, -0.5)) {
// initialize range
const auto p = compassController->posRelToSide(coords.x, coords.y);
lastProj = std::atan2(-p.y, p.x);
compassController->createStroke(lastProj);
return true;
}
return false;
} else if (event.type == MOTION_EVENT) {
// update range and paint
if (compassController->existsStroke()) {
const auto p = compassController->posRelToSide(coords.x, coords.y);
auto proj = std::atan2(-p.y, p.x);
proj = lastProj + std::remainder(proj - lastProj, 2 * M_PI);
compassController->updateStroke(proj);
lastProj = proj;
return true;
}
return false;
} else if (event.type == BUTTON_RELEASE_EVENT) {
// add stroke to layer and reset
if (compassController->existsStroke()) {
compassController->finalizeStroke();
lastProj = NAN;
return true;
}
}
return false;
case TOOL_HAND:
if (event.type == BUTTON_PRESS_EVENT) {
if (!controller->isInsideGeometryTool(coords.x, coords.y, 0)) {
return false;
} else {
sequenceStart(event);
this->handScrolling = true;
return true;
}
} else if (event.type == MOTION_EVENT && this->handScrolling) {
scrollMotion(event);
return true;
} else if (event.type == BUTTON_RELEASE_EVENT && this->handScrolling) {
this->handScrolling = false;
}
default:
return false;
}
}

auto CompassInputHandler::getMinHeight() const -> double { return MIN_HEIGHT; }
auto CompassInputHandler::getMaxHeight() const -> double { return MAX_HEIGHT; }

0 comments on commit bb51258

Please sign in to comment.