Skip to content

Commit

Permalink
some lint improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
mcnuttandrew committed Jan 24, 2024
1 parent 9f0b220 commit feef98d
Show file tree
Hide file tree
Showing 19 changed files with 338 additions and 66 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ First time you start it up you should also run `yarn prep data`

# Todo bankruptcy

- [ ] per cols 4 all: color blindness metric should maybe be sensitive to task?
- [ ] Minor: Make keyboard short cut (option+up/down) for the z-direction
- [ ] Chore: Extract some common types into a top level location (like types.ts type thing)
- [ ] Insert color theory options, eg insert opposing, inserting analogous color, etc, mine from the adobe picker
- [ ] Labels, tooltips, etc
- [ ] Nice to have: Rest of basic geometry manipulations: flip (horizontal, vertical), scale, Distribute radially
- [ ] For the palettes, a couple of ideas. Right now, feels like selecting a palette in the list reorders all the palettes, which is disorienting. I realize what you're doing in removing the palette I clicked on, adding the one I was editing. I think don't do this. Keep a fixed order (which I could change if I wanted to), and simply mark the palette that's being editing, don't remove it from the list. Similarly, if I copy a palette, put it right after the palette I copied, not up at the top. I also think adding a compact view of the palettes will eventually be necessary, so now might be the time to do it. Even as a default, would make the circles a bit smaller and pack them tighter. Or maybe make them tall rectangles that would pack even tighter.
- [ ] For the palettes, there is a copy and delete bug. Try this: Copy Example 2, then immediately click delete (menu is still up). Surpise!
- [ ] Tool tip not staying put
- [ ] Bug: Color channel usage slightly cursed (doesn't update positions correctly)
Expand All @@ -27,7 +27,8 @@ First time you start it up you should also run `yarn prep data`
- [ ] XYY is probaably now possible
- [ ] Bug: rotate in polar coordinates doesn't work right
- [ ] Ad hoc lints seem possible, do a spike
- [ ] Directional subtlies for aligns
- [ ] Directional subtlies for aligns, they do not work in polar also
- [x] For the palettes, a couple of ideas. Right now, feels like selecting a palette in the list reorders all the palettes, which is disorienting. I realize what you're doing in removing the palette I clicked on, adding the one I was editing. I think don't do this. Keep a fixed order (which I could change if I wanted to), and simply mark the palette that's being editing, don't remove it from the list. Similarly, if I copy a palette, put it right after the palette I copied, not up at the top. I also think adding a compact view of the palettes will eventually be necessary, so now might be the time to do it. Even as a default, would make the circles a bit smaller and pack them tighter. Or maybe make them tall rectangles that would pack even tighter.
- [x] Bug report. If I am comparing a palette with itself and select a color in the main view, the color is not selected in the xy plot of the comparison panel. It is selected in the vertical only plot
- [x] Similarly, would like to work through the various options for the examples. Delete, Edit, Hide (not Mute) seem good. If you're going to Hide, then ideally you provide a selective unhide (dropdown of names is the easiest) as well as the unhide all. Your Solo means "hide everything else" which I'm not convinced is worth a button. However, a "full screen" or "fit width" "fit height" options that scales it up and reduces the rest to thumbnails might be cool. And I'm sure there are a few more we'll discover as we work with it.
- [x] Albers color theory games style examples
Expand Down
23 changes: 23 additions & 0 deletions src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@
$: currentPal = $colorStore.palettes[$colorStore.currentPal];
const tabs = ["examples", "compare", "eval"];
const descriptions = {
sequential:
"Sequential palettes are used to represent a range of values. They are often used to represent quantitative data, such as temperature or elevation.",
diverging:
"Diverging palettes are used to represent a range of values around a central point. They are often used to represent quantitative data, such as temperature or elevation.",
categorical:
"Categorical palettes are used to represent a set of discrete values. They are often used to represent qualitative data, such as different types of land cover or different political parties.",
};
$: palType = currentPal.type;
</script>

<main class="flex h-full">
Expand Down Expand Up @@ -101,6 +110,20 @@
allowModification={true}
/>
<GetColorsFromString />
<div class="max-w-lg">
This is a <select
value={palType}
on:change={(e) => {
// @ts-ignore
colorStore.setCurrentPalType(e.target.value);
}}
>
{#each ["sequential", "diverging", "categorical"] as type}
<option value={type}>{type}</option>
{/each}
</select>
palette. {descriptions[palType]}
</div>
</div>
</div>
</div>
Expand Down
28 changes: 1 addition & 27 deletions src/content-modules/Eval.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@
$: colorNames = colorNameSimple(colors);
$: palType = currentPal.type;
$: evalConfig = currentPal.evalConfig;
$: checks = runLintChecks(currentPal).filter((x) =>
x.taskTypes.includes(palType)
);
$: checks = runLintChecks(currentPal, palType);
$: checkGroups = checks.reduce(
(acc, check) => {
Expand Down Expand Up @@ -56,15 +54,6 @@
.split(" ")
.map((x) => x[0].toUpperCase() + x.slice(1))
.join(" ");
const descriptions = {
sequential:
"Sequential palettes are used to represent a range of values. They are often used to represent quantitative data, such as temperature or elevation.",
diverging:
"Diverging palettes are used to represent a range of values around a central point. They are often used to represent quantitative data, such as temperature or elevation.",
categorical:
"Categorical palettes are used to represent a set of discrete values. They are often used to represent qualitative data, such as different types of land cover or different political parties.",
};
</script>

<div class="flex h-full">
Expand Down Expand Up @@ -157,21 +146,6 @@
</div>
</div>
<div class="flex flex-col ml-2">
<div>
This is a <select
value={palType}
on:change={(e) => {
// @ts-ignore
colorStore.setCurrentPalType(e.target.value);
}}
>
{#each ["sequential", "diverging", "categorical"] as type}
<option value={type}>{type}</option>
{/each}
</select>
palette. {descriptions[palType]}
</div>

<div class="overflow-auto h-full max-w-md">
{#each Object.entries(checkGroups) as checkGroup}
<div class="flex mt-5">
Expand Down
49 changes: 41 additions & 8 deletions src/content-modules/contextual-tools/EvalResponse.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,22 @@
$: suggestion = false as false | Palette;
function proposeFix() {
function proposeFix(useAi: boolean = false) {
requestState = "loading";
let hasRetried = false;
const getFix = () =>
check.suggestFix($configStore.engine).then((x) => {
suggestion = x;
requestState = "loaded";
});
const getFix = () => {
if (useAi) {
return check.suggestAIFix().then((x) => {
suggestion = x;
requestState = "loaded";
});
} else {
return check.suggestFix().then((x) => {
suggestion = x;
requestState = "loaded";
});
}
};
getFix().catch((e) => {
console.log(e);
Expand Down Expand Up @@ -49,13 +57,38 @@
[checkName]: { ...evalConfig[checkName], val },
});
}
function activateCB() {
const match = options.find((x) => check.name.includes(x));
if (match) {
configStore.setColorSim(match);
}
}
const options = ["deuteranopia", "protanopia", "tritanopia"] as const;
$: cbMatch = options.find((x) =>
check.name.includes(x)
) as (typeof options)[number];
</script>

<Tooltip positionAlongRightEdge={true}>
<div slot="content" let:onClick>
<button class={buttonStyle} on:click={() => proposeFix()}>
Try to fix
{#if cbMatch}
<button
class={buttonStyle}
on:click={() => configStore.setColorSim(cbMatch)}
>
Activate {cbMatch} simulator
</button>
{/if}
<button class={buttonStyle} on:click={() => proposeFix(true)}>
Try to fix (AI)
</button>
{#if check.hasHeuristicFix}
<button class={buttonStyle} on:click={() => proposeFix(false)}>
Try to fix (hueristics)
</button>
{/if}

<button
class={buttonStyle}
on:click={() => {
Expand Down
43 changes: 29 additions & 14 deletions src/lib/linter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,34 @@ import SequentialOrder from "./lints/sequential-order";
import AvoidExtremes from "./lints/avoid-extremes";
import DivergingOrder from "./lints/diverging-order";
import BackgroundContrast from "./lints/contrast";
import Fair from "./lints/fair";
import EvenDistribution from "./lints/even-distribution";

export function runLintChecks(palette: Palette): ColorLint<any, any>[] {
return [
new NameDiscrim(palette),
new MaxColors(palette),
...Discrims.map((x) => new x(palette)),
...Blinds.map((x) => new x(palette)),
new ColorSimilarity(palette),
new BackgroundDifferentiability(palette),
new UglyColors(palette),
new SequentialOrder(palette),
new AvoidExtremes(palette),
new DivergingOrder(palette),
new BackgroundContrast(palette),
].map((x) => x.run());
export function runLintChecks(
palette: Palette,
palType: any
): ColorLint<any, any>[] {
return (
[
NameDiscrim,
MaxColors,
...Discrims,
...Blinds,
ColorSimilarity,
BackgroundDifferentiability,
UglyColors,
SequentialOrder,
AvoidExtremes,
DivergingOrder,
BackgroundContrast,
...Fair,
EvenDistribution,
] as (typeof ColorLint)[]
)
.map((x) => new x(palette))
.filter((x) => x.taskTypes.includes(palType))
.map((x) => x.run());
}

// typesript type for an list of classes
// https://stackoverflow.com/questions/57465358/typescript-type-for-an-list-of-classes
5 changes: 5 additions & 0 deletions src/lib/lints/ColorLint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export class ColorLint<CheckData, ParamType> {
palette: Palette;
message: string = "";
hasParam: boolean = false;
hasHeuristicFix: boolean = false;
config: { val?: ParamType } = {};
defaultParam: ParamType = false as any;
group: string = "";
Expand All @@ -51,6 +52,7 @@ export class ColorLint<CheckData, ParamType> {
copy.hasParam = this.hasParam;
copy.config = this.config;
copy.defaultParam = this.defaultParam;
copy.hasHeuristicFix = this.hasHeuristicFix;
copy.level = this.level;
return copy;
}
Expand Down Expand Up @@ -80,4 +82,7 @@ export class ColorLint<CheckData, ParamType> {
async suggestFix(engine?: string) {
return AIFix(this.palette, this.message, engine || "openai");
}
async suggestAIFix(engine?: string) {
return AIFix(this.palette, this.message, engine || "openai");
}
}
1 change: 1 addition & 0 deletions src/lib/lints/avoid-extremes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default class ExtremeColors extends ColorLint<Color[], false> {
level: "error" | "warning" = "warning";
group = "aesthetics";
description = `Colors at either end of the lightness spectrum can be hard to discriminate in some contexts, and are sometimes advised against.`;
hasHeuristicFix = true;

_runCheck() {
const { colors } = this.palette;
Expand Down
1 change: 1 addition & 0 deletions src/lib/lints/background-differentiability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export default class BackgroundDifferentiability extends ColorLint<
const str = hexJoin(this.checkData.map((x) => colors[x]));
return `This palette has some colors (${str}) that are close to the background color`;
}
hasHeuristicFix = true;
async suggestFix() {
const { colors, background, colorSpace } = this.palette;
const backgroundL = background.toColorIO().to("lab").coords[0];
Expand Down
12 changes: 7 additions & 5 deletions src/lib/lints/color-similarity.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { ColorLint } from "./ColorLint";
import type { TaskType } from "./ColorLint";
import { computeStats } from "../color-stats";

export default class ColorSimilarity extends ColorLint<number[], number> {
name = "Colors are differentiable in order (dE units)";
name = "Colors are differentiable in order";
taskTypes = ["sequential", "diverging", "categorical"] as TaskType[];
hasParam = true;
defaultParam: number = 10;
Expand All @@ -17,9 +16,12 @@ export default class ColorSimilarity extends ColorLint<number[], number> {
};
_runCheck() {
const { colors } = this.palette;
const data = computeStats(colors, "dE");
const des = [];
for (let i = 0; i < colors.length - 1; i++) {
des.push(colors[i].deltaE(colors[i + 1]));
}
const failingIndexes =
data?.dE.reduce((acc, x, idx) => {
des.reduce((acc, x, idx) => {
if (x < this.config.val!) {
return [...acc, idx];
}
Expand All @@ -37,6 +39,6 @@ export default class ColorSimilarity extends ColorLint<number[], number> {
return `(${a} ${b})`;
})
.join(", ");
return `Some colors are too similar based on dE scores: ${pairs}`;
return `Some colors are too similar based on dE scores: ${pairs}. Try reordering them or making them more distinguishable.`;
}
}
5 changes: 1 addition & 4 deletions src/lib/lints/contrast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ export default class BackgroundContrast extends ColorLint<Color[], Algorithm> {
}
buildMessage(): string {
const colors = this.checkData?.map((x) => x.toHex());
return `These colors (${colors}) do not have a sufficient contrast ratio with the background and may be hard to discriminate in some contexts`;
}
getOptions() {
return this.paramOptions.options;
return `These colors (${colors}) do not have a sufficient contrast ratio with the background and may be hard to discriminate in some contexts. Contrast is calculated using the ${this.config.val} algorithm.`;
}
}
1 change: 1 addition & 0 deletions src/lib/lints/diverging-order.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export default class SequentialOrder extends ColorLint<boolean, false> {
buildMessage(): string {
return `This pal should have a middle color that is the lightest or darkest color`;
}
hasHeuristicFix = true;
async suggestFix() {
// figure out if its centered on a light color or a dark color?
// a dumb hueristic is just look at what the center color is in lab space, and see if its darker or lighter than most colors
Expand Down
Loading

0 comments on commit feef98d

Please sign in to comment.