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];
424 copy->m_gcTextNum = m_gcTextNum->
GetCopy();
425 copy->m_gcTextDen = m_gcTextDen->
GetCopy();
437rapidxml::xml_node<>* TransferFunction::SaveElement(rapidxml::xml_document<>& doc,
438 rapidxml::xml_node<>* elementListNode)
440 auto elementNode = XMLParser::AppendNode(doc, elementListNode,
"TransferFunction");
441 XMLParser::SetNodeAttribute(doc, elementNode,
"ID", m_elementID);
443 SaveCADProperties(doc, elementNode);
444 SaveControlNodes(doc, elementNode);
447 auto numeratorNode = XMLParser::AppendNode(doc, elementNode,
"Numerator");
448 for (
unsigned int i = 0; i < m_numerator.size(); ++i) {
449 auto value = XMLParser::AppendNode(doc, numeratorNode,
"Value");
450 XMLParser::SetNodeValue(doc, value, m_numerator[i]);
452 auto denominatorNode = XMLParser::AppendNode(doc, elementNode,
"Denominator");
453 for (
unsigned int i = 0; i < m_denominator.size(); ++i) {
454 auto value = XMLParser::AppendNode(doc, denominatorNode,
"Value");
455 XMLParser::SetNodeValue(doc, value, m_denominator[i]);
461bool TransferFunction::OpenElement(rapidxml::xml_node<>* elementNode)
463 if (!OpenCADProperties(elementNode))
return false;
464 if (!OpenControlNodes(elementNode))
return false;
467 std::vector<double> numerator, denominator;
469 m_denominator.clear();
470 auto numeratorNode = elementNode->first_node(
"Numerator");
471 auto nValue = numeratorNode->first_node(
"Value");
474 wxString(nValue->value()).ToCDouble(&value);
475 m_numerator.push_back(value);
476 nValue = nValue->next_sibling(
"Value");
478 auto denominatorNode = elementNode->first_node(
"Denominator");
479 auto dValue = denominatorNode->first_node(
"Value");
482 wxString(dValue->value()).ToCDouble(&value);
483 m_denominator.push_back(value);
484 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 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.
virtual bool ShowForm(wxWindow *parent, Element *element)
Show element data form.