Затухание между 2 цветами в Curses (Python)

Я пытаюсь затушевывать цвет фона подокна Curses между 2 произвольными значениями RGB, переданными в функцию setupColor() в моем коде.

В некоторых случаях код ведет себя как ожидалось и может удовлетворительно исчезать между цветами, но большую часть времени он работает странно.

setupColor((0,0,0),(0,255,55))

Это исчезнет подокно между черным и аквамарином, и работает хорошо.

Однако, если я попытаюсь исчезнуть между, скажем, желтым и фиолетовым, вот так:

setupColor((255,200,0),(200,0,200))

Это будет циклически переключаться между двумя цветами в течение первых нескольких циклов, но, похоже, теряет синхронизацию и в итоге выдает заметно отличающиеся цвета от тех, которые были переданы функции.

Исходя из исходного кода на эскизе Arduino LED Fade, я искал, пытался ли кто-нибудь сделать что-то подобное с физическими светодиодами RGB, что привело к появлению этой темы: C++ Fade между цветами? (Ардуино).

Похоже, что решение, опубликованное в этой ветке, было бы идеальным для моих нужд, но я недостаточно хорошо знаю C++ или JavaScript, чтобы иметь возможность отслеживать, понимать и переносить код на Python.

Можно ли адаптировать мой код для правильного выцветания цветов, или стоит отказаться от него и начать все заново?

import curses,time
from curses import wrapper

def setupColor((red,green,blue),(red1,green1,blue1)):

    global color1,color2                                   #initialise globals for use later on in the program.
    global incrFloatRed,incrFloatGreen,incrFloatBlue
    global minRed,minGreen,minBlue,maxRed,maxGreen,maxBlue

    stepNumber = 60.00                 # number of steps within each color transition.

    color1 = (red,green,blue)         # clone the input tuples for use later on...
    color2 = (red1,green1,blue1)


    differenceRed = red - red1          # subtract each channel of color1 from color2,
    differenceGreen = green - green1    # this will return either a positive or negative float.
    differenceBlue = blue - blue1


    incrFloatRed = differenceRed / stepNumber        # divide the difference between the 2 colors by the 
    incrFloatGreen = differenceGreen / stepNumber    # step rate to obtain the color increments.
    incrFloatBlue = differenceBlue / stepNumber

    if red > red1:                            # if the red channel value of the 1st color is greater than 
            incrFloatRed = -incrFloatRed      # that of the 2nd, invert the increment (to allow
            maxRed = red                      # color subtraction), then set the top end of the range as 
            minRed = red1                     # red 1st channel and the bottom as red 2nd channel.
    else:                                     # Else, perform the inverse operation.
            incrFloatRed = abs(incrFloatRed)
            maxRed = red1
            minRed = red

    if green > green1:
            incrFloatGreen = -incrFloatGreen
            maxGreen = green
            minGreen = green1
    elif green < green1:
            incrFloatGreen = abs(incrFloatGreen)        
            maxGreen = green1
            minGreen = green

    if blue > blue1:
            incrFloatBlue = -incrFloatBlue
            maxBlue = blue
            minBlue = blue1
    else:
            incrFloatBlue = abs(incrFloatBlue)
            maxBlue = blue1
            minBlue = blue



def main(stdscr):
    global incrFloatRed,incrFloatGreen,incrFloatBlue

    setupColor((0,0,0),(0,255,255))

    red = color1[0]                #red,green and blue are the variables used to control the fade.
    green = color1[1]              #To begin with, they is set to the colors contained in the first 
    blue = color1[2]               #tuple that is passed to setupColor()



    label = stdscr.subwin(10,50,1,4)  # create a subwindow, draw a box around it, then add the string
    label.box()                       # "Hello, World!" to the subwindow at position row 1, column 1.
    label.addstr(1,1,"Hello, World!")
    curses.curs_set(0)                # Disable cursor visibility


    while True:




            red = red + incrFloatRed           # color = colorValue + colorIncrement
            green = green + incrFloatGreen
            blue = blue + incrFloatBlue

            if red <= minRed or red >= maxRed:   # if color is less than the start of the color range or
                    incrFloatRed = -incrFloatRed # greater than end of the color range, invert the color increment

            if green <= minGreen or green >= maxGreen:
                    incrFloatGreen = -incrFloatGreen

            if blue <= minBlue or blue >= maxBlue:
                    incrFloatBlue = -incrFloatBlue
                                                      # curses.init_color takes a tuple of rgb values as it's argument,
            cursesRed = int(int(red) / 0.255)         # but uses funky 1000-point intensity values, instead of the usual
            cursesGreen = int(int(green) / 0.255)     # 255. e.g. rgb(1000,0,0) for full intensity red, instead of
            cursesBlue = int(int(blue) / 0.255)       #           rgb(255,0,0).
                                                      # To convert between them, divide the integer of the color value float 
                                                      # by 0.255, then obtain the integer of the resulting float. 



            if cursesRed >=1000:                      # Sometimes a color value is set to greater
                    cursesRed = 1000                  # than 1k or less than 0. When a negative value or a value greater 
            if cursesGreen >=1000:                    # than 1k is passed to curses.init_color(), it will return ERR and
                    cursesGreen = 999                 # Curses will crash. 
            if cursesBlue >=1000:                     
                    cursesBlue = 999

            if cursesRed <=0:
                    cursesRed = 0
            if cursesGreen <=0:
                    cursesGreen = 0
            if cursesBlue <=0:
                    cursesBlue = 0


            curses.init_color(1,cursesRed,cursesGreen,cursesBlue) # reassign Curses color (1) to the RGB1K color of the current step... 

            curses.init_pair(1,255,1)                             # then create a color pair with the dynamic value (1) as 
                                                                  # the BG color, and white (255) as the FG. 



            label.bkgd(curses.color_pair(1)) # set the background of the label subwindow to the current color pair.. 
            label.refresh()                  # then refresh, so that we can see the change.

            time.sleep(0.02)                 # Take a little snooze, then do it all again. 


wrapper(main)

1 ответ

Я понял это сам в конце, это было относительно простым (но хакерским и не элегантным) решением. Я напечатал вывод каждого из цветовых каналов (переменные красный, зеленый и синий в приведенном выше коде) в этой точке программы:

if red <= minRed or red >= maxRed:   
    outFile.write("Red : " + str(int(red)) + "|Green : " + str(int(green)) + "|Blue : " + str(int(blue)) + "\n")
    incrFloatRed = -incrFloatRed 

Это выходные данные для первых трех циклов программы:

Red : 199|Green : 3  |Blue : 196
Red : 255|Green : 193|Blue : 0

Red : 199|Green : 9  |Blue : 196
Red : 255|Green : 186|Blue : 0

Red : 199|Green : 16 |Blue : 196
Red : 255|Green : 179|Blue : 0

Как видите, зеленый канал постепенно смещается по сравнению с другими, а значения красного и синего немного отличаются от значений, передаваемых в функцию setupColor().

Неточность значения цвета можно исправить с помощью операторов if для прямой установки значений:

while True:
        red = red + incrFloatRed           # color = colorValue + colorIncrement
        green = green + incrFloatGreen
        blue = blue + incrFloatBlue

        ##### Add the if statements after this ####

        if red < minRed:
            red = minRed
        if red > maxRed:
            red = maxRed

        if blue < minBlue:
            blue = minBlue
        if blue > maxBlue:
            blue = maxBlue

        if green < minGreen:
            green = minGreen
        if green > maxGreen:
            green = maxGreen

И проблема синхронизации / синхронизации может быть исправлена ​​с помощью одного оператора if для изменения направления исчезновения цвета. Вместо использования выражения для каждого цвета, как это...

        if green <= minGreen or green >= maxGreen:
            incrFloatGreen = -incrFloatGreen 

... установить их все так:

        if green <= minGreen or green >=maxGreen:
            incrFloatRed = -incrFloatRed 
            incrFloatGreen = -incrFloatGreen
            incrFloatBlue = -incrFloatBlue   

Орел наверняка заметил, что если оба параметра maxGreen и minGreen установлены в 0 (например, rgb(230,0100) и rgb(100,0,200)), ничего не произойдет. Если вы измените зеленый цвет на другой, он будет работать нормально.

Я сомневаюсь, что было бы трудно добавить некоторую логику, чтобы определить, какие цветовые каналы будут работать, но, учитывая, что этого можно избежать, просто передав 1 вместо 0, я не стал беспокоиться.

Другим хорошим шагом (с точки зрения эффективности) было бы сбросить все значения затухания в массив и установить цвета на его основе, вместо того чтобы каждый раз вычислять значения.

Другие вопросы по тегам