19#include "../../forms/TransferFunctionForm.h"
24 m_supNumber[0] = L
'\u2070';
25 m_supNumber[1] = L
'\u00B9';
26 m_supNumber[2] = L
'\u00B2';
27 m_supNumber[3] = L
'\u00B3';
28 m_supNumber[4] = L
'\u2074';
29 m_supNumber[5] = L
'\u2075';
30 m_supNumber[6] = L
'\u2076';
31 m_supNumber[7] = L
'\u2077';
32 m_supNumber[8] = L
'\u2078';
33 m_supNumber[9] = L
'\u2079';
36 m_numerator.push_back(1);
37 m_denominator.clear();
38 m_denominator.push_back(1);
39 m_denominator.push_back(1);
42 Node* node1 =
new Node(m_position + wxPoint2DDouble(-m_width / 2, 0), Node::NodeType::NODE_IN, m_borderSize);
43 node1->StartMove(m_position);
44 Node* node2 =
new Node(m_position + wxPoint2DDouble(m_width / 2, 0), Node::NodeType::NODE_OUT, m_borderSize);
45 node2->SetAngle(180.0);
46 node2->StartMove(m_position);
47 m_nodeList.push_back(node1);
48 m_nodeList.push_back(node2);
51TransferFunction::~TransferFunction()
54 if (m_gcTextDen)
delete m_gcTextDen;
55 if (m_gcTextNum)
delete m_gcTextNum;
57 for (
auto& node : m_nodeList) if (node) delete node;
89 gc->SetPen(*wxTRANSPARENT_PEN);
90 gc->SetBrush(wxBrush(m_selectionColour));
91 double borderSize = (m_borderSize * 2.0 + 1.0) / scale;
92 gc->DrawRectangle(m_position.m_x - m_width / 2 - borderSize / 2, m_position.m_y - m_height / 2 - borderSize / 2, m_width + borderSize, m_height + borderSize);
94 gc->SetPen(*wxBLACK_PEN);
95 gc->SetBrush(*wxWHITE_BRUSH);
96 gc->DrawRectangle(m_position.m_x - m_width / 2, m_position.m_y - m_height / 2, m_width, m_height);
98 wxPoint2DDouble linePts[2];
99 linePts[0] = wxPoint2DDouble(m_position.m_x - m_width / 2 + 5 + m_borderSize, m_position.m_y);
100 linePts[1] = wxPoint2DDouble(m_position.m_x + m_width / 2 - 5 - m_borderSize, m_position.m_y);
101 gc->StrokeLines(2, linePts);
103 gc->SetPen(*wxTRANSPARENT_PEN);
104 gc->SetBrush(*wxBLACK_BRUSH);
108 m_gcTextNum->
Draw(m_position + wxPoint2DDouble(-m_gcTextNum->GetWidth() / 2, -m_height / 4 - m_gcTextNum->GetHeight() / 2), gc);
109 m_gcTextDen->
Draw(m_position + wxPoint2DDouble(-m_gcTextDen->GetWidth() / 2, m_height / 4 - m_gcTextDen->GetHeight() / 2), gc);
112void TransferFunction::SetText(wxString numerator, wxString denominator)
115 m_gcTextNum->
SetText(numerator);
117 m_gcTextNum =
new GCText(numerator);
120 m_gcTextDen->
SetText(denominator);
122 m_gcTextDen =
new GCText(denominator);
124 double nWidth =
static_cast<double>(m_gcTextNum->GetWidth()) + 5 + m_borderSize;
125 double dWidth =
static_cast<double>(m_gcTextDen->GetWidth()) + 5 + m_borderSize;
127 m_width = nWidth > dWidth ? nWidth : dWidth;
128 m_height =
static_cast<double>(m_gcTextNum->GetHeight()) +
static_cast<double>(m_gcTextDen->GetHeight()) + 2 * m_borderSize;
132wxString TransferFunction::GetSuperscriptNumber(
int number)
134 wxString strNumber = wxString::Format(
"%d", number);
135 wxString superscriptStr =
"";
136 for (
int i = 0; i < (int)strNumber.length(); ++i) {
137 wxString digitStr = strNumber[i];
139 digitStr.ToLong(&digit);
140 superscriptStr += wxString(m_supNumber[digit]);
142 return superscriptStr;
145void TransferFunction::GetTFString(wxString& numerator, wxString& denominator)
149 int index =
static_cast<int>(m_numerator.size()) - 1;
150 for (
auto it = m_numerator.begin(), itEnd = m_numerator.end(); it != itEnd; ++it) {
154 if (index ==
static_cast<int>(m_numerator.size()) - 1) {
171 else if (index == 1) {
173 numerator += signal +
"s";
181 numerator += signal +
"s" + GetSuperscriptNumber(index);
184 numerator += signal +
StringFromDouble(std::abs(value), 0) +
"s" + GetSuperscriptNumber(index);
192 index =
static_cast<int>(m_denominator.size()) - 1;
193 for (
auto it = m_denominator.begin(), itEnd = m_denominator.end(); it != itEnd; ++it) {
197 if (index ==
static_cast<int>(m_denominator.size()) - 1) {
214 else if (index == 1) {
216 denominator += signal +
"s";
224 denominator += signal +
"s" + GetSuperscriptNumber(index);
227 denominator += signal +
StringFromDouble(std::abs(value), 0) +
"s" + GetSuperscriptNumber(index);
236void TransferFunction::UpdateTFText()
239 GetTFString(num, den);
241 if (m_nodeList.size() == 2) {
242 if (m_angle == 0.0) {
243 m_nodeList[0]->SetPosition(m_position + wxPoint2DDouble(-m_width / 2, 0));
244 m_nodeList[1]->SetPosition(m_position + wxPoint2DDouble(m_width / 2, 0));
246 else if (m_angle == 90.0) {
247 m_nodeList[0]->SetPosition(m_position + wxPoint2DDouble(0, -m_height / 2));
248 m_nodeList[1]->SetPosition(m_position + wxPoint2DDouble(0, m_height / 2));
250 else if (m_angle == 180.0) {
251 m_nodeList[0]->SetPosition(m_position + wxPoint2DDouble(m_width / 2, 0));
252 m_nodeList[1]->SetPosition(m_position + wxPoint2DDouble(-m_width / 2, 0));
254 else if (m_angle == 270.0) {
255 m_nodeList[0]->SetPosition(m_position + wxPoint2DDouble(0, m_height / 2));
256 m_nodeList[1]->SetPosition(m_position + wxPoint2DDouble(0, -m_height / 2));
264 tfForm.CenterOnParent();
265 if (tfForm.ShowModal() == wxID_OK) {
277 if (m_angle >= 360.0)
279 else if (m_angle < 0)
282 if (m_angle == 0.0) {
283 m_nodeList[0]->SetPosition(m_position + wxPoint2DDouble(-m_width / 2, 0));
284 m_nodeList[1]->SetPosition(m_position + wxPoint2DDouble(m_width / 2, 0));
286 else if (m_angle == 90.0) {
287 m_nodeList[0]->SetPosition(m_position + wxPoint2DDouble(0, -m_height / 2));
288 m_nodeList[1]->SetPosition(m_position + wxPoint2DDouble(0, m_height / 2));
290 else if (m_angle == 180.0) {
291 m_nodeList[0]->SetPosition(m_position + wxPoint2DDouble(m_width / 2, 0));
292 m_nodeList[1]->SetPosition(m_position + wxPoint2DDouble(-m_width / 2, 0));
294 else if (m_angle == 270.0) {
295 m_nodeList[0]->SetPosition(m_position + wxPoint2DDouble(0, m_height / 2));
296 m_nodeList[1]->SetPosition(m_position + wxPoint2DDouble(0, -m_height / 2));
299 for (
auto it = m_nodeList.begin(), itEnd = m_nodeList.end(); it != itEnd; ++it) {
301 node->Rotate(clockwise);
307 m_maxIteration = maxIteration;
310 int order =
static_cast<int>(m_denominator.size());
311 std::vector<double> denominator = m_denominator;
312 std::vector<double> numerator;
316 for (
int i = 0; i < order; i++) {
317 int numIndex = i - (order -
static_cast<int>(m_numerator.size()));
319 numerator.push_back(0.0);
321 numerator.push_back(m_numerator[numIndex]);
326 for (
int i = 0; i < (order - 1); i++) {
327 std::vector<double> lineA;
328 for (
int j = 0; j < (order - 1); j++) {
330 lineA.push_back(1.0);
332 lineA.push_back(0.0);
334 ss.A.push_back(lineA);
338 for (
int i = 0; i < order - 1; i++) {
339 ss.A[order - 2][i] = -(denominator[order - 1 - i] / denominator[0]);
340 ss.C[i] = numerator[order - 1 - i] / denominator[0] -
341 (denominator[order - 1 - i] / denominator[0]) * (numerator[0] / denominator[0]);
343 ss.B[order - 2] = 1.0;
344 ss.D = numerator[0] / denominator[0];
352 for (
unsigned int i = 0; i < m_denominator.size(); ++i) {
365 int order =
static_cast<int>(m_ss.A.size());
367 std::vector<double> x;
368 std::vector<double> oldx;
369 std::vector<double> dx;
370 std::vector<double> olddx;
371 for (
int i = 0; i < order; i++) {
373 oldx.push_back(m_x[i]);
375 dx.push_back(m_dx[i]);
376 olddx.push_back(m_dx[i]);
383 double dxError = 0.0;
384 for (
int i = 0; i < order; i++) {
386 x[i] = m_x[i] + 0.5 * timeStep * (m_dx[i] + dx[i]);
388 if (std::abs(x[i] - oldx[i]) > xError) xError = std::abs(x[i] - oldx[i]);
392 for (
int i = 0; i < order; i++) {
395 for (
int j = 0; j < order; j++) dx[i] += m_ss.A[i][j] * x[j];
396 dx[i] += m_ss.B[i] * input[0];
398 if (std::abs(dx[i] - olddx[i]) > dxError) dxError = std::abs(dx[i] - olddx[i]);
402 if (std::max(xError, dxError) < m_error) exit =
true;
405 if (iter >= m_maxIteration)
return false;
409 for (
int i = 0; i < order; i++) {
410 m_output += m_ss.C[i] * x[i];
415 m_output += m_ss.D * input[0];
423 copy->m_gcTextNum = m_gcTextNum ? m_gcTextNum->
GetCopy() :
nullptr;
424 copy->m_gcTextDen = m_gcTextDen ? m_gcTextDen->
GetCopy() :
nullptr;
436rapidxml::xml_node<>* TransferFunction::SaveElement(rapidxml::xml_document<>& doc,
437 rapidxml::xml_node<>* elementListNode)
439 auto elementNode = XMLParser::AppendNode(doc, elementListNode,
"TransferFunction");
440 XMLParser::SetNodeAttribute(doc, elementNode,
"ID", m_elementID);
442 SaveCADProperties(doc, elementNode);
443 SaveControlNodes(doc, elementNode);
446 auto numeratorNode = XMLParser::AppendNode(doc, elementNode,
"Numerator");
447 for (
unsigned int i = 0; i < m_numerator.size(); ++i) {
448 auto value = XMLParser::AppendNode(doc, numeratorNode,
"Value");
449 XMLParser::SetNodeValue(doc, value, m_numerator[i]);
451 auto denominatorNode = XMLParser::AppendNode(doc, elementNode,
"Denominator");
452 for (
unsigned int i = 0; i < m_denominator.size(); ++i) {
453 auto value = XMLParser::AppendNode(doc, denominatorNode,
"Value");
454 XMLParser::SetNodeValue(doc, value, m_denominator[i]);
460bool TransferFunction::OpenElement(rapidxml::xml_node<>* elementNode)
462 if (!OpenCADProperties(elementNode))
return false;
463 if (!OpenControlNodes(elementNode))
return false;
466 std::vector<double> numerator, denominator;
468 m_denominator.clear();
469 auto numeratorNode = elementNode->first_node(
"Numerator");
470 auto nValue = numeratorNode->first_node(
"Value");
473 wxString(nValue->value()).ToCDouble(&value);
474 m_numerator.push_back(value);
475 nValue = nValue->next_sibling(
"Value");
477 auto denominatorNode = elementNode->first_node(
"Denominator");
478 auto dValue = denominatorNode->first_node(
"Value");
481 wxString(dValue->value()).ToCDouble(&value);
482 m_denominator.push_back(value);
483 dValue = dValue->next_sibling(
"Value");
virtual void StartMove(wxPoint2DDouble position)
Update the element attributes related to the movement.
Base class of all elements of the program. This class is responsible for manage graphical and his dat...
void SetPosition(const wxPoint2DDouble position)
Set the element position and update the rectangle.
static wxString StringFromDouble(double value, int minDecimal=1, int maxDecimals=13)
Convert a double value to string.
Class to draw text on Graphics Context using wxWidgets.
virtual GCText * GetCopy()
Get a deep text copy.
virtual void Draw(wxPoint2DDouble position, wxGraphicsContext *gc, double angle=0.0, wxColour colour= *wxBLACK) const
Draw the text in wxGraphicsContext.
virtual void SetText(wxString text)
Set correctly a new text string.
Node of a control element. This class manages the user interaction with the connection and control el...
Calculates the time response by a frequency domain transfer function.
virtual void DrawDC(wxPoint2DDouble translation, double scale, wxGraphicsContext *gc) const
Draw the element using GDI+.
virtual Element * GetCopy()
Get a the element copy.
virtual bool UpdateText()
Update the OpenGL text in the element (if present).
virtual void CalculateSpaceState(int maxIteration=100, double error=1e-3)
Convert the transfer function to space state on controllable canonical form (CCF).
virtual bool ShowForm(wxWindow *parent, Element *element, wxWindow *workspace=nullptr)
Show element data form.
virtual void Rotate(bool clockwise=true)
Rotate the element.
virtual bool Solve(double *input, double timeStep)
Calculates the time response by the space state form of transfer function.