aboutsummaryrefslogtreecommitdiff
path: root/src/client/views
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views')
-rw-r--r--src/client/views/ExtractColors.ts111
-rw-r--r--src/client/views/PropertiesView.tsx5
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx3
3 files changed, 116 insertions, 3 deletions
diff --git a/src/client/views/ExtractColors.ts b/src/client/views/ExtractColors.ts
index f78d9a355..f6928c52a 100644
--- a/src/client/views/ExtractColors.ts
+++ b/src/client/views/ExtractColors.ts
@@ -1,4 +1,5 @@
import { extractColors } from 'extract-colors';
+import { FinalColor } from 'extract-colors/lib/types/Color';
// Manages image color extraction
export class ExtractColors {
@@ -54,4 +55,114 @@ export class ExtractColors {
const colors = await extractColors(img, { distance: 0.35 });
return colors;
};
+
+ static simpleSort = (colors: FinalColor[]): FinalColor[] => {
+ colors.sort((a, b) => {
+ if (a.hue !== b.hue) {
+ return b.hue - a.hue;
+ } else {
+ return b.saturation - a.saturation;
+ }
+ });
+ return colors;
+ };
+
+ static sortColors(colors: FinalColor[]): FinalColor[] {
+ // Convert color from RGB to CIELAB format
+ const convertToLab = (color: FinalColor): number[] => {
+ const r = color.red / 255;
+ const g = color.green / 255;
+ const b = color.blue / 255;
+
+ const x = r * 0.4124564 + g * 0.3575761 + b * 0.1804375;
+ const y = r * 0.2126729 + g * 0.7151522 + b * 0.072175;
+ const z = r * 0.0193339 + g * 0.119192 + b * 0.9503041;
+
+ const pivot = 0.008856;
+ const factor = 903.3;
+
+ const fx = x > pivot ? Math.cbrt(x) : (factor * x + 16) / 116;
+ const fy = y > pivot ? Math.cbrt(y) : (factor * y + 16) / 116;
+ const fz = z > pivot ? Math.cbrt(z) : (factor * z + 16) / 116;
+
+ const L = 116 * fy - 16;
+ const a = (fx - fy) * 500;
+ const b1 = (fy - fz) * 200;
+
+ return [L, a, b1];
+ };
+
+ // Sort colors using CIELAB distance for smooth transitions
+ colors.sort((colorA, colorB) => {
+ const labA = convertToLab(colorA);
+ const labB = convertToLab(colorB);
+
+ // Calculate Euclidean distance in CIELAB space
+ const distanceA = Math.sqrt(Math.pow(labA[0] - labB[0], 2) + Math.pow(labA[1] - labB[1], 2) + Math.pow(labA[2] - labB[2], 2));
+
+ const distanceB = Math.sqrt(Math.pow(labB[0] - labA[0], 2) + Math.pow(labB[1] - labA[1], 2) + Math.pow(labB[2] - labA[2], 2));
+
+ return distanceA - distanceB; // Sort by CIELAB distance
+ });
+
+ return colors;
+ }
+
+ static hexToFinalColor = (hex: string): FinalColor => {
+ const rgb = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+
+ if (!rgb) {
+ throw new Error('Invalid hex color format.');
+ }
+
+ const red = parseInt(rgb[1], 16);
+ const green = parseInt(rgb[2], 16);
+ const blue = parseInt(rgb[3], 16);
+
+ const max = Math.max(red, green, blue);
+ const min = Math.min(red, green, blue);
+ const area = max - min;
+ const intensity = (max + min) / 2;
+
+ let hue = 0;
+ let saturation = 0;
+ let lightness = intensity;
+
+ if (area !== 0) {
+ saturation = area / (1 - Math.abs(2 * intensity - 1));
+ if (max === red) {
+ hue = (60 * ((green - blue) / area) + 360) % 360;
+ } else if (max === green) {
+ hue = (60 * ((blue - red) / area) + 120) % 360;
+ } else {
+ hue = (60 * ((red - green) / area) + 240) % 360;
+ }
+ }
+
+ return {
+ hex,
+ red,
+ green,
+ blue,
+ area,
+ hue,
+ saturation,
+ lightness,
+ intensity,
+ };
+ };
}
+
+// for reference
+
+// type FinalColor = {
+// hex: string;
+// red: number;
+// green: number;
+// blue: number;
+// area: number;
+// hue: number;
+// saturation: number;
+// lightness: number;
+// intensity: number;
+// }
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index fb2d811f4..86784d467 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -41,6 +41,7 @@ import './PropertiesView.scss';
import { DefaultStyleProvider } from './StyleProvider';
import { FaFillDrip } from 'react-icons/fa';
import { GeneratedResponse } from '../apis/gpt/customization';
+import { ExtractColors } from './ExtractColors';
const _global = (window /* browser */ || global) /* node */ as any;
interface PropertiesViewProps {
@@ -1191,8 +1192,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
? this.generatedStyles.map((style, i) => (
<div className="propertiesView-palette" style={{ display: 'flex', gap: '4px', backgroundColor: this.selectedStyle === i ? StrCast(Doc.UserDoc().userVariantColor) : '#00000000' }} onClick={() => this.styleCollection(i)}>
<div style={{ width: '24px', height: '24px', backgroundColor: style.collectionBackgroundColor, borderRadius: '2px' }}></div>
- {style.documentsWithColors.map(c => (
- <div style={{ width: '24px', height: '24px', backgroundColor: c.color, borderRadius: '2px' }}></div>
+ {ExtractColors.sortColors(style.documentsWithColors.map(doc => ExtractColors.hexToFinalColor(doc.color))).map(c => (
+ <div style={{ width: '24px', height: '24px', backgroundColor: c.hex, borderRadius: '2px' }}></div>
))}
</div>
))
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index fb2de5647..b0be060bd 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1820,10 +1820,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
gptStyling = async () => {
this.openProperties();
+ PropertiesView.Instance?.setGeneratedStyles([]);
console.log('Title', this.rootDoc.title);
console.log('bgcolor', this.layoutDoc._backgroundColor);
// doc.backgroundColor
- console.log('styling');
const inputDocs = this.childDocs.filter(doc => doc.type == 'rich text');
const imgDocs = this.childDocs.filter(doc => doc.type == 'image');
const imgUrls = imgDocs.map(doc => this.choosePath((doc.data as ImageField).url));
@@ -1837,6 +1837,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
console.log('Hexes', colorHexes);
PropertiesView.Instance?.setInputDocs(inputDocs);
+
// also pass it colors
const gptInput = inputDocs.map((doc, i) => ({
id: i,