Squashed commit of the following: [standardize_color_handling]

closes #11

Check the abandoned feature branch for PyLinkedColor, a time-expensive but now abandoned feature to link a color value to a UIDrawable.

There are some TODOs left in the PyColor class, but that can go under cleanup. I'm way over time on this, so I'm taking a small victory :)

commit 572aa52605
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sat Mar 30 21:18:26 2024 -0400

    More color table updates

commit 01706bd59d
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sat Mar 30 21:13:31 2024 -0400

    Color wrapup... Cutting PyLinkedColor to simplify my cursedly mortal, finite existence

commit 3991ac13d6
Author: John McCardle <mccardle.john@gmail.com>
Date:   Thu Mar 28 23:50:50 2024 -0400

    Still having segfaults with LinkedColor and captions (specifically outline color, but that might not be the actual cause). PyColor shaping back up in simplified form.

commit 06e24a1b27
Author: John McCardle <mccardle.john@gmail.com>
Date:   Thu Mar 28 20:53:49 2024 -0400

    LinkedColor now reflecting changes to the linked color value. Needs set method + RGBA / color properties

commit 41509dfe96
Author: John McCardle <mccardle.john@gmail.com>
Date:   Wed Mar 27 21:10:03 2024 -0400

    Addressing issues with PyColor by splitting behavior off into PyLinkedColor

commit 13a4ddf41b
Author: John McCardle <mccardle.john@gmail.com>
Date:   Tue Mar 26 23:02:00 2024 -0400

    Build runs again. PyColor objects are being instantiated, with bugs and no test of color changing

commit 1601fc7fab
Author: John McCardle <mccardle.john@gmail.com>
Date:   Mon Mar 25 20:48:08 2024 -0400

    Still doesn't compile, but now the issue is in UI.h overcoupling. Progress!

commit 13672c8fdb
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sun Mar 24 21:19:37 2024 -0400

    Dabbling around this morning; still not building

commit 79090b553f
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sun Mar 24 08:36:06 2024 -0400

    Unsaved changes from last night

commit 2cac6f03c6
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sat Mar 23 23:07:10 2024 -0400

    untested PyColor base implementation

commit 3728e5fcc8
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sat Mar 23 23:06:36 2024 -0400

    Color naming prototype
This commit is contained in:
John McCardle 2024-03-30 21:20:40 -04:00
parent 4ffe438d1b
commit f82508b753
10 changed files with 1974 additions and 300 deletions

157
css_colors.txt Normal file
View File

@ -0,0 +1,157 @@
aqua #00FFFF
black #000000
blue #0000FF
fuchsia #FF00FF
gray #808080
green #008000
lime #00FF00
maroon #800000
navy #000080
olive #808000
purple #800080
red #FF0000
silver #C0C0C0
teal #008080
white #FFFFFF
yellow #FFFF00
aliceblue #F0F8FF
antiquewhite #FAEBD7
aqua #00FFFF
aquamarine #7FFFD4
azure #F0FFFF
beige #F5F5DC
bisque #FFE4C4
black #000000
blanchedalmond #FFEBCD
blue #0000FF
blueviolet #8A2BE2
brown #A52A2A
burlywood #DEB887
cadetblue #5F9EA0
chartreuse #7FFF00
chocolate #D2691E
coral #FF7F50
cornflowerblue #6495ED
cornsilk #FFF8DC
crimson #DC143C
cyan #00FFFF
darkblue #00008B
darkcyan #008B8B
darkgoldenrod #B8860B
darkgray #A9A9A9
darkgreen #006400
darkkhaki #BDB76B
darkmagenta #8B008B
darkolivegreen #556B2F
darkorange #FF8C00
darkorchid #9932CC
darkred #8B0000
darksalmon #E9967A
darkseagreen #8FBC8F
darkslateblue #483D8B
darkslategray #2F4F4F
darkturquoise #00CED1
darkviolet #9400D3
deeppink #FF1493
deepskyblue #00BFFF
dimgray #696969
dodgerblue #1E90FF
firebrick #B22222
floralwhite #FFFAF0
forestgreen #228B22
fuchsia #FF00FF
gainsboro #DCDCDC
ghostwhite #F8F8FF
gold #FFD700
goldenrod #DAA520
gray #7F7F7F
green #008000
greenyellow #ADFF2F
honeydew #F0FFF0
hotpink #FF69B4
indianred #CD5C5C
indigo #4B0082
ivory #FFFFF0
khaki #F0E68C
lavender #E6E6FA
lavenderblush #FFF0F5
lawngreen #7CFC00
lemonchiffon #FFFACD
lightblue #ADD8E6
lightcoral #F08080
lightcyan #E0FFFF
lightgoldenrodyellow #FAFAD2
lightgreen #90EE90
lightgrey #D3D3D3
lightpink #FFB6C1
lightsalmon #FFA07A
lightseagreen #20B2AA
lightskyblue #87CEFA
lightslategray #778899
lightsteelblue #B0C4DE
lightyellow #FFFFE0
lime #00FF00
limegreen #32CD32
linen #FAF0E6
magenta #FF00FF
maroon #800000
mediumaquamarine #66CDAA
mediumblue #0000CD
mediumorchid #BA55D3
mediumpurple #9370DB
mediumseagreen #3CB371
mediumslateblue #7B68EE
mediumspringgreen #00FA9A
mediumturquoise #48D1CC
mediumvioletred #C71585
midnightblue #191970
mintcream #F5FFFA
mistyrose #FFE4E1
moccasin #FFE4B5
navajowhite #FFDEAD
navy #000080
navyblue #9FAFDF
oldlace #FDF5E6
olive #808000
olivedrab #6B8E23
orange #FFA500
orangered #FF4500
orchid #DA70D6
palegoldenrod #EEE8AA
palegreen #98FB98
paleturquoise #AFEEEE
palevioletred #DB7093
papayawhip #FFEFD5
peachpuff #FFDAB9
peru #CD853F
pink #FFC0CB
plum #DDA0DD
powderblue #B0E0E6
purple #800080
red #FF0000
rosybrown #BC8F8F
royalblue #4169E1
saddlebrown #8B4513
salmon #FA8072
sandybrown #FA8072
seagreen #2E8B57
seashell #FFF5EE
sienna #A0522D
silver #C0C0C0
skyblue #87CEEB
slateblue #6A5ACD
slategray #708090
snow #FFFAFA
springgreen #00FF7F
steelblue #4682B4
tan #D2B48C
teal #008080
thistle #D8BFD8
tomato #FF6347
turquoise #40E0D0
violet #EE82EE
wheat #F5DEB3
white #FFFFFF
whitesmoke #F5F5F5
yellow #FFFF00
yellowgreen #9ACD32

111
generate_color_table.py Normal file
View File

@ -0,0 +1,111 @@
# data sources: CSS docs, jennyscrayoncollection 2017 article on Crayola colors, XKCD color survey
# target: Single C++ header file to provide a struct of color RGB codes and names.
# This file pre-computes the nearest neighbor of every color.
# if an RGB code being searched for is closer than the nearest neighbor, it's the closest color name.
def hex_to_rgb(txt):
if '#' in txt: txt = txt.replace('#', '')
r = txt[0:2]
g = txt[2:4]
b = txt[4:6]
return tuple([int(s, 16) for s in (r,g,b)])
class palette:
def __init__(self, name, filename, priority):
self.name = name
self.priority = priority
with open(filename, "r") as f:
print(f"scanning {filename}")
self.colors = {}
for line in f.read().split('\n'):
if len(line.split('\t')) < 2: continue
name, code = line.split('\t')
#print(name, code)
self.colors[name] = hex_to_rgb(code)
def __repr__(self):
return f"<Palette '{self.name}' - {len(self.colors)} colors, priority = {self.priority}>"
palettes = [
#palette("jenny", "jenny_colors.txt", 3), # I should probably use wikipedia as a source for copyright reasons
palette("crayon", "wikicrayons_colors.txt", 2),
palette("xkcd", "xkcd_colors.txt", 1),
palette("css", "css_colors.txt", 0),
#palette("matplotlib", "matplotlib_colors.txt", 2) # there's like 10 colors total, I think we'll survive without them
]
all_colors = []
from math import sqrt
def rgbdist(c1, c2):
return sqrt((c1.r - c2.r)**2 + (c1.g - c2.g)**2 + (c1.b - c2.b)**2)
class Color:
def __init__(self, r, g, b, name, prefix, priority):
self.r = r
self.g = g
self.b = b
self.name = name
self.prefix = prefix
self.priority = priority
self.nearest_neighbor = None
def __repr__(self):
return f"<Color ({self.r}, {self.g}, {self.b}) - '{self.prefix}:{self.name}', priority = {self.priority}, nearest_neighbor={self.nearest_neighbor.name if self.nearest_neighbor is not None else None}>"
def nn(self, colors):
nearest = None
nearest_dist = 999999
for c in colors:
dist = rgbdist(self, c)
if dist == 0: continue
if dist < nearest_dist:
nearest = c
nearest_dist = dist
self.nearest_neighbor = nearest
for p in palettes:
prefix = p.name
priority = p.priority
for name, rgb in p.colors.items():
all_colors.append(Color(*rgb, name, prefix, priority))
print(f"{prefix}->{len(all_colors)}")
for c in all_colors:
c.nn(all_colors)
smallest_dist = 9999999999999
largest_dist = 0
for c in all_colors:
dist = rgbdist(c, c.nearest_neighbor)
if dist > largest_dist: largest_dist = dist
if dist < smallest_dist: smallest_dist = dist
#print(f"{c.prefix}:{c.name} -> {c.nearest_neighbor.prefix}:{c.nearest_neighbor.name}\t{rgbdist(c, c.nearest_neighbor):.2f}")
# questions -
# are there any colors where their nearest neighbor's nearest neighbor isn't them? (There should be)
nonnear_pairs = 0
for c in all_colors:
neighbor = c.nearest_neighbor
their_neighbor = neighbor.nearest_neighbor
if c is not their_neighbor:
#print(f"{c.prefix}:{c.name} -> {neighbor.prefix}:{neighbor.name} -> {their_neighbor.prefix}:{their_neighbor.name}")
nonnear_pairs += 1
print("Non-near pairs:", nonnear_pairs)
#print(f"{c.prefix}:{c.name} -> {c.nearest_neighbor.prefix}:{c.nearest_neighbor.name}\t{rgbdist(c, c.nearest_neighbor):.2f}")
# Are there duplicates? They should be removed from the palette that won't be used
dupes = 0
for c in all_colors:
for c2 in all_colors:
if c is c2: continue
if c.r == c2.r and c.g == c2.g and c.b == c2.b:
dupes += 1
print("dupes:", dupes, "this many will need to be removed:", dupes / 2)
# What order to put them in? Do we want large radiuses first, or some sort of "common color" table?
# does manhattan distance change any answers over the 16.7M RGB values?
# What's the worst case lookup? (Checking all 1200 colors to find the name?)

View File

@ -56,7 +56,7 @@ PyObject* PyInit_mcrfpy()
using namespace mcrfpydef;
PyTypeObject* pytypes[] = {
/*SFML exposed types*/
&PyColorType, &PyFontType, &PyTextureType,
&PyColorType, /*&PyLinkedColorType,*/ &PyFontType, &PyTextureType,
/*UI widgets*/
&PyUICaptionType, &PyUISpriteType, &PyUIFrameType, &PyUIEntityType, &PyUIGridType,

151
src/PyColor.cpp Normal file
View File

@ -0,0 +1,151 @@
#include "PyColor.h"
PyGetSetDef PyColor::getsetters[] = {
{"r", (getter)PyColor::get_member, (setter)PyColor::set_member, "Red component", (void*)0},
{"g", (getter)PyColor::get_member, (setter)PyColor::set_member, "Green component", (void*)1},
{"b", (getter)PyColor::get_member, (setter)PyColor::set_member, "Blue component", (void*)2},
{"a", (getter)PyColor::get_member, (setter)PyColor::set_member, "Alpha component", (void*)3},
{NULL}
};
PyColor::PyColor(sf::Color target)
:data(target) {}
PyObject* PyColor::pyObject()
{
PyObject* obj = PyType_GenericAlloc(&mcrfpydef::PyColorType, 0);
Py_INCREF(obj);
PyColorObject* self = (PyColorObject*)obj;
self->data = data;
return obj;
}
sf::Color PyColor::fromPy(PyObject* obj)
{
PyColorObject* self = (PyColorObject*)obj;
return self->data;
}
sf::Color PyColor::fromPy(PyColorObject* self)
{
return self->data;
}
void PyColor::set(sf::Color color)
{
data = color;
}
sf::Color PyColor::get()
{
return data;
}
Py_hash_t PyColor::hash(PyObject* obj)
{
auto self = (PyColorObject*)obj;
Py_hash_t value = 0;
value += self->data.r;
value << 8; value += self->data.g;
value << 8; value += self->data.b;
value << 8; value += self->data.a;
return value;
}
PyObject* PyColor::repr(PyObject* obj)
{
PyColorObject* self = (PyColorObject*)obj;
std::ostringstream ss;
sf::Color c = self->data;
ss << "<Color (" << int(c.r) << ", " << int(c.g) << ", " << int(c.b) << ", " << int(c.a) << ")>";
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
int PyColor::init(PyColorObject* self, PyObject* args, PyObject* kwds)
{
using namespace mcrfpydef;
static const char* keywords[] = { "r", "g", "b", "a", nullptr };
PyObject* leader;
int r = -1, g = -1, b = -1, a = 255;
if (!PyArg_ParseTupleAndKeywords, args, kwds, "O|iii", leader, &g, &b, &a)
{
PyErr_SetString(PyExc_TypeError, "mcrfpy.Color requires a color object, 3-tuple, 4-tuple, color name, or integer values within 0-255 (r, g, b, optionally a)");
return -1;
}
// if the "r" arg is already a color, yoink that color value
if (PyObject_IsInstance(leader, (PyObject*)&PyColorType))
{
self->data = ((PyColorObject*)leader)->data;
return 0;
}
// else if the "r" arg is a 3-tuple, initialize to (r, g, b, 255)
// (if the "r" arg is a 4-tuple, initialize to (r, g, b, a))
else if (PyTuple_Check(leader))
{
if (PyTuple_Size(leader) < 3 && PyTuple_Size(leader) > 4)
{
PyErr_SetString(PyExc_TypeError, "Invalid tuple length: mcrfpy.Color requires a color object, 3-tuple, 4-tuple, color name, or integer values within 0-255 (r, g, b, optionally a)");
return -1;
}
r = PyLong_AsLong(PyTuple_GetItem(leader, 0));
g = PyLong_AsLong(PyTuple_GetItem(leader, 1));
b = PyLong_AsLong(PyTuple_GetItem(leader, 2));
//a = 255; //default value
if (PyTuple_Size(leader) == 4)
{
a = PyLong_AsLong(PyTuple_GetItem(leader, 3));
}
// value validation
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255)
{
PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255.");
return -1;
}
self->data = sf::Color(r, g, b, a);
}
// else if the "r" arg is a string, initialize to {color lookup function value}
else if (PyUnicode_Check(leader))
{
PyErr_SetString(Py_NotImplemented, "Color names aren't ready yet");
return -1;
}
// else -
else if (!PyLong_Check(leader))
{
PyErr_SetString(PyExc_TypeError, "mcrfpy.Color requires a color object, 3-tuple, 4-tuple, color name, or integer values within 0-255 (r, g, b, optionally a)");
return -1;
}
r = PyLong_AsLong(leader);
// assert r, g, b are present and ints in range (0, 255) - if not enough ints were provided to the args/kwds parsed by init, g and/or b will still hold -1.
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255)
{
PyErr_SetString(PyExc_ValueError, "R, G, B values are required, A value is optional; Color values must be between 0 and 255.");
return -1;
}
self->data = sf::Color(r, g, b, a);
return 0;
}
PyObject* PyColor::pynew(PyTypeObject* type, PyObject* args, PyObject* kwds)
{
return (PyObject*)type->tp_alloc(type, 0);
}
PyObject* PyColor::get_member(PyObject* obj, void* closure)
{
// TODO
return Py_None;
}
int PyColor::set_member(PyObject* obj, PyObject* value, void* closure)
{
// TODO
return 0;
}

47
src/PyColor.h Normal file
View File

@ -0,0 +1,47 @@
#pragma once
#include "Common.h"
#include "Python.h"
class PyColor;
class UIDrawable; // forward declare for pointer
typedef struct {
PyObject_HEAD
sf::Color data;
} PyColorObject;
class PyColor
{
private:
public:
sf::Color data;
PyColor(sf::Color);
void set(sf::Color);
sf::Color get();
PyObject* pyObject();
static sf::Color fromPy(PyObject*);
static sf::Color fromPy(PyColorObject*);
static PyObject* repr(PyObject*);
static Py_hash_t hash(PyObject*);
static int init(PyColorObject*, PyObject*, PyObject*);
static PyObject* pynew(PyTypeObject* type, PyObject* args=NULL, PyObject* kwds=NULL);
static PyObject* get_member(PyObject*, void*);
static int set_member(PyObject*, PyObject*, void*);
static PyGetSetDef getsetters[];
};
namespace mcrfpydef {
static PyTypeObject PyColorType = {
.tp_name = "mcrfpy.Color",
.tp_basicsize = sizeof(PyColorObject),
.tp_itemsize = 0,
.tp_repr = PyColor::repr,
.tp_hash = PyColor::hash,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = PyDoc_STR("SFML Color Object"),
.tp_getset = PyColor::getsetters,
.tp_init = (initproc)PyColor::init,
.tp_new = PyColor::pynew,
};
}

222
src/UI.h
View File

@ -5,8 +5,13 @@
#include "IndexTexture.h"
#include "Resources.h"
#include <list>
#define ui_h
#include "PyCallable.h"
#include "PyTexture.h"
#include "PyColor.h"
//#include "PyLinkedColor.h"
enum PyObjectsEnum : int
{
@ -46,10 +51,12 @@ typedef struct {
} PyColorObject;
*/
/* // Moved to PyColor.h
typedef struct {
PyObject_HEAD
std::shared_ptr<sf::Color> data;
} PyColorObject;
*/
class UIFrame: public UIDrawable
{
@ -444,89 +451,89 @@ static int PyUIDrawable_set_click(PyUIGridObject* self, PyObject* value, void* c
*/
static PyObject* PyColor_get_member(PyColorObject* self, void* closure)
{
auto member_ptr = reinterpret_cast<long>(closure);
if (member_ptr == 0)
return PyLong_FromLong(self->data->r);
else if (member_ptr == 1)
return PyLong_FromLong(self->data->g);
else if (member_ptr == 2)
return PyLong_FromLong(self->data->b);
else if (member_ptr == 3)
return PyLong_FromLong(self->data->a);
else
{
PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
return nullptr;
}
}
static int PyColor_set_member(PyColorObject* self, PyObject* value, void* closure)
{
if (PyLong_Check(value))
{
long int_val = PyLong_AsLong(value);
if (int_val < 0)
int_val = 0;
else if (int_val > 255)
int_val = 255;
auto member_ptr = reinterpret_cast<long>(closure);
if (member_ptr == 0)
self->data->r = static_cast<sf::Uint8>(int_val);
else if (member_ptr == 1)
self->data->g = static_cast<sf::Uint8>(int_val);
else if (member_ptr == 2)
self->data->b = static_cast<sf::Uint8>(int_val);
else if (member_ptr == 3)
self->data->a = static_cast<sf::Uint8>(int_val);
}
else
{
PyErr_SetString(PyExc_TypeError, "Value must be an integer.");
return -1;
}
return 0;
}
static PyGetSetDef PyColor_getsetters[] = {
{"r", (getter)PyColor_get_member, (setter)PyColor_set_member, "Red component", (void*)0},
{"g", (getter)PyColor_get_member, (setter)PyColor_set_member, "Green component", (void*)1},
{"b", (getter)PyColor_get_member, (setter)PyColor_set_member, "Blue component", (void*)2},
{"a", (getter)PyColor_get_member, (setter)PyColor_set_member, "Alpha component", (void*)3},
{NULL}
};
static PyTypeObject PyColorType = {
//PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "mcrfpy.Color",
.tp_basicsize = sizeof(PyColorObject),
.tp_itemsize = 0,
.tp_dealloc = (destructor)[](PyObject* self)
{
PyColorObject* obj = (PyColorObject*)self;
obj->data.reset();
Py_TYPE(self)->tp_free(self);
},
//.tp_repr = (reprfunc)PyUIFrame_repr,
//.tp_hash = NULL,
//.tp_iter
//.tp_iternext
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = PyDoc_STR("SFML Color object (RGBA)"),
//.tp_methods = PyUIFrame_methods,
//.tp_members = PyColor_members,
.tp_getset = PyColor_getsetters,
//.tp_base = NULL,
//.tp_init = (initproc)PyUIFrame_init,
.tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject*
{
PyColorObject* self = (PyColorObject*)type->tp_alloc(type, 0);
if (self) self->data = std::make_shared<sf::Color>();
return (PyObject*)self;
}
};
// static PyObject* PyColor_get_member(PyColorObject* self, void* closure)
// {
// auto member_ptr = reinterpret_cast<long>(closure);
// if (member_ptr == 0)
// return PyLong_FromLong(self->data->r);
// else if (member_ptr == 1)
// return PyLong_FromLong(self->data->g);
// else if (member_ptr == 2)
// return PyLong_FromLong(self->data->b);
// else if (member_ptr == 3)
// return PyLong_FromLong(self->data->a);
// else
// {
// PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
// return nullptr;
// }
// }
//
// static int PyColor_set_member(PyColorObject* self, PyObject* value, void* closure)
// {
// if (PyLong_Check(value))
// {
// long int_val = PyLong_AsLong(value);
// if (int_val < 0)
// int_val = 0;
// else if (int_val > 255)
// int_val = 255;
// auto member_ptr = reinterpret_cast<long>(closure);
// if (member_ptr == 0)
// self->data->r = static_cast<sf::Uint8>(int_val);
// else if (member_ptr == 1)
// self->data->g = static_cast<sf::Uint8>(int_val);
// else if (member_ptr == 2)
// self->data->b = static_cast<sf::Uint8>(int_val);
// else if (member_ptr == 3)
// self->data->a = static_cast<sf::Uint8>(int_val);
// }
// else
// {
// PyErr_SetString(PyExc_TypeError, "Value must be an integer.");
// return -1;
// }
// return 0;
// }
//
// static PyGetSetDef PyColor_getsetters[] = {
// {"r", (getter)PyColor_get_member, (setter)PyColor_set_member, "Red component", (void*)0},
// {"g", (getter)PyColor_get_member, (setter)PyColor_set_member, "Green component", (void*)1},
// {"b", (getter)PyColor_get_member, (setter)PyColor_set_member, "Blue component", (void*)2},
// {"a", (getter)PyColor_get_member, (setter)PyColor_set_member, "Alpha component", (void*)3},
// {NULL}
// };
//
//
// static PyTypeObject PyColorType = {
// //PyVarObject_HEAD_INIT(NULL, 0)
// .tp_name = "mcrfpy.Color",
// .tp_basicsize = sizeof(PyColorObject),
// .tp_itemsize = 0,
// .tp_dealloc = (destructor)[](PyObject* self)
// {
// PyColorObject* obj = (PyColorObject*)self;
// obj->data.reset();
// Py_TYPE(self)->tp_free(self);
// },
// //.tp_repr = (reprfunc)PyUIFrame_repr,
// //.tp_hash = NULL,
// //.tp_iter
// //.tp_iternext
// .tp_flags = Py_TPFLAGS_DEFAULT,
// .tp_doc = PyDoc_STR("SFML Color object (RGBA)"),
// //.tp_methods = PyUIFrame_methods,
// //.tp_members = PyColor_members,
// .tp_getset = PyColor_getsetters,
// //.tp_base = NULL,
// //.tp_init = (initproc)PyUIFrame_init,
// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject*
// {
// PyColorObject* self = (PyColorObject*)type->tp_alloc(type, 0);
// if (self) self->data = std::make_shared<sf::Color>();
// return (PyObject*)self;
// }
// };
/*
*
@ -591,32 +598,50 @@ static int PyUIDrawable_set_click(PyUIGridObject* self, PyObject* value, void* c
}
// TODO: manually calling tp_alloc to create a PyColorObject seems like an antipattern
PyTypeObject* colorType = &PyColorType;
PyObject* pyColor = colorType->tp_alloc(colorType, 0);
/*
PyTypeObject* colorType = &PyLinkedColorType;
PyObject* pyColor = colorType->tp_alloc(colorLinkedType, 0);
if (pyColor == NULL)
{
std::cout << "failure to allocate mcrfpy.Color / PyColorType" << std::endl;
std::cout << "failure to allocate mcrfpy.LinkedColor / PyLinkedColorType" << std::endl;
return NULL;
}
PyColorObject* pyColorObj = reinterpret_cast<PyColorObject*>(pyColor);
PyColorObject* pyColorObj = reinterpret_cast<PyLinkedColorObject*>(pyColor);
*/
// fetch correct member data
sf::Color color;
//sf::Color (*cgetter)();
//void (*csetter)(sf::Color);
//std::function<void(sf::Color)> csetter;
//std::function<sf::Color()> cgetter;
if (member_ptr == 0)
{
color = self->data->text.getFillColor();
//return Py_BuildValue("(iii)", color.r, color.g, color.b);
//csetter = &self->data->text.setFillColor;
//cgetter = &self->data->text.getFillColor;
//csetter = [s = self->data](sf::Color c){s->text.setFillColor(c);};
//cgetter = [s = self->data](){return s->text.getFillColor();};
}
else if (member_ptr == 1)
{
color = self->data->text.getOutlineColor();
//return Py_BuildValue("(iii)", color.r, color.g, color.b);
//csetter = &self->data->text.setOutlineColor;
//cgetter = &self->data->text.getOutlineColor;
//csetter = [s = self->data](sf::Color c){s->text.setOutlineColor(c);};
//cgetter = [s = self->data](){return s->text.getOutlineColor();};
}
// initialize new mcrfpy.Color instance
pyColorObj->data = std::make_shared<sf::Color>(color);
//pyColorObj->data = std::make_shared<sf::Color>(color);
//PyLinkedColor::fromPy(pyColorObj).set(color);
//auto linkedcolor = PyLinkedColor(csetter, cgetter, self->data, member_ptr);
//linkedcolor.set(color); // don't need to set a linked color!
return pyColor;
//return pyColor;
return PyColor(color).pyObject();
}
static int PyUICaption_set_color_member(PyUICaptionObject* self, PyObject* value, void* closure)
@ -627,11 +652,20 @@ static int PyUIDrawable_set_click(PyUIGridObject* self, PyObject* value, void* c
if (PyObject_IsInstance(value, (PyObject*)&PyColorType))
{
// get value from mcrfpy.Color instance
/*
PyColorObject* color = reinterpret_cast<PyColorObject*>(value);
r = color->data->r;
g = color->data->g;
b = color->data->b;
a = color->data->a;
*/
//std::cout << "Build LinkedColor" << std::endl;
//auto lc = PyLinkedColor::fromPy(value);
auto c = ((PyColorObject*)value)->data;
//std::cout << "Fetch value" << std::endl;
//auto c = lc.get();
r = c.r; g = c.g; b = c.b; a = c.a;
std::cout << "got " << int(r) << ", " << int(g) << ", " << int(b) << ", " << int(a) << std::endl;
}
else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4)
{
@ -898,9 +932,11 @@ static int PyUIDrawable_set_click(PyUIGridObject* self, PyObject* value, void* c
}
// initialize new mcrfpy.Color instance
pyColorObj->data = std::make_shared<sf::Color>(color);
//pyColorObj->data = std::make_shared<sf::Color>(color);
//PyColor::fromPy(pyColorObj).set(color);
return pyColor;
// TODO - supposed to return Linked
return PyColor(color).pyObject();
}
static int PyUIFrame_set_color_member(PyUIFrameObject* self, PyObject* value, void* closure)
@ -911,12 +947,16 @@ static int PyUIDrawable_set_click(PyUIGridObject* self, PyObject* value, void* c
if (PyObject_IsInstance(value, (PyObject*)&PyColorType))
{
// get value from mcrfpy.Color instance
/*
PyColorObject* color = reinterpret_cast<PyColorObject*>(value);
r = color->data->r;
g = color->data->g;
b = color->data->b;
a = color->data->a;
std::cout << "using color: " << r << " " << g << " " << b << " " << a << std::endl;
*/
sf::Color c = ((PyColorObject*)value)->data;
r = c.r; g = c.g; b = c.b; a = c.a;
}
else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4)
{

View File

@ -1,221 +1,51 @@
#print("Hello mcrogueface")
import mcrfpy
import cos_play
# Universal stuff
font = mcrfpy.Font("assets/JetbrainsMono.ttf")
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16) #12, 11)
texture_cold = mcrfpy.Texture("assets/kenney_ice.png", 16, 16) #12, 11)
texture_hot = mcrfpy.Texture("assets/kenney_lava.png", 16, 16) #12, 11)
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
# Test stuff
mcrfpy.createScene("boom")
mcrfpy.setScene("boom")
ui = mcrfpy.sceneUI("boom")
box = mcrfpy.Frame(40, 60, 200, 300, fill_color=(255,128,0), outline=4.0, outline_color=(64,64,255,96))
ui.append(box)
# build test widgets
#caption = mcrfpy.Caption(10, 10, "Clicky", font, (255, 255, 255, 255), (0, 0, 0, 255))
#box.click = lambda x, y, btn, type: print("Hello callback: ", x, y, btn, type)
#box.children.append(caption)
mcrfpy.createScene("pytest")
mcrfpy.setScene("pytest")
ui = mcrfpy.sceneUI("pytest")
test_sprite_number = 86
sprite = mcrfpy.Sprite(20, 60, texture, test_sprite_number, 4.0)
spritecap = mcrfpy.Caption(5, 5, "60", font)
def click_sprite(x, y, btn, action):
global test_sprite_number
if action != "start": return
if btn in ("left", "wheel_up"):
test_sprite_number -= 1
elif btn in ("right", "wheel_down"):
test_sprite_number += 1
sprite.sprite_number = test_sprite_number # TODO - inconsistent naming for __init__, __repr__ and getsetter: sprite_number vs sprite_index
spritecap.text = test_sprite_number
# Frame
f = mcrfpy.Frame(25, 19, 462, 346, fill_color=(255, 92, 92))
# fill (LinkedColor / Color): f.fill_color
# outline (LinkedColor / Color): f.outline_color
# pos (LinkedVector / Vector): f.pos
# size (LinkedVector / Vector): f.size
sprite.click = click_sprite # TODO - sprites don't seem to correct for screen position or scale when clicking
box.children.append(sprite)
box.children.append(spritecap)
box.click = click_sprite
# Caption
c = mcrfpy.Caption(512+25, 19, "Hi.", font)
# fill (LinkedColor / Color): c.fill_color
#color_val = c.fill_color
print(c.fill_color)
print("Set a fill color")
c.fill_color = (255, 255, 255)
print("Lol, did it segfault?")
# outline (LinkedColor / Color): c.outline_color
# font (Font): c.font
# pos (LinkedVector / Vector): c.pos
f_a = mcrfpy.Frame(250, 60, 80, 80, fill_color=(255, 92, 92))
f_a_txt = mcrfpy.Caption(5, 5, "0", font)
# Sprite
s = mcrfpy.Sprite(25, 384+19, texture, 86, 9.0)
# pos (LinkedVector / Vector): s.pos
# texture (Texture): s.texture
f_b = mcrfpy.Frame(340, 60, 80, 80, fill_color=(92, 255, 92))
f_b_txt = mcrfpy.Caption(5, 5, "0", font)
# Grid
g = mcrfpy.Grid(10, 10, texture, 512+25, 384+19, 462, 346)
# texture (Texture): g.texture
# pos (LinkedVector / Vector): g.pos
# size (LinkedVector / Vector): g.size
f_c = mcrfpy.Frame(430, 60, 80, 80, fill_color=(92, 92, 255))
f_c_txt = mcrfpy.Caption(5, 5, "0", font)
for _x in range(10):
for _y in range(10):
g.at((_x, _y)).color = (255 - _x*25, 255 - _y*25, 255)
g.zoom = 2.0
[ui.append(d) for d in (f, c, s, g)]
ui.append(f_a)
f_a.children.append(f_a_txt)
ui.append(f_b)
f_b.children.append(f_b_txt)
ui.append(f_c)
f_c.children.append(f_c_txt)
print("built!")
import sys
def ding(*args):
f_a_txt.text = str(sys.getrefcount(ding)) + " refs"
f_b_txt.text = sys.getrefcount(dong)
f_c_txt.text = sys.getrefcount(stress_test)
# tests
def dong(*args):
f_a_txt.text = str(sys.getrefcount(ding)) + " refs"
f_b_txt.text = sys.getrefcount(dong)
f_c_txt.text = sys.getrefcount(stress_test)
running = False
timers = []
def add_ding():
global timers
n = len(timers)
mcrfpy.setTimer(f"timer{n}", ding, 100)
print("+1 ding:", timers)
def add_dong():
global timers
n = len(timers)
mcrfpy.setTimer(f"timer{n}", dong, 100)
print("+1 dong:", timers)
def remove_random():
global timers
target = random.choice(timers)
print("-1 timer:", target)
print("remove from list")
timers.remove(target)
print("delTimer")
mcrfpy.delTimer(target)
print("done")
import random
import time
def stress_test(*args):
global running
global timers
if not running:
print("stress test initial")
running = True
timers.append("recurse")
add_ding()
add_dong()
mcrfpy.setTimer("recurse", stress_test, 1000)
mcrfpy.setTimer("terminate", lambda *args: mcrfpy.delTimer("recurse"), 30000)
ding(); dong()
else:
#print("stress test random activity")
#random.choice([
# add_ding,
# add_dong,
# remove_random
# ])()
#print(timers)
print("Segfaultin' time")
mcrfpy.delTimer("recurse")
print("Does this still work?")
time.sleep(0.5)
print("How about now?")
stress_test()
# Loading Screen
mcrfpy.createScene("loading")
ui = mcrfpy.sceneUI("loading")
#mcrfpy.setScene("loading")
logo_texture = mcrfpy.Texture("assets/temp_logo.png", 1024, 1024)#1, 1)
logo_sprite = mcrfpy.Sprite(50, 50, logo_texture, 0, 0.5)
ui.append(logo_sprite)
logo_sprite.click = lambda *args: mcrfpy.setScene("menu")
logo_caption = mcrfpy.Caption(70, 600, "Click to Proceed", font, (255, 0, 0, 255), (0, 0, 0, 255))
logo_caption.fill_color =(255, 0, 0, 255)
ui.append(logo_caption)
# menu screen
mcrfpy.createScene("menu")
for e in [
mcrfpy.Caption(10, 10, "Crypt of Sokoban", font, (255, 255, 255), (0, 0, 0)),
mcrfpy.Caption(20, 55, "a McRogueFace demo project", font, (192, 192, 192), (0, 0, 0)),
mcrfpy.Frame(15, 70, 150, 60, fill_color=(64, 64, 128)),
mcrfpy.Frame(15, 145, 150, 60, fill_color=(64, 64, 128)),
mcrfpy.Frame(15, 220, 150, 60, fill_color=(64, 64, 128)),
mcrfpy.Frame(15, 295, 150, 60, fill_color=(64, 64, 128)),
#mcrfpy.Frame(900, 10, 100, 100, fill_color=(255, 0, 0)),
]:
mcrfpy.sceneUI("menu").append(e)
def click_once(fn):
def wraps(*args, **kwargs):
#print(args)
action = args[3]
if action != "start": return
return fn(*args, **kwargs)
return wraps
@click_once
def asdf(x, y, btn, action):
print(f"clicky @({x},{y}) {action}->{btn}")
@click_once
def clicked_exit(*args):
mcrfpy.exit()
menu_btns = [
("Boom", lambda *args: 1 / 0),
("Exit", clicked_exit),
("About", lambda *args: mcrfpy.setScene("about")),
("Settings", lambda *args: mcrfpy.setScene("settings")),
("Start", lambda *args: mcrfpy.setScene("play"))
]
for i in range(len(mcrfpy.sceneUI("menu"))):
e = mcrfpy.sceneUI("menu")[i] # TODO - fix iterator
#print(e, type(e))
if type(e) is not mcrfpy.Frame: continue
label, fn = menu_btns.pop()
#print(label)
e.children.append(mcrfpy.Caption(5, 5, label, font, (192, 192, 255), (0,0,0)))
e.click = fn
# settings screen
mcrfpy.createScene("settings")
window_scaling = 1.0
scale_caption = mcrfpy.Caption(180, 70, "1.0x", font, (255, 255, 255), (0, 0, 0))
scale_caption.fill_color = (255, 255, 255) # TODO - mcrfpy.Caption.__init__ is not setting colors
for e in [
mcrfpy.Caption(10, 10, "Settings", font, (255, 255, 255), (0, 0, 0)),
mcrfpy.Frame(15, 70, 150, 60, fill_color=(64, 64, 128)), # +
mcrfpy.Frame(300, 70, 150, 60, fill_color=(64, 64, 128)), # -
mcrfpy.Frame(15, 295, 150, 60, fill_color=(64, 64, 128)),
scale_caption,
]:
mcrfpy.sceneUI("settings").append(e)
@click_once
def game_scale(x, y, btn, action, delta):
global window_scaling
print(f"WIP - scale the window from {window_scaling:.1f} to {window_scaling+delta:.1f}")
window_scaling += delta
scale_caption.text = f"{window_scaling:.1f}x"
mcrfpy.setScale(window_scaling)
#mcrfpy.setScale(2)
settings_btns = [
("back", lambda *args: mcrfpy.setScene("menu")),
("-", lambda x, y, btn, action: game_scale(x, y, btn, action, -0.1)),
("+", lambda x, y, btn, action: game_scale(x, y, btn, action, +0.1))
]
for i in range(len(mcrfpy.sceneUI("settings"))):
e = mcrfpy.sceneUI("settings")[i] # TODO - fix iterator
#print(e, type(e))
if type(e) is not mcrfpy.Frame: continue
label, fn = settings_btns.pop()
#print(label, fn)
e.children.append(mcrfpy.Caption(5, 5, label, font, (192, 192, 255), (0,0,0)))
e.click = fn

221
src/scripts/game_old.py Normal file
View File

@ -0,0 +1,221 @@
#print("Hello mcrogueface")
import mcrfpy
import cos_play
# Universal stuff
font = mcrfpy.Font("assets/JetbrainsMono.ttf")
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16) #12, 11)
texture_cold = mcrfpy.Texture("assets/kenney_ice.png", 16, 16) #12, 11)
texture_hot = mcrfpy.Texture("assets/kenney_lava.png", 16, 16) #12, 11)
# Test stuff
mcrfpy.createScene("boom")
mcrfpy.setScene("boom")
ui = mcrfpy.sceneUI("boom")
box = mcrfpy.Frame(40, 60, 200, 300, fill_color=(255,128,0), outline=4.0, outline_color=(64,64,255,96))
ui.append(box)
#caption = mcrfpy.Caption(10, 10, "Clicky", font, (255, 255, 255, 255), (0, 0, 0, 255))
#box.click = lambda x, y, btn, type: print("Hello callback: ", x, y, btn, type)
#box.children.append(caption)
test_sprite_number = 86
sprite = mcrfpy.Sprite(20, 60, texture, test_sprite_number, 4.0)
spritecap = mcrfpy.Caption(5, 5, "60", font)
def click_sprite(x, y, btn, action):
global test_sprite_number
if action != "start": return
if btn in ("left", "wheel_up"):
test_sprite_number -= 1
elif btn in ("right", "wheel_down"):
test_sprite_number += 1
sprite.sprite_number = test_sprite_number # TODO - inconsistent naming for __init__, __repr__ and getsetter: sprite_number vs sprite_index
spritecap.text = test_sprite_number
sprite.click = click_sprite # TODO - sprites don't seem to correct for screen position or scale when clicking
box.children.append(sprite)
box.children.append(spritecap)
box.click = click_sprite
f_a = mcrfpy.Frame(250, 60, 80, 80, fill_color=(255, 92, 92))
f_a_txt = mcrfpy.Caption(5, 5, "0", font)
f_b = mcrfpy.Frame(340, 60, 80, 80, fill_color=(92, 255, 92))
f_b_txt = mcrfpy.Caption(5, 5, "0", font)
f_c = mcrfpy.Frame(430, 60, 80, 80, fill_color=(92, 92, 255))
f_c_txt = mcrfpy.Caption(5, 5, "0", font)
ui.append(f_a)
f_a.children.append(f_a_txt)
ui.append(f_b)
f_b.children.append(f_b_txt)
ui.append(f_c)
f_c.children.append(f_c_txt)
import sys
def ding(*args):
f_a_txt.text = str(sys.getrefcount(ding)) + " refs"
f_b_txt.text = sys.getrefcount(dong)
f_c_txt.text = sys.getrefcount(stress_test)
def dong(*args):
f_a_txt.text = str(sys.getrefcount(ding)) + " refs"
f_b_txt.text = sys.getrefcount(dong)
f_c_txt.text = sys.getrefcount(stress_test)
running = False
timers = []
def add_ding():
global timers
n = len(timers)
mcrfpy.setTimer(f"timer{n}", ding, 100)
print("+1 ding:", timers)
def add_dong():
global timers
n = len(timers)
mcrfpy.setTimer(f"timer{n}", dong, 100)
print("+1 dong:", timers)
def remove_random():
global timers
target = random.choice(timers)
print("-1 timer:", target)
print("remove from list")
timers.remove(target)
print("delTimer")
mcrfpy.delTimer(target)
print("done")
import random
import time
def stress_test(*args):
global running
global timers
if not running:
print("stress test initial")
running = True
timers.append("recurse")
add_ding()
add_dong()
mcrfpy.setTimer("recurse", stress_test, 1000)
mcrfpy.setTimer("terminate", lambda *args: mcrfpy.delTimer("recurse"), 30000)
ding(); dong()
else:
#print("stress test random activity")
#random.choice([
# add_ding,
# add_dong,
# remove_random
# ])()
#print(timers)
print("Segfaultin' time")
mcrfpy.delTimer("recurse")
print("Does this still work?")
time.sleep(0.5)
print("How about now?")
stress_test()
# Loading Screen
mcrfpy.createScene("loading")
ui = mcrfpy.sceneUI("loading")
#mcrfpy.setScene("loading")
logo_texture = mcrfpy.Texture("assets/temp_logo.png", 1024, 1024)#1, 1)
logo_sprite = mcrfpy.Sprite(50, 50, logo_texture, 0, 0.5)
ui.append(logo_sprite)
logo_sprite.click = lambda *args: mcrfpy.setScene("menu")
logo_caption = mcrfpy.Caption(70, 600, "Click to Proceed", font, (255, 0, 0, 255), (0, 0, 0, 255))
#logo_caption.fill_color =(255, 0, 0, 255)
ui.append(logo_caption)
# menu screen
mcrfpy.createScene("menu")
for e in [
mcrfpy.Caption(10, 10, "Crypt of Sokoban", font, (255, 255, 255), (0, 0, 0)),
mcrfpy.Caption(20, 55, "a McRogueFace demo project", font, (192, 192, 192), (0, 0, 0)),
mcrfpy.Frame(15, 70, 150, 60, fill_color=(64, 64, 128)),
mcrfpy.Frame(15, 145, 150, 60, fill_color=(64, 64, 128)),
mcrfpy.Frame(15, 220, 150, 60, fill_color=(64, 64, 128)),
mcrfpy.Frame(15, 295, 150, 60, fill_color=(64, 64, 128)),
#mcrfpy.Frame(900, 10, 100, 100, fill_color=(255, 0, 0)),
]:
mcrfpy.sceneUI("menu").append(e)
def click_once(fn):
def wraps(*args, **kwargs):
#print(args)
action = args[3]
if action != "start": return
return fn(*args, **kwargs)
return wraps
@click_once
def asdf(x, y, btn, action):
print(f"clicky @({x},{y}) {action}->{btn}")
@click_once
def clicked_exit(*args):
mcrfpy.exit()
menu_btns = [
("Boom", lambda *args: 1 / 0),
("Exit", clicked_exit),
("About", lambda *args: mcrfpy.setScene("about")),
("Settings", lambda *args: mcrfpy.setScene("settings")),
("Start", lambda *args: mcrfpy.setScene("play"))
]
for i in range(len(mcrfpy.sceneUI("menu"))):
e = mcrfpy.sceneUI("menu")[i] # TODO - fix iterator
#print(e, type(e))
if type(e) is not mcrfpy.Frame: continue
label, fn = menu_btns.pop()
#print(label)
e.children.append(mcrfpy.Caption(5, 5, label, font, (192, 192, 255), (0,0,0)))
e.click = fn
# settings screen
mcrfpy.createScene("settings")
window_scaling = 1.0
scale_caption = mcrfpy.Caption(180, 70, "1.0x", font, (255, 255, 255), (0, 0, 0))
#scale_caption.fill_color = (255, 255, 255) # TODO - mcrfpy.Caption.__init__ is not setting colors
for e in [
mcrfpy.Caption(10, 10, "Settings", font, (255, 255, 255), (0, 0, 0)),
mcrfpy.Frame(15, 70, 150, 60, fill_color=(64, 64, 128)), # +
mcrfpy.Frame(300, 70, 150, 60, fill_color=(64, 64, 128)), # -
mcrfpy.Frame(15, 295, 150, 60, fill_color=(64, 64, 128)),
scale_caption,
]:
mcrfpy.sceneUI("settings").append(e)
@click_once
def game_scale(x, y, btn, action, delta):
global window_scaling
print(f"WIP - scale the window from {window_scaling:.1f} to {window_scaling+delta:.1f}")
window_scaling += delta
scale_caption.text = f"{window_scaling:.1f}x"
mcrfpy.setScale(window_scaling)
#mcrfpy.setScale(2)
settings_btns = [
("back", lambda *args: mcrfpy.setScene("menu")),
("-", lambda x, y, btn, action: game_scale(x, y, btn, action, -0.1)),
("+", lambda x, y, btn, action: game_scale(x, y, btn, action, +0.1))
]
for i in range(len(mcrfpy.sceneUI("settings"))):
e = mcrfpy.sceneUI("settings")[i] # TODO - fix iterator
#print(e, type(e))
if type(e) is not mcrfpy.Frame: continue
label, fn = settings_btns.pop()
#print(label, fn)
e.children.append(mcrfpy.Caption(5, 5, label, font, (192, 192, 255), (0,0,0)))
e.click = fn

168
wikicrayons_colors.txt Normal file
View File

@ -0,0 +1,168 @@
Red #ED0A3F
Maroon #C32148
Scarlet #FD0E35
Brick Red #C62D42
English Vermilion #CC474B
Madder Lake #CC3336
Permanent Geranium Lake #E12C2C
Maximum Red #D92121
Chestnut #B94E48
Orange-Red #FF5349
Sunset Orange #FE4C40
Bittersweet #FE6F5E
Dark Venetian Red #B33B24
Venetian Red #CC553D
Light Venetian Red #E6735C
Vivid Tangerine #FF9980
Middle Red #E58E73
Burnt Orange #FF7034
Red-Orange #FF3F34
Orange #FF8833
Macaroni and Cheese #FFB97B
Middle Yellow Red #ECAC76
Mango Tango #E77200
Yellow-Orange #FFAE42
Maximum Yellow Red #F2BA49
Banana Mania #FBE7B2
Maize #F2C649
Orange-Yellow #F8D568
Goldenrod #FCD667
Dandelion #FED85D
Yellow #FBE870
Green-Yellow #F1E788
Middle Yellow #FFEB00
Olive Green #B5B35C
Spring Green #ECEBBD
Maximum Yellow #FAFA37
Canary #FFFF99
Lemon Yellow #FFFF9F
Maximum Green Yellow #D9E650
Middle Green Yellow #ACBF60
Inchworm #B0E313
Light Chrome Green #BEE64B
Yellow-Green #C5E17A
Maximum Green #5E8C31
Asparagus #7BA05B
Granny Smith Apple #9DE093
Fern #63B76C
Middle Green #4D8C57
Green #01A638
Medium Chrome Green #6CA67C
Forest Green #5FA777
Sea Green #93DFB8
Shamrock #33CC99
Mountain Meadow #1AB385
Jungle Green #29AB87
Caribbean Green #00CC99
Tropical Rain Forest #00755E
Middle Blue Green #8DD9CC
Pine Green #01796F
Maximum Blue Green #30BFBF
Robin's Egg Blue #00CCCC
Teal Blue #008080
Light Blue #8FD8D8
Aquamarine #458B74
Turquoise Blue #6CDAE7
Outer Space #2D383A
Sky Blue #76D7EA
Middle Blue #7ED4E6
Blue-Green #0095B7
Pacific Blue #009DC4
Cerulean #02A4D3
Maximum Blue #47ABCC
Blue (I) #2EB4E6
Cerulean Blue #339ACC
Cornflower #93CCEA
Green-Blue #2887C8
Midnight Blue #003366
Navy Blue #0066CC
Denim #1560BD
Blue (III) #0066FF
Cadet Blue #A9B2C3
Periwinkle #C3CDE6
Blue (II) #4570E6
Bluetiful #3C69E7
Wild Blue Yonder #7A89B8
Indigo #4F69C6
Manatee #8D90A1
Cobalt Blue #8C90C8
Celestial Blue #7070CC
Blue Bell #9999CC
Maximum Blue Purple #ACACE6
Violet-Blue #766EC8
Blue-Violet #6456B7
Ultramarine Blue #3F26BF
Middle Blue Purple #8B72BE
Purple Heart #652DC1
Royal Purple #6B3FA0
Violet (II) #8359A3
Medium Violet #8F47B3
Wisteria #C9A0DC
Lavender (I) #BF8FCC
Vivid Violet #803790
Maximum Purple #733380
Purple Mountains' Majesty #D6AEDD
Fuchsia #C154C1
Pink Flamingo #F2583E
Violet (I) #732E6C
Brilliant Rose #E667CE
Orchid #E29CD2
Plum #843179
Medium Rose #D96CBE
Thistle #D8BFD8
Mulberry #C8509B
Red-Violet #BB3385
Middle Purple #D982B5
Maximum Red Purple #A63A79
Jazzberry Jam #A50B5E
Eggplant #614051
Magenta #F653A6
Cerise #DA3287
Wild Strawberry #FF3399
Lavender (II) #FBAED2
Cotton Candy #FFB7D5
Carnation Pink #FFA6C9
Violet-Red #F7468A
Razzmatazz #E30B5C
Piggy Pink #FDD7E4
Carmine #E62E6B
Blush #DB5079
Tickle Me Pink #FC80A5
Mauvelous #F091A9
Salmon #FF91A4
Middle Red Purple #A55353
Mahogany #CA3435
Melon #FEBAAD
Pink Sherbert #F7A38E
Burnt Sienna #E97451
Brown #AF593E
Sepia #9E5B40
Fuzzy Wuzzy #87421F
Beaver #926F5B
Tumbleweed #DEA681
Raw Sienna #D27D46
Van Dyke Brown #664228
Tan #FA9D5A
Desert Sand #EDC9AF
Peach #FFCBA4
Burnt Umber #805533
Apricot #FDD5B1
Almond #EED9C4
Raw Umber #665233
Shadow #837050