<commit_before>// -*- mode: c++; coding: utf-8 -*-
#pragma once
#ifndef WTL_ITERTOOLS_HPP_
#define WTL_ITERTOOLS_HPP_

#include <cstdint>
#include <vector>
#include <type_traits>

#include <boost/coroutine2/coroutine.hpp>

#include "numeric.hpp"

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////
namespace wtl { namespace itertools {
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////

template <class value_type>
class Generator {
  public:
    typedef boost::coroutines2::coroutine<value_type> coro_t;
    typedef uintmax_t size_type;
    typedef typename std::remove_const<decltype(value_type().size())>::type value_size_t;

    Generator() = default;
    virtual ~Generator() = default;

    size_type count() const {return cnt_;}
    virtual size_type max_count() const = 0;
    typename coro_t::pull_type operator()(const size_type skip=0) {
        return typename coro_t::pull_type([this,skip](typename coro_t::push_type& yield){source(yield, skip);});
    }
  protected:
    size_type cnt_ = 0;
    virtual void source(typename coro_t::push_type& yield, const size_type skip) = 0;
};

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////

template <class value_type>
class Product final: public Generator<value_type> {
  public:
    using typename Generator<value_type>::coro_t;
    using typename Generator<value_type>::size_type;
    using typename Generator<value_type>::value_size_t;
    Product() = delete;
    explicit Product(const std::vector<value_type>& axes):
        Generator<value_type>(),
        axes_(axes),
        value_(axes_.size()),
        pos_(axes_.size()) {}
    ~Product() = default;

    void reset() {
        pos_ = axes_.size();
        this->cnt_ = 0;
    }
    virtual size_type max_count() const override {
        size_type n = 1;
        for (const auto& c: axes_) {n *= c.size();}
        return n;
    }

  private:
    virtual void source(typename coro_t::push_type& yield, const size_type skip) override {
        if (--pos_ > 0) {
            const value_size_t n = axes_[pos_].size();
            for (value_size_t i=0; i<n; ++i) {
                value_[pos_] = axes_[pos_][i];
                source(yield, skip);
            }
            ++pos_;
        } else {
            const value_size_t n = axes_[pos_].size();
            for (value_size_t i=0; i<n; ++i) {
                value_[pos_] = axes_[pos_][i];
                if (++this->cnt_ > skip) {
                    yield(value_type(value_));
                }
            }
            ++pos_;
        }
    }
    const std::vector<value_type>& axes_;
    value_type value_;
    value_size_t pos_;
};

template <class value_type>
Product<value_type> product(const std::vector<value_type>& axes) {
    return Product<value_type>(axes);
}

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////

template <class value_type>
class UniAxis final: public Generator<value_type> {
  public:
    using typename Generator<value_type>::coro_t;
    using typename Generator<value_type>::size_type;
    using typename Generator<value_type>::value_size_t;
    UniAxis() = delete;
    UniAxis(const std::vector<value_type>& axes, const value_type& center):
      Generator<value_type>(),
      axes_(axes), center_(center) {}
    ~UniAxis() = default;

    virtual size_type max_count() const override {
        size_type n = 0;
        for (const auto& c: axes_) {n += c.size();}
        return n;
    }

  private:
    virtual void source(typename coro_t::push_type& yield, const size_type skip) override {
        for (size_t i=0; i<axes_.size(); ++i) {
            auto value = center_;
            const auto& axis = axes_[i];
            const auto l = axis.size();
            for (value_size_t j=0; j<l; ++j) {
                value[i] = axis[j];
                if (++this->cnt_ > skip) {
                    yield(value_type(value));
                }
            }
        }
    }
    const std::vector<value_type>& axes_;
    const value_type center_;
};

template <class value_type>
UniAxis<value_type> uniaxis(const std::vector<value_type>& axes, const value_type& center) {
    return UniAxis<value_type>(axes, center);
}

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////

template <class value_type>
class Prototype final: public Generator<value_type> {
  public:
    using typename Generator<value_type>::coro_t;
    using typename Generator<value_type>::size_type;
    using typename Generator<value_type>::value_size_t;
    Prototype() = delete;
    Prototype(const std::vector<value_type>& main_axes, const std::vector<value_type>& vicinity):
      Generator<value_type>(),
      main_axes_(main_axes), product_(vicinity) {}
    ~Prototype() = default;

    virtual size_type max_count() const override {
        size_type n = 0;
        for (const auto& v: main_axes_) {n += v.size();}
        return n * product_.max_count();
    }

  private:
    virtual void source(typename coro_t::push_type& yield, const size_type skip) override {
        for (size_t i=0; i<main_axes_.size(); ++i) {
            const auto& axis = main_axes_[i];
            const auto l = axis.size();
            for (const auto& center: product_()) {
                auto value = center;
                for (value_size_t j=0; j<l; ++j) {
                    value[i] = axis[j];
                    if (++this->cnt_ > skip) {
                        yield(value_type(value));
                    }
                }
            }
            product_.reset();
        }
    }
    const std::vector<value_type> main_axes_;
    Product<value_type> product_;
};

template <class value_type>
Prototype<value_type> prototype(const std::vector<value_type>& axes, const std::vector<value_type>& vicinity) {
    return Prototype<value_type>(axes, vicinity);
}

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////

template <class value_type>
class Simplex  final: public Generator<value_type> {
  public:
    using typename Generator<value_type>::coro_t;
    using typename Generator<value_type>::size_type;
    Simplex() = delete;
    explicit Simplex(const std::vector<value_type>& axes, const double sum=1.0):
        product_(axes), sum_(sum) {}

    void reset() {product_.reset();}
    size_type raw_count() const {return product_.count();}
    virtual size_type max_count() const override {return product_.max_count();}

  private:
    virtual void source(typename coro_t::push_type& yield, const size_type skip) override {
        for (const auto& v: product_()) {
            if (wtl::approx(sum_, v.sum())) {
                if (++this->cnt_ > skip) yield(v);
            }
        }
    }
    Product<value_type> product_;
    const double sum_;
};

template <class value_type>
Simplex<value_type> simplex(const std::vector<value_type>& axes, const double sum=1.0) {
    return Simplex<value_type>(axes, sum);
}

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////
}} // namespace wtl::itertools
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////

#endif // WTL_ITERTOOLS_HPP_
<commit_msg>Calculate max_count in constructor; add percent()<commit_after>// -*- mode: c++; coding: utf-8 -*-
#pragma once
#ifndef WTL_ITERTOOLS_HPP_
#define WTL_ITERTOOLS_HPP_

#include <cstdint>
#include <vector>
#include <type_traits>

#include <boost/coroutine2/coroutine.hpp>

#include "numeric.hpp"

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////
namespace wtl { namespace itertools {
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////

template <class value_type>
class Generator {
  public:
    typedef boost::coroutines2::coroutine<value_type> coro_t;
    typedef uintmax_t size_type;
    typedef typename std::remove_const<decltype(value_type().size())>::type value_size_t;

    Generator() = default;
    virtual ~Generator() = default;

    size_type count() const {return cnt_;}
    size_type max_count() const {return max_cnt_;}
    double percent() const {return 100.0 * cnt_ / max_cnt_;}
    typename coro_t::pull_type operator()(const size_type skip=0) {
        return typename coro_t::pull_type([this,skip](typename coro_t::push_type& yield){source(yield, skip);});
    }
  protected:
    size_type cnt_ = 0;
    size_type max_cnt_ = 0;
    virtual void source(typename coro_t::push_type& yield, const size_type skip) = 0;
};

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////

template <class value_type>
class Product final: public Generator<value_type> {
  public:
    using typename Generator<value_type>::coro_t;
    using typename Generator<value_type>::size_type;
    using typename Generator<value_type>::value_size_t;
    Product() = delete;
    explicit Product(const std::vector<value_type>& axes)
        : Generator<value_type>(),
          axes_(axes),
          value_(axes_.size()),
          pos_(axes_.size()) {
        this->max_cnt_ = 1;
        for (const auto& c: axes_) {this->max_cnt_ *= c.size();}
    }
    ~Product() = default;

    void reset() {
        pos_ = axes_.size();
        this->cnt_ = 0;
    }

  private:
    virtual void source(typename coro_t::push_type& yield, const size_type skip) override {
        if (--pos_ > 0) {
            const value_size_t n = axes_[pos_].size();
            for (value_size_t i=0; i<n; ++i) {
                value_[pos_] = axes_[pos_][i];
                source(yield, skip);
            }
            ++pos_;
        } else {
            const value_size_t n = axes_[pos_].size();
            for (value_size_t i=0; i<n; ++i) {
                value_[pos_] = axes_[pos_][i];
                if (++this->cnt_ > skip) {
                    yield(value_type(value_));
                }
            }
            ++pos_;
        }
    }
    const std::vector<value_type>& axes_;
    value_type value_;
    value_size_t pos_;
};

template <class value_type>
Product<value_type> product(const std::vector<value_type>& axes) {
    return Product<value_type>(axes);
}

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////

template <class value_type>
class UniAxis final: public Generator<value_type> {
  public:
    using typename Generator<value_type>::coro_t;
    using typename Generator<value_type>::size_type;
    using typename Generator<value_type>::value_size_t;
    UniAxis() = delete;
    UniAxis(const std::vector<value_type>& axes, const value_type& center)
        : Generator<value_type>(),
          axes_(axes),
          center_(center) {
        this->max_cnt_ = 0;
        for (const auto& c: axes_) {this->max_cnt_ += c.size();}
    }
    ~UniAxis() = default;

  private:
    virtual void source(typename coro_t::push_type& yield, const size_type skip) override {
        for (size_t i=0; i<axes_.size(); ++i) {
            auto value = center_;
            const auto& axis = axes_[i];
            const auto l = axis.size();
            for (value_size_t j=0; j<l; ++j) {
                value[i] = axis[j];
                if (++this->cnt_ > skip) {
                    yield(value_type(value));
                }
            }
        }
    }
    const std::vector<value_type>& axes_;
    const value_type center_;
};

template <class value_type>
UniAxis<value_type> uniaxis(const std::vector<value_type>& axes, const value_type& center) {
    return UniAxis<value_type>(axes, center);
}

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////

template <class value_type>
class Prototype final: public Generator<value_type> {
  public:
    using typename Generator<value_type>::coro_t;
    using typename Generator<value_type>::size_type;
    using typename Generator<value_type>::value_size_t;
    Prototype() = delete;
    Prototype(const std::vector<value_type>& main_axes, const std::vector<value_type>& vicinity)
        : Generator<value_type>(),
          main_axes_(main_axes),
          product_(vicinity) {
        this->max_cnt_ = 0;
        for (const auto& v: main_axes_) {this->max_cnt_ += v.size();}
        this->max_cnt_ *= product_.max_count();
    }
    ~Prototype() = default;

  private:
    virtual void source(typename coro_t::push_type& yield, const size_type skip) override {
        for (size_t i=0; i<main_axes_.size(); ++i) {
            const auto& axis = main_axes_[i];
            const auto l = axis.size();
            for (const auto& center: product_()) {
                auto value = center;
                for (value_size_t j=0; j<l; ++j) {
                    value[i] = axis[j];
                    if (++this->cnt_ > skip) {
                        yield(value_type(value));
                    }
                }
            }
            product_.reset();
        }
    }
    const std::vector<value_type> main_axes_;
    Product<value_type> product_;
};

template <class value_type>
Prototype<value_type> prototype(const std::vector<value_type>& axes, const std::vector<value_type>& vicinity) {
    return Prototype<value_type>(axes, vicinity);
}

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////

template <class value_type>
class Simplex  final: public Generator<value_type> {
  public:
    using typename Generator<value_type>::coro_t;
    using typename Generator<value_type>::size_type;
    Simplex() = delete;
    explicit Simplex(const std::vector<value_type>& axes, const double sum=1.0)
        : product_(axes),
          sum_(sum) {
        this->max_cnt_ = product_.max_count();
    }

    void reset() {product_.reset();}
    size_type raw_count() const {return product_.count();}

  private:
    virtual void source(typename coro_t::push_type& yield, const size_type skip) override {
        for (const auto& v: product_()) {
            if (wtl::approx(sum_, v.sum())) {
                if (++this->cnt_ > skip) yield(v);
            }
        }
    }
    Product<value_type> product_;
    const double sum_;
};

template <class value_type>
Simplex<value_type> simplex(const std::vector<value_type>& axes, const double sum=1.0) {
    return Simplex<value_type>(axes, sum);
}

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////
}} // namespace wtl::itertools
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////

#endif // WTL_ITERTOOLS_HPP_
<|endoftext|>


<commit_before>#include "NeoPatterns.h"

NeoPatterns::NeoPatterns(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)())
  : Adafruit_NeoPixel(pixels, pin, type)
{
  OnComplete = callback;
}

// Update the pattern
void NeoPatterns::Update()
{
  if ((millis() - lastUpdate) > Interval) // time to update
  {

	setBrightness(128);
	lastUpdate = millis();
    switch (ActivePattern)
    {
      case RAINBOW_CYCLE:
        RainbowCycleUpdate();
        break;
      case THEATER_CHASE:
        TheaterChaseUpdate();
        break;
      case COLOR_WIPE:
        ColorWipeUpdate();
        break;
      case SCANNER:
        ScannerUpdate();
        break;
      case FADE:
        FadeUpdate();
        break;
      case COMPASS:
        CompassUpdate();
        break;
		case DOUBLESCANNER:
		DoubleScannerUpdate();
		break;
		case FOLLOWER:
		FollowerUpdate();
		break;
		case HALFUPDOWN:
		HalfUpDownUpdate();
		break;
      default:
		break;
    }
  }

}

// Increment the Index and reset at the end
void
NeoPatterns::Increment()
{
  if (Direction == FORWARD)
  {
    Index++;
    if (Index >= TotalSteps)
    {
      Index = 0;
      if (OnComplete != NULL)
      {
        OnComplete(); // call the comlpetion callback
      }
    }
  }
  else // Direction == REVERSE
  {
    --Index;
    if (Index <= 0)
    {
      Index = TotalSteps - 1;
      if (OnComplete != NULL)
      {
        OnComplete(); // call the comlpetion callback
      }
    }
  }
}

// Reverse pattern direction
void
NeoPatterns::Reverse()
{
  if (Direction == FORWARD)
  {
    Direction = REVERSE;
    Index = TotalSteps - 1;
  }
  else
  {
    Direction = FORWARD;
    Index = 0;
  }
}

// Initialize for a RainbowCycle
void NeoPatterns::RainbowCycle(uint32_t interval, direction dir)
{
  ActivePattern = RAINBOW_CYCLE;
  Interval = interval;
  TotalSteps = 255;
  Index = 0;
  Direction = dir;
}

// Update the Rainbow Cycle Pattern
void
NeoPatterns::RainbowCycleUpdate()
{
  for (int i = 0; i < numPixels(); i++)
  {
    setPixelColor(i, Wheel(((i * 256 / numPixels()) + Index) & 255));
  }
  show();
  Increment();
}

// Initialize for a Theater Chase
void
NeoPatterns::TheaterChase(uint32_t color1, uint32_t color2, uint32_t interval, direction dir)
{
  ActivePattern = THEATER_CHASE;
  Interval = interval;
  TotalSteps = numPixels();
  Color1 = color1;
  Color2 = color2;
  Index = 0;
  Direction = dir;
}

// Update the Theater Chase Pattern
void
NeoPatterns::TheaterChaseUpdate()
{
  for (int i = 0; i < numPixels(); i++)
  {
    if ((i + Index) % 3 == 0)
    {
      setPixelColor(i, Color1);
    }
    else
    {
      setPixelColor(i, Color2);
    }
  }
  show();
  Increment();
}

// Initialize for a ColorWipe
void
NeoPatterns::ColorWipe(uint32_t color, uint32_t interval, direction dir)
{
  ActivePattern = COLOR_WIPE;
  Interval = interval;
  TotalSteps = numPixels();
  Color1 = color;
  Index = 0;
  Direction = dir;
}

// Update the Color Wipe Pattern
void
NeoPatterns::ColorWipeUpdate()
{
  setPixelColor(Index, Color1);
  show();
  Increment();
}

// Initialize for a SCANNNER
void
NeoPatterns::Scanner(uint32_t color1, uint32_t interval)
{
  ActivePattern = SCANNER;
  Interval = interval;
  TotalSteps = (numPixels() - 1) * 2;
  Color1 = color1;
  Index = 0;
}

// Update the Scanner Pattern
void
NeoPatterns::ScannerUpdate()
{
  for (int i = 0; i < numPixels(); i++)
  {
    if (i == Index)  // Scan Pixel to the right
    {
      setPixelColor(i, Color1);
    }
    else if (i == TotalSteps - Index) // Scan Pixel to the left
    {
      setPixelColor(i, Color1);
    }
    else // Fading tail
    {
      setPixelColor(i, DimColor(getPixelColor(i)));
    }
  }
  show();
  Increment();
}
void
NeoPatterns::Follower(uint32_t color1, uint32_t interval, uint8_t followers)
{
  ActivePattern = FOLLOWER;
  Interval = interval;
  TotalSteps = numPixels();
  Color1 = color1;
  Followers = followers;
  Index = 0;
}

// Initialize for a SCANNNER
void
NeoPatterns::DoubleScanner(uint32_t color1, uint32_t interval, uint8_t followers)
{
  ActivePattern = DOUBLESCANNER;
  Interval = interval;
  TotalSteps = numPixels();
  Color1 = color1;
  Followers = followers;
  Index = 0;
}

// Update the Scanner Pattern
void
NeoPatterns::DoubleScannerUpdate()
{
  for (int i = 0; i < numPixels(); i++)
  {
    if (i == Index)  // Scan Pixel to the right
    {
      setPixelColor(i, Color1);
    }
    else if (i == TotalSteps - Index) // Scan Pixel to the left
    {
      setPixelColor(i, Color1);
    }
    else // Fading tail
    {
      setPixelColor(i, DimColor(getPixelColor(i)));
    }
  }
  show();
  Increment();
}

// Update the Scanner Pattern
void
NeoPatterns::FollowerUpdate()
{

  for (int i = 0; i < numPixels(); i++)
  {
	  if (i == Index)  // Scan Pixel to the right
	  {
	    	for (int j = 0; j < Followers; j++)
		  	{
				if (i + ((TotalSteps / Followers) * j) >= TotalSteps)
				{
			  	  setPixelColor(i + ((TotalSteps / Followers) * j) - TotalSteps , Color1);
				}
				else
				{
	 			  setPixelColor(i + ((TotalSteps / Followers) * j) , Color1);
	  			}
  	  		 }
	   }

    /*
	else if (i == TotalSteps - Index) // Scan Pixel to the left
    {
      setPixelColor(i, Color1);
    }
	*/
   	  else // Fading tail
    	  {

	  		setPixelColor(i, DimColor(getPixelColor(i)));
    	  }

	}
  show();
  Increment();
}

// Initialize for a Fade
void
NeoPatterns::Fade(uint32_t color1, uint32_t color2, uint16_t steps, uint32_t interval, direction dir)
{
  ActivePattern = FADE;
  Interval = interval;
  TotalSteps = steps;
  Color1 = color1;
  Color2 = color2;
  Index = 0;
  Direction = dir;
}

// Update the Fade Pattern
void
NeoPatterns::FadeUpdate()
{
  // Calculate linear interpolation between Color1 and Color2
  // Optimise order of operations to minimize truncation error
  uint8_t red = ((Red(Color1) * (TotalSteps - Index)) + (Red(Color2) * Index)) / TotalSteps;
  uint8_t green = ((Green(Color1) * (TotalSteps - Index)) + (Green(Color2) * Index)) / TotalSteps;
  uint8_t blue = ((Blue(Color1) * (TotalSteps - Index)) + (Blue(Color2) * Index)) / TotalSteps;

  ColorSet(Color(red, green, blue));
  show();
  Increment();
}

// Initialize for a SCANNNER
void
NeoPatterns::HalfUpDown(uint32_t color1, uint32_t interval)
{
  ActivePattern = HALFUPDOWN;
  Interval = interval;
  TotalSteps = (numPixels() / 2) + 1;
  Color1 = color1;
  Index = 0;
}

// Update the Scanner Pattern
void
NeoPatterns::HalfUpDownUpdate()
{
  for (int i = 0; i < numPixels(); i++)
  {
    if (i == Index)  // Scan Pixel to the right
    {
      setPixelColor(i, Color1);
	  setPixelColor(numPixels()-i, Color1);
    }

  }
  show();
  Increment();
}

// Initialize for a Compass Function
void
NeoPatterns::Compass( uint16_t compassreading, uint16_t steps)
{
  ActivePattern = COMPASS;
  CompassReading = compassreading;
  Index = 0;
  TotalSteps = steps;
}

// Update the Fade Pattern
void
NeoPatterns::CompassUpdate()
{
  // Calculate linear interpolation between Color1 and Color2
  // Optimise order of operations to minimize truncation error
  //uint8_t red = ((Red(Color1) * (TotalSteps - Index)) + (Red(Color2) * Index)) / TotalSteps;
  //uint8_t green = ((Green(Color1) * (TotalSteps - Index)) + (Green(Color2) * Index)) / TotalSteps;
  //uint8_t blue = ((Blue(Color1) * (TotalSteps - Index)) + (Blue(Color2) * Index)) / TotalSteps;
  uint8_t red = 0;
  uint8_t green = 0;
  uint8_t blue = 0;

  if ( CompassReading / 4 == 0 )
  {
    red = 255;
    green = 0;
    blue = 0;

  }
  else if ( CompassReading / 4 == 1 )
  {

    red = 128;
    green = 0;
    blue = 255;
  }
  else if ( CompassReading / 4 == 2 )
  {

    red = 0;
    green = 255;
    blue = 255;
  }
  else if ( CompassReading / 4 == 3 )
  {

    red = 128;
    green = 255;
    blue = 0;
  }
  //calculate based on 0-255 what direction we are facing from compass, brightness 50%-100%
  setBrightness( ( ( (CompassReading % 64) *.5 ) + 32 ) * 4);
  ColorSet(Color(red, green, blue));
  show();
}

// Calculate 50% dimmed version of a color (used by ScannerUpdate)
uint32_t
NeoPatterns::DimColor(uint32_t color)
{
  // Shift R, G and B components one bit to the right
  uint32_t dimColor = Color(Red(color) >> 1, Green(color) >> 1, Blue(color) >> 1);
  return dimColor;
}

// Set all pixels to a color (synchronously)
void
NeoPatterns::ColorSet(uint32_t color)
{
  for (int i = 0; i < numPixels(); i++)
  {
    setPixelColor(i, color);
  }
  show();
}

// Returns the Red component of a 32-bit color
uint8_t
NeoPatterns::Red(uint32_t color)
{
  return (color >> 16) & 0xFF;
}

// Returns the Green component of a 32-bit color
uint8_t
NeoPatterns::Green(uint32_t color)
{
  return (color >> 8) & 0xFF;
}

// Returns the Blue component of a 32-bit color
uint8_t
NeoPatterns::Blue(uint32_t color)
{
  return color & 0xFF;
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t
NeoPatterns::Wheel(byte WheelPos)
{
  WheelPos = 255 - WheelPos;
  if (WheelPos < 85)
  {
    return Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  else if (WheelPos < 170)
  {
    WheelPos -= 85;
    return Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  else
  {
    WheelPos -= 170;
    return Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  }
}
<commit_msg>fix minor bug<commit_after>#include "NeoPatterns.h"

NeoPatterns::NeoPatterns(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)())
  : Adafruit_NeoPixel(pixels, pin, type)
{
  OnComplete = callback;
}

// Update the pattern
void NeoPatterns::Update()
{
  if ((millis() - lastUpdate) > Interval) // time to update
  {

	setBrightness(128);
	lastUpdate = millis();
    switch (ActivePattern)
    {
      case RAINBOW_CYCLE:
        RainbowCycleUpdate();
        break;
      case THEATER_CHASE:
        TheaterChaseUpdate();
        break;
      case COLOR_WIPE:
        ColorWipeUpdate();
        break;
      case SCANNER:
        ScannerUpdate();
        break;
      case FADE:
        FadeUpdate();
        break;
      case COMPASS:
        CompassUpdate();
        break;
		case DOUBLESCANNER:
		DoubleScannerUpdate();
		break;
		case FOLLOWER:
		FollowerUpdate();
		break;
		case HALFUPDOWN:
		HalfUpDownUpdate();
		break;
      default:
		break;
    }
  }

}

// Increment the Index and reset at the end
void
NeoPatterns::Increment()
{
  if (Direction == FORWARD)
  {
    Index++;
    if (Index >= TotalSteps)
    {
      Index = 0;
      if (OnComplete != NULL)
      {
        OnComplete(); // call the comlpetion callback
      }
    }
  }
  else // Direction == REVERSE
  {
    --Index;
    if (Index < 0)
    {
      Index = TotalSteps - 1;
      if (OnComplete != NULL)
      {
        OnComplete(); // call the comlpetion callback
      }
    }
  }
}

// Reverse pattern direction
void
NeoPatterns::Reverse()
{
  if (Direction == FORWARD)
  {
    Direction = REVERSE;
    Index = TotalSteps - 1;
  }
  else
  {
    Direction = FORWARD;
    Index = 0;
  }
}

// Initialize for a RainbowCycle
void NeoPatterns::RainbowCycle(uint32_t interval, direction dir)
{
  ActivePattern = RAINBOW_CYCLE;
  Interval = interval;
  TotalSteps = 255;
  Index = 0;
  Direction = dir;
}

// Update the Rainbow Cycle Pattern
void
NeoPatterns::RainbowCycleUpdate()
{
  for (int i = 0; i < numPixels(); i++)
  {
    setPixelColor(i, Wheel(((i * 256 / numPixels()) + Index) & 255));
  }
  show();
  Increment();
}

// Initialize for a Theater Chase
void
NeoPatterns::TheaterChase(uint32_t color1, uint32_t color2, uint32_t interval, direction dir)
{
  ActivePattern = THEATER_CHASE;
  Interval = interval;
  TotalSteps = numPixels();
  Color1 = color1;
  Color2 = color2;
  Index = 0;
  Direction = dir;
}

// Update the Theater Chase Pattern
void
NeoPatterns::TheaterChaseUpdate()
{
  for (int i = 0; i < numPixels(); i++)
  {
    if ((i + Index) % 3 == 0)
    {
      setPixelColor(i, Color1);
    }
    else
    {
      setPixelColor(i, Color2);
    }
  }
  show();
  Increment();
}

// Initialize for a ColorWipe
void
NeoPatterns::ColorWipe(uint32_t color, uint32_t interval, direction dir)
{
  ActivePattern = COLOR_WIPE;
  Interval = interval;
  TotalSteps = numPixels();
  Color1 = color;
  Index = 0;
  Direction = dir;
}

// Update the Color Wipe Pattern
void
NeoPatterns::ColorWipeUpdate()
{
  setPixelColor(Index, Color1);
  show();
  Increment();
}

// Initialize for a SCANNNER
void
NeoPatterns::Scanner(uint32_t color1, uint32_t interval)
{
  ActivePattern = SCANNER;
  Interval = interval;
  TotalSteps = (numPixels() - 1) * 2;
  Color1 = color1;
  Index = 0;
}

// Update the Scanner Pattern
void
NeoPatterns::ScannerUpdate()
{
  for (int i = 0; i < numPixels(); i++)
  {
    if (i == Index)  // Scan Pixel to the right
    {
      setPixelColor(i, Color1);
    }
    else if (i == TotalSteps - Index) // Scan Pixel to the left
    {
      setPixelColor(i, Color1);
    }
    else // Fading tail
    {
      setPixelColor(i, DimColor(getPixelColor(i)));
    }
  }
  show();
  Increment();
}
void
NeoPatterns::Follower(uint32_t color1, uint32_t interval, uint8_t followers)
{
  ActivePattern = FOLLOWER;
  Interval = interval;
  TotalSteps = numPixels();
  Color1 = color1;
  Followers = followers;
  Index = 0;
}

// Initialize for a SCANNNER
void
NeoPatterns::DoubleScanner(uint32_t color1, uint32_t interval, uint8_t followers)
{
  ActivePattern = DOUBLESCANNER;
  Interval = interval;
  TotalSteps = numPixels();
  Color1 = color1;
  Followers = followers;
  Index = 0;
}

// Update the Scanner Pattern
void
NeoPatterns::DoubleScannerUpdate()
{
  for (int i = 0; i < numPixels(); i++)
  {
    if (i == Index)  // Scan Pixel to the right
    {
      setPixelColor(i, Color1);
    }
    else if (i == TotalSteps - Index) // Scan Pixel to the left
    {
      setPixelColor(i, Color1);
    }
    else // Fading tail
    {
      setPixelColor(i, DimColor(getPixelColor(i)));
    }
  }
  show();
  Increment();
}

// Update the Scanner Pattern
void
NeoPatterns::FollowerUpdate()
{

  for (int i = 0; i < numPixels(); i++)
  {
	  if (i == Index)  // Scan Pixel to the right
	  {
	    	for (int j = 0; j < Followers; j++)
		  	{
				if (i + ((TotalSteps / Followers) * j) >= TotalSteps)
				{
			  	  setPixelColor(i + ((TotalSteps / Followers) * j) - TotalSteps , Color1);
				}
				else
				{
	 			  setPixelColor(i + ((TotalSteps / Followers) * j) , Color1);
	  			}
  	  		 }
	   }

    /*
	else if (i == TotalSteps - Index) // Scan Pixel to the left
    {
      setPixelColor(i, Color1);
    }
	*/
   	  else // Fading tail
    	  {

	  		setPixelColor(i, DimColor(getPixelColor(i)));
    	  }

	}
  show();
  Increment();
}

// Initialize for a Fade
void
NeoPatterns::Fade(uint32_t color1, uint32_t color2, uint16_t steps, uint32_t interval, direction dir)
{
  ActivePattern = FADE;
  Interval = interval;
  TotalSteps = steps;
  Color1 = color1;
  Color2 = color2;
  Index = 0;
  Direction = dir;
}

// Update the Fade Pattern
void
NeoPatterns::FadeUpdate()
{
  // Calculate linear interpolation between Color1 and Color2
  // Optimise order of operations to minimize truncation error
  uint8_t red = ((Red(Color1) * (TotalSteps - Index)) + (Red(Color2) * Index)) / TotalSteps;
  uint8_t green = ((Green(Color1) * (TotalSteps - Index)) + (Green(Color2) * Index)) / TotalSteps;
  uint8_t blue = ((Blue(Color1) * (TotalSteps - Index)) + (Blue(Color2) * Index)) / TotalSteps;

  ColorSet(Color(red, green, blue));
  show();
  Increment();
}

// Initialize for a SCANNNER
void
NeoPatterns::HalfUpDown(uint32_t color1, uint32_t interval)
{
  ActivePattern = HALFUPDOWN;
  Interval = interval;
  TotalSteps = (numPixels() / 2) + 1;
  Color1 = color1;
  Index = 0;
}

// Update the Scanner Pattern
void
NeoPatterns::HalfUpDownUpdate()
{
  for (int i = 0; i < numPixels(); i++)
  {
    if (i == Index)  // Scan Pixel to the right
    {
      setPixelColor(i, Color1);
	  setPixelColor(numPixels()-i, Color1);
    }

  }
  show();
  Increment();
}

// Initialize for a Compass Function
void
NeoPatterns::Compass( uint16_t compassreading, uint16_t steps)
{
  ActivePattern = COMPASS;
  CompassReading = compassreading;
  Index = 0;
  TotalSteps = steps;
}

// Update the Fade Pattern
void
NeoPatterns::CompassUpdate()
{
  // Calculate linear interpolation between Color1 and Color2
  // Optimise order of operations to minimize truncation error
  //uint8_t red = ((Red(Color1) * (TotalSteps - Index)) + (Red(Color2) * Index)) / TotalSteps;
  //uint8_t green = ((Green(Color1) * (TotalSteps - Index)) + (Green(Color2) * Index)) / TotalSteps;
  //uint8_t blue = ((Blue(Color1) * (TotalSteps - Index)) + (Blue(Color2) * Index)) / TotalSteps;
  uint8_t red = 0;
  uint8_t green = 0;
  uint8_t blue = 0;

  if ( CompassReading / 4 == 0 )
  {
    red = 255;
    green = 0;
    blue = 0;

  }
  else if ( CompassReading / 4 == 1 )
  {

    red = 128;
    green = 0;
    blue = 255;
  }
  else if ( CompassReading / 4 == 2 )
  {

    red = 0;
    green = 255;
    blue = 255;
  }
  else if ( CompassReading / 4 == 3 )
  {

    red = 128;
    green = 255;
    blue = 0;
  }
  //calculate based on 0-255 what direction we are facing from compass, brightness 50%-100%
  setBrightness( ( ( (CompassReading % 64) *.5 ) + 32 ) * 4);
  ColorSet(Color(red, green, blue));
  show();
}

// Calculate 50% dimmed version of a color (used by ScannerUpdate)
uint32_t
NeoPatterns::DimColor(uint32_t color)
{
  // Shift R, G and B components one bit to the right
  uint32_t dimColor = Color(Red(color) >> 1, Green(color) >> 1, Blue(color) >> 1);
  return dimColor;
}

// Set all pixels to a color (synchronously)
void
NeoPatterns::ColorSet(uint32_t color)
{
  for (int i = 0; i < numPixels(); i++)
  {
    setPixelColor(i, color);
  }
  show();
}

// Returns the Red component of a 32-bit color
uint8_t
NeoPatterns::Red(uint32_t color)
{
  return (color >> 16) & 0xFF;
}

// Returns the Green component of a 32-bit color
uint8_t
NeoPatterns::Green(uint32_t color)
{
  return (color >> 8) & 0xFF;
}

// Returns the Blue component of a 32-bit color
uint8_t
NeoPatterns::Blue(uint32_t color)
{
  return color & 0xFF;
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t
NeoPatterns::Wheel(byte WheelPos)
{
  WheelPos = 255 - WheelPos;
  if (WheelPos < 85)
  {
    return Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  else if (WheelPos < 170)
  {
    WheelPos -= 85;
    return Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  else
  {
    WheelPos -= 170;
    return Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  }
}
<|endoftext|>


<commit_before>/****************************************************************/
/*               DO NOT MODIFY THIS HEADER                      */
/* MOOSE - Multiphysics Object Oriented Simulation Environment  */
/*                                                              */
/*           (c) 2010 Battelle Energy Alliance, LLC             */
/*                   ALL RIGHTS RESERVED                        */
/*                                                              */
/*          Prepared by Battelle Energy Alliance, LLC           */
/*            Under Contract No. DE-AC07-05ID14517              */
/*            With the U. S. Department of Energy               */
/*                                                              */
/*            See COPYRIGHT for full restrictions               */
/****************************************************************/

#include "TwoCircleMarker.h"

template <>
InputParameters
validParams<TwoCircleMarker>()
{
  InputParameters params = validParams<Marker>();
  params.addRequiredParam<Point>("point1", "The center of the first circle.");
  params.addRequiredParam<Real>("radius1",
                                "Distance from the center of the first circle to mark elements.");
  params.addRequiredParam<Point>("point2", "The center of the second circle.");
  params.addRequiredParam<Real>("radius2",
                                "Distance from the center of the second circle to mark elements.");
  params.addParam<Real>("shut_off_time", std::numeric_limits<Real>::max(),
                                "Time at which the second circle becomes inactive.");

  MooseEnum marker_states = Marker::markerStates();

  params.addRequiredParam<MooseEnum>(
      "inside", marker_states, "How to mark elements inside the circles.");
  params.addRequiredParam<MooseEnum>(
      "outside", marker_states, "How to mark elements outside the circles.");

  return params;
}

TwoCircleMarker::TwoCircleMarker(const InputParameters & parameters)
  : Marker(parameters),
    _inside((MarkerValue)(int)parameters.get<MooseEnum>("inside")),
    _outside((MarkerValue)(int)parameters.get<MooseEnum>("outside")),
    _p1(getParam<Point>("point1")),
    _r1(getParam<Real>("radius1")),
    _p2(getParam<Point>("point2")),
    _r2(getParam<Real>("radius2")),
    _shut_off_time(getParam<Real>("shut_off_time"))
{
}

Marker::MarkerValue
TwoCircleMarker::computeElementMarker()
{
  Point centroid = _current_elem->centroid();

  if (((centroid - _p1).size() < _r1) || (((centroid - _p2).size() < _r2) && (_fe_problem.time() < _shut_off_time)))
    return _inside;

  return _outside;
}
<commit_msg>apply clang format patch<commit_after>/****************************************************************/
/*               DO NOT MODIFY THIS HEADER                      */
/* MOOSE - Multiphysics Object Oriented Simulation Environment  */
/*                                                              */
/*           (c) 2010 Battelle Energy Alliance, LLC             */
/*                   ALL RIGHTS RESERVED                        */
/*                                                              */
/*          Prepared by Battelle Energy Alliance, LLC           */
/*            Under Contract No. DE-AC07-05ID14517              */
/*            With the U. S. Department of Energy               */
/*                                                              */
/*            See COPYRIGHT for full restrictions               */
/****************************************************************/

#include "TwoCircleMarker.h"

template <>
InputParameters
validParams<TwoCircleMarker>()
{
  InputParameters params = validParams<Marker>();
  params.addRequiredParam<Point>("point1", "The center of the first circle.");
  params.addRequiredParam<Real>("radius1",
                                "Distance from the center of the first circle to mark elements.");
  params.addRequiredParam<Point>("point2", "The center of the second circle.");
  params.addRequiredParam<Real>("radius2",
                                "Distance from the center of the second circle to mark elements.");
  params.addParam<Real>("shut_off_time",
                        std::numeric_limits<Real>::max(),
                        "Time at which the second circle becomes inactive.");

  MooseEnum marker_states = Marker::markerStates();

  params.addRequiredParam<MooseEnum>(
      "inside", marker_states, "How to mark elements inside the circles.");
  params.addRequiredParam<MooseEnum>(
      "outside", marker_states, "How to mark elements outside the circles.");

  return params;
}

TwoCircleMarker::TwoCircleMarker(const InputParameters & parameters)
  : Marker(parameters),
    _inside((MarkerValue)(int)parameters.get<MooseEnum>("inside")),
    _outside((MarkerValue)(int)parameters.get<MooseEnum>("outside")),
    _p1(getParam<Point>("point1")),
    _r1(getParam<Real>("radius1")),
    _p2(getParam<Point>("point2")),
    _r2(getParam<Real>("radius2")),
    _shut_off_time(getParam<Real>("shut_off_time"))
{
}

Marker::MarkerValue
TwoCircleMarker::computeElementMarker()
{
  Point centroid = _current_elem->centroid();

  if (((centroid - _p1).size() < _r1) ||
      (((centroid - _p2).size() < _r2) && (_fe_problem.time() < _shut_off_time)))
    return _inside;

  return _outside;
}
<|endoftext|>


<commit_before>// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "gtest/gtest.h"
#include "test/util/capability_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/fs_util.h"
#include "test/util/temp_path.h"
#include "test/util/temp_umask.h"
#include "test/util/test_util.h"

namespace gvisor {
namespace testing {

namespace {
TEST(CreateTest, TmpFile) {
  int fd;
  EXPECT_THAT(fd = open(JoinPath(GetAbsoluteTestTmpdir(), "a").c_str(),
                        O_RDWR | O_CREAT, 0666),
              SyscallSucceeds());
  EXPECT_THAT(close(fd), SyscallSucceeds());
}

TEST(CreateTest, ExistingFile) {
  int fd;
  EXPECT_THAT(
      fd = open(JoinPath(GetAbsoluteTestTmpdir(), "ExistingFile").c_str(),
                O_RDWR | O_CREAT, 0666),
      SyscallSucceeds());
  EXPECT_THAT(close(fd), SyscallSucceeds());

  EXPECT_THAT(
      fd = open(JoinPath(GetAbsoluteTestTmpdir(), "ExistingFile").c_str(),
                O_RDWR | O_CREAT, 0666),
      SyscallSucceeds());
  EXPECT_THAT(close(fd), SyscallSucceeds());
}

TEST(CreateTest, CreateAtFile) {
  int dirfd;
  EXPECT_THAT(dirfd = open(GetAbsoluteTestTmpdir().c_str(), O_DIRECTORY, 0666),
              SyscallSucceeds());
  EXPECT_THAT(openat(dirfd, "CreateAtFile", O_RDWR | O_CREAT, 0666),
              SyscallSucceeds());
  EXPECT_THAT(close(dirfd), SyscallSucceeds());
}

TEST(CreateTest, HonorsUmask_NoRandomSave) {
  const DisableSave ds;  // file cannot be re-opened as writable.
  TempUmask mask(0222);
  int fd;
  ASSERT_THAT(
      fd = open(JoinPath(GetAbsoluteTestTmpdir(), "UmaskedFile").c_str(),
                O_RDWR | O_CREAT, 0666),
      SyscallSucceeds());
  struct stat statbuf;
  ASSERT_THAT(fstat(fd, &statbuf), SyscallSucceeds());
  EXPECT_EQ(0444, statbuf.st_mode & 0777);
  EXPECT_THAT(close(fd), SyscallSucceeds());
}

TEST(CreateTest, CreateExclusively) {
  std::string filename = NewTempAbsPath();

  int fd;
  ASSERT_THAT(fd = open(filename.c_str(), O_CREAT | O_RDWR, 0644),
              SyscallSucceeds());
  EXPECT_THAT(close(fd), SyscallSucceeds());

  EXPECT_THAT(open(filename.c_str(), O_CREAT | O_EXCL | O_RDWR, 0644),
              SyscallFailsWithErrno(EEXIST));
}

TEST(CreateTest, CreatWithOTrunc) {
  std::string dirpath = JoinPath(GetAbsoluteTestTmpdir(), "truncd");
  ASSERT_THAT(mkdir(dirpath.c_str(), 0777), SyscallSucceeds());
  ASSERT_THAT(open(dirpath.c_str(), O_CREAT | O_TRUNC, 0666),
              SyscallFailsWithErrno(EISDIR));
}

TEST(CreateTest, CreatDirWithOTruncAndReadOnly) {
  std::string dirpath = JoinPath(GetAbsoluteTestTmpdir(), "truncd");
  ASSERT_THAT(mkdir(dirpath.c_str(), 0777), SyscallSucceeds());
  ASSERT_THAT(open(dirpath.c_str(), O_CREAT | O_TRUNC | O_RDONLY, 0666),
              SyscallFailsWithErrno(EISDIR));
}

TEST(CreateTest, CreatFileWithOTruncAndReadOnly) {
  std::string dirpath = JoinPath(GetAbsoluteTestTmpdir(), "truncfile");
  int dirfd;
  ASSERT_THAT(dirfd = open(dirpath.c_str(), O_RDWR | O_CREAT, 0666),
              SyscallSucceeds());
  ASSERT_THAT(open(dirpath.c_str(), O_CREAT | O_TRUNC | O_RDONLY, 0666),
              SyscallSucceeds());
  ASSERT_THAT(close(dirfd), SyscallSucceeds());
}

TEST(CreateTest, CreateFailsOnUnpermittedDir) {
  // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to
  // always override directory permissions.
  ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));
  ASSERT_THAT(open("/foo", O_CREAT | O_RDWR, 0644),
              SyscallFailsWithErrno(EACCES));
}

TEST(CreateTest, CreateFailsOnDirWithoutWritePerms) {
  // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to
  // always override directory permissions.
  ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));
  auto parent = ASSERT_NO_ERRNO_AND_VALUE(
      TempPath::CreateDirWith(GetAbsoluteTestTmpdir(), 0555));
  auto file = JoinPath(parent.path(), "foo");
  ASSERT_THAT(open(file.c_str(), O_CREAT | O_RDWR, 0644),
              SyscallFailsWithErrno(EACCES));
}

// A file originally created RW, but opened RO can later be opened RW.
// Regression test for b/65385065.
TEST(CreateTest, OpenCreateROThenRW) {
  TempPath file(NewTempAbsPath());

  // Create a RW file, but only open it RO.
  FileDescriptor fd1 = ASSERT_NO_ERRNO_AND_VALUE(
      Open(file.path(), O_CREAT | O_EXCL | O_RDONLY, 0644));

  // Now get a RW FD.
  FileDescriptor fd2 = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR));

  // fd1 is not writable, but fd2 is.
  char c = 'a';
  EXPECT_THAT(WriteFd(fd1.get(), &c, 1), SyscallFailsWithErrno(EBADF));
  EXPECT_THAT(WriteFd(fd2.get(), &c, 1), SyscallSucceedsWithValue(1));
}

TEST(CreateTest, ChmodReadToWriteBetweenOpens_NoRandomSave) {
  // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to
  // override file read/write permissions. CAP_DAC_READ_SEARCH needs to be
  // cleared for the same reason.
  ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));
  ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false));

  const TempPath file =
      ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0400));

  const FileDescriptor rfd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY));

  // Cannot restore after making permissions more restrictive.
  const DisableSave ds;
  ASSERT_THAT(fchmod(rfd.get(), 0200), SyscallSucceeds());

  EXPECT_THAT(open(file.path().c_str(), O_RDONLY),
              SyscallFailsWithErrno(EACCES));

  const FileDescriptor wfd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY));

  char c = 'x';
  EXPECT_THAT(write(wfd.get(), &c, 1), SyscallSucceedsWithValue(1));
  c = 0;
  EXPECT_THAT(read(rfd.get(), &c, 1), SyscallSucceedsWithValue(1));
  EXPECT_EQ(c, 'x');
}

TEST(CreateTest, ChmodWriteToReadBetweenOpens_NoRandomSave) {
  // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to
  // override file read/write permissions.
  ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));

  const TempPath file =
      ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0200));

  const FileDescriptor wfd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY));

  // Cannot restore after making permissions more restrictive.
  const DisableSave ds;
  ASSERT_THAT(fchmod(wfd.get(), 0400), SyscallSucceeds());

  EXPECT_THAT(open(file.path().c_str(), O_WRONLY),
              SyscallFailsWithErrno(EACCES));

  const FileDescriptor rfd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY));

  char c = 'x';
  EXPECT_THAT(write(wfd.get(), &c, 1), SyscallSucceedsWithValue(1));
  c = 0;
  EXPECT_THAT(read(rfd.get(), &c, 1), SyscallSucceedsWithValue(1));
  EXPECT_EQ(c, 'x');
}

TEST(CreateTest, CreateWithReadFlagNotAllowedByMode_NoRandomSave) {
  // The only time we can open a file with flags forbidden by its permissions
  // is when we are creating the file. We cannot re-open with the same flags,
  // so we cannot restore an fd obtained from such an operation.
  const DisableSave ds;

  // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to
  // override file read/write permissions. CAP_DAC_READ_SEARCH needs to be
  // cleared for the same reason.
  ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));
  ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false));

  // Create and open a file with read flag but without read permissions.
  const std::string path = NewTempAbsPath();
  const FileDescriptor rfd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_CREAT | O_RDONLY, 0222));

  EXPECT_THAT(open(path.c_str(), O_RDONLY), SyscallFailsWithErrno(EACCES));
  const FileDescriptor wfd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_WRONLY));

  char c = 'x';
  EXPECT_THAT(write(wfd.get(), &c, 1), SyscallSucceedsWithValue(1));
  c = 0;
  EXPECT_THAT(read(rfd.get(), &c, 1), SyscallSucceedsWithValue(1));
  EXPECT_EQ(c, 'x');
}

TEST(CreateTest, CreateWithWriteFlagNotAllowedByMode_NoRandomSave) {
  // The only time we can open a file with flags forbidden by its permissions
  // is when we are creating the file. We cannot re-open with the same flags,
  // so we cannot restore an fd obtained from such an operation.
  const DisableSave ds;

  // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to
  // override file read/write permissions.
  ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));

  // Create and open a file with write flag but without write permissions.
  const std::string path = NewTempAbsPath();
  const FileDescriptor wfd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_CREAT | O_WRONLY, 0444));

  EXPECT_THAT(open(path.c_str(), O_WRONLY), SyscallFailsWithErrno(EACCES));
  const FileDescriptor rfd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_RDONLY));

  char c = 'x';
  EXPECT_THAT(write(wfd.get(), &c, 1), SyscallSucceedsWithValue(1));
  c = 0;
  EXPECT_THAT(read(rfd.get(), &c, 1), SyscallSucceedsWithValue(1));
  EXPECT_EQ(c, 'x');
}

}  // namespace

}  // namespace testing
}  // namespace gvisor
<commit_msg>Remove invalid test case.<commit_after>// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "gtest/gtest.h"
#include "test/util/capability_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/fs_util.h"
#include "test/util/temp_path.h"
#include "test/util/temp_umask.h"
#include "test/util/test_util.h"

namespace gvisor {
namespace testing {

namespace {
TEST(CreateTest, TmpFile) {
  int fd;
  EXPECT_THAT(fd = open(JoinPath(GetAbsoluteTestTmpdir(), "a").c_str(),
                        O_RDWR | O_CREAT, 0666),
              SyscallSucceeds());
  EXPECT_THAT(close(fd), SyscallSucceeds());
}

TEST(CreateTest, ExistingFile) {
  int fd;
  EXPECT_THAT(
      fd = open(JoinPath(GetAbsoluteTestTmpdir(), "ExistingFile").c_str(),
                O_RDWR | O_CREAT, 0666),
      SyscallSucceeds());
  EXPECT_THAT(close(fd), SyscallSucceeds());

  EXPECT_THAT(
      fd = open(JoinPath(GetAbsoluteTestTmpdir(), "ExistingFile").c_str(),
                O_RDWR | O_CREAT, 0666),
      SyscallSucceeds());
  EXPECT_THAT(close(fd), SyscallSucceeds());
}

TEST(CreateTest, CreateAtFile) {
  int dirfd;
  EXPECT_THAT(dirfd = open(GetAbsoluteTestTmpdir().c_str(), O_DIRECTORY, 0666),
              SyscallSucceeds());
  EXPECT_THAT(openat(dirfd, "CreateAtFile", O_RDWR | O_CREAT, 0666),
              SyscallSucceeds());
  EXPECT_THAT(close(dirfd), SyscallSucceeds());
}

TEST(CreateTest, HonorsUmask_NoRandomSave) {
  const DisableSave ds;  // file cannot be re-opened as writable.
  TempUmask mask(0222);
  int fd;
  ASSERT_THAT(
      fd = open(JoinPath(GetAbsoluteTestTmpdir(), "UmaskedFile").c_str(),
                O_RDWR | O_CREAT, 0666),
      SyscallSucceeds());
  struct stat statbuf;
  ASSERT_THAT(fstat(fd, &statbuf), SyscallSucceeds());
  EXPECT_EQ(0444, statbuf.st_mode & 0777);
  EXPECT_THAT(close(fd), SyscallSucceeds());
}

TEST(CreateTest, CreateExclusively) {
  std::string filename = NewTempAbsPath();

  int fd;
  ASSERT_THAT(fd = open(filename.c_str(), O_CREAT | O_RDWR, 0644),
              SyscallSucceeds());
  EXPECT_THAT(close(fd), SyscallSucceeds());

  EXPECT_THAT(open(filename.c_str(), O_CREAT | O_EXCL | O_RDWR, 0644),
              SyscallFailsWithErrno(EEXIST));
}

TEST(CreateTest, CreatWithOTrunc) {
  std::string dirpath = JoinPath(GetAbsoluteTestTmpdir(), "truncd");
  ASSERT_THAT(mkdir(dirpath.c_str(), 0777), SyscallSucceeds());
  ASSERT_THAT(open(dirpath.c_str(), O_CREAT | O_TRUNC, 0666),
              SyscallFailsWithErrno(EISDIR));
}

TEST(CreateTest, CreatDirWithOTruncAndReadOnly) {
  std::string dirpath = JoinPath(GetAbsoluteTestTmpdir(), "truncd");
  ASSERT_THAT(mkdir(dirpath.c_str(), 0777), SyscallSucceeds());
  ASSERT_THAT(open(dirpath.c_str(), O_CREAT | O_TRUNC | O_RDONLY, 0666),
              SyscallFailsWithErrno(EISDIR));
}

TEST(CreateTest, CreatFileWithOTruncAndReadOnly) {
  std::string dirpath = JoinPath(GetAbsoluteTestTmpdir(), "truncfile");
  int dirfd;
  ASSERT_THAT(dirfd = open(dirpath.c_str(), O_RDWR | O_CREAT, 0666),
              SyscallSucceeds());
  ASSERT_THAT(open(dirpath.c_str(), O_CREAT | O_TRUNC | O_RDONLY, 0666),
              SyscallSucceeds());
  ASSERT_THAT(close(dirfd), SyscallSucceeds());
}

TEST(CreateTest, CreateFailsOnDirWithoutWritePerms) {
  // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to
  // always override directory permissions.
  ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));
  auto parent = ASSERT_NO_ERRNO_AND_VALUE(
      TempPath::CreateDirWith(GetAbsoluteTestTmpdir(), 0555));
  auto file = JoinPath(parent.path(), "foo");
  ASSERT_THAT(open(file.c_str(), O_CREAT | O_RDWR, 0644),
              SyscallFailsWithErrno(EACCES));
}

// A file originally created RW, but opened RO can later be opened RW.
// Regression test for b/65385065.
TEST(CreateTest, OpenCreateROThenRW) {
  TempPath file(NewTempAbsPath());

  // Create a RW file, but only open it RO.
  FileDescriptor fd1 = ASSERT_NO_ERRNO_AND_VALUE(
      Open(file.path(), O_CREAT | O_EXCL | O_RDONLY, 0644));

  // Now get a RW FD.
  FileDescriptor fd2 = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR));

  // fd1 is not writable, but fd2 is.
  char c = 'a';
  EXPECT_THAT(WriteFd(fd1.get(), &c, 1), SyscallFailsWithErrno(EBADF));
  EXPECT_THAT(WriteFd(fd2.get(), &c, 1), SyscallSucceedsWithValue(1));
}

TEST(CreateTest, ChmodReadToWriteBetweenOpens_NoRandomSave) {
  // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to
  // override file read/write permissions. CAP_DAC_READ_SEARCH needs to be
  // cleared for the same reason.
  ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));
  ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false));

  const TempPath file =
      ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0400));

  const FileDescriptor rfd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY));

  // Cannot restore after making permissions more restrictive.
  const DisableSave ds;
  ASSERT_THAT(fchmod(rfd.get(), 0200), SyscallSucceeds());

  EXPECT_THAT(open(file.path().c_str(), O_RDONLY),
              SyscallFailsWithErrno(EACCES));

  const FileDescriptor wfd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY));

  char c = 'x';
  EXPECT_THAT(write(wfd.get(), &c, 1), SyscallSucceedsWithValue(1));
  c = 0;
  EXPECT_THAT(read(rfd.get(), &c, 1), SyscallSucceedsWithValue(1));
  EXPECT_EQ(c, 'x');
}

TEST(CreateTest, ChmodWriteToReadBetweenOpens_NoRandomSave) {
  // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to
  // override file read/write permissions.
  ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));

  const TempPath file =
      ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0200));

  const FileDescriptor wfd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY));

  // Cannot restore after making permissions more restrictive.
  const DisableSave ds;
  ASSERT_THAT(fchmod(wfd.get(), 0400), SyscallSucceeds());

  EXPECT_THAT(open(file.path().c_str(), O_WRONLY),
              SyscallFailsWithErrno(EACCES));

  const FileDescriptor rfd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY));

  char c = 'x';
  EXPECT_THAT(write(wfd.get(), &c, 1), SyscallSucceedsWithValue(1));
  c = 0;
  EXPECT_THAT(read(rfd.get(), &c, 1), SyscallSucceedsWithValue(1));
  EXPECT_EQ(c, 'x');
}

TEST(CreateTest, CreateWithReadFlagNotAllowedByMode_NoRandomSave) {
  // The only time we can open a file with flags forbidden by its permissions
  // is when we are creating the file. We cannot re-open with the same flags,
  // so we cannot restore an fd obtained from such an operation.
  const DisableSave ds;

  // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to
  // override file read/write permissions. CAP_DAC_READ_SEARCH needs to be
  // cleared for the same reason.
  ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));
  ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false));

  // Create and open a file with read flag but without read permissions.
  const std::string path = NewTempAbsPath();
  const FileDescriptor rfd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_CREAT | O_RDONLY, 0222));

  EXPECT_THAT(open(path.c_str(), O_RDONLY), SyscallFailsWithErrno(EACCES));
  const FileDescriptor wfd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_WRONLY));

  char c = 'x';
  EXPECT_THAT(write(wfd.get(), &c, 1), SyscallSucceedsWithValue(1));
  c = 0;
  EXPECT_THAT(read(rfd.get(), &c, 1), SyscallSucceedsWithValue(1));
  EXPECT_EQ(c, 'x');
}

TEST(CreateTest, CreateWithWriteFlagNotAllowedByMode_NoRandomSave) {
  // The only time we can open a file with flags forbidden by its permissions
  // is when we are creating the file. We cannot re-open with the same flags,
  // so we cannot restore an fd obtained from such an operation.
  const DisableSave ds;

  // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to
  // override file read/write permissions.
  ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));

  // Create and open a file with write flag but without write permissions.
  const std::string path = NewTempAbsPath();
  const FileDescriptor wfd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_CREAT | O_WRONLY, 0444));

  EXPECT_THAT(open(path.c_str(), O_WRONLY), SyscallFailsWithErrno(EACCES));
  const FileDescriptor rfd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_RDONLY));

  char c = 'x';
  EXPECT_THAT(write(wfd.get(), &c, 1), SyscallSucceedsWithValue(1));
  c = 0;
  EXPECT_THAT(read(rfd.get(), &c, 1), SyscallSucceedsWithValue(1));
  EXPECT_EQ(c, 'x');
}

}  // namespace

}  // namespace testing
}  // namespace gvisor
<|endoftext|>


<commit_before>/* +---------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)               |
   |                          http://www.mrpt.org/                             |
   |                                                                           |
   | Copyright (c) 2005-2016, Individual contributors, see AUTHORS file        |
   | See: http://www.mrpt.org/Authors - All rights reserved.                   |
   | Released under BSD License. See details in http://www.mrpt.org/License    |
   +---------------------------------------------------------------------------+ */

#include "base-precomp.h"  // Precompiled headers

#include <mrpt/utils/CFileOutputStream.h>
#include <mrpt/utils/COutputLogger.h>
#include <mrpt/system/threads.h>

#include <vector>
#include <cstdio>

#ifdef _MSC_VER
	#define WIN32_LEAN_AND_MEAN
	#include <windows.h>   // OutputDebugString() for MSVC
#endif

using namespace mrpt;
using namespace mrpt::system;
using namespace mrpt::utils;

using namespace std;

/**
 * Implementation file for the COutputLogger header class
 */

// COutputLogger
// ////////////////////////////////////////////////////////////

// variables shared among the various logger instances
size_t COutputLogger::m_times_for_log_const = 0;
size_t COutputLogger::m_times_for_log_const_thresh = 2; // warn 2 times at most

COutputLogger::COutputLogger(std::string name) {
	this->reset();
	m_name = name;

}
COutputLogger::COutputLogger() {
	this->reset();
}
COutputLogger::~COutputLogger() { }

void COutputLogger::log(const std::string& msg_str) {
	if (m_curr_level < m_min_verbosity_level)
		return;

	// initialize a TMsg object
	TMsg msg(msg_str, *this);
	m_history.push_back(msg);

	if (print_message_automatically) {
		msg.dumpToConsole();

		// automatically set the color back to normal
		mrpt::system::setConsoleColor(CONCOL_NORMAL);
	}

}
void COutputLogger::log(const std::string& msg_str) const {
	warnForLogConstMethod();

	TMsg msg(msg_str, *this);
	msg.dumpToConsole();
}

void COutputLogger::log(const std::string& msg_str, const VerbosityLevel& level) {
	// temporarily override the logging level
	VerbosityLevel global_level = m_curr_level;
	m_curr_level = level;

	this->log(msg_str);

	m_curr_level = global_level;
}
void COutputLogger::log(const std::string& msg_str, const VerbosityLevel& level) const {
	warnForLogConstMethod();

	TMsg msg(msg_str, *this);
	msg.level = level;
	msg.dumpToConsole();
}

void COutputLogger::logFmt(const char* fmt, ...) {
	// see MRPT/libs/base/src/utils/CDeugOutputCapable.cpp for the iniitial
	// implementtion

	// check for NULL pointer
	if (!fmt) return;

	// initialize the va_list and let generateStringFromFormat do the work
	// http://c-faq.com/varargs/handoff.html
	va_list argp;
	va_start(argp, fmt);
	std::string str = this->generateStringFromFormat(fmt, argp);
	va_end(argp);

	this->log(str);
}

void COutputLogger::logFmt(const char* fmt, ...) const {
	if (!fmt) return;

	va_list argp;
	va_start(argp, fmt);
	std::string str = this->generateStringFromFormat(fmt, argp);
	va_end(argp);

	this->log(str);
}

std::string COutputLogger::generateStringFromFormat(const char* fmt, va_list argp) const{
	int   result = -1, length = 1024;
	std::vector<char> buffer;
	// make sure that the buffer is large enough to handle the string
	while (result == -1)
	{
		buffer.resize(length + 10);
		result = os::vsnprintf(&buffer[0], length, fmt, argp);

		// http://www.cplusplus.com/reference/cstdio/vsnprintf/
		// only when this returned value is non-negative and less than n, the
		// string has been completely written
		if (result>=length) result=-1;
		length*=2;
	}

	// return result to the caller
	return std::string(&buffer[0]);

}
void COutputLogger::logCond(const std::string& msg_str,
		bool cond) {
	if (cond) {
		this->log(msg_str);
	}
}
void COutputLogger::logCond(const std::string& msg_str,
		bool cond) const {
	if (cond) {
		this->log(msg_str);
	}
}
void COutputLogger::logCond(const std::string& msg_str,
		const VerbosityLevel& level,
		bool cond) {
	if (cond) {
		this->log(msg_str, level);
	}
}
void COutputLogger::logCond(const std::string& msg_str,
		const VerbosityLevel& level,
		bool cond) const {
	if (cond) {
		this->log(msg_str, level);
	}
}

void COutputLogger::warnForLogConstMethod() const {
	if (m_times_for_log_const++ >= m_times_for_log_const_thresh) {
		return;
	}

	// warn the user of the method behavior
	std::string msg_str("Using the log method inside a const function/method. Message will not be recorded in COutputLogger history");
	TMsg warning_msg(msg_str, *this);
	warning_msg.level = LVL_WARN;
	warning_msg.dumpToConsole();
	mrpt::system::sleep(1000);
}

void COutputLogger::setName(const std::string& name) { m_name = name; }

std::string COutputLogger::getName() const { return m_name; }

void COutputLogger::setLoggingLevel(const VerbosityLevel& level /*= LVL_INFO */) {
	m_curr_level = level;

	if (m_curr_level < m_min_verbosity_level) {
		mrpt::system::setConsoleColor(CONCOL_RED);
		std::cout << format("Current VerbosityLevel provided %d is smaller than the minimum allowed verbosity level %d. Logger will ignore all logging directives\n", m_curr_level, m_min_verbosity_level);
		mrpt::system::setConsoleColor(CONCOL_NORMAL);
		mrpt::system::sleep(1000);
	}
}

void COutputLogger::setMinLoggingLevel(const VerbosityLevel& level /*= LVL_INFO */) {
	m_min_verbosity_level = level;

	if (m_curr_level < m_min_verbosity_level) {
		mrpt::system::setConsoleColor(CONCOL_RED);
		std::cout << format("Current VerbosityLevel %d is smaller than the minimum allowed verbosity level %d.\nLogger will ignore all logging directives using this VerbosityLevel\n", m_curr_level, m_min_verbosity_level);
		mrpt::system::setConsoleColor(CONCOL_NORMAL);
		mrpt::system::sleep(1000);
	}
}

VerbosityLevel COutputLogger::getLoggingLevel() const { return m_curr_level; }

void COutputLogger::getAsString(std::string* fname) const {
	for (std::vector<TMsg>::const_iterator hist_it = m_history.begin();
			hist_it != m_history.end(); ++hist_it) {
		*fname += hist_it->getAsString() + "\n";
	}
}
std::string COutputLogger::getAsString() const{
	std::string str;
	this->getAsString(&str);

	return str;
}
void COutputLogger::writeToFile(const std::string* fname_in /* = NULL */) const {
	// determine the filename - open it
	std::string fname;
	if (fname_in) {
		fname = *fname_in;
	}
	else {
		fname = m_name + ".log";
	}
	CFileOutputStream fstream(fname);
	ASSERTMSG_(fstream.fileOpenCorrectly(),
			mrpt::format("\n[%s:] Could not open external file: %s",
				m_name.c_str(), fname.c_str()) );

	std::string hist_str;
	this->getAsString(&hist_str);
	fstream.printf("%s\n", hist_str.c_str());

	fstream.close();
}

void COutputLogger::dumpToConsole() const{
	for (std::vector<TMsg>::const_iterator hist_it = m_history.begin();
			hist_it != m_history.end(); ++hist_it) {
		hist_it->dumpToConsole();
	}
}

std::string COutputLogger::getLastMsg() const {
	TMsg last_msg = m_history.back();
	return last_msg.getAsString();
}

void COutputLogger::getLastMsg(std::string* msg_str) const {
	*msg_str = this->getLastMsg();
}

void COutputLogger::reset() {
	m_name = "Logger"; // just the default name

	m_history.clear();
	m_curr_level = LVL_INFO;

	print_message_automatically = true;

	// set the minimum logging level allowed for printing. By default its
	// LVL_DEBUG
	m_min_verbosity_level = LVL_DEBUG;

}

// TMsg Struct
// ////////////////////////////////////////////////////////////

COutputLogger::TMsg::TMsg(const std::string& msg_str, const COutputLogger& logger) {
	this->reset();

	name = logger.getName();
	level = logger.getLoggingLevel();
	timestamp = mrpt::system::getCurrentTime(); // fill with the current time
	body = msg_str;
}
COutputLogger::TMsg::~TMsg() { }

mrpt::system::TConsoleColor COutputLogger::TMsg::getConsoleColor(VerbosityLevel level) const {
	TConsoleColor color;

  map<VerbosityLevel, mrpt::system::TConsoleColor>::const_iterator search;
  search = m_levels_to_colors.find(level);

	// if found return the corresponding color, otherwise return normal...
  if (search != m_levels_to_colors.end()) {
  	color = search->second;
  }
  else {
  	color = CONCOL_NORMAL;
  }

	return color;
}

std::string COutputLogger::TMsg::getLoggingLevelName(VerbosityLevel level) const {
	std::string name;

  map<VerbosityLevel, std::string>::const_iterator search;
  search = m_levels_to_names.find(level);

	// if found return the corresponding color, otherwise return normal...
  if (search != m_levels_to_names.end()) {
  	name = search->second;
  }
  else {
  	name = "UNKNOWN_LVL";
  }

	return name;
}

void COutputLogger::TMsg::reset() {
	timestamp = INVALID_TIMESTAMP;
	level = LVL_INFO;
	name = "Message"; // default name
	body.clear();

	m_levels_to_colors.clear();
	m_levels_to_colors[LVL_DEBUG] = CONCOL_BLUE;
	m_levels_to_colors[LVL_INFO]  = CONCOL_NORMAL;
	m_levels_to_colors[LVL_WARN]  = CONCOL_GREEN;
	m_levels_to_colors[LVL_ERROR] = CONCOL_RED;

	m_levels_to_names.clear();
 	m_levels_to_names[LVL_DEBUG] = "DEBUG";
	m_levels_to_names[LVL_INFO]  = "INFO ";
	m_levels_to_names[LVL_WARN]  = "WARN ";
	m_levels_to_names[LVL_ERROR] = "ERROR";
}

std::string COutputLogger::TMsg::getAsString() const {
	stringstream out;
	out.str("");
	out << "[" << name <<  " | " << this->getLoggingLevelName(level) << " | "
		<< mrpt::system::timeToString(timestamp)
		<< "] " << body;

	return out.str();
}
void COutputLogger::TMsg::getAsString(std::string* contents) const {
	*contents = this->getAsString();
}
void COutputLogger::TMsg::writeToStream(mrpt::utils::CStream& out) const {
	const std::string str = getAsString();
	out.printf("%s\n", str.c_str());
#ifdef _MSC_VER
	OutputDebugStringA(str.c_str());
#endif
}
void COutputLogger::TMsg::dumpToConsole() const {
	const std::string str = getAsString();

	const bool dump_to_cerr = false; // TO-DO: Allow LVL_ERROR to be alternatively dumped to stderr instead of stdout??

	mrpt::system::setConsoleColor(this->getConsoleColor(level), dump_to_cerr);
	::fputs(str.c_str(), dump_to_cerr ? stderr:stdout );
#ifdef _MSC_VER
	OutputDebugStringA(str.c_str());
#endif
}

<commit_msg>Newline in TMsg::dumpToConsole method<commit_after>/* +---------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)               |
   |                          http://www.mrpt.org/                             |
   |                                                                           |
   | Copyright (c) 2005-2016, Individual contributors, see AUTHORS file        |
   | See: http://www.mrpt.org/Authors - All rights reserved.                   |
   | Released under BSD License. See details in http://www.mrpt.org/License    |
   +---------------------------------------------------------------------------+ */

#include "base-precomp.h"  // Precompiled headers

#include <mrpt/utils/CFileOutputStream.h>
#include <mrpt/utils/COutputLogger.h>
#include <mrpt/system/threads.h>

#include <vector>
#include <cstdio>

#ifdef _MSC_VER
	#define WIN32_LEAN_AND_MEAN
	#include <windows.h>   // OutputDebugString() for MSVC
#endif

using namespace mrpt;
using namespace mrpt::system;
using namespace mrpt::utils;

using namespace std;

/**
 * Implementation file for the COutputLogger header class
 */

// COutputLogger
// ////////////////////////////////////////////////////////////

// variables shared among the various logger instances
size_t COutputLogger::m_times_for_log_const = 0;
size_t COutputLogger::m_times_for_log_const_thresh = 2; // warn 2 times at most

COutputLogger::COutputLogger(std::string name) {
	this->reset();
	m_name = name;

}
COutputLogger::COutputLogger() {
	this->reset();
}
COutputLogger::~COutputLogger() { }

void COutputLogger::log(const std::string& msg_str) {
	if (m_curr_level < m_min_verbosity_level)
		return;

	// initialize a TMsg object
	TMsg msg(msg_str, *this);
	m_history.push_back(msg);

	if (print_message_automatically) {
		msg.dumpToConsole();

		// automatically set the color back to normal
		mrpt::system::setConsoleColor(CONCOL_NORMAL);
	}

}
void COutputLogger::log(const std::string& msg_str) const {
	warnForLogConstMethod();

	TMsg msg(msg_str, *this);
	msg.dumpToConsole();
}

void COutputLogger::log(const std::string& msg_str, const VerbosityLevel& level) {
	// temporarily override the logging level
	VerbosityLevel global_level = m_curr_level;
	m_curr_level = level;

	this->log(msg_str);

	m_curr_level = global_level;
}
void COutputLogger::log(const std::string& msg_str, const VerbosityLevel& level) const {
	warnForLogConstMethod();

	TMsg msg(msg_str, *this);
	msg.level = level;
	msg.dumpToConsole();
}

void COutputLogger::logFmt(const char* fmt, ...) {
	// see MRPT/libs/base/src/utils/CDeugOutputCapable.cpp for the iniitial
	// implementtion

	// check for NULL pointer
	if (!fmt) return;

	// initialize the va_list and let generateStringFromFormat do the work
	// http://c-faq.com/varargs/handoff.html
	va_list argp;
	va_start(argp, fmt);
	std::string str = this->generateStringFromFormat(fmt, argp);
	va_end(argp);

	this->log(str);
}

void COutputLogger::logFmt(const char* fmt, ...) const {
	if (!fmt) return;

	va_list argp;
	va_start(argp, fmt);
	std::string str = this->generateStringFromFormat(fmt, argp);
	va_end(argp);

	this->log(str);
}

std::string COutputLogger::generateStringFromFormat(const char* fmt, va_list argp) const{
	int   result = -1, length = 1024;
	std::vector<char> buffer;
	// make sure that the buffer is large enough to handle the string
	while (result == -1)
	{
		buffer.resize(length + 10);
		result = os::vsnprintf(&buffer[0], length, fmt, argp);

		// http://www.cplusplus.com/reference/cstdio/vsnprintf/
		// only when this returned value is non-negative and less than n, the
		// string has been completely written
		if (result>=length) result=-1;
		length*=2;
	}

	// return result to the caller
	return std::string(&buffer[0]);

}
void COutputLogger::logCond(const std::string& msg_str,
		bool cond) {
	if (cond) {
		this->log(msg_str);
	}
}
void COutputLogger::logCond(const std::string& msg_str,
		bool cond) const {
	if (cond) {
		this->log(msg_str);
	}
}
void COutputLogger::logCond(const std::string& msg_str,
		const VerbosityLevel& level,
		bool cond) {
	if (cond) {
		this->log(msg_str, level);
	}
}
void COutputLogger::logCond(const std::string& msg_str,
		const VerbosityLevel& level,
		bool cond) const {
	if (cond) {
		this->log(msg_str, level);
	}
}

void COutputLogger::warnForLogConstMethod() const {
	if (m_times_for_log_const++ >= m_times_for_log_const_thresh) {
		return;
	}

	// warn the user of the method behavior
	std::string msg_str("Using the log method inside a const function/method. Message will not be recorded in COutputLogger history");
	TMsg warning_msg(msg_str, *this);
	warning_msg.level = LVL_WARN;
	warning_msg.dumpToConsole();
	mrpt::system::sleep(1000);
}

void COutputLogger::setName(const std::string& name) { m_name = name; }

std::string COutputLogger::getName() const { return m_name; }

void COutputLogger::setLoggingLevel(const VerbosityLevel& level /*= LVL_INFO */) {
	m_curr_level = level;

	if (m_curr_level < m_min_verbosity_level) {
		mrpt::system::setConsoleColor(CONCOL_RED);
		std::cout << format("Current VerbosityLevel provided %d is smaller than the minimum allowed verbosity level %d. Logger will ignore all logging directives\n", m_curr_level, m_min_verbosity_level);
		mrpt::system::setConsoleColor(CONCOL_NORMAL);
		mrpt::system::sleep(1000);
	}
}

void COutputLogger::setMinLoggingLevel(const VerbosityLevel& level /*= LVL_INFO */) {
	m_min_verbosity_level = level;

	if (m_curr_level < m_min_verbosity_level) {
		mrpt::system::setConsoleColor(CONCOL_RED);
		std::cout << format("Current VerbosityLevel %d is smaller than the minimum allowed verbosity level %d.\nLogger will ignore all logging directives using this VerbosityLevel\n", m_curr_level, m_min_verbosity_level);
		mrpt::system::setConsoleColor(CONCOL_NORMAL);
		mrpt::system::sleep(1000);
	}
}

VerbosityLevel COutputLogger::getLoggingLevel() const { return m_curr_level; }

void COutputLogger::getAsString(std::string* fname) const {
	for (std::vector<TMsg>::const_iterator hist_it = m_history.begin();
			hist_it != m_history.end(); ++hist_it) {
		*fname += hist_it->getAsString() + "\n";
	}
}
std::string COutputLogger::getAsString() const{
	std::string str;
	this->getAsString(&str);

	return str;
}
void COutputLogger::writeToFile(const std::string* fname_in /* = NULL */) const {
	// determine the filename - open it
	std::string fname;
	if (fname_in) {
		fname = *fname_in;
	}
	else {
		fname = m_name + ".log";
	}
	CFileOutputStream fstream(fname);
	ASSERTMSG_(fstream.fileOpenCorrectly(),
			mrpt::format("\n[%s:] Could not open external file: %s",
				m_name.c_str(), fname.c_str()) );

	std::string hist_str;
	this->getAsString(&hist_str);
	fstream.printf("%s\n", hist_str.c_str());

	fstream.close();
}

void COutputLogger::dumpToConsole() const{
	for (std::vector<TMsg>::const_iterator hist_it = m_history.begin();
			hist_it != m_history.end(); ++hist_it) {
		hist_it->dumpToConsole();
	}
}

std::string COutputLogger::getLastMsg() const {
	TMsg last_msg = m_history.back();
	return last_msg.getAsString();
}

void COutputLogger::getLastMsg(std::string* msg_str) const {
	*msg_str = this->getLastMsg();
}

void COutputLogger::reset() {
	m_name = "Logger"; // just the default name

	m_history.clear();
	m_curr_level = LVL_INFO;

	print_message_automatically = true;

	// set the minimum logging level allowed for printing. By default its
	// LVL_DEBUG
	m_min_verbosity_level = LVL_DEBUG;

}

// TMsg Struct
// ////////////////////////////////////////////////////////////

COutputLogger::TMsg::TMsg(const std::string& msg_str, const COutputLogger& logger) {
	this->reset();

	name = logger.getName();
	level = logger.getLoggingLevel();
	timestamp = mrpt::system::getCurrentTime(); // fill with the current time
	body = msg_str;
}
COutputLogger::TMsg::~TMsg() { }

mrpt::system::TConsoleColor COutputLogger::TMsg::getConsoleColor(VerbosityLevel level) const {
	TConsoleColor color;

  map<VerbosityLevel, mrpt::system::TConsoleColor>::const_iterator search;
  search = m_levels_to_colors.find(level);

	// if found return the corresponding color, otherwise return normal...
  if (search != m_levels_to_colors.end()) {
  	color = search->second;
  }
  else {
  	color = CONCOL_NORMAL;
  }

	return color;
}

std::string COutputLogger::TMsg::getLoggingLevelName(VerbosityLevel level) const {
	std::string name;

  map<VerbosityLevel, std::string>::const_iterator search;
  search = m_levels_to_names.find(level);

	// if found return the corresponding color, otherwise return normal...
  if (search != m_levels_to_names.end()) {
  	name = search->second;
  }
  else {
  	name = "UNKNOWN_LVL";
  }

	return name;
}

void COutputLogger::TMsg::reset() {
	timestamp = INVALID_TIMESTAMP;
	level = LVL_INFO;
	name = "Message"; // default name
	body.clear();

	m_levels_to_colors.clear();
	m_levels_to_colors[LVL_DEBUG] = CONCOL_BLUE;
	m_levels_to_colors[LVL_INFO]  = CONCOL_NORMAL;
	m_levels_to_colors[LVL_WARN]  = CONCOL_GREEN;
	m_levels_to_colors[LVL_ERROR] = CONCOL_RED;

	m_levels_to_names.clear();
 	m_levels_to_names[LVL_DEBUG] = "DEBUG";
	m_levels_to_names[LVL_INFO]  = "INFO ";
	m_levels_to_names[LVL_WARN]  = "WARN ";
	m_levels_to_names[LVL_ERROR] = "ERROR";
}

std::string COutputLogger::TMsg::getAsString() const {
	stringstream out;
	out.str("");
	out << "[" << name <<  " | " << this->getLoggingLevelName(level) << " | "
		<< mrpt::system::timeToString(timestamp)
		<< "] " << body;

	return out.str();
}
void COutputLogger::TMsg::getAsString(std::string* contents) const {
	*contents = this->getAsString();
}
void COutputLogger::TMsg::writeToStream(mrpt::utils::CStream& out) const {
	const std::string str = getAsString();
	out.printf("%s\n", str.c_str());
#ifdef _MSC_VER
	OutputDebugStringA(str.c_str());
#endif
}
void COutputLogger::TMsg::dumpToConsole() const {
	const std::string str = getAsString() + "\n";

	const bool dump_to_cerr = false; // TO-DO: Allow LVL_ERROR to be alternatively dumped to stderr instead of stdout??

	mrpt::system::setConsoleColor(this->getConsoleColor(level), dump_to_cerr);
	::fputs(str.c_str(), dump_to_cerr ? stderr:stdout );
#ifdef _MSC_VER
	OutputDebugStringA(str.c_str());
#endif
}

<|endoftext|>


<commit_before>// MIT License
//
// Copyright (c) 2017-2018 Artur Wyszyński, aljen at hitomi dot pl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include "spaghetti/socket_item.h"

#include <QApplication>
#include <QCursor>
#include <QDebug>
#include <QDrag>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsWidget>
#include <QMimeData>
#include <QWidget>

#include "spaghetti/node.h"
#include "spaghetti/package.h"
#include "ui/colors.h"
#include "ui/link_item.h"
#include "ui/package_view.h"

namespace spaghetti {

SocketItem::SocketItem(Node *const a_node, Type const a_type)
  : QGraphicsItem{ a_node }
  , m_node{ a_node }
  , m_type{ a_type }
{
  m_font.setFamily("Consolas");
  m_font.setPointSize(10);

  setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsFocusable |
           QGraphicsItem::ItemSendsScenePositionChanges);
  setAcceptHoverEvents(true);
  setAcceptedMouseButtons(Qt::LeftButton);

  setZValue(1);

  if (a_type == Type::eOutput)
    setCursor(Qt::OpenHandCursor);
  else
    setAcceptDrops(true);
}

QRectF SocketItem::boundingRect() const
{
  return QRectF{ -(static_cast<qreal>(SIZE) / 2.), -(static_cast<qreal>(SIZE) / 2.), static_cast<qreal>(SIZE),
                 static_cast<qreal>(SIZE) };
}

void SocketItem::paint(QPainter *a_painter, QStyleOptionGraphicsItem const *a_option, QWidget *a_widget)
{
  Q_UNUSED(a_option);
  Q_UNUSED(a_widget);

  QRectF const rect{ boundingRect() };

  QPen pen{ get_color(Color::eSocketBorder) };
  pen.setWidth(2);

  QBrush brush{};
  if (m_isHover)
    brush.setColor(get_color(Color::eSocketHover));
  else if (m_isDrop)
    brush.setColor(get_color(Color::eSocketDrop));
  else if (m_isSignalOn)
    brush.setColor(m_colorSignalOn);
  else if (!m_isSignalOn)
    brush.setColor(m_colorSignalOff);
  //  else if (m_type == Type::eInput)
  //    brush.setColor(config.getColor(Config::Color::eSocketInput));
  //  else if (m_type == Type::eOutput)
  //    brush.setColor(config.getColor(Config::Color::eSocketOutput));
  brush.setStyle(Qt::SolidPattern);

  a_painter->setPen(pen);
  a_painter->setBrush(brush);
  if (m_type == Type::eOutput)
    a_painter->drawEllipse(rect);
  else
    a_painter->drawRect(rect);

  if (m_used) {
    a_painter->save();
    a_painter->setPen(Qt::NoPen);
    a_painter->setBrush(pen.color());
    if (m_type == Type::eOutput)
      a_painter->drawEllipse(rect.adjusted(4, 4, -4, -4));
    else
      a_painter->drawRect(rect.adjusted(4, 4, -4, -4));
    a_painter->restore();
  }

  if (!m_nameHidden) {
    pen.setColor(get_color(Color::eFontName));
    a_painter->setPen(pen);
    a_painter->setFont(m_font);

    QFontMetrics const metrics{ m_font };
    int const FONT_HEIGHT = metrics.height();

    if (m_type == Type::eInput)
      a_painter->drawText(static_cast<int>(rect.width()) - 4, (FONT_HEIGHT / 2) - metrics.strikeOutPos(), m_name);
    else
      a_painter->drawText(-metrics.width(m_name) - SIZE + SIZE / 3, (FONT_HEIGHT / 2) - metrics.strikeOutPos(), m_name);
  }
}

void SocketItem::hoverEnterEvent(QGraphicsSceneHoverEvent *a_event)
{
  Q_UNUSED(a_event);

  m_isHover = true;

  for (auto const link : m_links) link->setHover(m_isHover);
}

void SocketItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *a_event)
{
  Q_UNUSED(a_event);

  m_isHover = false;

  for (auto const link : m_links) link->setHover(m_isHover);
}

void SocketItem::dragEnterEvent(QGraphicsSceneDragDropEvent *a_event)
{
  Q_UNUSED(a_event);

  if (m_used) {
    a_event->ignore();
    return;
  }

  auto const view = reinterpret_cast<PackageView *>(scene()->views()[0]);

  LinkItem *const linkItem{ view->dragLink() };
  if (!linkItem || m_valueType != linkItem->valueType()) {
    a_event->ignore();
    return;
  }

  linkItem->setTo(this);
  m_isDrop = true;
}

void SocketItem::dragLeaveEvent(QGraphicsSceneDragDropEvent *a_event)
{
  Q_UNUSED(a_event);

  m_isDrop = false;

  auto const view = reinterpret_cast<PackageView *>(scene()->views()[0]);

  LinkItem *const linkItem{ view->dragLink() };
  if (!linkItem) return;
  linkItem->setTo(nullptr);
}

void SocketItem::dragMoveEvent(QGraphicsSceneDragDropEvent *a_event)
{
  Q_UNUSED(a_event);
}

void SocketItem::dropEvent(QGraphicsSceneDragDropEvent *a_event)
{
  Q_UNUSED(a_event);

  auto const packageView = reinterpret_cast<PackageView *>(scene()->views()[0]);

  LinkItem *const linkItem{ packageView->dragLink() };
  if (!linkItem) return;

  m_links.push_back(linkItem);
  linkItem->setTo(this);
  m_used = true;
  m_isDrop = false;

  packageView->acceptDragLink();

  auto const package = packageView->package();
  auto const from = linkItem->from();

  package->connect(from->elementId(), from->socketId(), m_elementId, m_socketId);

  setSignal(linkItem->isSignalOn());
}

void SocketItem::mousePressEvent(QGraphicsSceneMouseEvent *a_event)
{
  Q_UNUSED(a_event);

  if (m_type == Type::eInput) return;

  setCursor(Qt::ClosedHandCursor);
}

void SocketItem::mouseMoveEvent(QGraphicsSceneMouseEvent *a_event)
{
  Q_UNUSED(a_event);

  if (m_type == Type::eInput) return;

  if (QLineF(a_event->screenPos(), a_event->buttonDownScreenPos(Qt::LeftButton)).length() <
      QApplication::startDragDistance())
    return;

  QDrag *const drag = new QDrag(a_event->widget());

  QMimeData *const mime = new QMimeData;
  drag->setMimeData(mime);

  LinkItem *linkItem{ new LinkItem };
  linkItem->setColors(m_colorSignalOff, m_colorSignalOn);
  linkItem->setValueType(m_valueType);
  linkItem->setFrom(this);
  linkItem->setSignal(m_isSignalOn);

  scene()->addItem(linkItem);
  m_links.push_back(linkItem);

  auto const view = reinterpret_cast<PackageView *>(scene()->views()[0]);
  view->setDragLink(linkItem);

  Qt::DropAction const action{ drag->exec() };
  if (action == Qt::IgnoreAction) {
    removeLink(linkItem);
    scene()->removeItem(linkItem);
    view->cancelDragLink();
  } else
    m_used = true;

  setCursor(Qt::OpenHandCursor);
}

void SocketItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *a_event)
{
  Q_UNUSED(a_event);

  if (m_type == Type::eInput) return;

  setCursor(Qt::OpenHandCursor);
}

QVariant SocketItem::itemChange(QGraphicsItem::GraphicsItemChange a_change, QVariant const &a_value)
{
  if (a_change == QGraphicsItem::ItemScenePositionHasChanged) {
    for (LinkItem *const link : m_links) link->trackNodes();
  }
  return QGraphicsItem::itemChange(a_change, a_value);
}

void SocketItem::setName(QString const &a_name)
{
  m_name = a_name;
}

int SocketItem::nameWidth() const
{
  QFontMetrics const metrics{ m_font };
  return metrics.width(m_name);
}

void SocketItem::setColors(QColor const a_signalOff, QColor const a_signalOn)
{
  m_colorSignalOff = a_signalOff;
  m_colorSignalOn = a_signalOn;
}

void SocketItem::setSignal(bool const a_signal)
{
  m_isSignalOn = a_signal;

  if (m_type == Type::eOutput)
    for (LinkItem *const link : m_links) link->setSignal(a_signal);
}

void SocketItem::connect(SocketItem *const a_other)
{
  auto const linkItem = new LinkItem;
  linkItem->setColors(m_colorSignalOff, m_colorSignalOn);
  linkItem->setValueType(m_valueType);
  linkItem->setFrom(this);
  linkItem->setTo(a_other);

  m_links.push_back(linkItem);
  m_used = true;

  a_other->m_links.push_back(linkItem);
  a_other->m_used = true;
  a_other->m_isDrop = false;

  scene()->addItem(linkItem);
}

void SocketItem::disconnect(SocketItem *const a_other)
{
  auto const link = linkBetween(this, a_other);
  if (!link) return;

  auto const FROM_ID = elementId();
  auto const FROM_SOCKET_ID = socketId();
  auto const TO_ID = a_other->elementId();
  auto const TO_SOCKET_ID = a_other->socketId();

  auto const packageView = m_node->packageView();
  auto const package = packageView->package();

  package->disconnect(FROM_ID, FROM_SOCKET_ID, TO_ID, TO_SOCKET_ID);

  removeLink(link);
  a_other->removeLink(link);

  if (m_links.empty()) m_used = false;
  m_isHover = false;
  a_other->m_used = false;
  a_other->m_isHover = false;

  delete link;
}

void SocketItem::disconnectAll()
{
  if (m_type == Type::eInput)
    disconnectAllInputs();
  else
    disconnectAllOutputs();
}

void SocketItem::disconnectAllInputs()
{
  auto links = m_links;
  for (auto &link : links) {
    auto const from = link->from();
    from->disconnect(this);
  }
}

void SocketItem::disconnectAllOutputs()
{
  auto links = m_links;
  for (auto &link : links) disconnect(link->to());
}

void SocketItem::setValueType(ValueType const a_type)
{
  m_valueType = a_type;

  switch (m_valueType) {
    case ValueType::eBool: setColors(get_color(Color::eBoolSignalOff), get_color(Color::eBoolSignalOn)); break;
    case ValueType::eFloat: setColors(get_color(Color::eFloatSignalOn), get_color(Color::eFloatSignalOff)); break;
    case ValueType::eInt: setColors(get_color(Color::eIntegerSignalOn), get_color(Color::eIntegerSignalOn)); break;
  }
}

void SocketItem::removeLink(LinkItem *const a_linkItem)
{
  auto const it = std::remove(std::begin(m_links), std::end(m_links), a_linkItem);
  m_links.erase(it, std::end(m_links));
}

LinkItem *SocketItem::linkBetween(SocketItem *const a_from, SocketItem *const a_to) const
{
  auto const it = std::find_if(std::begin(m_links), std::end(m_links), [a_from, a_to](LinkItem *const a_link) {
    return a_link->from() == a_from && a_link->to() == a_to;
  });

  if (it == std::end(m_links)) return nullptr;

  return *it;
}

} // namespace spaghetti
<commit_msg>Use more auto<commit_after>// MIT License
//
// Copyright (c) 2017-2018 Artur Wyszyński, aljen at hitomi dot pl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include "spaghetti/socket_item.h"

#include <QApplication>
#include <QCursor>
#include <QDebug>
#include <QDrag>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsWidget>
#include <QMimeData>
#include <QWidget>

#include "spaghetti/node.h"
#include "spaghetti/package.h"
#include "ui/colors.h"
#include "ui/link_item.h"
#include "ui/package_view.h"

namespace spaghetti {

SocketItem::SocketItem(Node *const a_node, Type const a_type)
  : QGraphicsItem{ a_node }
  , m_node{ a_node }
  , m_type{ a_type }
{
  m_font.setFamily("Consolas");
  m_font.setPointSize(10);

  setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsFocusable |
           QGraphicsItem::ItemSendsScenePositionChanges);
  setAcceptHoverEvents(true);
  setAcceptedMouseButtons(Qt::LeftButton);

  setZValue(1);

  if (a_type == Type::eOutput)
    setCursor(Qt::OpenHandCursor);
  else
    setAcceptDrops(true);
}

QRectF SocketItem::boundingRect() const
{
  return QRectF{ -(static_cast<qreal>(SIZE) / 2.), -(static_cast<qreal>(SIZE) / 2.), static_cast<qreal>(SIZE),
                 static_cast<qreal>(SIZE) };
}

void SocketItem::paint(QPainter *a_painter, QStyleOptionGraphicsItem const *a_option, QWidget *a_widget)
{
  Q_UNUSED(a_option);
  Q_UNUSED(a_widget);

  QRectF const rect{ boundingRect() };

  QPen pen{ get_color(Color::eSocketBorder) };
  pen.setWidth(2);

  QBrush brush{};
  if (m_isHover)
    brush.setColor(get_color(Color::eSocketHover));
  else if (m_isDrop)
    brush.setColor(get_color(Color::eSocketDrop));
  else if (m_isSignalOn)
    brush.setColor(m_colorSignalOn);
  else if (!m_isSignalOn)
    brush.setColor(m_colorSignalOff);
  //  else if (m_type == Type::eInput)
  //    brush.setColor(config.getColor(Config::Color::eSocketInput));
  //  else if (m_type == Type::eOutput)
  //    brush.setColor(config.getColor(Config::Color::eSocketOutput));
  brush.setStyle(Qt::SolidPattern);

  a_painter->setPen(pen);
  a_painter->setBrush(brush);
  if (m_type == Type::eOutput)
    a_painter->drawEllipse(rect);
  else
    a_painter->drawRect(rect);

  if (m_used) {
    a_painter->save();
    a_painter->setPen(Qt::NoPen);
    a_painter->setBrush(pen.color());
    if (m_type == Type::eOutput)
      a_painter->drawEllipse(rect.adjusted(4, 4, -4, -4));
    else
      a_painter->drawRect(rect.adjusted(4, 4, -4, -4));
    a_painter->restore();
  }

  if (!m_nameHidden) {
    pen.setColor(get_color(Color::eFontName));
    a_painter->setPen(pen);
    a_painter->setFont(m_font);

    QFontMetrics const metrics{ m_font };
    int const FONT_HEIGHT = metrics.height();

    if (m_type == Type::eInput)
      a_painter->drawText(static_cast<int>(rect.width()) - 4, (FONT_HEIGHT / 2) - metrics.strikeOutPos(), m_name);
    else
      a_painter->drawText(-metrics.width(m_name) - SIZE + SIZE / 3, (FONT_HEIGHT / 2) - metrics.strikeOutPos(), m_name);
  }
}

void SocketItem::hoverEnterEvent(QGraphicsSceneHoverEvent *a_event)
{
  Q_UNUSED(a_event);

  m_isHover = true;

  for (auto const link : m_links) link->setHover(m_isHover);
}

void SocketItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *a_event)
{
  Q_UNUSED(a_event);

  m_isHover = false;

  for (auto const link : m_links) link->setHover(m_isHover);
}

void SocketItem::dragEnterEvent(QGraphicsSceneDragDropEvent *a_event)
{
  Q_UNUSED(a_event);

  if (m_used) {
    a_event->ignore();
    return;
  }

  auto const view = reinterpret_cast<PackageView *>(scene()->views()[0]);

  LinkItem *const linkItem{ view->dragLink() };
  if (!linkItem || m_valueType != linkItem->valueType()) {
    a_event->ignore();
    return;
  }

  linkItem->setTo(this);
  m_isDrop = true;
}

void SocketItem::dragLeaveEvent(QGraphicsSceneDragDropEvent *a_event)
{
  Q_UNUSED(a_event);

  m_isDrop = false;

  auto const view = reinterpret_cast<PackageView *>(scene()->views()[0]);

  auto const linkItem = view->dragLink();
  if (!linkItem) return;
  linkItem->setTo(nullptr);
}

void SocketItem::dragMoveEvent(QGraphicsSceneDragDropEvent *a_event)
{
  Q_UNUSED(a_event);
}

void SocketItem::dropEvent(QGraphicsSceneDragDropEvent *a_event)
{
  Q_UNUSED(a_event);

  auto const packageView = reinterpret_cast<PackageView *>(scene()->views()[0]);

  auto const linkItem = packageView->dragLink();
  if (!linkItem) return;

  m_links.push_back(linkItem);
  linkItem->setTo(this);
  m_used = true;
  m_isDrop = false;

  packageView->acceptDragLink();

  auto const package = packageView->package();
  auto const from = linkItem->from();

  package->connect(from->elementId(), from->socketId(), m_elementId, m_socketId);

  setSignal(linkItem->isSignalOn());
}

void SocketItem::mousePressEvent(QGraphicsSceneMouseEvent *a_event)
{
  Q_UNUSED(a_event);

  if (m_type == Type::eInput) return;

  setCursor(Qt::ClosedHandCursor);
}

void SocketItem::mouseMoveEvent(QGraphicsSceneMouseEvent *a_event)
{
  Q_UNUSED(a_event);

  if (m_type == Type::eInput) return;

  if (QLineF(a_event->screenPos(), a_event->buttonDownScreenPos(Qt::LeftButton)).length() <
      QApplication::startDragDistance())
    return;

  auto const mime = new QMimeData;
  auto const drag = new QDrag(a_event->widget());
  drag->setMimeData(mime);

  auto linkItem = new LinkItem;
  linkItem->setColors(m_colorSignalOff, m_colorSignalOn);
  linkItem->setValueType(m_valueType);
  linkItem->setFrom(this);
  linkItem->setSignal(m_isSignalOn);

  scene()->addItem(linkItem);
  m_links.push_back(linkItem);

  auto const view = reinterpret_cast<PackageView *>(scene()->views()[0]);
  view->setDragLink(linkItem);

  Qt::DropAction const action{ drag->exec() };
  if (action == Qt::IgnoreAction) {
    removeLink(linkItem);
    scene()->removeItem(linkItem);
    view->cancelDragLink();
  } else
    m_used = true;

  setCursor(Qt::OpenHandCursor);
}

void SocketItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *a_event)
{
  Q_UNUSED(a_event);

  if (m_type == Type::eInput) return;

  setCursor(Qt::OpenHandCursor);
}

QVariant SocketItem::itemChange(QGraphicsItem::GraphicsItemChange a_change, QVariant const &a_value)
{
  if (a_change == QGraphicsItem::ItemScenePositionHasChanged) {
    for (LinkItem *const link : m_links) link->trackNodes();
  }
  return QGraphicsItem::itemChange(a_change, a_value);
}

void SocketItem::setName(QString const &a_name)
{
  m_name = a_name;
}

int SocketItem::nameWidth() const
{
  QFontMetrics const metrics{ m_font };
  return metrics.width(m_name);
}

void SocketItem::setColors(QColor const a_signalOff, QColor const a_signalOn)
{
  m_colorSignalOff = a_signalOff;
  m_colorSignalOn = a_signalOn;
}

void SocketItem::setSignal(bool const a_signal)
{
  m_isSignalOn = a_signal;

  if (m_type == Type::eOutput)
    for (LinkItem *const link : m_links) link->setSignal(a_signal);
}

void SocketItem::connect(SocketItem *const a_other)
{
  auto const linkItem = new LinkItem;
  linkItem->setColors(m_colorSignalOff, m_colorSignalOn);
  linkItem->setValueType(m_valueType);
  linkItem->setFrom(this);
  linkItem->setTo(a_other);

  m_links.push_back(linkItem);
  m_used = true;

  a_other->m_links.push_back(linkItem);
  a_other->m_used = true;
  a_other->m_isDrop = false;

  scene()->addItem(linkItem);
}

void SocketItem::disconnect(SocketItem *const a_other)
{
  auto const link = linkBetween(this, a_other);
  if (!link) return;

  auto const FROM_ID = elementId();
  auto const FROM_SOCKET_ID = socketId();
  auto const TO_ID = a_other->elementId();
  auto const TO_SOCKET_ID = a_other->socketId();

  auto const packageView = m_node->packageView();
  auto const package = packageView->package();

  package->disconnect(FROM_ID, FROM_SOCKET_ID, TO_ID, TO_SOCKET_ID);

  removeLink(link);
  a_other->removeLink(link);

  if (m_links.empty()) m_used = false;
  m_isHover = false;
  a_other->m_used = false;
  a_other->m_isHover = false;

  delete link;
}

void SocketItem::disconnectAll()
{
  if (m_type == Type::eInput)
    disconnectAllInputs();
  else
    disconnectAllOutputs();
}

void SocketItem::disconnectAllInputs()
{
  auto links = m_links;
  for (auto &link : links) {
    auto const from = link->from();
    from->disconnect(this);
  }
}

void SocketItem::disconnectAllOutputs()
{
  auto links = m_links;
  for (auto &link : links) disconnect(link->to());
}

void SocketItem::setValueType(ValueType const a_type)
{
  m_valueType = a_type;

  switch (m_valueType) {
    case ValueType::eBool: setColors(get_color(Color::eBoolSignalOff), get_color(Color::eBoolSignalOn)); break;
    case ValueType::eFloat: setColors(get_color(Color::eFloatSignalOn), get_color(Color::eFloatSignalOff)); break;
    case ValueType::eInt: setColors(get_color(Color::eIntegerSignalOn), get_color(Color::eIntegerSignalOn)); break;
  }
}

void SocketItem::removeLink(LinkItem *const a_linkItem)
{
  auto const it = std::remove(std::begin(m_links), std::end(m_links), a_linkItem);
  m_links.erase(it, std::end(m_links));
}

LinkItem *SocketItem::linkBetween(SocketItem *const a_from, SocketItem *const a_to) const
{
  auto const it = std::find_if(std::begin(m_links), std::end(m_links), [a_from, a_to](LinkItem *const a_link) {
    return a_link->from() == a_from && a_link->to() == a_to;
  });

  if (it == std::end(m_links)) return nullptr;

  return *it;
}

} // namespace spaghetti
<|endoftext|>