mirror of
https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13.git
synced 2025-12-10 09:54:52 +00:00
user-friendly colormates - adds HSV coloration to colormates!
This commit is contained in:
@@ -138,6 +138,25 @@ round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), ro
|
||||
var/sinval = round(sin(angle), 0.001); var/cosval = round(cos(angle), 0.001)
|
||||
return list(cosval,sinval,0,0, -sinval,cosval,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,0)
|
||||
|
||||
/**
|
||||
* Builds a color matrix that transforms the hue, saturation, and value, all in one operation.
|
||||
*/
|
||||
/proc/color_matrix_hsv(hue, saturation, value)
|
||||
hue = clamp(360 - hue, 0, 360)
|
||||
|
||||
// This is very much a rough approximation of hueshifting. This carries some artifacting, such as negative values that simply shouldn't exist, but it does get the job done, and that's what matters.
|
||||
var/cos_a = cos(hue) // These have to be inverted from 360, otherwise the hue's inverted
|
||||
var/sin_a = sin(hue)
|
||||
var/rot_x = cos_a + (1 - cos_a) / 3
|
||||
var/rot_y = (1 - cos_a) / 3 - 0.5774 * sin_a // 0.5774 is sqrt(1/3)
|
||||
var/rot_z = (1 - cos_a) / 3 + 0.5774 * sin_a
|
||||
|
||||
return list(
|
||||
round((((1-saturation) * LUMA_R) + (rot_x * saturation)) * value, 0.01), round((((1-saturation) * LUMA_R) + (rot_y * saturation)) * value, 0.01), round((((1-saturation) * LUMA_R) + (rot_z * saturation)) * value, 0.01),
|
||||
round((((1-saturation) * LUMA_G) + (rot_z * saturation)) * value, 0.01), round((((1-saturation) * LUMA_G) + (rot_x * saturation)) * value, 0.01), round((((1-saturation) * LUMA_G) + (rot_y * saturation)) * value, 0.01),
|
||||
round((((1-saturation) * LUMA_B) + (rot_y * saturation)) * value, 0.01), round((((1-saturation) * LUMA_B) + (rot_z * saturation)) * value, 0.01), round((((1-saturation) * LUMA_B) + (rot_x * saturation)) * value, 0.01),
|
||||
0, 0, 0
|
||||
)
|
||||
|
||||
//Returns a matrix addition of A with B
|
||||
/proc/color_matrix_add(list/A, list/B)
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#define COLORMATE_TINT 1
|
||||
#define COLORMATE_HSV 2
|
||||
#define COLORMATE_MATRIX 3
|
||||
|
||||
/obj/machinery/gear_painter
|
||||
name = "\improper Color Mate"
|
||||
desc = "A machine to give your apparel a fresh new color! Recommended to use with white items for best results."
|
||||
desc = "A machine to give your apparel a fresh new color!"
|
||||
icon = 'icons/obj/vending.dmi'
|
||||
icon_state = "colormate"
|
||||
// light_mask = "colormate-light-mask"
|
||||
@@ -10,7 +14,11 @@
|
||||
var/atom/movable/inserted
|
||||
var/activecolor = "#FFFFFF"
|
||||
var/list/color_matrix_last
|
||||
var/matrix_mode = FALSE
|
||||
var/active_mode = COLORMATE_HSV
|
||||
|
||||
var/build_hue = 0
|
||||
var/build_sat = 1
|
||||
var/build_val = 1
|
||||
/// Allow holder'd mobs
|
||||
var/allow_mobs = TRUE
|
||||
/// Minimum lightness for normal mode
|
||||
@@ -71,8 +79,9 @@
|
||||
return
|
||||
if(!QDELETED(H))
|
||||
H.release()
|
||||
user.visible_message("<span class='notice'>[user] stuffs [I] into [src]'s receptable! Oh dear.</span>")
|
||||
|
||||
insert_mob(victim, user)
|
||||
temp = "[victim] has been inserted."
|
||||
SStgui.update_uis(src)
|
||||
|
||||
if(is_type_in_list(I, allowed_types) && is_operational())
|
||||
@@ -85,7 +94,6 @@
|
||||
|
||||
inserted = I
|
||||
update_icon()
|
||||
temp = "[I] has been inserted."
|
||||
SStgui.update_uis(src)
|
||||
|
||||
else
|
||||
@@ -134,7 +142,7 @@
|
||||
|
||||
/obj/machinery/gear_painter/ui_data(mob/user)
|
||||
. = list()
|
||||
.["matrixactive"] = matrix_mode
|
||||
.["activemode"] = active_mode
|
||||
.["matrixcolors"] = list(
|
||||
"rr" = color_matrix_last[1],
|
||||
"rg" = color_matrix_last[2],
|
||||
@@ -149,6 +157,9 @@
|
||||
"cg" = color_matrix_last[11],
|
||||
"cb" = color_matrix_last[12]
|
||||
)
|
||||
.["buildhue"] = build_hue
|
||||
.["buildsat"] = build_sat
|
||||
.["buildval"] = build_val
|
||||
if(temp)
|
||||
.["temp"] = temp
|
||||
if(inserted)
|
||||
@@ -166,7 +177,7 @@
|
||||
if(inserted)
|
||||
switch(action)
|
||||
if("switch_modes")
|
||||
matrix_mode = text2num(params["mode"])
|
||||
active_mode = text2num(params["mode"])
|
||||
return TRUE
|
||||
if("choose_color")
|
||||
var/chosen_color = input(usr, "Choose a color: ", "Colormate color picking", activecolor) as color|null
|
||||
@@ -174,14 +185,11 @@
|
||||
activecolor = chosen_color
|
||||
return TRUE
|
||||
if("paint")
|
||||
if(!check_valid_color(activecolor, usr))
|
||||
return TRUE
|
||||
inserted.add_atom_colour(activecolor, FIXED_COLOUR_PRIORITY)
|
||||
playsound(src, 'sound/effects/spray3.ogg', 50, 1)
|
||||
do_paint(usr)
|
||||
temp = "Painted Successfully!"
|
||||
return TRUE
|
||||
if("drop")
|
||||
temp = "Ejected \the [inserted]!"
|
||||
temp = ""
|
||||
drop_item()
|
||||
return TRUE
|
||||
if("clear")
|
||||
@@ -192,8 +200,24 @@
|
||||
if("set_matrix_color")
|
||||
color_matrix_last[params["color"]] = params["value"]
|
||||
return TRUE
|
||||
if("matrix_paint")
|
||||
var/list/cm = rgb_construct_color_matrix(
|
||||
if("set_hue")
|
||||
build_hue = clamp(text2num(params["buildhue"]), 0, 360)
|
||||
return TRUE
|
||||
if("set_sat")
|
||||
build_sat = clamp(text2num(params["buildsat"]), -10, 10)
|
||||
return TRUE
|
||||
if("set_val")
|
||||
build_val = clamp(text2num(params["buildval"]), -10, 10)
|
||||
return TRUE
|
||||
|
||||
|
||||
/obj/machinery/gear_painter/proc/do_paint(mob/user)
|
||||
var/color_to_use
|
||||
switch(active_mode)
|
||||
if(COLORMATE_TINT)
|
||||
color_to_use = activecolor
|
||||
if(COLORMATE_MATRIX)
|
||||
color_to_use = rgb_construct_color_matrix(
|
||||
text2num(color_matrix_last[1]),
|
||||
text2num(color_matrix_last[2]),
|
||||
text2num(color_matrix_last[3]),
|
||||
@@ -207,18 +231,24 @@
|
||||
text2num(color_matrix_last[11]),
|
||||
text2num(color_matrix_last[12])
|
||||
)
|
||||
if(!check_valid_color(cm, usr))
|
||||
return TRUE
|
||||
inserted.add_atom_colour(cm, FIXED_COLOUR_PRIORITY)
|
||||
if(COLORMATE_HSV)
|
||||
color_to_use = color_matrix_hsv(build_hue, build_sat, build_val)
|
||||
color_matrix_last = color_to_use
|
||||
if(!color_to_use || !check_valid_color(color_to_use, user))
|
||||
to_chat(user, "<span class='notice'>Invalid color.</span>")
|
||||
return FALSE
|
||||
inserted.add_atom_colour(color_to_use, FIXED_COLOUR_PRIORITY)
|
||||
playsound(src, 'sound/effects/spray3.ogg', 50, 1)
|
||||
temp = "Matrix Painted Successfully!"
|
||||
return TRUE
|
||||
|
||||
|
||||
/// Produces the preview image of the item, used in the UI, the way the color is not stacking is a sin.
|
||||
/obj/machinery/gear_painter/proc/build_preview()
|
||||
if(inserted) //sanity
|
||||
if(matrix_mode)
|
||||
var/list/cm = rgb_construct_color_matrix(
|
||||
var/list/cm
|
||||
switch(active_mode)
|
||||
if(COLORMATE_MATRIX)
|
||||
cm = rgb_construct_color_matrix(
|
||||
text2num(color_matrix_last[1]),
|
||||
text2num(color_matrix_last[2]),
|
||||
text2num(color_matrix_last[3]),
|
||||
@@ -233,27 +263,24 @@
|
||||
text2num(color_matrix_last[12])
|
||||
)
|
||||
if(!check_valid_color(cm, usr))
|
||||
temp = "Failed to generate preview: Invalid color!"
|
||||
return getFlatIcon(inserted, defdir=SOUTH, no_anim=TRUE)
|
||||
|
||||
var/cur_color = inserted.color
|
||||
inserted.color = null
|
||||
inserted.color = cm
|
||||
var/icon/preview = getFlatIcon(inserted, defdir=SOUTH, no_anim=TRUE)
|
||||
inserted.color = cur_color
|
||||
|
||||
. = preview
|
||||
|
||||
else
|
||||
if(COLORMATE_TINT)
|
||||
if(!check_valid_color(activecolor, usr))
|
||||
temp = "Failed to generate preview: Invalid color!"
|
||||
return getFlatIcon(inserted, defdir=SOUTH, no_anim=TRUE)
|
||||
|
||||
if(COLORMATE_HSV)
|
||||
cm = color_matrix_hsv(build_hue, build_sat, build_val)
|
||||
color_matrix_last = cm
|
||||
if(!check_valid_color(cm, usr))
|
||||
return getFlatIcon(inserted, defdir=SOUTH, no_anim=TRUE)
|
||||
|
||||
var/cur_color = inserted.color
|
||||
inserted.color = null
|
||||
inserted.color = activecolor
|
||||
inserted.color = (active_mode == COLORMATE_TINT ? activecolor : cm)
|
||||
var/icon/preview = getFlatIcon(inserted, defdir=SOUTH, no_anim=TRUE)
|
||||
inserted.color = cur_color
|
||||
temp = ""
|
||||
|
||||
. = preview
|
||||
|
||||
@@ -261,7 +288,7 @@
|
||||
if(!islist(cm)) // normal
|
||||
var/list/HSV = ReadHSV(RGBtoHSV(cm))
|
||||
if(HSV[3] < minimum_normal_lightness)
|
||||
temp = "[cm] is far too dark (min lightness [minimum_normal_lightness]!"
|
||||
temp = "[cm] is too dark (Minimum lightness: [minimum_normal_lightness])"
|
||||
return FALSE
|
||||
return TRUE
|
||||
else // matrix
|
||||
@@ -275,6 +302,6 @@
|
||||
COLORTEST("FFFFFF", cm)
|
||||
#undef COLORTEST
|
||||
if(passed < minimum_matrix_tests)
|
||||
temp = "[english_list(color)] is not allowed (passed [passed] out of 4, minimum [minimum_matrix_tests], minimum lightness [minimum_matrix_lightness])."
|
||||
temp = "Matrix is too dark. (passed [passed] out of [minimum_matrix_tests] required tests. Minimum lightness: [minimum_matrix_lightness])."
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Window } from '../layouts';
|
||||
|
||||
export const Colormate = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { matrixactive, temp } = data;
|
||||
const { activemode, temp } = data;
|
||||
const item = data.item || [];
|
||||
return (
|
||||
<Window width="980" height="720" resizable>
|
||||
@@ -39,28 +39,43 @@ export const Colormate = (props, context) => {
|
||||
<Section>
|
||||
<Tabs fluid>
|
||||
<Tabs.Tab
|
||||
key="0"
|
||||
selected={!matrixactive}
|
||||
key="1"
|
||||
selected={activemode === 1}
|
||||
onClick={() => act('switch_modes', {
|
||||
mode: "0",
|
||||
mode: 1,
|
||||
})} >
|
||||
Regular coloring
|
||||
Tint coloring (Simple)
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
key="1"
|
||||
selected={matrixactive}
|
||||
key="2"
|
||||
selected={activemode === 2}
|
||||
onClick={() => act('switch_modes', {
|
||||
mode: "1",
|
||||
mode: 2,
|
||||
})} >
|
||||
Matrixed coloring
|
||||
HSV coloring (Normal)
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
key="3"
|
||||
selected={activemode === 3}
|
||||
onClick={() => act('switch_modes', {
|
||||
mode: 3,
|
||||
})} >
|
||||
Matrix coloring (Advanced)
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
{matrixactive ? (
|
||||
{}
|
||||
{activemode === 3 ? (
|
||||
<>
|
||||
<center>Coloring: {item.name}</center>
|
||||
<ColormateMatrix />
|
||||
</>
|
||||
) : (
|
||||
) : activemode === 2 ? (
|
||||
<>
|
||||
<center>Coloring: {item.name}</center>
|
||||
<ColormateHSL />
|
||||
</>
|
||||
)
|
||||
: (
|
||||
<>
|
||||
<center>Coloring: {item.name}</center>
|
||||
<ColormateNoMatrix />
|
||||
@@ -140,8 +155,9 @@ export const ColormateMatrix = (props, context) => {
|
||||
<Flex.Item>
|
||||
RR: <NumberInput
|
||||
width="50px"
|
||||
minValue={-1000}
|
||||
maxValue={1000}
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.rr}
|
||||
onChange={(e, value) => act('set_matrix_color', {
|
||||
color: 1,
|
||||
@@ -151,8 +167,9 @@ export const ColormateMatrix = (props, context) => {
|
||||
<Flex.Item>
|
||||
GR: <NumberInput
|
||||
width="50px"
|
||||
minValue={-1000}
|
||||
maxValue={1000}
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.gr}
|
||||
onChange={(e, value) => act('set_matrix_color', {
|
||||
color: 4,
|
||||
@@ -162,8 +179,9 @@ export const ColormateMatrix = (props, context) => {
|
||||
<Flex.Item>
|
||||
BR: <NumberInput
|
||||
width="50px"
|
||||
minValue={-1000}
|
||||
maxValue={1000}
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.br}
|
||||
onChange={(e, value) => act('set_matrix_color', {
|
||||
color: 7,
|
||||
@@ -175,8 +193,9 @@ export const ColormateMatrix = (props, context) => {
|
||||
<Flex.Item>
|
||||
RG: <NumberInput
|
||||
width="50px"
|
||||
minValue={-1000}
|
||||
maxValue={1000}
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.rg}
|
||||
onChange={(e, value) => act('set_matrix_color', {
|
||||
color: 2,
|
||||
@@ -186,8 +205,9 @@ export const ColormateMatrix = (props, context) => {
|
||||
<Flex.Item>
|
||||
GG: <NumberInput
|
||||
width="50px"
|
||||
minValue={-1000}
|
||||
maxValue={1000}
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.gg}
|
||||
onChange={(e, value) => act('set_matrix_color', {
|
||||
color: 5,
|
||||
@@ -197,8 +217,9 @@ export const ColormateMatrix = (props, context) => {
|
||||
<Flex.Item>
|
||||
BG: <NumberInput
|
||||
width="50px"
|
||||
minValue={-1000}
|
||||
maxValue={1000}
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.bg}
|
||||
onChange={(e, value) => act('set_matrix_color', {
|
||||
color: 8,
|
||||
@@ -210,8 +231,9 @@ export const ColormateMatrix = (props, context) => {
|
||||
<Flex.Item>
|
||||
RB: <NumberInput
|
||||
width="50px"
|
||||
minValue={-1000}
|
||||
maxValue={1000}
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.rb}
|
||||
onChange={(e, value) => act('set_matrix_color', {
|
||||
color: 3,
|
||||
@@ -221,8 +243,9 @@ export const ColormateMatrix = (props, context) => {
|
||||
<Flex.Item>
|
||||
GB: <NumberInput
|
||||
width="50px"
|
||||
minValue={-1000}
|
||||
maxValue={1000}
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.gb}
|
||||
onChange={(e, value) => act('set_matrix_color', {
|
||||
color: 6,
|
||||
@@ -232,8 +255,9 @@ export const ColormateMatrix = (props, context) => {
|
||||
<Flex.Item>
|
||||
BB: <NumberInput
|
||||
width="50px"
|
||||
minValue={-1000}
|
||||
maxValue={1000}
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.bb}
|
||||
onChange={(e, value) => act('set_matrix_color', {
|
||||
color: 9,
|
||||
@@ -245,8 +269,9 @@ export const ColormateMatrix = (props, context) => {
|
||||
<Flex.Item>
|
||||
CR: <NumberInput
|
||||
width="50px"
|
||||
minValue={-1000}
|
||||
maxValue={1000}
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.cr}
|
||||
onChange={(e, value) => act('set_matrix_color', {
|
||||
color: 10,
|
||||
@@ -256,8 +281,9 @@ export const ColormateMatrix = (props, context) => {
|
||||
<Flex.Item>
|
||||
CG: <NumberInput
|
||||
width="50px"
|
||||
minValue={-1000}
|
||||
maxValue={1000}
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.cg}
|
||||
onChange={(e, value) => act('set_matrix_color', {
|
||||
color: 11,
|
||||
@@ -267,8 +293,9 @@ export const ColormateMatrix = (props, context) => {
|
||||
<Flex.Item>
|
||||
CB: <NumberInput
|
||||
width="50px"
|
||||
minValue={-1000}
|
||||
maxValue={1000}
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={matrixcolors.cb}
|
||||
onChange={(e, value) => act('set_matrix_color', {
|
||||
color: 12,
|
||||
@@ -278,7 +305,67 @@ export const ColormateMatrix = (props, context) => {
|
||||
</Flex.Item>
|
||||
<Flex.Item width="33%">
|
||||
<Icon name="question-circle" color="blue" /> RG means red will become this much green.<br />
|
||||
<Icon name="question-circle" color="blue" /> CR means that red will have this much constrast applied to it.
|
||||
<Icon name="question-circle" color="blue" /> CR means this much red will be added.
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
export const ColormateHSL = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { buildhue, buildsat, buildval } = data;
|
||||
return (
|
||||
<Section>
|
||||
<Flex grow={1} fill>
|
||||
<Flex.Item width="33%">
|
||||
<Button
|
||||
fluid
|
||||
content="Paint"
|
||||
icon="fill"
|
||||
onClick={() => act('paint')} />
|
||||
<Button
|
||||
fluid
|
||||
content="Clear"
|
||||
icon="eraser"
|
||||
onClick={() => act('clear')} />
|
||||
<Button
|
||||
fluid
|
||||
content="Eject"
|
||||
icon="eject"
|
||||
onClick={() => act('drop')} />
|
||||
</Flex.Item>
|
||||
<Flex.Item width="66%">
|
||||
<Flex.Item>
|
||||
Hue: <NumberInput
|
||||
minValue={0}
|
||||
maxValue={360}
|
||||
step={1}
|
||||
value={buildhue}
|
||||
onDrag={(e, value) => act('set_hue', {
|
||||
buildhue: value,
|
||||
})} />
|
||||
</Flex.Item>
|
||||
<Flex.Item>
|
||||
Saturation: <NumberInput
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={buildsat}
|
||||
onDrag={(e, value) => act('set_sat', {
|
||||
buildsat: value,
|
||||
})} />
|
||||
</Flex.Item>
|
||||
<Flex.Item>
|
||||
Value: <NumberInput
|
||||
minValue={-10}
|
||||
maxValue={10}
|
||||
step={0.01}
|
||||
value={buildval}
|
||||
onDrag={(e, value) => act('set_val', {
|
||||
buildval: value,
|
||||
})} />
|
||||
</Flex.Item>
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Section>
|
||||
|
||||
Reference in New Issue
Block a user