diff --git a/src/components/output/OutputView.svelte b/src/components/output/OutputView.svelte
index 08886fba0..679e3c870 100644
--- a/src/components/output/OutputView.svelte
+++ b/src/components/output/OutputView.svelte
@@ -38,7 +38,7 @@
import Pointer from '../../input/Pointer';
import Place from '../../output/Place';
import moveOutput, { addStageContent } from '../palette/editOutput';
- import { getPlace } from '../../output/getPlace';
+ import { getOrCreatePlace } from '../../output/getOrCreatePlace';
import { SvelteComponent, afterUpdate, beforeUpdate } from 'svelte';
import Placement from '../../input/Placement';
import { toExpression } from '../../parser/parseExpression';
@@ -446,7 +446,7 @@
? renderedFocus
: // If there's selected output, it's the first output selected, and it has a place
$selectedOutput && $selectedOutput.length > 0
- ? getPlace(
+ ? getOrCreatePlace(
project,
$locale,
$selectedOutput[0],
diff --git a/src/components/palette/BindColor.svelte b/src/components/palette/BindColor.svelte
index feef58817..36e9bd5e0 100644
--- a/src/components/palette/BindColor.svelte
+++ b/src/components/palette/BindColor.svelte
@@ -9,6 +9,7 @@
import type OutputProperty from '../../edit/OutputProperty';
import { getProject, getSelectedOutput } from '../project/Contexts';
import { Projects } from '../../db/Database';
+ import type Bind from '../../nodes/Bind';
export let property: OutputProperty;
export let values: OutputPropertyValueSet;
@@ -17,9 +18,15 @@
let project = getProject();
let selectedOutput = getSelectedOutput();
- $: lightness = getColorValue('lightness') ?? 0;
- $: chroma = getColorValue('chroma') ?? 0;
- $: hue = getColorValue('hue') ?? 0;
+ $: lightness = $project
+ ? getColorValue($project.shares.output.Color.inputs[0]) ?? 0
+ : 0;
+ $: chroma = $project
+ ? getColorValue($project.shares.output.Color.inputs[1]) ?? 0
+ : 0;
+ $: hue = $project
+ ? getColorValue($project.shares.output.Color.inputs[1]) ?? 0
+ : 0;
// Whenever the slider value changes, revise the Evaluates to match the new value.
function handleChange(l: number, c: number, h: number) {
@@ -51,19 +58,20 @@
);
}
- function getColorValue(name: string) {
+ function getColorValue(bind: Bind) {
if ($project === undefined) return undefined;
// The value of this facet on every value selected.
const facets = values.values.map((val) => {
if ($project && val.expression instanceof Evaluate) {
const mapping = val.expression.getMappingFor(
- name,
+ bind,
$project.getNodeContext(val.expression)
);
const number =
mapping && mapping.given instanceof NumberLiteral
? mapping.given.getValue().toNumber() *
- (name === 'lightness' && mapping.given.isPercent()
+ (bind === $project.shares.output.Color.inputs[0] &&
+ mapping.given.isPercent()
? 0.01
: 1)
: undefined;
diff --git a/src/components/palette/PlaceEditor.svelte b/src/components/palette/PlaceEditor.svelte
index f00d6e93a..c3d858be2 100644
--- a/src/components/palette/PlaceEditor.svelte
+++ b/src/components/palette/PlaceEditor.svelte
@@ -1,5 +1,4 @@
- {project.shares.output.Place.names.getSymbolicName()}{#each [getFirstName($locale.output.Place.x.names), getFirstName($locale.output.Place.y.names), getFirstName($locale.output.Place.z.names)] as dimension, index}
- {@const given = place?.getMappingFor(
+ {project.shares.output.Place.names.getSymbolicName()}{#each project.shares.output.Place.inputs as dimension, index}
+ {@const given = place?.getInput(
dimension,
project.getNodeContext(place)
- )?.given}
+ )}
{@const value =
given instanceof Expression ? getNumber(given) : undefined}
@@ -70,7 +70,7 @@
text={`${value}`}
validator={valid}
{editable}
- placeholder={getFirstName(dimension)}
+ placeholder={dimension.names.getNames()[0]}
description={$locale.ui.palette.field.coordinate}
changed={(value) => handleChange(dimension, value)}
bind:view={views[index]}
diff --git a/src/components/palette/VelocityEditor.svelte b/src/components/palette/VelocityEditor.svelte
index 1672a4700..c79a34236 100644
--- a/src/components/palette/VelocityEditor.svelte
+++ b/src/components/palette/VelocityEditor.svelte
@@ -1,5 +1,4 @@
- {project.shares.output.Velocity.names.getSymbolicName()}{#each project.shares.output.Velocity.inputs.map( (input) => input.getPreferredName($locales) ) as dimension, index}
- {@const mapping = velocity?.getMappingFor(
+ {project.shares.output.Velocity.names.getSymbolicName()}{#each project.shares.output.Velocity.inputs as dimension, index}
+ {@const given = velocity?.getInput(
dimension,
project.getNodeContext(velocity)
)}
- {@const given = mapping?.given}
{@const value =
given instanceof Expression ? getNumber(given) : undefined}
@@ -73,7 +68,7 @@
text={`${value}`}
validator={valid}
{editable}
- placeholder={getFirstName(dimension)}
+ placeholder={dimension.names.getNames()[0]}
description={$locale.ui.palette.field.coordinate}
changed={(value) => handleChange(dimension, index, value)}
bind:view={views[index]}
diff --git a/src/components/palette/editOutput.ts b/src/components/palette/editOutput.ts
index 39ee83132..f09bfb166 100644
--- a/src/components/palette/editOutput.ts
+++ b/src/components/palette/editOutput.ts
@@ -20,6 +20,7 @@ import {
STAGE_SYMBOL,
} from '../../parser/Symbols';
import { toExpression } from '../../parser/parseExpression';
+import { getPlaceExpression } from '../../output/getOrCreatePlace';
export function getNumber(given: Expression): number | undefined {
const measurement =
@@ -54,65 +55,83 @@ export default function moveOutput(
evaluates.map((evaluate) => {
const ctx = project.getNodeContext(evaluate);
- const given = evaluate.getMappingFor('place', ctx);
+ const given = getPlaceExpression(project, evaluate, ctx);
const place =
- given &&
- given.given instanceof Evaluate &&
- given.given.is(PlaceType, ctx)
- ? given.given
- : given &&
- given.given instanceof Bind &&
- given.given.value instanceof Evaluate &&
- given.given.value.is(PlaceType, ctx)
- ? given.given.value
+ given instanceof Evaluate && given.is(PlaceType, ctx)
+ ? given
: undefined;
- const x = place?.getMappingFor('x', ctx)?.given;
- const y = place?.getMappingFor('y', ctx)?.given;
- const z = place?.getMappingFor('z', ctx)?.given;
+ const x = place?.getInput(
+ project.shares.output.Place.inputs[0],
+ ctx
+ );
+ const y = place?.getInput(
+ project.shares.output.Place.inputs[1],
+ ctx
+ );
+ const z = place?.getInput(
+ project.shares.output.Place.inputs[2],
+ ctx
+ );
const xValue = x instanceof Expression ? getNumber(x) : undefined;
const yValue = y instanceof Expression ? getNumber(y) : undefined;
const zValue = z instanceof Expression ? getNumber(z) : undefined;
+ const bind = evaluate.is(project.shares.output.Phrase, ctx)
+ ? project.shares.output.Phrase.inputs[3]
+ : evaluate.is(project.shares.output.Group, ctx)
+ ? project.shares.output.Phrase.inputs[4]
+ : undefined;
+
return [
evaluate,
- evaluate.withBindAs(
- 'place',
- Evaluate.make(
- Reference.make(
- PlaceType.names.getPreferredNameString(locales),
- PlaceType
- ),
- [
- // If coordinate is computed, and not a literal, don't change it.
- x instanceof Expression && xValue === undefined
- ? x
- : NumberLiteral.make(
- relative
- ? new Decimal(xValue ?? 0)
- .add(horizontal)
- .toNumber()
- : horizontal,
- Unit.create(['m'])
- ),
- y instanceof Expression && yValue === undefined
- ? y
- : NumberLiteral.make(
- relative
- ? new Decimal(yValue ?? 0)
- .add(vertical)
- .toNumber()
- : vertical,
- Unit.create(['m'])
+ bind === undefined
+ ? evaluate
+ : evaluate.withBindAs(
+ bind,
+ Evaluate.make(
+ Reference.make(
+ PlaceType.names.getPreferredNameString(
+ locales
),
- z instanceof Expression && zValue !== undefined
- ? z
- : NumberLiteral.make(0, Unit.create(['m'])),
- ]
- ),
- ctx
- ),
+ PlaceType
+ ),
+ [
+ // If coordinate is computed, and not a literal, don't change it.
+ x instanceof Expression &&
+ xValue === undefined
+ ? x
+ : NumberLiteral.make(
+ relative
+ ? new Decimal(xValue ?? 0)
+ .add(horizontal)
+ .toNumber()
+ : horizontal,
+ Unit.create(['m'])
+ ),
+ y instanceof Expression &&
+ yValue === undefined
+ ? y
+ : NumberLiteral.make(
+ relative
+ ? new Decimal(yValue ?? 0)
+ .add(vertical)
+ .toNumber()
+ : vertical,
+ Unit.create(['m'])
+ ),
+ z instanceof Expression &&
+ zValue !== undefined
+ ? z
+ : NumberLiteral.make(
+ 0,
+ Unit.create(['m'])
+ ),
+ ]
+ ),
+ ctx
+ ),
];
})
);
@@ -231,12 +250,12 @@ export function addStageContent(
if (stage) {
const context = project.getNodeContext(stage);
- const list = stage.getExpressionFor(
- StageType.inputs[0].getNames()[0],
- context
- );
- if (list instanceof ListLiteral) {
- reviseContent(database, project, list, [...list.values, content]);
+ const content = stage.getInput(StageType.inputs[0], context);
+ if (content instanceof ListLiteral) {
+ reviseContent(database, project, content, [
+ ...content.values,
+ content,
+ ]);
}
}
}
diff --git a/src/edit/OutputExpression.ts b/src/edit/OutputExpression.ts
index 350ca0d8a..c20971806 100644
--- a/src/edit/OutputExpression.ts
+++ b/src/edit/OutputExpression.ts
@@ -166,10 +166,18 @@ export default class OutputExpression {
}
withPropertyUnset(name: string): Evaluate {
- return this.node.withBindAs(
- name,
- undefined,
- this.project.getNodeContext(this.node)
- );
+ // Find the bind corresponding to the given name.
+
+ const context = this.project.getNodeContext(this.node);
+ const fun = this.node.getFunction(context);
+ const bind = fun?.inputs.find((bind) => bind.hasName(name));
+
+ return bind
+ ? this.node.withBindAs(
+ bind,
+ undefined,
+ this.project.getNodeContext(this.node)
+ )
+ : this.node;
}
}
diff --git a/src/models/Project.ts b/src/models/Project.ts
index 84e4c4077..3de4f455c 100644
--- a/src/models/Project.ts
+++ b/src/models/Project.ts
@@ -806,14 +806,22 @@ export default class Project {
name: string,
value: Expression | undefined
): [Evaluate, Evaluate | undefined][] {
- return evaluates.map((evaluate) => [
- evaluate,
- evaluate.withBindAs(
- name,
- value?.clone(),
- this.getNodeContext(evaluate)
- ),
- ]);
+ return evaluates.map((evaluate) => {
+ // Find the bind corresponding to the name.
+ const context = this.getNodeContext(evaluate);
+ const fun = evaluate.getFunction(context);
+ const bind = fun?.inputs.find((bind) => bind.hasName(name));
+ return bind
+ ? [
+ evaluate,
+ evaluate.withBindAs(
+ bind,
+ value?.clone(),
+ this.getNodeContext(evaluate)
+ ),
+ ]
+ : [evaluate, evaluate];
+ });
}
/** Get all the languages used in the project */
diff --git a/src/nodes/Evaluate.ts b/src/nodes/Evaluate.ts
index a411df248..ff6af9698 100644
--- a/src/nodes/Evaluate.ts
+++ b/src/nodes/Evaluate.ts
@@ -355,16 +355,16 @@ export default class Evaluate extends Expression {
}
/**
- * Given a name and an expression, create a new evaluate that binds this name to this value instead of its current binding,
+ * Given a bind of the function being evaluated and an expression, create a new evaluate that binds this name to this value instead of its current binding,
* and if there is no current binding, create one.
*/
withBindAs(
- name: string,
+ bind: Bind,
expression: Expression | undefined,
context: Context,
named = true
): Evaluate {
- const mapping = this.getMappingFor(name, context);
+ const mapping = this.getMappingFor(bind, context);
if (mapping === undefined) return this;
// If we'replacing with nothing
@@ -385,7 +385,7 @@ export default class Evaluate extends Expression {
named
? Bind.make(
undefined,
- Names.make([name]),
+ Names.make([bind.getNames()[0]]),
undefined,
expression
)
@@ -407,23 +407,12 @@ export default class Evaluate extends Expression {
);
}
- getExpressionFor(name: string, context: Context) {
- const mapping = this.getMappingFor(name, context);
- return mapping === undefined
- ? undefined
- : mapping.given instanceof Bind
- ? mapping.given.value
- : mapping.given;
- }
-
- getMappingFor(name: string, context: Context) {
+ getMappingFor(bind: Bind, context: Context) {
// Figure out what the current mapping is.
const mappings = this.getInputMapping(context);
// Find the bind.
- return mappings?.inputs.find((input) =>
- input.expected.names.hasName(name)
- );
+ return mappings?.inputs.find((input) => input.expected === bind);
}
computeConflicts(context: Context): Conflict[] {
diff --git a/src/output/getPlace.ts b/src/output/getOrCreatePlace.ts
similarity index 51%
rename from src/output/getPlace.ts
rename to src/output/getOrCreatePlace.ts
index 22dc65531..10c6e54c9 100644
--- a/src/output/getPlace.ts
+++ b/src/output/getOrCreatePlace.ts
@@ -5,18 +5,33 @@ import Evaluate from '../nodes/Evaluate';
import evaluateCode from '../runtime/evaluate';
import { toPlace } from './Place';
-export function getPlace(
+export function getPlaceExpression(
+ project: Project,
+ evaluate: Evaluate,
+ context: Context
+) {
+ return (
+ evaluate.getInput(project.shares.output.Phrase.inputs[3], context) ??
+ evaluate.getInput(project.shares.output.Group.inputs[4], context)
+ );
+}
+
+export function getOrCreatePlace(
project: Project,
locale: Locale,
evaluate: Evaluate,
context: Context
) {
- const place = evaluate.getExpressionFor('place', context);
+ const place = getPlaceExpression(project, evaluate, context);
if (place instanceof Evaluate) {
+ // Only return a place if it's a Place creator.
if (
place.is(project.shares.output.Place, project.getNodeContext(place))
)
return toPlace(evaluateCode(place.toWordplay(), [], locale));
else return undefined;
- } else return toPlace(evaluateCode('Place()', [], locale));
+ } else
+ return toPlace(
+ evaluateCode(`${project.shares.output.Place.names.getNames()[0]}()`)
+ );
}