Skip to content

Commit

Permalink
ColorChooser._ColorField : Add hue wheel
Browse files Browse the repository at this point in the history
  • Loading branch information
ericmehl committed Aug 9, 2024
1 parent 25e993c commit 6862a1f
Showing 1 changed file with 114 additions and 53 deletions.
167 changes: 114 additions & 53 deletions python/GafferUI/ColorChooser.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,11 +242,32 @@ def __xyAxes( self ) :

return xAxis, yAxis

def __useWheel( self ) :

return self.__staticComponent in "sv"

def __cartesianToPolar( self, p, center, maxRadius ) :

p = p - center
radius = p.length()
theta = math.atan2( p.y, p.x )
twoPi = math.pi * 2.0
theta = theta if theta > 0 else twoPi + theta

return imath.V2f( theta / twoPi, radius / maxRadius )


def __colorToPosition( self, color ) :

xIndex, yIndex = self.__xyIndices()
color = imath.V2f( color[xIndex], color[yIndex] )

if self.__useWheel() :
theta = 2 * math.pi * color.x
return imath.V2f( self.bound().size() ) * 0.5 * (
imath.V2f( color.y * math.cos( theta ), -color.y * math.sin( theta ) ) + imath.V2f( 1.0 )
)

xComponent, yComponent = self.__xyAxes()
minC = imath.V2f( _ranges[xComponent].min, _ranges[yComponent].min )
maxC = imath.V2f( _ranges[xComponent].max, _ranges[yComponent].max )
Expand All @@ -267,8 +288,14 @@ def __positionToColor( self, position ) :

xComponent, yComponent = self.__xyAxes()

c[xIndex] = ( position.x / float( size.x ) ) * ( _ranges[xComponent].max - _ranges[xComponent].min ) + _ranges[xComponent].min
c[yIndex] = ( 1.0 - ( position.y / float( size.y ) ) ) * ( _ranges[yComponent].max - _ranges[yComponent].min ) + _ranges[yComponent].min
if self.__useWheel() :
halfSize = imath.V2f( size ) * 0.5
p = self.__cartesianToPolar( imath.V2f( position.x, size.y - position.y ), halfSize, halfSize.x )
c[xIndex] = p.x
c[yIndex] = p.y
else :
c[xIndex] = ( position.x / float( size.x ) ) * ( _ranges[xComponent].max - _ranges[xComponent].min ) + _ranges[xComponent].min
c[yIndex] = ( 1.0 - ( position.y / float( size.y ) ) ) * ( _ranges[yComponent].max - _ranges[yComponent].min ) + _ranges[yComponent].min

return c

Expand All @@ -285,6 +312,16 @@ def __buttonPress( self, widget, event ) :
def __clampPosition( self, position ) :

size = self.bound().size()
if self.__useWheel() :
halfSize = imath.V2f( size ) * 0.5
centeredPosition = imath.V2f( position.x, position.y ) - halfSize
radiusOriginal = centeredPosition.length()

if radiusOriginal == 0 :
return halfSize

return halfSize + ( centeredPosition / imath.V2f( radiusOriginal ) ) * min( radiusOriginal, halfSize.x )

return imath.V2f( min( size.x, max( 0.0, position.x ) ), min( size.y, max( 0.0, position.y ) ) )

def __dragBegin( self, widget, event ) :
Expand Down Expand Up @@ -353,24 +390,37 @@ def __drawBackground( self, painter ) :

xComponent, yComponent = self.__xyAxes()

for x in range( 0, numStops ) :
tx = float( x ) / ( numStops - 1 )
c[xIndex] = _ranges[xComponent].min + ( _ranges[xComponent].max - _ranges[xComponent].min ) * tx
if self.__useWheel() :
maxRadius = float( numStops - 1 ) * 0.5
for x in range( 0, numStops ) :
for y in range( 0, numStops ) :
p = self.__cartesianToPolar( imath.V2f( x, y ), imath.V2f( maxRadius ), maxRadius )

for y in range( 0, numStops ) :
ty = float( y ) / ( numStops - 1 )
c[xIndex] = p.x
c[yIndex] = p.y

c[yIndex] = _ranges[yComponent].min + ( _ranges[yComponent].max - _ranges[yComponent].min ) * ty

if colorSpace == ColorSpace.RGB :
cRGB = c
elif colorSpace == ColorSpace.HSV :
cRGB = c.hsv2rgb()
else :
cRGB = _tmiToRGB( c )
cRGB = displayTransform( cRGB )
self.__colorFieldToDraw.setPixel( x, numStops - 1 - y, self._qtColor( cRGB ).rgb() )
else :
for x in range( 0, numStops ) :
tx = float( x ) / ( numStops - 1 )
c[xIndex] = _ranges[xComponent].min + ( _ranges[xComponent].max - _ranges[xComponent].min ) * tx

for y in range( 0, numStops ) :
ty = float( y ) / ( numStops - 1 )

cRGB = displayTransform( cRGB )
self.__colorFieldToDraw.setPixel( x, numStops - 1 - y, self._qtColor( cRGB ).rgb() )
c[yIndex] = _ranges[yComponent].min + ( _ranges[yComponent].max - _ranges[yComponent].min ) * ty

if colorSpace == ColorSpace.RGB :
cRGB = c
elif colorSpace == ColorSpace.HSV :
cRGB = c.hsv2rgb()
else :
cRGB = _tmiToRGB( c )

cRGB = displayTransform( cRGB )
self.__colorFieldToDraw.setPixel( x, numStops - 1 - y, self._qtColor( cRGB ).rgb() )

painter.drawImage( self._qtWidget().contentsRect(), self.__colorFieldToDraw )

Expand All @@ -389,52 +439,56 @@ def __drawValue( self, painter ) :
size = self.size()

# Use a dot when both axes are a valid value.
if position.x >= 0 and position.y >= 0 and position.x <= size.x and position.y <= size.y :
positionClamped = self.__clampPosition( position )
delta = position - positionClamped

if abs( delta.x ) < 1e-3 and abs( delta.y ) < 1e-3 :
painter.drawEllipse( QtCore.QPoint( position.x, position.y ), 4.5, 4.5 )
return

triangleWidth = 5.0
triangleSpacing = 2.0
positionClamped = imath.V2f(
min( max( 0.0, position.x ), size.x ),
min( max( 0.0, position.y ), size.y )
)

offset = imath.V2f( 0 )
# Use a corner triangle if both axes are invalid values.
if position.x > size.x and position.y < 0 :
rotation = -45.0 # Triangle pointing to the top-right
offset = imath.V2f( -triangleSpacing, triangleSpacing )
elif position.x < 0 and position.y < 0 :
rotation = -135.0 # Top-left
offset = imath.V2f( triangleSpacing, triangleSpacing )
elif position.x < 0 and position.y > size.y :
rotation = -225.0 # Bottom-left
offset = imath.V2f( triangleSpacing, -triangleSpacing )
elif position.x > size.x and position.y > size.y :
rotation = -315.0 # Bottom-right
offset = imath.V2f( -triangleSpacing, -triangleSpacing )

# Finally, use a top / left / bottom / right triangle if one axis is an invalid value.
elif position.y < 0 :
rotation = -90.0 # Top
offset = imath.V2f( 0, triangleSpacing )
# Clamp it in more to account for the triangle size
positionClamped.x = min( max( triangleWidth + triangleSpacing, positionClamped.x ), size.x - triangleWidth - triangleSpacing )
elif position.x < 0 :
rotation = -180.0 # Left
offset = imath.V2f( triangleSpacing, 0 )
positionClamped.y = min( max( triangleWidth + triangleSpacing, positionClamped.y ), size.y - triangleWidth - triangleSpacing )
elif position.y > size.y :
rotation = -270.0 # Bottom
offset = imath.V2f( 0, -triangleSpacing )
positionClamped.x = min( max( triangleWidth + triangleSpacing, positionClamped.x ), size.x - triangleWidth - triangleSpacing )

if self.__useWheel() :
rotation = math.atan2( delta.y, delta.x )
else :
rotation = 0.0 # Right
offset = imath.V2f( -triangleSpacing, 0 )
positionClamped.y = min( max( triangleWidth + triangleSpacing, positionClamped.y ), size.y - triangleWidth - triangleSpacing )
# Use a corner triangle if both axes are invalid values.
if position.x > size.x and position.y < 0 :
rotation = math.radians( -45.0 ) # Triangle pointing to the top-right
offset = imath.V2f( -triangleSpacing, triangleSpacing )
elif position.x < 0 and position.y < 0 :
rotation = math.radians( -135.0 ) # Top-left
offset = imath.V2f( triangleSpacing, triangleSpacing )
elif position.x < 0 and position.y > size.y :
rotation = math.radians( -225.0 ) # Bottom-left
offset = imath.V2f( triangleSpacing, -triangleSpacing )
elif position.x > size.x and position.y > size.y :
rotation = math.radians( -315.0 ) # Bottom-right
offset = imath.V2f( -triangleSpacing, -triangleSpacing )

# Finally, use a top / left / bottom / right triangle if one axis is an invalid value.
elif position.y < 0 :
rotation = math.radians( -90.0 ) # Top
offset = imath.V2f( 0, triangleSpacing )
# Clamp it in more to account for the triangle size
positionClamped.x = min( max( triangleWidth + triangleSpacing, positionClamped.x ), size.x - triangleWidth - triangleSpacing )
elif position.x < 0 :
rotation = math.radians( -180.0 ) # Left
offset = imath.V2f( triangleSpacing, 0 )
positionClamped.y = min( max( triangleWidth + triangleSpacing, positionClamped.y ), size.y - triangleWidth - triangleSpacing )
elif position.y > size.y :
rotation = math.radians( -270.0 ) # Bottom
offset = imath.V2f( 0, -triangleSpacing )
positionClamped.x = min( max( triangleWidth + triangleSpacing, positionClamped.x ), size.x - triangleWidth - triangleSpacing )
else :
rotation = 0.0 # Right
offset = imath.V2f( -triangleSpacing, 0 )
positionClamped.y = min( max( triangleWidth + triangleSpacing, positionClamped.y ), size.y - triangleWidth - triangleSpacing )

rightPoints = [ imath.V2f( 0, 0 ), imath.V2f( -6, triangleWidth ), imath.V2f( -6, -triangleWidth ) ]
xform = imath.M33f().rotate( math.radians( rotation ) ) * imath.M33f().translate(
xform = imath.M33f().rotate( rotation ) * imath.M33f().translate(
positionClamped + offset
)
points = [ p * xform for p in rightPoints ]
Expand All @@ -450,8 +504,15 @@ def __paintEvent( self, event ) :
painter.setRenderHint( QtGui.QPainter.Antialiasing )
painter.setRenderHint( QtGui.QPainter.SmoothPixmapTransform)

if self.__useWheel() :
mask = QtGui.QPainterPath()
mask.addEllipse( 0, 0, self.size().x, self.size().y )
painter.setClipPath( mask )

self.__drawBackground( painter )

painter.setClipping( False )

self.__drawValue( painter )

class ColorChooser( GafferUI.Widget ) :
Expand Down

0 comments on commit 6862a1f

Please sign in to comment.