Feature/GraphingCalculator initial commit (#450)

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

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

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

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)));
}