mirror of
https://github.com/barsdeveloper/ueblueprint.git
synced 2026-02-20 05:24:52 +08:00
Color picker refactoring
This commit is contained in:
@@ -240,6 +240,22 @@ export default class Utility {
|
||||
|
||||
/** @param {LinearColorEntity} value */
|
||||
static printLinearColor(value) {
|
||||
return `${Math.round(value.R * 255)}, ${Math.round(value.G * 255)}, ${Math.round(value.B * 255)}`
|
||||
return `${Math.round(value.R.valueOf() * 255)}, ${Math.round(value.G.valueOf() * 255)}, ${Math.round(value.B.valueOf() * 255)}`
|
||||
}
|
||||
|
||||
/** @param {[Number, Number]} param0 */
|
||||
static getPolarCoordinates([x, y]) {
|
||||
return [
|
||||
Math.sqrt(x * x + y * y),
|
||||
Math.atan2(y, x),
|
||||
]
|
||||
}
|
||||
|
||||
/** @param {[Number, Number]} param0 */
|
||||
static getCartesianCoordinates([r, theta]) {
|
||||
return [
|
||||
r * Math.cos(theta),
|
||||
r * Math.sin(theta)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import IDraggableElement from "./IDraggableElement"
|
||||
* @typedef {import("./WindowElement").default<T>} WindowElement
|
||||
*/
|
||||
|
||||
/** @extends {IDraggableElement<Object, ColorHandlerTemplate>} */
|
||||
export default class ColorHandlerElement extends IDraggableElement {
|
||||
|
||||
/** @type {WindowElement<ColorPickerWindowTemplate>} */
|
||||
@@ -23,10 +24,8 @@ export default class ColorHandlerElement extends IDraggableElement {
|
||||
}
|
||||
|
||||
/** @param {Number[]} param0 */
|
||||
addLocation([x, y]) {
|
||||
super.addLocation([x, y])
|
||||
this.windowElement.windowOptions
|
||||
this.windowElement.template.color = this.computeColor()
|
||||
setLocation([x, y]) {
|
||||
super.setLocation(this.template.adjustLocation([x, y]))
|
||||
}
|
||||
|
||||
computeColor() {
|
||||
|
||||
@@ -91,6 +91,7 @@ export default class IElement extends LitElement {
|
||||
this.template.inputSetup()
|
||||
}
|
||||
|
||||
/** @param {Map<String, String>} */
|
||||
updated(changedProperties) {
|
||||
super.updated(changedProperties)
|
||||
this.template.updated(changedProperties)
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import Utility from "../Utility"
|
||||
import IntegerEntity from "./IntegerEntity"
|
||||
|
||||
export default class ColorChannelRealValueEntity extends IntegerEntity {
|
||||
|
||||
toString() {
|
||||
return (this.value / 255).toFixed(6)
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import Utility from "../Utility"
|
||||
import IntegerEntity from "./IntegerEntity"
|
||||
|
||||
export default class ColorChannelValueEntity extends IntegerEntity {
|
||||
|
||||
static attributes = {
|
||||
value: Number,
|
||||
}
|
||||
|
||||
/** @param {Object | Number | String} options */
|
||||
constructor(options = 0) {
|
||||
super(options)
|
||||
this.value = Utility.clamp(this.value, 0, 255)
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,56 @@
|
||||
import ColorChannelRealValueEntity from "./ColorChannelRealValueEntity"
|
||||
import IEntity from "./IEntity"
|
||||
import Utility from "../Utility"
|
||||
import RealUnitEntity from "./UnitRealEntity"
|
||||
|
||||
export default class LinearColorEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
R: ColorChannelRealValueEntity,
|
||||
G: ColorChannelRealValueEntity,
|
||||
B: ColorChannelRealValueEntity,
|
||||
A: ColorChannelRealValueEntity,
|
||||
R: RealUnitEntity,
|
||||
G: RealUnitEntity,
|
||||
B: RealUnitEntity,
|
||||
A: new RealUnitEntity(1),
|
||||
}
|
||||
|
||||
static fromWheelLocation([x, y], radius) {
|
||||
x -= radius
|
||||
y -= radius
|
||||
const mod = Math.sqrt(x * x + y * y)
|
||||
const [r, theta] = Utility.getPolarCoordinates([x, y])
|
||||
return LinearColorEntity.fromHSV([-theta, r])
|
||||
}
|
||||
|
||||
/** @param {Number[]} param0 */
|
||||
static fromHSV([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]]
|
||||
return new LinearColorEntity({
|
||||
R: r,
|
||||
G: g,
|
||||
B: b,
|
||||
A: a,
|
||||
})
|
||||
}
|
||||
|
||||
constructor(options = {}) {
|
||||
super(options)
|
||||
/** @type {ColorChannelRealValueEntity} */ this.R
|
||||
/** @type {ColorChannelRealValueEntity} */ this.G
|
||||
/** @type {ColorChannelRealValueEntity} */ this.B
|
||||
/** @type {ColorChannelRealValueEntity} */ this.A
|
||||
/** @type {RealUnitEntity} */ this.R
|
||||
/** @type {RealUnitEntity} */ this.G
|
||||
/** @type {RealUnitEntity} */ this.B
|
||||
/** @type {RealUnitEntity} */ this.A
|
||||
}
|
||||
|
||||
toRGBA() {
|
||||
return [this.R, this.G, this.B, this.A]
|
||||
return [this.R.value * 255, this.G.value * 255, this.B.value * 255, this.A.value * 255]
|
||||
}
|
||||
|
||||
toHSV() {
|
||||
const max = Math.max(this.R.value, this.G.value, this.B.value)
|
||||
const min = Math.min(this.R.value, this.G.value, this.B.value)
|
||||
const [r, g, b, a] = this.toRGBA()
|
||||
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)
|
||||
@@ -40,14 +59,14 @@ export default class LinearColorEntity extends IEntity {
|
||||
case min:
|
||||
h = 0
|
||||
break
|
||||
case this.R.value:
|
||||
h = (this.G.value - this.B.value) + d * (this.G.value < this.B.value ? 6 : 0)
|
||||
case r:
|
||||
h = (g - b) + d * (g < b ? 6 : 0)
|
||||
break
|
||||
case this.G.value:
|
||||
h = (this.B.value - this.R.value) + d * 2
|
||||
case g:
|
||||
h = (b - r) + d * 2
|
||||
break
|
||||
case this.B.value:
|
||||
h = (this.R.value - this.G.value) + d * 4
|
||||
case b:
|
||||
h = (r - g) + d * 4
|
||||
break
|
||||
}
|
||||
h /= 6 * d
|
||||
@@ -55,7 +74,7 @@ export default class LinearColorEntity extends IEntity {
|
||||
}
|
||||
|
||||
toNumber() {
|
||||
return (this.R.value << 24) + (this.G.value << 16) + (this.B.value << 8) + this.A.value
|
||||
return (this.R.value * 0xff << 3 * 0x8) + (this.G.value * 0xff << 2 * 0x8) + (this.B.value * 0xff << 0x8) + this.A.value
|
||||
}
|
||||
|
||||
toString() {
|
||||
|
||||
24
js/entity/UnitRealEntity.js
Normal file
24
js/entity/UnitRealEntity.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import IEntity from "./IEntity"
|
||||
import Utility from "../Utility"
|
||||
|
||||
export default class RealUnitEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
value: Number,
|
||||
}
|
||||
|
||||
/** @param {Object | Number | String} options */
|
||||
constructor(options = 0) {
|
||||
super(options)
|
||||
/** @type {Number} */
|
||||
this.value = Utility.clamp(this.value, 0, 1)
|
||||
}
|
||||
|
||||
valueOf() {
|
||||
return this.value
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.value.toFixed(6)
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,13 @@ import Configuration from "../../Configuration"
|
||||
import IPointing from "./IPointing"
|
||||
import Utility from "../../Utility"
|
||||
|
||||
/** @typedef {import("../../Blueprint").default} Blueprint */
|
||||
/**
|
||||
* @typedef {import("../../Blueprint").default} Blueprint
|
||||
* @typedef {import("../../element/IDraggableElement").default} IDraggableElement
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {HTMLElement} T
|
||||
* @template {IDraggableElement} T
|
||||
* @extends {IPointing<T>}
|
||||
*/
|
||||
export default class IMouseClickDrag extends IPointing {
|
||||
@@ -29,6 +32,7 @@ export default class IMouseClickDrag extends IPointing {
|
||||
started = false
|
||||
stepSize = 1
|
||||
clickedPosition = [0, 0]
|
||||
clickedOffset = [0, 0]
|
||||
mouseLocation = [0, 0]
|
||||
|
||||
/**
|
||||
@@ -66,6 +70,10 @@ export default class IMouseClickDrag extends IPointing {
|
||||
self.#movementListenedElement.addEventListener("mousemove", self.#mouseStartedMovingHandler)
|
||||
document.addEventListener("mouseup", self.#mouseUpHandler)
|
||||
self.clickedPosition = self.locationFromEvent(e)
|
||||
self.clickedOffset = [
|
||||
self.clickedPosition[0] - self.target.locationX,
|
||||
self.clickedPosition[1] - self.target.locationY,
|
||||
]
|
||||
self.clicked(self.clickedPosition)
|
||||
}
|
||||
break
|
||||
|
||||
@@ -18,6 +18,7 @@ export default class MouseMoveDraggable extends IMouseClickDrag {
|
||||
? Utility.snapToGrid(location, this.stepSize)
|
||||
: location
|
||||
)
|
||||
this.clickedOffset = [0, 0]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +43,9 @@ export default class MouseMoveDraggable extends IMouseClickDrag {
|
||||
}
|
||||
|
||||
dragAction(location, offset) {
|
||||
this.target.addLocation(offset)
|
||||
this.target.setLocation([
|
||||
location[0] - this.clickedOffset[0],
|
||||
location[1] - this.clickedOffset[1]
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// @ts-nocheck
|
||||
import ColorChannelValueEntity from "../entity/ColorChannelValueEntity"
|
||||
import FunctionReferenceEntity from "../entity/FunctionReferenceEntity"
|
||||
import GuidEntity from "../entity/GuidEntity"
|
||||
import IdentifierEntity from "../entity/IdentifierEntity"
|
||||
@@ -14,6 +13,7 @@ import Parsimmon from "parsimmon"
|
||||
import PathSymbolEntity from "../entity/PathSymbolEntity"
|
||||
import PinEntity from "../entity/PinEntity"
|
||||
import PinReferenceEntity from "../entity/PinReferenceEntity"
|
||||
import RealUnitEntity from "../entity/UnitRealEntity"
|
||||
import RotatorEntity from "../entity/RotatorEntity"
|
||||
import SimpleSerializationRotatorEntity from "../entity/SimpleSerializationRotatorEntity"
|
||||
import SimpleSerializationVectorEntity from "../entity/SimpleSerializationVectorEntity"
|
||||
@@ -37,44 +37,6 @@ export default class Grammar {
|
||||
return result
|
||||
}
|
||||
switch (Utility.getType(attributeType)) {
|
||||
case Boolean:
|
||||
return r.Boolean
|
||||
case Number:
|
||||
return r.Number
|
||||
case IntegerEntity:
|
||||
return r.Integer
|
||||
case String:
|
||||
return r.String
|
||||
case GuidEntity:
|
||||
return r.Guid
|
||||
case IdentifierEntity:
|
||||
return r.Identifier
|
||||
case ObjectReferenceEntity:
|
||||
return r.Reference
|
||||
case LocalizedTextEntity:
|
||||
return r.LocalizedText
|
||||
case InvariantTextEntity:
|
||||
return r.InvariantText
|
||||
case PinReferenceEntity:
|
||||
return r.PinReference
|
||||
case VectorEntity:
|
||||
return r.Vector
|
||||
case RotatorEntity:
|
||||
return r.Rotator
|
||||
case SimpleSerializationRotatorEntity:
|
||||
return r.SimpleSerializationRotator
|
||||
case SimpleSerializationVectorEntity:
|
||||
return r.SimpleSerializationVector
|
||||
case ColorChannelValueEntity:
|
||||
return r.ColorChannelValue
|
||||
case ColorChannelRealValue:
|
||||
return r.ColorChannelRealValue
|
||||
case LinearColorEntity:
|
||||
return r.LinearColor
|
||||
case FunctionReferenceEntity:
|
||||
return r.FunctionReference
|
||||
case PinEntity:
|
||||
return r.Pin
|
||||
case Array:
|
||||
return P.seqMap(
|
||||
P.string("("),
|
||||
@@ -91,6 +53,42 @@ export default class Grammar {
|
||||
P.string(")"),
|
||||
(_, grammar, __) => grammar
|
||||
)
|
||||
case Boolean:
|
||||
return r.Boolean
|
||||
case FunctionReferenceEntity:
|
||||
return r.FunctionReference
|
||||
case GuidEntity:
|
||||
return r.Guid
|
||||
case IdentifierEntity:
|
||||
return r.Identifier
|
||||
case IntegerEntity:
|
||||
return r.Integer
|
||||
case InvariantTextEntity:
|
||||
return r.InvariantText
|
||||
case LinearColorEntity:
|
||||
return r.LinearColor
|
||||
case LocalizedTextEntity:
|
||||
return r.LocalizedText
|
||||
case Number:
|
||||
return r.Number
|
||||
case ObjectReferenceEntity:
|
||||
return r.Reference
|
||||
case PinEntity:
|
||||
return r.Pin
|
||||
case PinReferenceEntity:
|
||||
return r.PinReference
|
||||
case RealUnitEntity:
|
||||
return r.RealUnit
|
||||
case RotatorEntity:
|
||||
return r.Rotator
|
||||
case SimpleSerializationRotatorEntity:
|
||||
return r.SimpleSerializationRotator
|
||||
case SimpleSerializationVectorEntity:
|
||||
return r.SimpleSerializationVector
|
||||
case String:
|
||||
return r.String
|
||||
case VectorEntity:
|
||||
return r.Vector
|
||||
default:
|
||||
return defaultGrammar
|
||||
}
|
||||
@@ -160,6 +158,9 @@ export default class Grammar {
|
||||
/** @param {Grammar} r */
|
||||
RealNumber = r => P.regex(/[-\+]?[0-9]+\.[0-9]+/).map(Number).desc("a number written as real")
|
||||
|
||||
/** @param {Grammar} r */
|
||||
RealUnit = r => P.regex(/\+?[0-9]+(?:\.[0-9]+)?/).map(Number).assert(v => v >= 0 && v <= 1).desc("a number between 0 and 1")
|
||||
|
||||
/** @param {Grammar} r */
|
||||
NaturalNumber = r => P.regex(/0|[1-9]\d*/).map(Number).desc("a natural number")
|
||||
|
||||
@@ -310,17 +311,6 @@ export default class Grammar {
|
||||
})
|
||||
)
|
||||
|
||||
/** @param {Grammar} r */
|
||||
ColorChannelValue = r => P.alt(
|
||||
r.RealNumber.map(v => new ColorChannelValueEntity(v * 255)),
|
||||
r.ColorNumber.map(v => new ColorChannelValueEntity(v)),
|
||||
)
|
||||
|
||||
/** @param {Grammar} r */
|
||||
ColorChannelRealValue = r => P.alt(
|
||||
r.RealNumber.map(v => new ColorChannelValueEntity(v * 255))
|
||||
)
|
||||
|
||||
/** @param {Grammar} r */
|
||||
LinearColor = r => Grammar.createEntityGrammar(r, LinearColorEntity)
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import ColorChannelRealValueEntity from "../entity/ColorChannelRealValueEntity"
|
||||
import ColorChannelValueEntity from "../entity/ColorChannelValueEntity"
|
||||
import CustomSerializer from "./CustomSerializer"
|
||||
import FunctionReferenceEntity from "../entity/FunctionReferenceEntity"
|
||||
import GeneralSerializer from "./GeneralSerializer"
|
||||
@@ -16,6 +14,7 @@ import ObjectSerializer from "./ObjectSerializer"
|
||||
import PathSymbolEntity from "../entity/PathSymbolEntity"
|
||||
import PinEntity from "../entity/PinEntity"
|
||||
import PinReferenceEntity from "../entity/PinReferenceEntity"
|
||||
import RealUnitEntity from "../entity/UnitRealEntity"
|
||||
import RotatorEntity from "../entity/RotatorEntity"
|
||||
import SerializerFactory from "./SerializerFactory"
|
||||
import SimpleSerializationRotatorEntity from "../entity/SimpleSerializationRotatorEntity"
|
||||
@@ -66,16 +65,6 @@ export default function initializeSerializerFactory() {
|
||||
)
|
||||
)
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
ColorChannelRealValueEntity,
|
||||
new ToStringSerializer(ColorChannelValueEntity)
|
||||
)
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
ColorChannelValueEntity,
|
||||
new ToStringSerializer(ColorChannelValueEntity)
|
||||
)
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
FunctionReferenceEntity,
|
||||
new GeneralSerializer(bracketsWrapped, FunctionReferenceEntity)
|
||||
@@ -157,6 +146,11 @@ export default function initializeSerializerFactory() {
|
||||
new GeneralSerializer(v => v, PinReferenceEntity, "", " ", false, "", _ => "")
|
||||
)
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
RealUnitEntity,
|
||||
new ToStringSerializer(RealUnitEntity)
|
||||
)
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
RotatorEntity,
|
||||
new GeneralSerializer(bracketsWrapped, RotatorEntity)
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
import IDraggableTemplate from "./IDraggableTemplate"
|
||||
import MouseMoveDraggable from "../input/mouse/MouseMoveDraggable"
|
||||
import Utility from "../Utility"
|
||||
|
||||
/** @typedef {import("../element/ColorHandlerElement").default} ColorHandlerElement */
|
||||
|
||||
/** @extends {IDraggableTemplate<ColorHandlerElement>} */
|
||||
export default class ColorHandlerTemplate extends IDraggableTemplate {
|
||||
|
||||
#locationChangeCallback
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback()
|
||||
this.window = this.element.closest("ueb-window")
|
||||
this.movementSpace = this.element.parentElement
|
||||
const bounding = this.movementSpace.getBoundingClientRect()
|
||||
this.movementSpaceSize = [bounding.width, bounding.height]
|
||||
}
|
||||
|
||||
createDraggableObject() {
|
||||
@@ -22,4 +28,21 @@ export default class ColorHandlerTemplate extends IDraggableTemplate {
|
||||
stepSize: 1,
|
||||
})
|
||||
}
|
||||
|
||||
/** @param {[Number, Number]} param0 */
|
||||
adjustLocation([x, y]) {
|
||||
const radius = Math.round(this.movementSpaceSize[0] / 2)
|
||||
x = x - radius
|
||||
y = -(y - radius)
|
||||
let [r, theta] = Utility.getPolarCoordinates([x, y])
|
||||
r = Math.min(r, radius), [x, y] = Utility.getCartesianCoordinates([r, theta])
|
||||
x = Math.round(x + radius)
|
||||
y = Math.round(-y + radius)
|
||||
this.#locationChangeCallback?.([x, y])
|
||||
return [x, y]
|
||||
}
|
||||
|
||||
setLocationChangeCallback(callback) {
|
||||
this.#locationChangeCallback = callback
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { html } from "lit"
|
||||
import ColorHandlerElement from "../element/ColorHandlerElement"
|
||||
import Configuration from "../Configuration"
|
||||
import LinearColorEntity from "../entity/LinearColorEntity"
|
||||
import Utility from "../Utility"
|
||||
import WindowTemplate from "./WindowTemplate"
|
||||
|
||||
/** @typedef {import("../element/WindowElement").default} WindowElement */
|
||||
@@ -16,10 +18,11 @@ export default class ColorPickerWindowTemplate extends WindowTemplate {
|
||||
}
|
||||
/** @param {LinearColorEntity} value */
|
||||
set color(value) {
|
||||
if (value.toNumber() == this.color.toNumber()) {
|
||||
this.element.requestUpdate("color", this.#color)
|
||||
this.#color = value
|
||||
if (value.toNumber() == this.color?.toNumber()) {
|
||||
return
|
||||
}
|
||||
this.element.requestUpdate("color", this.#color)
|
||||
this.#color = value
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
@@ -29,6 +32,14 @@ export default class ColorPickerWindowTemplate extends WindowTemplate {
|
||||
|
||||
/** @param {Map} changedProperties */
|
||||
firstUpdated(changedProperties) {
|
||||
const wheelHandler = new ColorHandlerElement()
|
||||
const spectrumHandler = new ColorHandlerElement()
|
||||
wheelHandler.template.setLocationChangeCallback(([x, y]) => {
|
||||
const [r, theta] = Utility.getPolarCoordinates([x, y])
|
||||
this.color = LinearColorEntity.fromWheelLocation(x, y)
|
||||
})
|
||||
this.element.querySelector(".ueb-color-picker-wheel").appendChild(new ColorHandlerElement())
|
||||
|
||||
}
|
||||
|
||||
renderContent() {
|
||||
@@ -40,9 +51,7 @@ export default class ColorPickerWindowTemplate extends WindowTemplate {
|
||||
<div class="ueb-color-picker-srgb"></div>
|
||||
</div>
|
||||
<div class="ueb-color-picker-main">
|
||||
<div class="ueb-color-picker-wheel">
|
||||
<ueb-color-handler></ueb-color-handler>
|
||||
</div>
|
||||
<div class="ueb-color-picker-wheel"></div>
|
||||
<div class="ueb-color-picker-saturation"></div>
|
||||
<div class="ueb-color-picker-value"></div>
|
||||
<div class="ueb-color-picker-preview">
|
||||
|
||||
@@ -18,9 +18,10 @@ export default class WindowTemplate extends IDraggableTemplate {
|
||||
createDraggableObject() {
|
||||
return new MouseMoveDraggable(this.element, this.element.blueprint, {
|
||||
draggableElement: this.getDraggableElement(),
|
||||
ignoreTranslateCompensate: true,
|
||||
looseTarget: true,
|
||||
stepSize: 1,
|
||||
movementSpace: this.element.blueprint,
|
||||
stepSize: 1,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user