Various color picker improvements

This commit is contained in:
barsdeveloper
2022-10-22 23:06:37 +02:00
parent 14d376d447
commit acea290c9d
23 changed files with 562 additions and 279 deletions

View File

@@ -17,42 +17,42 @@ export default class IEntity extends Observable {
super()
/**
* @param {Object} target
* @param {Object} properties
* @param {Object} attributes
* @param {Object} values
* @param {String} prefix
*/
const defineAllAttributes = (target, properties, values, prefix = "") => {
for (let property of Utility.mergeArrays(
Object.getOwnPropertyNames(properties),
const defineAllAttributes = (target, attributes, values, prefix = "") => {
for (let attribute of Utility.mergeArrays(
Object.getOwnPropertyNames(attributes),
Object.getOwnPropertyNames(values ?? {})
)) {
let value = Utility.objectGet(values, [property])
let defaultValue = properties[property]
let value = Utility.objectGet(values, [attribute])
let defaultValue = attributes[attribute]
let defaultType = Utility.getType(defaultValue)
if (defaultValue instanceof CalculatedType) {
defaultValue = defaultValue.calculate(this)
defaultType = Utility.getType(defaultValue)
}
if (!(property in properties)) {
if (!(attribute in attributes)) {
console.warn(
`Property ${prefix}${property} in the serialized data is not defined in ${this.constructor.name}.properties`
`Attribute ${prefix}${attribute} in the serialized data is not defined in ${this.constructor.name}.attributes`
)
} else if (
!(property in values)
!(attribute in values)
&& defaultValue !== undefined
&& !(defaultValue instanceof TypeInitialization && !defaultValue.showDefault)
) {
console.warn(
`${this.constructor.name}.properties will add property ${prefix}${property} not defined in the serialized data`
`${this.constructor.name} will add attribute ${prefix}${attribute} not defined in the serialized data`
)
}
// Not instanceof because all objects are instenceof Object, exact match needed
// @ts-expect-error
if (defaultType === Object) {
target[property] = {}
defineAllAttributes(target[property], properties[property], values[property], property + ".")
target[attribute] = {}
defineAllAttributes(target[attribute], attributes[attribute], values[attribute], attribute + ".")
continue
}
@@ -67,13 +67,13 @@ export default class IEntity extends Observable {
// @ts-expect-error
value = SerializerFactory.getSerializer(defaultValue.type).deserialize(value)
}
target[property] = TypeInitialization.sanitize(value, Utility.getType(defaultValue))
target[attribute] = TypeInitialization.sanitize(value, Utility.getType(defaultValue))
continue // We have a value, need nothing more
}
if (defaultValue instanceof TypeInitialization) {
if (!defaultValue.showDefault) {
target[property] = undefined // Declare undefined to preserve the order of attributes
target[attribute] = undefined // Declare undefined to preserve the order of attributes
continue
}
if (defaultValue.serialized) {
@@ -82,12 +82,15 @@ export default class IEntity extends Observable {
// @ts-expect-error
defaultType = defaultValue.type
defaultValue = defaultValue.value
if (defaultValue instanceof Function) {
defaultValue = defaultValue()
}
}
}
if (defaultValue instanceof Array) {
defaultValue = []
}
target[property] = TypeInitialization.sanitize(defaultValue, defaultType)
target[attribute] = TypeInitialization.sanitize(defaultValue, defaultType)
}
}
// @ts-expect-error

View File

@@ -1,6 +1,7 @@
import IEntity from "./IEntity"
import Utility from "../Utility"
import RealUnitEntity from "./UnitRealEntity"
import TypeInitialization from "./TypeInitialization"
import Utility from "../Utility"
export default class LinearColorEntity extends IEntity {
@@ -8,36 +9,10 @@ export default class LinearColorEntity extends IEntity {
R: RealUnitEntity,
G: RealUnitEntity,
B: RealUnitEntity,
A: new RealUnitEntity(1),
}
static fromWheelLocation([x, y], radius, v, a) {
x -= radius
y -= radius
const [r, theta] = Utility.getPolarCoordinates([x, y], true)
return LinearColorEntity.fromHSVA([
1 - theta / (2 * Math.PI),
r / radius,
v,
a,
])
}
/** @param {Number[]} param0 */
static fromHSVA([h, s, v, a = 1]) {
const i = Math.floor(h * 6)
const f = h * 6 - i
const p = v * (1 - s)
const q = v * (1 - f * s)
const t = v * (1 - (1 - f) * s)
const values = [v, q, p, p, t, v]
const [r, g, b] = [values[i % 6], values[(i + 2) % 6], values[(i + 4) % 6]]
return new LinearColorEntity({
R: r,
G: g,
B: b,
A: a,
})
A: new TypeInitialization(RealUnitEntity, true, () => new RealUnitEntity(1), false, true),
H: new TypeInitialization(RealUnitEntity, true, undefined, false, true),
S: new TypeInitialization(RealUnitEntity, true, undefined, false, true),
V: new TypeInitialization(RealUnitEntity, true, undefined, false, true),
}
constructor(options = {}) {
@@ -46,25 +21,27 @@ export default class LinearColorEntity extends IEntity {
/** @type {RealUnitEntity} */ this.G
/** @type {RealUnitEntity} */ this.B
/** @type {RealUnitEntity} */ this.A
/** @type {RealUnitEntity} */ this.H
/** @type {RealUnitEntity} */ this.S
/** @type {RealUnitEntity} */ this.V
this.#updateValues()
}
toRGBA() {
return [
Math.round(this.R.value * 255),
Math.round(this.G.value * 255),
Math.round(this.B.value * 255),
Math.round(this.A.value * 255),
]
}
toHSVA() {
const [r, g, b, a] = [this.R.value, this.G.value, this.B.value, this.A.value]
#updateValues() {
const r = this.R.value
const g = this.G.value
const b = this.B.value
if (
!(Math.abs(r - g) > Number.EPSILON)
&& !(Math.abs(r - b) > Number.EPSILON)
&& !(Math.abs(g - b) > Number.EPSILON)
) {
return
}
const max = Math.max(r, g, b)
const min = Math.min(r, g, b)
const d = max - min
let h
const s = (max == 0 ? 0 : d / max)
const v = max
switch (max) {
case min:
h = 0
@@ -80,11 +57,67 @@ export default class LinearColorEntity extends IEntity {
break
}
h /= 6
return [new RealUnitEntity(h), new RealUnitEntity(s), new RealUnitEntity(v), new RealUnitEntity(a)]
this.H.value = h
this.S.value = max == 0 ? 0 : d / max
this.V.value = max
}
/** @param {Number[]} param0 */
setFromHSVA([h, s, v, a = 1]) {
const i = Math.floor(h * 6)
const f = h * 6 - i
const p = v * (1 - s)
const q = v * (1 - f * s)
const t = v * (1 - (1 - f) * s)
const values = [v, q, p, p, t, v]
const [r, g, b] = [values[i % 6], values[(i + 4) % 6], values[(i + 2) % 6]]
this.R.value = r
this.G.value = g
this.B.value = b
this.A.value = a
this.H.value = h
this.S.value = s
this.V.value = v
}
setFromWheelLocation([x, y], v, a) {
const [r, theta] = Utility.getPolarCoordinates([x, y], true)
this.setFromHSVA([
1 - theta / (2 * Math.PI),
r,
v,
a,
])
}
toRGBA() {
return [
Math.round(this.R.value * 255),
Math.round(this.G.value * 255),
Math.round(this.B.value * 255),
Math.round(this.A.value * 255),
]
}
toRGBAString() {
return this.toRGBA().map(v => v.toString(16).padStart(2, "0")).join("")
}
toHSVA() {
const r = this.R.value
const g = this.G.value
const b = this.B.value
const a = this.A.value
const max = Math.max(r, g, b)
const min = Math.min(r, g, b)
const d = max - min
const s = (max == 0 ? 0 : d / max)
const v = max
return [this.H.value, s, v, a]
}
toNumber() {
return (this.R.value * 0xff << 3 * 0x8) + (this.G.value * 0xff << 2 * 0x8) + (this.B.value * 0xff << 0x8) + this.A.value
return (this.R.value << 24) + (this.G.value << 16) + (this.B.value << 8) + this.A.value
}
toString() {

View File

@@ -32,7 +32,7 @@ export default class TypeInitialization {
this.#showDefault = v
}
/** @type {T | T[] | String} */
/** @type {T | T[] | String | (() => T) | (() => T[])} */
#value
get value() {
return this.#value
@@ -50,6 +50,14 @@ export default class TypeInitialization {
this.#serialized = v
}
#ignored
get ignored() {
return this.#ignored
}
set ignored(v) {
this.#ignored = v
}
static sanitize(value, targetType) {
if (targetType === undefined) {
targetType = value?.constructor
@@ -70,22 +78,23 @@ export default class TypeInitialization {
/**
* @param {AnyValueConstructor<T>|AnyValueConstructor<T>[]} type
* @param {Boolean} showDefault
* @param {T | T[] | String} value
* @param {T | T[] | String | (() => T) | (() => T[])} value
* @param {Boolean} serialized
*/
constructor(type, showDefault = true, value = undefined, serialized = false) {
constructor(type, showDefault = true, value = undefined, serialized = false, ignored = false) {
if (value === undefined) {
if (type instanceof Array) {
value = []
} else if (serialized) {
value = ""
} else {
value = TypeInitialization.sanitize(new type())
value = () => TypeInitialization.sanitize(new type())
}
}
this.#type = type
this.#showDefault = showDefault
this.#value = value
this.#serialized = serialized
this.#ignored = ignored
}
}

View File

@@ -4,7 +4,7 @@ import Utility from "../Utility"
export default class RealUnitEntity extends IEntity {
static attributes = {
value: Number,
value: 0,
}
/** @param {Object | Number | String} options */