Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Template graph scaffolding #3019

Merged
merged 1 commit into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions media/js/src/GraphEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import NonLinearDemandSupplyEditor from './editors/NonLinearDemandSupplyEditor.j
import ConsumptionLeisureEditor from './editors/ConsumptionLeisureEditor.jsx';
import ConsumptionSavingEditor from './editors/ConsumptionSavingEditor.jsx';
import DemandSupplyEditor from './editors/DemandSupplyEditor.jsx';
import TemplateGraphEditor from './editors/TemplateGraphEditor.jsx';
import CommonGraphEditor from './editors/CommonGraphEditor.jsx';
import CommonGraphSettings from './editors/CommonGraphSettings.jsx';
import JXGBoard from './JXGBoard.jsx';
Expand Down Expand Up @@ -245,6 +246,13 @@ export default class GraphEditor extends React.Component {
{...commonEditorProps}
{...this.props}
/>;
} else if (this.props.gType === 16) {
// Template Graph: free-form equations
rightSide =
<TemplateGraphEditor
{...commonEditorProps}
{...this.props}
/>;
}

return (
Expand Down Expand Up @@ -380,6 +388,7 @@ GraphEditor.propTypes = {
gNName: PropTypes.string,

gFunctionChoice: PropTypes.number,
gExpression: PropTypes.string,

gAreaConfiguration: PropTypes.number,
gIsAreaDisplayed: PropTypes.bool,
Expand Down
21 changes: 17 additions & 4 deletions media/js/src/GraphMapping.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,9 @@ const exportGraph = function(state) {

area_a_name: state.gAreaAName,
area_b_name: state.gAreaBName,
area_c_name: state.gAreaCName
area_c_name: state.gAreaCName,

expression: state.gExpression
};

if (state.gType === 3 || state.gType === 12) {
Expand Down Expand Up @@ -215,7 +217,9 @@ const convertGraph = function(json) {

gAreaAName: json.area_a_name,
gAreaBName: json.area_b_name,
gAreaCName: json.area_c_name
gAreaCName: json.area_c_name,

gExpression: json.expression
};
};

Expand Down Expand Up @@ -315,7 +319,10 @@ const importGraph = function(json, obj, callback=null) {

gAreaAName: json.area_a_name,
gAreaBName: json.area_b_name,
gAreaCName: json.area_c_name
gAreaCName: json.area_c_name,

// TODO: enable this when expression back-end is in place.
// gExpression: json.expression
};

// When importing a graph for display, save the initial state of
Expand Down Expand Up @@ -572,6 +579,8 @@ const defaultGraph = {
gAreaBName: 'B',
gAreaCName: 'C',

gExpression: 'x',

// Use a hard-coded proof-of-concept assessment spreadsheet for
// now. Eventually, this will be defined using a Google
// Spreadsheet, or some react-spreadsheet component with its data
Expand All @@ -581,4 +590,8 @@ const defaultGraph = {
assessment: []
};

export { convertGraph, exportGraph, importGraph, defaultGraph, defaultEvaluation, defaultModificationEvaluation, defaultLabelEvaluation };
export {
convertGraph, exportGraph, importGraph, defaultGraph,
defaultEvaluation, defaultModificationEvaluation,
defaultLabelEvaluation
};
9 changes: 9 additions & 0 deletions media/js/src/GraphViewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import ConsumptionLeisureEditor from './editors/ConsumptionLeisureEditor.jsx';
import ConsumptionSavingEditor from './editors/ConsumptionSavingEditor.jsx';
import DemandSupplyEditor from './editors/DemandSupplyEditor.jsx';
import NonLinearDemandSupplyEditor from './editors/NonLinearDemandSupplyEditor.jsx';
import TemplateGraphEditor from './editors/TemplateGraphEditor.jsx';
import ExportGraphButton from './buttons/ExportGraphButton.jsx';
import ResetGraphButton from './buttons/ResetGraphButton.jsx';
import SubmitButton from './buttons/SubmitButton.jsx';
Expand Down Expand Up @@ -187,6 +188,13 @@ export default class GraphViewer extends React.Component {
{...commonViewerProps}
{...this.props}
/>;
} else if (this.props.gType === 16) {
// Template Graph: free-form equations
rightSide =
<TemplateGraphEditor
{...commonViewerProps}
{...this.props}
/>;
}

// Show side-by-side view here.
Expand Down Expand Up @@ -447,6 +455,7 @@ GraphViewer.propTypes = {
gNName: PropTypes.string,

gFunctionChoice: PropTypes.number,
gExpression: PropTypes.string,

assessment: PropTypes.array,
submission: PropTypes.object,
Expand Down
6 changes: 5 additions & 1 deletion media/js/src/JXGBoard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,9 @@ export default class JXGBoard extends React.Component {
'gCobbDouglasAlpha', 'gCobbDouglasAlphaInitial', 'gNName',
'gFunctionChoice', 'gAreaConfiguration',
'gAreaConfigurationInitial', 'gIsAreaDisplayed', 'gAreaAName',
'gAreaBName', 'gAreaCName', 'gNeedsSubmit', 'submission',
'gAreaBName', 'gAreaCName',
'gExpression',
'gNeedsSubmit', 'submission',
'shadow'
];

Expand Down Expand Up @@ -704,6 +706,8 @@ JXGBoard.propTypes = {
gAreaBName: PropTypes.string,
gAreaCName: PropTypes.string,

gExpression: PropTypes.string,

id: PropTypes.string.isRequired,
locked: PropTypes.bool
};
4 changes: 4 additions & 0 deletions media/js/src/Viewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ class Viewer extends Component {
gAreaBName={this.state.gAreaBName}
gAreaCName={this.state.gAreaCName}

gExpression={this.state.gExpression}

updateDisplayIntersection={this.updateDisplayIntersection.bind(this)}
updateGraph={this.handleGraphUpdate.bind(this)}
saveGraph={this.handleSaveGraph.bind(this)}
Expand Down Expand Up @@ -268,6 +270,8 @@ class Viewer extends Component {
gAreaBName={this.state.gAreaBName}
gAreaCName={this.state.gAreaCName}

gExpression={this.state.gExpression}

assessment={this.state.assessment}
submission={this.state.submission}
updateGraph={this.handleGraphUpdate.bind(this)}
Expand Down
88 changes: 88 additions & 0 deletions media/js/src/editors/TemplateGraphEditor.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React from 'react';
import PropTypes from 'prop-types';
import { MathComponent } from 'mathjax-react';

import EditableControl from '../form-components/EditableControl.js';
import { handleFormUpdate } from '../utils.js';

export default class TemplateGraphEditor extends React.Component {
render() {
const func1 = String.raw`MP_N = (1 - \alpha)AK^\alpha N^{-\alpha}`;
const func2 = String.raw`MP_K = \alpha AK^{\alpha - 1} N^{1 - \alpha}`;

return (
<>
<div className="d-flex flex-wrap">
<div className="row">
<div className="col">
<label
className="form-check-label me-2"
htmlFor="gExpression">
<MathComponent tex="y = " />
</label>
<EditableControl
id="gExpression"
name="Expression"
value={this.props.gExpression}
valueEditable={true}
isInstructor={this.props.isInstructor}
disabled={this.props.disabled}
updateGraph={this.props.updateGraph} />
</div>
</div>
</div>

<h3 className="mt-3">
NLDS Function
</h3>

<div className="row">
<div className="form-check">
<input
className="form-check-input"
aria-label={func1}
type="radio"
name="gFunctionChoice"
id="gFunctionChoice1"
onChange={handleFormUpdate.bind(this)}
value={0}
checked={this.props.gFunctionChoice === 0} />
<label
className="form-check-label"
htmlFor="gFunctionChoice1">
<MathComponent tex={func1} />
</label>
</div>
</div>
<div className="row">
<div className="form-check">
<input
className="form-check-input"
aria-label={func2}
type="radio"
name="gFunctionChoice"
id="gFunctionChoice2"
onChange={handleFormUpdate.bind(this)}
value={1}
checked={this.props.gFunctionChoice === 1} />
<label
className="form-check-label"
htmlFor="gFunctionChoice2">
<MathComponent tex={func2} />
</label>
</div>
</div>
</>
);
}
}

TemplateGraphEditor.propTypes = {
gType: PropTypes.number,
updateGraph: PropTypes.func.isRequired,
isInstructor: PropTypes.bool.isRequired,
disabled: PropTypes.bool,

gExpression: PropTypes.string,
gFunctionChoice: PropTypes.number
};
5 changes: 3 additions & 2 deletions media/js/src/form-components/EditableControl.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default class EditableControl extends React.Component {
name={this.props.id}
className="form-control"
type="text"
maxLength="60"
maxLength={this.props.maxLength || 60}
disabled={this.props.disabled}
value={this.props.value}
onChange={handleFormUpdate.bind(this)} />
Expand All @@ -37,5 +37,6 @@ EditableControl.propTypes = {
isInstructor: PropTypes.bool.isRequired,
value: PropTypes.string,
valueEditable: PropTypes.bool.isRequired,
disabled: PropTypes.bool
disabled: PropTypes.bool,
maxLength: PropTypes.number
};
28 changes: 28 additions & 0 deletions media/js/src/graphs/TemplateGraph.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,35 @@
import { create, all } from 'mathjs';
import {Graph} from './Graph.js';

const math = create(all, {});

export class TemplateGraph extends Graph {
make() {
const me = this;
const f = function(x) {
const scope = {
x: x
};

let func = function() {};
try {
func = math.evaluate(me.options.gExpression, scope);
} catch (e) {
console.error('Parse error!');
}

return func;
};

this.board.create('functiongraph', [f], {
name: 'expression',
withLabel: true,
strokeWidth: 2,
strokeColor: this.options.gType === 12 ?
this.l2Color : this.l1Color,
recursionDepthLow: 8,
recursionDepthHigh: 15
});
}
}

Expand Down
Loading
Loading