Skip to content

Commit 0602016

Browse files
author
Oskar Widmark
committed
feat: color gradient for columns
1 parent b9e519c commit 0602016

File tree

7 files changed

+100
-22
lines changed

7 files changed

+100
-22
lines changed

src/App.tsx

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ class App extends React.Component<Props> {
6161
resetPreset: ResetPreset;
6262
algorithmOptions: AlgorithmOptions;
6363
colorPreset: ColorPreset;
64-
columnColor: string;
64+
columnColor1: string;
65+
columnColor2: string;
6566
backgroundColor: string;
6667
highlightColor: string;
6768
};
@@ -101,7 +102,8 @@ class App extends React.Component<Props> {
101102
ref as React.RefObject<HTMLCanvasElement>,
102103
this.state.settings.columnNbr,
103104
this.state.settings.colorPreset,
104-
this.state.settings.columnColor,
105+
this.state.settings.columnColor1,
106+
this.state.settings.columnColor2,
105107
this.state.settings.highlightColor,
106108
);
107109
}
@@ -371,9 +373,16 @@ class App extends React.Component<Props> {
371373
this.canvasController.redraw(this.arr);
372374
};
373375

374-
setColumnColor = (columnColor: string) => {
375-
this.setSettings({ columnColor });
376-
this.canvasController.columnColor = columnColor;
376+
setColumnColor1 = (columnColor1: string) => {
377+
this.setSettings({ columnColor1 });
378+
this.canvasController.columnColor1 = columnColor1;
379+
this.stopSorting();
380+
this.canvasController.redraw(this.arr);
381+
};
382+
383+
setColumnColor2 = (columnColor2: string) => {
384+
this.setSettings({ columnColor2 });
385+
this.canvasController.columnColor2 = columnColor2;
377386
this.stopSorting();
378387
this.canvasController.redraw(this.arr);
379388
};
@@ -394,6 +403,7 @@ class App extends React.Component<Props> {
394403
case ColorPreset.Rainbow:
395404
return RAINBOW_BACKGROUND_COLOR;
396405
case ColorPreset.Custom:
406+
case ColorPreset.CustomGradient:
397407
return this.state.settings.backgroundColor;
398408
}
399409
};
@@ -470,11 +480,13 @@ class App extends React.Component<Props> {
470480
/>
471481
<Colors
472482
colorPreset={this.state.settings.colorPreset}
473-
columnColor={this.state.settings.columnColor}
483+
columnColor1={this.state.settings.columnColor1}
484+
columnColor2={this.state.settings.columnColor2}
474485
backgroundColor={this.state.settings.backgroundColor}
475486
highlightColor={this.state.settings.highlightColor}
476487
setColorPreset={this.setColorPreset}
477-
setColumnColor={this.setColumnColor}
488+
setColumnColor1={this.setColumnColor1}
489+
setColumnColor2={this.setColumnColor2}
478490
setBackgroundColor={this.setBackgroundColor}
479491
setHighlightColor={this.setHighlightColor}
480492
/>

src/Colors.tsx

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ import { ColorPreset } from './types';
1111
export function Colors(props: {
1212
colorPreset: ColorPreset;
1313
setColorPreset: (string: ColorPreset) => void;
14-
columnColor: string;
15-
setColumnColor: (string: string) => void;
14+
columnColor1: string;
15+
setColumnColor1: (string: string) => void;
16+
columnColor2: string;
17+
setColumnColor2: (string: string) => void;
1618
backgroundColor: string;
1719
setBackgroundColor: (string: string) => void;
1820
highlightColor: string;
@@ -21,8 +23,10 @@ export function Colors(props: {
2123
const {
2224
colorPreset,
2325
setColorPreset,
24-
columnColor,
25-
setColumnColor,
26+
columnColor1,
27+
setColumnColor1,
28+
columnColor2,
29+
setColumnColor2,
2630
backgroundColor,
2731
setBackgroundColor,
2832
highlightColor,
@@ -56,16 +60,29 @@ export function Colors(props: {
5660
</MenuItem>
5761
))}
5862
</TextField>
59-
{colorPreset === ColorPreset.Custom && (
63+
{(colorPreset === ColorPreset.Custom ||
64+
colorPreset === ColorPreset.CustomGradient) && (
6065
<Grid2 container spacing={2}>
6166
<TextField
62-
label="Columns"
63-
value={columnColor}
67+
label={
68+
colorPreset === ColorPreset.Custom ? 'Columns' : 'Columns(1)'
69+
}
70+
value={columnColor1}
6471
type="color"
6572
size="small"
6673
sx={{ width: 100 }}
67-
onChange={(e) => setColumnColor(e.target.value)}
74+
onChange={(e) => setColumnColor1(e.target.value)}
6875
/>
76+
{colorPreset === ColorPreset.CustomGradient && (
77+
<TextField
78+
label="Columns(2)"
79+
value={columnColor2}
80+
type="color"
81+
size="small"
82+
sx={{ width: 100 }}
83+
onChange={(e) => setColumnColor2(e.target.value)}
84+
/>
85+
)}
6986
<TextField
7087
label="Background"
7188
value={backgroundColor}

src/Options.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ export function Options({
119119
{algorithmOptionFields.map((field) => (
120120
<FormControl component="fieldset">
121121
<TextField
122+
key={field}
122123
select={ALGORITHM_OPTION_TEXT_FIELD_TYPES[field] === 'select'}
123124
label={ALGORITHM_OPTION_LABELS[field]}
124125
value={nonValidatedOptions[field]}

src/canvas-controller.ts

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { MouseEvent } from 'react';
22
import { ColorPreset, DrawData, SortValue } from './types';
3-
import { hsvToRgbHex } from './utils';
3+
import { hsvToRgbHex, rgbHexToHsv } from './utils';
44

55
export class CanvasController {
66
private prevHighlightIndices: number[] | null = null;
@@ -14,7 +14,8 @@ export class CanvasController {
1414
public canvasRef: React.RefObject<HTMLCanvasElement>,
1515
public columnNbr: number,
1616
public colorPreset: ColorPreset,
17-
public columnColor: string,
17+
public columnColor1: string,
18+
public columnColor2: string,
1819
public highlightColor: string,
1920
) {}
2021

@@ -43,10 +44,40 @@ export class CanvasController {
4344
return context;
4445
}
4546

46-
getColumnColor(value: number) {
47+
getGradientColor(value: number) {
48+
// eslint-disable-next-line prefer-const
49+
let [h1, s1, v1] = rgbHexToHsv(this.columnColor1);
50+
// eslint-disable-next-line prefer-const
51+
let [h2, s2, v2] = rgbHexToHsv(this.columnColor2);
52+
if (h1 === 0 && s1 === 0) {
53+
h1 = h2;
54+
}
55+
if (h2 === 0 && s2 === 0) {
56+
h2 = h1;
57+
}
58+
59+
// Use the shortest path in the hue circle
60+
let hDiff = h2 - h1;
61+
hDiff += hDiff > 180 ? -360 : hDiff < -180 ? 360 : 0;
62+
63+
const sDiff = s2 - s1;
64+
const vDiff = v2 - v1;
65+
const multiplier = value / this.columnNbr;
66+
67+
return hsvToRgbHex(
68+
// TODO: Use additive/subtractive color mixing instead of hue shifting?
69+
(h1 + hDiff * multiplier + 360) % 360,
70+
s1 + sDiff * multiplier,
71+
v1 + vDiff * multiplier,
72+
);
73+
}
74+
75+
getColumnColor1(value: number) {
4776
switch (this.colorPreset) {
4877
case ColorPreset.Custom:
49-
return this.columnColor;
78+
return this.columnColor1;
79+
case ColorPreset.CustomGradient:
80+
return this.getGradientColor(value);
5081
case ColorPreset.Rainbow:
5182
return hsvToRgbHex((360 * value) / this.columnNbr, 1, 1);
5283
}
@@ -55,6 +86,7 @@ export class CanvasController {
5586
getHighlightColor() {
5687
switch (this.colorPreset) {
5788
case ColorPreset.Custom:
89+
case ColorPreset.CustomGradient:
5890
return this.highlightColor;
5991
case ColorPreset.Rainbow:
6092
return '#FFFFFF';
@@ -192,7 +224,7 @@ export class CanvasController {
192224
(this.canvas2dCtx.canvas.height / this.columnNbr) * (arr[i].value + 1);
193225
const startX = width * i;
194226

195-
this.canvas2dCtx.fillStyle = color || this.getColumnColor(arr[i].value);
227+
this.canvas2dCtx.fillStyle = color || this.getColumnColor1(arr[i].value);
196228
this.fillRect(startX, 0, width, height);
197229
};
198230

src/constants.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ export const INIT_SWAP_TIME = 1;
44
export const INIT_COMPARE_TIME = 1;
55
export const INIT_COLUMN_NUMBER = 100;
66
export const POWERS_OF_TWO = [8, 16, 32, 64, 128, 256, 512, 1024];
7-
export const DEFAULT_COLUMN_COLOR = '#ffffff';
7+
export const DEFAULT_COLUMN_COLOR_1 = '#ffffff';
8+
export const DEFAULT_COLUMN_COLOR_2 = '#ffffff';
89
export const DEFAULT_BACKGROUND_COLOR = '#000000';
910
export const DEFAULT_HIGHLIGHT_COLOR = '#ff0000';
1011
export const RAINBOW_BACKGROUND_COLOR = '#282c34';
@@ -30,7 +31,8 @@ export const INIT_SETTINGS = {
3031
resetPreset: ResetPreset.Shuffle,
3132
algorithmOptions: DEFAULT_ALGORITHM_OPTIONS,
3233
colorPreset: ColorPreset.Rainbow,
33-
columnColor: DEFAULT_COLUMN_COLOR,
34+
columnColor1: DEFAULT_COLUMN_COLOR_1,
35+
columnColor2: DEFAULT_COLUMN_COLOR_2,
3436
backgroundColor: DEFAULT_BACKGROUND_COLOR,
3537
highlightColor: DEFAULT_HIGHLIGHT_COLOR,
3638
};

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,5 @@ export enum SortType {
5050
export enum ColorPreset {
5151
Rainbow = 'Rainbow',
5252
Custom = 'Custom',
53+
CustomGradient = 'Custom (gradient)',
5354
}

src/utils.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,19 @@ export function hsvToRgbHex(h: number, s: number, v: number) {
1212
);
1313
}
1414

15+
export function rgbHexToHsv(hex: string): [number, number, number] {
16+
const r = parseInt(hex.slice(1, 3), 16) / 255;
17+
const g = parseInt(hex.slice(3, 5), 16) / 255;
18+
const b = parseInt(hex.slice(5, 7), 16) / 255;
19+
20+
const v = Math.max(r, g, b);
21+
const c = v - Math.min(r, g, b);
22+
// ?
23+
const h =
24+
c && (v == r ? (g - b) / c : v == g ? 2 + (b - r) / c : 4 + (r - g) / c);
25+
return [60 * (h < 0 ? h + 6 : h), v && c / v, v];
26+
}
27+
1528
export function shuffleArray(arr: unknown[]) {
1629
for (let i = arr.length - 1; i > 0; i--) {
1730
const j = Math.floor(Math.random() * (i + 1));

0 commit comments

Comments
 (0)