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

Externalities: Add graphics for first scenario #3790

Merged
merged 1 commit into from
Mar 6, 2025
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
8 changes: 8 additions & 0 deletions media/js/src/GraphEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import RevenueElasticityEditor from './editors/RevenueElasticityEditor.jsx';
import OptimalChoiceCostMinimizingEditor from './editors/OptimalChoiceCostMinimizingEditor.jsx';
import TaxationLinearDemandEditor from './editors/TaxationLinearDemandEditor.jsx';
import TaxRevenueEditor from './editors/TaxRevenueEditor.jsx';
import NegativeProductionExternalityProducerEditor from './editors/NegativeProductionExternalityProducerEditor.jsx';

import JXGBoard from './JXGBoard.jsx';
import GraphPane from './GraphPane.jsx';
Expand Down Expand Up @@ -330,6 +331,13 @@ export default class GraphEditor extends React.Component {
{...commonEditorProps}
{...this.props}
/>;
} else if (this.props.gType === 26) {
rightSide =
<NegativeProductionExternalityProducerEditor
updateGraph={this.props.updateGraph}
{...commonEditorProps}
{...this.props}
/>;
}

const hasIntersection = ![
Expand Down
9 changes: 9 additions & 0 deletions media/js/src/GraphViewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import OptimalChoiceConsumptionEditor from './editors/OptimalChoiceConsumption.j
import CostFunctionsEditor from './editors/CostFunctionsEditor.jsx';
import OptimalChoiceCostMinimizingEditor from './editors/OptimalChoiceCostMinimizingEditor.jsx';
import TaxationLinearDemandEditor from './editors/TaxationLinearDemandEditor.jsx';
import NegativeProductionExternalityProducerEditor from
'./editors/NegativeProductionExternalityProducerEditor.jsx';

import ExportGraphButton from './buttons/ExportGraphButton.jsx';
import ResetGraphButton from './buttons/ResetGraphButton.jsx';
Expand Down Expand Up @@ -330,6 +332,13 @@ export default class GraphViewer extends React.Component {
{...commonViewerProps}
{...this.props}
/>;
} else if (this.props.gType === 26) {
rightSide =
<NegativeProductionExternalityProducerEditor
updateGraph={this.props.updateGraph}
{...commonViewerProps}
{...this.props}
/>;
}

return (
Expand Down
12 changes: 12 additions & 0 deletions media/js/src/JXGBoard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,18 @@ export default class JXGBoard extends React.Component {
xTicks = this.visibleTicks;
yTicks = xTicks;
break;
case 26:
xTicks = this.visibleTicks;
yTicks = xTicks;
break;
case 28:
xTicks = this.visibleTicks;
yTicks = xTicks;
break;
case 27:
xTicks = this.visibleTicks;
yTicks = xTicks;
break;
default:
xAxisLabel = options.gXAxisLabel ? options.gXAxisLabel : 'x';
yAxisLabel = options.gYAxisLabel ? options.gYAxisLabel : 'y';
Expand Down
137 changes: 137 additions & 0 deletions media/js/src/editors/NegativeProductionExternalityProducerEditor.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import React from 'react';
import PropTypes from 'prop-types';
import RangeEditor from '../form-components/RangeEditor.jsx';
import { handleFormUpdate } from '../utils.js';

export default class NegativeProductionExternalityProducerEditor extends React.Component {
render() {
const me = this;

const modesLeft = [
'Negative Producer Externality',
'Unregulated',
'Welfare',
];
const modesRight = [
'Pigouvian Tax',
'Pigouvian Tax (Welfare)',
];

const radioButtons1 = modesLeft.map((optionTitle, idx) =>
<div key={idx} className="form-check">
<input
type="radio" id={`functionChoice-${idx}`}
className="form-check-input"
value={idx}
name="gFunctionChoice"
checked={me.props.gFunctionChoice === idx}
onChange={handleFormUpdate.bind(me)} />
<label
className="form-check-label"
htmlFor={`functionChoice-${idx}`}>
{optionTitle}
</label>
</div>
);

const radioButtons2 = modesRight.map(function(optionTitle, idx) {
const newIdx = idx + modesLeft.length;
return (
<div key={newIdx} className="form-check">
<input
type="radio" id={`functionChoice-${newIdx}`}
className="form-check-input"
value={newIdx}
name="gFunctionChoice"
checked={me.props.gFunctionChoice === newIdx}
onChange={handleFormUpdate.bind(me)} />
<label
className="form-check-label"
htmlFor={`functionChoice-${newIdx}`}>
{optionTitle}
</label>
</div>
);
});

return (
<>
<div className="row">
<div className="col">
{radioButtons1}
</div>
<div className="col">
{radioButtons2}
</div>
</div>

<div>
{this.props.displaySliders && (
<>
<RangeEditor
label="MB Constant"
rawLabel={true}
id="gA1"
value={this.props.gA1}
min={0}
max={10000}
handler={handleFormUpdate.bind(this)} />

<RangeEditor
label="MC Constant"
rawLabel={true}
id="gA2"
value={this.props.gA2}
min={0}
max={10000}
handler={handleFormUpdate.bind(this)} />

<RangeEditor
label="MC Slope"
rawLabel={true}
id="gA3"
value={this.props.gA3}
min={0.01}
max={35}
handler={handleFormUpdate.bind(this)} />

<RangeEditor
label="EMC Constant"
rawLabel={true}
id="gA4"
value={this.props.gA4}
min={0}
max={10000}
handler={handleFormUpdate.bind(this)} />

<RangeEditor
label="EMC Slope"
rawLabel={true}
id="gA5"
value={this.props.gA5}
min={0.0}
max={35}
handler={handleFormUpdate.bind(this)} />
</>
)}
</div>
</>
);
}
}

NegativeProductionExternalityProducerEditor.propTypes = {
gType: PropTypes.number.isRequired,

gA1: PropTypes.number.isRequired,
gA2: PropTypes.number.isRequired,
gA3: PropTypes.number.isRequired,
gA4: PropTypes.number.isRequired,
gA5: PropTypes.number.isRequired,
gA6: PropTypes.number,

gFunctionChoice: PropTypes.number.isRequired,
gToggle: PropTypes.bool.isRequired,

displaySliders: PropTypes.bool.isRequired
};
5 changes: 5 additions & 0 deletions media/js/src/graphUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import {
import {
defaults as cobbDouglasDefaults
} from './graphs/CobbDouglasGraph.js';
import {
defaults as externalitiesDefaults
} from './graphs/NegativeProductionExternalityProducerGraph.js';

/**
* Set defaults to the given state update object based on toggle and
Expand Down Expand Up @@ -130,6 +133,8 @@ export const getDefaultGraphState = function(graphType, state) {
});
} else if (graphType === 25) {
Object.assign(state, linearDemandSupplySurplusDefaults[0]);
} else if (graphType >= 26 && graphType <= 28) {
Object.assign(state, externalitiesDefaults[0]);
}

return state;
Expand Down
142 changes: 141 additions & 1 deletion media/js/src/graphs/NegativeProductionExternalityProducerGraph.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,146 @@
import {Graph} from './Graph.js';
import {Graph, positiveRange} from './Graph.js';

export const defaults = [
{
gXAxisMax: 1000,
gYAxisMax: 2500,
gA1: 1500,
gA2: 500,
gA3: 2,
gA4: 50,
gA5: 2
}
];

// Marginal Benefit
const mb = function(a) {
return a;
};

const mc = function(c, d, q) {
return c + d * q;
};

// External Marginal Cost
const emc = function(f, g, q) {
return f + g * q;
};

/*const pemc = function(f, g, p) {
return (p - f) / g;
};*/


// Social Marginal Cost
const smc = function(c, d, f, g, q) {
return mc(c, d, q) + emc(f, g, q);
};

/*const psmc = function(c, d, f, g, p) {
return (p - c - f) / (d + g);
};*/

export class NegativeProductionExternalityProducerGraph extends Graph {
static getGraphPane(gFunctionChoice) {
return [
{
label: 'Unregulated Output q*',
color: 'red',
value: 500
},
{
label: 'Socially Desirable Output q<sup>soc</sup>',
color: 'orange',
value: 247.5
},
{
label: 'Market Price P*',
color: 'red',
value: 1500
},
];
}
make() {
const me = this;

const mbLine = function() {
return mb(me.options.gA1);
};

this.l2 = this.board.create(
'functiongraph',
[positiveRange(mbLine), 0, this.options.gXAxisMax], {
name: 'MB',
withLabel: true,
label: {
strokeColor: this.l2Color
},
strokeWidth: 2,
strokeColor: this.l2Color,
fixed: true,
highlight: false
}
);

const mcLine = function(x) {
return mc(me.options.gA2, me.options.gA3, x);
};

this.l1 = this.board.create(
'functiongraph',
[positiveRange(mcLine), 0, this.options.gXAxisMax], {
name: 'MC',
withLabel: true,
label: {
strokeColor: this.l1Color
},
strokeWidth: 2,
strokeColor: this.l1Color,
fixed: true,
highlight: false
}
);

const emcLine = function(x) {
return emc(me.options.gA4, me.options.gA5, x);
};

this.l3 = this.board.create(
'functiongraph',
[positiveRange(emcLine), 0, this.options.gXAxisMax], {
name: 'EMC',
withLabel: true,
label: {
strokeColor: this.l3Color
},
strokeWidth: 2,
strokeColor: this.l3Color,
fixed: true,
highlight: false
}
);

const smcLine = function(x) {
return smc(
me.options.gA2, me.options.gA3,
me.options.gA4, me.options.gA5, x);
};

this.l4 = this.board.create(
'functiongraph',
[positiveRange(smcLine), 0, this.options.gXAxisMax], {
name: 'SMC',
withLabel: true,
label: {
strokeColor: this.l4Color
},
strokeWidth: 2,
strokeColor: this.l4Color,
fixed: true,
highlight: false
}
);
}
}

export const mkNegativeProductionExternalityProducer = function(board, options) {
Expand Down
Loading