- Initial commit for Graphing Calculator feature.

This commit is contained in:
Daniel Belcher 2019-03-20 14:13:20 -07:00
commit a59a26519b
64 changed files with 5186 additions and 109 deletions

1
.gitignore vendored
View file

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

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.
#include "pch.h"
@ -42,6 +42,7 @@ namespace
ApplicationViewModel::ApplicationViewModel() :
m_CalculatorViewModel(nullptr),
m_DateCalcViewModel(nullptr),
m_GraphingCalcViewModel(nullptr),
m_ConverterViewModel(nullptr),
m_PreviousMode(ViewMode::None),
m_mode(ViewMode::None),
@ -132,6 +133,13 @@ void ApplicationViewModel::OnModeChanged()
}
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))
{
TraceLogger::GetInstance().LogDateCalculatorModeViewed(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()));

View file

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

View file

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

View file

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

View file

@ -62,10 +62,6 @@ namespace CalculatorApp
IsStandardMode = (int) CM::Command::ModeBasic,
None = (int) CM::Command::CommandNULL,
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,
Ror = (int) CM::Command::CommandROR,
Rol = (int) CM::Command::CommandROL,
@ -87,12 +83,21 @@ namespace CalculatorApp
InvSinh = (int) CM::Command::CommandASINH,
InvCosh = (int) CM::Command::CommandACOSH,
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,
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,
BINPOS0 = (int) CM::Command::CommandBINPOS0,
@ -159,8 +164,7 @@ namespace CalculatorApp
BINPOS61 = (int) CM::Command::CommandBINPOS61,
BINPOS62 = (int) CM::Command::CommandBINPOS62,
BINPOS63 = (int) CM::Command::CommandBINPOS63,
BINEND = (int) CM::Command::CommandBINEDITEND,
Hyp = (int) CM::Command::CommandHYP
BINEND = (int) CM::Command::CommandBINEDITEND
};
// This contains list of functions whose usage we are tracelogging

View file

@ -10,6 +10,7 @@
using namespace Concurrency;
using namespace Platform;
using namespace std;
using namespace std::chrono;
using namespace Windows::ApplicationModel::Resources;
using namespace Windows::UI::Xaml;
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
// 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
VisualStateManager::GoToState(button, "Pressed", true);
// This timer will fire after lightUpTime and make the button
// 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;
winrt::apartment_context uiThreadContext;
WeakReference timerWeakReference(timer);
WeakReference buttonWeakReference(button);
timer->Tick += ref new EventHandler<Object^>(
[buttonWeakReference, timerWeakReference](Object^, Object^)
{
auto button = buttonWeakReference.Resolve<ButtonBase>();
if (button)
{
VisualStateManager::GoToState(button, "Normal", true);
}
co_await winrt::resume_background();
co_await winrt::resume_after(500ms);
// Cancel the timer after we're done so it only fires once
auto timer = timerWeakReference.Resolve<DispatcherTimer>();
if (timer)
{
timer->Stop();
}
});
timer->Start();
co_await uiThreadContext;
// Restore the normal state
VisualStateManager::GoToState(button, "Normal", true);
}
// 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);
auto buttons = s_CharacterForButtons.find(viewId)->second.equal_range(character);
RunFirstEnabledButtonCommand(buttons);
LightUpButtons(buttons);
RunFirstEnabledButtonCommand(buttons);
}
}
}
@ -613,8 +595,6 @@ void KeyboardShortcutManager::OnKeyDownHandler(CoreWindow^ sender, KeyEventArgs^
{
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.
// 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
@ -627,6 +607,8 @@ void KeyboardShortcutManager::OnKeyDownHandler(CoreWindow^ sender, KeyEventArgs^
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 ANGLE_ID = 15;
static constexpr int CURRENCY_ID = 16;
static constexpr int GRAPHING_ID = 17;
// ^^^ THESE CONSTANTS SHOULD NEVER CHANGE ^^^
// 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::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::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::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 },
@ -121,9 +123,15 @@ bool NavCategory::IsValidViewMode(ViewMode mode)
bool NavCategory::IsCalculatorViewMode(ViewMode mode)
{
// Historically, Date Calculator is not a Calculator mode
// even though it is in the Calculator category.
return !IsDateCalculatorViewMode(mode) && IsModeInCategoryGroup(mode, CategoryGroupType::Calculator);
// Historically, Calculator modes are Standard, Scientific, and Programmer.
return !IsDateCalculatorViewMode(mode)
&& !IsGraphingCalculatorViewMode(mode)
&& IsModeInCategoryGroup(mode, CategoryGroupType::Calculator);
}
bool NavCategory::IsGraphingCalculatorViewMode(ViewMode mode)
{
return mode == ViewMode::Graphing;
}
bool NavCategory::IsDateCalculatorViewMode(ViewMode mode)

View file

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

View file

@ -15,11 +15,13 @@
using namespace CalculatorApp;
using namespace CalculatorApp::Common;
using namespace concurrency;
using namespace Graphing::Renderer;
using namespace Platform;
using namespace std;
using namespace Utils;
using namespace Windows::ApplicationModel::Resources;
using namespace Windows::Storage::Streams;
using namespace Windows::UI;
using namespace Windows::UI::Core;
using namespace Windows::UI::ViewManagement;
using namespace Windows::UI::Xaml;
@ -68,7 +70,7 @@ int Utils::GetWindowId()
return windowId;
}
void Utils::RunOnUIThreadNonblocking(std::function<void()>&& function, _In_ CoreDispatcher^ currentDispatcher)
void Utils::RunOnUIThreadNonblocking(function<void()>&& function, _In_ CoreDispatcher^ currentDispatcher)
{
if (currentDispatcher != nullptr)
{
@ -91,7 +93,7 @@ wstring Utils::RemoveUnwantedCharsFromWstring(wstring input, wchar_t* unwantedCh
{
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;
}
@ -225,3 +227,53 @@ task<String^> Utils::ReadFileFromFolder(IStorageFolder^ folder, String^ fileName
String^ contents = co_await FileIO::ReadTextAsync(file);
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/ExpressionCommandInterface.h"
#include "DelegateCommand.h"
#include "GraphingInterfaces/GraphingEnums.h"
// Utility macros to make Models easier to write
// 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<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
@ -421,3 +431,18 @@ namespace CalculatorApp
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
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CalculatorUnitTests", "CalculatorUnitTests\CalculatorUnitTests.vcxproj", "{D3BAED2C-4B07-4E1D-8807-9D6499450349}"
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
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM = Debug|ARM
@ -28,22 +32,6 @@ Global
Release|x86 = Release|x86
EndGlobalSection
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.Build.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.Build.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.Build.0 = Debug|ARM
{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.Build.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
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

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

View file

@ -218,6 +218,9 @@
<Filter Include="Views\StateTriggers">
<UniqueIdentifier>{0120c344-0bc0-4a1d-b82c-df7945f46189}</UniqueIdentifier>
</Filter>
<Filter Include="Views\GraphingCalculator">
<UniqueIdentifier>{e23e2a6e-491b-4200-9bf7-d355a1ee695b}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml" />
@ -480,6 +483,12 @@
<Page Include="Views\OperatorsPanel.xaml">
<Filter>Views</Filter>
</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>
<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">
<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" />
<Properties>
<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>
</resheader>
<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>
</data>
<data name="DevAppName" xml:space="preserve">
@ -1271,7 +1271,7 @@
</data>
<data name="equalButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<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 name="shiftButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Inverse Function</value>
@ -3379,4 +3379,40 @@
<value>Microsoft Services Agreement</value>
<comment>Displayed on a link to the Microsoft Services Agreement in the about this app information</comment>
</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>

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">
<!-- PLACEHOLDER!!!! This is where the date calculator goes when it is delay loaded -->
</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">
<!-- PLACEHOLDER!!!! This is where the converter goes when it is delay loaded -->
</Border>

View file

@ -167,6 +167,10 @@ void MainPage::OnAppPropertyChanged(_In_ Platform::Object^ sender, _In_ Windows:
}
EnsureDateCalculator();
}
else if (newValue == ViewMode::Graphing)
{
EnsureGraphingCalculator();
}
else if (NavCategory::IsConverterViewMode(newValue))
{
if (m_model->CalculatorViewModel)
@ -196,9 +200,10 @@ void MainPage::OnAppPropertyChanged(_In_ Platform::Object^ sender, _In_ Windows:
void MainPage::ShowHideControls(ViewMode mode)
{
auto isCalcViewMode = NavCategory::IsCalculatorViewMode(mode);
auto isDateCalcViewMode = NavCategory::IsDateCalculatorViewMode(mode);
auto isConverterViewMode = NavCategory::IsConverterViewMode(mode);
bool isCalcViewMode = NavCategory::IsCalculatorViewMode(mode);
bool isDateCalcViewMode = NavCategory::IsDateCalculatorViewMode(mode);
bool isGraphingCalcViewMode = NavCategory::IsGraphingCalculatorViewMode(mode);
bool isConverterViewMode = NavCategory::IsConverterViewMode(mode);
if (m_calculator)
{
@ -212,6 +217,12 @@ void MainPage::ShowHideControls(ViewMode mode)
m_dateCalculator->IsEnabled = isDateCalcViewMode;
}
if (m_graphingCalculator)
{
m_graphingCalculator->Visibility = BooleanToVisibilityConverter::Convert(isGraphingCalcViewMode);
m_graphingCalculator->IsEnabled = isGraphingCalcViewMode;
}
if (m_converter)
{
m_converter->Visibility = BooleanToVisibilityConverter::Convert(isConverterViewMode);
@ -239,7 +250,7 @@ void MainPage::UpdatePanelViewState()
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
EnsureCalculator();
@ -292,6 +303,10 @@ void MainPage::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)
{
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()
{
if (!m_converter)
@ -571,7 +598,7 @@ void MainPage::SetHeaderAutomationName()
else
{
wstring full;
if (NavCategory::IsCalculatorViewMode(mode))
if (NavCategory::IsCalculatorViewMode(mode) || NavCategory::IsGraphingCalculatorViewMode(mode))
{
full = resProvider.GetResourceString(L"HeaderAutomationName_Calculator")->Data();
}
@ -581,6 +608,7 @@ void MainPage::SetHeaderAutomationName()
}
string::size_type found = full.find(L"%1");
assert(found != wstring::npos);
wstring strMode = m_model->CategoryName->Data();
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.
#pragma once
@ -6,6 +6,7 @@
#include "Views/Calculator.xaml.h"
#include "Views/MainPage.g.h"
#include "Views/DateCalculator.xaml.h"
#include "Views/GraphingCalculator/GraphingCalculator.xaml.h"
#include "Views/UnitConverter.xaml.h"
#include "CalcViewModel/ApplicationViewModel.h"
#include "Views/TitleBar.xaml.h"
@ -24,9 +25,9 @@ namespace CalculatorApp
{
public:
MainPage();
property CalculatorApp::ViewModel::ApplicationViewModel^ Model
property ViewModel::ApplicationViewModel^ Model
{
CalculatorApp::ViewModel::ApplicationViewModel^ get(){
ViewModel::ApplicationViewModel^ get(){
return m_model;
}
}
@ -36,7 +37,7 @@ namespace CalculatorApp
void SetDefaultFocus();
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:
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 OnAboutFlyoutClosed(_In_ Platform::Object^ sender, _In_ Platform::Object^ e);
Microsoft::UI::Xaml::Controls::NavigationViewItemHeader^ CreateNavViewHeaderFromGroup(CalculatorApp::Common::NavCategoryGroup^ group);
Microsoft::UI::Xaml::Controls::NavigationViewItem^ CreateNavViewItemFromCategory(CalculatorApp::Common::NavCategory^ category);
Microsoft::UI::Xaml::Controls::NavigationViewItemHeader^ CreateNavViewHeaderFromGroup(Common::NavCategoryGroup^ group);
Microsoft::UI::Xaml::Controls::NavigationViewItem^ CreateNavViewItemFromCategory(Common::NavCategory^ category);
Windows::Foundation::EventRegistrationToken m_fullscreenFlyoutClosedToken;
void OnFullscreenFlyoutClosed();
void ShowHideControls(CalculatorApp::Common::ViewMode mode);
void ShowHideControls(Common::ViewMode mode);
void UpdateViewState();
void UpdatePanelViewState();
@ -73,21 +74,23 @@ namespace CalculatorApp
void PinUnpinAppBarButtonOnClicked(_In_ Platform::Object^ sender, _In_ Windows::UI::Xaml::RoutedEventArgs^ e);
void EnsureCalculator();
void EnsureConverter();
void EnsureDateCalculator();
void EnsureGraphingCalculator();
void EnsureConverter();
void ShowAboutPage();
void AnnounceCategoryName();
CalculatorApp::Calculator^ m_calculator;
CalculatorApp::UnitConverter^ m_converter;
CalculatorApp::DateCalculator^ m_dateCalculator;
Calculator^ m_calculator;
GraphingCalculator^ m_graphingCalculator;
UnitConverter^ m_converter;
DateCalculator^ m_dateCalculator;
Windows::Foundation::EventRegistrationToken _windowSizeEventToken;
Windows::Foundation::EventRegistrationToken m_hardwareButtonsBackPressedToken;
Windows::Foundation::EventRegistrationToken m_colorValuesChangedToken;
CalculatorApp::ViewModel::ApplicationViewModel^ m_model;
ViewModel::ApplicationViewModel^ m_model;
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
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()
@ -106,6 +106,7 @@ namespace CalculatorUnitTests
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Scientific));
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Programmer));
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::Volume));
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Length));
@ -125,9 +126,9 @@ namespace CalculatorUnitTests
{
VERIFY_IS_FALSE(NavCategory::IsValidViewMode(ViewMode::None));
// There are 17 total options so int 17 should be the first invalid
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(static_cast<ViewMode>(16)));
VERIFY_IS_FALSE(NavCategory::IsValidViewMode(static_cast<ViewMode>(17)));
// There are 18 total options so int 18 should be the first invalid
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(static_cast<ViewMode>(17)));
VERIFY_IS_FALSE(NavCategory::IsValidViewMode(static_cast<ViewMode>(18)));
// Also verify the lower bound
VERIFY_IS_TRUE(NavCategory::IsValidViewMode(static_cast<ViewMode>(0)));
@ -141,6 +142,7 @@ namespace CalculatorUnitTests
VERIFY_IS_TRUE(NavCategory::IsCalculatorViewMode(ViewMode::Programmer));
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::Volume));
@ -165,6 +167,8 @@ namespace CalculatorUnitTests
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::Volume));
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::Scientific));
VERIFY_IS_FALSE(NavCategory::IsConverterViewMode(ViewMode::Programmer));
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::Volume));
@ -209,6 +213,7 @@ namespace CalculatorUnitTests
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"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"Volume"), NavCategory::GetFriendlyName(ViewMode::Volume));
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::Programmer));
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::Volume));
@ -251,11 +257,12 @@ namespace CalculatorUnitTests
void NavCategoryUnitTests::GetIndex()
{
// Index is the 0-based ordering of modes
vector<ViewMode> orderedModes = {
ViewMode orderedModes[] = {
ViewMode::Standard,
ViewMode::Scientific,
ViewMode::Programmer,
ViewMode::Date,
ViewMode::Graphing,
ViewMode::Currency,
ViewMode::Volume,
ViewMode::Length,
@ -271,7 +278,8 @@ namespace CalculatorUnitTests
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];
VERIFY_ARE_EQUAL(index, (size_t)NavCategory::GetIndex(mode));
@ -283,11 +291,12 @@ namespace CalculatorUnitTests
void NavCategoryUnitTests::GetPosition()
{
// Position is the 1-based ordering of modes
vector<ViewMode> orderedModes = {
ViewMode orderedModes[] = {
ViewMode::Standard,
ViewMode::Scientific,
ViewMode::Programmer,
ViewMode::Date,
ViewMode::Graphing,
ViewMode::Currency,
ViewMode::Volume,
ViewMode::Length,
@ -303,7 +312,8 @@ namespace CalculatorUnitTests
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];
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(2, NavCategory::GetIndexInGroup(ViewMode::Programmer, 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(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::Scientific, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number2));
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)
@ -380,29 +393,30 @@ namespace CalculatorUnitTests
VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, calculatorGroup->GroupType);
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, 1u, ViewMode::Scientific, 2);
ValidateNavCategory(calculatorCategories, 2u, ViewMode::Programmer, 3);
ValidateNavCategory(calculatorCategories, 3u, ViewMode::Date, 4);
ValidateNavCategory(calculatorCategories, 4u, ViewMode::Graphing, 5);
NavCategoryGroup^ converterGroup = menuOptions->GetAt(1);
VERIFY_ARE_EQUAL(CategoryGroupType::Converter, converterGroup->GroupType);
IObservableVector<NavCategory^>^ converterCategories = converterGroup->Categories;
VERIFY_ARE_EQUAL(13, converterCategories->Size);
ValidateNavCategory(converterCategories, 0u, ViewMode::Currency, 5);
ValidateNavCategory(converterCategories, 1u, ViewMode::Volume, 6);
ValidateNavCategory(converterCategories, 2u, ViewMode::Length, 7);
ValidateNavCategory(converterCategories, 3u, ViewMode::Weight, 8);
ValidateNavCategory(converterCategories, 4u, ViewMode::Temperature, 9);
ValidateNavCategory(converterCategories, 5u, ViewMode::Energy, 10);
ValidateNavCategory(converterCategories, 6u, ViewMode::Area, 11);
ValidateNavCategory(converterCategories, 7u, ViewMode::Speed, 12);
ValidateNavCategory(converterCategories, 8u, ViewMode::Time, 13);
ValidateNavCategory(converterCategories, 9u, ViewMode::Power, 14);
ValidateNavCategory(converterCategories, 10u, ViewMode::Data, 15);
ValidateNavCategory(converterCategories, 11u, ViewMode::Pressure, 16);
ValidateNavCategory(converterCategories, 12u, ViewMode::Angle, 17);
ValidateNavCategory(converterCategories, 0u, ViewMode::Currency, 6);
ValidateNavCategory(converterCategories, 1u, ViewMode::Volume, 7);
ValidateNavCategory(converterCategories, 2u, ViewMode::Length, 8);
ValidateNavCategory(converterCategories, 3u, ViewMode::Weight, 9);
ValidateNavCategory(converterCategories, 4u, ViewMode::Temperature, 10);
ValidateNavCategory(converterCategories, 5u, ViewMode::Energy, 11);
ValidateNavCategory(converterCategories, 6u, ViewMode::Area, 12);
ValidateNavCategory(converterCategories, 7u, ViewMode::Speed, 13);
ValidateNavCategory(converterCategories, 8u, ViewMode::Time, 14);
ValidateNavCategory(converterCategories, 9u, ViewMode::Power, 15);
ValidateNavCategory(converterCategories, 10u, ViewMode::Data, 16);
ValidateNavCategory(converterCategories, 11u, ViewMode::Pressure, 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>