1 /*
2 KmPlot - a math. function plotter for the KDE-Desktop
3
4 SPDX-FileCopyrightText: 1998, 1999, 2000, 2002 Klaus-Dieter Möller <kd.moeller@t-online.de>
5 SPDX-FileCopyrightText: 2006 David Saxton <david@bluehaze.org>
6
7 This file is part of the KDE Project.
8 KmPlot is part of the KDE-EDU Project.
9
10 SPDX-License-Identifier: GPL-2.0-or-later
11
12 */
13
14 #include "function.h"
15 #include "ksliderwindow.h"
16 #include "settings.h"
17 #include "view.h"
18 #include "xparser.h"
19
20 #include <QImage>
21 #include <QLinearGradient>
22 #include <QPainter>
23
24 #include <assert.h>
25 #include <cmath>
26
27 int MAX_PM = 4;
28
29
30 //BEGIN class Value
Value(const QString & expression)31 Value::Value( const QString & expression )
32 {
33 m_value = 0.0;
34 if ( expression.isEmpty() )
35 m_expression = '0';
36 else
37 updateExpression( expression );
38 }
39
40
Value(double value)41 Value::Value( double value )
42 {
43 updateExpression( value );
44 }
45
46
updateExpression(const QString & expression)47 bool Value::updateExpression( const QString & expression )
48 {
49 Parser::Error error;
50 double newValue = XParser::self()->eval( expression, & error );
51 if ( error != Parser::ParseSuccess )
52 return false;
53
54 m_value = newValue;
55 m_expression = expression;
56 return true;
57 }
58
59
updateExpression(double value)60 void Value::updateExpression( double value )
61 {
62 m_value = value;
63 m_expression = Parser::number( value );
64 }
65
66
operator ==(const Value & other) const67 bool Value::operator == ( const Value & other ) const
68 {
69 return m_expression == other.expression();
70 }
71 //END class Value
72
73
74
75 //BEGIN class PlotAppearance
PlotAppearance()76 PlotAppearance::PlotAppearance( )
77 {
78 lineWidth = 0.3;
79 color = Qt::black;
80 useGradient = false;
81 visible = false;
82 style = Qt::SolidLine;
83 showExtrema = false;
84 showTangentField = false;
85 showPlotName = false;
86 }
87
88
operator !=(const PlotAppearance & other) const89 bool PlotAppearance::operator !=( const PlotAppearance & other ) const
90 {
91 return
92 (lineWidth != other.lineWidth) ||
93 (color != other.color) ||
94 (useGradient != other.useGradient) ||
95 (gradient.stops() != other.gradient.stops()) ||
96 (visible != other.visible) ||
97 (style != other.style) ||
98 (showExtrema != other.showExtrema) ||
99 (showTangentField != other.showTangentField) ||
100 (showPlotName != other.showPlotName);
101 }
102
103
penStyleToString(Qt::PenStyle style)104 QString PlotAppearance::penStyleToString( Qt::PenStyle style )
105 {
106 switch ( style )
107 {
108 case Qt::NoPen:
109 return "NoPen";
110
111 case Qt::SolidLine:
112 return "SolidLine";
113
114 case Qt::DashLine:
115 return "DashLine";
116
117 case Qt::DotLine:
118 return "DotLine";
119
120 case Qt::DashDotLine:
121 return "DashDotLine";
122
123 case Qt::DashDotDotLine:
124 return "DashDotDotLine";
125
126 case Qt::MPenStyle:
127 case Qt::CustomDashLine:
128 qWarning() << "Unsupported pen style\n";
129 break;
130 }
131
132 qWarning() << "Unknown style " << style ;
133 return "SolidLine";
134 }
135
136
stringToPenStyle(const QString & style)137 Qt::PenStyle PlotAppearance::stringToPenStyle( const QString & style )
138 {
139 if ( style == "NoPen" )
140 return Qt::NoPen;
141
142 if ( style == "SolidLine" )
143 return Qt::SolidLine;
144
145 if ( style == "DashLine" )
146 return Qt::DashLine;
147
148 if ( style == "DotLine" )
149 return Qt::DotLine;
150
151 if ( style == "DashDotLine" )
152 return Qt::DashDotLine;
153
154 if ( style == "DashDotDotLine" )
155 return Qt::DashDotDotLine;
156
157 qWarning() << "Unknown style " << style ;
158 return Qt::SolidLine;
159 }
160 //END class PlotAppearance
161
162
163
164 //BEGIN class DifferentialState
DifferentialState()165 DifferentialState::DifferentialState()
166 {
167 x = 0;
168 }
169
170
DifferentialState(int order)171 DifferentialState::DifferentialState( int order )
172 {
173 x = 0;
174 setOrder( order );
175 }
176
177
setOrder(int order)178 void DifferentialState::setOrder( int order )
179 {
180 bool orderWasZero = (y0.size() == 0);
181
182 y.resize( order );
183 y0.resize( order );
184
185 if ( orderWasZero && order >= 1 )
186 y0[0].updateExpression( "1" );
187
188 resetToInitial();
189 }
190
191
setStep(const Value & step)192 bool DifferentialStates::setStep( const Value & step )
193 {
194 if ( step.value() <= 0 )
195 return false;
196
197 m_step = step;
198 return true;
199 }
200
201
resetToInitial()202 void DifferentialState::resetToInitial()
203 {
204 x = x0.value();
205 y = y0;
206 }
207
208
operator ==(const DifferentialState & other) const209 bool DifferentialState::operator == ( const DifferentialState & other ) const
210 {
211 return (x0 == other.x0) && (x == other.x) && (y0 == other.y0) && (y == other.y);
212 }
213 //END class DifferentialState
214
215
216
217 //BEGIN class DifferentialStates
DifferentialStates()218 DifferentialStates::DifferentialStates()
219 {
220 m_uniqueState = false;
221 m_order = 0;
222 m_step.updateExpression( 0.05 );
223 }
224
225
setOrder(int order)226 void DifferentialStates::setOrder( int order )
227 {
228 m_order = order;
229 for ( int i = 0; i < m_data.size(); ++i )
230 m_data[i].setOrder( order );
231 }
232
233
add()234 DifferentialState * DifferentialStates::add()
235 {
236 if ( !m_uniqueState || m_data.isEmpty() )
237 m_data << DifferentialState( order() );
238 else
239 qDebug() << "Unable to add another state!\n";
240
241 return & m_data[ size() - 1 ];
242 }
243
244
setUniqueState(bool unique)245 void DifferentialStates::setUniqueState( bool unique )
246 {
247 m_uniqueState = unique;
248 if ( m_uniqueState && m_data.size() > 1 )
249 {
250 // Remove any states other than the first
251 m_data.resize( 1 );
252 }
253 }
254
255
resetToInitial()256 void DifferentialStates::resetToInitial( )
257 {
258 for ( int i = 0; i < m_data.size(); ++i )
259 m_data[i].resetToInitial();
260 }
261 //END class DifferentialStates
262
263
264
265 //BEGIN class Equation
Equation(Type type,Function * parent)266 Equation::Equation( Type type, Function * parent )
267 : m_type( type ),
268 m_parent( parent )
269 {
270 m_usesParameter = false;
271 mptr = 0;
272
273 if ( type == Differential || type == Cartesian )
274 {
275 differentialStates.setUniqueState( type == Cartesian );
276 differentialStates.setOrder( order() );
277 differentialStates.add();
278 }
279 }
280
281
~Equation()282 Equation::~ Equation()
283 {
284 }
285
286
order() const287 int Equation::order( ) const
288 {
289 if ( type() == Cartesian )
290 {
291 // For drawing integrals
292 return 1;
293 }
294 else
295 return name(false).count( '\'' );
296 }
297
298
pmCount() const299 int Equation::pmCount() const
300 {
301 return m_fstr.count( PmSymbol );
302 }
303
304
name(bool removePrimes) const305 QString Equation::name( bool removePrimes ) const
306 {
307 if ( m_fstr.isEmpty() )
308 return QString();
309
310 int open = m_fstr.indexOf( '(' );
311 int equals = m_fstr.indexOf( '=' );
312
313 if ( (equals == -1) && (open == -1) )
314 return QString();
315
316 int pos;
317 if ( ((equals > open) && (open != -1)) || (equals == -1) )
318 pos = open;
319 else
320 pos = equals;
321
322 QString n = m_fstr.left( pos ).trimmed();
323
324 if ( removePrimes )
325 n.remove( '\'' );
326
327 return n;
328 }
329
330
looksLikeFunction() const331 bool Equation::looksLikeFunction( ) const
332 {
333 int open = m_fstr.indexOf( '(' );
334 int equals = m_fstr.indexOf( '=' );
335
336 if ( (open != -1) && (open < equals) )
337 return true;
338
339 switch ( type() )
340 {
341 case Cartesian:
342 case Differential:
343 case ParametricY:
344 return (name() != "y");
345
346 case Polar:
347 return (name() != "r");
348
349 case ParametricX:
350 return (name() != "x");
351
352 case Implicit:
353 return false;
354
355 case Constant:
356 return false;
357 }
358
359 return true;
360 }
361
362
updateVariables()363 void Equation::updateVariables()
364 {
365 if ( type() == Constant )
366 {
367 return;
368 }
369
370 m_variables.clear();
371
372 if ( looksLikeFunction() )
373 {
374 int p1 = m_fstr.indexOf( '(' );
375 int p2 = m_fstr.indexOf( ')' );
376
377 const QStringList listSplit = ( (p1 != -1) && (p2 != -1) ) ? m_fstr.mid( p1+1, p2-p1-1 ).split( ',', QString::SkipEmptyParts ) : QStringList();
378
379 // Variables shouldn't contain spaces!
380 for ( QString s : listSplit ) {
381 s = s.remove(' ');
382 if ( !s.isEmpty() )
383 m_variables << s;
384 }
385 }
386 else switch ( type() )
387 {
388 case Cartesian:
389 case Differential:
390 m_variables << "x" << "k";
391 break;
392
393 case Polar:
394 m_variables << QChar( 0x3b8 ) << "k"; // (theta)
395 break;
396
397 case ParametricX:
398 case ParametricY:
399 m_variables << "t" << "k";
400 break;
401
402 case Implicit:
403 m_variables << "x" << "y" << "k";
404 break;
405
406 case Constant:
407 break;
408 }
409
410 // If we are a differential equation, then add on y, y', etc
411 if ( type() == Differential && !name().isEmpty() )
412 {
413 QString n = name();
414
415 int order = this->order();
416 for ( int i = 0; i < order; ++i )
417 {
418 m_variables << n;
419 n += '\'';
420 }
421 }
422
423
424 //BEGIN Update whether we accept a parameter or not
425 int expectedNumVariables = 0;
426
427 switch ( m_type )
428 {
429 case Cartesian:
430 case ParametricX:
431 case ParametricY:
432 case Polar:
433 expectedNumVariables = 1;
434 break;
435
436 case Implicit:
437 expectedNumVariables = 2;
438 break;
439
440 case Differential:
441 expectedNumVariables = order()+1;
442 break;
443
444 case Constant:
445 expectedNumVariables = 0;
446 break;
447 }
448
449 m_usesParameter = (variables().size() > expectedNumVariables);
450 //END Update whether we accept a parameter or not
451 }
452
453
parameterName() const454 QString Equation::parameterName( ) const
455 {
456 if ( !usesParameter() )
457 return QString();
458
459 int parAt = (type() == Implicit) ? 2 : 1;
460 return variables()[parAt];
461 }
462
463
setFstr(const QString & fstr,int * error,int * errorPosition,bool force)464 bool Equation::setFstr( const QString & fstr, int * error, int * errorPosition, bool force )
465 {
466 #define HANDLE_ERROR \
467 if ( !force ) \
468 { \
469 m_fstr = prevFstr; \
470 updateVariables(); \
471 } \
472 else \
473 { \
474 qDebug() << "fstr "<<fstr<<" invalid, but forcing anyway: " << Parser::errorString( Parser::Error(*error) ) << " at position " << *errorPosition; \
475 mem.clear(); \
476 }
477
478 int temp1, temp2;
479 if ( !error )
480 error = & temp1;
481 if ( !errorPosition )
482 errorPosition = & temp2;
483
484 *error = Parser::ParseSuccess;
485 *errorPosition = -1;
486
487 QString prevFstr = m_fstr;
488 m_fstr = fstr;
489 updateVariables();
490
491 if ( !fstr.contains('=') || QString(fstr).right( fstr.length() - fstr.indexOf('=') - 1).simplified().isEmpty() )
492 {
493 *error = Parser::SyntaxError;
494 HANDLE_ERROR;
495 return false;
496 }
497
498 // require order to be greater than 0 for differential equations
499 if ( (type() == Differential) && (order() < 1) )
500 {
501 *error = Parser::ZeroOrder;
502 HANDLE_ERROR;
503 /// \todo indicate the position of the error
504 return false;
505 }
506
507 int maxArg = order() + (( type() == Implicit ) ? 3 : 2);
508 if ( variables().size() > maxArg )
509 {
510 *error = Parser::TooManyArguments;
511 HANDLE_ERROR;
512 /// \todo indicate the position of the invalid argument?
513 return false;
514 }
515
516 XParser::self()->initEquation( this, (Parser::Error*)error, errorPosition );
517 if ( *error != Parser::ParseSuccess )
518 {
519 HANDLE_ERROR;
520 if ( !force )
521 XParser::self()->initEquation( this );
522 return false;
523 }
524
525 differentialStates.setOrder( order() );
526 return true;
527 }
528
529
setPMSignature(QVector<bool> pmSignature)530 void Equation::setPMSignature( QVector< bool > pmSignature )
531 {
532 differentialStates.resetToInitial();
533 m_pmSignature = pmSignature;
534 }
535
536
operator !=(const Equation & other)537 bool Equation::operator !=( const Equation & other )
538 {
539 return (fstr() != other.fstr()) ||
540 (differentialStates != other.differentialStates);
541 }
542
543
operator =(const Equation & other)544 Equation & Equation::operator =( const Equation & other )
545 {
546 setFstr( other.fstr() );
547 differentialStates = other.differentialStates;
548
549 return * this;
550 }
551 //END class Equation
552
553
554
555 //BEGIN class Function
Function(Type type)556 Function::Function( Type type )
557 : m_type( type )
558 {
559 x = y = 0;
560 m_implicitMode = UnfixedXY;
561
562 usecustomxmin = false;
563 usecustomxmax = false;
564
565 dmin.updateExpression( QChar('0') );
566 if ( Settings::anglemode() == Parser::Radians )
567 dmax.updateExpression( QString(QChar('2')) + PiSymbol );
568 else
569 dmax.updateExpression( "360" );
570
571 switch ( m_type )
572 {
573 case Cartesian:
574 eq << new Equation( Equation::Cartesian, this );
575 break;
576
577 case Polar:
578 eq << new Equation( Equation::Polar, this );
579 usecustomxmin = true;
580 usecustomxmax = true;
581 break;
582
583 case Parametric:
584 eq << new Equation( Equation::ParametricX, this );
585 eq << new Equation( Equation::ParametricY, this );
586 usecustomxmin = true;
587 usecustomxmax = true;
588 break;
589
590 case Implicit:
591 eq << new Equation( Equation::Implicit, this );
592 break;
593
594 case Differential:
595 eq << new Equation( Equation::Differential, this );
596 break;
597 }
598
599 m_id = 0;
600 f0.visible = true;
601
602 k = 0;
603 }
604
605
~Function()606 Function::~Function()
607 {
608 for ( Equation * e : qAsConst(eq) )
609 delete e;
610 }
611
612
copyFrom(const Function & function)613 bool Function::copyFrom( const Function & function )
614 {
615 bool changed = false;
616 int i = 0;
617 #define COPY_AND_CHECK(s) \
618 { \
619 if ( s != function.s ) \
620 { \
621 s = function.s; \
622 changed = true; \
623 } \
624 } \
625 i++;
626
627 COPY_AND_CHECK( f0 ); // 0
628 if ( type() == Cartesian )
629 {
630 COPY_AND_CHECK( f1 ); // 1
631 COPY_AND_CHECK( f2 ); // 2
632 COPY_AND_CHECK( f3 ); // 3
633 COPY_AND_CHECK( integral ); // 4
634 }
635 COPY_AND_CHECK( dmin ); // 5,1
636 COPY_AND_CHECK( dmax ); // 6,2
637 COPY_AND_CHECK( usecustomxmin ); // 7,3
638 COPY_AND_CHECK( usecustomxmax ); // 8,4
639 COPY_AND_CHECK( m_parameters ); // 9,5
640
641 // handle equations separately
642 for ( int i = 0; i < eq.size(); ++i )
643 {
644 if ( *eq[i] != *function.eq[i] )
645 {
646 changed = true;
647 *eq[i] = *function.eq[i];
648 }
649 }
650
651 return changed;
652 }
653
654
name() const655 QString Function::name() const
656 {
657 QString n = eq[0]->fstr();
658 for ( int i = 1; i < eq.size(); ++i )
659 n += '\n' + eq[i]->fstr();
660
661 return n;
662 }
663
664
plotAppearance(PMode plot)665 PlotAppearance & Function::plotAppearance( PMode plot )
666 {
667 // NOTE: This function is identical to the const one, so changes to this should be applied to both
668
669 switch ( plot )
670 {
671 case Function::Derivative0:
672 return f0;
673 case Function::Derivative1:
674 return f1;
675 case Function::Derivative2:
676 return f2;
677 case Function::Derivative3:
678 return f3;
679 case Function::Integral:
680 return integral;
681 }
682
683 qCritical() << "Unknown plot " << plot;
684 return f0;
685 }
plotAppearance(PMode plot) const686 PlotAppearance Function::plotAppearance( PMode plot ) const
687 {
688 // NOTE: This function is identical to the none-const one, so changes to this should be applied to both
689
690 switch ( plot )
691 {
692 case Function::Derivative0:
693 return f0;
694 case Function::Derivative1:
695 return f1;
696 case Function::Derivative2:
697 return f2;
698 case Function::Derivative3:
699 return f3;
700 case Function::Integral:
701 return integral;
702 }
703
704 qCritical() << "Unknown plot " << plot;
705 return f0;
706 }
707
708
allPlotsAreHidden() const709 bool Function::allPlotsAreHidden( ) const
710 {
711 return !f0.visible && !f1.visible && !f2.visible && !integral.visible;
712 }
713
714
typeToString(Type type)715 QString Function::typeToString( Type type )
716 {
717 switch ( type )
718 {
719 case Cartesian:
720 return "cartesian";
721
722 case Parametric:
723 return "parametric";
724
725 case Polar:
726 return "polar";
727
728 case Implicit:
729 return "implicit";
730
731 case Differential:
732 return "differential";
733 }
734
735 qWarning() << "Unknown type " << type ;
736 return "unknown";
737 }
738
739
stringToType(const QString & type)740 Function::Type Function::stringToType( const QString & type )
741 {
742 if ( type == "cartesian" )
743 return Cartesian;
744
745 if ( type == "parametric" )
746 return Parametric;
747
748 if ( type == "polar" )
749 return Polar;
750
751 if ( type == "implicit" )
752 return Implicit;
753
754 if ( type == "differential" )
755 return Differential;
756
757 qWarning() << "Unknown type " << type ;
758 return Cartesian;
759 }
760
761
plots(PlotCombinations combinations) const762 QList< Plot > Function::plots( PlotCombinations combinations ) const
763 {
764 QList< Plot > list;
765
766 if ( allPlotsAreHidden() )
767 return list;
768
769 Plot plot;
770 plot.setFunctionID( id() );
771 plot.plotNumberCount = m_parameters.useList ? m_parameters.list.size() + (m_parameters.useSlider?1:0) : 1;
772
773 bool singlePlot = (!m_parameters.useList && !m_parameters.useSlider) ||
774 m_parameters.animating ||
775 (~combinations & DifferentParameters) ||
776 (!m_parameters.useSlider && m_parameters.useList && m_parameters.list.isEmpty());
777
778 if ( singlePlot )
779 {
780 if ( m_parameters.animating )
781 plot.parameter = Parameter( Parameter::Animated );
782
783 list << plot;
784 }
785 else
786 {
787 int i = 0;
788
789 if ( m_parameters.useSlider )
790 {
791 Parameter param( Parameter::Slider );
792 param.setSliderID( m_parameters.sliderID );
793 plot.parameter = param;
794 plot.plotNumber = i++;
795 list << plot;
796 }
797
798 if ( m_parameters.useList )
799 {
800 const int listsize = m_parameters.list.size();
801 for ( int pos = 0; pos < listsize; ++pos )
802 {
803 Parameter param( Parameter::List );
804 param.setListPos( pos );
805 plot.parameter = param;
806 plot.plotNumber = i++;
807 list << plot;
808 }
809 }
810 }
811
812
813 // Copy each plot in the list for other variations
814 if ( (type() == Cartesian) && (combinations & DifferentDerivatives) )
815 {
816 QList< Plot > duplicated;
817
818 for ( PMode p = Derivative0; p <= Integral; p = PMode(p+1) )
819 {
820 for ( Plot plot : qAsConst(list) ) {
821 if ( !plotAppearance(p).visible )
822 continue;
823 plot.plotMode = p;
824 duplicated << plot;
825 }
826 }
827
828 list = duplicated;
829 }
830
831 if ( (type() == Differential) && (combinations & DifferentInitialStates) )
832 {
833 QList< Plot > duplicated;
834
835 for ( int i = 0; i < eq[0]->differentialStates.size(); ++i )
836 {
837 for ( Plot plot : qAsConst(list) ) {
838 plot.stateNumber = i;
839 duplicated << plot;
840 }
841 }
842
843 list = duplicated;
844 }
845
846 if ( combinations & DifferentPMSignatures )
847 {
848 int size = 0;
849 for ( Equation * equation : qAsConst(eq) )
850 size += equation->pmCount();
851
852 unsigned max = unsigned( std::pow( 2.0, (double)size ) );
853 QVector< QVector<bool> > signatures( max );
854
855 for ( unsigned i = 0; i < max; ++i )
856 {
857 QVector<bool> sig( size );
858
859 for ( int j = 0; j < size; ++j )
860 sig[ j ] = i & (1<<j);
861
862 signatures[i] = sig;
863 }
864
865 // Generate a plot for each signature in signatures
866 QList< Plot > duplicated;
867 for ( const QVector<bool> &signature : qAsConst(signatures) )
868 {
869 int at = 0;
870 QList< QVector<bool> > pmSignature;
871
872 for ( Equation * equation : qAsConst(eq) )
873 {
874 int pmCount = equation->pmCount();
875 QVector<bool> sig( pmCount );
876 for ( int i = 0; i < pmCount; ++i )
877 sig[i] = signature[ i + at];
878 at += pmCount;
879
880 pmSignature << sig;
881 }
882
883 for ( Plot plot : qAsConst(list) ) {
884 plot.pmSignature = pmSignature;
885 duplicated << plot;
886 }
887 }
888 list = duplicated;
889 }
890
891 return list;
892 }
893
894
addFunctionDependency(Function * function)895 void Function::addFunctionDependency( Function * function )
896 {
897 if ( !function || m_dependencies.contains( function->id() ) )
898 return;
899
900 Q_ASSERT_X( !function->dependsOn( this ), "addFunctionDependency", "circular dependency" );
901
902 m_dependencies << function->id();
903 }
904
905
dependsOn(Function * function) const906 bool Function::dependsOn( Function * function ) const
907 {
908 if ( !function )
909 return false;
910
911 if ( m_dependencies.contains( function->id() ) )
912 return true;
913
914 for ( int functionId : qAsConst(m_dependencies) )
915 {
916 Function * f = XParser::self()->functionWithID( functionId );
917
918 if ( f->dependsOn( function ) )
919 return true;
920 }
921
922 return false;
923 }
924 //END class Function
925
926
927
928 //BEGIN class ParameterSettings
ParameterSettings()929 ParameterSettings::ParameterSettings()
930 {
931 animating = false;
932 useSlider = false;
933 sliderID = 0;
934 useList = false;
935 }
936
937
operator ==(const ParameterSettings & other) const938 bool ParameterSettings::operator == ( const ParameterSettings & other ) const
939 {
940 return ( useSlider == other.useSlider ) &&
941 ( sliderID == other.sliderID ) &&
942 ( useList == other.useList ) &&
943 ( list == other.list );
944 }
945 //END class ParameterSettings
946
947
948
949 //BEGIN class Parameter
Parameter(Type type)950 Parameter::Parameter( Type type )
951 : m_type( type )
952 {
953 m_sliderID = -1;
954 m_listPos = -1;
955 }
956
957
operator ==(const Parameter & other) const958 bool Parameter::operator == ( const Parameter & other ) const
959 {
960 return ( type() == other.type() ) &&
961 ( listPos() == other.listPos() ) &&
962 ( sliderID() == other.sliderID() );
963 }
964 //END class Parameter
965
966
967
968 //BEGIN class Plot
Plot()969 Plot::Plot( )
970 {
971 stateNumber = -1;
972 plotNumberCount = 1;
973 plotNumber = 0;
974 m_function = 0;
975 m_functionID = -1;
976 plotMode = Function::Derivative0;
977 }
978
979
operator ==(const Plot & other) const980 bool Plot::operator ==( const Plot & other ) const
981 {
982 return ( m_functionID == other.functionID() ) &&
983 ( plotMode == other.plotMode ) &&
984 ( parameter == other.parameter ) &&
985 ( stateNumber == other.stateNumber );
986 }
987
988
setFunctionID(int id)989 void Plot::setFunctionID( int id )
990 {
991 m_functionID = id;
992 updateCached();
993 }
994
995
updateCached()996 void Plot::updateCached()
997 {
998 m_function = XParser::self()->functionWithID( m_functionID );
999 }
1000
1001
name() const1002 QString Plot::name( ) const
1003 {
1004 if ( !m_function )
1005 return QString();
1006
1007 QString n = m_function->name();
1008
1009 if ( m_function->eq[0]->usesParameter() )
1010 n += QString( "\n%1 = %2" ).arg( m_function->eq[0]->parameterName() ).arg( Parser::number( parameterValue() ) );
1011
1012 if ( plotMode == Function::Derivative1 )
1013 n = n.section('=', 0, 0).replace('(', "\'(");
1014
1015 if ( plotMode == Function::Derivative2 )
1016 n = n.section('=', 0, 0).replace('(', "\'\'(");
1017
1018 if ( plotMode == Function::Integral )
1019 {
1020 QString functionName = n.section('=', 0, 0);
1021 n = QChar(0x222B) + ' ' + functionName + 'd' + functionName.section('(', 1, 1).remove(')').section(',', 0, 0);
1022 }
1023
1024 return n;
1025 }
1026
1027
updateFunction() const1028 void Plot::updateFunction() const
1029 {
1030 if ( !m_function )
1031 return;
1032
1033 // Update the plus-minus signature
1034 assert( pmSignature.size() <= m_function->eq.size() );
1035 for ( int i = 0; i < pmSignature.size(); ++i )
1036 m_function->eq[i]->setPMSignature( pmSignature[i] );
1037
1038 if ( parameter.type() != Parameter::Animated )
1039 m_function->setParameter( parameterValue() );
1040 }
1041
1042
parameterValue() const1043 double Plot::parameterValue() const
1044 {
1045 switch ( parameter.type() )
1046 {
1047 case Parameter::Unknown:
1048 return 0;
1049
1050 case Parameter::Slider:
1051 {
1052 KSliderWindow * sw = View::self()->m_sliderWindow;
1053
1054 if ( !sw )
1055 {
1056 // Slider window isn't open. Ask View to open it
1057 View::self()->updateSliders();
1058
1059 // It should now be open
1060 sw = View::self()->m_sliderWindow;
1061 assert( sw );
1062 }
1063
1064 return sw->value( parameter.sliderID() );
1065 }
1066
1067 case Parameter::List:
1068 {
1069 if ( (parameter.listPos() >= 0) && (parameter.listPos() < m_function->m_parameters.list.size()) )
1070 return m_function->m_parameters.list[ parameter.listPos() ].value();
1071 return 0;
1072 }
1073
1074 case Parameter::Animated:
1075 {
1076 qWarning() << "Shouldn't use this function for animated parameter!\n";
1077 return 0;
1078 }
1079 }
1080
1081 return 0;
1082 }
1083
1084
differentiate()1085 void Plot::differentiate()
1086 {
1087 switch ( plotMode )
1088 {
1089 case Function::Integral:
1090 plotMode = Function::Derivative0;
1091 break;
1092
1093 case Function::Derivative0:
1094 plotMode = Function::Derivative1;
1095 break;
1096
1097 case Function::Derivative1:
1098 plotMode = Function::Derivative2;
1099 break;
1100
1101 case Function::Derivative2:
1102 plotMode = Function::Derivative3;
1103 break;
1104
1105 case Function::Derivative3:
1106 qWarning() << "Can't handle this yet!\n";
1107 break;
1108 }
1109 }
1110
1111
integrate()1112 void Plot::integrate()
1113 {
1114 switch ( plotMode )
1115 {
1116 case Function::Integral:
1117 qWarning() << "Can't handle this yet!\n";
1118 break;
1119
1120 case Function::Derivative0:
1121 plotMode = Function::Integral;
1122 break;
1123
1124 case Function::Derivative1:
1125 plotMode = Function::Derivative0;
1126 break;
1127
1128 case Function::Derivative2:
1129 plotMode = Function::Derivative1;
1130 break;
1131
1132 case Function::Derivative3:
1133 plotMode = Function::Derivative2;
1134 break;
1135 }
1136 }
1137
1138
color() const1139 QColor Plot::color( ) const
1140 {
1141 Function * f = function();
1142 assert(f); // Shouldn't call color without a function
1143 PlotAppearance appearance = f->plotAppearance( plotMode );
1144
1145 if ( (plotNumberCount <= 1) || !appearance.useGradient )
1146 return appearance.color;
1147
1148 // Is a gradient
1149
1150 int x = plotNumber;
1151 int l = plotNumberCount;
1152
1153 QLinearGradient lg( 0, 0, l-1, 0 );
1154 lg.setStops( appearance.gradient.stops() );
1155 QImage im( l, 1, QImage::Format_RGB32 );
1156 QPainter p(&im);
1157 p.setPen( QPen( lg, 1 ) );
1158 p.drawLine( 0, 0, l, 0 );
1159 return im.pixel( x, 0 );
1160 }
1161
1162
derivativeNumber() const1163 int Plot::derivativeNumber( ) const
1164 {
1165 switch ( plotMode )
1166 {
1167 case Function::Integral:
1168 return -1;
1169 case Function::Derivative0:
1170 return 0;
1171 case Function::Derivative1:
1172 return 1;
1173 case Function::Derivative2:
1174 return 2;
1175 case Function::Derivative3:
1176 return 3;
1177 }
1178
1179 qWarning() << "Unknown derivative number.\n";
1180 return 0;
1181 }
1182
1183
state() const1184 DifferentialState * Plot::state( ) const
1185 {
1186 if ( !function() || (stateNumber < 0) )
1187 return 0;
1188
1189 if ( function()->eq[0]->differentialStates.size() <= stateNumber )
1190 return 0;
1191
1192 return & function()->eq[0]->differentialStates[stateNumber];
1193 }
1194 //END class Plot
1195
1196