From 6a4de8d05ce46dc29ac69f696c419a57e604f516 Mon Sep 17 00:00:00 2001 From: Sophie Zhang Date: Thu, 16 Nov 2023 01:30:42 -0500 Subject: sorting? --- src/client/views/ExtractColors.ts | 111 +++++++++++++++++++++ src/client/views/PropertiesView.tsx | 5 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 3 +- 3 files changed, 116 insertions(+), 3 deletions(-) (limited to 'src/client/views') 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 { ? this.generatedStyles.map((style, i) => (
this.styleCollection(i)}>
- {style.documentsWithColors.map(c => ( -
+ {ExtractColors.sortColors(style.documentsWithColors.map(doc => ExtractColors.hexToFinalColor(doc.color))).map(c => ( +
))}
)) 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 { 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 ({ id: i, -- cgit v1.2.3-70-g09d2