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;
64 gc->SetPen(*wxTRANSPARENT_PEN);
65 gc->SetBrush(wxBrush(guiColour->selection));
66 double borderSize = (m_borderSize * 2.0 + 1.0) / scale;
67 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);
69 gc->SetPen(wxPen(guiColour->enabled));
70 gc->SetBrush(wxBrush(guiColour->background));
71 gc->DrawRectangle(m_position.m_x - m_width / 2, m_position.m_y - m_height / 2, m_width, m_height);
73 wxPoint2DDouble linePts[2];
74 linePts[0] = wxPoint2DDouble(m_position.m_x - m_width / 2 + 5 + m_borderSize, m_position.m_y);
75 linePts[1] = wxPoint2DDouble(m_position.m_x + m_width / 2 - 5 - m_borderSize, m_position.m_y);
76 gc->StrokeLines(2, linePts);
78 gc->SetPen(*wxTRANSPARENT_PEN);
79 gc->SetBrush(wxBrush(guiColour->enabled));
83 m_gcTextNum->
Draw(m_position + wxPoint2DDouble(-m_gcTextNum->GetWidth() / 2, -m_height / 4 - m_gcTextNum->GetHeight() / 2), gc,0.0, guiColour->text);
84 m_gcTextDen->
Draw(m_position + wxPoint2DDouble(-m_gcTextDen->GetWidth() / 2, m_height / 4 - m_gcTextDen->GetHeight() / 2), gc, 0.0, guiColour->text);
87void TransferFunction::SetText(wxString numerator, wxString denominator)
90 m_gcTextNum->
SetText(numerator);
92 m_gcTextNum =
new GCText(numerator);
95 m_gcTextDen->
SetText(denominator);
97 m_gcTextDen =
new GCText(denominator);
99 double nWidth =
static_cast<double>(m_gcTextNum->GetWidth()) + 5 + m_borderSize;
100 double dWidth =
static_cast<double>(m_gcTextDen->GetWidth()) + 5 + m_borderSize;
102 m_width = nWidth > dWidth ? nWidth : dWidth;
103 m_height =
static_cast<double>(m_gcTextNum->GetHeight()) +
static_cast<double>(m_gcTextDen->GetHeight()) + 2 * m_borderSize;
107wxString TransferFunction::GetSuperscriptNumber(
int number)
109 wxString strNumber = wxString::Format(
"%d", number);
110 wxString superscriptStr =
"";
111 for (
int i = 0; i < (int)strNumber.length(); ++i) {
112 wxString digitStr = strNumber[i];
114 digitStr.ToLong(&digit);
115 superscriptStr += wxString(m_supNumber[digit]);
117 return superscriptStr;
120void TransferFunction::GetTFString(wxString& numerator, wxString& denominator)
124 int index =
static_cast<int>(m_numerator.size()) - 1;
125 for (
auto it = m_numerator.begin(), itEnd = m_numerator.end(); it != itEnd; ++it) {
129 if (index ==
static_cast<int>(m_numerator.size()) - 1) {
146 else if (index == 1) {
148 numerator += signal +
"s";
156 numerator += signal +
"s" + GetSuperscriptNumber(index);
159 numerator += signal +
StringFromDouble(std::abs(value), 0) +
"s" + GetSuperscriptNumber(index);
167 index =
static_cast<int>(m_denominator.size()) - 1;
168 for (
auto it = m_denominator.begin(), itEnd = m_denominator.end(); it != itEnd; ++it) {
172 if (index ==
static_cast<int>(m_denominator.size()) - 1) {
189 else if (index == 1) {
191 denominator += signal +
"s";
199 denominator += signal +
"s" + GetSuperscriptNumber(index);
202 denominator += signal +
StringFromDouble(std::abs(value), 0) +
"s" + GetSuperscriptNumber(index);
211void TransferFunction::UpdateTFText()
214 GetTFString(num, den);
216 if (m_nodeList.size() == 2) {
217 if (m_angle == 0.0) {
218 m_nodeList[0]->SetPosition(m_position + wxPoint2DDouble(-m_width / 2, 0));
219 m_nodeList[1]->SetPosition(m_position + wxPoint2DDouble(m_width / 2, 0));
221 else if (m_angle == 90.0) {
222 m_nodeList[0]->SetPosition(m_position + wxPoint2DDouble(0, -m_height / 2));
223 m_nodeList[1]->SetPosition(m_position + wxPoint2DDouble(0, m_height / 2));
225 else if (m_angle == 180.0) {
226 m_nodeList[0]->SetPosition(m_position + wxPoint2DDouble(m_width / 2, 0));
227 m_nodeList[1]->SetPosition(m_position + wxPoint2DDouble(-m_width / 2, 0));
229 else if (m_angle == 270.0) {
230 m_nodeList[0]->SetPosition(m_position + wxPoint2DDouble(0, m_height / 2));
231 m_nodeList[1]->SetPosition(m_position + wxPoint2DDouble(0, -m_height / 2));
239 tfForm.CenterOnParent();
240 if (tfForm.ShowModal() == wxID_OK) {
252 if (m_angle >= 360.0)
254 else if (m_angle < 0)
257 if (m_angle == 0.0) {
258 m_nodeList[0]->SetPosition(m_position + wxPoint2DDouble(-m_width / 2, 0));
259 m_nodeList[1]->SetPosition(m_position + wxPoint2DDouble(m_width / 2, 0));
261 else if (m_angle == 90.0) {
262 m_nodeList[0]->SetPosition(m_position + wxPoint2DDouble(0, -m_height / 2));
263 m_nodeList[1]->SetPosition(m_position + wxPoint2DDouble(0, m_height / 2));
265 else if (m_angle == 180.0) {
266 m_nodeList[0]->SetPosition(m_position + wxPoint2DDouble(m_width / 2, 0));
267 m_nodeList[1]->SetPosition(m_position + wxPoint2DDouble(-m_width / 2, 0));
269 else if (m_angle == 270.0) {
270 m_nodeList[0]->SetPosition(m_position + wxPoint2DDouble(0, m_height / 2));
271 m_nodeList[1]->SetPosition(m_position + wxPoint2DDouble(0, -m_height / 2));
274 for (
auto it = m_nodeList.begin(), itEnd = m_nodeList.end(); it != itEnd; ++it) {
276 node->Rotate(clockwise);
280void TransferFunction::SetFont(wxFont& font)
282 if (m_gcTextNum) m_gcTextNum->SetFont(font);
283 if (m_gcTextDen) m_gcTextDen->SetFont(font);
290 m_maxIteration = maxIteration;
293 int order =
static_cast<int>(m_denominator.size());
294 std::vector<double> denominator = m_denominator;
295 std::vector<double> numerator;
299 for (
int i = 0; i < order; i++) {
300 int numIndex = i - (order -
static_cast<int>(m_numerator.size()));
302 numerator.push_back(0.0);
304 numerator.push_back(m_numerator[numIndex]);
309 for (
int i = 0; i < (order - 1); i++) {
310 std::vector<double> lineA;
311 for (
int j = 0; j < (order - 1); j++) {
313 lineA.push_back(1.0);
315 lineA.push_back(0.0);
317 ss.A.push_back(lineA);
321 for (
int i = 0; i < order - 1; i++) {
322 ss.A[order - 2][i] = -(denominator[order - 1 - i] / denominator[0]);
323 ss.C[i] = numerator[order - 1 - i] / denominator[0] -
324 (denominator[order - 1 - i] / denominator[0]) * (numerator[0] / denominator[0]);
326 ss.B[order - 2] = 1.0;
327 ss.D = numerator[0] / denominator[0];
335 for (
unsigned int i = 0; i < m_denominator.size(); ++i) {
348 int order =
static_cast<int>(m_ss.A.size());
350 std::vector<double> x;
351 std::vector<double> oldx;
352 std::vector<double> dx;
353 std::vector<double> olddx;
354 for (
int i = 0; i < order; i++) {
356 oldx.push_back(m_x[i]);
358 dx.push_back(m_dx[i]);
359 olddx.push_back(m_dx[i]);
366 double dxError = 0.0;
367 for (
int i = 0; i < order; i++) {
369 x[i] = m_x[i] + 0.5 * timeStep * (m_dx[i] + dx[i]);
371 if (std::abs(x[i] - oldx[i]) > xError) xError = std::abs(x[i] - oldx[i]);
375 for (
int i = 0; i < order; i++) {
378 for (
int j = 0; j < order; j++) dx[i] += m_ss.A[i][j] * x[j];
379 dx[i] += m_ss.B[i] * input[0];
381 if (std::abs(dx[i] - olddx[i]) > dxError) dxError = std::abs(dx[i] - olddx[i]);
385 if (std::max(xError, dxError) < m_error) exit =
true;
388 if (iter >= m_maxIteration)
return false;
392 for (
int i = 0; i < order; i++) {
393 m_output += m_ss.C[i] * x[i];
398 m_output += m_ss.D * input[0];
406 copy->m_gcTextNum = m_gcTextNum ? m_gcTextNum->
GetCopy() :
nullptr;
407 copy->m_gcTextDen = m_gcTextDen ? m_gcTextDen->
GetCopy() :
nullptr;
419rapidxml::xml_node<>* TransferFunction::SaveElement(rapidxml::xml_document<>& doc,
420 rapidxml::xml_node<>* elementListNode)
422 auto elementNode = XMLParser::AppendNode(doc, elementListNode,
"TransferFunction");
423 XMLParser::SetNodeAttribute(doc, elementNode,
"ID", m_elementID);
425 SaveCADProperties(doc, elementNode);
426 SaveControlNodes(doc, elementNode);
429 auto numeratorNode = XMLParser::AppendNode(doc, elementNode,
"Numerator");
430 for (
unsigned int i = 0; i < m_numerator.size(); ++i) {
431 auto value = XMLParser::AppendNode(doc, numeratorNode,
"Value");
432 XMLParser::SetNodeValue(doc, value, m_numerator[i]);
434 auto denominatorNode = XMLParser::AppendNode(doc, elementNode,
"Denominator");
435 for (
unsigned int i = 0; i < m_denominator.size(); ++i) {
436 auto value = XMLParser::AppendNode(doc, denominatorNode,
"Value");
437 XMLParser::SetNodeValue(doc, value, m_denominator[i]);
443bool TransferFunction::OpenElement(rapidxml::xml_node<>* elementNode)
445 if (!OpenCADProperties(elementNode))
return false;
446 if (!OpenControlNodes(elementNode))
return false;
449 std::vector<double> numerator, denominator;
451 m_denominator.clear();
452 auto numeratorNode = elementNode->first_node(
"Numerator");
453 auto nValue = numeratorNode->first_node(
"Value");
456 wxString(nValue->value()).ToCDouble(&value);
457 m_numerator.push_back(value);
458 nValue = nValue->next_sibling(
"Value");
460 auto denominatorNode = elementNode->first_node(
"Denominator");
461 auto dValue = denominatorNode->first_node(
"Value");
464 wxString(dValue->value()).ToCDouble(&value);
465 m_denominator.push_back(value);
466 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 Element * GetCopy()
Get a the element copy.
virtual void DrawDC(GUIColour *guiColour, wxPoint2DDouble translation, double scale, wxGraphicsContext *gc) const
Draw the element using GDI+.
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.