linear_gradient.cpp
algorithm
cmath
cstddef
ftxui/dom/linear_gradient.hpp
memory
optional
utility
vector
ftxui/dom/elements.hpp
ftxui/dom/node_decorator.hpp
ftxui/screen/box.hpp
ftxui/screen/color.hpp
ftxui/screen/screen.hpp
ftxui/dom/canvas.hpp
ftxui/dom/deprecated.hpp
ftxui/dom/direction.hpp
ftxui/dom/elements.hpp
ftxui/dom/flexbox_config.hpp
ftxui/dom/linear_gradient.hpp
ftxui/dom/node.hpp
ftxui/dom/requirement.hpp
ftxui/dom/selection.hpp
ftxui/screen/box.hpp
ftxui/screen/color.hpp
ftxui/screen/image.hpp
ftxui/screen/pixel.hpp
ftxui/screen/screen.hpp
ftxui/screen/string.hpp
ftxui/screen/terminal.hpp
ftxui/util/autoreset.hpp
ftxui/util/ref.hpp
src/ftxui/dom/linear_gradient.cpp
ftxui/dom/node_decorator.hpp
algorithm
cmath
cstddef
cstdint
ftxui/dom/take_any_args.hpp
functional
memory
optional
sstream
string
unordered_map
utility
variant
vector
ftxui

    //Copyright2023ArthurSonzogni.Allrightsreserved.
    //UseofthissourcecodeisgovernedbytheMITlicensethatcanbefoundin
    //theLICENSEfile.
    #include<algorithm>//formax,min,sort,copy
    #include<cmath>//forfmod,cos,sin
    #include<cstddef>//forsize_t
    #include<ftxui/dom/linear_gradient.hpp>//forLinearGradient::Stop,LinearGradient
    #include<memory>//forallocator_traits<>::value_type,make_shared
    #include<optional>//foroptional,operator!=,operator<
    #include<utility>//formove
    #include<vector>//forvector

    #include"ftxui/dom/elements.hpp"//forElement,Decorator,bgcolor,color
    #include"ftxui/dom/node_decorator.hpp"//forNodeDecorator
    #include"ftxui/screen/box.hpp"//forBox
    #include"ftxui/screen/color.hpp"//forColor,Color::Default,Color::Blue
    #include"ftxui/screen/screen.hpp"//forPixel,Screen

    namespaceftxui{
    namespace{

    structLinearGradientNormalized{
    floatangle=0.F;
    std::vector<Color>colors;
    std::vector<float>positions;//Sorted.
    };

    //ConvertaLinearGradienttoanormalizedversion.
    LinearGradientNormalizedNormalize(LinearGradientgradient){
    //Handlegradientofsize0.
    if(gradient.stops.empty()){
    returnLinearGradientNormalized{
    0.F,
    {Color::Default,Color::Default},
    {0.F,1.F},
    };
    }

    //Fillinthetwoextent,ifnotprovided.
    if(!gradient.stops.front().position){
    gradient.stops.front().position=0.F;
    }
    if(!gradient.stops.back().position){
    gradient.stops.back().position=1.F;
    }

    //Fillintheblank,byinterpolatingpositions.
    size_tlast_checkpoint=0;
    for(size_ti=1;i<gradient.stops.size();++i){
    if(!gradient.stops[i].position){
    continue;
    }

    if(i-last_checkpoint>=2){
    constfloatmin=gradient.stops[i].position.value();//NOLINT
    constfloatmax=
    gradient.stops[last_checkpoint].position.value();//NOLINT
    for(size_tj=last_checkpoint+1;j<i;++j){
    gradient.stops[j].position=min+(max-min)*
    float(j-last_checkpoint)/
    float(i-last_checkpoint);
    }
    }

    last_checkpoint=i;
    }

    //Sortthestopsbyposition.
    std::sort(
    gradient.stops.begin(),gradient.stops.end(),
    [](constauto&a,constauto&b){returna.position<b.position;});

    //Ifwedon'tbeingwithzero,addastopatzero.
    if(gradient.stops.front().position!=0){
    gradient.stops.insert(gradient.stops.begin(),
    {gradient.stops.front().color,0.F});
    }
    //Ifwedon'tendwithone,addastopatone.
    if(gradient.stops.back().position!=1){
    gradient.stops.push_back({gradient.stops.back().color,1.F});
    }

    //Normalizetheangle.
    LinearGradientNormalizednormalized;
    constfloatmodulo=360.F;
    normalized.angle=
    std::fmod(std::fmod(gradient.angle,modulo)+modulo,modulo);
    for(auto&stop:gradient.stops){
    normalized.colors.push_back(stop.color);
    //NOLINTNEXTLINE
    normalized.positions.push_back(stop.position.value());
    }
    returnnormalized;
    }

    ColorInterpolate(constLinearGradientNormalized&gradient,floatt){
    //Findtherightcolorinthegradient'sstops.
    size_ti=1;
    while(true){
    //Notethat`t`mightbeslightlygreaterthan1.0duetofloatingpoint
    //precision.Thisiswhyweneedtohandlethecasewhere`t`isgreater
    //thanthelaststop'sposition.
    //Seehttps://github.com/ArthurSonzogni/FTXUI/issues/998
    if(i>=gradient.positions.size()){
    constfloathalf=0.5F;
    returnColor::Interpolate(half,gradient.colors.back(),
    gradient.colors.back());
    }
    if(t<=gradient.positions[i]){
    break;
    }
    ++i;
    }

    constfloatt0=gradient.positions[i-1];
    constfloatt1=gradient.positions[i-0];
    constfloattt=(t-t0)/(t1-t0);

    constColor&c0=gradient.colors[i-1];
    constColor&c1=gradient.colors[i-0];
    constColor&cc=Color::Interpolate(tt,c0,c1);

    returncc;
    }

    classLinearGradientColor:publicNodeDecorator{
    public:
    explicitLinearGradientColor(Elementchild,
    constLinearGradient&gradient,
    boolbackground_color)
    :NodeDecorator(std::move(child)),
    gradient_(Normalize(gradient)),
    background_color_{background_color}{}

    private:
    voidRender(Screen&screen)override{
    constfloatdegtorad=0.01745329251F;
    constfloatdx=std::cos(gradient_.angle*degtorad);
    constfloatdy=std::sin(gradient_.angle*degtorad);

    //Projecteverycornertogettheextentofthegradient.
    constfloatp1=float(box_.x_min)*dx+float(box_.y_min)*dy;
    constfloatp2=float(box_.x_min)*dx+float(box_.y_max)*dy;
    constfloatp3=float(box_.x_max)*dx+float(box_.y_min)*dy;
    constfloatp4=float(box_.x_max)*dx+float(box_.y_max)*dy;
    constfloatmin=std::min({p1,p2,p3,p4});
    constfloatmax=std::max({p1,p2,p3,p4});

    //Renormalizetheprojectionto[0,1]usingtheextentandprojective
    //geometry.
    constfloatdX=dx/(max-min);
    constfloatdY=dy/(max-min);
    constfloatdZ=-min/(max-min);

    //Projecteverypixeltogetthecolor.
    if(background_color_){
    for(inty=box_.y_min;y<=box_.y_max;++y){
    for(intx=box_.x_min;x<=box_.x_max;++x){
    constfloatt=float(x)*dX+float(y)*dY+dZ;
    screen.PixelAt(x,y).background_color=Interpolate(gradient_,t);
    }
    }
    }else{
    for(inty=box_.y_min;y<=box_.y_max;++y){
    for(intx=box_.x_min;x<=box_.x_max;++x){
    constfloatt=float(x)*dX+float(y)*dY+dZ;
    screen.PixelAt(x,y).foreground_color=Interpolate(gradient_,t);
    }
    }
    }

    NodeDecorator::Render(screen);
    }

    LinearGradientNormalizedgradient_;
    boolbackground_color_;
    };

    }//namespace

    ///@briefBuildthe"empty"gradient.Thisisoftenfollowedbycallsto
    ///LinearGradient::Angle()andLinearGradient::Stop().
    ///Example:
    ///```cpp
    ///autogradient=
    ///LinearGradient()
    ///.Angle(45)
    ///.Stop(Color::Red,0.0)
    ///.Stop(Color::Green,0.5)
    ///.Stop(Color::Blue,1.0);;
    ///```
    ///@ingroupdom
    LinearGradient::LinearGradient()=default;

    ///@briefBuildagradientwithtwocolors.
    ///@parambeginThecoloratthebeginningofthegradient.
    ///@paramendThecolorattheendofthegradient.
    ///@ingroupdom
    LinearGradient::LinearGradient(Colorbegin,Colorend)
    :LinearGradient(0,begin,end){}

    ///@briefBuildagradientwithtwocolorsandanangle.
    ///@paramaTheangleofthegradient.
    ///@parambeginThecoloratthebeginningofthegradient.
    ///@paramendThecolorattheendofthegradient.
    ///@ingroupdom
    LinearGradient::LinearGradient(floata,Colorbegin,Colorend):angle(a){
    stops.push_back({begin,{}});
    stops.push_back({end,{}});
    }

    ///@briefSettheangleofthegradient.
    ///@paramaTheangleofthegradient.
    ///@returnThegradient.
    ///@ingroupdom
    LinearGradient&LinearGradient::Angle(floata){
    angle=a;
    return*this;
    }

    ///@briefAddacolorstoptothegradient.
    ///@paramcThecolorofthestop.
    ///@parampThepositionofthestop.
    ///@returnThegradient.
    LinearGradient&LinearGradient::Stop(Colorc,floatp){
    stops.push_back({c,p});
    return*this;
    }

    ///@briefAddacolorstoptothegradient.
    ///@paramcThecolorofthestop.
    ///@returnThegradient.
    ///@ingroupdom
    ///@noteThepositionofthestopisinterpolatedfromnearbystops.
    LinearGradient&LinearGradient::Stop(Colorc){
    stops.push_back({c,{}});
    return*this;
    }

    ///@briefSettheforegroundcolorofanelementwithlinear-gradienteffect.
    ///@paramgradientThegradienteffecttobeappliedontheoutputelement.
    ///@paramchildTheinputelement.
    ///@returnTheoutputelementcolored.
    ///@ingroupdom
    ///
    ///###Example
    ///
    ///```cpp
    ///color(LinearGradient{0,{Color::Red,Color::Blue}},text("Hello"))
    ///```
    Elementcolor(constLinearGradient&gradient,Elementchild){
    returnstd::make_shared<LinearGradientColor>(std::move(child),gradient,
    /*background_color*/false);
    }

    ///@briefSetthebackgroundcolorofanelementwithlinear-gradienteffect.
    ///@paramgradientThegradienteffecttobeappliedontheoutputelement.
    ///@paramchildTheinputelement.
    ///@returnTheoutputelementcolored.
    ///@ingroupdom
    ///
    ///###Example
    ///
    ///```cpp
    ///bgcolor(LinearGradient{0,{Color::Red,Color::Blue}},text("Hello"))
    ///```
    Elementbgcolor(constLinearGradient&gradient,Elementchild){
    returnstd::make_shared<LinearGradientColor>(std::move(child),gradient,
    /*background_color*/true);
    }

    ///@briefDecorateusingalinear-gradienteffectontheforegroundcolor.
    ///@paramgradientThegradienteffecttobeappliedontheoutputelement.
    ///@returnTheDecoratorapplyingthecolor.
    ///@ingroupdom
    ///
    ///###Example
    ///
    ///```cpp
    ///text("Hello")|color(LinearGradient{0,{Color::Red,Color::Blue}})
    ///```
    Decoratorcolor(constLinearGradient&gradient){
    return
    [gradient](Elementchild){returncolor(gradient,std::move(child));};
    }

    ///@briefDecorateusingalinear-gradienteffectonthebackgroundcolor.
    ///@paramgradientThegradienteffecttobeappliedontheoutputelement.
    ///@returnTheDecoratorapplyingthecolor.
    ///@ingroupdom
    ///
    ///###Example
    ///
    ///```cpp
    ///text("Hello")|color(LinearGradient{0,{Color::Red,Color::Blue}})
    ///```
    Decoratorbgcolor(constLinearGradient&gradient){
    return
    [gradient](Elementchild){returnbgcolor(gradient,std::move(child));};
    }

    }//namespaceftxui