calculator/src/GraphControl/Control/Grapher.h
Quentin Al-Timimi 0175b51655
Updated APIs to use new GetClosePointData() from Graphing Engine. (#1250)
* Updated APIs to use new GetClosePointData() from Graphing Engine. Now specifiying precision on API consumption to aid with correct display and rounding.

* Updated function to be const-corect

* Updated to use correct APIs

* Converted TraceValue from Point to two doubles, point's X and Y was using float and conversion between float and doubles was causing unwanted rounding.

* Update to pch file and fixing typo

* Point to updated graphing version
2020-06-30 15:08:54 -07:00

359 lines
14 KiB
C++

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "DirectX/RenderMain.h"
#include "Models/Equation.h"
#include "Models/EquationCollection.h"
#include "Models/Variable.h"
#include "Utils.h"
#include "IGraphAnalyzer.h"
#include "IMathSolver.h"
#include "Common.h"
#include "Models/KeyGraphFeaturesInfo.h"
#include <ppltasks.h>
#include "Logger/TraceLogger.h"
namespace GraphControl
{
public
delegate void TracingChangedEventHandler(bool newValue);
public
delegate void TracingValueChangedEventHandler(double xPointValue, double yPointValue);
public
delegate void PointerValueChangedEventHandler(Windows::Foundation::Point value);
public enum class GraphViewChangedReason
{
Manipulation,
Reset
};
[Windows::UI::Xaml::Markup::ContentPropertyAttribute(Name = L"Equations")] public ref class Grapher sealed
: public Windows::UI::Xaml::Controls::Control,
public Windows::UI::Xaml::Data::INotifyPropertyChanged
{
public:
event TracingValueChangedEventHandler ^ TracingValueChangedEvent;
event PointerValueChangedEventHandler ^ PointerValueChangedEvent;
event TracingChangedEventHandler ^ TracingChangedEvent;
event Windows::Foundation::EventHandler<GraphViewChangedReason> ^ GraphViewChangedEvent;
event Windows::UI::Xaml::RoutedEventHandler ^ GraphPlottedEvent;
virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler ^ PropertyChanged;
public:
Grapher();
DEPENDENCY_PROPERTY_OWNER(Grapher);
DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(bool, ForceProportionalAxes, true);
DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(bool, UseCommaDecimalSeperator, false);
DEPENDENCY_PROPERTY_WITH_DEFAULT(
SINGLE_ARG(Windows::Foundation::Collections::IObservableMap<Platform::String ^, GraphControl::Variable ^> ^),
Variables,
SINGLE_ARG(ref new Platform::Collections::Map<Platform::String ^, GraphControl::Variable ^>()));
DEPENDENCY_PROPERTY_R_WITH_DEFAULT_AND_CALLBACK(GraphControl::EquationCollection ^, Equations, nullptr);
DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(Windows::UI::Color, AxesColor, Windows::UI::Colors::Transparent);
DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(Windows::UI::Color, GraphBackground, Windows::UI::Colors::Transparent);
DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(Windows::UI::Color, GridLinesColor, Windows::UI::Colors::Transparent);
DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(double, LineWidth, 2.0);
DEPENDENCY_PROPERTY_WITH_DEFAULT(bool, IsKeepCurrentView, false);
// Pass active tracing turned on or off down to the renderer
property bool ActiveTracing
{
bool get()
{
return m_renderMain != nullptr && m_renderMain->ActiveTracing;
}
void set(bool value)
{
if (m_renderMain != nullptr && m_renderMain->ActiveTracing != value)
{
m_renderMain->ActiveTracing = value;
UpdateTracingChanged();
PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(L"ActiveTracing"));
}
}
}
void ZoomFromCenter(double scale);
void ResetGrid();
property Windows::Foundation::Point TraceLocation
{
Windows::Foundation::Point get()
{
return m_renderMain->TraceLocation;
}
}
property Windows::Foundation::Point ActiveTraceCursorPosition
{
Windows::Foundation::Point get()
{
return m_renderMain->ActiveTraceCursorPosition;
}
void set(Windows::Foundation::Point newValue)
{
if (m_renderMain->ActiveTraceCursorPosition != newValue)
{
m_renderMain->ActiveTraceCursorPosition = newValue;
UpdateTracingChanged();
}
}
}
event Windows::Foundation::EventHandler<Windows::Foundation::Collections::IMap<Platform::String ^, Variable ^> ^> ^ VariablesUpdated;
void SetVariable(Platform::String ^ variableName, double newValue);
Platform::String ^ ConvertToLinear(Platform::String ^ mmlString);
Platform::String ^ FormatMathML(Platform::String ^ mmlString);
/// <summary>
/// Draw the graph. Call this method if you add or modify an equation.
/// </summary>
/// <param name="keepCurrentView">Force the graph control to not pan or zoom to adapt the view.</param>
void PlotGraph(bool keepCurrentView);
GraphControl::KeyGraphFeaturesInfo ^ AnalyzeEquation(GraphControl::Equation ^ equation);
// We can't use the EvalTrigUnitMode enum directly in as the property type because it comes from another module which doesn't expose
// it as a public enum class. So the compiler doesn't recognize it as a valid type for the ABI boundary.
property int TrigUnitMode
{
void set(int value)
{
if (value != (int)m_solver->EvalOptions().GetTrigUnitMode())
{
m_solver->EvalOptions().SetTrigUnitMode((Graphing::EvalTrigUnitMode)value);
m_trigUnitsChanged = true;
PlotGraph(true);
}
}
int get()
{
return (int)m_solver->EvalOptions().GetTrigUnitMode();
}
}
property double XAxisMin
{
double get()
{
return m_graph->GetOptions().GetDefaultXRange().first;
}
void set(double value)
{
std::pair<double, double> newValue(value, XAxisMax);
if (m_graph != nullptr)
{
m_graph->GetOptions().SetDefaultXRange(newValue);
if (m_renderMain != nullptr)
{
m_renderMain->RunRenderPass();
}
}
}
}
property double XAxisMax
{
double get()
{
return m_graph->GetOptions().GetDefaultXRange().second;
}
void set(double value)
{
std::pair<double, double> newValue(XAxisMin, value);
if (m_graph != nullptr)
{
m_graph->GetOptions().SetDefaultXRange(newValue);
if (m_renderMain != nullptr)
{
m_renderMain->RunRenderPass();
}
}
}
}
property double YAxisMin
{
double get()
{
return m_graph->GetOptions().GetDefaultXRange().first;
}
void set(double value)
{
std::pair<double, double> newValue(value, YAxisMax);
if (m_graph != nullptr)
{
m_graph->GetOptions().SetDefaultYRange(newValue);
if (m_renderMain != nullptr)
{
m_renderMain->RunRenderPass();
}
}
}
}
property double YAxisMax
{
double get()
{
return m_graph->GetOptions().GetDefaultXRange().second;
}
void set(double value)
{
std::pair<double, double> newValue(YAxisMin, value);
if (m_graph != nullptr)
{
m_graph->GetOptions().SetDefaultYRange(newValue);
if (m_renderMain != nullptr)
{
m_renderMain->RunRenderPass();
}
}
}
}
void GetDisplayRanges(double* xMin, double* xMax, double* yMin, double* yMax)
{
try
{
if (m_graph != nullptr && m_renderMain != nullptr)
{
if (auto render = m_graph->GetRenderer())
{
Concurrency::critical_section::scoped_lock lock(m_renderMain->GetCriticalSection());
render->GetDisplayRanges(*xMin, *xMax, *yMin, *yMax);
}
}
}
catch (const std::exception&)
{
OutputDebugString(L"GetDisplayRanges failed\r\n");
}
}
void SetDisplayRanges(double xMin, double xMax, double yMin, double yMax)
{
try
{
if (auto render = m_graph->GetRenderer())
{
render->SetDisplayRanges(xMin, xMax, yMin, yMax);
m_rangeUpdatedBySettings = true;
if (m_renderMain)
{
m_renderMain->RunRenderPass();
GraphViewChangedEvent(this, GraphViewChangedReason::Manipulation);
}
}
}
catch (const std::exception&)
{
OutputDebugString(L"SetDisplayRanges failed\r\n");
}
}
protected:
#pragma region Control Overrides
void OnApplyTemplate() override;
void OnPointerEntered(Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e) override;
void OnPointerMoved(Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e) override;
void OnPointerExited(Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e) override;
void OnPointerWheelChanged(Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e) override;
void OnPointerPressed(Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e) override;
void OnPointerReleased(Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e) override;
void OnPointerCanceled(Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e) override;
void OnManipulationDelta(Windows::UI::Xaml::Input::ManipulationDeltaRoutedEventArgs ^ e) override;
#pragma endregion
private:
void OnForceProportionalAxesPropertyChanged(bool oldValue, bool newValue);
void OnUseCommaDecimalSeperatorPropertyChanged(bool oldValue, bool newValue);
void OnEquationsPropertyChanged(EquationCollection ^ oldValue, EquationCollection ^ newValue);
void OnAxesColorPropertyChanged(Windows::UI::Color oldValue, Windows::UI::Color newValue);
void OnGraphBackgroundPropertyChanged(Windows::UI::Color oldValue, Windows::UI::Color newValue);
void OnGridLinesColorPropertyChanged(Windows::UI::Color /*oldValue*/, Windows::UI::Color newValue);
void OnLineWidthPropertyChanged(double oldValue, double newValue);
void OnEquationChanged(Equation ^ equation);
void OnEquationStyleChanged(Equation ^ equation);
void OnEquationLineEnabledChanged(Equation ^ equation);
concurrency::task<bool> TryUpdateGraph(bool keepCurrentView);
concurrency::task<void> TryPlotGraph(bool keepCurrentView, bool shouldRetry);
void UpdateGraphOptions(Graphing::IGraphingOptions& options, const std::vector<Equation ^>& validEqs);
std::vector<Equation ^> GetGraphableEquations();
void SetGraphArgs(std::shared_ptr<Graphing::IGraph> graph);
std::shared_ptr<Graphing::IGraph> GetGraph(GraphControl::Equation ^ equation);
void UpdateVariables();
void ScaleRange(double centerX, double centerY, double scale);
void OnCoreKeyDown(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::KeyEventArgs ^ e);
void OnCoreKeyUp(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::KeyEventArgs ^ e);
void UpdateTracingChanged();
void HandleTracingMovementTick(Object ^ sender, Object ^ e);
void HandleKey(bool keyDown, Windows::System::VirtualKey key);
void SetEquationsAsValid();
void SetEquationErrors();
std::optional<std::vector<std::shared_ptr<Graphing::IEquation>>> TryInitializeGraph(bool keepCurrentView, _In_ const Graphing::IExpression* graphingExp = nullptr);
private:
DX::RenderMain ^ m_renderMain = nullptr;
static Windows::UI::Xaml::DependencyProperty ^ s_equationTemplateProperty;
static Windows::UI::Xaml::DependencyProperty ^ s_equationsSourceProperty;
Windows::Foundation::EventRegistrationToken m_tokenDataSourceChanged;
static Windows::UI::Xaml::DependencyProperty ^ s_equationsProperty;
static Windows::UI::Xaml::DependencyProperty ^ s_variablesProperty;
Windows::Foundation::EventRegistrationToken m_tokenEquationsChanged;
Windows::Foundation::EventRegistrationToken m_tokenEquationStyleChanged;
Windows::Foundation::EventRegistrationToken m_tokenEquationChanged;
Windows::Foundation::EventRegistrationToken m_tokenEquationLineEnabledChanged;
Windows::Foundation::EventRegistrationToken m_tokenBackgroundColorChanged;
const std::unique_ptr<Graphing::IMathSolver> m_solver;
const std::shared_ptr<Graphing::IGraph> m_graph;
bool m_calculatedForceProportional = false;
bool m_tracingTracking;
bool m_trigUnitsChanged;
enum KeysPressedSlots
{
Left,
Right,
Down,
Up,
Accelerator
};
bool m_KeysPressed[5];
bool m_Moving;
Windows::UI::Xaml::DispatcherTimer ^ m_TracingTrackingTimer;
Windows::UI::Core::CoreCursor ^ m_cachedCursor;
int m_errorType;
int m_errorCode;
bool m_resetUsingInitialDisplayRange;
bool m_rangeUpdatedBySettings;
double m_initialDisplayRangeXMin;
double m_initialDisplayRangeXMax;
double m_initialDisplayRangeYMin;
double m_initialDisplayRangeYMax;
public:
Windows::Storage::Streams::RandomAccessStreamReference ^ GetGraphBitmapStream();
};
}