# Find position for color in Color Picker?

Hi. I had to catch some pneumonia to get time to use Codea again. I’m writing a color picker, and wondered if anyone has made a solution to find a cursor position for a given color in a color picker? The other way around is easier.

I want to call picker:setColor(c) and I want the UI to find a suitable place in the picker where the color c exists.

Here is the code for my Picker.

Edit: Updated with a solution

``````--# Main
-- Picker

function setup()
-- displayMode(FULLSCREEN)
p = Picker(200)
parameter.color("C", color(255,255,255), function (c)
p:setColor(c)
end)
end

function draw()
background(p.color)
translate(WIDTH/2,HEIGHT/2)
p:draw()
end

function touched(touch)
p:touched(touch)
end

--# Picker
Picker = class()

function Picker:init(size)
self.size = size or 200
self.m = matrix()
self:createHexagon()
self:touched(vec2(0,0)) -- set start color white
end

function Picker:hexVector(i)
local a = i/6 * math.pi * 2 + math.pi/6
return vec2(math.cos(a), math.sin(a)) * self.size
end

function Picker:setColor(c)
local rv = self:hexVector(4) -- where red is 0
local gv = self:hexVector(6) -- where green is 0
local bv = self:hexVector(2) -- where blue is 0

local m = math.max(c.r, c.g, c.b)
local p = (rv * (m - c.r) + gv * (m - c.g) + bv * (m - c.b))/255
self:touchedHexagon(p)
end

function Picker:createHexagon()
local vs, cs, colors = {}, {}, {
color(255, 255, 255, 255),
color(255, 0, 0, 255),
color(255, 255, 0, 255),
color(0, 255, 0, 255),
color(0, 255, 255, 255),
color(0, 0, 255, 255),
color(255, 0, 255, 255)
}

for i=1,6 do
table.insert(vs, vec2(0,0))
table.insert(cs, colors[1])
table.insert(vs, self:hexVector(i))
table.insert(cs, colors[i+1])
table.insert(vs, self:hexVector(i+1))
table.insert(cs, colors[i+2] or colors[2])
end
self.hexagon = mesh()
self.hexagon.vertices = vs
self.hexagon.colors = cs
self.colors = cs
end
function Picker:updateCenterColor(c)
local cs = self.colors
for i=1,6*3,3 do
cs[i] = c
end
self.hexagon.colors = cs
self:touchedHexagon(self.pos)
end

local x, y = self.size*(math.sqrt(3)/2)+10, self.size
local w, h = 44, y*2
self.sliderCenterX = x + w/2
local vs = {
vec2(x,y),vec2(x+w,y), vec2(x+w,y-h),
vec2(x,y),vec2(x+w,y-h),vec2(x,y-h)
}
local bc, wc = color(0,0,0,255), color(255,255,255,255)
self.slider = mesh()
self.slider.vertices = vs
self.slider.colors = {wc,wc,bc,wc,bc,bc}
self.bpos = vec2(x+w/2,y)
self.bcolor = wc
end
local p = vec2(self.sliderCenterX, -self.size + self.size*2*v/256)
end

function Picker:draw()
self.m = modelMatrix()
self.hexagon:draw()
self.slider:draw()
end

function Picker:touched(touch)
local p = vec2(touch.x-self.m[13], touch.y-self.m[14])

if not self:touchedHexagon(p) then
end
end

function Picker:touchedHexagon(p)
local c = self:touchedMesh(p, self.hexagon)
if c then
self.color, self.pos = c, p
return true
end
end

local c = self:touchedMesh(p, self.slider)
if c then
p.x = self.sliderCenterX
self.bcolor, self.bpos = c, p
self:updateCenterColor(c)
end
end

function Picker:touchedMesh(p, h)
for i=1,#h.vertices,3 do
local a, b, c = h.vertices[i], h.vertices[i+1], h.vertices[i+2]
local inside, u, v = insideTriangle(a,b,c,vec3(p.x,p.y,0))
if inside then
local d, e, f = h.colors[i]*255, h.colors[i+1]*255, h.colors[i+2]*255
return (1-u-v)*d + u*f + v*e -- color in triangle
end
end
end

uniform mat4 modelViewProjection;
attribute vec4 position;
varying highp vec2 vTexCoord;
attribute vec4 color;
varying lowp vec4 vColor;

void main() {
vTexCoord = position.xy;
vColor = color;
gl_Position = modelViewProjection * position;
}
]],[[
precision highp float;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
uniform vec2 p;
uniform vec4 c;

void main() {
lowp vec4 col = vColor;
float l = length(vTexCoord - p);
if(l < 20.) {
col = c;
} else if(l < 22.) {
// complementary brightness for circle border
float y = 1. - (0.2126*col.x + 0.7152*col.y + 0.0722*col.z);
col = vec4(y, y, y, 1.);
}
gl_FragColor = col;
}
]])
end

--# Utils
-- texture coordinates for p in the triangle a,b,c
function uv(a, b, c, p)
local v0 = c - a
local v1 = b - a
local v2 = p - a
local dot00 = v0:dot(v0)
local dot01 = v0:dot(v1)
local dot02 = v0:dot(v2)
local dot11 = v1:dot(v1)
local dot12 = v1:dot(v2)

-- Compute barycentric coordinates
local invDenom = 1 / (dot00 * dot11 - dot01 * dot01)
local u = (dot11 * dot02 - dot01 * dot12) * invDenom
local v = (dot00 * dot12 - dot01 * dot02) * invDenom

return u, v
end

function insideTriangle(a, b, c, p)
local u, v = uv(a,b,c,p)
return (u >= 0) and (v >= 0) and (u + v < 1), u, v
end

``````

I haven’t looked at your code so haven’t seen whether you’re using a hexagon (which is correct) or circle (which isn’t) as the basis for your colour selector.

Here’s my code for setting the colour of a hexagonal picker:

``````function ColourWheel:setFromColour(rc)
local c = color(rc.r,rc.g,rc.b,rc.a) -- clone the colour
self.alpha = c.a -- set our alpha
local x,y = math.min(c.r,c.g,c.b)/255,math.max(c.r,c.g,c.b)/255 -- get the range of the colour values (renormalised to [0,1])
self.ratio = vec2(x,y)
local i,j,ar
if x == y then -- our colour was a grey, so we pick red as an arbitrary rim colour
self.rimcolour = Colour.svg.Red
self.angle = math.pi/3
else
-- we got an actual colour, so we saturate the colours
c.r = (c.r - x*255)/(y - x)
c.g = (c.g - x*255)/(y - x)
c.b = (c.b - x*255)/(y - x)
c.a = 255
-- this is our rim colour
self.rimcolour = c
-- next step is to locate that colour on the rim
-- this will determine how far along a particular side we are
ar = (c.r + c.g + c.b)/255 - 1
-- we find the two primary/secondary colours on either side of it
if c.r >= c.g and c.r >= c.b then
-- red is the dominant colour, so either we're red-yellow or red-purple
i = 1
if c.g >= c.b then
-- red-yellow
j = 2
else
-- red-purple
j = 6
end
elseif c.g >= c.b then
-- green is the dominant colour (already discounted red)
i = 3
if c.b >= c.r then
-- green-cyan
j = 4
else
-- green-yellow
j = 2
end
else
-- blue is the dominant colour
i = 5
if c.r >= c.g then
-- blue-purple
j = 6
else
-- blue-cyan
j = 4
end
end
-- we linearly interpolate the angle between the starting and ending colours
-- to be honest, we should probably linearly interpolate the positions and then take the atan2 of that
self.angle = (i*(1-ar) + ar*j)*math.pi/3
end
-- now bake the colours into the mesh
self.meshsqr:color(2,self.rimcolour)
self.meshsqr:color(5,Colour.complement(self.rimcolour,false))
self:setColour()
end
``````

Thanks for getting me thinking in the right direction.

My solution now looks like this where hexVector creates a vector that point to the colors in the hexagon where red/green or blue part is zero, so that they can be used to calculate the vector for a color. And setShade sets the color in the center of the hexagon. Don’t know if I’ve used the hexagon in the “correct” way, but I like it.

``````function Picker:setColor(c)
local rv = self:hexVector(4) -- where red is 0
local gv = self:hexVector(6) -- where green is 0
local bv = self:hexVector(2) -- where blue is 0

local m = math.max(c.r, c.g, c.b)
local p = (rv * (m - c.r) + gv * (m - c.g) + bv * (m - c.b))/255
self:touchedHexagon(p)
end
``````