Skip to content

Commit b7e31fc

Browse files
committed
Differentiate between global and game-specific contexts and palettes; add 'custom' colour function and buttons to the playground
1 parent 67134d9 commit b7e31fc

5 files changed

Lines changed: 120 additions & 4 deletions

File tree

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ export const render = (json: APRenderRep, opts = {} as IRenderOptions): Svg => {
245245
if ( (renderer === undefined) || (renderer === null) ) {
246246
throw new Error(`Could not find the renderer "${ json.renderer }".`);
247247
}
248-
renderer.render(json, draw, {sheets: opts.sheets, patterns: opts.patterns, patternList: opts.patternList, colourBlind: opts.colourBlind, colours: opts.colours, colourContext: opts.colourContext, rotate: opts.rotate, showAnnotations: opts.showAnnotations, boardClick, boardHover, glyphmap: opts.glyphmap});
248+
renderer.render(json, draw, {sheets: opts.sheets, patterns: opts.patterns, patternList: opts.patternList, colourBlind: opts.colourBlind, colours: opts.colours, coloursGlobal: opts.coloursGlobal, colourContext: opts.colourContext, contextGlobal: opts.contextGlobal, rotate: opts.rotate, showAnnotations: opts.showAnnotations, boardClick, boardHover, glyphmap: opts.glyphmap,});
249249
if (draw.bbox().h !== 0
250250
&& draw.viewbox().h === 0 // Only set it here if the renderer didn't set it
251251
) {

src/renderers/_base.ts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,24 @@ export interface IRendererOptionsIn {
4545
* turns it into an IColourContext object.
4646
*/
4747
colourContext?: {[k: string]: string};
48+
/**
49+
* I want to know whether the custom context being passed is a global preference or
50+
* a game-specific one. The idea being that the colourFunc `custom` should prefer
51+
* the developer defaults over global customizations, but defer to game-specific ones.
52+
*/
53+
contextGlobal: boolean;
4854
/**
4955
* A list of hexadecimal colour strings to be used as player colours. Be sure to provide enough for the number of players defined.
5056
* Defaults to a set of nine colours, defined in the constructor.
5157
*
5258
*/
5359
colours?: string[];
60+
/**
61+
* I want to know whether the custom colours being passed are a global preference or
62+
* a game-specific one. The idea being that the colourFunc `custom` should prefer
63+
* the developer defaults over global customizations, but defer to game-specific ones.
64+
*/
65+
coloursGlobal: boolean;
5466
/**
5567
* Signals whether you want black and white patterns used instead of colours.
5668
*
@@ -113,7 +125,9 @@ export interface IRendererOptionsIn {
113125
export interface IRendererOptionsOut {
114126
sheets: string[];
115127
colourContext: IColourContext;
128+
contextGlobal: boolean;
116129
colours: string[];
130+
coloursGlobal: boolean;
117131
patterns: boolean;
118132
patternList: string[];
119133
colourBlind: boolean;
@@ -221,8 +235,10 @@ export abstract class RendererBase {
221235
annotations: "#000",
222236
labels: "#000",
223237
},
238+
contextGlobal: true,
224239
colourBlind: false,
225240
colours: [],
241+
coloursGlobal: true,
226242
patterns: false,
227243
patternList: ["microbial", "chevrons", "honeycomb", "triangles", "wavy", "slant", "dots", "starsWhite", "cross", "houndstooth"],
228244
showAnnotations: true,
@@ -315,6 +331,7 @@ export abstract class RendererBase {
315331
this.options.colourContext[label] = color.toHexString();
316332
}
317333
}
334+
this.options.contextGlobal = opts.contextGlobal ?? true;
318335
}
319336

320337
// Validate colour list if given
@@ -328,6 +345,7 @@ export abstract class RendererBase {
328345
normalized.push(color.toHexString());
329346
}
330347
this.options.colours = [...normalized];
348+
this.options.coloursGlobal = opts.coloursGlobal ?? true;
331349
}
332350

333351
// Check for annotation screening
@@ -3940,7 +3958,49 @@ export abstract class RendererBase {
39403958
else if (val.func === "bestContrast") {
39413959
const bg = this.resolveColour(val.bg) as string;
39423960
const fg = val.fg.map(c => this.resolveColour(c) as string);
3943-
return tinycolor.mostReadable(bg, fg).toHexString();
3961+
colour = tinycolor.mostReadable(bg, fg).toHexString();
3962+
}
3963+
else if (val.func === "custom") {
3964+
// if `palette` is a number
3965+
if (typeof val.palette === "number") {
3966+
// only choose if passed colours are game-specific customizations
3967+
if (!this.options.coloursGlobal) {
3968+
colour = this.resolveColour(val.palette) as string;
3969+
} else {
3970+
colour = this.resolveColour(val.default) as string;
3971+
}
3972+
}
3973+
// if `palette` is a context
3974+
else if (typeof val.palette === "string" && val.palette.startsWith("_context_")) {
3975+
// only choose if passed context is game-specific customization
3976+
if (!this.options.contextGlobal) {
3977+
colour = this.resolveColour(val.palette) as string;
3978+
} else {
3979+
colour = this.resolveColour(val.default) as string;
3980+
}
3981+
}
3982+
// otherwise, rely on `paletteType`
3983+
else if ("paletteType" in val && val.paletteType !== undefined) {
3984+
if (val.paletteType === "context") {
3985+
// only choose if passed context is game-specific customization
3986+
if (!this.options.contextGlobal) {
3987+
colour = this.resolveColour(val.palette) as string;
3988+
} else {
3989+
colour = this.resolveColour(val.default) as string;
3990+
}
3991+
} else {
3992+
// only choose if passed colours are game-specific customizations
3993+
if (!this.options.coloursGlobal) {
3994+
colour = this.resolveColour(val.palette) as string;
3995+
} else {
3996+
colour = this.resolveColour(val.default) as string;
3997+
}
3998+
}
3999+
}
4000+
// otherwise throw
4001+
else {
4002+
throw new Error(`Could not resolve the custom function. If "palette" is neither a number nor a context, then "paletteType" is required.`);
4003+
}
39444004
}
39454005
}
39464006
} else if (typeof val === "number") {

src/schemas/schema.d.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export type Colourstrings = string;
1212
/**
1313
* Colours can also be derived using various functions.
1414
*/
15-
export type Colourfuncs = FunctionFlatten | FunctionBestContrast | FunctionLighten;
15+
export type Colourfuncs = FunctionFlatten | FunctionBestContrast | FunctionLighten | FunctionCustom;
1616
export type PositiveInteger = number;
1717
/**
1818
* Schema for the `matrix` part of a polyomino-related feature
@@ -386,6 +386,18 @@ export interface FunctionLighten {
386386
ds: number;
387387
dl: number;
388388
}
389+
/**
390+
* Supports game developers choosing nonstandard default colours while still allowing players to customize. Ties a default value to a player number or context value, and the function chooses which to use based on whether the user passed in custom values.
391+
*/
392+
export interface FunctionCustom {
393+
func: "custom";
394+
default: PositiveInteger | Colourstrings | Colourfuncs;
395+
palette: PositiveInteger | Colourstrings | Colourfuncs;
396+
/**
397+
* Only needed if the `palette` value is a function and so the function can't tell what test to use.
398+
*/
399+
paletteType?: "player" | "context";
400+
}
389401
/**
390402
* A gradient one can use for flood fills and the like.
391403
*/

src/schemas/schema.json

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,42 @@
117117
"required": ["func", "bg", "fg"],
118118
"additionalProperties": false
119119
},
120+
"functionCustom": {
121+
"description": "Supports game developers choosing nonstandard default colours while still allowing players to customize. Ties a default value to a player number or context value, and the function chooses which to use based on whether the user passed in custom values.",
122+
"type": "object",
123+
"properties": {
124+
"func": {
125+
"enum": ["custom"]
126+
},
127+
"default": {
128+
"anyOf": [
129+
{"$ref": "#/$defs/positiveInteger"},
130+
{"$ref": "#/$defs/colourstrings"},
131+
{"$ref": "#/$defs/colourfuncs"}
132+
]
133+
},
134+
"palette": {
135+
"anyOf": [
136+
{"$ref": "#/$defs/positiveInteger"},
137+
{"$ref": "#/$defs/colourstrings"},
138+
{"$ref": "#/$defs/colourfuncs"}
139+
]
140+
},
141+
"paletteType": {
142+
"description": "Only needed if the `palette` value is a function and so the function can't tell what test to use.",
143+
"enum": ["player", "context"]
144+
}
145+
},
146+
"required": ["func", "default", "palette"],
147+
"additionalProperties": false
148+
},
120149
"colourfuncs": {
121150
"description": "Colours can also be derived using various functions.",
122151
"anyOf": [
123152
{"$ref": "#/$defs/functionFlatten"},
124153
{"$ref": "#/$defs/functionBestContrast"},
125-
{"$ref": "#/$defs/functionLighten"}
154+
{"$ref": "#/$defs/functionLighten"},
155+
{"$ref": "#/$defs/functionCustom"}
126156
]
127157
},
128158
"rowCol": {

test/playground.html

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626
} else if (radio === "patterns") {
2727
options.patterns = true;
2828
}
29+
if (document.getElementById("chkColoursGlobal").checked) {
30+
options.colours = ["#e31a1c","#1f78b4","#33a02c","#ffff99","#6a3d9a","#ff7f00","#b15928","#fb9a99","#a6cee3","#b2df8a","#fdbf6f","#cab2d6"]
31+
options.coloursGlobal = false;
32+
}
2933
let rotation = 0;
3034
const freestyle = parseInt(document.getElementById("rotFree").value, 10);
3135
if (freestyle === 0) {
@@ -51,6 +55,9 @@
5155
labels: labels.value,
5256
annotations: notes.value,
5357
};
58+
if (document.getElementById("chkContextGlobal").checked) {
59+
options.contextGlobal = false;
60+
}
5461
var div = document.getElementById("drawing");
5562
div.style.backgroundColor = bg.value;
5663

@@ -596,6 +603,10 @@ <h1>JSON Input</h1>
596603
<input type="radio" id="fillPatterns" name="playerfill" value="patterns">
597604
<label for="fillPatterns">Black-and-white patterns (10 max)</label>
598605
</div>
606+
<div>
607+
<input type="checkbox" id="chkColoursGlobal">
608+
<label for="chkColoursGlobal">Game-specific customization</label>
609+
</div>
599610
<hr>
600611
<div>
601612
<label for="ccBackground">Background</label>
@@ -616,6 +627,9 @@ <h1>JSON Input</h1>
616627
<div>
617628
<button id="btnLight" type="button">Light Mode</button>
618629
<button id="btnDark" type="button">Dark Mode</button>
630+
&emsp;
631+
<input type="checkbox" id="chkContextGlobal">
632+
<label for="chkContextGlobal">Game-specific customization</label>
619633
</div>
620634
<hr>
621635
<div>

0 commit comments

Comments
 (0)