Feature/GraphingCalculator initial commit (#450)

Initial PR for the feature/GraphingCalculator feature branch, part of #338.

The feature incorporates a proprietary Microsoft-owned graphing engine to drive graphing experiences in the Windows Calculator app. Due to the private nature of the graphing engine, the source available in the public repo will make use of a mock graphing engine. See README.md for more details.

This PR simply serves as a base for future feature development. As such, the PR will be immediately merged. Feedback on the content of this PR, and on the feature in general, is encouraged. If there is feedback related to the content of this specific PR, please leave comments on the PR page. We will address the comments in future PRs to the feature branch.
This commit is contained in:
Daniel Belcher 2019-04-10 18:15:10 -07:00 committed by GitHub
parent 47a2741218
commit 091732aa94
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
65 changed files with 5190 additions and 109 deletions

1
.gitignore vendored
View file

@ -289,6 +289,7 @@ __pycache__/
# Calculator specific # Calculator specific
Generated Files/ Generated Files/
src/GraphControl/GraphingImplOverrides.props
!/build/config/TRexDefs/** !/build/config/TRexDefs/**
!src/Calculator/TemporaryKey.pfx !src/Calculator/TemporaryKey.pfx
!src/CalculatorUnitTests/CalculatorUnitTests_TemporaryKey.pfx !src/CalculatorUnitTests/CalculatorUnitTests_TemporaryKey.pfx

View file

@ -45,6 +45,10 @@ We also welcome [issues submitted on GitHub](https://github.com/Microsoft/calcul
## Roadmap ## Roadmap
For information regarding Windows Calculator plans and release schedule, please see the [Windows Calculator Roadmap](docs/Roadmap.md). For information regarding Windows Calculator plans and release schedule, please see the [Windows Calculator Roadmap](docs/Roadmap.md).
### Graphing Mode
Adding graphing calculator functionality [is on the project roadmap](https://github.com/Microsoft/calculator/issues/338) and we hope that this project can create a great end-user experience around graphing. To that end, the UI from the official in-box Windows Calculator is currently part of this repository, although the proprietary Microsoft-built graphing engine, which also drives graphing in Microsoft Mathematics and OneNote, is not. Community members can still be involved in the creation of the UI, however developer builds will not have graphing functionality due to the use of a [mock implementation of the engine](/src/MockGraphingImpl) built on top of a
[common graphing API](/src/GraphingInterfaces).
## Data / Telemetry ## Data / Telemetry
This project collects usage data and sends it to Microsoft to help improve our products and services. This project collects usage data and sends it to Microsoft to help improve our products and services.
Read our [privacy statement](https://go.microsoft.com/fwlink/?LinkId=521839) to learn more. Read our [privacy statement](https://go.microsoft.com/fwlink/?LinkId=521839) to learn more.

View file

@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved. // Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. // Licensed under the MIT License.
#include "pch.h" #include "pch.h"
@ -42,6 +42,7 @@ namespace
ApplicationViewModel::ApplicationViewModel() : ApplicationViewModel::ApplicationViewModel() :
m_CalculatorViewModel(nullptr), m_CalculatorViewModel(nullptr),
m_DateCalcViewModel(nullptr), m_DateCalcViewModel(nullptr),
m_GraphingCalcViewModel(nullptr),
m_ConverterViewModel(nullptr), m_ConverterViewModel(nullptr),
m_PreviousMode(ViewMode::None), m_PreviousMode(ViewMode::None),
m_mode(ViewMode::None), m_mode(ViewMode::None),
@ -132,6 +133,13 @@ void ApplicationViewModel::OnModeChanged()
} }
m_CalculatorViewModel->SetCalculatorType(m_mode); m_CalculatorViewModel->SetCalculatorType(m_mode);
} }
else if (NavCategory::IsGraphingCalculatorViewMode(m_mode))
{
if (!m_GraphingCalcViewModel)
{
m_GraphingCalcViewModel = ref new GraphingCalculatorViewModel();
}
}
else if (NavCategory::IsDateCalculatorViewMode(m_mode)) else if (NavCategory::IsDateCalculatorViewMode(m_mode))
{ {
TraceLogger::GetInstance().LogDateCalculatorModeViewed(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); TraceLogger::GetInstance().LogDateCalculatorModeViewed(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()));

View file

@ -5,6 +5,7 @@
#include "StandardCalculatorViewModel.h" #include "StandardCalculatorViewModel.h"
#include "DateCalculatorViewModel.h" #include "DateCalculatorViewModel.h"
#include "GraphingCalculator/GraphingCalculatorViewModel.h"
#include "UnitConverterViewModel.h" #include "UnitConverterViewModel.h"
namespace CalculatorApp namespace CalculatorApp
@ -22,6 +23,7 @@ namespace CalculatorApp
OBSERVABLE_OBJECT(); OBSERVABLE_OBJECT();
OBSERVABLE_PROPERTY_RW(StandardCalculatorViewModel^, CalculatorViewModel); OBSERVABLE_PROPERTY_RW(StandardCalculatorViewModel^, CalculatorViewModel);
OBSERVABLE_PROPERTY_RW(DateCalculatorViewModel^, DateCalcViewModel); OBSERVABLE_PROPERTY_RW(DateCalculatorViewModel^, DateCalcViewModel);
OBSERVABLE_PROPERTY_RW(GraphingCalculatorViewModel^, GraphingCalcViewModel);
OBSERVABLE_PROPERTY_RW(UnitConverterViewModel^, ConverterViewModel); OBSERVABLE_PROPERTY_RW(UnitConverterViewModel^, ConverterViewModel);
OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::ViewMode, PreviousMode); OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::ViewMode, PreviousMode);
OBSERVABLE_NAMED_PROPERTY_RW(Platform::String^, CategoryName); OBSERVABLE_NAMED_PROPERTY_RW(Platform::String^, CategoryName);

View file

@ -350,6 +350,8 @@
<ClInclude Include="DataLoaders\UnitConverterDataConstants.h" /> <ClInclude Include="DataLoaders\UnitConverterDataConstants.h" />
<ClInclude Include="DataLoaders\UnitConverterDataLoader.h" /> <ClInclude Include="DataLoaders\UnitConverterDataLoader.h" />
<ClInclude Include="DateCalculatorViewModel.h" /> <ClInclude Include="DateCalculatorViewModel.h" />
<ClInclude Include="GraphingCalculator\EquationViewModel.h" />
<ClInclude Include="GraphingCalculator\GraphingCalculatorViewModel.h" />
<ClInclude Include="HistoryItemViewModel.h" /> <ClInclude Include="HistoryItemViewModel.h" />
<ClInclude Include="HistoryViewModel.h" /> <ClInclude Include="HistoryViewModel.h" />
<ClInclude Include="MemoryItemViewModel.h" /> <ClInclude Include="MemoryItemViewModel.h" />
@ -386,6 +388,8 @@
<ClCompile Include="DataLoaders\CurrencyHttpClient.cpp" /> <ClCompile Include="DataLoaders\CurrencyHttpClient.cpp" />
<ClCompile Include="DataLoaders\UnitConverterDataLoader.cpp" /> <ClCompile Include="DataLoaders\UnitConverterDataLoader.cpp" />
<ClCompile Include="DateCalculatorViewModel.cpp" /> <ClCompile Include="DateCalculatorViewModel.cpp" />
<ClCompile Include="GraphingCalculator\EquationViewModel.cpp" />
<ClCompile Include="GraphingCalculator\GraphingCalculatorViewModel.cpp" />
<ClCompile Include="HistoryItemViewModel.cpp" /> <ClCompile Include="HistoryItemViewModel.cpp" />
<ClCompile Include="HistoryViewModel.cpp" /> <ClCompile Include="HistoryViewModel.cpp" />
<ClCompile Include="MemoryItemViewModel.cpp" /> <ClCompile Include="MemoryItemViewModel.cpp" />

View file

@ -10,6 +10,9 @@
<Filter Include="DataLoaders"> <Filter Include="DataLoaders">
<UniqueIdentifier>{0184f727-b8aa-4af8-a699-63f1b56e7853}</UniqueIdentifier> <UniqueIdentifier>{0184f727-b8aa-4af8-a699-63f1b56e7853}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="GraphingCalculator">
<UniqueIdentifier>{f7519cec-2ebd-432b-9d59-9647de131d50}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="pch.cpp" /> <ClCompile Include="pch.cpp" />
@ -93,6 +96,12 @@
<ClCompile Include="DataLoaders\UnitConverterDataLoader.cpp"> <ClCompile Include="DataLoaders\UnitConverterDataLoader.cpp">
<Filter>DataLoaders</Filter> <Filter>DataLoaders</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="GraphingCalculator\EquationViewModel.cpp">
<Filter>GraphingCalculator</Filter>
</ClCompile>
<ClCompile Include="GraphingCalculator\GraphingCalculatorViewModel.cpp">
<Filter>GraphingCalculator</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="pch.h" /> <ClInclude Include="pch.h" />
@ -213,6 +222,12 @@
<ClInclude Include="Common\TraceActivity.h"> <ClInclude Include="Common\TraceActivity.h">
<Filter>Common</Filter> <Filter>Common</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="GraphingCalculator\EquationViewModel.h">
<Filter>GraphingCalculator</Filter>
</ClInclude>
<ClInclude Include="GraphingCalculator\GraphingCalculatorViewModel.h">
<Filter>GraphingCalculator</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="DataLoaders\DefaultFromToCurrency.json"> <None Include="DataLoaders\DefaultFromToCurrency.json">

View file

@ -62,10 +62,6 @@ namespace CalculatorApp
IsStandardMode = (int) CM::Command::ModeBasic, IsStandardMode = (int) CM::Command::ModeBasic,
None = (int) CM::Command::CommandNULL, None = (int) CM::Command::CommandNULL,
IsProgrammerMode = (int) CM::Command::ModeProgrammer, IsProgrammerMode = (int) CM::Command::ModeProgrammer,
DecButton = (int) CM::Command::CommandDec,
OctButton = (int) CM::Command::CommandOct,
HexButton = (int) CM::Command::CommandHex,
BinButton = (int) CM::Command::CommandBin,
And = (int) CM::Command::CommandAnd, And = (int) CM::Command::CommandAnd,
Ror = (int) CM::Command::CommandROR, Ror = (int) CM::Command::CommandROR,
Rol = (int) CM::Command::CommandROL, Rol = (int) CM::Command::CommandROL,
@ -87,12 +83,21 @@ namespace CalculatorApp
InvSinh = (int) CM::Command::CommandASINH, InvSinh = (int) CM::Command::CommandASINH,
InvCosh = (int) CM::Command::CommandACOSH, InvCosh = (int) CM::Command::CommandACOSH,
InvTanh = (int) CM::Command::CommandATANH, InvTanh = (int) CM::Command::CommandATANH,
Qword = (int) CM::Command::CommandQword,
Dword = (int) CM::Command::CommandDword,
Word = (int) CM::Command::CommandWord,
Byte = (int) CM::Command::CommandByte,
Cube = (int) CM::Command::CommandCUB, Cube = (int) CM::Command::CommandCUB,
DMS = (int) CM::Command::CommandDMS, DMS = (int) CM::Command::CommandDMS,
Hyp = (int)CM::Command::CommandHYP,
HexButton = (int)CM::Command::CommandHex,
DecButton = (int)CM::Command::CommandDec,
OctButton = (int)CM::Command::CommandOct,
BinButton = (int)CM::Command::CommandBin,
Qword = (int)CM::Command::CommandQword,
Dword = (int)CM::Command::CommandDword,
Word = (int)CM::Command::CommandWord,
Byte = (int)CM::Command::CommandByte,
Plot,
X,
Y,
BINSTART = (int) CM::Command::CommandBINEDITSTART, BINSTART = (int) CM::Command::CommandBINEDITSTART,
BINPOS0 = (int) CM::Command::CommandBINPOS0, BINPOS0 = (int) CM::Command::CommandBINPOS0,
@ -159,8 +164,7 @@ namespace CalculatorApp
BINPOS61 = (int) CM::Command::CommandBINPOS61, BINPOS61 = (int) CM::Command::CommandBINPOS61,
BINPOS62 = (int) CM::Command::CommandBINPOS62, BINPOS62 = (int) CM::Command::CommandBINPOS62,
BINPOS63 = (int) CM::Command::CommandBINPOS63, BINPOS63 = (int) CM::Command::CommandBINPOS63,
BINEND = (int) CM::Command::CommandBINEDITEND, BINEND = (int) CM::Command::CommandBINEDITEND
Hyp = (int) CM::Command::CommandHYP
}; };
// This contains list of functions whose usage we are tracelogging // This contains list of functions whose usage we are tracelogging

View file

@ -10,6 +10,7 @@
using namespace Concurrency; using namespace Concurrency;
using namespace Platform; using namespace Platform;
using namespace std; using namespace std;
using namespace std::chrono;
using namespace Windows::ApplicationModel::Resources; using namespace Windows::ApplicationModel::Resources;
using namespace Windows::UI::Xaml; using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls; using namespace Windows::UI::Xaml::Controls;
@ -70,7 +71,7 @@ namespace CalculatorApp
} }
} }
void LightUpButton(ButtonBase^ button) winrt::fire_and_forget LightUpButton(ButtonBase^ button)
{ {
// If the button is a toggle button then we don't need // If the button is a toggle button then we don't need
// to change the UI of the button // to change the UI of the button
@ -82,33 +83,15 @@ namespace CalculatorApp
// The button will go into the visual Pressed state with this call // The button will go into the visual Pressed state with this call
VisualStateManager::GoToState(button, "Pressed", true); VisualStateManager::GoToState(button, "Pressed", true);
// This timer will fire after lightUpTime and make the button winrt::apartment_context uiThreadContext;
// go back to the normal state.
// This timer will only fire once after which it will be destroyed
auto timer = ref new DispatcherTimer();
TimeSpan lightUpTime{};
lightUpTime.Duration = 500000L; // Half second (in 100-ns units)
timer->Interval = lightUpTime;
WeakReference timerWeakReference(timer); co_await winrt::resume_background();
WeakReference buttonWeakReference(button); co_await winrt::resume_after(500ms);
timer->Tick += ref new EventHandler<Object^>(
[buttonWeakReference, timerWeakReference](Object^, Object^)
{
auto button = buttonWeakReference.Resolve<ButtonBase>();
if (button)
{
VisualStateManager::GoToState(button, "Normal", true);
}
// Cancel the timer after we're done so it only fires once co_await uiThreadContext;
auto timer = timerWeakReference.Resolve<DispatcherTimer>();
if (timer) // Restore the normal state
{ VisualStateManager::GoToState(button, "Normal", true);
timer->Stop();
}
});
timer->Start();
} }
// Looks for the first button reference that it can resolve // Looks for the first button reference that it can resolve
@ -457,9 +440,8 @@ void KeyboardShortcutManager::OnCharacterReceivedHandler(CoreWindow^ sender, Cha
wchar_t character = static_cast<wchar_t>(args->KeyCode); wchar_t character = static_cast<wchar_t>(args->KeyCode);
auto buttons = s_CharacterForButtons.find(viewId)->second.equal_range(character); auto buttons = s_CharacterForButtons.find(viewId)->second.equal_range(character);
RunFirstEnabledButtonCommand(buttons);
LightUpButtons(buttons); LightUpButtons(buttons);
RunFirstEnabledButtonCommand(buttons);
} }
} }
} }
@ -613,8 +595,6 @@ void KeyboardShortcutManager::OnKeyDownHandler(CoreWindow^ sender, KeyEventArgs^
{ {
if (currentHonorShortcuts->second) if (currentHonorShortcuts->second)
{ {
RunFirstEnabledButtonCommand(buttons);
// Ctrl+C and Ctrl+V shifts focus to some button because of which enter doesn't work after copy/paste. So don't shift focus if Ctrl+C or Ctrl+V is pressed. // Ctrl+C and Ctrl+V shifts focus to some button because of which enter doesn't work after copy/paste. So don't shift focus if Ctrl+C or Ctrl+V is pressed.
// When drop down is open, pressing escape shifts focus to clear button. So dont's shift focus if drop down is open. // When drop down is open, pressing escape shifts focus to clear button. So dont's shift focus if drop down is open.
// Ctrl+Insert is equivalent to Ctrl+C and Shift+Insert is equivalent to Ctrl+V // Ctrl+Insert is equivalent to Ctrl+C and Shift+Insert is equivalent to Ctrl+V
@ -627,6 +607,8 @@ void KeyboardShortcutManager::OnKeyDownHandler(CoreWindow^ sender, KeyEventArgs^
LightUpButtons(buttons); LightUpButtons(buttons);
} }
} }
RunFirstEnabledButtonCommand(buttons);
} }
} }
} }

View file

@ -46,14 +46,16 @@ static constexpr int DATA_ID = 13;
static constexpr int PRESSURE_ID = 14; static constexpr int PRESSURE_ID = 14;
static constexpr int ANGLE_ID = 15; static constexpr int ANGLE_ID = 15;
static constexpr int CURRENCY_ID = 16; static constexpr int CURRENCY_ID = 16;
static constexpr int GRAPHING_ID = 17;
// ^^^ THESE CONSTANTS SHOULD NEVER CHANGE ^^^ // ^^^ THESE CONSTANTS SHOULD NEVER CHANGE ^^^
// The order of items in this list determines the order of items in the menu. // The order of items in this list determines the order of items in the menu.
static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = { static constexpr array<const NavCategoryInitializer, 18> s_categoryManifest = {
NavCategoryInitializer { ViewMode::Standard, STANDARD_ID, L"Standard", L"StandardMode", L"\uE8EF", CategoryGroupType::Calculator, MyVirtualKey::Number1, SUPPORTS_ALL }, NavCategoryInitializer { ViewMode::Standard, STANDARD_ID, L"Standard", L"StandardMode", L"\uE8EF", CategoryGroupType::Calculator, MyVirtualKey::Number1, SUPPORTS_ALL },
NavCategoryInitializer { ViewMode::Scientific, SCIENTIFIC_ID, L"Scientific", L"ScientificMode", L"\uF196", CategoryGroupType::Calculator, MyVirtualKey::Number2, SUPPORTS_ALL }, NavCategoryInitializer { ViewMode::Scientific, SCIENTIFIC_ID, L"Scientific", L"ScientificMode", L"\uF196", CategoryGroupType::Calculator, MyVirtualKey::Number2, SUPPORTS_ALL },
NavCategoryInitializer { ViewMode::Programmer, PROGRAMMER_ID, L"Programmer", L"ProgrammerMode", L"\uECCE", CategoryGroupType::Calculator, MyVirtualKey::Number3, SUPPORTS_ALL }, NavCategoryInitializer { ViewMode::Programmer, PROGRAMMER_ID, L"Programmer", L"ProgrammerMode", L"\uECCE", CategoryGroupType::Calculator, MyVirtualKey::Number3, SUPPORTS_ALL },
NavCategoryInitializer { ViewMode::Date, DATE_ID, L"Date", L"DateCalculationMode", L"\uE787", CategoryGroupType::Calculator, MyVirtualKey::Number4, SUPPORTS_ALL }, NavCategoryInitializer { ViewMode::Date, DATE_ID, L"Date", L"DateCalculationMode", L"\uE787", CategoryGroupType::Calculator, MyVirtualKey::Number4, SUPPORTS_ALL },
NavCategoryInitializer { ViewMode::Graphing, GRAPHING_ID, L"Graphing", L"GraphingCalculatorMode", L"\uF770", CategoryGroupType::Calculator, MyVirtualKey::Number5, SUPPORTS_ALL },
NavCategoryInitializer { ViewMode::Currency, CURRENCY_ID, L"Currency", L"CategoryName_Currency", L"\uEB0D", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY }, NavCategoryInitializer { ViewMode::Currency, CURRENCY_ID, L"Currency", L"CategoryName_Currency", L"\uEB0D", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY },
NavCategoryInitializer { ViewMode::Volume, VOLUME_ID, L"Volume", L"CategoryName_Volume", L"\uF1AA", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY }, NavCategoryInitializer { ViewMode::Volume, VOLUME_ID, L"Volume", L"CategoryName_Volume", L"\uF1AA", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY },
NavCategoryInitializer { ViewMode::Length, LENGTH_ID, L"Length", L"CategoryName_Length", L"\uECC6", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY }, NavCategoryInitializer { ViewMode::Length, LENGTH_ID, L"Length", L"CategoryName_Length", L"\uECC6", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY },
@ -121,9 +123,15 @@ bool NavCategory::IsValidViewMode(ViewMode mode)
bool NavCategory::IsCalculatorViewMode(ViewMode mode) bool NavCategory::IsCalculatorViewMode(ViewMode mode)
{ {
// Historically, Date Calculator is not a Calculator mode // Historically, Calculator modes are Standard, Scientific, and Programmer.
// even though it is in the Calculator category. return !IsDateCalculatorViewMode(mode)
return !IsDateCalculatorViewMode(mode) && IsModeInCategoryGroup(mode, CategoryGroupType::Calculator); && !IsGraphingCalculatorViewMode(mode)
&& IsModeInCategoryGroup(mode, CategoryGroupType::Calculator);
}
bool NavCategory::IsGraphingCalculatorViewMode(ViewMode mode)
{
return mode == ViewMode::Graphing;
} }
bool NavCategory::IsDateCalculatorViewMode(ViewMode mode) bool NavCategory::IsDateCalculatorViewMode(ViewMode mode)

View file

@ -43,7 +43,8 @@ namespace CalculatorApp
Data = 13, Data = 13,
Pressure = 14, Pressure = 14,
Angle = 15, Angle = 15,
Currency = 16 Currency = 16,
Graphing = 17
}; };
public enum class CategoryGroupType public enum class CategoryGroupType
@ -163,6 +164,7 @@ namespace CalculatorApp
static bool IsValidViewMode(ViewMode mode); static bool IsValidViewMode(ViewMode mode);
static bool IsCalculatorViewMode(ViewMode mode); static bool IsCalculatorViewMode(ViewMode mode);
static bool IsGraphingCalculatorViewMode(ViewMode mode);
static bool IsDateCalculatorViewMode(ViewMode mode); static bool IsDateCalculatorViewMode(ViewMode mode);
static bool IsConverterViewMode(ViewMode mode); static bool IsConverterViewMode(ViewMode mode);

View file

@ -15,11 +15,13 @@
using namespace CalculatorApp; using namespace CalculatorApp;
using namespace CalculatorApp::Common; using namespace CalculatorApp::Common;
using namespace concurrency; using namespace concurrency;
using namespace Graphing::Renderer;
using namespace Platform; using namespace Platform;
using namespace std; using namespace std;
using namespace Utils; using namespace Utils;
using namespace Windows::ApplicationModel::Resources; using namespace Windows::ApplicationModel::Resources;
using namespace Windows::Storage::Streams; using namespace Windows::Storage::Streams;
using namespace Windows::UI;
using namespace Windows::UI::Core; using namespace Windows::UI::Core;
using namespace Windows::UI::ViewManagement; using namespace Windows::UI::ViewManagement;
using namespace Windows::UI::Xaml; using namespace Windows::UI::Xaml;
@ -68,7 +70,7 @@ int Utils::GetWindowId()
return windowId; return windowId;
} }
void Utils::RunOnUIThreadNonblocking(std::function<void()>&& function, _In_ CoreDispatcher^ currentDispatcher) void Utils::RunOnUIThreadNonblocking(function<void()>&& function, _In_ CoreDispatcher^ currentDispatcher)
{ {
if (currentDispatcher != nullptr) if (currentDispatcher != nullptr)
{ {
@ -91,7 +93,7 @@ wstring Utils::RemoveUnwantedCharsFromWstring(wstring input, wchar_t* unwantedCh
{ {
for (unsigned int i = 0; i < size; ++i) for (unsigned int i = 0; i < size; ++i)
{ {
input.erase(std::remove(input.begin(), input.end(), unwantedChars[i]), input.end()); input.erase(remove(input.begin(), input.end(), unwantedChars[i]), input.end());
} }
return input; return input;
} }
@ -225,3 +227,53 @@ task<String^> Utils::ReadFileFromFolder(IStorageFolder^ folder, String^ fileName
String^ contents = co_await FileIO::ReadTextAsync(file); String^ contents = co_await FileIO::ReadTextAsync(file);
co_return contents; co_return contents;
} }
bool Utils::AreColorsEqual(const Color& color1, const Color& color2)
{
return ((color1.A == color2.A)
&& (color1.R == color2.R)
&& (color1.G == color2.G)
&& (color1.B == color2.B));
}
String^ Utils::Trim(String^ value)
{
if (!value)
{
return nullptr;
}
wstring trimmed = value->Data();
Trim(trimmed);
return ref new String(trimmed.c_str());
}
void Utils::Trim(wstring& value)
{
TrimFront(value);
TrimBack(value);
}
void Utils::TrimFront(wstring& value)
{
value.erase(value.begin(), find_if(value.cbegin(), value.cend(), [](int ch){
return !isspace(ch);
}));
}
void Utils::TrimBack(wstring& value)
{
value.erase(find_if(value.crbegin(), value.crend(), [](int ch) {
return !isspace(ch);
}).base(), value.end());
}
bool operator==(const Color& color1, const Color& color2)
{
return equal_to<Color>()(color1, color2);
}
bool operator!=(const Color& color1, const Color& color2)
{
return !(color1 == color2);
}

View file

@ -6,6 +6,7 @@
#include "CalcManager/CalculatorVector.h" #include "CalcManager/CalculatorVector.h"
#include "CalcManager/ExpressionCommandInterface.h" #include "CalcManager/ExpressionCommandInterface.h"
#include "DelegateCommand.h" #include "DelegateCommand.h"
#include "GraphingInterfaces/GraphingEnums.h"
// Utility macros to make Models easier to write // Utility macros to make Models easier to write
// generates a member variable called m_<n> // generates a member variable called m_<n>
@ -301,6 +302,15 @@ namespace Utils
concurrency::task<void> WriteFileToFolder(Windows::Storage::IStorageFolder^ folder, Platform::String^ fileName, Platform::String^ contents, Windows::Storage::CreationCollisionOption collisionOption); concurrency::task<void> WriteFileToFolder(Windows::Storage::IStorageFolder^ folder, Platform::String^ fileName, Platform::String^ contents, Windows::Storage::CreationCollisionOption collisionOption);
concurrency::task<Platform::String^> ReadFileFromFolder(Windows::Storage::IStorageFolder^ folder, Platform::String^ fileName); concurrency::task<Platform::String^> ReadFileFromFolder(Windows::Storage::IStorageFolder^ folder, Platform::String^ fileName);
bool AreColorsEqual(const Windows::UI::Color& color1, const Windows::UI::Color& color2);
Platform::String^ Trim(Platform::String^ value);
void Trim(std::wstring& value);
void TrimFront(std::wstring& value);
void TrimBack(std::wstring& value);
} }
// This goes into the header to define the property, in the public: section of the class // This goes into the header to define the property, in the public: section of the class
@ -421,3 +431,18 @@ namespace CalculatorApp
return to; return to;
} }
} }
// There's no standard definition of equality for Windows::UI::Color structs.
// Define a template specialization for std::equal_to.
template<>
class std::equal_to<Windows::UI::Color>
{
public:
bool operator()(const Windows::UI::Color& color1, const Windows::UI::Color& color2)
{
return Utils::AreColorsEqual(color1, color2);
}
};
bool operator==(const Windows::UI::Color& color1, const Windows::UI::Color& color2);
bool operator!=(const Windows::UI::Color& color1, const Windows::UI::Color& color2);

View file

@ -0,0 +1,12 @@
#include "pch.h"
#include "EquationViewModel.h"
using namespace Windows::UI;
namespace CalculatorApp::ViewModel
{
EquationViewModel::EquationViewModel()
: m_LineColor{ Colors::Transparent }
{
}
}

View file

@ -0,0 +1,16 @@
#pragma once
#include "../Common/Utils.h"
namespace CalculatorApp::ViewModel
{
public ref class EquationViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
{
public:
EquationViewModel();
OBSERVABLE_OBJECT();
OBSERVABLE_PROPERTY_RW(Platform::String^, Expression);
OBSERVABLE_PROPERTY_RW(Windows::UI::Color, LineColor);
};
}

View file

@ -0,0 +1,18 @@
#include "pch.h"
#include "GraphingCalculatorViewModel.h"
using namespace CalculatorApp::ViewModel;
using namespace Platform::Collections;
namespace CalculatorApp::ViewModel
{
GraphingCalculatorViewModel::GraphingCalculatorViewModel()
: m_IsDecimalEnabled{ true }
, m_Equations{ ref new Vector< EquationViewModel^ >() }
{
}
void GraphingCalculatorViewModel::OnButtonPressed(Object^ parameter)
{
}
}

View file

@ -0,0 +1,23 @@
#pragma once
#include "../Common/Utils.h"
#include "EquationViewModel.h"
namespace CalculatorApp::ViewModel
{
[Windows::UI::Xaml::Data::Bindable]
public ref class GraphingCalculatorViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
{
public:
GraphingCalculatorViewModel();
OBSERVABLE_OBJECT();
OBSERVABLE_PROPERTY_R(bool, IsDecimalEnabled);
OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector< EquationViewModel^ >^, Equations);
COMMAND_FOR_METHOD(ButtonPressed, GraphingCalculatorViewModel::OnButtonPressed);
private:
void OnButtonPressed(Platform::Object^ parameter);
};
}

View file

@ -16,6 +16,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CalcViewModel", "CalcViewMo
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CalculatorUnitTests", "CalculatorUnitTests\CalculatorUnitTests.vcxproj", "{D3BAED2C-4B07-4E1D-8807-9D6499450349}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CalculatorUnitTests", "CalculatorUnitTests\CalculatorUnitTests.vcxproj", "{D3BAED2C-4B07-4E1D-8807-9D6499450349}"
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MockGraphingImpl", "MockGraphingImpl\MockGraphingImpl.vcxproj", "{52E03A58-B378-4F50-8BFB-F659FB85E790}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GraphControl", "GraphControl\GraphControl.vcxproj", "{E727A92B-F149-492C-8117-C039A298719B}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM = Debug|ARM Debug|ARM = Debug|ARM
@ -28,22 +32,6 @@ Global
Release|x86 = Release|x86 Release|x86 = Release|x86
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM.ActiveCfg = Debug|ARM
{311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM.Build.0 = Debug|ARM
{311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM64.ActiveCfg = Debug|ARM64
{311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM64.Build.0 = Debug|ARM64
{311E866D-8B93-4609-A691-265941FEE101}.Debug|x64.ActiveCfg = Debug|x64
{311E866D-8B93-4609-A691-265941FEE101}.Debug|x64.Build.0 = Debug|x64
{311E866D-8B93-4609-A691-265941FEE101}.Debug|x86.ActiveCfg = Debug|Win32
{311E866D-8B93-4609-A691-265941FEE101}.Debug|x86.Build.0 = Debug|Win32
{311E866D-8B93-4609-A691-265941FEE101}.Release|ARM.ActiveCfg = Release|ARM
{311E866D-8B93-4609-A691-265941FEE101}.Release|ARM.Build.0 = Release|ARM
{311E866D-8B93-4609-A691-265941FEE101}.Release|ARM64.ActiveCfg = Release|ARM64
{311E866D-8B93-4609-A691-265941FEE101}.Release|ARM64.Build.0 = Release|ARM64
{311E866D-8B93-4609-A691-265941FEE101}.Release|x64.ActiveCfg = Release|x64
{311E866D-8B93-4609-A691-265941FEE101}.Release|x64.Build.0 = Release|x64
{311E866D-8B93-4609-A691-265941FEE101}.Release|x86.ActiveCfg = Release|Win32
{311E866D-8B93-4609-A691-265941FEE101}.Release|x86.Build.0 = Release|Win32
{9447424A-0E05-4911-BEB8-E0354405F39A}.Debug|ARM.ActiveCfg = Debug|ARM {9447424A-0E05-4911-BEB8-E0354405F39A}.Debug|ARM.ActiveCfg = Debug|ARM
{9447424A-0E05-4911-BEB8-E0354405F39A}.Debug|ARM.Build.0 = Debug|ARM {9447424A-0E05-4911-BEB8-E0354405F39A}.Debug|ARM.Build.0 = Debug|ARM
{9447424A-0E05-4911-BEB8-E0354405F39A}.Debug|ARM.Deploy.0 = Debug|ARM {9447424A-0E05-4911-BEB8-E0354405F39A}.Debug|ARM.Deploy.0 = Debug|ARM
@ -68,6 +56,22 @@ Global
{9447424A-0E05-4911-BEB8-E0354405F39A}.Release|x86.ActiveCfg = Release|Win32 {9447424A-0E05-4911-BEB8-E0354405F39A}.Release|x86.ActiveCfg = Release|Win32
{9447424A-0E05-4911-BEB8-E0354405F39A}.Release|x86.Build.0 = Release|Win32 {9447424A-0E05-4911-BEB8-E0354405F39A}.Release|x86.Build.0 = Release|Win32
{9447424A-0E05-4911-BEB8-E0354405F39A}.Release|x86.Deploy.0 = Release|Win32 {9447424A-0E05-4911-BEB8-E0354405F39A}.Release|x86.Deploy.0 = Release|Win32
{311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM.ActiveCfg = Debug|ARM
{311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM.Build.0 = Debug|ARM
{311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM64.ActiveCfg = Debug|ARM64
{311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM64.Build.0 = Debug|ARM64
{311E866D-8B93-4609-A691-265941FEE101}.Debug|x64.ActiveCfg = Debug|x64
{311E866D-8B93-4609-A691-265941FEE101}.Debug|x64.Build.0 = Debug|x64
{311E866D-8B93-4609-A691-265941FEE101}.Debug|x86.ActiveCfg = Debug|Win32
{311E866D-8B93-4609-A691-265941FEE101}.Debug|x86.Build.0 = Debug|Win32
{311E866D-8B93-4609-A691-265941FEE101}.Release|ARM.ActiveCfg = Release|ARM
{311E866D-8B93-4609-A691-265941FEE101}.Release|ARM.Build.0 = Release|ARM
{311E866D-8B93-4609-A691-265941FEE101}.Release|ARM64.ActiveCfg = Release|ARM64
{311E866D-8B93-4609-A691-265941FEE101}.Release|ARM64.Build.0 = Release|ARM64
{311E866D-8B93-4609-A691-265941FEE101}.Release|x64.ActiveCfg = Release|x64
{311E866D-8B93-4609-A691-265941FEE101}.Release|x64.Build.0 = Release|x64
{311E866D-8B93-4609-A691-265941FEE101}.Release|x86.ActiveCfg = Release|Win32
{311E866D-8B93-4609-A691-265941FEE101}.Release|x86.Build.0 = Release|Win32
{90E9761D-9262-4773-942D-CAEAE75D7140}.Debug|ARM.ActiveCfg = Debug|ARM {90E9761D-9262-4773-942D-CAEAE75D7140}.Debug|ARM.ActiveCfg = Debug|ARM
{90E9761D-9262-4773-942D-CAEAE75D7140}.Debug|ARM.Build.0 = Debug|ARM {90E9761D-9262-4773-942D-CAEAE75D7140}.Debug|ARM.Build.0 = Debug|ARM
{90E9761D-9262-4773-942D-CAEAE75D7140}.Debug|ARM64.ActiveCfg = Debug|ARM64 {90E9761D-9262-4773-942D-CAEAE75D7140}.Debug|ARM64.ActiveCfg = Debug|ARM64
@ -100,6 +104,38 @@ Global
{D3BAED2C-4B07-4E1D-8807-9D6499450349}.Release|x86.ActiveCfg = Release|Win32 {D3BAED2C-4B07-4E1D-8807-9D6499450349}.Release|x86.ActiveCfg = Release|Win32
{D3BAED2C-4B07-4E1D-8807-9D6499450349}.Release|x86.Build.0 = Release|Win32 {D3BAED2C-4B07-4E1D-8807-9D6499450349}.Release|x86.Build.0 = Release|Win32
{D3BAED2C-4B07-4E1D-8807-9D6499450349}.Release|x86.Deploy.0 = Release|Win32 {D3BAED2C-4B07-4E1D-8807-9D6499450349}.Release|x86.Deploy.0 = Release|Win32
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|ARM.ActiveCfg = Debug|ARM
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|ARM.Build.0 = Debug|ARM
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|ARM64.ActiveCfg = Debug|ARM64
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|ARM64.Build.0 = Debug|ARM64
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|x64.ActiveCfg = Debug|x64
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|x64.Build.0 = Debug|x64
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|x86.ActiveCfg = Debug|Win32
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|x86.Build.0 = Debug|Win32
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|ARM.ActiveCfg = Release|ARM
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|ARM.Build.0 = Release|ARM
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|ARM64.ActiveCfg = Release|ARM64
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|ARM64.Build.0 = Release|ARM64
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|x64.ActiveCfg = Release|x64
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|x64.Build.0 = Release|x64
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|x86.ActiveCfg = Release|Win32
{52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|x86.Build.0 = Release|Win32
{E727A92B-F149-492C-8117-C039A298719B}.Debug|ARM.ActiveCfg = Debug|ARM
{E727A92B-F149-492C-8117-C039A298719B}.Debug|ARM.Build.0 = Debug|ARM
{E727A92B-F149-492C-8117-C039A298719B}.Debug|ARM64.ActiveCfg = Debug|ARM64
{E727A92B-F149-492C-8117-C039A298719B}.Debug|ARM64.Build.0 = Debug|ARM64
{E727A92B-F149-492C-8117-C039A298719B}.Debug|x64.ActiveCfg = Debug|x64
{E727A92B-F149-492C-8117-C039A298719B}.Debug|x64.Build.0 = Debug|x64
{E727A92B-F149-492C-8117-C039A298719B}.Debug|x86.ActiveCfg = Debug|Win32
{E727A92B-F149-492C-8117-C039A298719B}.Debug|x86.Build.0 = Debug|Win32
{E727A92B-F149-492C-8117-C039A298719B}.Release|ARM.ActiveCfg = Release|ARM
{E727A92B-F149-492C-8117-C039A298719B}.Release|ARM.Build.0 = Release|ARM
{E727A92B-F149-492C-8117-C039A298719B}.Release|ARM64.ActiveCfg = Release|ARM64
{E727A92B-F149-492C-8117-C039A298719B}.Release|ARM64.Build.0 = Release|ARM64
{E727A92B-F149-492C-8117-C039A298719B}.Release|x64.ActiveCfg = Release|x64
{E727A92B-F149-492C-8117-C039A298719B}.Release|x64.Build.0 = Release|x64
{E727A92B-F149-492C-8117-C039A298719B}.Release|x86.ActiveCfg = Release|Win32
{E727A92B-F149-492C-8117-C039A298719B}.Release|x86.Build.0 = Release|Win32
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View file

@ -130,6 +130,9 @@
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup> </ImportGroup>
<ImportGroup>
<Import Project="$(SolutionDir)\GraphControl\GraphingImplOverrides.props" Condition="Exists('$(SolutionDir)\GraphControl\GraphingImplOverrides.props')" />
</ImportGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<ClCompile> <ClCompile>
<AdditionalOptions>/bigobj /await /std:c++17 </AdditionalOptions> <AdditionalOptions>/bigobj /await /std:c++17 </AdditionalOptions>
@ -281,6 +284,12 @@
<ClInclude Include="Views\CalculatorStandardOperators.xaml.h"> <ClInclude Include="Views\CalculatorStandardOperators.xaml.h">
<DependentUpon>Views\CalculatorStandardOperators.xaml</DependentUpon> <DependentUpon>Views\CalculatorStandardOperators.xaml</DependentUpon>
</ClInclude> </ClInclude>
<ClInclude Include="Views\GraphingCalculator\EquationInputArea.xaml.h">
<DependentUpon>Views\GraphingCalculator\EquationInputArea.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="Views\GraphingCalculator\GraphingCalculator.xaml.h">
<DependentUpon>Views\GraphingCalculator\GraphingCalculator.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="Views\HistoryList.xaml.h"> <ClInclude Include="Views\HistoryList.xaml.h">
<DependentUpon>Views\HistoryList.xaml</DependentUpon> <DependentUpon>Views\HistoryList.xaml</DependentUpon>
</ClInclude> </ClInclude>
@ -336,6 +345,8 @@
</Page> </Page>
<Page Include="Views\CalculatorStandardOperators.xaml" /> <Page Include="Views\CalculatorStandardOperators.xaml" />
<Page Include="Views\DelighterUnitStyles.xaml" /> <Page Include="Views\DelighterUnitStyles.xaml" />
<Page Include="Views\GraphingCalculator\EquationInputArea.xaml" />
<Page Include="Views\GraphingCalculator\GraphingCalculator.xaml" />
<Page Include="Views\HistoryList.xaml" /> <Page Include="Views\HistoryList.xaml" />
<Page Include="Views\MainPage.xaml" /> <Page Include="Views\MainPage.xaml" />
<Page Include="Views\Memory.xaml" /> <Page Include="Views\Memory.xaml" />
@ -421,6 +432,12 @@
<ClCompile Include="Views\CalculatorStandardOperators.xaml.cpp"> <ClCompile Include="Views\CalculatorStandardOperators.xaml.cpp">
<DependentUpon>Views\CalculatorStandardOperators.xaml</DependentUpon> <DependentUpon>Views\CalculatorStandardOperators.xaml</DependentUpon>
</ClCompile> </ClCompile>
<ClCompile Include="Views\GraphingCalculator\EquationInputArea.xaml.cpp">
<DependentUpon>Views\GraphingCalculator\EquationInputArea.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="Views\GraphingCalculator\GraphingCalculator.xaml.cpp">
<DependentUpon>Views\GraphingCalculator\GraphingCalculator.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="Views\HistoryList.xaml.cpp"> <ClCompile Include="Views\HistoryList.xaml.cpp">
<DependentUpon>Views\HistoryList.xaml</DependentUpon> <DependentUpon>Views\HistoryList.xaml</DependentUpon>
</ClCompile> </ClCompile>
@ -829,6 +846,9 @@
<ProjectReference Include="..\CalcViewModel\CalcViewModel.vcxproj"> <ProjectReference Include="..\CalcViewModel\CalcViewModel.vcxproj">
<Project>{90e9761d-9262-4773-942d-caeae75d7140}</Project> <Project>{90e9761d-9262-4773-942d-caeae75d7140}</Project>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\GraphControl\GraphControl.vcxproj">
<Project>{e727a92b-f149-492c-8117-c039a298719b}</Project>
</ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<!-- Override MSBuild target to workaround VS bug with building using framework packages --> <!-- Override MSBuild target to workaround VS bug with building using framework packages -->

View file

@ -218,6 +218,9 @@
<Filter Include="Views\StateTriggers"> <Filter Include="Views\StateTriggers">
<UniqueIdentifier>{0120c344-0bc0-4a1d-b82c-df7945f46189}</UniqueIdentifier> <UniqueIdentifier>{0120c344-0bc0-4a1d-b82c-df7945f46189}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="Views\GraphingCalculator">
<UniqueIdentifier>{e23e2a6e-491b-4200-9bf7-d355a1ee695b}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ApplicationDefinition Include="App.xaml" /> <ApplicationDefinition Include="App.xaml" />
@ -480,6 +483,12 @@
<Page Include="Views\OperatorsPanel.xaml"> <Page Include="Views\OperatorsPanel.xaml">
<Filter>Views</Filter> <Filter>Views</Filter>
</Page> </Page>
<Page Include="Views\GraphingCalculator\EquationInputArea.xaml">
<Filter>Views\GraphingCalculator</Filter>
</Page>
<Page Include="Views\GraphingCalculator\GraphingCalculator.xaml">
<Filter>Views\GraphingCalculator</Filter>
</Page>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PRIResource Include="Resources\en-US\CEngineStrings.resw"> <PRIResource Include="Resources\en-US\CEngineStrings.resw">

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" IgnorableNamespaces="uap uap5 mp"> <Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" IgnorableNamespaces="uap uap5 mp">
<Identity Name="Microsoft.WindowsCalculator.Dev" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" Version="0.0.0.0" /> <Identity Name="Microsoft.WindowsCalculator.Graphing" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" Version="0.0.0.0" />
<mp:PhoneIdentity PhoneProductId="b58171c6-c70c-4266-a2e8-8f9c994f4456" PhonePublisherId="95d94207-0c7c-47ed-82db-d75c81153c35" /> <mp:PhoneIdentity PhoneProductId="b58171c6-c70c-4266-a2e8-8f9c994f4456" PhonePublisherId="95d94207-0c7c-47ed-82db-d75c81153c35" />
<Properties> <Properties>
<DisplayName>ms-resource:DevAppStoreName</DisplayName> <DisplayName>ms-resource:DevAppStoreName</DisplayName>

View file

@ -118,7 +118,7 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="AppName" xml:space="preserve"> <data name="AppName" xml:space="preserve">
<value>Calculator</value> <value>Graphing Calculator</value>
<comment>{@Appx_ShortDisplayName@}{StringCategory="Feature Title"} This is the title of the official application when published through Windows Store.</comment> <comment>{@Appx_ShortDisplayName@}{StringCategory="Feature Title"} This is the title of the official application when published through Windows Store.</comment>
</data> </data>
<data name="DevAppName" xml:space="preserve"> <data name="DevAppName" xml:space="preserve">
@ -1271,7 +1271,7 @@
</data> </data>
<data name="equalButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve"> <data name="equalButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Equals</value> <value>Equals</value>
<comment>Screen reader prompt for the invert button on the scientific operator keypad</comment> <comment>Screen reader prompt for the equals button on the scientific operator keypad</comment>
</data> </data>
<data name="shiftButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve"> <data name="shiftButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Inverse Function</value> <value>Inverse Function</value>
@ -3379,4 +3379,40 @@
<value>Microsoft Services Agreement</value> <value>Microsoft Services Agreement</value>
<comment>Displayed on a link to the Microsoft Services Agreement in the about this app information</comment> <comment>Displayed on a link to the Microsoft Services Agreement in the about this app information</comment>
</data> </data>
<data name="GraphingCalculatorModeText" xml:space="preserve">
<value>Graphing</value>
<comment>Name of the Graphing mode of the Calculator app. Displayed in the navigation menu.</comment>
</data>
<data name="graphingEqualButton.[using:CalculatorApp.Common]KeyboardShortcutManager.Character" xml:space="preserve">
<value>=</value>
<comment>{Locked}This is the character that should trigger this button. Note that it is a character and not a key, so it does not come from the Windows::System::VirtualKey enum.</comment>
</data>
<data name="graphingEqualButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Equals</value>
<comment>Screen reader prompt for the equal button on the graphing calculator operator keypad</comment>
</data>
<data name="plotButton.[using:CalculatorApp.Common]KeyboardShortcutManager.VirtualKey" xml:space="preserve">
<value>Enter</value>
<comment>{Locked}This is the value from the VirtualKey enum that maps to this button</comment>
</data>
<data name="plotButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Plot</value>
<comment>Screen reader prompt for the plot button on the graphing calculator operator keypad</comment>
</data>
<data name="xButton.[using:CalculatorApp.Common]KeyboardShortcutManager.VirtualKey" xml:space="preserve">
<value>X</value>
<comment>{Locked}This is the value that comes from the VirtualKey enum that represents the button. This value is not localized and must be one value that comes from the Windows::System::VirtualKey enum.</comment>
</data>
<data name="yButton.[using:CalculatorApp.Common]KeyboardShortcutManager.VirtualKey" xml:space="preserve">
<value>Y</value>
<comment>{Locked}This is the value that comes from the VirtualKey enum that represents the button. This value is not localized and must be one value that comes from the Windows::System::VirtualKey enum.</comment>
</data>
<data name="EquationInputAreaHeader.Text" xml:space="preserve">
<value>Equations</value>
<comment>The text that shows as the header for the equation input area in Graphing Calculator mode.</comment>
</data>
<data name="graphingPowerButton.[using:CalculatorApp.Common]KeyboardShortcutManager.Character" xml:space="preserve">
<value>^</value>
<comment>{Locked}This is the character that should trigger this button. Note that it is a character and not a key, so it does not come from the Windows::System::VirtualKey enum.</comment>
</data>
</root> </root>

View file

@ -0,0 +1,86 @@
<UserControl x:Class="CalculatorApp.EquationInputArea"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:CalculatorApp.ViewModel"
d:DesignHeight="300"
d:DesignWidth="400"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Margin="0,0,17,0">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock x:Uid="EquationInputAreaHeader"
Grid.Column="0"
Style="{ThemeResource SubheaderTextBlockStyle}"
FontSize="20"/>
<Button Grid.Column="1"
Click="AddEquationButton_Click"
Content="+"/>
</Grid>
<ListView x:Name="EquationInputList"
Grid.Row="2"
Margin="0,4,0,0"
IsItemClickEnabled="False"
ItemsSource="{x:Bind Equations}"
SelectionMode="None">
<ListView.Resources>
<x:Double x:Key="ItemHeight">40</x:Double>
</ListView.Resources>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0,0,17,1"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="vm:EquationViewModel">
<Grid Height="{StaticResource ItemHeight}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.Resources>
<SolidColorBrush x:Key="LineColorBrush" Color="{x:Bind LineColor, Mode=OneWay}"/>
</Grid.Resources>
<Grid.Background>
<StaticResource ResourceKey="LineColorBrush"/>
</Grid.Background>
<Button Width="{StaticResource ItemHeight}"
Height="{StaticResource ItemHeight}"
Background="{StaticResource LineColorBrush}"/>
<Rectangle x:Name="InputBackplate"
Grid.Column="1"
Margin="0,2,3,2"
Fill="White"/>
<TextBox Grid.Column="1"
Margin="0,2,3,2"
GotFocus="InputTextBox_GotFocus"
KeyUp="InputTextBox_KeyUp"
LostFocus="InputTextBox_LostFocus"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</UserControl>

View file

@ -0,0 +1,108 @@
#include "pch.h"
#include "EquationInputArea.xaml.h"
#include "CalcViewModel/Common/KeyboardShortcutManager.h"
using namespace CalculatorApp;
using namespace CalculatorApp::Common;
using namespace CalculatorApp::ViewModel;
using namespace Platform;
using namespace std;
using namespace Windows::System;
using namespace Windows::UI;
using namespace Windows::UI::ViewManagement;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Input;
namespace
{
const Color accentColor = (ref new UISettings())->GetColorValue(UIColorType::Accent);
const Color lineColors[] = {
accentColor,
Colors::DarkOrange,
Colors::MediumPurple,
Colors::ForestGreen,
Colors::BlueViolet,
Colors::DarkRed,
Colors::LightGoldenrodYellow,
Colors::DarkOliveGreen
};
const size_t lineColorsSize = std::size(lineColors);
StringReference EquationsPropertyName(L"Equations");
}
EquationInputArea::EquationInputArea()
: m_lastLineColorIndex{ -1 }
{
InitializeComponent();
}
void EquationInputArea::OnPropertyChanged(String^ propertyName)
{
if (propertyName == EquationsPropertyName)
{
OnEquationsPropertyChanged();
}
}
void EquationInputArea::OnEquationsPropertyChanged()
{
if (Equations != nullptr && Equations->Size == 0)
{
AddNewEquation();
// For now, the first equation needs to be y = 0.
// We can remove this when we can create empty graphs.
if (EquationViewModel^ eqvm = Equations->GetAt(0))
{
eqvm->Expression = L"0";
}
}
}
void EquationInputArea::AddEquationButton_Click(Object^ sender, RoutedEventArgs^ e)
{
AddNewEquation();
}
void EquationInputArea::AddNewEquation()
{
auto eq = ref new EquationViewModel();
eq->LineColor = GetNextLineColor();
Equations->Append(eq);
EquationInputList->ScrollIntoView(eq);
}
void EquationInputArea::InputTextBox_GotFocus(Object^ sender, RoutedEventArgs^ e)
{
KeyboardShortcutManager::HonorShortcuts(false);
}
void EquationInputArea::InputTextBox_LostFocus(Object^ sender, RoutedEventArgs^ e)
{
KeyboardShortcutManager::HonorShortcuts(true);
auto tb = static_cast<TextBox^>(sender);
auto eq = static_cast<EquationViewModel^>(tb->DataContext);
tb->Text = eq->Expression;
}
void EquationInputArea::InputTextBox_KeyUp(Object^ sender, KeyRoutedEventArgs^ e)
{
if (e->Key == VirtualKey::Enter)
{
auto tb = static_cast<TextBox^>(sender);
auto eq = static_cast<EquationViewModel^>(tb->DataContext);
eq->Expression = tb->Text;
e->Handled = true;
}
}
Color EquationInputArea::GetNextLineColor()
{
m_lastLineColorIndex = (m_lastLineColorIndex + 1) % lineColorsSize;
return lineColors[m_lastLineColorIndex];
}

View file

@ -0,0 +1,33 @@
#pragma once
#include "Views/GraphingCalculator/EquationInputArea.g.h"
#include "CalcViewModel/Common/Utils.h"
#include "CalcViewModel/GraphingCalculator/EquationViewModel.h"
namespace CalculatorApp
{
public ref class EquationInputArea sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
{
public:
EquationInputArea();
OBSERVABLE_OBJECT_CALLBACK(OnPropertyChanged);
OBSERVABLE_PROPERTY_RW(Windows::Foundation::Collections::IObservableVector< ViewModel::EquationViewModel^ >^, Equations);
private:
void OnPropertyChanged(Platform::String^ propertyName);
void OnEquationsPropertyChanged();
void AddEquationButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void AddNewEquation();
void InputTextBox_GotFocus(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void InputTextBox_LostFocus(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void InputTextBox_KeyUp(Platform::Object^ sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs^ e);
Windows::UI::Color GetNextLineColor();
private:
int m_lastLineColorIndex;
};
}

View file

@ -0,0 +1,242 @@
<UserControl x:Class="CalculatorApp.GraphingCalculator"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:CalculatorApp.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:graphControl="using:GraphControl"
xmlns:local="using:CalculatorApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:CalculatorApp.ViewModel"
DataContextChanged="GraphingCalculator_DataContextChanged"
mc:Ignorable="d">
<Grid x:Name="RootGrid">
<Grid.RowDefinitions>
<RowDefinition x:Name="RowHamburger" Height="{StaticResource HamburgerHeightGridLength}"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<!-- Left portion of the screen -->
<Grid x:Name="LeftGrid"
Grid.Row="1"
Grid.Column="0">
<graphControl:Grapher Grid.Row="0"
Margin="4,7,4,4"
EquationsSource="{x:Bind ViewModel.Equations, Mode=OneWay}"
ForceProportionalAxes="True"
UseSystemFocusVisuals="True">
<graphControl:Grapher.Background>
<SolidColorBrush Color="White"/>
</graphControl:Grapher.Background>
<graphControl:Grapher.EquationTemplate>
<DataTemplate x:DataType="vm:EquationViewModel">
<graphControl:Equation Expression="{x:Bind Expression, Mode=OneWay}" LineColor="{x:Bind LineColor, Mode=OneWay}"/>
</DataTemplate>
</graphControl:Grapher.EquationTemplate>
</graphControl:Grapher>
</Grid>
<!-- Right portion of the screen -->
<Grid x:Name="RightGrid"
Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
Margin="4,0,4,0">
<Grid.RowDefinitions>
<RowDefinition Height="5*"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
<local:EquationInputArea Grid.Row="0" Equations="{x:Bind ViewModel.Equations}"/>
<Grid x:Name="ButtonContainerGrid"
Grid.Row="1"
Margin="3,0,3,3"
UseLayoutRounding="False">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<controls:CalculatorButton x:Name="XButton"
x:Uid="xButton"
Grid.Row="0"
Grid.Column="0"
Style="{StaticResource OperatorButtonStyle}"
FontSize="16"
ButtonId="X"
Content="𝑥"
FlowDirection="LeftToRight"/>
<controls:CalculatorButton x:Name="YButton"
x:Uid="yButton"
Grid.Row="0"
Grid.Column="1"
Style="{StaticResource OperatorButtonStyle}"
FontSize="16"
ButtonId="Y"
Content="𝑦"
FlowDirection="LeftToRight"/>
<controls:CalculatorButton x:Name="PowerButton"
x:Uid="graphingPowerButton"
Grid.Row="0"
Grid.Column="2"
Style="{StaticResource OperatorButtonStyle}"
ButtonId="XPowerY"
Content="^"/>
<controls:CalculatorButton x:Name="squareRootButton"
x:Uid="squareRootButton"
Grid.Row="0"
Grid.Column="3"
Style="{StaticResource SymbolOperatorButtonStyle}"
FontFamily="{StaticResource SymbolThemeFontFamily}"
ButtonId="Sqrt"
Content="&#xE94B;"/>
<!-- Display controls -->
<controls:CalculatorButton x:Name="ClearButton"
x:Uid="clearButton"
Grid.Row="1"
Grid.Column="2"
Style="{StaticResource OperatorButtonStyle}"
FontSize="16"
ButtonId="Clear"
Content="C"/>
<controls:CalculatorButton x:Name="BackSpaceButton"
x:Uid="backSpaceButton"
Grid.Row="1"
Grid.Column="3"
Style="{StaticResource SymbolOperatorButtonStyle}"
FontSize="16"
ButtonId="Backspace"
Content="&#xE94F;"/>
<!-- Basic operators -->
<controls:CalculatorButton x:Name="EqualButton"
x:Uid="graphingEqualButton"
Grid.Row="0"
Grid.Column="4"
Style="{StaticResource AccentCalcButtonStyle}"
ButtonId="Equals"
Content="&#xE94E;"/>
<controls:CalculatorButton x:Name="DivideButton"
x:Uid="divideButton"
Grid.Row="1"
Grid.Column="4"
Style="{StaticResource AccentCalcButtonStyle}"
ButtonId="Divide"
Content="&#xE94A;"/>
<controls:CalculatorButton x:Name="MultiplyButton"
x:Uid="multiplyButton"
Grid.Row="2"
Grid.Column="4"
Style="{StaticResource AccentCalcButtonStyle}"
ButtonId="Multiply"
Content="&#xE947;"/>
<controls:CalculatorButton x:Name="MinusButton"
x:Uid="minusButton"
Grid.Row="3"
Grid.Column="4"
Style="{StaticResource AccentCalcButtonStyle}"
ButtonId="Subtract"
Content="&#xE949;"/>
<controls:CalculatorButton x:Name="PlusButton"
x:Uid="plusButton"
Grid.Row="4"
Grid.Column="4"
Style="{StaticResource AccentCalcButtonStyle}"
ButtonId="Add"
Content="&#xE948;"/>
<controls:CalculatorButton x:Name="logBase10Button"
x:Uid="logBase10Button"
Grid.Row="1"
Grid.Column="0"
Style="{StaticResource OperatorButtonStyle}"
ButtonId="LogBase10"
Content="log"/>
<controls:CalculatorButton x:Name="logBaseEButton"
x:Uid="logBaseEButton"
Grid.Row="1"
Grid.Column="1"
Style="{StaticResource OperatorButtonStyle}"
ButtonId="LogBaseE"
Content="ln"/>
<controls:CalculatorButton x:Name="powerOf10Button"
x:Uid="powerOf10Button"
Grid.Row="2"
Grid.Column="0"
Style="{StaticResource SymbolOperatorButtonStyle}"
AutomationProperties.AutomationId="powerOf10Button"
ButtonId="TenPowerX"
Content="&#xF7CC;"/>
<controls:CalculatorButton x:Name="powerOfEButton"
x:Uid="powerOfEButton"
Grid.Row="3"
Grid.Column="0"
Style="{StaticResource SymbolOperatorButtonStyle}"
ButtonId="EPowerX"
Content="&#xf7ce;"/>
<controls:CalculatorButton x:Name="piButton"
x:Uid="piButton"
Grid.Row="4"
Grid.Column="0"
Style="{StaticResource SymbolOperatorButtonStyle}"
FontSize="14"
ButtonId="Pi"
Content="&#xf7cf;"/>
<controls:CalculatorButton x:Name="openParenthesisButton"
x:Uid="openParenthesisButton"
Grid.Row="5"
Grid.Column="0"
Style="{StaticResource ParenthesisCalcButtonStyle}"
FontSize="19"
ButtonId="OpenParenthesis"
Content="("/>
<controls:CalculatorButton x:Name="closeParenthesisButton"
x:Uid="closeParenthesisButton"
Grid.Row="5"
Grid.Column="1"
Style="{StaticResource OperatorButtonStyle}"
FontSize="19"
ButtonId="CloseParenthesis"
Content=")"/>
<!-- The Numberpad -->
<local:NumberPad x:Name="NumberPad"
x:Uid="NumberPad"
Grid.Row="2"
Grid.RowSpan="4"
Grid.Column="1"
Grid.ColumnSpan="3"
ButtonStyle="{StaticResource NumericButtonStyle24}"/>
<controls:CalculatorButton x:Name="PlotButton"
x:Uid="plotButton"
Grid.Row="5"
Grid.Column="4"
Style="{StaticResource AccentCalcButtonStyle}"
FontFamily="{StaticResource SymbolThemeFontFamily}"
ButtonId="Plot"
Content="&#xE72A;"/>
</Grid>
</Grid>
</Grid>
</UserControl>

View file

@ -0,0 +1,51 @@
#include "pch.h"
#include "GraphingCalculator.xaml.h"
#include "CalcViewModel/Common/KeyboardShortcutManager.h"
#include "Controls/CalculationResult.h"
using namespace CalculatorApp;
using namespace CalculatorApp::Common;
using namespace CalculatorApp::Controls;
using namespace CalculatorApp::ViewModel;
using namespace concurrency;
using namespace GraphControl;
using namespace Platform;
using namespace std::chrono;
using namespace Utils;
using namespace Windows::Foundation::Collections;
using namespace Windows::Storage::Streams;
using namespace Windows::System;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Media::Imaging;
constexpr auto sc_ViewModelPropertyName = L"ViewModel";
GraphingCalculator::GraphingCalculator()
{
Equation::RegisterDependencyProperties();
Grapher::RegisterDependencyProperties();
InitializeComponent();
}
void GraphingCalculator::GraphingCalculator_DataContextChanged(FrameworkElement^ sender, DataContextChangedEventArgs^ args)
{
ViewModel = dynamic_cast<GraphingCalculatorViewModel^>(args->NewValue);
}
GraphingCalculatorViewModel^ GraphingCalculator::ViewModel::get()
{
return m_viewModel;
}
void GraphingCalculator::ViewModel::set(GraphingCalculatorViewModel^ vm)
{
if (m_viewModel != vm)
{
m_viewModel = vm;
RaisePropertyChanged(StringReference(sc_ViewModelPropertyName));
}
}

View file

@ -0,0 +1,28 @@
#pragma once
#include "Views\GraphingCalculator\GraphingCalculator.g.h"
#include "CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.h"
#include "Views\NumberPad.xaml.h"
namespace CalculatorApp
{
public ref class GraphingCalculator sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
{
public:
GraphingCalculator();
OBSERVABLE_OBJECT();
property CalculatorApp::ViewModel::GraphingCalculatorViewModel^ ViewModel
{
CalculatorApp::ViewModel::GraphingCalculatorViewModel^ get();
void set(CalculatorApp::ViewModel::GraphingCalculatorViewModel^ vm);
}
private:
void GraphingCalculator_DataContextChanged(Windows::UI::Xaml::FrameworkElement^ sender, Windows::UI::Xaml::DataContextChangedEventArgs^ args);
private:
CalculatorApp::ViewModel::GraphingCalculatorViewModel^ m_viewModel;
};
}

View file

@ -154,6 +154,9 @@
<Border x:Name="DateCalcHolder"> <Border x:Name="DateCalcHolder">
<!-- PLACEHOLDER!!!! This is where the date calculator goes when it is delay loaded --> <!-- PLACEHOLDER!!!! This is where the date calculator goes when it is delay loaded -->
</Border> </Border>
<Border x:Name="GraphingCalcHolder" Grid.Row="1">
<!-- PLACEHOLDER!!!! This is where the graphing calculator goes when it is delay loaded -->
</Border>
<Border x:Name="ConverterHolder"> <Border x:Name="ConverterHolder">
<!-- PLACEHOLDER!!!! This is where the converter goes when it is delay loaded --> <!-- PLACEHOLDER!!!! This is where the converter goes when it is delay loaded -->
</Border> </Border>

View file

@ -167,6 +167,10 @@ void MainPage::OnAppPropertyChanged(_In_ Platform::Object^ sender, _In_ Windows:
} }
EnsureDateCalculator(); EnsureDateCalculator();
} }
else if (newValue == ViewMode::Graphing)
{
EnsureGraphingCalculator();
}
else if (NavCategory::IsConverterViewMode(newValue)) else if (NavCategory::IsConverterViewMode(newValue))
{ {
if (m_model->CalculatorViewModel) if (m_model->CalculatorViewModel)
@ -196,9 +200,10 @@ void MainPage::OnAppPropertyChanged(_In_ Platform::Object^ sender, _In_ Windows:
void MainPage::ShowHideControls(ViewMode mode) void MainPage::ShowHideControls(ViewMode mode)
{ {
auto isCalcViewMode = NavCategory::IsCalculatorViewMode(mode); bool isCalcViewMode = NavCategory::IsCalculatorViewMode(mode);
auto isDateCalcViewMode = NavCategory::IsDateCalculatorViewMode(mode); bool isDateCalcViewMode = NavCategory::IsDateCalculatorViewMode(mode);
auto isConverterViewMode = NavCategory::IsConverterViewMode(mode); bool isGraphingCalcViewMode = NavCategory::IsGraphingCalculatorViewMode(mode);
bool isConverterViewMode = NavCategory::IsConverterViewMode(mode);
if (m_calculator) if (m_calculator)
{ {
@ -212,6 +217,12 @@ void MainPage::ShowHideControls(ViewMode mode)
m_dateCalculator->IsEnabled = isDateCalcViewMode; m_dateCalculator->IsEnabled = isDateCalcViewMode;
} }
if (m_graphingCalculator)
{
m_graphingCalculator->Visibility = BooleanToVisibilityConverter::Convert(isGraphingCalcViewMode);
m_graphingCalculator->IsEnabled = isGraphingCalcViewMode;
}
if (m_converter) if (m_converter)
{ {
m_converter->Visibility = BooleanToVisibilityConverter::Convert(isConverterViewMode); m_converter->Visibility = BooleanToVisibilityConverter::Convert(isConverterViewMode);
@ -239,7 +250,7 @@ void MainPage::UpdatePanelViewState()
void MainPage::OnPageLoaded(_In_ Object^, _In_ RoutedEventArgs^ args) void MainPage::OnPageLoaded(_In_ Object^, _In_ RoutedEventArgs^ args)
{ {
if (!m_converter && !m_calculator && !m_dateCalculator) if (!m_converter && !m_calculator && !m_dateCalculator && !m_graphingCalculator)
{ {
// We have just launched into our default mode (standard calc) so ensure calc is loaded // We have just launched into our default mode (standard calc) so ensure calc is loaded
EnsureCalculator(); EnsureCalculator();
@ -292,6 +303,10 @@ void MainPage::SetDefaultFocus()
{ {
m_dateCalculator->SetDefaultFocus(); m_dateCalculator->SetDefaultFocus();
} }
if (m_graphingCalculator != nullptr && m_graphingCalculator->Visibility == ::Visibility::Visible)
{
m_graphingCalculator->Focus(::FocusState::Programmatic);
}
if (m_converter != nullptr && m_converter->Visibility == ::Visibility::Visible) if (m_converter != nullptr && m_converter->Visibility == ::Visibility::Visible)
{ {
m_converter->SetDefaultFocus(); m_converter->SetDefaultFocus();
@ -354,6 +369,18 @@ void MainPage::EnsureDateCalculator()
} }
} }
void MainPage::EnsureGraphingCalculator()
{
if (!m_graphingCalculator)
{
m_graphingCalculator = ref new GraphingCalculator();
m_graphingCalculator->Name = L"GraphingCalculator";
m_graphingCalculator->DataContext = m_model->GraphingCalcViewModel;
GraphingCalcHolder->Child = m_graphingCalculator;
}
}
void MainPage::EnsureConverter() void MainPage::EnsureConverter()
{ {
if (!m_converter) if (!m_converter)
@ -571,7 +598,7 @@ void MainPage::SetHeaderAutomationName()
else else
{ {
wstring full; wstring full;
if (NavCategory::IsCalculatorViewMode(mode)) if (NavCategory::IsCalculatorViewMode(mode) || NavCategory::IsGraphingCalculatorViewMode(mode))
{ {
full = resProvider.GetResourceString(L"HeaderAutomationName_Calculator")->Data(); full = resProvider.GetResourceString(L"HeaderAutomationName_Calculator")->Data();
} }
@ -581,6 +608,7 @@ void MainPage::SetHeaderAutomationName()
} }
string::size_type found = full.find(L"%1"); string::size_type found = full.find(L"%1");
assert(found != wstring::npos);
wstring strMode = m_model->CategoryName->Data(); wstring strMode = m_model->CategoryName->Data();
full = full.replace(found, 2, strMode); full = full.replace(found, 2, strMode);

View file

@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved. // Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. // Licensed under the MIT License.
#pragma once #pragma once
@ -6,6 +6,7 @@
#include "Views/Calculator.xaml.h" #include "Views/Calculator.xaml.h"
#include "Views/MainPage.g.h" #include "Views/MainPage.g.h"
#include "Views/DateCalculator.xaml.h" #include "Views/DateCalculator.xaml.h"
#include "Views/GraphingCalculator/GraphingCalculator.xaml.h"
#include "Views/UnitConverter.xaml.h" #include "Views/UnitConverter.xaml.h"
#include "CalcViewModel/ApplicationViewModel.h" #include "CalcViewModel/ApplicationViewModel.h"
#include "Views/TitleBar.xaml.h" #include "Views/TitleBar.xaml.h"
@ -24,9 +25,9 @@ namespace CalculatorApp
{ {
public: public:
MainPage(); MainPage();
property CalculatorApp::ViewModel::ApplicationViewModel^ Model property ViewModel::ApplicationViewModel^ Model
{ {
CalculatorApp::ViewModel::ApplicationViewModel^ get(){ ViewModel::ApplicationViewModel^ get(){
return m_model; return m_model;
} }
} }
@ -36,7 +37,7 @@ namespace CalculatorApp
void SetDefaultFocus(); void SetDefaultFocus();
void SetHeaderAutomationName(); void SetHeaderAutomationName();
Windows::Foundation::Collections::IObservableVector<Platform::Object^>^ CreateUIElementsForCategories(_In_ Windows::Foundation::Collections::IObservableVector<CalculatorApp::Common::NavCategoryGroup^>^ categories); Windows::Foundation::Collections::IObservableVector<Platform::Object^>^ CreateUIElementsForCategories(_In_ Windows::Foundation::Collections::IObservableVector<Common::NavCategoryGroup^>^ categories);
protected: protected:
void OnNavigatedTo(_In_ Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override; void OnNavigatedTo(_In_ Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override;
@ -57,13 +58,13 @@ namespace CalculatorApp
void OnAboutFlyoutOpened(_In_ Platform::Object^ sender, _In_ Platform::Object^ e); void OnAboutFlyoutOpened(_In_ Platform::Object^ sender, _In_ Platform::Object^ e);
void OnAboutFlyoutClosed(_In_ Platform::Object^ sender, _In_ Platform::Object^ e); void OnAboutFlyoutClosed(_In_ Platform::Object^ sender, _In_ Platform::Object^ e);
Microsoft::UI::Xaml::Controls::NavigationViewItemHeader^ CreateNavViewHeaderFromGroup(CalculatorApp::Common::NavCategoryGroup^ group); Microsoft::UI::Xaml::Controls::NavigationViewItemHeader^ CreateNavViewHeaderFromGroup(Common::NavCategoryGroup^ group);
Microsoft::UI::Xaml::Controls::NavigationViewItem^ CreateNavViewItemFromCategory(CalculatorApp::Common::NavCategory^ category); Microsoft::UI::Xaml::Controls::NavigationViewItem^ CreateNavViewItemFromCategory(Common::NavCategory^ category);
Windows::Foundation::EventRegistrationToken m_fullscreenFlyoutClosedToken; Windows::Foundation::EventRegistrationToken m_fullscreenFlyoutClosedToken;
void OnFullscreenFlyoutClosed(); void OnFullscreenFlyoutClosed();
void ShowHideControls(CalculatorApp::Common::ViewMode mode); void ShowHideControls(Common::ViewMode mode);
void UpdateViewState(); void UpdateViewState();
void UpdatePanelViewState(); void UpdatePanelViewState();
@ -73,21 +74,23 @@ namespace CalculatorApp
void PinUnpinAppBarButtonOnClicked(_In_ Platform::Object^ sender, _In_ Windows::UI::Xaml::RoutedEventArgs^ e); void PinUnpinAppBarButtonOnClicked(_In_ Platform::Object^ sender, _In_ Windows::UI::Xaml::RoutedEventArgs^ e);
void EnsureCalculator(); void EnsureCalculator();
void EnsureConverter();
void EnsureDateCalculator(); void EnsureDateCalculator();
void EnsureGraphingCalculator();
void EnsureConverter();
void ShowAboutPage(); void ShowAboutPage();
void AnnounceCategoryName(); void AnnounceCategoryName();
CalculatorApp::Calculator^ m_calculator; Calculator^ m_calculator;
CalculatorApp::UnitConverter^ m_converter; GraphingCalculator^ m_graphingCalculator;
CalculatorApp::DateCalculator^ m_dateCalculator; UnitConverter^ m_converter;
DateCalculator^ m_dateCalculator;
Windows::Foundation::EventRegistrationToken _windowSizeEventToken; Windows::Foundation::EventRegistrationToken _windowSizeEventToken;
Windows::Foundation::EventRegistrationToken m_hardwareButtonsBackPressedToken; Windows::Foundation::EventRegistrationToken m_hardwareButtonsBackPressedToken;
Windows::Foundation::EventRegistrationToken m_colorValuesChangedToken; Windows::Foundation::EventRegistrationToken m_colorValuesChangedToken;
CalculatorApp::ViewModel::ApplicationViewModel^ m_model; ViewModel::ApplicationViewModel^ m_model;
Windows::UI::ViewManagement::UISettings^ m_uiSettings; Windows::UI::ViewManagement::UISettings^ m_uiSettings;
std::unique_ptr<CalculatorApp::Common::TitleBarHelper> m_titleBarHelper; std::unique_ptr<Common::TitleBarHelper> m_titleBarHelper;
}; };
} }

View file

@ -97,7 +97,7 @@ namespace CalculatorUnitTests
// Boundary testing // Boundary testing
VERIFY_ARE_EQUAL(ViewMode::None, NavCategory::Deserialize(ref new Box<int>(-1))); VERIFY_ARE_EQUAL(ViewMode::None, NavCategory::Deserialize(ref new Box<int>(-1)));
VERIFY_ARE_EQUAL(ViewMode::None, NavCategory::Deserialize(ref new Box<int>(17))); VERIFY_ARE_EQUAL(ViewMode::None, NavCategory::Deserialize(ref new Box<int>(18)));
} }
void NavCategoryUnitTests::IsValidViewMode_AllValid() void NavCategoryUnitTests::IsValidViewMode_AllValid()
@ -106,6 +106,7 @@ namespace CalculatorUnitTests
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Scientific)); VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Scientific));
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Programmer)); VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Programmer));
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Date)); VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Date));
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Graphing));
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Currency)); VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Currency));
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Volume)); VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Volume));
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Length)); VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Length));
@ -125,9 +126,9 @@ namespace CalculatorUnitTests
{ {
VERIFY_IS_FALSE(NavCategory::IsValidViewMode(ViewMode::None)); VERIFY_IS_FALSE(NavCategory::IsValidViewMode(ViewMode::None));
// There are 17 total options so int 17 should be the first invalid // There are 18 total options so int 18 should be the first invalid
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(static_cast<ViewMode>(16))); VERIFY_IS_TRUE(NavCategory::IsValidViewMode(static_cast<ViewMode>(17)));
VERIFY_IS_FALSE(NavCategory::IsValidViewMode(static_cast<ViewMode>(17))); VERIFY_IS_FALSE(NavCategory::IsValidViewMode(static_cast<ViewMode>(18)));
// Also verify the lower bound // Also verify the lower bound
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(static_cast<ViewMode>(0))); VERIFY_IS_TRUE(NavCategory::IsValidViewMode(static_cast<ViewMode>(0)));
@ -141,6 +142,7 @@ namespace CalculatorUnitTests
VERIFY_IS_TRUE(NavCategory::IsCalculatorViewMode(ViewMode::Programmer)); VERIFY_IS_TRUE(NavCategory::IsCalculatorViewMode(ViewMode::Programmer));
VERIFY_IS_FALSE(NavCategory::IsCalculatorViewMode(ViewMode::Date)); VERIFY_IS_FALSE(NavCategory::IsCalculatorViewMode(ViewMode::Date));
VERIFY_IS_FALSE(NavCategory::IsCalculatorViewMode(ViewMode::Graphing));
VERIFY_IS_FALSE(NavCategory::IsCalculatorViewMode(ViewMode::Currency)); VERIFY_IS_FALSE(NavCategory::IsCalculatorViewMode(ViewMode::Currency));
VERIFY_IS_FALSE(NavCategory::IsCalculatorViewMode(ViewMode::Volume)); VERIFY_IS_FALSE(NavCategory::IsCalculatorViewMode(ViewMode::Volume));
@ -165,6 +167,8 @@ namespace CalculatorUnitTests
VERIFY_IS_TRUE(NavCategory::IsDateCalculatorViewMode(ViewMode::Date)); VERIFY_IS_TRUE(NavCategory::IsDateCalculatorViewMode(ViewMode::Date));
VERIFY_IS_FALSE(NavCategory::IsDateCalculatorViewMode(ViewMode::Graphing));
VERIFY_IS_FALSE(NavCategory::IsDateCalculatorViewMode(ViewMode::Currency)); VERIFY_IS_FALSE(NavCategory::IsDateCalculatorViewMode(ViewMode::Currency));
VERIFY_IS_FALSE(NavCategory::IsDateCalculatorViewMode(ViewMode::Volume)); VERIFY_IS_FALSE(NavCategory::IsDateCalculatorViewMode(ViewMode::Volume));
VERIFY_IS_FALSE(NavCategory::IsDateCalculatorViewMode(ViewMode::Length)); VERIFY_IS_FALSE(NavCategory::IsDateCalculatorViewMode(ViewMode::Length));
@ -185,8 +189,8 @@ namespace CalculatorUnitTests
VERIFY_IS_FALSE(NavCategory::IsConverterViewMode(ViewMode::Standard)); VERIFY_IS_FALSE(NavCategory::IsConverterViewMode(ViewMode::Standard));
VERIFY_IS_FALSE(NavCategory::IsConverterViewMode(ViewMode::Scientific)); VERIFY_IS_FALSE(NavCategory::IsConverterViewMode(ViewMode::Scientific));
VERIFY_IS_FALSE(NavCategory::IsConverterViewMode(ViewMode::Programmer)); VERIFY_IS_FALSE(NavCategory::IsConverterViewMode(ViewMode::Programmer));
VERIFY_IS_FALSE(NavCategory::IsConverterViewMode(ViewMode::Date)); VERIFY_IS_FALSE(NavCategory::IsConverterViewMode(ViewMode::Date));
VERIFY_IS_FALSE(NavCategory::IsConverterViewMode(ViewMode::Graphing));
VERIFY_IS_TRUE(NavCategory::IsConverterViewMode(ViewMode::Currency)); VERIFY_IS_TRUE(NavCategory::IsConverterViewMode(ViewMode::Currency));
VERIFY_IS_TRUE(NavCategory::IsConverterViewMode(ViewMode::Volume)); VERIFY_IS_TRUE(NavCategory::IsConverterViewMode(ViewMode::Volume));
@ -209,6 +213,7 @@ namespace CalculatorUnitTests
VERIFY_ARE_EQUAL(StringReference(L"Scientific"), NavCategory::GetFriendlyName(ViewMode::Scientific)); VERIFY_ARE_EQUAL(StringReference(L"Scientific"), NavCategory::GetFriendlyName(ViewMode::Scientific));
VERIFY_ARE_EQUAL(StringReference(L"Programmer"), NavCategory::GetFriendlyName(ViewMode::Programmer)); VERIFY_ARE_EQUAL(StringReference(L"Programmer"), NavCategory::GetFriendlyName(ViewMode::Programmer));
VERIFY_ARE_EQUAL(StringReference(L"Date"), NavCategory::GetFriendlyName(ViewMode::Date)); VERIFY_ARE_EQUAL(StringReference(L"Date"), NavCategory::GetFriendlyName(ViewMode::Date));
VERIFY_ARE_EQUAL(StringReference(L"Graphing"), NavCategory::GetFriendlyName(ViewMode::Graphing));
VERIFY_ARE_EQUAL(StringReference(L"Currency"), NavCategory::GetFriendlyName(ViewMode::Currency)); VERIFY_ARE_EQUAL(StringReference(L"Currency"), NavCategory::GetFriendlyName(ViewMode::Currency));
VERIFY_ARE_EQUAL(StringReference(L"Volume"), NavCategory::GetFriendlyName(ViewMode::Volume)); VERIFY_ARE_EQUAL(StringReference(L"Volume"), NavCategory::GetFriendlyName(ViewMode::Volume));
VERIFY_ARE_EQUAL(StringReference(L"Length"), NavCategory::GetFriendlyName(ViewMode::Length)); VERIFY_ARE_EQUAL(StringReference(L"Length"), NavCategory::GetFriendlyName(ViewMode::Length));
@ -232,6 +237,7 @@ namespace CalculatorUnitTests
VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, NavCategory::GetGroupType(ViewMode::Scientific)); VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, NavCategory::GetGroupType(ViewMode::Scientific));
VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, NavCategory::GetGroupType(ViewMode::Programmer)); VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, NavCategory::GetGroupType(ViewMode::Programmer));
VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, NavCategory::GetGroupType(ViewMode::Date)); VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, NavCategory::GetGroupType(ViewMode::Date));
VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, NavCategory::GetGroupType(ViewMode::Graphing));
VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategory::GetGroupType(ViewMode::Currency)); VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategory::GetGroupType(ViewMode::Currency));
VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategory::GetGroupType(ViewMode::Volume)); VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategory::GetGroupType(ViewMode::Volume));
@ -251,11 +257,12 @@ namespace CalculatorUnitTests
void NavCategoryUnitTests::GetIndex() void NavCategoryUnitTests::GetIndex()
{ {
// Index is the 0-based ordering of modes // Index is the 0-based ordering of modes
vector<ViewMode> orderedModes = { ViewMode orderedModes[] = {
ViewMode::Standard, ViewMode::Standard,
ViewMode::Scientific, ViewMode::Scientific,
ViewMode::Programmer, ViewMode::Programmer,
ViewMode::Date, ViewMode::Date,
ViewMode::Graphing,
ViewMode::Currency, ViewMode::Currency,
ViewMode::Volume, ViewMode::Volume,
ViewMode::Length, ViewMode::Length,
@ -271,7 +278,8 @@ namespace CalculatorUnitTests
ViewMode::Angle ViewMode::Angle
}; };
for (size_t index = 0; index < orderedModes.size(); index++) auto orderedModesSize = size(orderedModes);
for (size_t index = 0; index < orderedModesSize; index++)
{ {
ViewMode mode = orderedModes[index]; ViewMode mode = orderedModes[index];
VERIFY_ARE_EQUAL(index, (size_t)NavCategory::GetIndex(mode)); VERIFY_ARE_EQUAL(index, (size_t)NavCategory::GetIndex(mode));
@ -283,11 +291,12 @@ namespace CalculatorUnitTests
void NavCategoryUnitTests::GetPosition() void NavCategoryUnitTests::GetPosition()
{ {
// Position is the 1-based ordering of modes // Position is the 1-based ordering of modes
vector<ViewMode> orderedModes = { ViewMode orderedModes[] = {
ViewMode::Standard, ViewMode::Standard,
ViewMode::Scientific, ViewMode::Scientific,
ViewMode::Programmer, ViewMode::Programmer,
ViewMode::Date, ViewMode::Date,
ViewMode::Graphing,
ViewMode::Currency, ViewMode::Currency,
ViewMode::Volume, ViewMode::Volume,
ViewMode::Length, ViewMode::Length,
@ -303,7 +312,8 @@ namespace CalculatorUnitTests
ViewMode::Angle ViewMode::Angle
}; };
for (size_t pos = 1; pos <= orderedModes.size(); pos++) auto orderedModesSize = size(orderedModes);
for (size_t pos = 1; pos <= orderedModesSize; pos++)
{ {
ViewMode mode = orderedModes[pos - 1]; ViewMode mode = orderedModes[pos - 1];
VERIFY_ARE_EQUAL(pos, (size_t)NavCategory::GetPosition(mode)); VERIFY_ARE_EQUAL(pos, (size_t)NavCategory::GetPosition(mode));
@ -327,6 +337,7 @@ namespace CalculatorUnitTests
VERIFY_ARE_EQUAL(1, NavCategory::GetIndexInGroup(ViewMode::Scientific, CategoryGroupType::Calculator)); VERIFY_ARE_EQUAL(1, NavCategory::GetIndexInGroup(ViewMode::Scientific, CategoryGroupType::Calculator));
VERIFY_ARE_EQUAL(2, NavCategory::GetIndexInGroup(ViewMode::Programmer, CategoryGroupType::Calculator)); VERIFY_ARE_EQUAL(2, NavCategory::GetIndexInGroup(ViewMode::Programmer, CategoryGroupType::Calculator));
VERIFY_ARE_EQUAL(3, NavCategory::GetIndexInGroup(ViewMode::Date, CategoryGroupType::Calculator)); VERIFY_ARE_EQUAL(3, NavCategory::GetIndexInGroup(ViewMode::Date, CategoryGroupType::Calculator));
VERIFY_ARE_EQUAL(4, NavCategory::GetIndexInGroup(ViewMode::Graphing, CategoryGroupType::Calculator));
VERIFY_ARE_EQUAL(0, NavCategory::GetIndexInGroup(ViewMode::Currency, CategoryGroupType::Converter)); VERIFY_ARE_EQUAL(0, NavCategory::GetIndexInGroup(ViewMode::Currency, CategoryGroupType::Converter));
VERIFY_ARE_EQUAL(1, NavCategory::GetIndexInGroup(ViewMode::Volume, CategoryGroupType::Converter)); VERIFY_ARE_EQUAL(1, NavCategory::GetIndexInGroup(ViewMode::Volume, CategoryGroupType::Converter));
@ -351,6 +362,8 @@ namespace CalculatorUnitTests
VERIFY_ARE_EQUAL(ViewMode::Standard, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number1)); VERIFY_ARE_EQUAL(ViewMode::Standard, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number1));
VERIFY_ARE_EQUAL(ViewMode::Scientific, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number2)); VERIFY_ARE_EQUAL(ViewMode::Scientific, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number2));
VERIFY_ARE_EQUAL(ViewMode::Programmer, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number3)); VERIFY_ARE_EQUAL(ViewMode::Programmer, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number3));
VERIFY_ARE_EQUAL(ViewMode::Date, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number4));
VERIFY_ARE_EQUAL(ViewMode::Graphing, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number5));
} }
TEST_CLASS(NavCategoryGroupUnitTests) TEST_CLASS(NavCategoryGroupUnitTests)
@ -380,29 +393,30 @@ namespace CalculatorUnitTests
VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, calculatorGroup->GroupType); VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, calculatorGroup->GroupType);
IObservableVector<NavCategory^>^ calculatorCategories = calculatorGroup->Categories; IObservableVector<NavCategory^>^ calculatorCategories = calculatorGroup->Categories;
VERIFY_ARE_EQUAL(4, calculatorCategories->Size); VERIFY_ARE_EQUAL(5, calculatorCategories->Size);
ValidateNavCategory(calculatorCategories, 0u, ViewMode::Standard, 1); ValidateNavCategory(calculatorCategories, 0u, ViewMode::Standard, 1);
ValidateNavCategory(calculatorCategories, 1u, ViewMode::Scientific, 2); ValidateNavCategory(calculatorCategories, 1u, ViewMode::Scientific, 2);
ValidateNavCategory(calculatorCategories, 2u, ViewMode::Programmer, 3); ValidateNavCategory(calculatorCategories, 2u, ViewMode::Programmer, 3);
ValidateNavCategory(calculatorCategories, 3u, ViewMode::Date, 4); ValidateNavCategory(calculatorCategories, 3u, ViewMode::Date, 4);
ValidateNavCategory(calculatorCategories, 4u, ViewMode::Graphing, 5);
NavCategoryGroup^ converterGroup = menuOptions->GetAt(1); NavCategoryGroup^ converterGroup = menuOptions->GetAt(1);
VERIFY_ARE_EQUAL(CategoryGroupType::Converter, converterGroup->GroupType); VERIFY_ARE_EQUAL(CategoryGroupType::Converter, converterGroup->GroupType);
IObservableVector<NavCategory^>^ converterCategories = converterGroup->Categories; IObservableVector<NavCategory^>^ converterCategories = converterGroup->Categories;
VERIFY_ARE_EQUAL(13, converterCategories->Size); VERIFY_ARE_EQUAL(13, converterCategories->Size);
ValidateNavCategory(converterCategories, 0u, ViewMode::Currency, 5); ValidateNavCategory(converterCategories, 0u, ViewMode::Currency, 6);
ValidateNavCategory(converterCategories, 1u, ViewMode::Volume, 6); ValidateNavCategory(converterCategories, 1u, ViewMode::Volume, 7);
ValidateNavCategory(converterCategories, 2u, ViewMode::Length, 7); ValidateNavCategory(converterCategories, 2u, ViewMode::Length, 8);
ValidateNavCategory(converterCategories, 3u, ViewMode::Weight, 8); ValidateNavCategory(converterCategories, 3u, ViewMode::Weight, 9);
ValidateNavCategory(converterCategories, 4u, ViewMode::Temperature, 9); ValidateNavCategory(converterCategories, 4u, ViewMode::Temperature, 10);
ValidateNavCategory(converterCategories, 5u, ViewMode::Energy, 10); ValidateNavCategory(converterCategories, 5u, ViewMode::Energy, 11);
ValidateNavCategory(converterCategories, 6u, ViewMode::Area, 11); ValidateNavCategory(converterCategories, 6u, ViewMode::Area, 12);
ValidateNavCategory(converterCategories, 7u, ViewMode::Speed, 12); ValidateNavCategory(converterCategories, 7u, ViewMode::Speed, 13);
ValidateNavCategory(converterCategories, 8u, ViewMode::Time, 13); ValidateNavCategory(converterCategories, 8u, ViewMode::Time, 14);
ValidateNavCategory(converterCategories, 9u, ViewMode::Power, 14); ValidateNavCategory(converterCategories, 9u, ViewMode::Power, 15);
ValidateNavCategory(converterCategories, 10u, ViewMode::Data, 15); ValidateNavCategory(converterCategories, 10u, ViewMode::Data, 16);
ValidateNavCategory(converterCategories, 11u, ViewMode::Pressure, 16); ValidateNavCategory(converterCategories, 11u, ViewMode::Pressure, 17);
ValidateNavCategory(converterCategories, 12u, ViewMode::Angle, 17); ValidateNavCategory(converterCategories, 12u, ViewMode::Angle, 18);
} }
} }

View file

@ -0,0 +1,104 @@
#include "pch.h"
#include "Equation.h"
using namespace Platform;
using namespace std;
using namespace Windows::UI;
using namespace Windows::UI::ViewManagement;
using namespace Windows::UI::Xaml;
namespace GraphControl
{
DependencyProperty^ Equation::s_expressionProperty;
static constexpr auto s_propertyName_Expression = L"Expression";
DependencyProperty^ Equation::s_lineColorProperty;
static constexpr auto s_propertyName_LineColor = L"LineColor";
namespace EquationProperties
{
String^ Expression = StringReference(s_propertyName_Expression);
String^ LineColor = StringReference(s_propertyName_LineColor);
}
void Equation::RegisterDependencyProperties()
{
if (!s_expressionProperty)
{
s_expressionProperty = DependencyProperty::Register(
EquationProperties::Expression,
String::typeid,
Equation::typeid,
ref new PropertyMetadata(
nullptr,
ref new PropertyChangedCallback(&Equation::OnCustomDependencyPropertyChanged)));
}
if (!s_lineColorProperty)
{
// Default line color should be the user's accent color
auto uiSettings = ref new UISettings();
Color accentColor = uiSettings->GetColorValue(UIColorType::Accent);
s_lineColorProperty = DependencyProperty::Register(
EquationProperties::LineColor,
Color::typeid,
Equation::typeid,
ref new PropertyMetadata(
accentColor,
ref new PropertyChangedCallback(&Equation::OnCustomDependencyPropertyChanged)));
}
}
void Equation::OnCustomDependencyPropertyChanged(DependencyObject^ obj, DependencyPropertyChangedEventArgs^ args)
{
if (auto eq = static_cast<Equation^>(obj))
{
String^ propertyName = nullptr;
if (args->Property == s_expressionProperty)
{
propertyName = EquationProperties::Expression;
}
else if (args->Property == s_lineColorProperty)
{
propertyName = EquationProperties::LineColor;
}
eq->PropertyChanged(eq, propertyName);
}
}
wstring Equation::GetRequest()
{
wstringstream ss{};
ss << GetRequestHeader()
<< GetExpression()
<< GetLineColor()
<< L")";
return ss.str();
}
wstring Equation::GetRequestHeader()
{
wstring expr{ Expression->Data() };
if (expr.find(L"=") != wstring::npos)
{
return L"plotEq2d("s;
}
else
{
return L"plot2d("s;
}
}
wstring Equation::GetExpression()
{
return Expression->Data();
}
wstring Equation::GetLineColor()
{
return L""s;
}
}

View file

@ -0,0 +1,80 @@
#pragma once
namespace GraphControl
{
namespace EquationProperties
{
extern Platform::String^ Expression;
extern Platform::String^ LineColor;
}
ref class Equation;
delegate void PropertyChangedEventHandler(Equation^ sender, Platform::String^ propertyName);
[Windows::UI::Xaml::Data::Bindable]
public ref class Equation sealed : public Windows::UI::Xaml::FrameworkElement
{
public:
Equation() {}
static void RegisterDependencyProperties();
#pragma region Platform::String^ Expression DependencyProperty
static property Windows::UI::Xaml::DependencyProperty^ ExpressionProperty
{
Windows::UI::Xaml::DependencyProperty^ get()
{
return s_expressionProperty;
}
}
property Platform::String^ Expression
{
Platform::String^ get()
{
return static_cast<Platform::String^>(GetValue(s_expressionProperty));
}
void set(Platform::String^ value)
{
SetValue(s_expressionProperty, value);
}
}
#pragma endregion
#pragma region Windows::UI::Color LineColor DependencyProperty
static property Windows::UI::Xaml::DependencyProperty^ LineColorProperty
{
Windows::UI::Xaml::DependencyProperty^ get()
{
return s_lineColorProperty;
}
}
property Windows::UI::Color LineColor
{
Windows::UI::Color get()
{
return static_cast<Windows::UI::Color>(GetValue(s_lineColorProperty));
}
void set(Windows::UI::Color value)
{
SetValue(s_lineColorProperty, value);
}
}
#pragma endregion
internal:
event PropertyChangedEventHandler^ PropertyChanged;
std::wstring GetRequest();
private:
static void OnCustomDependencyPropertyChanged(Windows::UI::Xaml::DependencyObject^ obj, Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ args);
std::wstring GetRequestHeader();
std::wstring GetExpression();
std::wstring GetLineColor();
private:
static Windows::UI::Xaml::DependencyProperty^ s_expressionProperty;
static Windows::UI::Xaml::DependencyProperty^ s_lineColorProperty;
};
}

View file

@ -0,0 +1,176 @@
#pragma once
#include "Equation.h"
namespace GraphControl
{
delegate void EquationChangedEventHandler();
public ref class EquationCollection sealed : public Windows::Foundation::Collections::IObservableVector< GraphControl::Equation^ >
{
public:
virtual ~EquationCollection()
{
}
#pragma region IIterable
virtual Windows::Foundation::Collections::IIterator< GraphControl::Equation^ >^ First()
{
return m_vector->First();
}
#pragma endregion
#pragma region IVector
virtual property unsigned int Size
{
unsigned int get()
{
return m_vector->Size;
}
}
virtual void Append(GraphControl::Equation^ value)
{
m_vector->Append(value);
m_tokens.emplace_back(
value->PropertyChanged += ref new GraphControl::PropertyChangedEventHandler(this, &EquationCollection::OnEquationPropertyChanged)
);
EquationChanged();
}
virtual void Clear()
{
auto numEqs = m_vector->Size;
for (auto i = 0u; i < numEqs; i++)
{
m_vector->GetAt(i)->PropertyChanged -= m_tokens[i];
}
m_vector->Clear();
m_tokens.clear();
EquationChanged();
}
virtual GraphControl::Equation^ GetAt(unsigned int index)
{
return m_vector->GetAt(index);
}
virtual unsigned int GetMany(unsigned int startIndex, Platform::WriteOnlyArray< GraphControl::Equation^ >^ items)
{
return m_vector->GetMany(startIndex, items);
}
virtual Windows::Foundation::Collections::IVectorView< GraphControl::Equation^ >^ GetView()
{
return m_vector->GetView();
}
virtual Platform::Boolean IndexOf(GraphControl::Equation^ value, unsigned int *index)
{
return m_vector->IndexOf(value, index);
}
virtual void InsertAt(unsigned int index, GraphControl::Equation^ value)
{
m_vector->InsertAt(index, value);
m_tokens.insert(
m_tokens.begin() + index,
value->PropertyChanged += ref new PropertyChangedEventHandler(this, &EquationCollection::OnEquationPropertyChanged)
);
EquationChanged();
}
virtual void RemoveAt(unsigned int index)
{
m_vector->GetAt(index)->PropertyChanged -= m_tokens[index];
m_vector->RemoveAt(index);
m_tokens.erase(m_tokens.begin() + index);
EquationChanged();
}
virtual void RemoveAtEnd()
{
auto size = m_vector->Size;
if (size > 0)
{
m_vector->GetAt(size - 1)->PropertyChanged -= *m_tokens.rbegin();
m_tokens.erase(m_tokens.end() - 1);
}
m_vector->RemoveAtEnd();
EquationChanged();
}
virtual void ReplaceAll(const Platform::Array< GraphControl::Equation^ >^ items)
{
auto size = m_vector->Size;
for (auto i = 0u; i < size; i++)
{
m_vector->GetAt(i)->PropertyChanged -= m_tokens[i];
}
size = items->Length;
m_tokens.resize(size);
for (auto i = 0u; i < size; i++)
{
m_tokens[i] = items[i]->PropertyChanged += ref new PropertyChangedEventHandler(this, &EquationCollection::OnEquationPropertyChanged);
}
m_vector->ReplaceAll(items);
EquationChanged();
}
virtual void SetAt(unsigned int index, GraphControl::Equation^ value)
{
m_vector->GetAt(index)->PropertyChanged -= m_tokens[index];
m_vector->SetAt(index, value);
m_tokens[index] =
value->PropertyChanged += ref new PropertyChangedEventHandler(this, &EquationCollection::OnEquationPropertyChanged);
EquationChanged();
}
#pragma endregion
#pragma region IObservableVector
virtual event Windows::Foundation::Collections::VectorChangedEventHandler< GraphControl::Equation^ >^ VectorChanged
{
Windows::Foundation::EventRegistrationToken add(Windows::Foundation::Collections::VectorChangedEventHandler< GraphControl::Equation^ >^ handler)
{
return m_vector->VectorChanged += handler;
}
void remove(Windows::Foundation::EventRegistrationToken token)
{
m_vector->VectorChanged -= token;
}
}
#pragma endregion
internal:
EquationCollection() :
m_vector(ref new Platform::Collections::Vector< GraphControl::Equation^ >())
{
}
event EquationChangedEventHandler^ EquationChanged;
private:
void OnEquationPropertyChanged(GraphControl::Equation^, Platform::String^ propertyName)
{
EquationChanged();
}
private:
Platform::Collections::Vector< GraphControl::Equation^ >^ m_vector;
std::vector<Windows::Foundation::EventRegistrationToken> m_tokens;
};
}

View file

@ -0,0 +1,392 @@
#include "pch.h"
#include "Grapher.h"
using namespace Graphing;
using namespace GraphControl;
using namespace GraphControl::DX;
using namespace Platform;
using namespace Platform::Collections;
using namespace std;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI;
using namespace Windows::UI::Input;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
namespace GraphControl
{
constexpr auto s_defaultStyleKey = L"GraphControl.Grapher";
constexpr auto s_templateKey_SwapChainPanel = L"GraphSurface";
DependencyProperty^ Grapher::s_equationTemplateProperty;
constexpr auto s_propertyName_EquationTemplate = L"EquationTemplate";
DependencyProperty^ Grapher::s_equationsProperty;
constexpr auto s_propertyName_Equations = L"Equations";
DependencyProperty^ Grapher::s_equationsSourceProperty;
constexpr auto s_propertyName_EquationsSource = L"EquationsSource";
DependencyProperty^ Grapher::s_forceProportionalAxesTemplateProperty;
constexpr auto s_propertyName_ForceProportionalAxes = L"ForceProportionalAxes";
Grapher::Grapher()
: m_solver{ IMathSolver::CreateMathSolver() }
, m_graph{ m_solver->CreateGrapher() }
{
m_solver->ParsingOptions().SetFormatType(FormatType::Linear);
DefaultStyleKey = StringReference(s_defaultStyleKey);
this->SetValue(EquationsProperty, ref new EquationCollection());
this->Loaded += ref new RoutedEventHandler(this, &Grapher::OnLoaded);
this->Unloaded += ref new RoutedEventHandler(this, &Grapher::OnUnloaded);
}
void Grapher::OnLoaded(Object^ sender, RoutedEventArgs^ args)
{
if (auto backgroundBrush = safe_cast<SolidColorBrush^>(this->Background))
{
m_tokenBackgroundColorChanged.Value =
backgroundBrush->RegisterPropertyChangedCallback(SolidColorBrush::ColorProperty, ref new DependencyPropertyChangedCallback(this, &Grapher::OnDependencyPropertyChanged));
OnBackgroundColorChanged(backgroundBrush->Color);
}
}
void Grapher::OnUnloaded(Object^ sender, RoutedEventArgs^ args)
{
if (auto backgroundBrush = safe_cast<SolidColorBrush^>(this->Background))
{
this->UnregisterPropertyChangedCallback(BackgroundProperty, m_tokenBackgroundColorChanged.Value);
}
}
void Grapher::OnApplyTemplate()
{
auto swapChainPanel = dynamic_cast<SwapChainPanel^>(GetTemplateChild(StringReference(s_templateKey_SwapChainPanel)));
if (swapChainPanel)
{
m_renderMain = ref new RenderMain(swapChainPanel);
}
UpdateGraph();
}
void Grapher::RegisterDependencyProperties()
{
if (!s_equationsProperty)
{
s_equationsProperty = DependencyProperty::Register(
StringReference(s_propertyName_Equations),
EquationCollection::typeid ,
Grapher::typeid,
ref new PropertyMetadata(
nullptr,
ref new PropertyChangedCallback(&Grapher::OnCustomDependencyPropertyChanged)));
}
if (!s_equationsSourceProperty)
{
s_equationsSourceProperty = DependencyProperty::Register(
StringReference(s_propertyName_EquationsSource),
Object::typeid,
Grapher::typeid,
ref new PropertyMetadata(
nullptr,
ref new PropertyChangedCallback(&Grapher::OnCustomDependencyPropertyChanged)));
}
if (!s_equationTemplateProperty)
{
s_equationTemplateProperty = DependencyProperty::Register(
StringReference(s_propertyName_EquationTemplate),
DataTemplate::typeid,
Grapher::typeid,
ref new PropertyMetadata(
nullptr,
ref new PropertyChangedCallback(&Grapher::OnCustomDependencyPropertyChanged)));
}
if (!s_forceProportionalAxesTemplateProperty)
{
s_forceProportionalAxesTemplateProperty = DependencyProperty::Register(
StringReference(s_propertyName_ForceProportionalAxes),
bool::typeid,
Grapher::typeid,
ref new PropertyMetadata(
true,
ref new PropertyChangedCallback(&Grapher::OnCustomDependencyPropertyChanged)));
}
}
void Grapher::OnCustomDependencyPropertyChanged(DependencyObject^ obj, DependencyPropertyChangedEventArgs^ args)
{
auto self = static_cast<Grapher^>(obj);
if (self)
{
if (args->Property == EquationsProperty)
{
self->OnEquationsChanged(args);
}
else if (args->Property == EquationsSourceProperty)
{
self->OnEquationsSourceChanged(args);
}
else if (args->Property == EquationTemplateProperty)
{
self->OnEquationTemplateChanged(args);
}
else if (args->Property == ForceProportionalAxesTemplateProperty)
{
self->OnForceProportionalAxesChanged(args);
}
}
}
void Grapher::OnDependencyPropertyChanged(DependencyObject^ obj, DependencyProperty^ p)
{
if (p == SolidColorBrush::ColorProperty)
{
auto brush = static_cast<SolidColorBrush^>(obj);
OnBackgroundColorChanged(brush->Color);
}
}
void Grapher::OnEquationTemplateChanged(DependencyPropertyChangedEventArgs^ args)
{
SyncEquationsWithItemsSource();
}
void Grapher::OnEquationsSourceChanged(DependencyPropertyChangedEventArgs^ args)
{
if (m_dataSource && m_tokenDataSourceChanged.Value != 0)
{
m_dataSource->DataSourceChanged -= m_tokenDataSourceChanged;
}
m_dataSource = args->NewValue ? ref new InspectingDataSource(args->NewValue) : nullptr;
if (m_dataSource)
{
m_tokenDataSourceChanged =
m_dataSource->DataSourceChanged += ref new TypedEventHandler<InspectingDataSource^, DataSourceChangedEventArgs>(this, &Grapher::OnDataSourceChanged);
}
SyncEquationsWithItemsSource();
}
void Grapher::OnDataSourceChanged(InspectingDataSource^ sender, DataSourceChangedEventArgs args)
{
switch (args.Action)
{
case DataSourceChangedAction::Insert:
OnItemsAdded(args.NewStartingIndex, args.NewItemsCount);
break;
case DataSourceChangedAction::Remove:
OnItemsRemoved(args.OldStartingIndex, args.OldItemsCount);
break;
case DataSourceChangedAction::Reset:
SyncEquationsWithItemsSource();
break;
case DataSourceChangedAction::Replace:
OnItemsRemoved(args.OldStartingIndex, args.OldItemsCount);
OnItemsAdded(args.NewStartingIndex, args.NewItemsCount);
break;
}
}
void Grapher::OnItemsAdded(int index, int count)
{
for (int i = index + count - 1; i >= index; i--)
{
auto eq = safe_cast<Equation^>(EquationTemplate->LoadContent());
eq->DataContext = m_dataSource->GetAt(i);
Equations->InsertAt(index, eq);
}
}
void Grapher::OnItemsRemoved(int index, int count)
{
for (int i = 0; i < count; i++)
{
Equations->RemoveAt(index);
}
}
void Grapher::SyncEquationsWithItemsSource()
{
Equations->Clear();
if (m_dataSource)
{
auto size = m_dataSource->GetSize();
for (auto i = 0u; i < size; i++)
{
auto eq = safe_cast<Equation^>(EquationTemplate->LoadContent());
eq->DataContext = m_dataSource->GetAt(i);
Equations->Append(eq);
}
}
}
void Grapher::OnEquationsChanged(DependencyPropertyChangedEventArgs^ args)
{
if (auto older = static_cast<EquationCollection^>(args->OldValue))
{
if (m_tokenEquationsChanged.Value != 0)
{
older->VectorChanged -= m_tokenEquationsChanged;
m_tokenEquationsChanged.Value = 0;
}
if (m_tokenEquationChanged.Value != 0)
{
older->EquationChanged -= m_tokenEquationChanged;
m_tokenEquationChanged.Value = 0;
}
}
if (auto newer = static_cast<EquationCollection^>(args->NewValue))
{
m_tokenEquationsChanged =
newer->VectorChanged += ref new VectorChangedEventHandler<Equation^>(this, &Grapher::OnEquationsVectorChanged);
m_tokenEquationChanged =
newer->EquationChanged += ref new EquationChangedEventHandler(this, &Grapher::OnEquationChanged);
}
UpdateGraph();
}
void Grapher::OnEquationsVectorChanged(IObservableVector<Equation^>^ sender, IVectorChangedEventArgs^ event)
{
UpdateGraph();
}
void Grapher::OnEquationChanged()
{
UpdateGraph();
}
void Grapher::UpdateGraph()
{
if (m_renderMain && m_graph != nullptr)
{
auto validEqs = GetValidEquations();
if (!validEqs.empty())
{
wstringstream ss{};
ss << L"show2d(";
int numValidEquations = 0;
for (Equation^ eq : validEqs)
{
if (numValidEquations++ > 0)
{
ss << L",";
}
ss << eq->GetRequest();
}
ss << L")";
wstring request = ss.str();
if (auto graphExpression = m_solver->ParseInput(request))
{
if (m_graph->TryInitialize(graphExpression.get()))
{
UpdateGraphOptions(m_graph->GetOptions(), validEqs);
m_renderMain->Graph = m_graph;
}
}
}
}
}
void Grapher::UpdateGraphOptions(IGraphingOptions& options, const vector<Equation^>& validEqs)
{
options.SetForceProportional(ForceProportionalAxes);
vector<Graphing::Color> graphColors;
graphColors.reserve(validEqs.size());
for (Equation^ eq : validEqs)
{
auto lineColor = eq->LineColor;
graphColors.emplace_back(
lineColor.R,
lineColor.G,
lineColor.B,
lineColor.A);
}
options.SetGraphColors(graphColors);
}
vector<Equation^> Grapher::GetValidEquations()
{
vector<Equation^> validEqs;
for (Equation^ eq : Equations)
{
if (!eq->Expression->IsEmpty())
{
validEqs.push_back(eq);
}
}
return validEqs;
}
void Grapher::OnForceProportionalAxesChanged(DependencyPropertyChangedEventArgs^ args)
{
UpdateGraph();
}
void Grapher::OnBackgroundColorChanged(const Windows::UI::Color& color)
{
if (m_renderMain)
{
m_renderMain->BackgroundColor = color;
}
}
void Grapher::OnPointerEntered(PointerRoutedEventArgs^ e)
{
if (m_renderMain)
{
OnPointerMoved(e);
m_renderMain->DrawNearestPoint = true;
e->Handled = true;
}
}
void Grapher::OnPointerMoved(PointerRoutedEventArgs^ e)
{
if (m_renderMain)
{
PointerPoint^ currPoint = e->GetCurrentPoint(/* relativeTo */ this);
m_renderMain->PointerLocation = currPoint->Position;
e->Handled = true;
}
}
void Grapher::OnPointerExited(PointerRoutedEventArgs^ e)
{
if (m_renderMain)
{
m_renderMain->DrawNearestPoint = false;
e->Handled = true;
}
}
}

View file

@ -0,0 +1,160 @@
#pragma once
#include "InspectingDataSource.h"
#include "DirectX/RenderMain.h"
#include "Equation.h"
#include "EquationCollection.h"
#include "IMathSolver.h"
namespace GraphControl
{
[Windows::UI::Xaml::Markup::ContentPropertyAttribute(Name = L"Equations")]
public ref class Grapher sealed : public Windows::UI::Xaml::Controls::Control
{
public:
Grapher();
static void RegisterDependencyProperties();
#pragma region Windows::UI::Xaml::DataTemplate^ EquationTemplate DependencyProperty
static property Windows::UI::Xaml::DependencyProperty^ EquationTemplateProperty
{
Windows::UI::Xaml::DependencyProperty^ get()
{
return s_equationTemplateProperty;
}
}
property Windows::UI::Xaml::DataTemplate^ EquationTemplate
{
Windows::UI::Xaml::DataTemplate^ get()
{
return static_cast<Windows::UI::Xaml::DataTemplate^>(GetValue(s_equationTemplateProperty));
}
void set(Windows::UI::Xaml::DataTemplate^ value)
{
SetValue(s_equationTemplateProperty, value);
}
}
#pragma endregion
#pragma region Platform::Object^ EquationsSource DependencyProperty
static property Windows::UI::Xaml::DependencyProperty^ EquationsSourceProperty
{
Windows::UI::Xaml::DependencyProperty^ get()
{
return s_equationsSourceProperty;
}
}
property Platform::Object^ EquationsSource
{
Platform::Object^ get()
{
return GetValue(s_equationsSourceProperty);
}
void set(Platform::Object^ value)
{
SetValue(s_equationsSourceProperty, value);
}
}
#pragma endregion
#pragma region GraphControl::EquationCollection^ Equations DependencyProperty
static property Windows::UI::Xaml::DependencyProperty^ EquationsProperty
{
Windows::UI::Xaml::DependencyProperty^ get()
{
return s_equationsProperty;
}
}
property GraphControl::EquationCollection^ Equations
{
GraphControl::EquationCollection^ get()
{
return static_cast< GraphControl::EquationCollection^ >(GetValue(s_equationsProperty));
}
}
#pragma endregion
#pragma region Windows::UI::Xaml::DataTemplate^ ForceProportionalAxes DependencyProperty
static property Windows::UI::Xaml::DependencyProperty^ ForceProportionalAxesTemplateProperty
{
Windows::UI::Xaml::DependencyProperty^ get()
{
return s_forceProportionalAxesTemplateProperty;
}
}
property bool ForceProportionalAxes
{
bool get()
{
return static_cast<bool>(GetValue(s_forceProportionalAxesTemplateProperty));
}
void set(bool value)
{
SetValue(s_forceProportionalAxesTemplateProperty, value);
}
}
#pragma endregion
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;
#pragma endregion
private:
void OnLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ args);
void OnUnloaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ args);
static void OnCustomDependencyPropertyChanged(Windows::UI::Xaml::DependencyObject^ obj, Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ args);
void OnDependencyPropertyChanged(Windows::UI::Xaml::DependencyObject^ obj, Windows::UI::Xaml::DependencyProperty^ p);
void OnEquationTemplateChanged(Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ args);
void OnEquationsSourceChanged(Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ args);
void OnDataSourceChanged(GraphControl::InspectingDataSource^ sender, GraphControl::DataSourceChangedEventArgs args);
void OnEquationsChanged(Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ args);
void OnEquationChanged();
void UpdateGraph();
void UpdateGraphOptions(Graphing::IGraphingOptions& options, const std::vector<Equation^>& validEqs);
std::vector<Equation^> GetValidEquations();
void OnForceProportionalAxesChanged(Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ args);
void OnBackgroundColorChanged(const Windows::UI::Color& color);
void SyncEquationsWithItemsSource();
void OnItemsAdded(int index, int count);
void OnItemsRemoved(int index, int count);
private:
DX::RenderMain^ m_renderMain = nullptr;
static Windows::UI::Xaml::DependencyProperty^ s_equationTemplateProperty;
static Windows::UI::Xaml::DependencyProperty^ s_equationsSourceProperty;
InspectingDataSource^ m_dataSource;
Windows::Foundation::EventRegistrationToken m_tokenDataSourceChanged;
static Windows::UI::Xaml::DependencyProperty^ s_equationsProperty;
Windows::Foundation::EventRegistrationToken m_tokenEquationsChanged;
Windows::Foundation::EventRegistrationToken m_tokenEquationChanged;
static Windows::UI::Xaml::DependencyProperty^ s_forceProportionalAxesTemplateProperty;
Windows::Foundation::EventRegistrationToken m_tokenBackgroundColorChanged;
const std::unique_ptr<Graphing::IMathSolver> m_solver;
const std::shared_ptr<Graphing::IGraph> m_graph;
void OnEquationsVectorChanged(Windows::Foundation::Collections::IObservableVector<GraphControl::Equation ^> ^sender, Windows::Foundation::Collections::IVectorChangedEventArgs ^event);
};
}

View file

@ -0,0 +1,242 @@
#include "pch.h"
#include "InspectingDataSource.h"
using namespace Platform;
using namespace Platform::Collections;
using namespace std;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::Xaml::Interop;
namespace winrt
{
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Windows::UI::Xaml::Interop;
}
namespace GraphControl
{
InspectingDataSource::InspectingDataSource(Object^ source)
{
if (!source)
{
throw ref new InvalidArgumentException(L"Argument 'source' is null.");
}
auto inspectable = from_cx<winrt::IInspectable>(source);
if (auto vector = inspectable.try_as<winrt::IVector<winrt::IInspectable>>())
{
m_vector = vector;
ListenToCollectionChanges();
}
else if (auto bindableVector = inspectable.try_as<winrt::IBindableVector>())
{
// The bindable interop interface are abi compatible with the corresponding
// WinRT interfaces.
m_vector = reinterpret_cast<const winrt::IVector<winrt::IInspectable>&>(bindableVector);
ListenToCollectionChanges();
}
else if (auto iterable = inspectable.try_as<winrt::IIterable<winrt::IInspectable>>())
{
m_vector = WrapIterable(iterable);
}
else if (auto bindableIterable = inspectable.try_as<winrt::IBindableIterable>())
{
m_vector = WrapIterable(reinterpret_cast<const winrt::IIterable<winrt::IInspectable> &>(bindableIterable));
}
else
{
throw ref new InvalidArgumentException(L"Argument 'source' is not a supported vector.");
}
}
InspectingDataSource::~InspectingDataSource()
{
UnlistenToCollectionChanges();
}
unsigned int InspectingDataSource::GetSize()
{
return m_vector.Size();
}
Object^ InspectingDataSource::GetAt(unsigned int index)
{
return to_cx<Object>(m_vector.GetAt(index));
}
optional<unsigned int> InspectingDataSource::IndexOf(Object^ value)
{
if ((m_vector != nullptr) && value)
{
uint32_t v;
auto inspectable = from_cx<winrt::IInspectable>(value);
if (m_vector.IndexOf(inspectable, v))
{
return v;
}
}
return nullopt;
}
winrt::IVector<winrt::IInspectable> InspectingDataSource::WrapIterable(const winrt::IIterable<winrt::IInspectable>& iterable)
{
auto vector = winrt::single_threaded_vector<winrt::IInspectable>();
auto iterator = iterable.First();
while (iterator.HasCurrent())
{
vector.Append(iterator.Current());
iterator.MoveNext();
}
return vector;
}
void InspectingDataSource::UnlistenToCollectionChanges()
{
if (m_notifyCollectionChanged)
{
m_notifyCollectionChanged.CollectionChanged(m_eventToken);
}
else if (m_observableVector)
{
m_observableVector.VectorChanged(m_eventToken);
}
else if (m_bindableObservableVector)
{
m_bindableObservableVector.VectorChanged(m_eventToken);
}
}
void InspectingDataSource::ListenToCollectionChanges()
{
assert(m_vector);
if (auto incc = m_vector.try_as<winrt::INotifyCollectionChanged>())
{
m_eventToken = incc.CollectionChanged([this](
const winrt::IInspectable& sender,
const winrt::NotifyCollectionChangedEventArgs& e)
{
OnCollectionChanged(sender, e);
});
m_notifyCollectionChanged = incc;
}
else if (auto observableVector = m_vector.try_as<winrt::IObservableVector<winrt::IInspectable>>())
{
m_eventToken = observableVector.VectorChanged([this](
const winrt::IObservableVector<winrt::IInspectable>& sender,
const winrt::IVectorChangedEventArgs& e)
{
OnVectorChanged(sender, e);
});
m_observableVector = observableVector;
}
else if (auto bindableObservableVector = m_vector.try_as<winrt::IBindableObservableVector>())
{
m_eventToken = bindableObservableVector.VectorChanged([this](
winrt::IBindableObservableVector const& vector,
winrt::IInspectable const& e)
{
OnBindableVectorChanged(vector, e);
});
m_bindableObservableVector = bindableObservableVector;
}
}
void InspectingDataSource::OnCollectionChanged(
const winrt::IInspectable& /*sender*/,
const winrt::NotifyCollectionChangedEventArgs& e)
{
DataSourceChangedAction action;
switch (e.Action())
{
case winrt::NotifyCollectionChangedAction::Add:
action = DataSourceChangedAction::Insert;
break;
case winrt::NotifyCollectionChangedAction::Remove:
action = DataSourceChangedAction::Remove;
break;
case winrt::NotifyCollectionChangedAction::Replace:
action = DataSourceChangedAction::Replace;
break;
case winrt::NotifyCollectionChangedAction::Reset:
action = DataSourceChangedAction::Reset;
break;
case winrt::NotifyCollectionChangedAction::Move:
throw ref new Exception(E_FAIL, L"Move operations are not supported. Use a combination of Add and Remove instead.");
break;
default:
assert(false);
break;
}
const auto& newItems = e.NewItems();
const auto& oldItems = e.OldItems();
DataSourceChanged(this, DataSourceChangedEventArgs{
action,
e.OldStartingIndex(),
oldItems ? static_cast<int>(oldItems.Size()) : 0,
e.NewStartingIndex(),
newItems ? static_cast<int>(newItems.Size()) : 0 });
}
void InspectingDataSource::OnVectorChanged(
const winrt::Collections::IObservableVector<winrt::IInspectable>& /*sender*/,
const winrt::Collections::IVectorChangedEventArgs& e)
{
DataSourceChangedAction action;
int oldStartingIndex = -1;
int oldItemsCount = 0;
int newStartingIndex = -1;
int newItemsCount = 0;
// Note that the event args' Index property should NOT be accessed
// in the Reset case, as the property accessor will throw an exception.
switch (e.CollectionChange())
{
case winrt::CollectionChange::ItemInserted:
action = DataSourceChangedAction::Insert;
newStartingIndex = e.Index();
newItemsCount = 1;
break;
case winrt::CollectionChange::ItemRemoved:
action = DataSourceChangedAction::Remove;
oldStartingIndex = e.Index();
oldItemsCount = 1;
break;
case winrt::CollectionChange::ItemChanged:
action = DataSourceChangedAction::Replace;
oldStartingIndex = e.Index();
oldItemsCount = 1;
newStartingIndex = e.Index();
newItemsCount = 1;
break;
case winrt::CollectionChange::Reset:
action = DataSourceChangedAction::Reset;
break;
default:
assert(false);
break;
}
DataSourceChanged(this, DataSourceChangedEventArgs{
action,
oldStartingIndex,
oldItemsCount,
newStartingIndex,
newItemsCount });
}
void InspectingDataSource::OnBindableVectorChanged(
winrt::IBindableObservableVector const& vector,
winrt::IInspectable const& e)
{
OnVectorChanged(nullptr, e.as<winrt::IVectorChangedEventArgs>());
}
}

View file

@ -0,0 +1,62 @@
#pragma once
namespace GraphControl
{
public enum class DataSourceChangedAction
{
Insert,
Remove,
Replace,
Reset
};
value struct DataSourceChangedEventArgs sealed
{
DataSourceChangedAction Action;
int OldStartingIndex;
int OldItemsCount;
int NewStartingIndex;
int NewItemsCount;
};
ref class InspectingDataSource sealed
{
internal:
InspectingDataSource(Platform::Object^ source);
event Windows::Foundation::TypedEventHandler<InspectingDataSource^, DataSourceChangedEventArgs>^ DataSourceChanged;
unsigned int GetSize();
Platform::Object^ GetAt(unsigned int index);
std::optional<unsigned int> IndexOf(Platform::Object^ value);
private:
~InspectingDataSource();
static winrt::Windows::Foundation::Collections::IVector<winrt::Windows::Foundation::IInspectable>
WrapIterable(const winrt::Windows::Foundation::Collections::IIterable<winrt::Windows::Foundation::IInspectable>& iterable);
void ListenToCollectionChanges();
void UnlistenToCollectionChanges();
void OnCollectionChanged(
const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::UI::Xaml::Interop::NotifyCollectionChangedEventArgs& e);
void OnVectorChanged(
const winrt::Windows::Foundation::Collections::IObservableVector<winrt::Windows::Foundation::IInspectable>& sender,
const winrt::Windows::Foundation::Collections::IVectorChangedEventArgs& e);
void OnBindableVectorChanged(
winrt::Windows::UI::Xaml::Interop::IBindableObservableVector const& vector,
winrt::Windows::Foundation::IInspectable const& e);
private:
winrt::Windows::Foundation::Collections::IVector<winrt::Windows::Foundation::IInspectable> m_vector;
winrt::Windows::UI::Xaml::Interop::INotifyCollectionChanged m_notifyCollectionChanged;
winrt::Windows::Foundation::Collections::IObservableVector<winrt::Windows::Foundation::IInspectable> m_observableVector;
winrt::Windows::UI::Xaml::Interop::IBindableObservableVector m_bindableObservableVector;
winrt::event_token m_eventToken;
};
}

View file

@ -0,0 +1,659 @@
#include "pch.h"
#include "DeviceResources.h"
#include "DirectXHelper.h"
using namespace D2D1;
using namespace DirectX;
using namespace Microsoft::WRL;
using namespace std;
using namespace Windows::Foundation;
using namespace Windows::Graphics::Display;
using namespace Windows::UI::Core;
using namespace Windows::UI::Xaml::Controls;
using namespace Platform;
namespace DisplayMetrics
{
// High resolution displays can require a lot of GPU and battery power to render.
// High resolution phones, for example, may suffer from poor battery life if
// games attempt to render at 60 frames per second at full fidelity.
// The decision to render at full fidelity across all platforms and form factors
// should be deliberate.
static constexpr bool SupportHighResolutions = false;
// The default thresholds that define a "high resolution" display. If the thresholds
// are exceeded and SupportHighResolutions is false, the dimensions will be scaled
// by 50%.
static constexpr float DpiThreshold = 192.0f; // 200% of standard desktop display.
static constexpr float WidthThreshold = 1920.0f; // 1080p width.
static constexpr float HeightThreshold = 1080.0f; // 1080p height.
};
// Constants used to calculate screen rotations.
namespace ScreenRotation
{
// 0-degree Z-rotation
static constexpr XMFLOAT4X4 Rotation0(
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
);
// 90-degree Z-rotation
static constexpr XMFLOAT4X4 Rotation90(
0.0f, 1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
);
// 180-degree Z-rotation
static constexpr XMFLOAT4X4 Rotation180(
-1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
);
// 270-degree Z-rotation
static constexpr XMFLOAT4X4 Rotation270(
0.0f, -1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
);
};
namespace GraphControl::DX
{
// Constructor for DeviceResources.
DeviceResources::DeviceResources(SwapChainPanel^ panel) :
m_screenViewport(),
m_d3dFeatureLevel(D3D_FEATURE_LEVEL_9_1),
m_d3dRenderTargetSize(),
m_outputSize(),
m_logicalSize(),
m_nativeOrientation(DisplayOrientations::None),
m_currentOrientation(DisplayOrientations::None),
m_dpi(-1.0f),
m_effectiveDpi(-1.0f),
m_compositionScaleX(1.0f),
m_compositionScaleY(1.0f),
m_deviceNotify(nullptr)
{
CreateDeviceIndependentResources();
CreateDeviceResources();
SetSwapChainPanel(panel);
}
// Configures resources that don't depend on the Direct3D device.
void DeviceResources::CreateDeviceIndependentResources()
{
// Initialize Direct2D resources.
D2D1_FACTORY_OPTIONS options;
ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS));
#if defined(_DEBUG)
// If the project is in a debug build, enable Direct2D debugging via SDK Layers.
options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
#endif
// Initialize the Direct2D Factory.
DX::ThrowIfFailed(
D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
__uuidof(ID2D1Factory3),
&options,
&m_d2dFactory
)
);
// Initialize the DirectWrite Factory.
DX::ThrowIfFailed(
DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory3),
&m_dwriteFactory
)
);
// Initialize the Windows Imaging Component (WIC) Factory.
DX::ThrowIfFailed(
CoCreateInstance(
CLSID_WICImagingFactory2,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&m_wicFactory)
)
);
}
// Configures the Direct3D device, and stores handles to it and the device context.
void DeviceResources::CreateDeviceResources()
{
// This flag adds support for surfaces with a different color channel ordering
// than the API default. It is required for compatibility with Direct2D.
UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
#if defined(_DEBUG)
if (DX::SdkLayersAvailable())
{
// If the project is in a debug build, enable debugging via SDK Layers with this flag.
creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
}
#endif
// This array defines the set of DirectX hardware feature levels this app will support.
// Note the ordering should be preserved.
// Don't forget to declare your application's minimum required feature level in its
// description. All applications are assumed to support 9.1 unless otherwise stated.
static constexpr UINT featureLevelsSize = 9;
static constexpr std::array<D3D_FEATURE_LEVEL, featureLevelsSize> featureLevels =
{
D3D_FEATURE_LEVEL_12_1,
D3D_FEATURE_LEVEL_12_0,
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1
};
// Create the Direct3D 11 API device object and a corresponding context.
ComPtr<ID3D11Device> device;
ComPtr<ID3D11DeviceContext> context;
HRESULT hr = D3D11CreateDevice(
nullptr, // Specify nullptr to use the default adapter.
D3D_DRIVER_TYPE_HARDWARE, // Create a device using the hardware graphics driver.
0, // Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE.
creationFlags, // Set debug and Direct2D compatibility flags.
&featureLevels[0], // List of feature levels this app can support.
featureLevelsSize, // Size of the list above.
D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Windows Store apps.
&device, // Returns the Direct3D device created.
&m_d3dFeatureLevel, // Returns feature level of device created.
&context // Returns the device immediate context.
);
if (FAILED(hr))
{
// If the initialization fails, fall back to the WARP device.
// For more information on WARP, see:
// https://go.microsoft.com/fwlink/?LinkId=286690
DX::ThrowIfFailed(
D3D11CreateDevice(
nullptr,
D3D_DRIVER_TYPE_WARP, // Create a WARP device instead of a hardware device.
0,
creationFlags,
&featureLevels[0],
featureLevelsSize,
D3D11_SDK_VERSION,
&device,
&m_d3dFeatureLevel,
&context
)
);
}
// Store pointers to the Direct3D 11.3 API device and immediate context.
DX::ThrowIfFailed(
device.As(&m_d3dDevice)
);
DX::ThrowIfFailed(
context.As(&m_d3dContext)
);
// Create the Direct2D device object and a corresponding context.
ComPtr<IDXGIDevice3> dxgiDevice;
DX::ThrowIfFailed(
m_d3dDevice.As(&dxgiDevice)
);
DX::ThrowIfFailed(
m_d2dFactory->CreateDevice(dxgiDevice.Get(), &m_d2dDevice)
);
DX::ThrowIfFailed(
m_d2dDevice->CreateDeviceContext(
D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
&m_d2dContext
)
);
}
// These resources need to be recreated every time the window size is changed.
void DeviceResources::CreateWindowSizeDependentResources()
{
// Clear the previous window size specific context.
static constexpr std::array<ID3D11RenderTargetView*, 1> nullViews = { nullptr };
m_d3dContext->OMSetRenderTargets(static_cast<UINT>(nullViews.size()), &nullViews[0], nullptr);
m_d3dRenderTargetView = nullptr;
m_d2dContext->SetTarget(nullptr);
m_d2dTargetBitmap = nullptr;
m_d3dDepthStencilView = nullptr;
m_d3dContext->Flush1(D3D11_CONTEXT_TYPE_ALL, nullptr);
UpdateRenderTargetSize();
m_d3dRenderTargetSize.Width = m_outputSize.Width;
m_d3dRenderTargetSize.Height = m_outputSize.Height;
if (m_swapChain != nullptr)
{
// If the swap chain already exists, resize it.
HRESULT hr = m_swapChain->ResizeBuffers(
2, // Double-buffered swap chain.
lround(m_d3dRenderTargetSize.Width),
lround(m_d3dRenderTargetSize.Height),
DXGI_FORMAT_B8G8R8A8_UNORM,
0
);
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
{
// If the device was removed for any reason, a new device and swap chain will need to be created.
HandleDeviceLost();
// Everything is set up now. Do not continue execution of this method. HandleDeviceLost will reenter this method
// and correctly set up the new device.
return;
}
else
{
DX::ThrowIfFailed(hr);
}
}
else
{
// Otherwise, create a new one using the same adapter as the existing Direct3D device.
DXGI_SCALING scaling = DisplayMetrics::SupportHighResolutions ? DXGI_SCALING_NONE : DXGI_SCALING_STRETCH;
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 };
swapChainDesc.Width = lround(m_d3dRenderTargetSize.Width); // Match the size of the window.
swapChainDesc.Height = lround(m_d3dRenderTargetSize.Height);
swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format.
swapChainDesc.Stereo = false;
swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling.
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency.
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Windows Store apps must use _FLIP_ SwapEffects.
swapChainDesc.Flags = 0;
swapChainDesc.Scaling = scaling;
swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
// This sequence obtains the DXGI factory that was used to create the Direct3D device above.
ComPtr<IDXGIDevice3> dxgiDevice;
DX::ThrowIfFailed(
m_d3dDevice.As(&dxgiDevice)
);
ComPtr<IDXGIAdapter> dxgiAdapter;
DX::ThrowIfFailed(
dxgiDevice->GetAdapter(&dxgiAdapter)
);
ComPtr<IDXGIFactory4> dxgiFactory;
DX::ThrowIfFailed(
dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory))
);
// When using XAML interop, the swap chain must be created for composition.
ComPtr<IDXGISwapChain1> swapChain;
DX::ThrowIfFailed(
dxgiFactory->CreateSwapChainForComposition(
m_d3dDevice.Get(),
&swapChainDesc,
nullptr,
&swapChain
)
);
DX::ThrowIfFailed(
swapChain.As(&m_swapChain)
);
// Associate swap chain with SwapChainPanel
// UI changes will need to be dispatched back to the UI thread
m_swapChainPanel->Dispatcher->RunAsync(CoreDispatcherPriority::High, ref new DispatchedHandler([=]()
{
// Get backing native interface for SwapChainPanel
ComPtr<ISwapChainPanelNative> panelNative;
DX::ThrowIfFailed(
reinterpret_cast<IUnknown*>(m_swapChainPanel)->QueryInterface(IID_PPV_ARGS(&panelNative))
);
DX::ThrowIfFailed(
panelNative->SetSwapChain(m_swapChain.Get())
);
}, CallbackContext::Any));
// Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and
// ensures that the application will only render after each VSync, minimizing power consumption.
DX::ThrowIfFailed(
dxgiDevice->SetMaximumFrameLatency(1)
);
}
// Set the proper orientation for the swap chain, and generate 2D and
// 3D matrix transformations for rendering to the rotated swap chain.
// Note the rotation angle for the 2D and 3D transforms are different.
// This is due to the difference in coordinate spaces. Additionally,
// the 3D matrix is specified explicitly to avoid rounding errors.
m_orientationTransform2D = Matrix3x2F::Identity();
m_orientationTransform3D = ScreenRotation::Rotation0;
// Setup inverse scale on the swap chain
DXGI_MATRIX_3X2_F inverseScale = { 0 };
inverseScale._11 = 1.0f / m_effectiveCompositionScaleX;
inverseScale._22 = 1.0f / m_effectiveCompositionScaleY;
ComPtr<IDXGISwapChain2> spSwapChain2;
DX::ThrowIfFailed(
m_swapChain.As<IDXGISwapChain2>(&spSwapChain2)
);
DX::ThrowIfFailed(
spSwapChain2->SetMatrixTransform(&inverseScale)
);
// Create a render target view of the swap chain back buffer.
ComPtr<ID3D11Texture2D1> backBuffer;
DX::ThrowIfFailed(
m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))
);
DX::ThrowIfFailed(
m_d3dDevice->CreateRenderTargetView1(
backBuffer.Get(),
nullptr,
&m_d3dRenderTargetView
)
);
// Create a depth stencil view for use with 3D rendering if needed.
CD3D11_TEXTURE2D_DESC1 depthStencilDesc(
DXGI_FORMAT_D24_UNORM_S8_UINT,
lround(m_d3dRenderTargetSize.Width),
lround(m_d3dRenderTargetSize.Height),
1, // This depth stencil view has only one texture.
1, // Use a single mipmap level.
D3D11_BIND_DEPTH_STENCIL
);
ComPtr<ID3D11Texture2D1> depthStencil;
DX::ThrowIfFailed(
m_d3dDevice->CreateTexture2D1(
&depthStencilDesc,
nullptr,
&depthStencil
)
);
CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D);
DX::ThrowIfFailed(
m_d3dDevice->CreateDepthStencilView(
depthStencil.Get(),
&depthStencilViewDesc,
&m_d3dDepthStencilView
)
);
// Set the 3D rendering viewport to target the entire window.
m_screenViewport = CD3D11_VIEWPORT(
0.0f,
0.0f,
m_d3dRenderTargetSize.Width,
m_d3dRenderTargetSize.Height
);
m_d3dContext->RSSetViewports(1, &m_screenViewport);
// Create a Direct2D target bitmap associated with the
// swap chain back buffer and set it as the current target.
D2D1_BITMAP_PROPERTIES1 bitmapProperties =
D2D1::BitmapProperties1(
D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
m_dpi,
m_dpi
);
ComPtr<IDXGISurface2> dxgiBackBuffer;
DX::ThrowIfFailed(
m_swapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer))
);
DX::ThrowIfFailed(
m_d2dContext->CreateBitmapFromDxgiSurface(
dxgiBackBuffer.Get(),
&bitmapProperties,
&m_d2dTargetBitmap
)
);
m_d2dContext->SetTarget(m_d2dTargetBitmap.Get());
m_d2dContext->SetDpi(m_effectiveDpi, m_effectiveDpi);
// Grayscale text anti-aliasing is recommended for all Windows Store apps.
m_d2dContext->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
}
// Determine the dimensions of the render target and whether it will be scaled down.
void DeviceResources::UpdateRenderTargetSize()
{
m_effectiveDpi = m_dpi;
m_effectiveCompositionScaleX = m_compositionScaleX;
m_effectiveCompositionScaleY = m_compositionScaleY;
// To improve battery life on high resolution devices, render to a smaller render target
// and allow the GPU to scale the output when it is presented.
if (!DisplayMetrics::SupportHighResolutions && m_dpi > DisplayMetrics::DpiThreshold)
{
float width = DX::ConvertDipsToPixels(m_logicalSize.Width, m_dpi);
float height = DX::ConvertDipsToPixels(m_logicalSize.Height, m_dpi);
// When the device is in portrait orientation, height > width. Compare the
// larger dimension against the width threshold and the smaller dimension
// against the height threshold.
if (max(width, height) > DisplayMetrics::WidthThreshold && min(width, height) > DisplayMetrics::HeightThreshold)
{
// To scale the app we change the effective DPI. Logical size does not change.
m_effectiveDpi /= 2.0f;
m_effectiveCompositionScaleX /= 2.0f;
m_effectiveCompositionScaleY /= 2.0f;
}
}
// Calculate the necessary render target size in pixels.
m_outputSize.Width = DX::ConvertDipsToPixels(m_logicalSize.Width, m_effectiveDpi);
m_outputSize.Height = DX::ConvertDipsToPixels(m_logicalSize.Height, m_effectiveDpi);
// Prevent zero size DirectX content from being created.
m_outputSize.Width = max(m_outputSize.Width, 1.0f);
m_outputSize.Height = max(m_outputSize.Height, 1.0f);
}
// This method is called when the XAML control is created (or re-created).
void DeviceResources::SetSwapChainPanel(SwapChainPanel^ panel)
{
DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView();
m_swapChainPanel = panel;
m_logicalSize = Windows::Foundation::Size(static_cast<float>(panel->ActualWidth), static_cast<float>(panel->ActualHeight));
m_nativeOrientation = currentDisplayInformation->NativeOrientation;
m_currentOrientation = currentDisplayInformation->CurrentOrientation;
m_compositionScaleX = panel->CompositionScaleX;
m_compositionScaleY = panel->CompositionScaleY;
m_dpi = currentDisplayInformation->LogicalDpi;
m_d2dContext->SetDpi(m_dpi, m_dpi);
CreateWindowSizeDependentResources();
}
// This method is called in the event handler for the SizeChanged event.
void DeviceResources::SetLogicalSize(Windows::Foundation::Size logicalSize)
{
if (m_logicalSize != logicalSize)
{
m_logicalSize = logicalSize;
CreateWindowSizeDependentResources();
}
}
// This method is called in the event handler for the DpiChanged event.
void DeviceResources::SetDpi(float dpi)
{
if (dpi != m_dpi)
{
m_dpi = dpi;
m_d2dContext->SetDpi(m_dpi, m_dpi);
CreateWindowSizeDependentResources();
}
}
// This method is called in the event handler for the OrientationChanged event.
void DeviceResources::SetCurrentOrientation(DisplayOrientations currentOrientation)
{
if (m_currentOrientation != currentOrientation)
{
m_currentOrientation = currentOrientation;
CreateWindowSizeDependentResources();
}
}
// This method is called in the event handler for the CompositionScaleChanged event.
void DeviceResources::SetCompositionScale(float compositionScaleX, float compositionScaleY)
{
if (m_compositionScaleX != compositionScaleX ||
m_compositionScaleY != compositionScaleY)
{
m_compositionScaleX = compositionScaleX;
m_compositionScaleY = compositionScaleY;
CreateWindowSizeDependentResources();
}
}
// This method is called in the event handler for the DisplayContentsInvalidated event.
void DeviceResources::ValidateDevice()
{
// The D3D Device is no longer valid if the default adapter changed since the device
// was created or if the device has been removed.
// First, get the information for the default adapter from when the device was created.
ComPtr<IDXGIDevice3> dxgiDevice;
DX::ThrowIfFailed(m_d3dDevice.As(&dxgiDevice));
ComPtr<IDXGIAdapter> deviceAdapter;
DX::ThrowIfFailed(dxgiDevice->GetAdapter(&deviceAdapter));
ComPtr<IDXGIFactory2> deviceFactory;
DX::ThrowIfFailed(deviceAdapter->GetParent(IID_PPV_ARGS(&deviceFactory)));
ComPtr<IDXGIAdapter1> previousDefaultAdapter;
DX::ThrowIfFailed(deviceFactory->EnumAdapters1(0, &previousDefaultAdapter));
DXGI_ADAPTER_DESC1 previousDesc;
DX::ThrowIfFailed(previousDefaultAdapter->GetDesc1(&previousDesc));
// Next, get the information for the current default adapter.
ComPtr<IDXGIFactory4> currentFactory;
DX::ThrowIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(&currentFactory)));
ComPtr<IDXGIAdapter1> currentDefaultAdapter;
DX::ThrowIfFailed(currentFactory->EnumAdapters1(0, &currentDefaultAdapter));
DXGI_ADAPTER_DESC1 currentDesc;
DX::ThrowIfFailed(currentDefaultAdapter->GetDesc1(&currentDesc));
// If the adapter LUIDs don't match, or if the device reports that it has been removed,
// a new D3D device must be created.
if (previousDesc.AdapterLuid.LowPart != currentDesc.AdapterLuid.LowPart ||
previousDesc.AdapterLuid.HighPart != currentDesc.AdapterLuid.HighPart ||
FAILED(m_d3dDevice->GetDeviceRemovedReason()))
{
// Release references to resources related to the old device.
dxgiDevice = nullptr;
deviceAdapter = nullptr;
deviceFactory = nullptr;
previousDefaultAdapter = nullptr;
// Create a new device and swap chain.
HandleDeviceLost();
}
}
// Recreate all device resources and set them back to the current state.
void DeviceResources::HandleDeviceLost()
{
m_swapChain = nullptr;
if (m_deviceNotify != nullptr)
{
m_deviceNotify->OnDeviceLost();
}
CreateDeviceResources();
m_d2dContext->SetDpi(m_dpi, m_dpi);
CreateWindowSizeDependentResources();
if (m_deviceNotify != nullptr)
{
m_deviceNotify->OnDeviceRestored();
}
}
// Register our DeviceNotify to be informed on device lost and creation.
void DeviceResources::RegisterDeviceNotify(DX::IDeviceNotify^ deviceNotify)
{
m_deviceNotify = deviceNotify;
}
// Call this method when the app suspends. It provides a hint to the driver that the app
// is entering an idle state and that temporary buffers can be reclaimed for use by other apps.
void DeviceResources::Trim()
{
ComPtr<IDXGIDevice3> dxgiDevice;
m_d3dDevice.As(&dxgiDevice);
dxgiDevice->Trim();
}
// Present the contents of the swap chain to the screen.
void DeviceResources::Present()
{
// The first argument instructs DXGI to block until VSync, putting the application
// to sleep until the next VSync. This ensures we don't waste any cycles rendering
// frames that will never be displayed to the screen.
DXGI_PRESENT_PARAMETERS parameters = { 0 };
HRESULT hr = m_swapChain->Present1(1, 0, &parameters);
// Discard the contents of the render target.
// This is a valid operation only when the existing contents will be entirely
// overwritten. If dirty or scroll rects are used, this call should be modified.
m_d3dContext->DiscardView1(m_d3dRenderTargetView.Get(), nullptr, 0);
// Discard the contents of the depth stencil.
m_d3dContext->DiscardView1(m_d3dDepthStencilView.Get(), nullptr, 0);
// If the device was removed either by a disconnection or a driver upgrade, we
// must recreate all device resources.
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
{
HandleDeviceLost();
}
else
{
DX::ThrowIfFailed(hr);
}
}
}

View file

@ -0,0 +1,108 @@
#pragma once
// Modified from the default template for Xaml and Direct3D 11 apps.
namespace GraphControl::DX
{
// Provides an interface for an application that owns DeviceResources to be notified of the device being lost or created.
interface class IDeviceNotify
{
virtual void OnDeviceLost();
virtual void OnDeviceRestored();
};
// Controls all the DirectX device resources.
class DeviceResources
{
public:
DeviceResources(Windows::UI::Xaml::Controls::SwapChainPanel^ panel);
void SetSwapChainPanel(Windows::UI::Xaml::Controls::SwapChainPanel^ panel);
void SetLogicalSize(Windows::Foundation::Size logicalSize);
void SetCurrentOrientation(Windows::Graphics::Display::DisplayOrientations currentOrientation);
void SetDpi(float dpi);
void SetCompositionScale(float compositionScaleX, float compositionScaleY);
void ValidateDevice();
void HandleDeviceLost();
void RegisterDeviceNotify(IDeviceNotify^ deviceNotify);
void Trim();
void Present();
// The size of the render target, in pixels.
Windows::Foundation::Size GetOutputSize() const { return m_outputSize; }
// The size of the render target, in dips.
Windows::Foundation::Size GetLogicalSize() const { return m_logicalSize; }
float GetDpi() const { return m_effectiveDpi; }
// D3D Accessors.
ID3D11Device3* GetD3DDevice() const { return m_d3dDevice.Get(); }
ID3D11DeviceContext3* GetD3DDeviceContext() const { return m_d3dContext.Get(); }
IDXGISwapChain3* GetSwapChain() const { return m_swapChain.Get(); }
D3D_FEATURE_LEVEL GetDeviceFeatureLevel() const { return m_d3dFeatureLevel; }
ID3D11RenderTargetView1* GetBackBufferRenderTargetView() const { return m_d3dRenderTargetView.Get(); }
ID3D11DepthStencilView* GetDepthStencilView() const { return m_d3dDepthStencilView.Get(); }
D3D11_VIEWPORT GetScreenViewport() const { return m_screenViewport; }
DirectX::XMFLOAT4X4 GetOrientationTransform3D() const { return m_orientationTransform3D; }
// D2D Accessors.
ID2D1Factory3* GetD2DFactory() const { return m_d2dFactory.Get(); }
ID2D1Device2* GetD2DDevice() const { return m_d2dDevice.Get(); }
ID2D1DeviceContext2* GetD2DDeviceContext() const { return m_d2dContext.Get(); }
ID2D1Bitmap1* GetD2DTargetBitmap() const { return m_d2dTargetBitmap.Get(); }
IDWriteFactory3* GetDWriteFactory() const { return m_dwriteFactory.Get(); }
IWICImagingFactory2* GetWicImagingFactory() const { return m_wicFactory.Get(); }
D2D1::Matrix3x2F GetOrientationTransform2D() const { return m_orientationTransform2D; }
private:
void CreateDeviceIndependentResources();
void CreateDeviceResources();
void CreateWindowSizeDependentResources();
void UpdateRenderTargetSize();
// Direct3D objects.
Microsoft::WRL::ComPtr<ID3D11Device3> m_d3dDevice;
Microsoft::WRL::ComPtr<ID3D11DeviceContext3> m_d3dContext;
Microsoft::WRL::ComPtr<IDXGISwapChain3> m_swapChain;
// Direct3D rendering objects. Required for 3D.
Microsoft::WRL::ComPtr<ID3D11RenderTargetView1> m_d3dRenderTargetView;
Microsoft::WRL::ComPtr<ID3D11DepthStencilView> m_d3dDepthStencilView;
D3D11_VIEWPORT m_screenViewport;
// Direct2D drawing components.
Microsoft::WRL::ComPtr<ID2D1Factory3> m_d2dFactory;
Microsoft::WRL::ComPtr<ID2D1Device2> m_d2dDevice;
Microsoft::WRL::ComPtr<ID2D1DeviceContext2> m_d2dContext;
Microsoft::WRL::ComPtr<ID2D1Bitmap1> m_d2dTargetBitmap;
// DirectWrite drawing components.
Microsoft::WRL::ComPtr<IDWriteFactory3> m_dwriteFactory;
Microsoft::WRL::ComPtr<IWICImagingFactory2> m_wicFactory;
// Cached reference to the XAML panel.
Windows::UI::Xaml::Controls::SwapChainPanel^ m_swapChainPanel;
// Cached device properties.
D3D_FEATURE_LEVEL m_d3dFeatureLevel;
Windows::Foundation::Size m_d3dRenderTargetSize;
Windows::Foundation::Size m_outputSize;
Windows::Foundation::Size m_logicalSize;
Windows::Graphics::Display::DisplayOrientations m_nativeOrientation;
Windows::Graphics::Display::DisplayOrientations m_currentOrientation;
float m_dpi;
float m_compositionScaleX;
float m_compositionScaleY;
// Variables that take into account whether the app supports high resolution screens or not.
float m_effectiveDpi;
float m_effectiveCompositionScaleX;
float m_effectiveCompositionScaleY;
// Transforms used for display orientation.
D2D1::Matrix3x2F m_orientationTransform2D;
DirectX::XMFLOAT4X4 m_orientationTransform3D;
// The IDeviceNotify can be held directly as it owns the DeviceResources.
IDeviceNotify^ m_deviceNotify;
};
}

View file

@ -0,0 +1,63 @@
#pragma once
// Taken from the default template for Xaml and Direct3D 11 apps.
namespace GraphControl::DX
{
inline void ThrowIfFailed(HRESULT hr)
{
if (FAILED(hr))
{
// Set a breakpoint on this line to catch Win32 API errors.
throw Platform::Exception::CreateException(hr);
}
}
// Function that reads from a binary file asynchronously.
inline Concurrency::task<std::vector<byte>> ReadDataAsync(const std::wstring& filename)
{
using namespace Windows::Storage;
using namespace Concurrency;
auto folder = Windows::ApplicationModel::Package::Current->InstalledLocation;
return create_task(folder->GetFileAsync(Platform::StringReference(filename.c_str()))).then([] (StorageFile^ file)
{
return FileIO::ReadBufferAsync(file);
}).then([] (Streams::IBuffer^ fileBuffer) -> std::vector<byte>
{
std::vector<byte> returnBuffer;
returnBuffer.resize(fileBuffer->Length);
Streams::DataReader::FromBuffer(fileBuffer)->ReadBytes(Platform::ArrayReference<byte>(returnBuffer.data(), fileBuffer->Length));
return returnBuffer;
});
}
// Converts a length in device-independent pixels (DIPs) to a length in physical pixels.
inline float ConvertDipsToPixels(float dips, float dpi)
{
static const float dipsPerInch = 96.0f;
return floorf(dips * dpi / dipsPerInch + 0.5f); // Round to nearest integer.
}
#if defined(_DEBUG)
// Check for SDK Layer support.
inline bool SdkLayersAvailable()
{
HRESULT hr = D3D11CreateDevice(
nullptr,
D3D_DRIVER_TYPE_NULL, // There is no need to create a real hardware device.
0,
D3D11_CREATE_DEVICE_DEBUG, // Check for the SDK layers.
nullptr, // Any feature level will do.
0,
D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Windows Store apps.
nullptr, // No need to keep the D3D device reference.
nullptr, // No need to know the feature level.
nullptr // No need to keep the D3D device context reference.
);
return SUCCEEDED(hr);
}
#endif
}

View file

@ -0,0 +1,66 @@
#include "pch.h"
#include "NearestPointRenderer.h"
#include "DirectXHelper.h"
using namespace D2D1;
using namespace GraphControl::DX;
using namespace std;
using namespace Windows::Foundation;
namespace
{
const ColorF c_DefaultPointColor = ColorF::Black;
constexpr float c_NearestPointRadius = 3;
}
NearestPointRenderer::NearestPointRenderer(DeviceResources* deviceResources)
: m_deviceResources{ deviceResources }
, m_color{ c_DefaultPointColor }
, m_ellipse{ D2D1_POINT_2F{ 0, 0 }, c_NearestPointRadius, c_NearestPointRadius }
{
CreateDeviceDependentResources();
}
void NearestPointRenderer::CreateDeviceDependentResources()
{
CreateBrush();
}
void NearestPointRenderer::ReleaseDeviceDependentResources()
{
m_brush.Reset();
}
void NearestPointRenderer::Render(const Point& location)
{
if (ID2D1DeviceContext* context = m_deviceResources->GetD2DDeviceContext())
{
m_ellipse.point.x = location.X;
m_ellipse.point.y = location.Y;
context->BeginDraw();
context->FillEllipse(m_ellipse, m_brush.Get());
// Ignore D2DERR_RECREATE_TARGET here. This error indicates that the device
// is lost. It will be handled during the next call to Present.
HRESULT hr = context->EndDraw();
if (hr != D2DERR_RECREATE_TARGET)
{
ThrowIfFailed(hr);
}
}
}
void NearestPointRenderer::SetColor(const ColorF& color)
{
m_color = color;
CreateBrush();
}
void NearestPointRenderer::CreateBrush()
{
m_brush.Reset();
ThrowIfFailed(
m_deviceResources->GetD2DDeviceContext()->CreateSolidColorBrush(m_color, &m_brush)
);
}

View file

@ -0,0 +1,30 @@
#pragma once
namespace GraphControl::DX
{
class DeviceResources;
class NearestPointRenderer
{
public:
NearestPointRenderer(DeviceResources* deviceResources);
void CreateDeviceDependentResources();
void ReleaseDeviceDependentResources();
void Render(const Windows::Foundation::Point& location);
void SetColor(const D2D1::ColorF& color);
private:
void CreateBrush();
private:
DeviceResources* const m_deviceResources;
D2D1::ColorF m_color;
D2D1_ELLIPSE m_ellipse;
// Resources related to rendering.
Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> m_brush;
};
}

View file

@ -0,0 +1,339 @@
#include "pch.h"
#include "RenderMain.h"
#include "DirectXHelper.h"
using namespace Concurrency;
using namespace Graphing;
using namespace Platform;
using namespace std;
using namespace Windows::Foundation;
using namespace Windows::Graphics::Display;
using namespace Windows::System::Threading;
using namespace Windows::UI::Core;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
namespace
{
constexpr unsigned int s_RedChannelIndex = 0;
constexpr unsigned int s_GreenChannelIndex = 1;
constexpr unsigned int s_BlueChannelIndex = 2;
constexpr unsigned int s_AlphaChannelIndex = 3;
constexpr float s_MaxChannelValue = 255.0f;
constexpr float nearestPointRadius = 3;
}
namespace GraphControl::DX
{
RenderMain::RenderMain(SwapChainPanel^ panel) :
m_deviceResources{ panel },
m_nearestPointRenderer{ &m_deviceResources },
m_backgroundColor{ {} },
m_swapChainPanel{ panel }
{
// Register to be notified if the Device is lost or recreated
m_deviceResources.RegisterDeviceNotify(this);
RegisterEventHandlers();
}
RenderMain::~RenderMain()
{
UnregisterEventHandlers();
}
void RenderMain::Graph::set(shared_ptr< IGraph > graph)
{
m_graph = move(graph);
if (m_graph)
{
if (auto renderer = m_graph->GetRenderer())
{
float dpi = m_deviceResources.GetDpi();
renderer->SetDpi(dpi, dpi);
renderer->SetGraphSize(
static_cast<unsigned int>(m_swapChainPanel->ActualWidth),
static_cast<unsigned int>(m_swapChainPanel->ActualHeight));
}
}
RunRenderPass();
}
void RenderMain::BackgroundColor::set(Windows::UI::Color backgroundColor)
{
m_backgroundColor[s_RedChannelIndex] = static_cast<float>(backgroundColor.R) / s_MaxChannelValue;
m_backgroundColor[s_GreenChannelIndex] = static_cast<float>(backgroundColor.G) / s_MaxChannelValue;
m_backgroundColor[s_BlueChannelIndex] = static_cast<float>(backgroundColor.B) / s_MaxChannelValue;
m_backgroundColor[s_AlphaChannelIndex] = static_cast<float>(backgroundColor.A) / s_MaxChannelValue;
RunRenderPass();
}
void RenderMain::DrawNearestPoint::set(bool value)
{
if (m_drawNearestPoint != value)
{
m_drawNearestPoint = value;
RunRenderPass();
}
}
void RenderMain::PointerLocation::set(Point location)
{
if (m_pointerLocation != location)
{
m_pointerLocation = location;
RunRenderPass();
}
}
// Updates application state when the window size changes (e.g. device orientation change)
void RenderMain::CreateWindowSizeDependentResources()
{
// TODO: Replace this with the sizedependent initialization of your app's content.
RunRenderPass();
}
void RenderMain::RunRenderPass()
{
if (Render())
{
m_deviceResources.Present();
}
}
// Renders the current frame according to the current application state.
// Returns true if the frame was rendered and is ready to be displayed.
bool RenderMain::Render()
{
bool successful = true;
// Must call BeginDraw before any draw commands.
ID2D1Factory3 *pFactory = m_deviceResources.GetD2DFactory();
ID2D1DeviceContext *pRenderTarget = m_deviceResources.GetD2DDeviceContext();
auto context = m_deviceResources.GetD3DDeviceContext();
// Clear the back buffer and set the background color.
context->ClearRenderTargetView(m_deviceResources.GetBackBufferRenderTargetView(), m_backgroundColor);
if (m_graph)
{
if (auto renderer = m_graph->GetRenderer())
{
pRenderTarget->BeginDraw();
bool hasMissingData = false;
successful = SUCCEEDED(renderer->DrawD2D1(pFactory, pRenderTarget, hasMissingData));
// We ignore D2DERR_RECREATE_TARGET here. This error indicates that the device
// is lost. It will be handled during the next call to Present.
HRESULT endDraw = pRenderTarget->EndDraw();
if (endDraw != D2DERR_RECREATE_TARGET)
{
DX::ThrowIfFailed(endDraw);
}
if (successful && m_drawNearestPoint)
{
int formulaId;
Point nearestPointLocation;
pair<float, float> nearestPointValue;
renderer->GetClosePointData(
m_pointerLocation.X, m_pointerLocation.Y,
formulaId,
nearestPointLocation.X, nearestPointLocation.Y,
nearestPointValue.first, nearestPointValue.second);
if (!isnan(nearestPointLocation.X) && !isnan(nearestPointLocation.Y))
{
m_nearestPointRenderer.Render(nearestPointLocation);
}
}
}
}
return successful;
}
void RenderMain::OnLoaded(Object^ sender, RoutedEventArgs^ e)
{
RunRenderPass();
}
void RenderMain::RegisterEventHandlers()
{
UnregisterEventHandlers();
// Register event handlers for control lifecycle.
m_coreWindow = Agile<CoreWindow>(Window::Current->CoreWindow);
if (m_coreWindow != nullptr)
{
m_tokenVisibilityChanged =
m_coreWindow->VisibilityChanged +=
ref new TypedEventHandler<CoreWindow^, VisibilityChangedEventArgs^>(this, &RenderMain::OnVisibilityChanged);
}
m_displayInformation = DisplayInformation::GetForCurrentView();
if (m_displayInformation != nullptr)
{
m_tokenDpiChanged =
m_displayInformation->DpiChanged +=
ref new TypedEventHandler<DisplayInformation^, Object^>(this, &RenderMain::OnDpiChanged);
m_tokenOrientationChanged =
m_displayInformation->OrientationChanged +=
ref new TypedEventHandler<DisplayInformation^, Object^>(this, &RenderMain::OnOrientationChanged);
}
m_tokenDisplayContentsInvalidated =
DisplayInformation::DisplayContentsInvalidated +=
ref new TypedEventHandler<DisplayInformation^, Object^>(this, &RenderMain::OnDisplayContentsInvalidated);
if (m_swapChainPanel != nullptr)
{
m_tokenLoaded =
m_swapChainPanel->Loaded += ref new RoutedEventHandler(this, &RenderMain::OnLoaded);
m_tokenCompositionScaleChanged =
m_swapChainPanel->CompositionScaleChanged +=
ref new TypedEventHandler< SwapChainPanel^, Object^ >(this, &RenderMain::OnCompositionScaleChanged);
m_tokenSizeChanged =
m_swapChainPanel->SizeChanged +=
ref new SizeChangedEventHandler(this, &RenderMain::OnSizeChanged);
}
}
void RenderMain::UnregisterEventHandlers()
{
if (m_coreWindow != nullptr)
{
if (m_tokenVisibilityChanged.Value != 0)
{
m_coreWindow->VisibilityChanged -= m_tokenVisibilityChanged;
m_tokenVisibilityChanged.Value = 0;
}
m_coreWindow = nullptr;
}
if (m_displayInformation != nullptr)
{
if (m_tokenDpiChanged.Value != 0)
{
m_displayInformation->DpiChanged -= m_tokenDpiChanged;
m_tokenDpiChanged.Value = 0;
}
if (m_tokenOrientationChanged.Value != 0)
{
m_displayInformation->OrientationChanged -= m_tokenOrientationChanged;
m_tokenOrientationChanged.Value = 0;
}
m_displayInformation = nullptr;
}
if (m_tokenDisplayContentsInvalidated.Value != 0)
{
DisplayInformation::DisplayContentsInvalidated -= m_tokenDisplayContentsInvalidated;
m_tokenDisplayContentsInvalidated.Value = 0;
}
if (m_swapChainPanel != nullptr)
{
if (m_tokenLoaded.Value != 0)
{
m_swapChainPanel->Loaded -= m_tokenLoaded;
m_tokenLoaded.Value = 0;
}
if (m_tokenCompositionScaleChanged.Value != 0)
{
m_swapChainPanel->CompositionScaleChanged -= m_tokenCompositionScaleChanged;
m_tokenCompositionScaleChanged.Value = 0;
}
if (m_tokenSizeChanged.Value != 0)
{
m_swapChainPanel->SizeChanged -= m_tokenSizeChanged;
m_tokenSizeChanged.Value = 0;
}
}
}
void RenderMain::OnVisibilityChanged(CoreWindow^ sender, VisibilityChangedEventArgs^ args)
{
if (args->Visible)
{
RunRenderPass();
}
}
void RenderMain::OnDpiChanged(DisplayInformation^ sender, Object^ args)
{
// Note: The value for LogicalDpi retrieved here may not match the effective DPI of the app
// if it is being scaled for high resolution devices. Once the DPI is set on DeviceResources,
// you should always retrieve it using the GetDpi method.
// See DeviceResources.cpp for more details.
m_deviceResources.SetDpi(sender->LogicalDpi);
if (m_graph)
{
if (auto renderer = m_graph->GetRenderer())
{
float dpi = m_deviceResources.GetDpi();
renderer->SetDpi(dpi, dpi);
}
}
CreateWindowSizeDependentResources();
}
void RenderMain::OnOrientationChanged(DisplayInformation^ sender, Object^ args)
{
m_deviceResources.SetCurrentOrientation(sender->CurrentOrientation);
CreateWindowSizeDependentResources();
}
void RenderMain::OnDisplayContentsInvalidated(DisplayInformation^ sender, Object^ args)
{
m_deviceResources.ValidateDevice();
}
void RenderMain::OnCompositionScaleChanged(SwapChainPanel^ sender, Object^ args)
{
m_deviceResources.SetCompositionScale(sender->CompositionScaleX, sender->CompositionScaleY);
CreateWindowSizeDependentResources();
}
void RenderMain::OnSizeChanged(Object^ sender, SizeChangedEventArgs^ e)
{
m_deviceResources.SetLogicalSize(e->NewSize);
if (m_graph)
{
if (auto renderer = m_graph->GetRenderer())
{
const auto& newSize = e->NewSize;
renderer->SetGraphSize(
static_cast<unsigned int>(newSize.Width),
static_cast<unsigned int>(newSize.Height));
}
}
CreateWindowSizeDependentResources();
}
// Notifies renderers that device resources need to be released.
void RenderMain::OnDeviceLost()
{
m_nearestPointRenderer.ReleaseDeviceDependentResources();
}
// Notifies renderers that device resources may now be recreated.
void RenderMain::OnDeviceRestored()
{
m_nearestPointRenderer.CreateDeviceDependentResources();
}
}

View file

@ -0,0 +1,102 @@
#pragma once
// Taken from the default template for Xaml and Direct3D 11 apps.
#include "DeviceResources.h"
#include "NearestPointRenderer.h"
#include "IGraph.h"
// Renders Direct2D and 3D content on the screen.
namespace GraphControl::DX
{
ref class RenderMain sealed : public IDeviceNotify
{
public:
virtual ~RenderMain();
// IDeviceNotify
virtual void OnDeviceLost();
virtual void OnDeviceRestored();
internal:
RenderMain(Windows::UI::Xaml::Controls::SwapChainPanel^ panel);
property std::shared_ptr< Graphing::IGraph > Graph
{
void set(std::shared_ptr< Graphing::IGraph > graph);
}
property Windows::UI::Color BackgroundColor
{
void set(Windows::UI::Color color);
}
property bool DrawNearestPoint
{
void set(bool value);
}
property Windows::Foundation::Point PointerLocation
{
void set(Windows::Foundation::Point location);
}
void CreateWindowSizeDependentResources();
void RunRenderPass();
private:
bool Render();
// Loaded/Unloaded
void OnLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
// Dependent event registration
void RegisterEventHandlers();
void UnregisterEventHandlers();
// Window event handlers.
void OnVisibilityChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::VisibilityChangedEventArgs^ args);
// DisplayInformation event handlers.
void OnDpiChanged(Windows::Graphics::Display::DisplayInformation^ sender, Platform::Object^ args);
void OnOrientationChanged(Windows::Graphics::Display::DisplayInformation^ sender, Platform::Object^ args);
void OnDisplayContentsInvalidated(Windows::Graphics::Display::DisplayInformation^ sender, Platform::Object^ args);
// Other event handlers.
void OnCompositionScaleChanged(Windows::UI::Xaml::Controls::SwapChainPanel^ sender, Object^ args);
void OnSizeChanged(Platform::Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e);
private:
DX::DeviceResources m_deviceResources;
NearestPointRenderer m_nearestPointRenderer;
// Cached Graph object with Renderer property.
std::shared_ptr< Graphing::IGraph > m_graph = nullptr;
// Track current input pointer position.
bool m_drawNearestPoint = false;
Windows::Foundation::Point m_pointerLocation;
float m_backgroundColor[4];
// The SwapChainPanel^ surface.
Windows::UI::Xaml::Controls::SwapChainPanel^ m_swapChainPanel = nullptr;
Windows::Foundation::EventRegistrationToken m_tokenLoaded;
Windows::Foundation::EventRegistrationToken m_tokenCompositionScaleChanged;
Windows::Foundation::EventRegistrationToken m_tokenSizeChanged;
// Cached references to event notifiers.
Platform::Agile<Windows::UI::Core::CoreWindow> m_coreWindow = nullptr;
Windows::Foundation::EventRegistrationToken m_tokenVisibilityChanged;
Windows::Graphics::Display::DisplayInformation^ m_displayInformation = nullptr;
Windows::Foundation::EventRegistrationToken m_tokenDpiChanged;
Windows::Foundation::EventRegistrationToken m_tokenOrientationChanged;
Windows::Foundation::EventRegistrationToken m_tokenDisplayContentsInvalidated;
// Track our independent input on a background worker thread.
Windows::Foundation::IAsyncAction^ m_inputLoopWorker = nullptr;
Windows::UI::Core::CoreIndependentInputSource^ m_coreInput = nullptr;
};
}

View file

@ -0,0 +1,346 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM">
<Configuration>Debug</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM">
<Configuration>Release</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{e727a92b-f149-492c-8117-c039a298719b}</ProjectGuid>
<Keyword>WindowsRuntimeComponent</Keyword>
<RootNamespace>GraphControl</RootNamespace>
<DefaultLanguage>en-US</DefaultLanguage>
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
<AppContainerApplication>true</AppContainerApplication>
<ApplicationType>Windows Store</ApplicationType>
<WindowsTargetPlatformVersion Condition="'$(WindowsTargetPlatformVersion)' == ''">10.0.17763.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformMinVersion>10.0.17134.0</WindowsTargetPlatformMinVersion>
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup>
<GraphingInterfaceDir>$([MSBuild]::ValueOrDefault($(GraphingInterfaceDir), '$(SolutionDir)\GraphingInterfaces\'))</GraphingInterfaceDir>
</PropertyGroup>
<ImportGroup Condition="Exists('GraphingImplOverrides.props')">
<Import Project="GraphingImplOverrides.props" />
</ImportGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalOptions>/bigobj /await %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir);$(GraphingInterfaceDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalLibraryDirectories>$(GraphingImplLibDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>$(GraphingImplLib);WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalOptions>/bigobj /await /d2CoroOptsWorkaround %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir);$(GraphingInterfaceDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>$(GraphingImplLib);WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(GraphingImplLibDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalOptions>/bigobj /await %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir);$(GraphingInterfaceDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>$(GraphingImplLib);WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(GraphingImplLibDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalOptions>/bigobj /await /d2CoroOptsWorkaround %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir);$(GraphingInterfaceDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>$(GraphingImplLib);WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(GraphingImplLibDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir);$(GraphingInterfaceDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>$(GraphingImplLib);WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(GraphingImplLibDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir);$(GraphingInterfaceDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>$(GraphingImplLib);WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(GraphingImplLibDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalOptions>/bigobj /await %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir);$(GraphingInterfaceDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>$(GraphingImplLib);WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(GraphingImplLibDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalOptions>/bigobj /await /d2CoroOptsWorkaround %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir);$(GraphingInterfaceDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>$(GraphingImplLib);WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(GraphingImplLibDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="Control\Equation.h" />
<ClInclude Include="Control\EquationCollection.h" />
<ClInclude Include="Control\Grapher.h" />
<ClInclude Include="Control\InspectingDataSource.h" />
<ClInclude Include="DirectX\DeviceResources.h" />
<ClInclude Include="DirectX\DirectXHelper.h" />
<ClInclude Include="DirectX\NearestPointRenderer.h" />
<ClInclude Include="DirectX\RenderMain.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="winrtHeaders.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Control\Equation.cpp" />
<ClCompile Include="Control\Grapher.cpp" />
<ClCompile Include="Control\InspectingDataSource.cpp" />
<ClCompile Include="DirectX\DeviceResources.cpp" />
<ClCompile Include="DirectX\NearestPointRenderer.cpp" />
<ClCompile Include="DirectX\RenderMain.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Page Include="Themes\generic.xaml" />
</ItemGroup>
<ItemGroup Condition="$([MSBuild]::ValueOrDefault($(UseMockGraphingImpl), true))">
<ProjectReference Include="$(SolutionDir)\MockGraphingImpl\MockGraphingImpl.vcxproj">
<Project>{52E03A58-B378-4F50-8BFB-F659FB85E790}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>

View file

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="DirectX">
<UniqueIdentifier>{0d550f5f-db67-4160-8648-397c9bdc0307}</UniqueIdentifier>
</Filter>
<Filter Include="Themes">
<UniqueIdentifier>{3d424f3b-ba30-440b-ac2b-8a2740506153}</UniqueIdentifier>
</Filter>
<Filter Include="Control">
<UniqueIdentifier>{e8d91a71-6933-4fd8-b333-421085d13896}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp" />
<ClCompile Include="DirectX\DeviceResources.cpp">
<Filter>DirectX</Filter>
</ClCompile>
<ClCompile Include="DirectX\RenderMain.cpp">
<Filter>DirectX</Filter>
</ClCompile>
<ClCompile Include="Control\Equation.cpp">
<Filter>Control</Filter>
</ClCompile>
<ClCompile Include="Control\Grapher.cpp">
<Filter>Control</Filter>
</ClCompile>
<ClCompile Include="Control\InspectingDataSource.cpp">
<Filter>Control</Filter>
</ClCompile>
<ClCompile Include="DirectX\NearestPointRenderer.cpp">
<Filter>DirectX</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="DirectX\DeviceResources.h">
<Filter>DirectX</Filter>
</ClInclude>
<ClInclude Include="DirectX\DirectXHelper.h">
<Filter>DirectX</Filter>
</ClInclude>
<ClInclude Include="DirectX\RenderMain.h">
<Filter>DirectX</Filter>
</ClInclude>
<ClInclude Include="winrtHeaders.h" />
<ClInclude Include="Control\Equation.h">
<Filter>Control</Filter>
</ClInclude>
<ClInclude Include="Control\EquationCollection.h">
<Filter>Control</Filter>
</ClInclude>
<ClInclude Include="Control\Grapher.h">
<Filter>Control</Filter>
</ClInclude>
<ClInclude Include="Control\InspectingDataSource.h">
<Filter>Control</Filter>
</ClInclude>
<ClInclude Include="DirectX\NearestPointRenderer.h">
<Filter>DirectX</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Page Include="Themes\generic.xaml">
<Filter>Themes</Filter>
</Page>
</ItemGroup>
</Project>

View file

@ -0,0 +1,13 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:GraphControl">
<Style TargetType="local:Grapher">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:Grapher">
<SwapChainPanel x:Name="GraphSurface"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

1
src/GraphControl/pch.cpp Normal file
View file

@ -0,0 +1 @@
#include "pch.h"

34
src/GraphControl/pch.h Normal file
View file

@ -0,0 +1,34 @@
#pragma once
//C4453: A '[WebHostHidden]' type should not be used on the published surface of a public type that is not '[WebHostHidden]'
// This warning is disabled because the types in this app will not be published for use in javascript environment
#pragma warning(disable:4453)
// Windows headers define a min/max macro.
// Include the below #def to avoid this behavior.
#define NOMINMAX
#include <collection.h>
#include <ppltasks.h>
#include <pplawait.h>
#include <concrt.h>
#include <future>
#include <memory>
#include <assert.h>
#include <functional>
#include <string>
#include <sstream>
// DirectX headers
#include <d2d1_3.h>
#include <d3d11_4.h>
#include <dwrite_3.h>
#include <DirectXColors.h>
#include <wincodec.h>
#include <wrl.h>
#include <windows.ui.xaml.media.dxinterop.h>
// C++/WinRT
#include "winrtHeaders.h"
#include "Control/Grapher.h"

View file

@ -0,0 +1,24 @@
#pragma once
#include <winrt/base.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.UI.Xaml.Interop.h>
template <typename T>
T from_cx(Platform::Object^ from)
{
T to{ nullptr };
winrt::check_hresult(reinterpret_cast<::IUnknown*>(from)
->QueryInterface(winrt::guid_of<T>(),
reinterpret_cast<void**>(winrt::put_abi(to))));
return to;
}
template <typename T>
T^ to_cx(winrt::Windows::Foundation::IUnknown const& from)
{
return safe_cast<T^>(reinterpret_cast<Platform::Object^>(winrt::get_abi(from)));
}

View file

@ -0,0 +1,96 @@
#pragma once
#include <memory>
#ifndef GRAPHINGAPI
#ifdef GRAPHING_ENGINE_IMPL
#define GRAPHINGAPI __declspec(dllexport)
#else
#define GRAPHINGAPI __declspec(dllimport)
#endif
#endif
namespace Graphing
{
struct NonCopyable
{
NonCopyable() = default;
virtual ~NonCopyable() = default;
NonCopyable(NonCopyable const&) = delete;
NonCopyable& operator=(NonCopyable const&) = delete;
};
struct NonMoveable
{
NonMoveable() = default;
virtual ~NonMoveable() = default;
NonMoveable(NonMoveable&&) = delete;
NonMoveable& operator=(NonMoveable&&) = delete;
};
struct IExpression
{
virtual ~IExpression() = default;
virtual unsigned int GetExpressionID() const = 0;
virtual bool IsEmptySet() const = 0;
};
struct IExpressible
{
virtual ~IExpressible() = default;
virtual std::shared_ptr< IExpression > GetExpression() const = 0;
};
class Color
{
private:
// Each color channel is given an 8 bit space in a 32 bit uint32_t.
// The format is (RRGGBBAA). As an example,
// Red is FF0000FF (4,278,190,335)
static constexpr uint8_t redChannelShift = 24;
static constexpr uint8_t greenChannelShift = 16;
static constexpr uint8_t blueChannelShift = 8;
static constexpr uint8_t alphaChannelShift = 0;
public:
uint8_t R;
uint8_t G;
uint8_t B;
uint8_t A;
Color::Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
: R{ r }, G{ g }, B{ b }, A{ a }
{}
Color::Color(uint8_t r, uint8_t g, uint8_t b) noexcept
: Color{ r, g, b, 0xFF }
{}
Color::Color() noexcept
: Color{ 0, 0, 0 }
{}
explicit Color(uint32_t value) noexcept
: Color{
static_cast<uint8_t>(value >> redChannelShift),
static_cast<uint8_t>(value >> greenChannelShift),
static_cast<uint8_t>(value >> blueChannelShift),
static_cast<uint8_t>(value >> alphaChannelShift)
}
{
}
explicit operator uint32_t() const
{
return (A << alphaChannelShift)
| (R << redChannelShift)
| (G << greenChannelShift)
| (B << blueChannelShift);
}
};
}

View file

@ -0,0 +1,387 @@
#pragma once
namespace Graphing
{
enum class LocalizationType
{
Unknown,
DecimalPointAndListComma,
DecimalPointAndListSemicolon,
DecimalCommaAndListSemicolon
};
enum class EquationParsingMode
{
// Solving an equation. At least one equal sign is required
SolveEquation,
// Graphing an equation. At least one equal sign is required
GraphEquation,
// Not expecting an equation. No equal sign is allowed
NonEquation,
// Accept zero or more equal signs
DoNotCare
};
enum class FormatType
{
// This format is not very human-readable, but best for machine processing and unit test.
// The meaning of an expression is precise, there is no need for parentheses, and it's easy to parse.
// While other serializers may contain little tweaks to make the UI component work as expected, this
// format doesn't change often because no real UI uses it.
// Example: Sum[1,Divide[2,x]]
Formula,
// Similar to Formula, except the variables are in the format of Var(0), Var(1), ... instead of in their names.
// Used in serialization only (CasContext.FormatOptions) but not in parsing (CasContext.ParsingOptions)
// Example: Sum[1,Divide[2,Var(0)]]
InvariantFormula,
// Similar to Formula, except the aggregates (grouping parentheses) are silently removed during serialization.
// The primary usage for this format is in internal test cases.
// When used in parsing, it's identical to Formula
// Example: Sum[1,Divide[2,x]]
FormulaWithoutAggregate,
// This format is the most human-readable one. Can be used in command line applications or application that accept
// and displays linear syntax. Also, the RichEdit wrapper returns linear syntax and it should be parsed with Linear format type.
// Example: 1+2/x
Linear,
// This format is similar to linear format, but during serialization it uses ASCII characters only (except for variable
// named specified by the user) and can round trip back to the linear parser.
//
// When used in parsing it's identical to Linear.
LinearInput,
// The standard MathML format. Note that the math engine can only parse a subset of the syntax in the MathML specification.
// Example: &lt;math&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/mfrac&gt;&lt;/math&gt;
MathML,
// Same as MathML, except this format type won't generate the root element &lt;math&gt; .
// Used in serialization only (CasContext.FormatOptions) but not in parsing (CasContext.ParsingOptions)
// Example: &lt;mn&gt;1&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/mfrac&gt;
MathMLNoWrapper,
// This format type is for the RichEdit wrapper to render math in RichEdit.
//
// Used in serialization only (CasContext.FormatOptions) but not in parsing (CasContext.ParsingOptions)
// Example: 1 + \frac{2,x}
MathRichEdit,
// This format type is for the RichEdit wrapper to render math in RichEdit.
// It's same with MathRichEdit format with one exception: fractions are rendered
// horizontally instead of vertically. This is a better choice if the display area
// is confined with a small height.
//
// Used in serialization only (CasContext.FormatOptions) but not in parsing (CasContext.ParsingOptions)
// Example: 1 + x/(2 a)
InlineMathRichEdit,
// The most compact format. Uses binary data. Like Format, it guarantees round trip between parsing and serialization.
Binary,
// Similar to Binary, except variableIds are used instead of variable name.
InvariantBinary,
// This is the base-64 encoded Binary format
Base64,
// This is the base-64 encoded InvariantBinary format.
InvariantBase64,
// Latex format
// Example: \frac{1}{2}
Latex
};
// Specify on what number field the evaluation should be performed: Real or Complex.
enum class EvalNumberField
{
// This is invalid setting.
Invalid,
// Evaluation should be performed on Real number field
Real,
// Evaluation should be performed on Complex number field
Complex
};
// Specify the evaluation direction: Expand, Factor or Neutral (neither Expand nor Factor)
enum class EvalExpandMode
{
// Neither Expand nor Factor
Neutral,
// To expand during evaluation
Expand,
// To factor during evaluation
Factor
};
// Specify the current trigonometry unit mode: Radians, Degrees, Grads. This has effect on these trig operators:
// Sin Cos Tan Cot Sec Csc
// ASin ACos ATan ACot ASec ACsc.
// It has NO effect on hyperbolic trig operators (same behavior as Windows Calc), and any other operators.
enum class EvalTrigUnitMode
{
// Invalid value.
Invalid,
// Default trig unit. Period of sin is 2pi
Radians,
// Degrees as trig unit. Period of sin is 360 degrees
Degrees,
// Grads as trig unit. Period of sin is 400 grads
Grads
};
// Specifies the type of contextual action
enum class ContextualActionType
{
// The input didn't generate any contextual action
None,
// Solve equation
SolveEquation,
// perform comparison
Compare,
// Expand the expression
Expand,
// Perform a 2D graphing
Graph2D,
// Perform a 2D graphing on all items in the list
ListGraph2D,
// Perform 2D graphing of the two functions on both side of the equal sign or inequality sign
GraphBothSides2D,
// Perform a 3D graphing
Graph3D,
// Perform 3D graphing of the two functions on both side of the equal sign or inequality sign
GraphBothSides3D,
// Perform 2D inequality graphing
GraphInequality,
// Perform assignment
Assign,
// Factor the expression
Factor,
// Compute the derivate of the expression
Deriv,
// Compute the integral of the expression
IndefiniteIntegral,
// Perform 2D graph
Graph2DExpression,
// Perform 3D graph
Graph3DExpression,
// solve the inequality
SolveInequality,
// calculate/simplify the expression
Calculate,
// round of the number
Round,
// floor of the number
Floor,
// ceiling of the number
Ceiling,
// The bit mask for matrix related contextual action types. if (type &amp; MatrixMask)!=0 then the type is matrix related
MatrixMask,
// Compute the determinant of the matrix
MatrixDeterminant,
// Compute the inverse of the matrix
MatrixInverse,
// Compute the trace of the matrix
MatrixTrace,
// Compute the transpose of the matrix
MatrixTranspose,
// Compute the size of the matrix
MatrixSize,
// Compute the reduce of the matrix
MatrixReduce,
// The bit mask for list related contextual action types. if (type &amp; ListMask)!=0 then the type is list related
ListMask,
// Sort the list
ListSort,
// Compute the mean of the list
ListMean,
// Compute the median of the list
ListMedian,
// Compute the mode of the list
ListMode,
// Compute the LCM (least common multiplier) of the list
ListLcm,
// Compute the GCF (greatest common factor) of the list
ListGcf,
// Compute the sum of the list elements
ListSum,
// Compute the product of the list elements
ListProduct,
// Compute the max of the list elements
ListMax,
// Compute the min of the list elements
ListMin,
// Compute the variance of the list elements
ListVariance,
// Compute the standard deviation of the list elements
ListStdDev,
// Show complete (verbose) solution
ShowVerboseSolution,
// bit mask of action type. Can be used to remove the Informational flag
TypeMask, // mask to get the type
// A flag that can be added onto any type. This is informational only that explains what the straight input would do.
// No action should be performed
Informational
};
enum class MathActionCategoryType
{
Unknown,
Calculate,
Solve,
Integrate,
Differentiate,
Algebra,
Matrix,
List,
Graph
};
enum class StepSequenceType
{
None,
Text,
Expression,
NewLine,
NewStep,
Conditional,
Composite,
Goto,
Call,
Return,
Stop,
Error,
GotoTemp
};
enum class FormatVerbosityMode
{
Verbose,
Simple
};
namespace Renderer
{
// Used to indicate what action should be performed to change the range.
enum class ChangeRangeAction
{
// Zoom in on all axes by the predefined ratio
ZoomIn,
// Zoom out on all axes by the predefined ratio
ZoomOut,
// Zoom out on X axis only, leave the range of Y (and Z in 3D) unchanged
WidenX,
// Zoom in on X axis only, leave the range of Y (and Z in 3D) unchanged
ShrinkX,
// Zoom out on Y axis only, leave the range of X (and Z in 3D) unchanged
WidenY,
// Zoom in on Y axis only, leave the range of X (and Z in 3D) unchanged
ShrinkY,
// Zoom out on Z axis only, leave the range of X and Y unchanged. Apply to 3D graph only but not 2D graph.
WidenZ,
// Zoom in on Z axis only, leave the range of X and Y unchanged. Apply to 3D graph only but not 2D graph.
ShrinkZ,
// Move the view window of the graph towards the negative X axis.
MoveNegativeX,
// Move the view window of the graph towards the positive X axis.
MovePositiveX,
// Move the view window of the graph towards the negative Y axis.
MoveNegativeY,
// Move the view window of the graph towards the positive Y axis.
MovePositiveY,
// Move the view window of the graph towards the negative Z axis.
MoveNegativeZ,
// Move the view window of the graph towards the positive Z axis.
MovePositiveZ,
// Zoom in on all axes by the predefined ratio. The ratio is smaller than used in ZoomIn result in a smoother motion
SmoothZoomIn,
// Zoom out on all axes by the predefined ratio. The ratio is smaller than used in ZoomIn result in a smoother motion
SmoothZoomOut,
// Zoom in on all axes by the predefined ratio
PinchZoomIn,
// Zoom out on all axes by the predefined ratio
PinchZoomOut
};
enum class LineStyle
{
Solid,
Dot,
Dash,
DashDot,
DashDotDot
};
}
}

View file

@ -0,0 +1,19 @@
#pragma once
#include "Common.h"
#include "IGraphingOptions.h"
#include "IGraphRenderer.h"
namespace Graphing
{
struct IGraph : public NonCopyable, public NonMoveable
{
virtual ~IGraph() = default;
virtual bool TryInitialize(const IExpression* graphingExp) = 0;
virtual IGraphingOptions& GetOptions() = 0;
virtual std::shared_ptr< Renderer::IGraphRenderer > GetRenderer() const = 0;
};
}

View file

@ -0,0 +1,20 @@
#pragma once
#include "Common.h"
struct ID2D1Factory;
struct ID2D1RenderTarget;
namespace Graphing::Renderer
{
struct IGraphRenderer : public NonCopyable, public NonMoveable
{
virtual ~IGraphRenderer() = default;
virtual HRESULT SetGraphSize(unsigned int width, unsigned int height) = 0;
virtual HRESULT SetDpi(float dpiX, float dpiY) = 0;
virtual HRESULT DrawD2D1(ID2D1Factory* pDirect2dFactory, ID2D1RenderTarget* pRenderTarget, bool& hasSomeMissingDataOut) = 0;
virtual HRESULT GetClosePointData(float inScreenPointX, float inScreenPointY, int& formulaIdOut, float& xScreenPointOut, float& yScreenPointOut, float& xValueOut, float& yValueOut) = 0;
};
}

View file

@ -0,0 +1,116 @@
#pragma once
#include <vector>
#include <string>
#include "Common.h"
#include "GraphingEnums.h"
namespace Graphing
{
struct IGraphingOptions : public NonCopyable, public NonMoveable
{
virtual ~IGraphingOptions() = default;
virtual void ResetMarkKeyGraphFeaturesData() = 0;
virtual bool GetMarkZeros() const = 0;
virtual void SetMarkZeros(bool value) = 0;
virtual bool GetMarkYIntercept() const = 0;
virtual void SetMarkYIntercept(bool value) = 0;
virtual bool GetMarkMinima() const = 0;
virtual void SetMarkMinima(bool value) = 0;
virtual bool GetMarkMaxima() const = 0;
virtual void SetMarkMaxima(bool value) = 0;
virtual bool GetMarkInflectionPoints() const = 0;
virtual void SetMarkInflectionPoints(bool value) = 0;
virtual bool GetMarkVerticalAsymptotes() const = 0;
virtual void SetMarkVerticalAsymptotes(bool value) = 0;
virtual bool GetMarkHorizontalAsymptotes() const = 0;
virtual void SetMarkHorizontalAsymptotes(bool value) = 0;
virtual bool GetMarkObliqueAsymptotes() const = 0;
virtual void SetMarkObliqueAsymptotes(bool value) = 0;
virtual unsigned long long GetMaxExecutionTime() const = 0;
virtual void SetMaxExecutionTime(unsigned long long value) = 0;
virtual void ResetMaxExecutionTime() = 0;
virtual std::vector<Graphing::Color> GetGraphColors() const = 0;
virtual bool SetGraphColors(const std::vector<Graphing::Color>& colors) = 0;
virtual void ResetGraphColors() = 0;
virtual Graphing::Color GetBackColor() const = 0;
virtual void SetBackColor(const Graphing::Color& value) = 0;
virtual void ResetBackColor() = 0;
virtual Graphing::Color GetZerosColor() const = 0;
virtual void SetZerosColor(const Graphing::Color& value) = 0;
virtual void ResetZerosColor() = 0;
virtual Graphing::Color GetExtremaColor() const = 0;
virtual void SetExtremaColor(const Graphing::Color& value) = 0;
virtual void ResetExtremaColor() = 0;
virtual Graphing::Color GetInflectionPointsColor() const = 0;
virtual void SetInflectionPointsColor(const Graphing::Color& value) = 0;
virtual void ResetInflectionPointsColor() = 0;
virtual Graphing::Color GetAsymptotesColor() const = 0;
virtual void SetAsymptotesColor(const Graphing::Color& value) = 0;
virtual void ResetAsymptotesColor() = 0;
virtual Graphing::Color GetAxisColor() const = 0;
virtual void SetAxisColor(const Graphing::Color& value) = 0;
virtual void ResetAxisColor() = 0;
virtual Graphing::Color GetBoxColor() const = 0;
virtual void SetBoxColor(const Graphing::Color& value) = 0;
virtual void ResetBoxColor() = 0;
virtual Graphing::Color GetFontColor() const = 0;
virtual void SetFontColor(const Graphing::Color& value) = 0;
virtual void ResetFontColor() = 0;
virtual bool GetShowAxis() const = 0;
virtual void SetShowAxis(bool value) = 0;
virtual void ResetShowAxis() = 0;
virtual bool GetShowGrid() const = 0;
virtual void SetShowGrid(bool value) = 0;
virtual void ResetShowGrid() = 0;
virtual bool GetShowBox() const = 0;
virtual void SetShowBox(bool value) = 0;
virtual void ResetShowBox() = 0;
virtual bool GetForceProportional() const = 0;
virtual void SetForceProportional(bool value) = 0;
virtual void ResetForceProportional() = 0;
virtual std::wstring GetAliasX() const = 0;
virtual void SetAliasX(const std::wstring& value) = 0;
virtual void ResetAliasX() = 0;
virtual std::wstring GetAliasY() const = 0;
virtual void SetAliasY(const std::wstring& value) = 0;
virtual void ResetAliasY() = 0;
virtual Graphing::Renderer::LineStyle GetLineStyle() const = 0;
virtual void SetLineStyle(Graphing::Renderer::LineStyle value) = 0;
virtual void ResetLineStyle() = 0;
virtual std::pair<double, double> GetDefaultXRange() const = 0;
virtual bool SetDefaultXRange(const std::pair<double, double>& minmax) = 0;
virtual void ResetDefaultXRange() = 0;
virtual std::pair<double, double> GetDefaultYRange() const = 0;
virtual bool SetDefaultYRange(const std::pair<double, double>& minmax) = 0;
virtual void ResetDefaultYRange() = 0;
};
}

View file

@ -0,0 +1,45 @@
#pragma once
#include "Common.h"
#include "IGraph.h"
#include "GraphingEnums.h"
namespace Graphing
{
struct IParsingOptions : public NonCopyable, public NonMoveable
{
virtual ~IParsingOptions() = default;
virtual void SetFormatType(FormatType type) = 0;
};
struct IEvalOptions : public NonCopyable, public NonMoveable
{
virtual ~IEvalOptions() = default;
};
struct IFormatOptions : public NonCopyable, public NonMoveable
{
virtual ~IFormatOptions() = default;
virtual void SetFormatType(FormatType type) = 0;
};
struct IMathSolver : public NonCopyable, public NonMoveable
{
virtual ~IMathSolver() = default;
static GRAPHINGAPI std::unique_ptr<IMathSolver> CreateMathSolver();
virtual IParsingOptions& ParsingOptions() = 0;
virtual IEvalOptions& EvalOptions() = 0;
virtual IFormatOptions& FormatOptions() = 0;
virtual std::unique_ptr<IExpression> ParseInput(const std::wstring& input) = 0;
virtual std::shared_ptr<IGraph> CreateGrapher(const IExpression* expression) = 0;
virtual std::shared_ptr<Graphing::IGraph> CreateGrapher() = 0;
virtual std::wstring Serialize(const IExpression* expression) = 0;
};
}

View file

@ -0,0 +1,297 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM">
<Configuration>Debug</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM">
<Configuration>Release</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{52e03a58-b378-4f50-8bfb-f659fb85e790}</ProjectGuid>
<Keyword>DynamicLibrary</Keyword>
<RootNamespace>MockGraphingImpl</RootNamespace>
<DefaultLanguage>en-US</DefaultLanguage>
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
<AppContainerApplication>true</AppContainerApplication>
<ApplicationType>Windows Store</ApplicationType>
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformMinVersion>10.0.17763.0</WindowsTargetPlatformMinVersion>
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<GenerateManifest>false</GenerateManifest>
<IgnoreImportLibrary>false</IgnoreImportLibrary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<GenerateManifest>false</GenerateManifest>
<IgnoreImportLibrary>false</IgnoreImportLibrary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<GenerateManifest>false</GenerateManifest>
<IgnoreImportLibrary>false</IgnoreImportLibrary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<GenerateManifest>false</GenerateManifest>
<IgnoreImportLibrary>false</IgnoreImportLibrary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<GenerateManifest>false</GenerateManifest>
<IgnoreImportLibrary>false</IgnoreImportLibrary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<GenerateManifest>false</GenerateManifest>
<IgnoreImportLibrary>false</IgnoreImportLibrary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<GenerateManifest>false</GenerateManifest>
<IgnoreImportLibrary>false</IgnoreImportLibrary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<GenerateManifest>false</GenerateManifest>
<IgnoreImportLibrary>false</IgnoreImportLibrary>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalOptions>/DGRAPHING_ENGINE_IMPL %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)\..\;$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalOptions>/DGRAPHING_ENGINE_IMPL %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)\..\;$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|arm'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalOptions>/DGRAPHING_ENGINE_IMPL %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)\..\;$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|arm'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalOptions>/DGRAPHING_ENGINE_IMPL %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)\..\;$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalOptions>/DGRAPHING_ENGINE_IMPL %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)\..\;$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalOptions>/DGRAPHING_ENGINE_IMPL %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)\..\;$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalOptions>/DGRAPHING_ENGINE_IMPL %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)\..\;$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalOptions>/DGRAPHING_ENGINE_IMPL %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)\..\;$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\GraphingInterfaces\Common.h" />
<ClInclude Include="..\GraphingInterfaces\GraphingEnums.h" />
<ClInclude Include="..\GraphingInterfaces\IGraph.h" />
<ClInclude Include="..\GraphingInterfaces\IGraphingOptions.h" />
<ClInclude Include="..\GraphingInterfaces\IGraphRenderer.h" />
<ClInclude Include="..\GraphingInterfaces\IMathSolver.h" />
<ClInclude Include="Mocks\MathSolver.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="targetver.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="Mocks\MathSolver.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="GraphingInterfaces">
<UniqueIdentifier>{a74bebcf-8242-4c82-bd5f-6735feda8879}</UniqueIdentifier>
</Filter>
<Filter Include="Mocks">
<UniqueIdentifier>{e5205167-e65a-458c-a7e4-b3bc468c60ab}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="pch.cpp" />
<ClCompile Include="Mocks\MathSolver.cpp">
<Filter>Mocks</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="targetver.h" />
<ClInclude Include="..\GraphingInterfaces\Common.h">
<Filter>GraphingInterfaces</Filter>
</ClInclude>
<ClInclude Include="..\GraphingInterfaces\GraphingEnums.h">
<Filter>GraphingInterfaces</Filter>
</ClInclude>
<ClInclude Include="..\GraphingInterfaces\IGraph.h">
<Filter>GraphingInterfaces</Filter>
</ClInclude>
<ClInclude Include="..\GraphingInterfaces\IGraphingOptions.h">
<Filter>GraphingInterfaces</Filter>
</ClInclude>
<ClInclude Include="..\GraphingInterfaces\IMathSolver.h">
<Filter>GraphingInterfaces</Filter>
</ClInclude>
<ClInclude Include="Mocks\MathSolver.h">
<Filter>Mocks</Filter>
</ClInclude>
<ClInclude Include="..\GraphingInterfaces\IGraphRenderer.h">
<Filter>GraphingInterfaces</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -0,0 +1,12 @@
#include "pch.h"
#include "MathSolver.h"
using namespace std;
namespace Graphing
{
unique_ptr<IMathSolver> IMathSolver::CreateMathSolver()
{
return make_unique<MockGraphingImpl::MathSolver>();
}
}

View file

@ -0,0 +1,68 @@
#pragma once
#include "GraphingInterfaces/IMathSolver.h"
namespace MockGraphingImpl
{
class ParsingOptions : public Graphing::IParsingOptions
{
public:
void SetFormatType(Graphing::FormatType type) override
{ }
};
class EvalOptions : public Graphing::IEvalOptions
{
};
class FormatOptions : public Graphing::IFormatOptions
{
public:
void SetFormatType(Graphing::FormatType type) override
{ }
};
class MathSolver : public Graphing::IMathSolver
{
public:
Graphing::IParsingOptions& ParsingOptions() override
{
return m_parsingOptions;
}
Graphing::IEvalOptions& EvalOptions() override
{
return m_evalOptions;
}
Graphing::IFormatOptions& FormatOptions() override
{
return m_formatOptions;
}
std::unique_ptr<Graphing::IExpression> ParseInput(const std::wstring& input) override
{
return nullptr;
}
std::shared_ptr<Graphing::IGraph> CreateGrapher(const Graphing::IExpression* expression) override
{
return nullptr;
}
std::shared_ptr<Graphing::IGraph> CreateGrapher() override
{
return nullptr;
}
std::wstring Serialize(const Graphing::IExpression* expression) override
{
return std::wstring{};
}
private:
MockGraphingImpl::ParsingOptions m_parsingOptions;
MockGraphingImpl::EvalOptions m_evalOptions;
MockGraphingImpl::FormatOptions m_formatOptions;
};
}

View file

@ -0,0 +1,14 @@
#include "pch.h"
BOOL APIENTRY DllMain(HMODULE /* hModule */, DWORD ul_reason_for_call, LPVOID /* lpReserved */)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

View file

@ -0,0 +1 @@
#include "pch.h"

View file

@ -0,0 +1,9 @@
#pragma once
#include "targetver.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>

View file

@ -0,0 +1,8 @@
#pragma once
// Including SDKDDKVer.h defines the highest available Windows platform.
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
#include <SDKDDKVer.h>