From 906ad3d3a45c28ed5352a977249012f8508fb787 Mon Sep 17 00:00:00 2001 From: Mariusz Wachowicz Date: Tue, 19 Sep 2017 10:01:54 +0200 Subject: [PATCH] Introduce dashed line state in PointScanner Horizontal dashed line allow to revert to beginning of scanning is case of missclick or inacurate selection Change-Id: Iaa051cea74e7903f886a19e9b29ade0fb3d1fb27 --- src/Geometry.cpp | 106 +++++++++++++++++--------------- src/Geometry.hpp | 14 ++--- src/PointScanner.cpp | 168 ++++++++++++++++++++++++++++++++++++++------------- src/RowScanner.cpp | 2 +- 4 files changed, 191 insertions(+), 99 deletions(-) diff --git a/src/Geometry.cpp b/src/Geometry.cpp index 0601594..a0143f8 100644 --- a/src/Geometry.cpp +++ b/src/Geometry.cpp @@ -24,47 +24,57 @@ Rectangle Rectangle::intersect(const Rectangle first, const Rectangle second) } -evas::Object evas::createAndShowLine(Evas *evasCanvas, Rectangle dimensions) +namespace { - auto line = evas_object_line_add(evasCanvas); - evas_object_line_xy_set(line, dimensions.position.x, dimensions.position.y, - dimensions.position.x + dimensions.size.width, dimensions.position.y + dimensions.size.height); - evas_object_color_set(line, DEFAULT_LINE_COLOUR); - evas_object_show(line); + evas::Object createAndShowThinLine(Evas *evasCanvas, Rectangle dimensions) + { + auto line = evas_object_line_add(evasCanvas); + evas_object_line_xy_set(line, dimensions.position.x, dimensions.position.y, + dimensions.position.x + dimensions.size.width, dimensions.position.y + dimensions.size.height); + evas_object_color_set(line, DEFAULT_LINE_COLOUR); + evas_object_show(line); - return evas::Object{line}; -} + return evas::Object{line}; + } -evas::Object evas::createAndShowThickLine(Evas *evasCanvas, Rectangle dimensions) -{ - ASSERT(!(dimensions.size.width && dimensions.size.height), "Unhandled line animation"); + evas::Object createAndShowRectangle(Evas *evasCanvas, Rectangle dimensions) + { + auto rect = evas_object_rectangle_add(evasCanvas); + evas_object_move(rect, dimensions.position.x, dimensions.position.y); + evas_object_resize(rect, dimensions.size.width, dimensions.size.height); + evas_object_color_set(rect, DEFAULT_RECTANGLE_COLOUR); + evas_object_show(rect); - dimensions.position.x -= DEFAULT_LINE_THICKNESS / 2; - dimensions.position.y -= DEFAULT_LINE_THICKNESS / 2; - dimensions.size.width += DEFAULT_LINE_THICKNESS; - dimensions.size.height += DEFAULT_LINE_THICKNESS; - return createAndShowRectangle(evasCanvas, dimensions); -} + return evas::Object{rect}; + } -evas::Object evas::createAndShowRectangle(Evas *evasCanvas, Rectangle dimensions) -{ - auto rect = evas_object_rectangle_add(evasCanvas); - evas_object_move(rect, dimensions.position.x, dimensions.position.y); - evas_object_resize(rect, dimensions.size.width, dimensions.size.height); - evas_object_color_set(rect, DEFAULT_RECTANGLE_COLOUR); - evas_object_show(rect); + evas::Object createAndShowThickLine(Evas *evasCanvas, Rectangle dimensions) + { + ASSERT(!(dimensions.size.width && dimensions.size.height), "Unhandled line animation"); + + dimensions.position.x -= DEFAULT_LINE_THICKNESS / 2; + dimensions.position.y -= DEFAULT_LINE_THICKNESS / 2; + dimensions.size.width += DEFAULT_LINE_THICKNESS; + dimensions.size.height += DEFAULT_LINE_THICKNESS; + return createAndShowRectangle(evasCanvas, dimensions); + } - return evas::Object{rect}; + evas::CompositeLine drawCompositeLine(Evas *evasCanvas, Rectangle dimensions) + { + return {createAndShowThickLine(evasCanvas, dimensions), createAndShowThinLine(evasCanvas, dimensions)}; + } } -evas::CompositeLine evas::drawCompositeLine(Evas *evasCanvas, Rectangle dimensions) +evas::Shape evas::drawSolidLine(Evas *evasCanvas, Rectangle dimensions) { - return {createAndShowThickLine(evasCanvas, dimensions), createAndShowLine(evasCanvas, dimensions)}; + evas::Shape line; + line.push_back(drawCompositeLine(evasCanvas, dimensions)); + return line; } -evas::Frame evas::drawDashedCompositeLine(Evas *evasCanvas, Rectangle dimensions) +evas::Shape evas::drawDashedLine(Evas *evasCanvas, Rectangle dimensions) { - Frame line; + Shape line; if (dimensions.size.height == 0) { //horizontal line for (auto x = dimensions.position.x; x + DASHED_LINE_LENGTH <= dimensions.position.x + dimensions.size.width; @@ -74,7 +84,7 @@ evas::Frame evas::drawDashedCompositeLine(Evas *evasCanvas, Rectangle dimensions } else if (dimensions.size.width == 0) { //vertical line for (auto y = dimensions.position.y; y + DASHED_LINE_LENGTH <= dimensions.position.y + dimensions.size.height; y += 2 * (DASHED_LINE_LENGTH + DEFAULT_LINE_THICKNESS)) { - line.push_back(evas::drawCompositeLine(evasCanvas, {{dimensions.position.x, y}, {0, DASHED_LINE_LENGTH}})); + line.push_back(drawCompositeLine(evasCanvas, {{dimensions.position.x, y}, {0, DASHED_LINE_LENGTH}})); } } else { ASSERT(0, "Unhandled line animation"); @@ -85,32 +95,32 @@ evas::Frame evas::drawDashedCompositeLine(Evas *evasCanvas, Rectangle dimensions namespace { - // return reference level object in z axis required in frame layout formating, - // reference object is calculated as the lowest thin line in given frame - // lowest thin line is the first line in frame vector - Evas_Object *getZAxisReferenceLevel(evas::Frame &frame) + // return reference level object in z axis required in shape layout formating, + // reference object is calculated as the lowest thin line in given shape + // lowest thin line is the first line in shape vector + Evas_Object *getZAxisReferenceLevel(evas::Shape &shape) { - if (frame.empty()) { - ERROR("Frame consist no elements"); + if (shape.empty()) { + ERROR("Shape consist no elements"); return {}; } - return frame[0].second.get(); + return shape[0].second.get(); } - // place thick part of the composite line below the thin part of the composite line for all segments of the frame - void formatFrameLayout(evas::Frame &frame) + // place thick part of the composite line below the thin part of the composite line for all segments of the shape + void formatShapeLayout(evas::Shape &shape) { - auto referenceLevel = getZAxisReferenceLevel(frame); + auto referenceLevel = getZAxisReferenceLevel(shape); if (!referenceLevel) return; - for (auto &f : frame) - evas_object_stack_below(f.first.get(), referenceLevel); + for (auto &segment : shape) + evas_object_stack_below(segment.first.get(), referenceLevel); } } -evas::Frame evas::drawSolidFrame(Evas *evasCanvas, Rectangle dimensions) +evas::Shape evas::drawSolidFrame(Evas *evasCanvas, Rectangle dimensions) { - Frame frame; + Shape frame; frame.push_back(drawCompositeLine(evasCanvas, {dimensions.position, {dimensions.size.width, 0}})); frame.push_back(drawCompositeLine(evasCanvas, @@ -118,14 +128,14 @@ evas::Frame evas::drawSolidFrame(Evas *evasCanvas, Rectangle dimensions) frame.push_back(drawCompositeLine(evasCanvas, {dimensions.position, {0, dimensions.size.height}})); frame.push_back(drawCompositeLine(evasCanvas, {{dimensions.position.x + dimensions.size.width, dimensions.position.y}, {0, dimensions.size.height}})); - formatFrameLayout(frame); + formatShapeLayout(frame); return frame; } -evas::Frame evas::drawDashedFrame(Evas *evasCanvas, Rectangle dimensions) +evas::Shape evas::drawDashedFrame(Evas *evasCanvas, Rectangle dimensions) { - Frame frame; + Shape frame; for (auto x = dimensions.position.x; x + DASHED_LINE_LENGTH <= dimensions.position.x + dimensions.size.width; x += 2 * (DASHED_LINE_LENGTH + DEFAULT_LINE_THICKNESS)) { @@ -139,7 +149,7 @@ evas::Frame evas::drawDashedFrame(Evas *evasCanvas, Rectangle dimensions) frame.push_back(drawCompositeLine(evasCanvas, {{dimensions.position.x + dimensions.size.width, y}, {0, DASHED_LINE_LENGTH}})); } - formatFrameLayout(frame); + formatShapeLayout(frame); return frame; } diff --git a/src/Geometry.hpp b/src/Geometry.hpp index de8dfb8..904e392 100644 --- a/src/Geometry.hpp +++ b/src/Geometry.hpp @@ -33,6 +33,7 @@ namespace evas }; using Object = std::unique_ptr; using CompositeLine = std::pair; + using Shape = std::vector; struct TransitDeleter { void operator()(Elm_Transit *ptr) const @@ -42,16 +43,11 @@ namespace evas }; using Transit = std::unique_ptr; - using Frame = std::vector; - Object createAndShowLine(Evas *evasCanvas, Rectangle dimensions); - Object createAndShowThickLine(Evas *evasCanvas, Rectangle dimensions); - Object createAndShowRectangle(Evas *evasCanvas, Rectangle dimensions); - - CompositeLine drawCompositeLine(Evas *evasCanvas, Rectangle dimensions); - Frame drawDashedCompositeLine(Evas *evasCanvas, Rectangle dimensions); - Frame drawSolidFrame(Evas *evasCanvas, Rectangle dimensions); - Frame drawDashedFrame(Evas *evasCanvas, Rectangle dimensions); + Shape drawSolidLine(Evas *evasCanvas, Rectangle dimensions); + Shape drawDashedLine(Evas *evasCanvas, Rectangle dimensions); + Shape drawSolidFrame(Evas *evasCanvas, Rectangle dimensions); + Shape drawDashedFrame(Evas *evasCanvas, Rectangle dimensions); } #endif diff --git a/src/PointScanner.cpp b/src/PointScanner.cpp index 1d81ffe..e19b97e 100644 --- a/src/PointScanner.cpp +++ b/src/PointScanner.cpp @@ -23,29 +23,37 @@ public: Optional> acceptAutoscanningPhase() override; private: - enum class State {END, VERTICAL, HORIZONTAL, START}; + enum class State {END, VERTICAL_REST, HORIZONTAL_DASHED, VERTICAL_FIRST, HORIZONTAL, START}; + + Optional> acceptEventTransition(); + ecore::TimerRepetitionPolicy timeEventTransition(); void disableScanner(); void resetGraphics(); - std::pair createAnimation(Rectangle dimensions, Point translation); - evas::Transit runTransit(evas::CompositeLine &compositeLine, Point translation); + std::pair createAnimation(Rectangle dimensions, Point translation); + evas::Transit runTransit(evas::Shape &solidLine, Point translation); void startHorizontalLine(); void stopHorizontalLine(); void startVerticalLine(); void stopVerticalLine(); + void setUpDashedLine(); + void tearDownDashedLine(); void onInactivity(); ecore::Timer inactivityTimer; + ecore::Timer stateMachineTimer; + double screenSweepTime = 0.0; State state = State::START; evas::Transit horizontalTransit; evas::Transit verticalTransit; - evas::CompositeLine horizontalLine; - evas::CompositeLine verticalLine; + evas::Shape horizontalLine; + evas::Shape verticalLine; + evas::Shape dashedLine; Point intersectionPoint; }; @@ -66,7 +74,9 @@ PointScannerImpl::~PointScannerImpl() void PointScannerImpl::disableScanner() { + DEBUG("disabling scanner"); inactivityTimer.reset(); + stateMachineTimer.reset(); resetGraphics(); } @@ -75,16 +85,20 @@ void PointScannerImpl::resetGraphics() horizontalTransit.reset(); verticalTransit.reset(); - horizontalLine.first.reset(); - horizontalLine.second.reset(); - verticalLine.first.reset(); - verticalLine.second.reset(); + horizontalLine.clear(); + verticalLine.clear(); + dashedLine.clear(); state = State::START; } Optional> PointScannerImpl::acceptAutoscanningPhase() { + return acceptEventTransition(); +} + +Optional> PointScannerImpl::acceptEventTransition() +{ switch (state) { case State::START: startHorizontalLine(); @@ -93,9 +107,20 @@ Optional> PointScannerImpl::acceptAutoscanningPhase() case State::HORIZONTAL: stopHorizontalLine(); startVerticalLine(); - state = State::VERTICAL; + state = State::VERTICAL_FIRST; + ASSERT(screenSweepTime > 0.0, "time for callback has to be positive"); + stateMachineTimer.reset(2.0 * screenSweepTime, [this]() { + return timeEventTransition(); + }); return {}; - case State::VERTICAL: + case State::HORIZONTAL_DASHED: + stateMachineTimer.reset(); + dashedLine.clear(); + startHorizontalLine(); + state = State::HORIZONTAL; + return {}; + case State::VERTICAL_FIRST: + case State::VERTICAL_REST: stopVerticalLine(); disableScanner(); state = State::END; @@ -108,30 +133,60 @@ Optional> PointScannerImpl::acceptAutoscanningPhase() return {}; } -std::pair PointScannerImpl::createAnimation(Rectangle dimensions, Point translation) +ecore::TimerRepetitionPolicy PointScannerImpl::timeEventTransition() +{ + switch (state) { + case State::VERTICAL_FIRST: { + setUpDashedLine(); + state = State::HORIZONTAL_DASHED; + stateMachineTimer.reset(properties.getAutoScanInterval(), [this]() { + return timeEventTransition(); + }); + return ecore::TimerRepetitionPolicy::cancel; + } + case State::HORIZONTAL_DASHED: { + tearDownDashedLine(); + startVerticalLine(); + state = State::VERTICAL_REST; + return ecore::TimerRepetitionPolicy::cancel; + } + case State::START: + case State::HORIZONTAL: + case State::VERTICAL_REST: + case State::END: + ASSERT(0, "This state is not supported in transition caused by time"); + } + return ecore::TimerRepetitionPolicy::cancel; +} + +std::pair PointScannerImpl::createAnimation(Rectangle dimensions, Point translation) { auto evasCanvas = evas_object_evas_get(Singleton::instance().getMainWindow()->getHandler()); - auto compositeLine = evas::drawCompositeLine(evasCanvas, dimensions); - auto transit = runTransit(compositeLine, translation); - return {std::move(transit), std::move(compositeLine)}; + auto solidLine = evas::drawSolidLine(evasCanvas, dimensions); + auto transit = runTransit(solidLine, translation); + return {std::move(transit), std::move(solidLine)}; } -evas::Transit PointScannerImpl::runTransit(evas::CompositeLine &compositeLine, Point translation) +evas::Transit PointScannerImpl::runTransit(evas::Shape &solidLine, Point translation) { auto transit = elm_transit_add(); - elm_transit_object_add(transit, compositeLine.first.get()); - elm_transit_object_add(transit, compositeLine.second.get()); + if (solidLine.size() != 1) { + ERROR("solidLine size mismatch. size = %d", solidLine.size()); + return evas::Transit{transit}; + } + elm_transit_object_add(transit, solidLine.front().first.get()); + elm_transit_object_add(transit, solidLine.front().second.get()); elm_transit_effect_translation_add(transit, 0, 0, translation.x, translation.y); int distance = std::max(std::abs(translation.x), std::abs(translation.y)); ASSERT(properties.getSpeed() > 0); - double time = static_cast(distance) / (properties.getSpeed() * SPEED_UNIT_FACTOR); - ASSERT(time > 0); + screenSweepTime = static_cast(distance) / (properties.getSpeed() * SPEED_UNIT_FACTOR); + ASSERT(screenSweepTime > 0); ASSERT(properties.getLoopLimitToInaction() > 0); - inactivityTimer.reset(2 * time * properties.getLoopLimitToInaction(), [this]() { + inactivityTimer.reset(2 * screenSweepTime * properties.getLoopLimitToInaction(), [this]() { onInactivity(); return ecore::TimerRepetitionPolicy::cancel; }); - elm_transit_duration_set(transit, time); + elm_transit_duration_set(transit, screenSweepTime); elm_transit_auto_reverse_set(transit, EINA_TRUE); elm_transit_repeat_times_set(transit, -1); elm_transit_go(transit); @@ -139,6 +194,36 @@ evas::Transit PointScannerImpl::runTransit(evas::CompositeLine &compositeLine, P return evas::Transit{transit}; } +void PointScannerImpl::startHorizontalLine() +{ + DEBUG("Starting horizontal line"); + auto scanningField = properties.getScanningField(); + Point origin = scanningField.position; + Point translation; + switch (properties.getDirectionVertical()) { + case VerticalScanningDirection::TO_BOTTOM: + translation.y += scanningField.size.height; + break; + case VerticalScanningDirection::TO_TOP: + origin.y += scanningField.size.height; + translation.y -= scanningField.size.height; + break; + } + + Size dimensions = {scanningField.size.width, 0}; + std::tie(horizontalTransit, horizontalLine) = createAnimation({origin, dimensions}, translation); +} + +void PointScannerImpl::stopHorizontalLine() +{ + elm_transit_paused_set(horizontalTransit.get(), true); + if (horizontalLine.empty()) { + ERROR("Algorithm do not work properly. horizontalLine should not be empty"); + return; + } + evas_object_geometry_get(horizontalLine.front().second.get(), nullptr, &intersectionPoint.y, nullptr, nullptr); +} + void PointScannerImpl::startVerticalLine() { DEBUG("Starting vertical line"); @@ -162,33 +247,34 @@ void PointScannerImpl::startVerticalLine() void PointScannerImpl::stopVerticalLine() { elm_transit_paused_set(verticalTransit.get(), true); - evas_object_geometry_get(verticalLine.second.get(), &intersectionPoint.x, nullptr, nullptr, nullptr); + if (verticalLine.empty()) { + ERROR("Algorithm do not work properly. verticalLine should not be empty"); + return; + } + evas_object_geometry_get(verticalLine.front().second.get(), &intersectionPoint.x, nullptr, nullptr, nullptr); } -void PointScannerImpl::startHorizontalLine() +void PointScannerImpl::setUpDashedLine() { - DEBUG("Starting horizontal line"); + DEBUG("Setting up dashed line"); + resetGraphics(); + + auto evasCanvas = evas_object_evas_get(Singleton::instance().getMainWindow()->getHandler()); auto scanningField = properties.getScanningField(); - Point origin = scanningField.position; - Point translation; - switch (properties.getDirectionVertical()) { - case VerticalScanningDirection::TO_BOTTOM: - translation.y += scanningField.size.height; - break; - case VerticalScanningDirection::TO_TOP: - origin.y += scanningField.size.height; - translation.y -= scanningField.size.height; - break; - } - Size dimensions = {scanningField.size.width, 0}; - std::tie(horizontalTransit, horizontalLine) = createAnimation({origin, dimensions}, translation); + dashedLine = evas::drawDashedLine(evasCanvas, + {{scanningField.position.x, intersectionPoint.y}, {scanningField.size.width, 0}}); } -void PointScannerImpl::stopHorizontalLine() +void PointScannerImpl::tearDownDashedLine() { - elm_transit_paused_set(horizontalTransit.get(), true); - evas_object_geometry_get(horizontalLine.second.get(), nullptr, &intersectionPoint.y, nullptr, nullptr); + dashedLine.clear(); + + auto evasCanvas = evas_object_evas_get(Singleton::instance().getMainWindow()->getHandler()); + auto scanningField = properties.getScanningField(); + + horizontalLine = evas::drawSolidLine(evasCanvas, + {{scanningField.position.x, intersectionPoint.y}, {scanningField.size.width, 0}}); } void PointScannerImpl::onInactivity() diff --git a/src/RowScanner.cpp b/src/RowScanner.cpp index 60d4e2d..62b00a4 100644 --- a/src/RowScanner.cpp +++ b/src/RowScanner.cpp @@ -58,7 +58,7 @@ private: std::vector navigationHandles; ecore::Timer timer; - evas::Frame frame; + evas::Shape frame; Rectangle lastFrameDimensions; bool skipFrameDrawing = false; -- 2.7.4