Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Color manipulation #149

Closed
Solido opened this issue Aug 18, 2020 · 20 comments
Closed

Color manipulation #149

Solido opened this issue Aug 18, 2020 · 20 comments
Labels
enhancement New feature or request question Further information is requested

Comments

@Solido
Copy link

Solido commented Aug 18, 2020

Hello !

When working with color on others langs like JS or Dart you'll find some libraries like

I'm looking for an equivalent or recommandation when working with OpenRNDR.

Regards

@Solido Solido added the enhancement New feature or request label Aug 18, 2020
@hamoid
Copy link
Member

hamoid commented Aug 18, 2020

Hi! I think the OPENRNDR forum would be a better place to ask as this is not really an issue :)

Have you checked out https://guide.openrndr.org/#/04_Drawing_basics/C03_Color ? OPENRNDR does provide several color models, you can shade and opacify colors, mix them... There's also an orx extension with many color palettes. Specifically what functionality are you missing?

@Solido
Copy link
Author

Solido commented Aug 18, 2020

Hi !

I decided to go with Github since I think Color manipulation question will be more discoverable here.

I've read the source and found all convertion methods but no manipulations, there's very few defined colors, you can't convert to HEX, rgb works only on the 0-1 scale, mix is great but imply creating another color where one call can't be simple enough, no range generator, ...

Java is not the host for this kind of graphics but OpenRNDR is really powerful so I try to move some of my works from other platform to it but I'm having some hard time working fast with colors manipulation and expressivity.

I found https://github.com/ajalt/colormath as a basic tool but it's still not enough so I was not sure if it's the state in the JVM world or if some tools exist out there that are as elegant as Chroma.

I also digged in ORX ... which one is chromotome ? ;)

Thx !

@Solido
Copy link
Author

Solido commented Aug 18, 2020

Also an example of the kind of stuff I'm trying to do to manipulate stuff but the result is surprising

Capture d’écran 2020-08-16 à 22 14 31

@hamoid
Copy link
Member

hamoid commented Aug 18, 2020

I think we should address each of those issues one by one :)

very few defined colors

Ported from openFrameworks

    val gray = rgb(0.5)
    val white = rgb(1.0)
    val red = rgb(1.0, 0.0, 0.0)
    val green = rgb(0.0, 1.0, 0.0)
    val blue = rgb(0.0, 0.0, 1.0)
    val cyan = rgb(0.0, 1.0, 1.0)
    val magenta = rgb(1.0, 0.0, 1.0)
    val yellow = rgb(1.0, 1.0, 0.0)
    val black = rgb(0.0)
    val aliceBlue = rgb(0.941176, 0.972549, 1.0)
    val antiqueWhite = rgb(0.980392, 0.921569, 0.843137)
    val aqua = rgb(0.0, 1.0, 1.0)
    val aquamarine = rgb(0.498039, 1.0, 0.831373)
    val azure = rgb(0.941176, 1.0, 1.0)
    val beige = rgb(0.960784, 0.960784, 0.862745)
    val bisque = rgb(1.0, 0.894118, 0.768627)
    val blanchedAlmond = rgb(1.0, 0.921569, 0.803922)
    val blueViolet = rgb(0.541176, 0.168627, 0.886275)
    val brown = rgb(0.647059, 0.164706, 0.164706)
    val burlyWood = rgb(0.870588, 0.721569, 0.529412)
    val cadetBlue = rgb(0.372549, 0.619608, 0.627451)
    val chartreuse = rgb(0.498039, 1.0, 0.0)
    val chocolate = rgb(0.823529, 0.411765, 0.117647)
    val coral = rgb(1.0, 0.498039, 0.313726)
    val cornflowerBlue = rgb(0.392157, 0.584314, 0.929412)
    val cornsilk = rgb(1.0, 0.972549, 0.862745)
    val crimson = rgb(0.862745, 0.0784314, 0.235294)
    val darkBlue = rgb(0.0, 0.0, 0.545098)
    val darkCyan = rgb(0.0, 0.545098, 0.545098)
    val darkGoldenRod = rgb(0.721569, 0.52549, 0.0431373)
    val darkGray = rgb(0.662745, 0.662745, 0.662745)
    val darkGrey = rgb(0.662745, 0.662745, 0.662745)
    val darkGreen = rgb(0.0, 0.392157, 0.0)
    val darkKhaki = rgb(0.741176, 0.717647, 0.419608)
    val darkMagenta = rgb(0.545098, 0.0, 0.545098)
    val darkOliveGreen = rgb(0.333333, 0.419608, 0.184314)
    val darkOrange = rgb(1.0, 0.54902, 0.0)
    val darkOrchid = rgb(0.6, 0.196078, 0.8)
    val darkRed = rgb(0.545098, 0.0, 0.0)
    val darkSalmon = rgb(0.913725, 0.588235, 0.478431)
    val darkSeaGreen = rgb(0.560784, 0.737255, 0.560784)
    val darkSlateBlue = rgb(0.282353, 0.239216, 0.545098)
    val darkSlateGray = rgb(0.184314, 0.309804, 0.309804)
    val darkSlateGrey = rgb(0.184314, 0.309804, 0.309804)
    val darkTurquoise = rgb(0.0, 0.807843, 0.819608)
    val darkViolet = rgb(0.580392, 0.0, 0.827451)
    val deepPink = rgb(1.0, 0.0784314, 0.576471)
    val deepSkyBlue = rgb(0.0, 0.74902, 1.0)
    val dimGray = rgb(0.411765, 0.411765, 0.411765)
    val dimGrey = rgb(0.411765, 0.411765, 0.411765)
    val dodgerBlue = rgb(0.117647, 0.564706, 1.0)
    val fireBrick = rgb(0.698039, 0.133333, 0.133333)
    val floralWhite = rgb(1.0, 0.980392, 0.941176)
    val forestGreen = rgb(0.133333, 0.545098, 0.133333)
    val fuchsia = rgb(1.0, 0.0, 1.0)
    val gainsboro = rgb(0.862745, 0.862745, 0.862745)
    val ghostWhite = rgb(0.972549, 0.972549, 1.0)
    val gold = rgb(1.0, 0.843137, 0.0)
    val goldenRod = rgb(0.854902, 0.647059, 0.12549)
    val grey = rgb(0.501961, 0.501961, 0.501961)
    val greenYellow = rgb(0.678431, 1.0, 0.184314)
    val honeyDew = rgb(0.941176, 1.0, 0.941176)
    val hotPink = rgb(1.0, 0.411765, 0.705882)
    val indianRed = rgb(0.803922, 0.360784, 0.360784)
    val indigo = rgb(0.294118, 0.0, 0.509804)
    val ivory = rgb(1.0, 1.0, 0.941176)
    val khaki = rgb(0.941176, 0.901961, 0.54902)
    val lavender = rgb(0.901961, 0.901961, 0.980392)
    val lavenderBlush = rgb(1.0, 0.941176, 0.960784)
    val lawnGreen = rgb(0.486275, 0.988235, 0.0)
    val lemonChiffon = rgb(1.0, 0.980392, 0.803922)
    val lightBlue = rgb(0.678431, 0.847059, 0.901961)
    val lightCoral = rgb(0.941176, 0.501961, 0.501961)
    val lightCyan = rgb(0.878431, 1.0, 1.0)
    val lightGoldenRodYellow = rgb(0.980392, 0.980392, 0.823529)
    val lightGray = rgb(0.827451, 0.827451, 0.827451)
    val lightGrey = rgb(0.827451, 0.827451, 0.827451)
    val lightGreen = rgb(0.564706, 0.933333, 0.564706)
    val lightPink = rgb(1.0, 0.713726, 0.756863)
    val lightSalmon = rgb(1.0, 0.627451, 0.478431)
    val lightSeaGreen = rgb(0.12549, 0.698039, 0.666667)
    val lightSkyBlue = rgb(0.529412, 0.807843, 0.980392)
    val lightSlateGray = rgb(0.466667, 0.533333, 0.6)
    val lightSlateGrey = rgb(0.466667, 0.533333, 0.6)
    val lightSteelBlue = rgb(0.690196, 0.768627, 0.870588)
    val lightYellow = rgb(1.0, 1.0, 0.878431)
    val lime = rgb(0.0, 1.0, 0.0)
    val limeGreen = rgb(0.196078, 0.803922, 0.196078)
    val linen = rgb(0.980392, 0.941176, 0.901961)
    val maroon = rgb(0.501961, 0.0, 0.0)
    val mediumAquaMarine = rgb(0.4, 0.803922, 0.666667)
    val mediumBlue = rgb(0.0, 0.0, 0.803922)
    val mediumOrchid = rgb(0.729412, 0.333333, 0.827451)
    val mediumPurple = rgb(0.576471, 0.439216, 0.858824)
    val mediumSeaGreen = rgb(0.235294, 0.701961, 0.443137)
    val mediumSlateBlue = rgb(0.482353, 0.407843, 0.933333)
    val mediumSpringGreen = rgb(0.0, 0.980392, 0.603922)
    val mediumTurquoise = rgb(0.282353, 0.819608, 0.8)
    val mediumVioletRed = rgb(0.780392, 0.0823529, 0.521569)
    val midnightBlue = rgb(0.0980392, 0.0980392, 0.439216)
    val mintCream = rgb(0.960784, 1.0, 0.980392)
    val mistyRose = rgb(1.0, 0.894118, 0.882353)
    val moccasin = rgb(1.0, 0.894118, 0.709804)
    val navajoWhite = rgb(1.0, 0.870588, 0.678431)
    val navy = rgb(0.0, 0.0, 0.501961)
    val oldLace = rgb(0.992157, 0.960784, 0.901961)
    val olive = rgb(0.501961, 0.501961, 0.0)
    val oliveDrab = rgb(0.419608, 0.556863, 0.137255)
    val orange = rgb(1.0, 0.647059, 0.0)
    val orangeRed = rgb(1.0, 0.270588, 0.0)
    val orchid = rgb(0.854902, 0.439216, 0.839216)
    val paleGoldenRod = rgb(0.933333, 0.909804, 0.666667)
    val paleGreen = rgb(0.596078, 0.984314, 0.596078)
    val paleTurquoise = rgb(0.686275, 0.933333, 0.933333)
    val paleVioletRed = rgb(0.858824, 0.439216, 0.576471)
    val papayaWhip = rgb(1.0, 0.937255, 0.835294)
    val peachPuff = rgb(1.0, 0.854902, 0.72549)
    val peru = rgb(0.803922, 0.521569, 0.247059)
    val pink = rgb(1.0, 0.752941, 0.796078)
    val plum = rgb(0.866667, 0.627451, 0.866667)
    val powderBlue = rgb(0.690196, 0.878431, 0.901961)
    val purple = rgb(0.501961, 0.0, 0.501961)
    val rosyBrown = rgb(0.737255, 0.560784, 0.560784)
    val royalBlue = rgb(0.254902, 0.411765, 0.882353)
    val saddleBrown = rgb(0.545098, 0.270588, 0.0745098)
    val salmon = rgb(0.980392, 0.501961, 0.447059)
    val sandyBrown = rgb(0.956863, 0.643137, 0.376471)
    val seaGreen = rgb(0.180392, 0.545098, 0.341176)
    val seaShell = rgb(1.0, 0.960784, 0.933333)
    val sienna = rgb(0.627451, 0.321569, 0.176471)
    val silver = rgb(0.752941, 0.752941, 0.752941)
    val skyBlue = rgb(0.529412, 0.807843, 0.921569)
    val slateBlue = rgb(0.415686, 0.352941, 0.803922)
    val slateGray = rgb(0.439216, 0.501961, 0.564706)
    val slateGrey = rgb(0.439216, 0.501961, 0.564706)
    val snow = rgb(1.0, 0.980392, 0.980392)
    val springGreen = rgb(0.0, 1.0, 0.498039)
    val steelBlue = rgb(0.27451, 0.509804, 0.705882)
    val blueSteel = rgb(0.27451, 0.509804, 0.705882)
    val tan = rgb(0.823529, 0.705882, 0.54902)
    val teal = rgb(0.0, 0.501961, 0.501961)
    val thistle = rgb(0.847059, 0.74902, 0.847059)
    val tomato = rgb(1.0, 0.388235, 0.278431)
    val turquoise = rgb(0.25098, 0.878431, 0.815686)
    val violet = rgb(0.933333, 0.509804, 0.933333)
    val wheat = rgb(0.960784, 0.870588, 0.701961)
    val whiteSmoke = rgb(0.960784, 0.960784, 0.960784)
    val yellowGreen = rgb(0.603922, 0.803922, 0.196078)

@hamoid
Copy link
Member

hamoid commented Aug 18, 2020

Also an example of the kind of stuff I'm trying to do to manipulate stuff but the result is surprising

Capture d’écran 2020-08-16 à 22 14 31

I think s and l should be normalized.

@Solido
Copy link
Author

Solido commented Aug 18, 2020

Ah ! Did not found those colors.
My discovery point was here https://guide.openrndr.org/#/04_Drawing_basics/C03_Color?id=predefined-colors
Maybe add a link would help newcomers like me
But Yes I was expecting finding any colors list inside the framework.

I'm not sure what would be the best approach to this manipulation, I was expecting an error or turns but not invalid range in RGB. From an API perspective this should be impossible but I did not grasp enough of OpenRNDR philosophy on this.

@hamoid
Copy link
Member

hamoid commented Aug 18, 2020

Did not found those colors.

I just converted them from another framework :)

@Solido
Copy link
Author

Solido commented Aug 18, 2020

But you get the point I'm looking for a one stop shop :)

@hamoid
Copy link
Member

hamoid commented Aug 18, 2020

I understand. I do believe that most of those features can be implemented quickly, so if you describe how you would like them to work someone can explain how to do it or show an example and if it is considered useful for everyone then it probably would be part of the next release.

I think that if something is not part of the framework it is probably because no one needed that feature so far.

Another thing you could maybe do is use TColor from ToxicLibs directly from OPENRNDR by using the .jar file from your project. You would only need conversion methods between ColorRGBa and TColor.

@hamoid
Copy link
Member

hamoid commented Aug 18, 2020

ColorRGBa to hex

fun ColorRGBa.toHex(): String {
    return "%06x".format(
        ((r * 255).toInt() shl 16) + ((g * 255).toInt() shl 8) + ((b * 255).toInt())
    )
}

@hamoid
Copy link
Member

hamoid commented Aug 18, 2020

Here's the chroma.js repo in case we want to study how they do things:
https://github.com/gka/chroma.js
It is released under BSD license and the colors it includes come from another library with Apache 2.0 license (what? you can license RGB colors? :) )

@edwinRNDR
Copy link
Member

I'm not sure what would be the best approach to this manipulation, I was expecting an error or turns but not invalid range in RGB. From an API perspective this should be impossible but I did not grasp enough of OpenRNDR philosophy on this.

The idea here is that the value ranges for ColorRGBa are unbound. This is because r/g/b values larger than 1.0 (or less than 0.0) do have a meaning when describing for example light sources.

Interesting conversation here, I am very much interested in improving the tools for dealing with color beyond what we have now. I have just started https://github.com/openrndr/orx/tree/master/orx-color and added the list of preset colors that @hamoid provided.

From a cursory glance it looks like most of what is in chroma.js is covered by converting between color spaces. Is there anything in specific you are looking for?

@edwinRNDR edwinRNDR added the question Further information is requested label Aug 18, 2020
@Solido
Copy link
Author

Solido commented Aug 19, 2020

Formats like Raw or HDR convey high precision of the energy level but I was thinking to RGB like CYMK as format that express limitation of physical hardware. ColorRGBa conveys more ! OK.

I'll try to list some ideas and maybe it can be of any interest for Orx-Color. Love the idea of such a package for my kind of work it's central.

OpenRNDR already offer everything we may want to do but where it really shine is it's expressivity and coherence.
There's an opportunity here with colors.

Talking about Chroma or TinyColor, they work with a proxy. You load some data in a generic structure, apply modification then export to the more appropriate format and not the reverse chroma('skyblue').lch()

This would translate to (r, g, b, a) = Colorx('sienna').darken(10).rgba

WIth the current implementation if I change ColorRGBa to ColorHSVa there's no common method darken, also we need to know methods for each model. Changing the model while exploring will slow down the velocity of the iteration as it imply rewriting methods and adapting parameters.

The use case of this ticket is generating range of Colors. I use ColorHSVa as for now and would be very interested to see how fluent it can be with Kotlin. Maybe Ranges and Progressions can be leveraged ? This one is very tricky because it's easy to step with RGB[Int] but what about RGB[unbound double] or ColorHSVa ?

Here are some rough ideas, they are the essence of what I'm doing by Re-implementing it myself each time.
It is error prone and a slow process.

// leveraging extensions : not that much required but to gives ideas
for (color in 'sienna'.rgb .. 0xff3399.rgb step 2) fill = color

Define a color Range

val range = ColorRange<hsv> (
 ('#fafa6e', .2),
 ('red', .8),
 ('theaf', 1.0),
)

Return an HSV iterable with n entries, slices is a specific class that exposes methods based on ColorRange Type here HSV
val slices = range(12).map {...}

Return a HSV entry
val slices = range.index(.6)

Return HSV iteration with 20 entries interpolating from .6 to .8
val slices = range.index(.6, .8, 20)

Then maybe we can have some sugars

val hueOffsets = List(20) {Random.nextInt()}

// I know it's HSV and offset will apply to Hue only
slices.offset(hueOffsets)

val saturationOffset = List(20) {Random.nextDouble() }
slices.offsetSaturation(hueOffsets)

So helpful when working with gradients !

Others tools, that can take ORX-Palette as input, include generations of ColorWatches.

Does is fuel some ideas and comments/critics ?

Thanks for reading this !

@edwinRNDR
Copy link
Member

@Solido thank you for taking the time to provide the extensive feedback. What you propose makes perfect sense to me and appears to be a valuable addition to our color framework. I don't think it would be complicated to modify the framework to fit this in. Will give it a shot shortly to see how what I have in mind for the implementation pans out.

@hamoid
Copy link
Member

hamoid commented Aug 20, 2020

I leave the toxiclibs color class for inspiration:
https://github.com/postspectacular/toxiclibs/tree/master/src.color/toxi/color

@edwinRNDR
Copy link
Member

I worked a bit on unification of the unification of the color interface and built some tools on of the new interface. You can check the demos of https://github.com/openrndr/orx/tree/master/orx-color

@Solido
Copy link
Author

Solido commented Aug 22, 2020

Love it !
Those new colour models are great additions too.
Thx @edwinRNDR @hamoid

Feedbacks

  • Can we think about another verb than blend, would something like sample convey more meaning ?
  • Add some quick comments on methods and expressive parameters for beginners like me so we can understand fully from the IDE

Capture d’écran 2020-08-22 à 21 33 57

@edwinRNDR
Copy link
Member

I am still looking for a good name, it is surprisingly hard.

Candidates:

  • steps; not a verb, too much like like step (which has completely different meaning)
  • sample: has connotations to random sampling, but here there is a strict order
  • blend; bit confusing as there are other things called blend (like blend modes), Illustrator uses blend terminology to describe similar actions.

I'd also like a name that can be used outside of colors, as I think that valA..valB. trick can be used nicely for other types too.

@Solido
Copy link
Author

Solido commented Aug 22, 2020

Some ideas

  • steps : clearly does not fit
  • sample : notion of estimation
  • blend : always used this verb passing another color and blend or ratio so surprising here
  • scale : used by chroma JS
  • interpolate : used by some tools

For variables names when you read the code your understand they are lower & upper limits would this fit in name conventions of the framework ?

@Solido Solido closed this as completed Jan 14, 2021
@Empyreans
Copy link

What you can also do is interface with these JavaScript libs directly by writing a node.js CLI and calling it from Kotlin in your case. As an alternative, you could write a REST api. I did this for a recent project where I really wanted to use chroma.js and tonal.js. I used Clojure andClojureScript with Quil as the Processing (P5) wrapper. You can read about this project and interaction here: https://github.com/Empyreans/midi-viz
Of course there is some overhead and it is likely not optimal for real time applications. I can update 10.000 colors at under 1 second though, it is fast enough for me!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request question Further information is requested
Projects
None yet
Development

No branches or pull requests

4 participants