<commit_before>//
// Log - simple message logging to a file.
//
// Copyright (c) 2002-2003 Virtual Terrain Project
// Free for all uses, see license.txt for details.
//

#include "cpl_error.h"	// From GDAL/include
#include "vtString.h"
#include "vtLog.h"
#include <stdarg.h>
#include <wchar.h>		// for fputws()

#ifdef _MSC_VER
#include <windows.h>	// for OutputDebugString, unfortunately
#endif

vtLog g_Log;

void cpl_error_handler(CPLErr eErrClass, int err_no, const char *msg)
{
	if (eErrClass == CE_Debug)
		g_Log._Log("CPL Debug: ");
	else if (eErrClass == CE_Warning)
		g_Log.Printf("CPL Warning %d: ", err_no);
	else
		g_Log.Printf("CPL Error %d: ", err_no);
	g_Log._Log(msg);
	g_Log._Log("\n");
}

vtLog::vtLog()
{
	m_log = NULL;
}

vtLog::~vtLog()
{
	if (m_log)
	{
		CPLPopErrorHandler();
		fclose(m_log);
		m_log = NULL;
	}
}

void vtLog::_StartLog(const char *fname)
{
	m_log = fopen(fname, "wb");
	CPLPushErrorHandler(cpl_error_handler);
}

void vtLog::_Log(const char *msg)
{
	if (m_log)
	{
		fputs(msg, m_log);
		fflush(m_log);
	}
#ifdef _MSC_VER
	OutputDebugStringA(msg);
#endif
}

void vtLog::_Log(const wchar_t *msg)
{
	if (m_log)
	{
		// it is not so useful to write wide characters to the file, which
		// otherwise contains 8-bit text, so convert back first
//		fputws(msg, m_log);
		wstring2 str = msg;
		fputs(str.eb_str(), m_log);
		fflush(m_log);
	}
#ifdef _MSC_VER
	OutputDebugStringW(msg);
#endif
}

void vtLog::Printf(const char *pFormat, ...)
{
	va_list va;
	va_start(va, pFormat);

	char ach[1024];
	vsprintf(ach, pFormat, va);

	_Log(ach);
}

void vtLog::Printf(const wchar_t *pFormat, ...)
{
	va_list va;
	va_start(va, pFormat);

#if defined (__DARWIN_OSX__) || defined (macintosh)
	// Mac seems to not have all the ANSI functions like vswprintf, so
	//  convert the format string to 8-bit and use vsprintf instead.
	char ach[1024];
	wstring2 strFormat = pFormat;
	vsprintf(ach, strFormat.eb_str(), va);
#else
	// Use wide characters
	wchar_t ach[1024];
#ifdef _MSC_VER
	vswprintf(ach, pFormat, va);
#else
	// apparently on non-MSVC platforms this takes 4 arguments (safer)
	vswprintf(ach, 1024, pFormat, va);
#endif
#endif

	_Log(ach);
}
<commit_msg>disable wchar stuff for __DARWIN_OSX__<commit_after>//
// Log - simple message logging to a file.
//
// Copyright (c) 2002-2003 Virtual Terrain Project
// Free for all uses, see license.txt for details.
//

#include "cpl_error.h"	// From GDAL/include
#include "vtString.h"
#include "vtLog.h"
#include <stdarg.h>

#ifndef __DARWIN_OSX__
#include <wchar.h>		// for fputws()
#endif

#ifdef _MSC_VER
#include <windows.h>	// for OutputDebugString, unfortunately
#endif

vtLog g_Log;

void cpl_error_handler(CPLErr eErrClass, int err_no, const char *msg)
{
	if (eErrClass == CE_Debug)
		g_Log._Log("CPL Debug: ");
	else if (eErrClass == CE_Warning)
		g_Log.Printf("CPL Warning %d: ", err_no);
	else
		g_Log.Printf("CPL Error %d: ", err_no);
	g_Log._Log(msg);
	g_Log._Log("\n");
}

vtLog::vtLog()
{
	m_log = NULL;
}

vtLog::~vtLog()
{
	if (m_log)
	{
		CPLPopErrorHandler();
		fclose(m_log);
		m_log = NULL;
	}
}

void vtLog::_StartLog(const char *fname)
{
	m_log = fopen(fname, "wb");
	CPLPushErrorHandler(cpl_error_handler);
}

void vtLog::_Log(const char *msg)
{
	if (m_log)
	{
		fputs(msg, m_log);
		fflush(m_log);
	}
#ifdef _MSC_VER
	OutputDebugStringA(msg);
#endif
}

void vtLog::_Log(const wchar_t *msg)
{
#ifndef __DARWIN_OSX__
	if (m_log)
	{
		// it is not so useful to write wide characters to the file, which
		// otherwise contains 8-bit text, so convert back first
//		fputws(msg, m_log);
		wstring2 str = msg;
		fputs(str.eb_str(), m_log);
		fflush(m_log);
	}
#endif	// __DARWIN_OSX__

#ifdef _MSC_VER
	OutputDebugStringW(msg);
#endif
}

void vtLog::Printf(const char *pFormat, ...)
{
	va_list va;
	va_start(va, pFormat);

	char ach[1024];
	vsprintf(ach, pFormat, va);

	_Log(ach);
}

void vtLog::Printf(const wchar_t *pFormat, ...)
{
#ifndef __DARWIN_OSX__
	va_list va;
	va_start(va, pFormat);

#if defined (__DARWIN_OSX__) || defined (macintosh)
	// Mac seems to not have all the ANSI functions like vswprintf, so
	//  convert the format string to 8-bit and use vsprintf instead.
	char ach[1024];
	wstring2 strFormat = pFormat;
	vsprintf(ach, strFormat.eb_str(), va);
#else
	// Use wide characters
	wchar_t ach[1024];
#ifdef _MSC_VER
	vswprintf(ach, pFormat, va);
#else
	// apparently on non-MSVC platforms this takes 4 arguments (safer)
	vswprintf(ach, 1024, pFormat, va);
#endif
#endif

	_Log(ach);
#endif	// __DARWIN_OSX__
}
<|endoftext|>


<commit_before>// Copyright 2017 Kyle Mayes
//
// 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.

#ifndef VCE_UTILITY_HPP
#define VCE_UTILITY_HPP

#include <ostream>

#include <vivace/meta.hpp>

namespace vce {

/// An ordering of two values.
enum class Ordering : int {
    Less = -1,
    Greater = 1,
    Equal = 0,
};

std::ostream& operator<<(std::ostream& stream, Ordering ordering);

namespace detail { VCE_HAS_MEMBER_FUNCTION(HasCompare, compare); }

/// Returns the ordering of the two supplied values.
template <class T, class U>
Ordering compare(const T& left, const U& right) {
    if constexpr (detail::HasCompareV<const T&, Ordering(const U&)>) {
        return left.compare(right);
    } else if (left < right) {
        return Ordering::Less;
    } else if (right < left) {
        return Ordering::Greater;
    } else {
        return Ordering::Equal;
    }
}

namespace detail {
    VCE_HAS_FIELD(HasRelocatable, RELOCATABLE);

    template <class T>
    struct IsRelocatable {
        static constexpr bool value = std::is_trivially_move_constructible_v<T>;
    };

    template <class T>
    static constexpr bool IsRelocatableV = IsRelocatable<T>::value;
}

/// Defines the supplied type as being relocatable.
#define VCE_IS_RELOCATABLE(...) \
    namespace vce::detail { \
        template <> \
        struct IsRelocatable<__VA_ARGS__> { \
            static constexpr bool value = true; \
        }; \
    }

/// Defines the supplied template type as being relocatable.
#define VCE_IS_RELOCATABLE_TEMPLATE(...) \
    namespace vce::detail { \
        template <class... N> \
        struct IsRelocatable<__VA_ARGS__<N...>> { \
            static constexpr bool value = true; \
        }; \
    }

/// Returns whether the supplied type may be safely moved to another location in memory.
template <class T>
constexpr bool is_relocatable() {
    if constexpr (detail::HasRelocatableV<T, bool>) {
        return T::RELOCATABLE;
    } else {
        return detail::IsRelocatableV<T>;
    }
}

/// A reference wrapper.
template <class T>
using Ref = std::reference_wrapper<T>;

/// A helper struct for SFINAE.
template <bool ENABLE>
using Sfinae = typename std::enable_if_t<ENABLE, int>;

/// The unit type.
struct Unit { };

bool operator==(Unit, Unit);
bool operator!=(Unit, Unit);
bool operator<(Unit, Unit);
bool operator>(Unit, Unit);
bool operator<=(Unit, Unit);
bool operator>=(Unit, Unit);

std::ostream& operator<<(std::ostream& stream, Unit);

/// The only value of the unit type.
constexpr static Unit UNIT{};

}

#endif
<commit_msg>Add Limits typedef<commit_after>// Copyright 2017 Kyle Mayes
//
// 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.

#ifndef VCE_UTILITY_HPP
#define VCE_UTILITY_HPP

#include <limits>
#include <ostream>

#include <vivace/meta.hpp>

namespace vce {

/// An ordering of two values.
enum class Ordering : int {
    Less = -1,
    Greater = 1,
    Equal = 0,
};

std::ostream& operator<<(std::ostream& stream, Ordering ordering);

namespace detail { VCE_HAS_MEMBER_FUNCTION(HasCompare, compare); }

/// Returns the ordering of the two supplied values.
template <class T, class U>
Ordering compare(const T& left, const U& right) {
    if constexpr (detail::HasCompareV<const T&, Ordering(const U&)>) {
        return left.compare(right);
    } else if (left < right) {
        return Ordering::Less;
    } else if (right < left) {
        return Ordering::Greater;
    } else {
        return Ordering::Equal;
    }
}

/// A collection of numeric limits.
template <class T>
using Limits = std::numeric_limits<T>;

namespace detail {
    VCE_HAS_FIELD(HasRelocatable, RELOCATABLE);

    template <class T>
    struct IsRelocatable {
        static constexpr bool value = std::is_trivially_move_constructible_v<T>;
    };

    template <class T>
    static constexpr bool IsRelocatableV = IsRelocatable<T>::value;
}

/// Defines the supplied type as being relocatable.
#define VCE_IS_RELOCATABLE(...) \
    namespace vce::detail { \
        template <> \
        struct IsRelocatable<__VA_ARGS__> { \
            static constexpr bool value = true; \
        }; \
    }

/// Defines the supplied template type as being relocatable.
#define VCE_IS_RELOCATABLE_TEMPLATE(...) \
    namespace vce::detail { \
        template <class... N> \
        struct IsRelocatable<__VA_ARGS__<N...>> { \
            static constexpr bool value = true; \
        }; \
    }

/// Returns whether the supplied type may be safely moved to another location in memory.
template <class T>
constexpr bool is_relocatable() {
    if constexpr (detail::HasRelocatableV<T, bool>) {
        return T::RELOCATABLE;
    } else {
        return detail::IsRelocatableV<T>;
    }
}

/// A reference wrapper.
template <class T>
using Ref = std::reference_wrapper<T>;

/// A helper struct for SFINAE.
template <bool ENABLE>
using Sfinae = typename std::enable_if_t<ENABLE, int>;

/// The unit type.
struct Unit { };

bool operator==(Unit, Unit);
bool operator!=(Unit, Unit);
bool operator<(Unit, Unit);
bool operator>(Unit, Unit);
bool operator<=(Unit, Unit);
bool operator>=(Unit, Unit);

std::ostream& operator<<(std::ostream& stream, Unit);

/// The only value of the unit type.
constexpr static Unit UNIT{};

}

#endif
<|endoftext|>


<commit_before>#include "COmponents.h"

using namespace platypus;

#define WAIT_FOR_CONDITION(condition, timeout_ms) for (unsigned int j = 0; j < (timeout_ms) && !(condition); ++j) delay(1);

// TODO: move these somewhere reasonable
// Default gains for the Roboclaw.
/*#define Kp 0x00010000
#define Ki 0x00008000
#define Kd 0x00004000
#define Qpps 44000
*/
//Velocity PID coefficients
#define Kp 2
#define Ki 4
#define Kd 0
#define Qpps 44000 //3600

//Position PID coefficients
#define PosKp 2500
#define PosKi 0
#define PosKd 20000
#define KiMax 0
#define DeadZone 5 //10
#define Min 50
#define Max 1950

#define addr 0x80

void VaporPro::arm()
{
  disable();
  delay(500);
  enable();

  velocity(1.0);
  delay(5500);

  velocity(-1.0);
  delay(3500);

  velocity(0.0);
  delay(8500);
}

void HobbyKingBoat::arm()
{
  disable();
  delay(1000);

  velocity(1.0);
  enable();
  delay(3000);

  velocity(0.0);
  delay(3000);
}

void Seaking::arm()
{
  disable();
  delay(500);
  disable();
  delay(500);

  velocity(1.0);
  enable();
  delay(3000);

  velocity(0.0);
  delay(2000);
}

void Swordfish::arm()
{
  disable();
  delay(500);

  velocity(1.0);
  enable();
  delay(5000);

  velocity(0.0);
  delay(3000);
}

AnalogSensor::AnalogSensor(int channel)
  : Sensor(channel), scale_(1.0f), offset_(0.0f) {}

bool AnalogSensor::set(char* param, char* value)
{
  // Set analog scale.
  if (!strncmp("scale", param, 6))
  {
    float s = atof(value);
    scale(s);
    return true;
  }
  // Set analog offset.
  else if (!strncmp("offset", param, 7))
  {
    float o = atof(value);
    offset(o);
    return true;
  }
  // Return false for unknown command.
  else
  {
    return false;
  }
}

void AnalogSensor::scale(float scale)
{
  scale_ = scale;
}

float AnalogSensor::scale()
{
  return scale_;
}

void AnalogSensor::offset(float offset)
{
  offset = offset_;
}

float AnalogSensor::offset()
{
  return offset_;
}

char* AnalogSensor::name()
{
  return "analog";
}

ServoSensor::ServoSensor(int channel)
  : Sensor(channel), position_(0.0)
{
  servo_.attach(board::SENSOR[channel].GPIO[board::TX_NEG]);
}

ServoSensor::~ServoSensor()
{
  servo_.detach();
}

void ServoSensor::position(float position)
{
  if (position > 1.0) {
    position = 1.0;
  }
  if (position < -1.0) {
    position = -1.0;
  }
  position_ = position;

  float command = (position_ * 600) + 1500;
  servo_.writeMicroseconds(command);
}

float ServoSensor::position()
{
  return position_;
}

bool ServoSensor::set(char *param, char *value)
{
  // Set motor velocity.
  if (!strncmp("p", param, 2))
  {
    float p = atof(value);
    position(p);
    return true;
  }
  // Return false for unknown command.
  else
  {
    return false;
  }
}

char* ServoSensor::name()
{
  return "servo";
}

PoweredSensor::PoweredSensor(int channel)
: Sensor(channel)
{
  pinMode(board::SENSOR[channel_].PWR_ENABLE, OUTPUT);
  digitalWrite(board::SENSOR[channel_].PWR_ENABLE, HIGH);
}

char* PoweredSensor::name()
{
  return "powered";
}

ES2::ES2(int channel)
: Sensor(channel), recv_index_(0)
{
  // Start up serial port
  SERIAL_PORTS[channel]->begin(1200);
}

char* ES2::name()
{
  return "es2";
}

void ES2::loop()
{
  // Enable +12V output.
  pinMode(board::SENSOR[channel_].PWR_ENABLE, OUTPUT);
  digitalWrite(board::SENSOR[channel_].PWR_ENABLE, HIGH);

  // Read response from sensor.
  delay(250);

  // Turn off +12V output.
  pinMode(board::SENSOR[channel_].PWR_ENABLE, OUTPUT);
  digitalWrite(board::SENSOR[channel_].PWR_ENABLE, LOW);

  // Wait a while for next sensor reading.
  delay(1750);
}

void ES2::onSerial()
{
  // TODO: verify checksum.
  char c = SERIAL_PORTS[channel_]->read();
  if (c == '\0') {
    return;
  }
  else if (c != '\r' && c != '\n' && recv_index_ < DEFAULT_BUFFER_SIZE)
  {
    recv_buffer_[recv_index_] = c;
    ++recv_index_;
  }
  else if (recv_index_ > 0)
  {
    recv_buffer_[recv_index_] = '\0';

    if (recv_index_ > 6) // Only send data strings
    {
      char output_str[DEFAULT_BUFFER_SIZE+3];
      snprintf(output_str, DEFAULT_BUFFER_SIZE,
        "{"
         "\"s%u\":{"
           "\"type\":\"es2\","
           "\"data\":\"%s\""
         "}"
        "}",
        channel_,
        recv_buffer_
      );
      send(output_str);
    }

    memset(recv_buffer_, 0, recv_index_);
    recv_index_ = 0;
  }
}

AtlasSensor::AtlasSensor(int channel)
: Sensor(channel), recv_index_(0)
{
  // Start up serial port.
  SERIAL_PORTS[channel]->begin(38400);

  // Tell the sensor to output continuously.
  SERIAL_PORTS[channel_]->print("C\r");
}

char* AtlasSensor::name()
{
  return "atlas";
}

void AtlasSensor::onSerial()
{
  char c = SERIAL_PORTS[channel_]->read();
  if (c != '\r' && c != '\n' && recv_index_ < DEFAULT_BUFFER_SIZE)
  {
    recv_buffer_[recv_index_] = c;
    ++recv_index_;
  }
  else if (recv_index_ > 0)
  {
    recv_buffer_[recv_index_] = '\0';

    char output_str[DEFAULT_BUFFER_SIZE+3];
    snprintf(output_str, DEFAULT_BUFFER_SIZE,
      "{"
       "\"s%u\":{"
         "\"type\":\"atlas\","
         "\"data\":\"%s\""
       "}"
      "}",
      channel_,
      recv_buffer_
    );
    send(output_str);

    memset(recv_buffer_, 0, recv_index_);
    recv_index_ = 0;
  }
}

Hds5::Hds5(int channel)
: Sensor(channel), recv_index_(0)
{
  // Enable +12V output
  pinMode(board::SENSOR[channel].PWR_ENABLE, OUTPUT);
  digitalWrite(board::SENSOR[channel].PWR_ENABLE, HIGH);

  // Enable RSxxx receiver
  pinMode(board::SENSOR[channel].RX_DISABLE, OUTPUT);
  digitalWrite(board::SENSOR[channel].RX_DISABLE, LOW);

  // Enable RSxxx transmitter
  pinMode(board::SENSOR[channel].TX_ENABLE, OUTPUT);
  digitalWrite(board::SENSOR[channel].TX_ENABLE, HIGH);

  // Enable RS485 termination resistor
  pinMode(board::SENSOR[channel].RS485_TE, OUTPUT);
  digitalWrite(board::SENSOR[channel].RS485_TE, HIGH);

  // Select RS485 (deselect RS232)
  pinMode(board::SENSOR[channel].RS485_232, OUTPUT);
  digitalWrite(board::SENSOR[channel].RS485_232, HIGH);

  // Start up serial port
  SERIAL_PORTS[channel]->begin(4800);
}

char* Hds5::name()
{
  return "hds5";
}

void Hds5::onSerial()
{
  char c = SERIAL_PORTS[channel_]->read();
  if (c != '\r' && c != '\n' && recv_index_ < DEFAULT_BUFFER_SIZE)
  {
    recv_buffer_[recv_index_] = c;
    ++recv_index_;
  }
  else if (recv_index_ > 0)
  {
    recv_buffer_[recv_index_] = '\0';

    char output_str[DEFAULT_BUFFER_SIZE+3];
    snprintf(output_str, DEFAULT_BUFFER_SIZE,
      "{"
       "\"s%u\":{"
         "\"type\":\"hds5\","
         "\"nmea\":\"%s\""
       "}"
      "}",
      channel_,
      recv_buffer_
    );
    send(output_str);

    memset(recv_buffer_, 0, recv_index_);
    recv_index_ = 0;
  }
}

Winch::Winch(int channel, uint8_t address)
: Sensor(channel)
, roboclaw_(platypus::SERIAL_PORTS[channel], 10000)
, address_(address)
, desired_position_(0)
, desired_velocity_(0)
, desired_acceleration_(12000)
{
  // Enable +12V output.
  pinMode(board::SENSOR[channel].PWR_ENABLE, OUTPUT);
  digitalWrite(board::SENSOR[channel].PWR_ENABLE, HIGH);

  // TODO: specifically enable e-stop line.
  // (Right now it is just pulled up by default.)

  // Start up Roboclaw.
  roboclaw_.begin(38400);
}

char* Winch::name()
{
  return "winch";
}

bool Winch::set(char* param, char* value)
{
  // Set winch position
  if (!strncmp("p", param, 2))
  {
    uint32_t pos = atol(value);

    position(pos);
    return true;
  }
  else if (!strncmp("v", param, 2))
  {
    int32_t vel = atol(value);

    velocity(vel);
    return true;
  }
  else if (!strncmp("reset", param, 6))
  {

    reset();
    return true;
  }
  // Return false for unknown command.
  else
  {

    return false;
  }
}

void Winch::reset()
{
  roboclaw_.ResetEncoders(address_);
}

void Winch::position(uint32_t pos)
{
  desired_position_ = pos;
  roboclaw_.SetM1PositionPID(addr, PosKd, PosKp, PosKi, KiMax, DeadZone, Min, Max);
}

void Winch::velocity(int32_t vel)
{
  desired_velocity_ = vel;
  roboclaw_.SetM1VelocityPID(addr, Kd, Kp, Ki, Qpps);
  roboclaw_.SpeedAccelDistanceM1(addr,
                                 desired_acceleration_,
                                 desired_velocity_,
                                 desired_position_);
}

uint32_t Winch::encoder(bool *valid)
{
  uint32_t enc1 = roboclaw_.ReadEncM1(addr, NULL, valid);
  return enc1;
}

//Cosntruct RC "sensor" by calling RC_Controller
RC::RC(int channel)
: Sensor(channel),
  RC_Controller(board::SENSOR[channel].GPIO[board::RX_NEG],
                board::SENSOR[channel].GPIO[board::TX_NEG],
                board::SENSOR[channel].GPIO[board::RX_POS])
{
    // Disable RSxxx receiver
  pinMode(board::SENSOR[channel].RX_DISABLE, OUTPUT);
  digitalWrite(board::SENSOR[channel].RX_DISABLE, HIGH);

  // Disable TSxxx transmitter
  pinMode(board::SENSOR[channel].TX_ENABLE, OUTPUT);
  digitalWrite(board::SENSOR[channel].TX_ENABLE, LOW);

}

char * RC::name()
{
    return "RC_Controller";
}

<commit_msg>Fixing typo<commit_after>#include "Components.h"

using namespace platypus;

#define WAIT_FOR_CONDITION(condition, timeout_ms) for (unsigned int j = 0; j < (timeout_ms) && !(condition); ++j) delay(1);

// TODO: move these somewhere reasonable
// Default gains for the Roboclaw.
/*#define Kp 0x00010000
#define Ki 0x00008000
#define Kd 0x00004000
#define Qpps 44000
*/
//Velocity PID coefficients
#define Kp 2
#define Ki 4
#define Kd 0
#define Qpps 44000 //3600

//Position PID coefficients
#define PosKp 2500
#define PosKi 0
#define PosKd 20000
#define KiMax 0
#define DeadZone 5 //10
#define Min 50
#define Max 1950

#define addr 0x80

void VaporPro::arm()
{
  disable();
  delay(500);
  enable();

  velocity(1.0);
  delay(5500);

  velocity(-1.0);
  delay(3500);

  velocity(0.0);
  delay(8500);
}

void HobbyKingBoat::arm()
{
  disable();
  delay(1000);

  velocity(1.0);
  enable();
  delay(3000);

  velocity(0.0);
  delay(3000);
}

void Seaking::arm()
{
  disable();
  delay(500);
  disable();
  delay(500);

  velocity(1.0);
  enable();
  delay(3000);

  velocity(0.0);
  delay(2000);
}

void Swordfish::arm()
{
  disable();
  delay(500);

  velocity(1.0);
  enable();
  delay(5000);

  velocity(0.0);
  delay(3000);
}

AnalogSensor::AnalogSensor(int channel)
  : Sensor(channel), scale_(1.0f), offset_(0.0f) {}

bool AnalogSensor::set(char* param, char* value)
{
  // Set analog scale.
  if (!strncmp("scale", param, 6))
  {
    float s = atof(value);
    scale(s);
    return true;
  }
  // Set analog offset.
  else if (!strncmp("offset", param, 7))
  {
    float o = atof(value);
    offset(o);
    return true;
  }
  // Return false for unknown command.
  else
  {
    return false;
  }
}

void AnalogSensor::scale(float scale)
{
  scale_ = scale;
}

float AnalogSensor::scale()
{
  return scale_;
}

void AnalogSensor::offset(float offset)
{
  offset = offset_;
}

float AnalogSensor::offset()
{
  return offset_;
}

char* AnalogSensor::name()
{
  return "analog";
}

ServoSensor::ServoSensor(int channel)
  : Sensor(channel), position_(0.0)
{
  servo_.attach(board::SENSOR[channel].GPIO[board::TX_NEG]);
}

ServoSensor::~ServoSensor()
{
  servo_.detach();
}

void ServoSensor::position(float position)
{
  if (position > 1.0) {
    position = 1.0;
  }
  if (position < -1.0) {
    position = -1.0;
  }
  position_ = position;

  float command = (position_ * 600) + 1500;
  servo_.writeMicroseconds(command);
}

float ServoSensor::position()
{
  return position_;
}

bool ServoSensor::set(char *param, char *value)
{
  // Set motor velocity.
  if (!strncmp("p", param, 2))
  {
    float p = atof(value);
    position(p);
    return true;
  }
  // Return false for unknown command.
  else
  {
    return false;
  }
}

char* ServoSensor::name()
{
  return "servo";
}

PoweredSensor::PoweredSensor(int channel)
: Sensor(channel)
{
  pinMode(board::SENSOR[channel_].PWR_ENABLE, OUTPUT);
  digitalWrite(board::SENSOR[channel_].PWR_ENABLE, HIGH);
}

char* PoweredSensor::name()
{
  return "powered";
}

ES2::ES2(int channel)
: Sensor(channel), recv_index_(0)
{
  // Start up serial port
  SERIAL_PORTS[channel]->begin(1200);
}

char* ES2::name()
{
  return "es2";
}

void ES2::loop()
{
  // Enable +12V output.
  pinMode(board::SENSOR[channel_].PWR_ENABLE, OUTPUT);
  digitalWrite(board::SENSOR[channel_].PWR_ENABLE, HIGH);

  // Read response from sensor.
  delay(250);

  // Turn off +12V output.
  pinMode(board::SENSOR[channel_].PWR_ENABLE, OUTPUT);
  digitalWrite(board::SENSOR[channel_].PWR_ENABLE, LOW);

  // Wait a while for next sensor reading.
  delay(1750);
}

void ES2::onSerial()
{
  // TODO: verify checksum.
  char c = SERIAL_PORTS[channel_]->read();
  if (c == '\0') {
    return;
  }
  else if (c != '\r' && c != '\n' && recv_index_ < DEFAULT_BUFFER_SIZE)
  {
    recv_buffer_[recv_index_] = c;
    ++recv_index_;
  }
  else if (recv_index_ > 0)
  {
    recv_buffer_[recv_index_] = '\0';

    if (recv_index_ > 6) // Only send data strings
    {
      char output_str[DEFAULT_BUFFER_SIZE+3];
      snprintf(output_str, DEFAULT_BUFFER_SIZE,
        "{"
         "\"s%u\":{"
           "\"type\":\"es2\","
           "\"data\":\"%s\""
         "}"
        "}",
        channel_,
        recv_buffer_
      );
      send(output_str);
    }

    memset(recv_buffer_, 0, recv_index_);
    recv_index_ = 0;
  }
}

AtlasSensor::AtlasSensor(int channel)
: Sensor(channel), recv_index_(0)
{
  // Start up serial port.
  SERIAL_PORTS[channel]->begin(38400);

  // Tell the sensor to output continuously.
  SERIAL_PORTS[channel_]->print("C\r");
}

char* AtlasSensor::name()
{
  return "atlas";
}

void AtlasSensor::onSerial()
{
  char c = SERIAL_PORTS[channel_]->read();
  if (c != '\r' && c != '\n' && recv_index_ < DEFAULT_BUFFER_SIZE)
  {
    recv_buffer_[recv_index_] = c;
    ++recv_index_;
  }
  else if (recv_index_ > 0)
  {
    recv_buffer_[recv_index_] = '\0';

    char output_str[DEFAULT_BUFFER_SIZE+3];
    snprintf(output_str, DEFAULT_BUFFER_SIZE,
      "{"
       "\"s%u\":{"
         "\"type\":\"atlas\","
         "\"data\":\"%s\""
       "}"
      "}",
      channel_,
      recv_buffer_
    );
    send(output_str);

    memset(recv_buffer_, 0, recv_index_);
    recv_index_ = 0;
  }
}

Hds5::Hds5(int channel)
: Sensor(channel), recv_index_(0)
{
  // Enable +12V output
  pinMode(board::SENSOR[channel].PWR_ENABLE, OUTPUT);
  digitalWrite(board::SENSOR[channel].PWR_ENABLE, HIGH);

  // Enable RSxxx receiver
  pinMode(board::SENSOR[channel].RX_DISABLE, OUTPUT);
  digitalWrite(board::SENSOR[channel].RX_DISABLE, LOW);

  // Enable RSxxx transmitter
  pinMode(board::SENSOR[channel].TX_ENABLE, OUTPUT);
  digitalWrite(board::SENSOR[channel].TX_ENABLE, HIGH);

  // Enable RS485 termination resistor
  pinMode(board::SENSOR[channel].RS485_TE, OUTPUT);
  digitalWrite(board::SENSOR[channel].RS485_TE, HIGH);

  // Select RS485 (deselect RS232)
  pinMode(board::SENSOR[channel].RS485_232, OUTPUT);
  digitalWrite(board::SENSOR[channel].RS485_232, HIGH);

  // Start up serial port
  SERIAL_PORTS[channel]->begin(4800);
}

char* Hds5::name()
{
  return "hds5";
}

void Hds5::onSerial()
{
  char c = SERIAL_PORTS[channel_]->read();
  if (c != '\r' && c != '\n' && recv_index_ < DEFAULT_BUFFER_SIZE)
  {
    recv_buffer_[recv_index_] = c;
    ++recv_index_;
  }
  else if (recv_index_ > 0)
  {
    recv_buffer_[recv_index_] = '\0';

    char output_str[DEFAULT_BUFFER_SIZE+3];
    snprintf(output_str, DEFAULT_BUFFER_SIZE,
      "{"
       "\"s%u\":{"
         "\"type\":\"hds5\","
         "\"nmea\":\"%s\""
       "}"
      "}",
      channel_,
      recv_buffer_
    );
    send(output_str);

    memset(recv_buffer_, 0, recv_index_);
    recv_index_ = 0;
  }
}

Winch::Winch(int channel, uint8_t address)
: Sensor(channel)
, roboclaw_(platypus::SERIAL_PORTS[channel], 10000)
, address_(address)
, desired_position_(0)
, desired_velocity_(0)
, desired_acceleration_(12000)
{
  // Enable +12V output.
  pinMode(board::SENSOR[channel].PWR_ENABLE, OUTPUT);
  digitalWrite(board::SENSOR[channel].PWR_ENABLE, HIGH);

  // TODO: specifically enable e-stop line.
  // (Right now it is just pulled up by default.)

  // Start up Roboclaw.
  roboclaw_.begin(38400);
}

char* Winch::name()
{
  return "winch";
}

bool Winch::set(char* param, char* value)
{
  // Set winch position
  if (!strncmp("p", param, 2))
  {
    uint32_t pos = atol(value);

    position(pos);
    return true;
  }
  else if (!strncmp("v", param, 2))
  {
    int32_t vel = atol(value);

    velocity(vel);
    return true;
  }
  else if (!strncmp("reset", param, 6))
  {

    reset();
    return true;
  }
  // Return false for unknown command.
  else
  {

    return false;
  }
}

void Winch::reset()
{
  roboclaw_.ResetEncoders(address_);
}

void Winch::position(uint32_t pos)
{
  desired_position_ = pos;
  roboclaw_.SetM1PositionPID(addr, PosKd, PosKp, PosKi, KiMax, DeadZone, Min, Max);
}

void Winch::velocity(int32_t vel)
{
  desired_velocity_ = vel;
  roboclaw_.SetM1VelocityPID(addr, Kd, Kp, Ki, Qpps);
  roboclaw_.SpeedAccelDistanceM1(addr,
                                 desired_acceleration_,
                                 desired_velocity_,
                                 desired_position_);
}

uint32_t Winch::encoder(bool *valid)
{
  uint32_t enc1 = roboclaw_.ReadEncM1(addr, NULL, valid);
  return enc1;
}

//Cosntruct RC "sensor" by calling RC_Controller
RC::RC(int channel)
: Sensor(channel),
  RC_Controller(board::SENSOR[channel].GPIO[board::RX_NEG],
                board::SENSOR[channel].GPIO[board::TX_NEG],
                board::SENSOR[channel].GPIO[board::RX_POS])
{
    // Disable RSxxx receiver
  pinMode(board::SENSOR[channel].RX_DISABLE, OUTPUT);
  digitalWrite(board::SENSOR[channel].RX_DISABLE, HIGH);

  // Disable TSxxx transmitter
  pinMode(board::SENSOR[channel].TX_ENABLE, OUTPUT);
  digitalWrite(board::SENSOR[channel].TX_ENABLE, LOW);

}

char * RC::name()
{
    return "RC_Controller";
}

<|endoftext|>


<commit_before>#include "InputParticle.h"

#include <maya/MFnAttribute.h>
#include <maya/MFnParticleSystem.h>
#include <maya/MDoubleArray.h>
#include <maya/MGlobal.h>
#include <maya/MMatrix.h>
#include <maya/MPlug.h>
#include <maya/MPlugArray.h>
#include <maya/MStringArray.h>
#include <maya/MVectorArray.h>

#include "util.h"

InputParticle::InputParticle(int assetId, int inputIdx) :
    Input(assetId, inputIdx),
    myInputAssetId(0)
{
    Util::PythonInterpreterLock pythonInterpreterLock;

    CHECK_HAPI(HAPI_CreateInputAsset(
                Util::theHAPISession.get(),
                &myInputAssetId,
                NULL
                ));

    if(!Util::statusCheckLoop())
    {
        DISPLAY_ERROR(MString("Unexpected error when creating input asset."));
    }

    myInputObjectId = 0;
    myInputGeoId = 0;

    CHECK_HAPI(HAPI_ConnectAssetGeometry(
                Util::theHAPISession.get(),
                myInputAssetId, myInputObjectId,
                myAssetId, myInputIdx
                ));
}

InputParticle::~InputParticle()
{
    CHECK_HAPI(HAPI_DestroyAsset(
                Util::theHAPISession.get(),
                myInputAssetId
                ));
}

Input::AssetInputType
InputParticle::assetInputType() const
{
    return Input::AssetInputType_Particle;
}

void
InputParticle::setInputTransform(MDataHandle &dataHandle)
{
    MMatrix transformMatrix = dataHandle.asMatrix();

    float matrix[16];
    transformMatrix.get(reinterpret_cast<float(*)[4]>(matrix));

    HAPI_TransformEuler transformEuler;
    HAPI_ConvertMatrixToEuler(
            Util::theHAPISession.get(),
            matrix,
            HAPI_SRT,
            HAPI_XYZ,
            &transformEuler
            );
    HAPI_SetObjectTransform(
            Util::theHAPISession.get(),
            myInputAssetId,
            myInputObjectId,
            &transformEuler
            );
}

void
InputParticle::setAttributePointData(
        const char* attributeName,
        HAPI_StorageType storage,
        int count,
        int tupleSize,
        void* data
        )
{
    HAPI_AttributeInfo attributeInfo;
    attributeInfo.exists = true;
    attributeInfo.owner = HAPI_ATTROWNER_POINT;
    attributeInfo.storage = storage;
    attributeInfo.count = count;
    attributeInfo.tupleSize = tupleSize;

    HAPI_AddAttribute(
            Util::theHAPISession.get(),
            myInputAssetId, myInputObjectId, myInputGeoId,
            attributeName,
            &attributeInfo
            );

    switch(storage)
    {
        case HAPI_STORAGETYPE_FLOAT:
            HAPI_SetAttributeFloatData(
                    Util::theHAPISession.get(),
                    myInputAssetId, myInputObjectId, myInputGeoId,
                    attributeName,
                    &attributeInfo,
                    static_cast<float*>(data),
                    0, count
                    );
            break;
        case HAPI_STORAGETYPE_INT:
            HAPI_SetAttributeIntData(
                    Util::theHAPISession.get(),
                    myInputAssetId, myInputObjectId, myInputGeoId,
                    attributeName,
                    &attributeInfo,
                    static_cast<int*>(data),
                    0, count
                    );
            break;
        default:
            break;
    }
}

void
InputParticle::setInputGeo(
        MDataBlock &dataBlock,
        const MPlug &plug
        )
{
    MStatus status;

    // get particle node
    MObject particleObj;
    {
        MPlugArray plugArray;
        plug.connectedTo(plugArray, true, false, &status);
        CHECK_MSTATUS(status);

        if(plugArray.length() == 1)
        {
            particleObj = plugArray[0].node();
        }
    }

    if(particleObj.isNull())
    {
        return;
    }

    MFnParticleSystem particleFn(particleObj);

    // get original particle node
    MObject originalParticleObj;
    // status parameter is needed due to a bug in Maya API
    if(particleFn.isDeformedParticleShape(&status))
    {
        originalParticleObj = particleFn.originalParticleShape(&status);
    }
    else
    {
        originalParticleObj = particleObj;
    }

    MFnParticleSystem originalParticleFn(originalParticleObj);

    // set up part info
    HAPI_PartInfo partInfo;
    HAPI_PartInfo_Init(&partInfo);
    partInfo.id = 0;
    partInfo.faceCount = 0;
    partInfo.vertexCount = 0;
    partInfo.pointCount = particleFn.count();

    HAPI_SetPartInfo(
            Util::theHAPISession.get(),
            myInputAssetId, myInputObjectId, myInputGeoId,
            &partInfo
            );

    // set per-particle attributes
    {
        // shared buffers for passing data
        MVectorArray vectorArray;
        MDoubleArray doubleArray;
        char* data = new char[sizeof(float) * 3 * particleFn.count()];

        // id
        {
            MIntArray ids;
            // Must get the IDs from the original particle node. Maya will
            // crash if we try to get the IDs from the deformed particle node.
            originalParticleFn.particleIds(ids);

            ids.get(reinterpret_cast<int*>(data));

            setAttributePointData(
                    "id",
                    HAPI_STORAGETYPE_INT,
                    particleFn.count(),
                    1,
                    data
                    );
        }

        // 0: vector
        // 1: double
        for(int i = 0; i < 2; i++)
        {
            int tupleSize = 0;
            MString getAttributesCommand = "particle -q ";
            switch(i)
            {
                case 0:
                    tupleSize = 3;
                    getAttributesCommand += "-perParticleVector ";
                    break;
                case 1:
                    tupleSize = 1;
                    getAttributesCommand += "-perParticleDouble ";
                    break;
            }

            // query the original particle for names of the per-particle attributes
            getAttributesCommand += originalParticleFn.fullPathName();

            // get the per-particle attribute names
            MStringArray attributeNames;
            MGlobal::executeCommand(getAttributesCommand, attributeNames);

            for(unsigned int j = 0; j < attributeNames.length(); j++)
            {
                const MString attributeName = attributeNames[j];

                MObject attributeObj = originalParticleFn.attribute(attributeName);
                if(attributeObj.isNull())
                {
                    continue;
                }

                // mimics "listAttr -v -w" from AEokayAttr
                MFnAttribute attributeFn(attributeObj);
                if(!(!attributeFn.isHidden() && attributeFn.isWritable()))
                {
                    continue;
                }

                // get the per-particle data
                switch(i)
                {
                    case 0:
                        if(attributeName == "position")
                        {
                            // Need to use position() so that we get the right
                            // positions in the case of deformed particles.
                            particleFn.position(vectorArray);
                        }
                        else
                        {
                            // Maya will automatically use the original
                            // particle node in the case of deformed particles.
                            particleFn.getPerParticleAttribute(
                                    attributeName,
                                    vectorArray
                                    );
                        }

                        vectorArray.get(reinterpret_cast<float(*)[3]>(data));
                        break;
                    case 1:
                        // Maya will automatically use the original
                        // particle node in the case of deformed particles.
                        particleFn.getPerParticleAttribute(
                                attributeName,
                                doubleArray
                                );

                        doubleArray.get(reinterpret_cast<float(*)>(data));
                        break;
                }

                // map the parameter name
                const char* parameterName = attributeName.asChar();
                switch(i)
                {
                    case 0:
                        if(strcmp(parameterName, "position") == 0)
                        {
                            parameterName = "P";
                        }
                        else if(strcmp(parameterName, "velocity") == 0)
                        {
                            parameterName = "v";
                        }
                        else if(strcmp(parameterName, "acceleration") == 0)
                        {
                            parameterName = "force";
                        }
                        else if(strcmp(parameterName, "rgbPP") == 0)
                        {
                            parameterName = "Cd";
                        }
                        break;
                    case 1:
                        if(strcmp(parameterName, "opacityPP") == 0)
                        {
                            parameterName = "Alpha";
                        }
                        else if(strcmp(parameterName, "radiusPP") == 0)
                        {
                            parameterName = "pscale";
                        }
                        break;
                }

                setAttributePointData(
                        parameterName,
                        HAPI_STORAGETYPE_FLOAT,
                        particleFn.count(),
                        tupleSize,
                        data
                        );
            }

            switch(i)
            {
                case 0:
                    vectorArray.clear();
                    break;
                case 1:
                    doubleArray.clear();
                    break;
            }
        }

        delete [] data;
    }

    Input::setInputPlugMetaData(
            plug,
            myInputAssetId, myInputObjectId, myInputGeoId
            );

    // Commit it
    HAPI_CommitGeo(
            Util::theHAPISession.get(),
            myInputAssetId, myInputObjectId, myInputGeoId
            );
}
<commit_msg>When inputting particles, also include the "age" attribute<commit_after>#include "InputParticle.h"

#include <maya/MFnAttribute.h>
#include <maya/MFnParticleSystem.h>
#include <maya/MDoubleArray.h>
#include <maya/MGlobal.h>
#include <maya/MMatrix.h>
#include <maya/MPlug.h>
#include <maya/MPlugArray.h>
#include <maya/MStringArray.h>
#include <maya/MVectorArray.h>

#include "util.h"

InputParticle::InputParticle(int assetId, int inputIdx) :
    Input(assetId, inputIdx),
    myInputAssetId(0)
{
    Util::PythonInterpreterLock pythonInterpreterLock;

    CHECK_HAPI(HAPI_CreateInputAsset(
                Util::theHAPISession.get(),
                &myInputAssetId,
                NULL
                ));

    if(!Util::statusCheckLoop())
    {
        DISPLAY_ERROR(MString("Unexpected error when creating input asset."));
    }

    myInputObjectId = 0;
    myInputGeoId = 0;

    CHECK_HAPI(HAPI_ConnectAssetGeometry(
                Util::theHAPISession.get(),
                myInputAssetId, myInputObjectId,
                myAssetId, myInputIdx
                ));
}

InputParticle::~InputParticle()
{
    CHECK_HAPI(HAPI_DestroyAsset(
                Util::theHAPISession.get(),
                myInputAssetId
                ));
}

Input::AssetInputType
InputParticle::assetInputType() const
{
    return Input::AssetInputType_Particle;
}

void
InputParticle::setInputTransform(MDataHandle &dataHandle)
{
    MMatrix transformMatrix = dataHandle.asMatrix();

    float matrix[16];
    transformMatrix.get(reinterpret_cast<float(*)[4]>(matrix));

    HAPI_TransformEuler transformEuler;
    HAPI_ConvertMatrixToEuler(
            Util::theHAPISession.get(),
            matrix,
            HAPI_SRT,
            HAPI_XYZ,
            &transformEuler
            );
    HAPI_SetObjectTransform(
            Util::theHAPISession.get(),
            myInputAssetId,
            myInputObjectId,
            &transformEuler
            );
}

void
InputParticle::setAttributePointData(
        const char* attributeName,
        HAPI_StorageType storage,
        int count,
        int tupleSize,
        void* data
        )
{
    HAPI_AttributeInfo attributeInfo;
    attributeInfo.exists = true;
    attributeInfo.owner = HAPI_ATTROWNER_POINT;
    attributeInfo.storage = storage;
    attributeInfo.count = count;
    attributeInfo.tupleSize = tupleSize;

    HAPI_AddAttribute(
            Util::theHAPISession.get(),
            myInputAssetId, myInputObjectId, myInputGeoId,
            attributeName,
            &attributeInfo
            );

    switch(storage)
    {
        case HAPI_STORAGETYPE_FLOAT:
            HAPI_SetAttributeFloatData(
                    Util::theHAPISession.get(),
                    myInputAssetId, myInputObjectId, myInputGeoId,
                    attributeName,
                    &attributeInfo,
                    static_cast<float*>(data),
                    0, count
                    );
            break;
        case HAPI_STORAGETYPE_INT:
            HAPI_SetAttributeIntData(
                    Util::theHAPISession.get(),
                    myInputAssetId, myInputObjectId, myInputGeoId,
                    attributeName,
                    &attributeInfo,
                    static_cast<int*>(data),
                    0, count
                    );
            break;
        default:
            break;
    }
}

void
InputParticle::setInputGeo(
        MDataBlock &dataBlock,
        const MPlug &plug
        )
{
    MStatus status;

    // get particle node
    MObject particleObj;
    {
        MPlugArray plugArray;
        plug.connectedTo(plugArray, true, false, &status);
        CHECK_MSTATUS(status);

        if(plugArray.length() == 1)
        {
            particleObj = plugArray[0].node();
        }
    }

    if(particleObj.isNull())
    {
        return;
    }

    MFnParticleSystem particleFn(particleObj);

    // get original particle node
    MObject originalParticleObj;
    // status parameter is needed due to a bug in Maya API
    if(particleFn.isDeformedParticleShape(&status))
    {
        originalParticleObj = particleFn.originalParticleShape(&status);
    }
    else
    {
        originalParticleObj = particleObj;
    }

    MFnParticleSystem originalParticleFn(originalParticleObj);

    // set up part info
    HAPI_PartInfo partInfo;
    HAPI_PartInfo_Init(&partInfo);
    partInfo.id = 0;
    partInfo.faceCount = 0;
    partInfo.vertexCount = 0;
    partInfo.pointCount = particleFn.count();

    HAPI_SetPartInfo(
            Util::theHAPISession.get(),
            myInputAssetId, myInputObjectId, myInputGeoId,
            &partInfo
            );

    // set per-particle attributes
    {
        // shared buffers for passing data
        MVectorArray vectorArray;
        MDoubleArray doubleArray;
        char* data = new char[sizeof(float) * 3 * particleFn.count()];

        // id
        {
            MIntArray ids;
            // Must get the IDs from the original particle node. Maya will
            // crash if we try to get the IDs from the deformed particle node.
            originalParticleFn.particleIds(ids);

            ids.get(reinterpret_cast<int*>(data));

            setAttributePointData(
                    "id",
                    HAPI_STORAGETYPE_INT,
                    particleFn.count(),
                    1,
                    data
                    );
        }

        // 0: vector
        // 1: double
        for(int i = 0; i < 2; i++)
        {
            int tupleSize = 0;
            MString getAttributesCommand = "particle -q ";
            switch(i)
            {
                case 0:
                    tupleSize = 3;
                    getAttributesCommand += "-perParticleVector ";
                    break;
                case 1:
                    tupleSize = 1;
                    getAttributesCommand += "-perParticleDouble ";
                    break;
            }

            // query the original particle for names of the per-particle attributes
            getAttributesCommand += originalParticleFn.fullPathName();

            // get the per-particle attribute names
            MStringArray attributeNames;
            MGlobal::executeCommand(getAttributesCommand, attributeNames);

            // explicitly include some special per-particle attributes that
            // aren't returned by the MEL command
            switch(i)
            {
                case 0:
                    break;
                case 1:
                    attributeNames.append("age");
                    break;
            }

            for(unsigned int j = 0; j < attributeNames.length(); j++)
            {
                const MString attributeName = attributeNames[j];

                MObject attributeObj = originalParticleFn.attribute(attributeName);
                if(attributeObj.isNull())
                {
                    continue;
                }

                // mimics "listAttr -v -w" from AEokayAttr
                MFnAttribute attributeFn(attributeObj);
                if(!(!attributeFn.isHidden() && attributeFn.isWritable())
                        && attributeName != "age")
                {
                    continue;
                }

                // get the per-particle data
                switch(i)
                {
                    case 0:
                        if(attributeName == "position")
                        {
                            // Need to use position() so that we get the right
                            // positions in the case of deformed particles.
                            particleFn.position(vectorArray);
                        }
                        else
                        {
                            // Maya will automatically use the original
                            // particle node in the case of deformed particles.
                            particleFn.getPerParticleAttribute(
                                    attributeName,
                                    vectorArray
                                    );
                        }

                        vectorArray.get(reinterpret_cast<float(*)[3]>(data));
                        break;
                    case 1:
                        // Maya will automatically use the original
                        // particle node in the case of deformed particles.
                        particleFn.getPerParticleAttribute(
                                attributeName,
                                doubleArray
                                );

                        doubleArray.get(reinterpret_cast<float(*)>(data));
                        break;
                }

                // map the parameter name
                const char* parameterName = attributeName.asChar();
                switch(i)
                {
                    case 0:
                        if(strcmp(parameterName, "position") == 0)
                        {
                            parameterName = "P";
                        }
                        else if(strcmp(parameterName, "velocity") == 0)
                        {
                            parameterName = "v";
                        }
                        else if(strcmp(parameterName, "acceleration") == 0)
                        {
                            parameterName = "force";
                        }
                        else if(strcmp(parameterName, "rgbPP") == 0)
                        {
                            parameterName = "Cd";
                        }
                        break;
                    case 1:
                        if(strcmp(parameterName, "opacityPP") == 0)
                        {
                            parameterName = "Alpha";
                        }
                        else if(strcmp(parameterName, "radiusPP") == 0)
                        {
                            parameterName = "pscale";
                        }
                        break;
                }

                setAttributePointData(
                        parameterName,
                        HAPI_STORAGETYPE_FLOAT,
                        particleFn.count(),
                        tupleSize,
                        data
                        );
            }

            switch(i)
            {
                case 0:
                    vectorArray.clear();
                    break;
                case 1:
                    doubleArray.clear();
                    break;
            }
        }

        delete [] data;
    }

    Input::setInputPlugMetaData(
            plug,
            myInputAssetId, myInputObjectId, myInputGeoId
            );

    // Commit it
    HAPI_CommitGeo(
            Util::theHAPISession.get(),
            myInputAssetId, myInputObjectId, myInputGeoId
            );
}
<|endoftext|>


<commit_before>#include <DO/Sara/Core/PhysicalQuantities.hpp>
#include <DO/Sara/DisjointSets/TwoPassConnectedComponents.hpp>
#include <DO/Sara/FeatureDetectors/EdgeDetector.hpp>
#include <DO/Sara/FeatureDetectors/EdgePostProcessing.hpp>
#include <DO/Sara/FeatureDetectors/EdgeUtilities.hpp>
#include <DO/Sara/Geometry/Algorithms/Polyline.hpp>
#include <DO/Sara/Graphics.hpp>
#include <DO/Sara/ImageIO.hpp>
#include <DO/Sara/ImageProcessing.hpp>

namespace sara = DO::Sara;
using sara::operator""_deg;

inline constexpr long double operator"" _percent(long double x)
{
  return x / 100;
}

// ========================================================================== //
// Chess corners are saddle points so the determinant of hessian is a good
// mathematical criterion
// ========================================================================== //
struct Saddle
{
  Eigen::Vector2i p;
  float score;

  auto operator<(const Saddle& other) const
  {
    return score < other.score;
  }
};

auto extract_saddle_points(const sara::Image<float>& hessian, float thres)
{
  auto saddle_points = std::vector<Saddle>{};
  saddle_points.reserve(std::max(hessian.width(), hessian.height()));
  for (auto y = 0; y < hessian.height(); ++y)
    for (auto x = 0; x < hessian.width(); ++x)
      if (hessian(x, y) < thres)
        saddle_points.push_back({{x, y}, std::abs(hessian(x, y))});

  return saddle_points;
}

auto nms(std::vector<Saddle>& saddle_points, const Eigen::Vector2i& image_sizes,
         int nms_radius)
{
  std::sort(saddle_points.begin(), saddle_points.end());

  auto saddle_points_filtered = std::vector<Saddle>{};
  saddle_points_filtered.reserve(saddle_points.size());

  auto saddle_map = sara::Image<std::uint8_t>{image_sizes};
  saddle_map.flat_array().fill(0);

  for (const auto& p : saddle_points)
  {
    if (saddle_map(p.p) == 1)
      continue;

    const auto vmin = std::clamp(p.p.y() - nms_radius, 0, saddle_map.height());
    const auto vmax = std::clamp(p.p.y() + nms_radius, 0, saddle_map.height());
    const auto umin = std::clamp(p.p.x() - nms_radius, 0, saddle_map.width());
    const auto umax = std::clamp(p.p.x() + nms_radius, 0, saddle_map.width());
    for (auto v = vmin; v < vmax; ++v)
      for (auto u = umin; u < umax; ++u)
        saddle_map(u, v) = 1;

    saddle_points_filtered.push_back(p);
  }
  saddle_points_filtered.swap(saddle_points);
}

// Adaptive thresholding using Otsu's method.
// Implemented from Wikipedia's explanation.
auto otsu_adaptive_binarization(const sara::Image<float>& image)
{
  const auto image_quantized = image.convert<std::uint8_t>();

  // Calculate the discrete probability distribution function.
  Eigen::Array<float, 256, 1> pdf = Eigen::Array<float, 256, 1>::Zero();
  for (const auto& pixel : image_quantized)
    pdf(pixel) += 1;
  pdf /= pdf.sum();

  // Calculate the cumulated distribution function for the 0 class (i.e., the
  // black color class).
  auto cdf = Eigen::Array<float, 256, 1>{};
  std::partial_sum(pdf.data(), pdf.data() + pdf.size(), cdf.data());

  // The mean intensity value.
  const auto mean = [&pdf]() {
    auto m = 0.f;
    for (auto t = 0; t < pdf.size(); ++t)
      m += t * pdf(t);
    return m;
  }();

  auto t = 0;

  auto cdf_0 = cdf(0);
  auto cdf_1 = 1 - cdf_0;

  auto mean_0_unnormalized = 0;
  auto mean_1_unnormalized = mean;

  auto mean_0 = mean_0_unnormalized;
  auto mean_1 = mean;

  auto best_t = cdf_0 * cdf_1 * std::pow(mean_0 - mean_1, 2);
  auto best_score = cdf_0 * cdf_1 * std::pow(mean_0 - mean_1, 2);

  for (t = 1; t < 256; ++t)
  {
    cdf_0 = cdf(t);
    cdf_1 = 1 - cdf_0;

    mean_0_unnormalized += t * pdf(t);
    mean_1_unnormalized -= t * pdf(t);

    mean_0 = mean_0_unnormalized / cdf_0;
    mean_1 = mean_1_unnormalized / cdf_1;

    const auto score = cdf_0 * cdf_1 * std::pow(mean_0 - mean_1, 2);
    if (score > best_score)
    {
      best_t = t;
      best_score = score;
    }
  }

  auto binary = sara::Image<std::uint8_t>{image.sizes()};
  std::transform(image_quantized.begin(), image_quantized.end(), binary.begin(),
                 [best_t](const auto& v) { return v < best_t ? 0 : 255; });

  return binary;
}

auto erode(const sara::Image<std::uint8_t>& image, int radius)
{
  auto image_eroded = sara::Image<std::uint8_t>{image.sizes()};

  for (auto y = radius; y < image.height() - radius; ++y)
  {
    for (auto x = radius; x < image.width() - radius; ++x)
    {
      auto value = image(x, y);
      for (auto v = y - radius; v <= y + radius; ++v)
        for (auto u = x - radius; u <= x + radius; ++u)
          value = std::min(value, image(u, v));
      image_eroded(x, y) = value;
    }
  }

  return image_eroded;
}

auto dilate(const sara::Image<std::uint8_t>& image, int radius)
{
  auto image_dilated = sara::Image<std::uint8_t>{image.sizes()};

  for (auto y = radius; y < image.height() - radius; ++y)
  {
    for (auto x = radius; x < image.width() - radius; ++x)
    {
      auto value = image(x, y);
      for (auto v = y - radius; v <= y + radius; ++v)
        for (auto u = x - radius; u <= x + radius; ++u)
          value = std::max(value, image(u, v));
      image_dilated(x, y) = value;
    }
  }

  return image_dilated;
}

auto mean_colors(const std::map<int, std::vector<Eigen::Vector2i>>& regions,
                 const sara::Image<sara::Rgb8>& image)
{
  auto colors = std::map<int, sara::Rgb8>{};
  for (const auto& [label, points] : regions)
  {
    const auto num_points = points.size();
    Eigen::Vector3f color = Eigen::Vector3f::Zero();
    for (const auto& p : points)
      color += image(p).cast<float>();
    color /= num_points;

    colors[label] = color.cast<std::uint8_t>();
  }
  return colors;
}

// ========================================================================== //
auto edge_signature(const sara::Image<sara::Rgb8>& color,
                    const sara::Image<Eigen::Vector2f>& gradients,
                    const std::vector<Eigen::Vector2i>& edge,  //
                    float delta = 1, int width = 3)
{
  auto darks = std::vector<sara::Rgb64f>{};
  auto brights = std::vector<sara::Rgb64f>{};
  for (auto s = 1; s <= width; ++s)
  {
    for (const auto& e : edge)
    {
      const Eigen::Vector2d n = gradients(e).cast<double>().normalized();

      const Eigen::Vector2d b = e.cast<double>() + s * delta * n;
      const Eigen::Vector2d d = e.cast<double>() - s * delta * n;

      darks.push_back(sara::interpolate(color, d));
      brights.push_back(sara::interpolate(color, b));
    }
  }

  Eigen::Vector2f mean_gradient = Eigen::Vector2f::Zero();
  std::accumulate(
      edge.begin(), edge.end(), mean_gradient,
      [&gradients](const auto& g, const auto& e) { return g + gradients(e); });
  mean_gradient /= edge.size();

  return std::make_tuple(darks, brights, mean_gradient);
}

int __main(int argc, char** argv)
{
  using namespace std::string_literals;

  const auto folder = std::string{argv[1]};

  constexpr auto sigma = 1.6f;
  constexpr auto nms_radius = 5;

  constexpr float high_threshold_ratio = static_cast<float>(10._percent);
  constexpr float low_threshold_ratio =
      static_cast<float>(high_threshold_ratio / 2.);
  constexpr float angular_threshold = static_cast<float>((20._deg).value);

  const auto color_threshold = std::sqrt(std::pow(2, 2) * 3);
  const auto segment_min_size = 50;

  auto ed = sara::EdgeDetector{{high_threshold_ratio,  //
                                low_threshold_ratio,   //
                                angular_threshold,     //
                                false}};

  for (auto i = 0; i <= 1790; i += 10)
  {
    const auto image_filepath = folder + "/" + std::to_string(i) + ".png";

    auto image = sara::imread<sara::Rgb8>(image_filepath);
    const auto image_gray = image.convert<float>();
    const auto image_blurred = image_gray.compute<sara::Gaussian>(sigma);

    // Calculate the first derivative.
    const auto gradient = image_blurred.compute<sara::Gradient>();

    // Chessboard corners are saddle points of the image, which are
    // characterized by the property det(H(x, y)) < 0.
    const auto det_hessian =
        image_blurred.compute<sara::Hessian>().compute<sara::Determinant>();

    // Adaptive thresholding.
    const auto thres = det_hessian.flat_array().minCoeff() * 0.05f;
    auto saddle_points = extract_saddle_points(det_hessian, thres);

    // Non-maxima suppression.
    nms(saddle_points, image.sizes(), nms_radius);

    if (sara::active_window() == nullptr)
    {
      sara::create_window(image.sizes());
      sara::set_antialiasing();
    }

    // Watershed.
    const auto regions = sara::color_watershed(image, color_threshold);

    // Display the good regions.
    const auto colors = mean_colors(regions, image);
    auto partitioning = sara::Image<sara::Rgb8>{image.sizes()};
    for (const auto& [label, points] : regions)
    {
      // Show big segments only.
      for (const auto& p : points)
        partitioning(p) = points.size() < segment_min_size  //
                              ? sara::Black8
                              : colors.at(label);
    }

    ed(image_blurred);
    const auto& edges = ed.pipeline.edges_as_list;

    sara::display(partitioning);
    for (const auto& e : edges)
    {
      const auto color = sara::Rgb8(rand() % 255, rand() % 255, rand() % 255);
      for (const auto& p : e)
        sara::fill_circle(p.x(), p.y(), 2, color);
    }

    for (const auto& p : saddle_points)
      sara::draw_circle(p.p, 5, sara::Green8, 2);

    sara::get_key();
  }

  return 0;
}

int main(int argc, char** argv)
{
  DO::Sara::GraphicsApplication app(argc, argv);
  app.register_user_main(__main);
  return app.exec();
}

<commit_msg>WIP: save work.<commit_after>#include <DO/Sara/Core/PhysicalQuantities.hpp>
#include <DO/Sara/DisjointSets/TwoPassConnectedComponents.hpp>
#include <DO/Sara/FeatureDetectors/EdgeDetector.hpp>
#include <DO/Sara/FeatureDetectors/EdgePostProcessing.hpp>
#include <DO/Sara/FeatureDetectors/EdgeUtilities.hpp>
#include <DO/Sara/Geometry/Algorithms/Polyline.hpp>
#include <DO/Sara/Graphics.hpp>
#include <DO/Sara/ImageIO.hpp>
#include <DO/Sara/ImageProcessing.hpp>

namespace sara = DO::Sara;
using sara::operator""_deg;

inline constexpr long double operator"" _percent(long double x)
{
  return x / 100;
}

// ========================================================================== //
// Chess corners are saddle points so the determinant of hessian is a good
// mathematical criterion
// ========================================================================== //
struct Saddle
{
  Eigen::Vector2i p;
  float score;

  auto operator<(const Saddle& other) const
  {
    return score < other.score;
  }
};

auto extract_saddle_points(const sara::Image<float>& hessian, float thres)
{
  auto saddle_points = std::vector<Saddle>{};
  saddle_points.reserve(std::max(hessian.width(), hessian.height()));
  for (auto y = 0; y < hessian.height(); ++y)
    for (auto x = 0; x < hessian.width(); ++x)
      if (hessian(x, y) < thres)
        saddle_points.push_back({{x, y}, std::abs(hessian(x, y))});

  return saddle_points;
}

auto nms(std::vector<Saddle>& saddle_points, const Eigen::Vector2i& image_sizes,
         int nms_radius)
{
  std::sort(saddle_points.begin(), saddle_points.end());

  auto saddle_points_filtered = std::vector<Saddle>{};
  saddle_points_filtered.reserve(saddle_points.size());

  auto saddle_map = sara::Image<std::uint8_t>{image_sizes};
  saddle_map.flat_array().fill(0);

  for (const auto& p : saddle_points)
  {
    if (saddle_map(p.p) == 1)
      continue;

    const auto vmin = std::clamp(p.p.y() - nms_radius, 0, saddle_map.height());
    const auto vmax = std::clamp(p.p.y() + nms_radius, 0, saddle_map.height());
    const auto umin = std::clamp(p.p.x() - nms_radius, 0, saddle_map.width());
    const auto umax = std::clamp(p.p.x() + nms_radius, 0, saddle_map.width());
    for (auto v = vmin; v < vmax; ++v)
      for (auto u = umin; u < umax; ++u)
        saddle_map(u, v) = 1;

    saddle_points_filtered.push_back(p);
  }
  saddle_points_filtered.swap(saddle_points);
}

// Adaptive thresholding using Otsu's method.
// Implemented from Wikipedia's explanation.
auto otsu_adaptive_binarization(const sara::Image<float>& image)
{
  const auto image_quantized = image.convert<std::uint8_t>();

  // Calculate the discrete probability distribution function.
  Eigen::Array<float, 256, 1> pdf = Eigen::Array<float, 256, 1>::Zero();
  for (const auto& pixel : image_quantized)
    pdf(pixel) += 1;
  pdf /= pdf.sum();

  // Calculate the cumulated distribution function for the 0 class (i.e., the
  // black color class).
  auto cdf = Eigen::Array<float, 256, 1>{};
  std::partial_sum(pdf.data(), pdf.data() + pdf.size(), cdf.data());

  // The mean intensity value.
  const auto mean = [&pdf]() {
    auto m = 0.f;
    for (auto t = 0; t < pdf.size(); ++t)
      m += t * pdf(t);
    return m;
  }();

  auto t = 0;

  auto cdf_0 = cdf(0);
  auto cdf_1 = 1 - cdf_0;

  auto mean_0_unnormalized = 0;
  auto mean_1_unnormalized = mean;

  auto mean_0 = mean_0_unnormalized;
  auto mean_1 = mean;

  auto best_t = cdf_0 * cdf_1 * std::pow(mean_0 - mean_1, 2);
  auto best_score = cdf_0 * cdf_1 * std::pow(mean_0 - mean_1, 2);

  for (t = 1; t < 256; ++t)
  {
    cdf_0 = cdf(t);
    cdf_1 = 1 - cdf_0;

    mean_0_unnormalized += t * pdf(t);
    mean_1_unnormalized -= t * pdf(t);

    mean_0 = mean_0_unnormalized / cdf_0;
    mean_1 = mean_1_unnormalized / cdf_1;

    const auto score = cdf_0 * cdf_1 * std::pow(mean_0 - mean_1, 2);
    if (score > best_score)
    {
      best_t = t;
      best_score = score;
    }
  }

  auto binary = sara::Image<std::uint8_t>{image.sizes()};
  std::transform(image_quantized.begin(), image_quantized.end(), binary.begin(),
                 [best_t](const auto& v) { return v < best_t ? 0 : 255; });

  return binary;
}

auto erode(const sara::Image<std::uint8_t>& image, int radius)
{
  auto image_eroded = sara::Image<std::uint8_t>{image.sizes()};

  for (auto y = radius; y < image.height() - radius; ++y)
  {
    for (auto x = radius; x < image.width() - radius; ++x)
    {
      auto value = image(x, y);
      for (auto v = y - radius; v <= y + radius; ++v)
        for (auto u = x - radius; u <= x + radius; ++u)
          value = std::min(value, image(u, v));
      image_eroded(x, y) = value;
    }
  }

  return image_eroded;
}

auto dilate(const sara::Image<std::uint8_t>& image, int radius)
{
  auto image_dilated = sara::Image<std::uint8_t>{image.sizes()};

  for (auto y = radius; y < image.height() - radius; ++y)
  {
    for (auto x = radius; x < image.width() - radius; ++x)
    {
      auto value = image(x, y);
      for (auto v = y - radius; v <= y + radius; ++v)
        for (auto u = x - radius; u <= x + radius; ++u)
          value = std::max(value, image(u, v));
      image_dilated(x, y) = value;
    }
  }

  return image_dilated;
}

auto mean_colors(const std::map<int, std::vector<Eigen::Vector2i>>& regions,
                 const sara::Image<sara::Rgb8>& image)
{
  auto colors = std::map<int, sara::Rgb8>{};
  for (const auto& [label, points] : regions)
  {
    const auto num_points = points.size();
    Eigen::Vector3f color = Eigen::Vector3f::Zero();
    for (const auto& p : points)
      color += image(p).cast<float>();
    color /= num_points;

    colors[label] = color.cast<std::uint8_t>();
  }
  return colors;
}

// ========================================================================== //
auto edge_signature(const sara::Image<sara::Rgb8>& color,
                    const sara::Image<Eigen::Vector2f>& gradients,
                    const std::vector<Eigen::Vector2i>& edge,  //
                    float delta = 1, int width = 3)
{
  auto darks = std::vector<sara::Rgb64f>{};
  auto brights = std::vector<sara::Rgb64f>{};
  for (auto s = 1; s <= width; ++s)
  {
    for (const auto& e : edge)
    {
      const Eigen::Vector2d n = gradients(e).cast<double>().normalized();

      const Eigen::Vector2d b = e.cast<double>() + s * delta * n;
      const Eigen::Vector2d d = e.cast<double>() - s * delta * n;

      if (0 <= d.x() && d.x() < color.width() &&  //
          0 <= d.y() && d.y() < color.height())
        darks.push_back(sara::interpolate(color, d));

      if (0 <= b.x() && b.x() < color.width() &&  //
          0 <= b.y() && b.y() < color.height())
        brights.push_back(sara::interpolate(color, b));
    }
  }

  Eigen::Vector2f mean_gradient = Eigen::Vector2f::Zero();
  mean_gradient = std::accumulate(edge.begin(), edge.end(), mean_gradient,
                                  [&gradients](const auto& g, const auto& e) {
                                    return g + gradients(e).normalized();  //
                                  });
  mean_gradient /= edge.size();

  return std::make_tuple(darks, brights, mean_gradient);
}

int __main(int argc, char** argv)
{
  using namespace std::string_literals;

  const auto folder = std::string{argv[1]};

  constexpr auto sigma = 1.6f;
  constexpr auto nms_radius = 5;

  constexpr float high_threshold_ratio = static_cast<float>(10._percent);
  constexpr float low_threshold_ratio =
      static_cast<float>(high_threshold_ratio / 2.);
  constexpr float angular_threshold = static_cast<float>((10._deg).value);

  const auto color_threshold = std::sqrt(std::pow(2, 2) * 3);
  const auto segment_min_size = 50;

  auto ed = sara::EdgeDetector{{high_threshold_ratio,  //
                                low_threshold_ratio,   //
                                angular_threshold,     //
                                false}};

  for (auto i = 0; i <= 1790; i += 10)
  {
    const auto image_filepath = folder + "/" + std::to_string(i) + ".png";

    auto image = sara::imread<sara::Rgb8>(image_filepath);
    const auto image_gray = image.convert<float>();
    const auto image_blurred = image_gray.compute<sara::Gaussian>(sigma);

    // Calculate the first derivative.
    const auto gradient = image_blurred.compute<sara::Gradient>();

    // Chessboard corners are saddle points of the image, which are
    // characterized by the property det(H(x, y)) < 0.
    const auto det_hessian =
        image_blurred.compute<sara::Hessian>().compute<sara::Determinant>();

    // Adaptive thresholding.
    const auto thres = det_hessian.flat_array().minCoeff() * 0.05f;
    auto saddle_points = extract_saddle_points(det_hessian, thres);

    // Non-maxima suppression.
    nms(saddle_points, image.sizes(), nms_radius);

    if (sara::active_window() == nullptr)
    {
      sara::create_window(image.sizes());
      sara::set_antialiasing();
    }

    // Watershed to find the chessboard quadrangles.
    const auto regions = sara::color_watershed(image, color_threshold);

    // Group segments.
    ed(image_blurred);
    const auto& edges = ed.pipeline.edges_as_list;

    auto darks = std::vector<std::vector<sara::Rgb64f>>{};
    auto brights = std::vector<std::vector<sara::Rgb64f>>{};
    auto mean_gradients = std::vector<Eigen::Vector2f>{};
    for (const auto& edge : edges)
    {
      auto [dark, bright, g] = edge_signature(  //
          image,                                //
          ed.pipeline.gradient_cartesian,       //
          edge);
      darks.push_back(std::move(dark));
      brights.push_back(std::move(bright));
      mean_gradients.push_back(g);
    }

    // const auto colors = mean_colors(regions, image);
    // auto partitioning = sara::Image<sara::Rgb8>{image.sizes()};
    // for (const auto& [label, points] : regions)
    // {
    //   // Show big segments only.
    //   for (const auto& p : points)
    //     partitioning(p) = points.size() < segment_min_size  //
    //                           ? sara::Black8
    //                           : colors.at(label);
    // }

    // sara::display(partitioning);

    sara::display(image);
    for (const auto& e : edges)
    {
      const auto color = sara::Rgb8(rand() % 255, rand() % 255, rand() % 255);
      for (const auto& p : e)
        sara::fill_circle(p.x(), p.y(), 2, color);
    }

    for (auto i = 0u; i < edges.size(); ++i)
    {
      const auto& e = edges[i];

      // Discard small edges.
      if (e.size() < 2)
        continue;

      const auto& g = mean_gradients[i];
      const Eigen::Vector2f a = std::accumulate(            //
                                    e.begin(), e.end(),     //
                                    Eigen::Vector2f{0, 0},  //
                                    [](const auto& a, const auto& b) {
                                      return a + b.template cast<float>();
                                    }) /
                                e.size();
      const Eigen::Vector2f b = a + 20 * g;

      sara::draw_arrow(a, b, sara::Magenta8, 2);
    }

    // for (const auto& p : saddle_points)
    //   sara::draw_circle(p.p, 5, sara::Green8, 2);

    sara::get_key();
  }

  return 0;
}

int main(int argc, char** argv)
{
  DO::Sara::GraphicsApplication app(argc, argv);
  app.register_user_main(__main);
  return app.exec();
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 Samsung Electronics Co., Ltd.

// CLASS HEADER
#include "image-view-impl.h"

// EXTERNAL INCLUDES
#include <dali/public-api/images/resource-image.h>
#include <dali/public-api/object/type-registry.h>
#include <dali/devel-api/object/type-registry-helper.h>
#include <dali/devel-api/scripting/scripting.h>

// INTERNAL INCLUDES
#include <dali-toolkit/public-api/controls/image-view/image-view.h>
#include <dali-toolkit/devel-api/controls/renderer-factory/renderer-factory.h>
#include <dali-toolkit/internal/controls/renderers/image/image-renderer.h>

namespace Dali
{

namespace Toolkit
{

namespace Internal
{

namespace
{

BaseHandle Create()
{
  return Toolkit::ImageView::New();
}

// Setup properties, signals and actions using the type-registry.
DALI_TYPE_REGISTRATION_BEGIN( Toolkit::ImageView, Toolkit::Control, Create );
DALI_PROPERTY_REGISTRATION( Toolkit, ImageView, "image", MAP, IMAGE )
DALI_TYPE_REGISTRATION_END()

} // anonymous namespace

using namespace Dali;

ImageView::ImageView()
: Control( ControlBehaviour( ACTOR_BEHAVIOUR_NONE ) )
{
}

ImageView::~ImageView()
{
}

Toolkit::ImageView ImageView::New()
{
  ImageView* impl = new ImageView();

  Toolkit::ImageView handle = Toolkit::ImageView( *impl );

  // Second-phase init of the implementation
  // This can only be done after the CustomActor connection has been made...
  impl->Initialize();

  return handle;
}

/////////////////////////////////////////////////////////////

void ImageView::SetImage( Image image )
{
  if( mImage != image )
  {
    mUrl.clear();
    mPropertyMap.Clear();

    mImage = image;

    bool newRendererCreated = false;
    if( mRenderer )
    {
      newRendererCreated = Toolkit::RendererFactory::Get().ResetRenderer( mRenderer, image );
    }
    else
    {
      mRenderer = Toolkit::RendererFactory::Get().GetControlRenderer( image );
      newRendererCreated = true;
    }

    //we need to inform any newly created renderers if it is on stage
    if( newRendererCreated && Self().OnStage() )
    {
      CustomActor self = Self();
      mRenderer.SetOnStage( self );
    }

    mImageSize = image ? ImageDimensions( image.GetWidth(), image.GetHeight() ) : ImageDimensions( 0, 0 );
  }
}

void ImageView::SetImage( Property::Map map )
{
  mPropertyMap = map;

  bool newRendererCreated = false;
  if( mRenderer )
  {
    newRendererCreated = Toolkit::RendererFactory::Get().ResetRenderer( mRenderer, mPropertyMap );
  }
  else
  {
    mRenderer = Toolkit::RendererFactory::Get().GetControlRenderer( mPropertyMap );
    newRendererCreated = true;
  }

  //we need to inform any newly created renderers if it is on stage
  CustomActor self = Self();
  if( newRendererCreated && self.OnStage() )
  {
    mRenderer.SetOnStage( self );
  }

  int width = 0;
  Property::Value* widthValue = mPropertyMap.Find( "width" );
  if( widthValue )
  {
    widthValue->Get( width );
  }

  int height = 0;
  Property::Value* heightValue = mPropertyMap.Find( "height" );
  if( heightValue )
  {
    heightValue->Get( height );
  }

  mImageSize = ImageDimensions( width, height );
}

void ImageView::SetImage( const std::string& url )
{
  if( mUrl != url )
  {
    mImage.Reset();
    mPropertyMap.Clear();

    mUrl = url;

    bool newRendererCreated = false;
    if( mRenderer )
    {
      newRendererCreated = Toolkit::RendererFactory::Get().ResetRenderer( mRenderer, mUrl );
    }
    else
    {
      mRenderer = Toolkit::RendererFactory::Get().GetControlRenderer( mUrl );
      newRendererCreated = true;
    }

    //we need to inform any newly created renderers if it is on stage
    if( newRendererCreated && Self().OnStage() )
    {
      CustomActor self = Self();
      mRenderer.SetOnStage( self );
    }

    mImageSize = ResourceImage::GetImageSize( mUrl );
  }
}

Vector3 ImageView::GetNaturalSize()
{
  Vector3 size;

  size.x = mImageSize.GetWidth();
  size.y = mImageSize.GetHeight();
  size.z = std::min(size.x, size.y);

  if( size.x > 0 && size.x > 0 )
  {
    return size;
  }
  else
  {
    // if no image then use Control's natural size
    return Control::GetNaturalSize();
  }
}

float ImageView::GetHeightForWidth( float width )
{
  if( mImageSize.GetWidth() > 0 && mImageSize.GetHeight() > 0 )
  {
    return GetHeightForWidthBase( width );
  }
  else
  {
    return Control::GetHeightForWidth( width );
  }
}

float ImageView::GetWidthForHeight( float height )
{
  if( mImageSize.GetWidth() > 0 && mImageSize.GetHeight() > 0 )
  {
    return GetWidthForHeightBase( height );
  }
  else
  {
    return Control::GetWidthForHeight( height );
  }
}

///////////////////////////////////////////////////////////
//
// Private methods
//

void ImageView::OnStageConnection( int depth )
{
  Control::OnStageConnection( depth );

  if( mRenderer )
  {
    CustomActor self = Self();
    mRenderer.SetOnStage( self );
  }
}

void ImageView::OnStageDisconnection()
{
  if( mRenderer )
  {
    CustomActor self = Self();
    mRenderer.SetOffStage( self );
  }

  Control::OnStageDisconnection();
}

///////////////////////////////////////////////////////////
//
// Properties
//

void ImageView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
{
  Toolkit::ImageView imageView = Toolkit::ImageView::DownCast( Dali::BaseHandle( object ) );

  if ( imageView )
  {
    switch ( index )
    {
      case Toolkit::ImageView::Property::IMAGE:
      {
        std::string imageUrl;
        if( value.Get( imageUrl ) )
        {
          ImageView& impl = GetImpl( imageView );
          impl.SetImage( imageUrl );
        }

        // if its not a string then get a Property::Map from the property if possible.
        Property::Map map;
        if( value.Get( map ) )
        {
          ImageView& impl = GetImpl( imageView );
          impl.SetImage( map );
        }

        break;
      }
    }
  }
}

Property::Value ImageView::GetProperty( BaseObject* object, Property::Index propertyIndex )
{
  Property::Value value;

  Toolkit::ImageView imageview = Toolkit::ImageView::DownCast( Dali::BaseHandle( object ) );

  if ( imageview )
  {
    switch ( propertyIndex )
    {
      case Toolkit::ImageView::Property::IMAGE:
      {
        ImageView& impl = GetImpl( imageview );
        if ( !impl.mUrl.empty() )
        {
          value = impl.mUrl;
        }
        else if( impl.mImage )
        {
          Property::Map map;
          Scripting::CreatePropertyMap( impl.mImage, map );
          value = map;
        }
        else if( !impl.mPropertyMap.Empty() )
        {
          value = impl.mPropertyMap;
        }
        break;
      }
    }
  }

  return value;
}

} // namespace Internal
} // namespace Toolkit
} // namespace Dali
<commit_msg>Fixed typo in ImageView::GetNaturalSize().<commit_after>// Copyright (c) 2014 Samsung Electronics Co., Ltd.

// CLASS HEADER
#include "image-view-impl.h"

// EXTERNAL INCLUDES
#include <dali/public-api/images/resource-image.h>
#include <dali/public-api/object/type-registry.h>
#include <dali/devel-api/object/type-registry-helper.h>
#include <dali/devel-api/scripting/scripting.h>

// INTERNAL INCLUDES
#include <dali-toolkit/public-api/controls/image-view/image-view.h>
#include <dali-toolkit/devel-api/controls/renderer-factory/renderer-factory.h>
#include <dali-toolkit/internal/controls/renderers/image/image-renderer.h>

namespace Dali
{

namespace Toolkit
{

namespace Internal
{

namespace
{

BaseHandle Create()
{
  return Toolkit::ImageView::New();
}

// Setup properties, signals and actions using the type-registry.
DALI_TYPE_REGISTRATION_BEGIN( Toolkit::ImageView, Toolkit::Control, Create );
DALI_PROPERTY_REGISTRATION( Toolkit, ImageView, "image", MAP, IMAGE )
DALI_TYPE_REGISTRATION_END()

} // anonymous namespace

using namespace Dali;

ImageView::ImageView()
: Control( ControlBehaviour( ACTOR_BEHAVIOUR_NONE ) )
{
}

ImageView::~ImageView()
{
}

Toolkit::ImageView ImageView::New()
{
  ImageView* impl = new ImageView();

  Toolkit::ImageView handle = Toolkit::ImageView( *impl );

  // Second-phase init of the implementation
  // This can only be done after the CustomActor connection has been made...
  impl->Initialize();

  return handle;
}

/////////////////////////////////////////////////////////////

void ImageView::SetImage( Image image )
{
  if( mImage != image )
  {
    mUrl.clear();
    mPropertyMap.Clear();

    mImage = image;

    bool newRendererCreated = false;
    if( mRenderer )
    {
      newRendererCreated = Toolkit::RendererFactory::Get().ResetRenderer( mRenderer, image );
    }
    else
    {
      mRenderer = Toolkit::RendererFactory::Get().GetControlRenderer( image );
      newRendererCreated = true;
    }

    //we need to inform any newly created renderers if it is on stage
    if( newRendererCreated && Self().OnStage() )
    {
      CustomActor self = Self();
      mRenderer.SetOnStage( self );
    }

    mImageSize = image ? ImageDimensions( image.GetWidth(), image.GetHeight() ) : ImageDimensions( 0, 0 );
  }
}

void ImageView::SetImage( Property::Map map )
{
  mPropertyMap = map;

  bool newRendererCreated = false;
  if( mRenderer )
  {
    newRendererCreated = Toolkit::RendererFactory::Get().ResetRenderer( mRenderer, mPropertyMap );
  }
  else
  {
    mRenderer = Toolkit::RendererFactory::Get().GetControlRenderer( mPropertyMap );
    newRendererCreated = true;
  }

  //we need to inform any newly created renderers if it is on stage
  CustomActor self = Self();
  if( newRendererCreated && self.OnStage() )
  {
    mRenderer.SetOnStage( self );
  }

  int width = 0;
  Property::Value* widthValue = mPropertyMap.Find( "width" );
  if( widthValue )
  {
    widthValue->Get( width );
  }

  int height = 0;
  Property::Value* heightValue = mPropertyMap.Find( "height" );
  if( heightValue )
  {
    heightValue->Get( height );
  }

  mImageSize = ImageDimensions( width, height );
}

void ImageView::SetImage( const std::string& url )
{
  if( mUrl != url )
  {
    mImage.Reset();
    mPropertyMap.Clear();

    mUrl = url;

    bool newRendererCreated = false;
    if( mRenderer )
    {
      newRendererCreated = Toolkit::RendererFactory::Get().ResetRenderer( mRenderer, mUrl );
    }
    else
    {
      mRenderer = Toolkit::RendererFactory::Get().GetControlRenderer( mUrl );
      newRendererCreated = true;
    }

    //we need to inform any newly created renderers if it is on stage
    if( newRendererCreated && Self().OnStage() )
    {
      CustomActor self = Self();
      mRenderer.SetOnStage( self );
    }

    mImageSize = ResourceImage::GetImageSize( mUrl );
  }
}

Vector3 ImageView::GetNaturalSize()
{
  Vector3 size;

  size.x = mImageSize.GetWidth();
  size.y = mImageSize.GetHeight();
  size.z = std::min(size.x, size.y);

  if( size.x > 0 && size.y > 0 )
  {
    return size;
  }
  else
  {
    // if no image then use Control's natural size
    return Control::GetNaturalSize();
  }
}

float ImageView::GetHeightForWidth( float width )
{
  if( mImageSize.GetWidth() > 0 && mImageSize.GetHeight() > 0 )
  {
    return GetHeightForWidthBase( width );
  }
  else
  {
    return Control::GetHeightForWidth( width );
  }
}

float ImageView::GetWidthForHeight( float height )
{
  if( mImageSize.GetWidth() > 0 && mImageSize.GetHeight() > 0 )
  {
    return GetWidthForHeightBase( height );
  }
  else
  {
    return Control::GetWidthForHeight( height );
  }
}

///////////////////////////////////////////////////////////
//
// Private methods
//

void ImageView::OnStageConnection( int depth )
{
  Control::OnStageConnection( depth );

  if( mRenderer )
  {
    CustomActor self = Self();
    mRenderer.SetOnStage( self );
  }
}

void ImageView::OnStageDisconnection()
{
  if( mRenderer )
  {
    CustomActor self = Self();
    mRenderer.SetOffStage( self );
  }

  Control::OnStageDisconnection();
}

///////////////////////////////////////////////////////////
//
// Properties
//

void ImageView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
{
  Toolkit::ImageView imageView = Toolkit::ImageView::DownCast( Dali::BaseHandle( object ) );

  if ( imageView )
  {
    switch ( index )
    {
      case Toolkit::ImageView::Property::IMAGE:
      {
        std::string imageUrl;
        if( value.Get( imageUrl ) )
        {
          ImageView& impl = GetImpl( imageView );
          impl.SetImage( imageUrl );
        }

        // if its not a string then get a Property::Map from the property if possible.
        Property::Map map;
        if( value.Get( map ) )
        {
          ImageView& impl = GetImpl( imageView );
          impl.SetImage( map );
        }

        break;
      }
    }
  }
}

Property::Value ImageView::GetProperty( BaseObject* object, Property::Index propertyIndex )
{
  Property::Value value;

  Toolkit::ImageView imageview = Toolkit::ImageView::DownCast( Dali::BaseHandle( object ) );

  if ( imageview )
  {
    switch ( propertyIndex )
    {
      case Toolkit::ImageView::Property::IMAGE:
      {
        ImageView& impl = GetImpl( imageview );
        if ( !impl.mUrl.empty() )
        {
          value = impl.mUrl;
        }
        else if( impl.mImage )
        {
          Property::Map map;
          Scripting::CreatePropertyMap( impl.mImage, map );
          value = map;
        }
        else if( !impl.mPropertyMap.Empty() )
        {
          value = impl.mPropertyMap;
        }
        break;
      }
    }
  }

  return value;
}

} // namespace Internal
} // namespace Toolkit
} // namespace Dali
<|endoftext|>


<commit_before>AliAnalysisTask *AddTaskJetChem(){

  //cout<<" OB : add JetTasks inlcude path ! "<<endl;
  //gSystem->AddIncludePath("-I$ALICE_ROOT/PWG4/JetTasks");

  //get the current analysis manager
  AliAnalysisManager *mgr = AliAnalysisManager::GetAnalysisManager();
  if (!mgr) {
    Error("AddTask_obusch_jets", "No analysis manager found.");
    return 0;
  }

  // physics event selection task - not for AOD analysis

  //gROOT->LoadMacro("$ALICE_ROOT/ANALYSIS/macros/AddTaskPhysicsSelection.C");
  //AliPhysicsSelectionTask* physSelTask = AddTaskPhysicsSelection();
  //AliPhysicsSelection* physSel = physSelTask->GetPhysicsSelection();
  //physSel->AddBackgroundIdentification(new AliBackgroundSelection());
  //physSel->SetAnalyzeMC();

  /*
  //============= Set Task Name ===================
  TString taskName=("AliAnalysisTaskJetChem.cxx+");
  //===============================================
  //            Load the task
  gROOT->LoadMacro(taskName.Data());
  if (gProof){
    TString taskSO=gSystem->pwd();
    taskSO+="/";
    taskSO+=taskName(0,taskName.First('.'))+"_cxx.so";
    gProof->Exec(Form("gSystem->Load(\"%s\")",taskSO.Data()),kTRUE);
  }
  */
  //========= Add task to the ANALYSIS manager =====
  AliAnalysisTaskJetChem *task = new AliAnalysisTaskJetChem;

  // configure task

  task->SetUseLOConeJets();
  task->SetUseLOConeMCJets();

  //task->SetUsePythiaJets();
  task->SetConeRadius(0.4);
  task->SetTrackPtCutJF(0.150); //
  task->SetFilterBitJF(0x01);   // official PWG4 high pt filter bit 0x10, but not all AliAnalysisTaskESDFilter had configured this ESDTrackCut
  task->SetRequireITSRefitJF(); // 0x01 + ITS refit = 0x10
  task->SetRejectK0TracksJF();  // uncomment for K0 analysis running jet finder in task - modifies jet spectrum

  task->SetJetPtCut(2.0);
  //task->SetJetPtCut(0.150); // lower pt cut: for plot of diffractive contribution to jet spectrum (goes up to 2 GeV ...)
  task->SetJetEtaCut(0.5);

  task->SetFilterBit((UInt_t) 0X01); // std AOD track cuts
  task->SetTrackPtCut(0.150);
  task->SetTrackEtaCut(0.9);

  task->SetUseOnFlyV0s();
  task->SetCutnSigdEdx(2);

  //task->ReadDeltaAOD(); // uncomment for DeltaAODs
  //task->SelectDeltaAODBranch("bla");
  //task->SelectAODBranch("jetsAOD_FASTKT04");
  //task->SelectAODBranch("jetsAOD_UA107");
  //task->SelectAODBranch("jets");

  //task->SelectCollisionCandidates(); // either here or in userExec of task - but not for AODs ...

  //  AliLog::SetGlobalLogLevel(AliLog::kInfo);    // kInfo // kDebug // kFatal
  //  task->SetDebugLevel(10);
  //  mgr->SetDebugLevel(10);

  mgr->AddTask(task);

  //================================================
  //              data containers
  //================================================
  //            find input container
  //below the trunk version
  AliAnalysisDataContainer *cinput  = mgr->GetCommonInputContainer();
  AliAnalysisDataContainer *coutput = mgr->CreateContainer("PWG4_JetChem",
							   TList::Class(), AliAnalysisManager::kOutputContainer,
							   Form("%s:PWG4_JetChem",AliAnalysisManager::GetCommonFileName()));

  mgr->ConnectInput(task,0,cinput );
  mgr->ConnectOutput(task,0,coutput);

  return task;
}
<commit_msg>correcting conenction of output container<commit_after>AliAnalysisTask *AddTaskJetChem(){

  //cout<<" OB : add JetTasks inlcude path ! "<<endl;
  //gSystem->AddIncludePath("-I$ALICE_ROOT/PWG4/JetTasks");

  //get the current analysis manager
  AliAnalysisManager *mgr = AliAnalysisManager::GetAnalysisManager();
  if (!mgr) {
    Error("AddTask_obusch_jets", "No analysis manager found.");
    return 0;
  }

  // physics event selection task - not for AOD analysis

  //gROOT->LoadMacro("$ALICE_ROOT/ANALYSIS/macros/AddTaskPhysicsSelection.C");
  //AliPhysicsSelectionTask* physSelTask = AddTaskPhysicsSelection();
  //AliPhysicsSelection* physSel = physSelTask->GetPhysicsSelection();
  //physSel->AddBackgroundIdentification(new AliBackgroundSelection());
  //physSel->SetAnalyzeMC();

  /*
  //============= Set Task Name ===================
  TString taskName=("AliAnalysisTaskJetChem.cxx+");
  //===============================================
  //            Load the task
  gROOT->LoadMacro(taskName.Data());
  if (gProof){
    TString taskSO=gSystem->pwd();
    taskSO+="/";
    taskSO+=taskName(0,taskName.First('.'))+"_cxx.so";
    gProof->Exec(Form("gSystem->Load(\"%s\")",taskSO.Data()),kTRUE);
  }
  */
  //========= Add task to the ANALYSIS manager =====
  AliAnalysisTaskJetChem *task = new AliAnalysisTaskJetChem;

  // configure task

  task->SetUseLOConeJets();
  task->SetUseLOConeMCJets();

  //task->SetUsePythiaJets();
  task->SetConeRadius(0.4);
  task->SetTrackPtCutJF(0.150); //
  task->SetFilterBitJF(0x01);   // official PWG4 high pt filter bit 0x10, but not all AliAnalysisTaskESDFilter had configured this ESDTrackCut
  task->SetRequireITSRefitJF(); // 0x01 + ITS refit = 0x10
  task->SetRejectK0TracksJF();  // uncomment for K0 analysis running jet finder in task - modifies jet spectrum

  task->SetJetPtCut(2.0);
  //task->SetJetPtCut(0.150); // lower pt cut: for plot of diffractive contribution to jet spectrum (goes up to 2 GeV ...)
  task->SetJetEtaCut(0.5);

  task->SetFilterBit((UInt_t) 0X01); // std AOD track cuts
  task->SetTrackPtCut(0.150);
  task->SetTrackEtaCut(0.9);

  task->SetUseOnFlyV0s();
  task->SetCutnSigdEdx(2);

  //task->ReadDeltaAOD(); // uncomment for DeltaAODs
  //task->SelectDeltaAODBranch("bla");
  //task->SelectAODBranch("jetsAOD_FASTKT04");
  //task->SelectAODBranch("jetsAOD_UA107");
  //task->SelectAODBranch("jets");

  //task->SelectCollisionCandidates(); // either here or in userExec of task - but not for AODs ...

  //  AliLog::SetGlobalLogLevel(AliLog::kInfo);    // kInfo // kDebug // kFatal
  //  task->SetDebugLevel(10);
  //  mgr->SetDebugLevel(10);

  mgr->AddTask(task);

  //================================================
  //              data containers
  //================================================
  //            find input container
  //below the trunk version
  AliAnalysisDataContainer *cinput  = mgr->GetCommonInputContainer();
  AliAnalysisDataContainer *coutput = mgr->CreateContainer("PWG4_JetChem",
							   TList::Class(), AliAnalysisManager::kOutputContainer,
							   Form("%s:PWG4_JetChem",AliAnalysisManager::GetCommonFileName()));

  mgr->ConnectInput(task,0,cinput );
  mgr->ConnectOutput(task,1,coutput);

  return task;
}
<|endoftext|>


<commit_before>// TestImage.cpp
//
// Image module tests.
//
// Copyright (c) 2017, 2019, 2020 Tristan Grimmer.
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
// granted, provided that the above copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

#include <Image/tTexture.h>
#include <Image/tImageDDS.h>
#include <Image/tImageEXR.h>
#include <Image/tImageGIF.h>
#include <Image/tImageHDR.h>
#include <Image/tImageICO.h>
#include <Image/tImageTGA.h>
#include <Image/tImageJPG.h>
#include <Image/tImageWEBP.h>
#include <System/tFile.h>
#include "UnitTests.h"
using namespace tStd;
namespace tUnitTest
{

tTestUnit(Image)
{
	if (!tSystem::tDirExists("TestData/"))
		tSkipUnit(Image)

	// Test direct loading classes.
	tImage::tImageDDS imgDDS("TestData/TestDXT1.dds");
	tRequire(imgDDS.IsValid());

	tImage::tImageEXR imgEXR("TestData/Desk.exr");
	tRequire(imgEXR.IsValid());

	tImage::tImageGIF imgGIF("TestData/8-cell-simple.gif");
	tRequire(imgGIF.IsValid());

	tImage::tImageHDR imgHDR("TestData/mpi_atrium_3.hdr");
	tRequire(imgHDR.IsValid());

	tImage::tImageICO imgICO("TestData/UpperBounds.ico");
	tRequire(imgICO.IsValid());

	tImage::tImageTGA imgTGA("TestData/WhiteBorderRLE.tga");
	tRequire(imgTGA.IsValid());

	tImage::tImageJPG imgJPG("TestData/WiredDrives.jpg");
	tRequire(imgJPG.IsValid());

	tImage::tImageWEBP imgWEBP("TestData/RockyBeach.webp");
	tRequire(imgWEBP.IsValid());

	// Test dxt1 texture.
	tImage::tTexture dxt1Tex("TestData/TestDXT1.dds");
	tRequire(dxt1Tex.IsValid());

	tChunkWriter writer("TestData/WrittenTestDXT1.tac");
	dxt1Tex.Save(writer);
	tRequire( tSystem::tFileExists("TestData/WrittenTestDXT1.tac") );

	tChunkReader reader("TestData/WrittenTestDXT1.tac");
	dxt1Tex.Load( reader.Chunk() );
	tRequire(dxt1Tex.IsValid());

	// Test cubemap.
	tImage::tTexture cubemap("TestData/CubemapLayoutGuide.dds");
	tRequire(cubemap.IsValid());

	// Test jpg to texture. This will do conversion to BC1.
	tImage::tTexture bc1Tex("TestData/WiredDrives.jpg", true);
	tRequire(bc1Tex.IsValid());
	tChunkWriter chunkWriterBC1("TestData/WrittenBC1.tac");
	bc1Tex.Save(chunkWriterBC1);
	tRequire( tSystem::tFileExists("TestData/WrittenBC1.tac"));

	// Test ico with alpha to texture. This will do conversion to BC3.
	tImage::tTexture bc3Tex("TestData/UpperBounds.ico", true);
	tRequire(bc3Tex.IsValid());
	tChunkWriter chunkWriterBC3("TestData/WrittenBC3.tac");
	bc3Tex.Save(chunkWriterBC3);
	tRequire( tSystem::tFileExists("TestData/WrittenBC3.tac"));

	// Test tPicture loading jpg and saving as tga.
	tImage::tPicture jpgPic("TestData/WiredDrives.jpg");
	tRequire(jpgPic.IsValid());

	jpgPic.Save("TestData/WrittenWiredDrives.tga");
	tRequire( tSystem::tFileExists("TestData/WrittenWiredDrives.tga"));

	// Test tPicture loading png (with alpha channel) and saving as tga (with alpha channel).
	tImage::tPicture pngPic("TestData/Xeyes.png");
	tRequire(pngPic.IsValid());

	pngPic.SaveTGA("TestData/WrittenXeyes.tga");
	tRequire( tSystem::tFileExists("TestData/WrittenXeyes.tga"));

	// Test saving tPicture in all supported formats.
	pngPic.Save("TestData/WrittenXeyesTGA.tga");
	tRequire( tSystem::tFileExists("TestData/WrittenXeyesTGA.tga"));

	pngPic.Save("TestData/WrittenXeyesPNG.png");
	tRequire( tSystem::tFileExists("TestData/WrittenXeyesPNG.png"));

	pngPic.Save("TestData/WrittenXeyesBMP.bmp");
	tRequire( tSystem::tFileExists("TestData/WrittenXeyesBMP.bmp"));

	pngPic.Save("TestData/WrittenXeyesJPG.jpg");
	tRequire( tSystem::tFileExists("TestData/WrittenXeyesJPG.jpg"));

	#ifdef PLATFORM_WINDOWS
	pngPic.Save("TestData/WrittenXeyesGIF.gif");
	tRequire( tSystem::tFileExists("TestData/WrittenXeyesGIF.gif"));
	#else
	tGoal(!"Write to gif not working on this platform.");
	#endif
}

}
<commit_msg>Removed writing to gif from unit tests. It was never properly supported even on windows.<commit_after>// TestImage.cpp
//
// Image module tests.
//
// Copyright (c) 2017, 2019, 2020 Tristan Grimmer.
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
// granted, provided that the above copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

#include <Image/tTexture.h>
#include <Image/tImageDDS.h>
#include <Image/tImageEXR.h>
#include <Image/tImageGIF.h>
#include <Image/tImageHDR.h>
#include <Image/tImageICO.h>
#include <Image/tImageTGA.h>
#include <Image/tImageJPG.h>
#include <Image/tImageWEBP.h>
#include <System/tFile.h>
#include "UnitTests.h"
using namespace tStd;
namespace tUnitTest
{

tTestUnit(Image)
{
	if (!tSystem::tDirExists("TestData/"))
		tSkipUnit(Image)

	// Test direct loading classes.
	tImage::tImageDDS imgDDS("TestData/TestDXT1.dds");
	tRequire(imgDDS.IsValid());

	tImage::tImageEXR imgEXR("TestData/Desk.exr");
	tRequire(imgEXR.IsValid());

	tImage::tImageGIF imgGIF("TestData/8-cell-simple.gif");
	tRequire(imgGIF.IsValid());

	tImage::tImageHDR imgHDR("TestData/mpi_atrium_3.hdr");
	tRequire(imgHDR.IsValid());

	tImage::tImageICO imgICO("TestData/UpperBounds.ico");
	tRequire(imgICO.IsValid());

	tImage::tImageTGA imgTGA("TestData/WhiteBorderRLE.tga");
	tRequire(imgTGA.IsValid());

	tImage::tImageJPG imgJPG("TestData/WiredDrives.jpg");
	tRequire(imgJPG.IsValid());

	tImage::tImageWEBP imgWEBP("TestData/RockyBeach.webp");
	tRequire(imgWEBP.IsValid());

	// Test dxt1 texture.
	tImage::tTexture dxt1Tex("TestData/TestDXT1.dds");
	tRequire(dxt1Tex.IsValid());

	tChunkWriter writer("TestData/WrittenTestDXT1.tac");
	dxt1Tex.Save(writer);
	tRequire( tSystem::tFileExists("TestData/WrittenTestDXT1.tac") );

	tChunkReader reader("TestData/WrittenTestDXT1.tac");
	dxt1Tex.Load( reader.Chunk() );
	tRequire(dxt1Tex.IsValid());

	// Test cubemap.
	tImage::tTexture cubemap("TestData/CubemapLayoutGuide.dds");
	tRequire(cubemap.IsValid());

	// Test jpg to texture. This will do conversion to BC1.
	tImage::tTexture bc1Tex("TestData/WiredDrives.jpg", true);
	tRequire(bc1Tex.IsValid());
	tChunkWriter chunkWriterBC1("TestData/WrittenBC1.tac");
	bc1Tex.Save(chunkWriterBC1);
	tRequire( tSystem::tFileExists("TestData/WrittenBC1.tac"));

	// Test ico with alpha to texture. This will do conversion to BC3.
	tImage::tTexture bc3Tex("TestData/UpperBounds.ico", true);
	tRequire(bc3Tex.IsValid());
	tChunkWriter chunkWriterBC3("TestData/WrittenBC3.tac");
	bc3Tex.Save(chunkWriterBC3);
	tRequire( tSystem::tFileExists("TestData/WrittenBC3.tac"));

	// Test tPicture loading jpg and saving as tga.
	tImage::tPicture jpgPic("TestData/WiredDrives.jpg");
	tRequire(jpgPic.IsValid());

	jpgPic.Save("TestData/WrittenWiredDrives.tga");
	tRequire( tSystem::tFileExists("TestData/WrittenWiredDrives.tga"));

	// Test tPicture loading png (with alpha channel) and saving as tga (with alpha channel).
	tImage::tPicture pngPic("TestData/Xeyes.png");
	tRequire(pngPic.IsValid());

	pngPic.SaveTGA("TestData/WrittenXeyes.tga");
	tRequire( tSystem::tFileExists("TestData/WrittenXeyes.tga"));

	// Test saving tPicture in all supported formats.
	pngPic.Save("TestData/WrittenXeyesTGA.tga");
	tRequire( tSystem::tFileExists("TestData/WrittenXeyesTGA.tga"));

	pngPic.Save("TestData/WrittenXeyesPNG.png");
	tRequire( tSystem::tFileExists("TestData/WrittenXeyesPNG.png"));

	pngPic.Save("TestData/WrittenXeyesBMP.bmp");
	tRequire( tSystem::tFileExists("TestData/WrittenXeyesBMP.bmp"));

	pngPic.Save("TestData/WrittenXeyesJPG.jpg");
	tRequire( tSystem::tFileExists("TestData/WrittenXeyesJPG.jpg"));
}

}
<|endoftext|>