-
Notifications
You must be signed in to change notification settings - Fork 4
/
ExploreInteractiveEquationNode.ts
167 lines (144 loc) · 7.1 KB
/
ExploreInteractiveEquationNode.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// Copyright 2018-2024, University of Colorado Boulder
/**
* Standard form equation, y = ax^2 + bx + c, with coefficients that can be changed via sliders.
* The slider for coefficient 'a' has a quadratic taper (since it's modifying a quadratic term), while
* the other sliders are linear.
*
* @author Andrea Lin
* @author Chris Malley (PixelZoom, Inc.)
*/
import Multilink from '../../../../axon/js/Multilink.js';
import NumberProperty from '../../../../axon/js/NumberProperty.js';
import optionize, { combineOptions, EmptySelfOptions } from '../../../../phet-core/js/optionize.js';
import PickRequired from '../../../../phet-core/js/types/PickRequired.js';
import StringUtils from '../../../../phetcommon/js/util/StringUtils.js';
import MathSymbols from '../../../../scenery-phet/js/MathSymbols.js';
import NumberDisplay, { NumberDisplayOptions } from '../../../../scenery-phet/js/NumberDisplay.js';
import { Node, NodeOptions, RichText, RichTextOptions } from '../../../../scenery/js/imports.js';
import GQColors from '../../common/GQColors.js';
import GQConstants from '../../common/GQConstants.js';
import GQSymbols from '../../common/GQSymbols.js';
import LinearSlider from '../../common/view/LinearSlider.js';
import QuadraticSlider from '../../common/view/QuadraticSlider.js';
import graphingQuadratics from '../../graphingQuadratics.js';
type SelfOptions = EmptySelfOptions;
type ExploreInteractiveEquationNodeOptions = SelfOptions & PickRequired<NodeOptions, 'tandem' | 'phetioDocumentation'>;
export default class ExploreInteractiveEquationNode extends Node {
/**
* Constructor parameters are coefficients of the standard form: y = ax^2 + bx + c
*/
public constructor( aProperty: NumberProperty, bProperty: NumberProperty, cProperty: NumberProperty,
providedOptions: ExploreInteractiveEquationNodeOptions ) {
const options = optionize<ExploreInteractiveEquationNodeOptions, SelfOptions, NodeOptions>()( {
// NodeOptions
excludeInvisibleChildrenFromBounds: true,
visiblePropertyOptions: {
phetioFeatured: true
}
}, providedOptions );
const equationOptions: RichTextOptions = {
font: GQConstants.INTERACTIVE_EQUATION_FONT
};
const xyOptions = combineOptions<RichTextOptions>( {}, equationOptions, {
maxWidth: 20 // determined empirically
} );
// y =
const yText = new RichText( GQSymbols.yMarkupStringProperty, xyOptions );
const equalToText = new RichText( MathSymbols.EQUAL_TO, equationOptions );
// a
const aNumberDisplay = new NumberDisplay( aProperty, aProperty.range,
combineOptions<NumberDisplayOptions>( {}, GQConstants.NUMBER_DISPLAY_OPTIONS, {
textOptions: {
fill: GQColors.exploreAColorProperty
},
decimalPlaces: GQConstants.EXPLORE_DECIMALS_A
} ) );
// x^2 +
const xSquaredText = new RichText( GQSymbols.xSquaredMarkupStringProperty, xyOptions );
const plusText = new RichText( MathSymbols.PLUS, equationOptions );
// b
const bNumberDisplay = new NumberDisplay( bProperty, bProperty.range,
combineOptions<NumberDisplayOptions>( {}, GQConstants.NUMBER_DISPLAY_OPTIONS, {
textOptions: {
fill: GQColors.exploreBColorProperty
},
decimalPlaces: GQConstants.EXPLORE_DECIMALS_B
} ) );
// x +
const xText = new RichText( GQSymbols.xMarkupStringProperty, xyOptions );
const plusText2 = new RichText( MathSymbols.PLUS, equationOptions );
// c
const cNumberDisplay = new NumberDisplay( cProperty, bProperty.range,
combineOptions<NumberDisplayOptions>( {}, GQConstants.NUMBER_DISPLAY_OPTIONS, {
textOptions: {
fill: GQColors.exploreCColorProperty
},
decimalPlaces: GQConstants.EXPLORE_DECIMALS_C
} ) );
// All parts of equation in one Node, for PhET-iO
const equationNode = new Node( {
children: [ yText, equalToText, aNumberDisplay, xSquaredText, plusText, xText, bNumberDisplay, plusText2, cNumberDisplay ],
tandem: options.tandem.createTandem( 'equationNode' ),
phetioDocumentation: 'the equation that changes as the sliders are adjusted',
visiblePropertyOptions: {
phetioFeatured: true
}
} );
// a, b, c sliders
const aSlider = new QuadraticSlider( GQSymbols.aMarkupStringProperty, aProperty, {
interval: GQConstants.EXPLORE_INTERVAL_A,
snapToZeroEpsilon: GQConstants.EXPLORE_SNAP_TO_ZERO_EPSILON_A,
labelColor: GQColors.exploreAColorProperty,
sliderOptions: {
tandem: options.tandem.createTandem( 'aSlider' ),
phetioDocumentation: StringUtils.fillIn( GQConstants.SLIDER_DOC, { symbol: 'a' } )
}
} );
const bSlider = new LinearSlider( GQSymbols.bMarkupStringProperty, bProperty, {
interval: GQConstants.EXPLORE_INTERVAL_B,
labelColor: GQColors.exploreBColorProperty,
sliderOptions: {
tandem: options.tandem.createTandem( 'bSlider' ),
phetioDocumentation: StringUtils.fillIn( GQConstants.SLIDER_DOC, { symbol: 'b' } )
}
} );
const cSlider = new LinearSlider( GQSymbols.cMarkupStringProperty, cProperty, {
interval: GQConstants.EXPLORE_INTERVAL_C,
labelColor: GQColors.exploreCColorProperty,
sliderOptions: {
tandem: options.tandem.createTandem( 'cSlider' ),
phetioDocumentation: StringUtils.fillIn( GQConstants.SLIDER_DOC, { symbol: 'c' } )
}
} );
options.children = [ equationNode, aSlider, bSlider, cSlider ];
super( options );
// If any of the components that include dynamic text change their size, redo the layout.
Multilink.multilink( [
yText.boundsProperty, xSquaredText.boundsProperty, xText.boundsProperty,
aSlider.boundsProperty, bSlider.boundsProperty, cSlider.boundsProperty
],
() => {
// equation layout: y = ax^2 + bx + c
equalToText.left = yText.right + GQConstants.EQUATION_OPERATOR_SPACING;
aNumberDisplay.left = equalToText.right + GQConstants.EQUATION_OPERATOR_SPACING;
xSquaredText.left = aNumberDisplay.right + GQConstants.EQUATION_TERM_SPACING;
plusText.left = xSquaredText.right + GQConstants.EQUATION_OPERATOR_SPACING;
bNumberDisplay.left = plusText.right + GQConstants.EQUATION_OPERATOR_SPACING;
xText.left = bNumberDisplay.right + GQConstants.EQUATION_TERM_SPACING;
plusText2.left = xText.right + GQConstants.EQUATION_OPERATOR_SPACING;
cNumberDisplay.left = plusText2.right + GQConstants.EQUATION_OPERATOR_SPACING;
aNumberDisplay.bottom = equalToText.bottom;
bNumberDisplay.bottom = equalToText.bottom;
cNumberDisplay.bottom = equalToText.bottom;
// horizontally align sliders under their associated values in the equation
const ySpacing = 3;
aSlider.x = aNumberDisplay.centerX;
aSlider.top = equationNode.bottom + ySpacing;
bSlider.x = bNumberDisplay.centerX;
bSlider.top = equationNode.bottom + ySpacing;
cSlider.x = cNumberDisplay.centerX;
cSlider.top = equationNode.bottom + ySpacing;
} );
}
}
graphingQuadratics.register( 'ExploreInteractiveEquationNode', ExploreInteractiveEquationNode );