5.3 Developing supporting view component
For creating a text panel of the form of a equation, I have implemented a class called EquationNode
in the EquationNode.js
file. This file is heavily inspired by the EquationNode.js
file for the Least-Squares Regression simulation by PhET Interactive Simulations. The simulation can be found here https://phet.colorado.edu/en/simulations/least-squares-regression
5.3.1 Constructing the equation
For the purpose of this simulation, we need the form of x = c
where c is the value referring to the current slider value. We make this equation in parts like so:
// 'y'
this.yText = new RichText( symbolYString, stringTextOptions );
// the '=' sign
this.equalText = new Text( MathSymbols.EQUAL_TO, stringTextOptions );
// a number
this.valueText = new Text( maxWidthString, numericalTextOptions );
5.3.2 Adding the equation child
Now in order to add this equation to our view, we do the following steps:
const mutableEquationText = new Node( {
children: [
this.yText,
this.equalText,
this.valueText
];
} )
// layout of the entire equation
this.yText.left = 0;
this.equalText.left = this.yText.right + 3;
this.valueText.left = this.equalText.right + 6;
this.addChild( mutableEquationText );
5.3.3 Functions
In order to update the value of this equation with the current slider value, we are using the following functions:
/**
* Set the text of the data.
* @public
* @param {number} data
*/
setText( data ) {
this.valueText.text = Utils.toFixed(this.numberToString( data ).absoluteNumber, 2);
}
/**
* Convert a number to String, rounding to a certain number of decimal places
* @private
* @param {number} number
* @returns {{absoluteNumber: number, optionalSign: string, sign: string}}
*/
numberToString( number ) {
const isNegative = ( this.roundNumber( number ) < 0 );
const signString = isNegative ? MathSymbols.MINUS : MathSymbols.PLUS;
const optionalSignString = isNegative ? MathSymbols.MINUS : ' ';
const absoluteNumber = this.roundNumber( Math.abs( this.roundNumber( number ) ) );
const numberString = {
absoluteNumber: absoluteNumber,
optionalSign: optionalSignString,
sign: signString
;
}return numberString;
}
/**
* Round a number to a certain number of decimal places.
* @private
* @param {number} number
* @returns {number}
*/
roundNumber( number ) {
let roundedNumber;
if ( Math.abs( number ) < 10 ) {
// eg. 9.99, 0.01 if this.options.maxDecimalPlaces=2
= Utils.toFixed( number, this.options.maxDecimalPlaces );
roundedNumber
}else if ( Math.abs( number ) < 100 ) {
// eg. 10.1, 99.9
= Utils.toFixed( number, this.options.maxDecimalPlaces - 1 );
roundedNumber
}else {
// 100, 1000, 10000, 99999
= Utils.toFixed( number, this.options.maxDecimalPlaces - 2 );
roundedNumber
}return roundedNumber;
} }
Here’s a quick rundown of what each function does:
setText()
- Arguments: It takes the new data as an argument.
- It updates the value of our equation to the new data.
numberToString()
- Arguments: It takes a number as an argument.
- Convert a number to a String, subject to rounding to a certain number of decimal places.
roundNumber()
- Arguemnts - It takes a number as an argument.
- Round a number to a certain number of decimal places. Higher numbers have less decimal places.
5.3.4 Code
Here’s the complete code of this file for better comprehension:
/**
* Equation Node that renders a text node of a linear equation.
*
* @author Mayank Pandey
*/
import Utils from '../../../../dot/js/Utils.js';
import merge from '../../../../phet-core/js/merge.js';
import MathSymbols from '../../../../scenery-phet/js/MathSymbols.js';
import Node from '../../../../scenery/js/nodes/Node.js';
import Text from '../../../../scenery/js/nodes/Text.js';
import RichText from '../../../../scenery/js/nodes/RichText.js';
import newtonRaphson from '../../newtonRaphson.js';
import NewtonRaphsonConstants from '../NewtonRaphsonConstants.js';
class EquationNode extends Node {
/**
* Scenery Node responsible for laying out the linear equation.
* @param {Object} [options]
*/
constructor( options, symbolYString ) {
super();
= merge( {
options maxDecimalPlaces: 4, // maximum of number of decimal places
maxCharacterWidth: 600
, options );
}
this.symbolYString = symbolYString;
this.options = options;
// options for the text elements of the equation
// font and fill options for numerical strings , i.e. '- 9.54'
let numericalTextOptions;
// font and fill options for 'pure' strings, eg. 'y'
let stringTextOptions;
= {
numericalTextOptions font: NewtonRaphsonConstants.TEXT_BOLD_FONT_EQUATION,
maxWidth: options.maxCharacterWidth
;
}
= {
stringTextOptions font: NewtonRaphsonConstants.TEXT_FONT,
fill: 'black',
maxWidth: options.maxCharacterWidth
;
}
// use the widest possible numbers for laying out the equation
let maxWidthString = '0.';
for ( let j = 0; j < options.maxDecimalPlaces; j++ ) {
= maxWidthString + '0';
maxWidthString
}
// 'y'
this.yText = new RichText( symbolYString, stringTextOptions );
// the '=' sign
this.equalText = new Text( MathSymbols.EQUAL_TO, stringTextOptions );
// a number
this.valueText = new Text( maxWidthString, numericalTextOptions );
const mutableEquationText = new Node( {
children: [
this.yText,
this.equalText,
this.valueText
];
} )
// layout of the entire equation
this.yText.left = 0;
this.equalText.left = this.yText.right + 3;
this.valueText.left = this.equalText.right + 6;
this.addChild( mutableEquationText );
this.mutate( options );
}
/**
* Set the text of the data.
* @public
* @param {number} data
*/
setText( data ) {
this.valueText.text = Utils.toFixed(this.numberToString( data ).absoluteNumber, 2);
}
/**
* Convert a number to String, rounding to a certain number of decimal places
* @private
* @param {number} number
* @returns {{absoluteNumber: number, optionalSign: string, sign: string}}
*/
numberToString( number ) {
const isNegative = ( this.roundNumber( number ) < 0 );
const signString = isNegative ? MathSymbols.MINUS : MathSymbols.PLUS;
const optionalSignString = isNegative ? MathSymbols.MINUS : ' ';
const absoluteNumber = this.roundNumber( Math.abs( this.roundNumber( number ) ) );
const numberString = {
absoluteNumber: absoluteNumber,
optionalSign: optionalSignString,
sign: signString
;
}return numberString;
}
/**
* Round a number to a certain number of decimal places.
* @private
* @param {number} number
* @returns {number}
*/
roundNumber( number ) {
let roundedNumber;
if ( Math.abs( number ) < 10 ) {
// eg. 9.99, 0.01 if this.options.maxDecimalPlaces=2
= Utils.toFixed( number, this.options.maxDecimalPlaces );
roundedNumber
}else if ( Math.abs( number ) < 100 ) {
// eg. 10.1, 99.9
= Utils.toFixed( number, this.options.maxDecimalPlaces - 1 );
roundedNumber
}else {
// 100, 1000, 10000, 99999
= Utils.toFixed( number, this.options.maxDecimalPlaces - 2 );
roundedNumber
}return roundedNumber;
}
}
.register( 'EquationNode', EquationNode );
newtonRaphson
export default EquationNode;