<commit_before>#include <iostream>
#include <iomanip>

#include <tightdb/column_string_enum.hpp>
#include <tightdb/index_string.hpp>

using namespace std;
using namespace tightdb;

namespace {

// Getter function for string index
StringData get_string(void* column, size_t ndx)
{
    return static_cast<ColumnStringEnum*>(column)->get(ndx);
}

} // anonymous namespace

ColumnStringEnum::ColumnStringEnum(ref_type keys, ref_type values, ArrayParent* column_parent,
                                   size_t column_ndx_in_parent, ArrayParent* keys_parent,
                                   size_t keys_ndx_in_parent, Allocator& alloc):
    Column(values, column_parent, column_ndx_in_parent, alloc), // Throws
    m_keys(keys,   keys_parent,   keys_ndx_in_parent,   alloc), // Throws
    m_index(0)
{
}

ColumnStringEnum::~ColumnStringEnum() TIGHTDB_NOEXCEPT
{
    delete m_index;
}

void ColumnStringEnum::destroy() TIGHTDB_NOEXCEPT
{
    m_keys.destroy();
    Column::destroy();
    if (m_index)
        m_index->destroy();
}

void ColumnStringEnum::adjust_keys_ndx_in_parent(int diff) TIGHTDB_NOEXCEPT
{
    m_keys.adjust_ndx_in_parent(diff);
}

void ColumnStringEnum::adjust_ndx_in_parent(int diff) TIGHTDB_NOEXCEPT
{
    Column::adjust_ndx_in_parent(diff);
}

void ColumnStringEnum::update_from_parent(size_t old_baseline) TIGHTDB_NOEXCEPT
{
    m_array->update_from_parent(old_baseline);
    m_keys.update_from_parent(old_baseline);
}

void ColumnStringEnum::add(StringData value)
{
    insert(Column::size(), value);
}

void ColumnStringEnum::set(size_t ndx, StringData value)
{
    TIGHTDB_ASSERT(ndx < Column::size());

    // Update index
    // (it is important here that we do it before actually setting
    //  the value, or the index would not be able to find the correct
    //  position to update (as it looks for the old value))
    if (m_index) {
        StringData oldVal = get(ndx);
        m_index->set(ndx, oldVal, value);
    }

    size_t key_ndx = GetKeyNdxOrAdd(value);
    Column::set(ndx, key_ndx);
}

void ColumnStringEnum::insert(size_t ndx, StringData value)
{
    TIGHTDB_ASSERT(ndx <= Column::size());

    size_t key_ndx = GetKeyNdxOrAdd(value);
    Column::insert(ndx, key_ndx);

    if (m_index) {
        bool is_last = ndx+1 == size();
        m_index->insert(ndx, value, is_last);
    }
}

void ColumnStringEnum::erase(size_t ndx, bool is_last)
{
    TIGHTDB_ASSERT(ndx < Column::size());

    // Update index
    // (it is important here that we do it before actually setting
    //  the value, or the index would not be able to find the correct
    //  position to update (as it looks for the old value))
    if (m_index) {
        StringData old_val = get(ndx);
        m_index->erase(ndx, old_val, is_last);
    }

    Column::erase(ndx, is_last);
}

void ColumnStringEnum::clear()
{
    // Note that clearing a StringEnum does not remove keys
    Column::clear();

    if (m_index)
        m_index->clear();
}

size_t ColumnStringEnum::count(size_t key_ndx) const
{
    return Column::count(key_ndx);
}

size_t ColumnStringEnum::count(StringData value) const
{
    if (m_index)
        return m_index->count(value);

    size_t key_ndx = m_keys.find_first(value);
    if (key_ndx == not_found) return 0;
    return Column::count(key_ndx);
}

void ColumnStringEnum::find_all(Array& res, StringData value, size_t begin, size_t end) const
{
    if (m_index && begin == 0 && end == size_t(-1))
        return m_index->find_all(res, value);

    size_t key_ndx = m_keys.find_first(value);
    if (key_ndx == size_t(-1)) return;
    Column::find_all(res, key_ndx, begin, end);
    return;
}

void ColumnStringEnum::find_all(Array& res, size_t key_ndx, size_t begin, size_t end) const
{
    if (key_ndx == size_t(-1)) return;
    Column::find_all(res, key_ndx, begin, end);
    return;
}

FindRes ColumnStringEnum::find_all_indexref(StringData value, size_t& dst) const
{
//    TIGHTDB_ASSERT(value.m_data); fixme
    TIGHTDB_ASSERT(m_index);

    return m_index->find_all(value, dst);
}

size_t ColumnStringEnum::find_first(size_t key_ndx, size_t begin, size_t end) const
{
    // Find key
    if (key_ndx == size_t(-1)) return size_t(-1);

    return Column::find_first(key_ndx, begin, end);
}

size_t ColumnStringEnum::find_first(StringData value, size_t begin, size_t end) const
{
    if (m_index && begin == 0 && end == size_t(-1))
        return m_index->find_first(value);

    // Find key
    size_t key_ndx = m_keys.find_first(value);
    if (key_ndx == size_t(-1)) return size_t(-1);

    return Column::find_first(key_ndx, begin, end);
}

size_t ColumnStringEnum::GetKeyNdx(StringData value) const
{
    return m_keys.find_first(value);
}

size_t ColumnStringEnum::GetKeyNdxOrAdd(StringData value)
{
    size_t res = m_keys.find_first(value);
    if (res != size_t(-1)) return res;
    else {
        // Add key if it does not exist
        size_t pos = m_keys.size();
        m_keys.add(value);
        return pos;
    }
}

bool ColumnStringEnum::compare_string(const AdaptiveStringColumn& c) const
{
    const size_t n = size();
    if (c.size() != n) return false;
    for (size_t i=0; i<n; ++i) {
        if (get(i) != c.get(i)) return false;
    }
    return true;
}

bool ColumnStringEnum::compare_string(const ColumnStringEnum& c) const
{
    const size_t n = size();
    if (c.size() != n) return false;
    for (size_t i=0; i<n; ++i) {
        if (get(i) != c.get(i)) return false;
    }
    return true;
}

StringIndex& ColumnStringEnum::create_index()
{
    TIGHTDB_ASSERT(m_index == null_ptr);

    // Create new index
    m_index = new StringIndex(this, &get_string, m_array->get_alloc());

    // Populate the index
    const size_t count = size();
    for (size_t i = 0; i < count; ++i) {
        StringData value = get(i);
        m_index->insert(i, value, true);
    }

    return *m_index;
}

void ColumnStringEnum::set_index_ref(ref_type ref, ArrayParent* parent, size_t ndx_in_parent)
{
    TIGHTDB_ASSERT(!m_index);
    m_index = new StringIndex(ref, parent, ndx_in_parent, this, &get_string, m_array->get_alloc());
}

void ColumnStringEnum::install_index(StringIndex* index) TIGHTDB_NOEXCEPT
{
    TIGHTDB_ASSERT(m_index == null_ptr);

    index->set_target(this, &get_string);
    m_index = index; // we now own this index
}

#ifdef TIGHTDB_DEBUG

void ColumnStringEnum::Verify() const
{
    m_keys.Verify();
    Column::Verify();
}

void ColumnStringEnum::to_dot(ostream& out, StringData title) const
{
    ref_type ref = m_keys.get_ref();
    out << "subgraph cluster_string_enum_column" << ref << " {" << endl;
    out << " label = \"String enum column";
    if (title.size() != 0)
        out << "\\n'" << title << "'";
    out << "\";" << endl;

    m_keys.to_dot(out, "keys");
    Column::to_dot(out, "values");

    out << "}" << endl;
}

namespace {

void leaf_dumper(MemRef mem, Allocator& alloc, ostream& out, int level)
{
    Array leaf(mem, 0, 0, alloc);
    int indent = level * 2;
    out << setw(indent) << "" << "String enumeration leaf (size: "<<leaf.size()<<")\n";
}

} // anonymous namespace

void ColumnStringEnum::dump_node_structure(ostream& out, int level) const
{
    m_array->dump_bptree_structure(out, level, &leaf_dumper);
}

#endif // TIGHTDB_DEBUG
<commit_msg>A bit of style cleanup<commit_after>#include <iostream>
#include <iomanip>

#include <tightdb/column_string_enum.hpp>
#include <tightdb/index_string.hpp>

using namespace std;
using namespace tightdb;

namespace {

// Getter function for string index
StringData get_string(void* column, size_t ndx)
{
    return static_cast<ColumnStringEnum*>(column)->get(ndx);
}

} // anonymous namespace

ColumnStringEnum::ColumnStringEnum(ref_type keys, ref_type values, ArrayParent* column_parent,
                                   size_t column_ndx_in_parent, ArrayParent* keys_parent,
                                   size_t keys_ndx_in_parent, Allocator& alloc):
    Column(values, column_parent, column_ndx_in_parent, alloc), // Throws
    m_keys(keys,   keys_parent,   keys_ndx_in_parent,   alloc), // Throws
    m_index(0)
{
}

ColumnStringEnum::~ColumnStringEnum() TIGHTDB_NOEXCEPT
{
    delete m_index;
}

void ColumnStringEnum::destroy() TIGHTDB_NOEXCEPT
{
    m_keys.destroy();
    Column::destroy();
    if (m_index)
        m_index->destroy();
}

void ColumnStringEnum::adjust_keys_ndx_in_parent(int diff) TIGHTDB_NOEXCEPT
{
    m_keys.adjust_ndx_in_parent(diff);
}

void ColumnStringEnum::adjust_ndx_in_parent(int diff) TIGHTDB_NOEXCEPT
{
    Column::adjust_ndx_in_parent(diff);
}

void ColumnStringEnum::update_from_parent(size_t old_baseline) TIGHTDB_NOEXCEPT
{
    m_array->update_from_parent(old_baseline);
    m_keys.update_from_parent(old_baseline);
}

void ColumnStringEnum::add(StringData value)
{
    insert(Column::size(), value);
}

void ColumnStringEnum::set(size_t ndx, StringData value)
{
    TIGHTDB_ASSERT(ndx < Column::size());

    // Update index
    // (it is important here that we do it before actually setting
    //  the value, or the index would not be able to find the correct
    //  position to update (as it looks for the old value))
    if (m_index) {
        StringData oldVal = get(ndx);
        m_index->set(ndx, oldVal, value);
    }

    size_t key_ndx = GetKeyNdxOrAdd(value);
    Column::set(ndx, key_ndx);
}

void ColumnStringEnum::insert(size_t ndx, StringData value)
{
    TIGHTDB_ASSERT(ndx <= Column::size());

    size_t key_ndx = GetKeyNdxOrAdd(value);
    Column::insert(ndx, key_ndx);

    if (m_index) {
        bool is_last = ndx+1 == size();
        m_index->insert(ndx, value, is_last);
    }
}

void ColumnStringEnum::erase(size_t ndx, bool is_last)
{
    TIGHTDB_ASSERT(ndx < Column::size());

    // Update index
    // (it is important here that we do it before actually setting
    //  the value, or the index would not be able to find the correct
    //  position to update (as it looks for the old value))
    if (m_index) {
        StringData old_val = get(ndx);
        m_index->erase(ndx, old_val, is_last);
    }

    Column::erase(ndx, is_last);
}

void ColumnStringEnum::clear()
{
    // Note that clearing a StringEnum does not remove keys
    Column::clear();

    if (m_index)
        m_index->clear();
}

size_t ColumnStringEnum::count(size_t key_ndx) const
{
    return Column::count(key_ndx);
}

size_t ColumnStringEnum::count(StringData value) const
{
    if (m_index)
        return m_index->count(value);

    size_t key_ndx = m_keys.find_first(value);
    if (key_ndx == not_found)
        return 0;
    return Column::count(key_ndx);
}

void ColumnStringEnum::find_all(Array& res, StringData value, size_t begin, size_t end) const
{
    if (m_index && begin == 0 && end == size_t(-1))
        return m_index->find_all(res, value);

    size_t key_ndx = m_keys.find_first(value);
    if (key_ndx == size_t(-1))
        return;
    Column::find_all(res, key_ndx, begin, end);
}

void ColumnStringEnum::find_all(Array& res, size_t key_ndx, size_t begin, size_t end) const
{
    if (key_ndx == size_t(-1))
        return;
    Column::find_all(res, key_ndx, begin, end);
}

FindRes ColumnStringEnum::find_all_indexref(StringData value, size_t& dst) const
{
//    TIGHTDB_ASSERT(value.m_data); fixme
    TIGHTDB_ASSERT(m_index);

    return m_index->find_all(value, dst);
}

size_t ColumnStringEnum::find_first(size_t key_ndx, size_t begin, size_t end) const
{
    // Find key
    if (key_ndx == size_t(-1))
        return size_t(-1);

    return Column::find_first(key_ndx, begin, end);
}

size_t ColumnStringEnum::find_first(StringData value, size_t begin, size_t end) const
{
    if (m_index && begin == 0 && end == size_t(-1))
        return m_index->find_first(value);

    // Find key
    size_t key_ndx = m_keys.find_first(value);
    if (key_ndx == size_t(-1))
        return size_t(-1);

    return Column::find_first(key_ndx, begin, end);
}

size_t ColumnStringEnum::GetKeyNdx(StringData value) const
{
    return m_keys.find_first(value);
}

size_t ColumnStringEnum::GetKeyNdxOrAdd(StringData value)
{
    size_t res = m_keys.find_first(value);
    if (res != size_t(-1))
        return res;
    else {
        // Add key if it does not exist
        size_t pos = m_keys.size();
        m_keys.add(value);
        return pos;
    }
}

bool ColumnStringEnum::compare_string(const AdaptiveStringColumn& c) const
{
    size_t n = size();
    if (c.size() != n)
        return false;
    for (size_t i = 0; i != n; ++i) {
        if (get(i) != c.get(i))
            return false;
    }
    return true;
}

bool ColumnStringEnum::compare_string(const ColumnStringEnum& c) const
{
    size_t n = size();
    if (c.size() != n)
        return false;
    for (size_t i = 0; i != n; ++i) {
        if (get(i) != c.get(i))
            return false;
    }
    return true;
}

StringIndex& ColumnStringEnum::create_index()
{
    TIGHTDB_ASSERT(!m_index);

    // Create new index
    m_index = new StringIndex(this, &get_string, m_array->get_alloc());

    // Populate the index
    size_t n = size();
    for (size_t i = 0; i != n; ++i) {
        StringData value = get(i);
        m_index->insert(i, value, true);
    }

    return *m_index;
}

void ColumnStringEnum::set_index_ref(ref_type ref, ArrayParent* parent, size_t ndx_in_parent)
{
    TIGHTDB_ASSERT(!m_index);
    m_index = new StringIndex(ref, parent, ndx_in_parent, this, &get_string, m_array->get_alloc());
}

void ColumnStringEnum::install_index(StringIndex* index) TIGHTDB_NOEXCEPT
{
    TIGHTDB_ASSERT(m_index == null_ptr);

    index->set_target(this, &get_string);
    m_index = index; // we now own this index
}

#ifdef TIGHTDB_DEBUG

void ColumnStringEnum::Verify() const
{
    m_keys.Verify();
    Column::Verify();
}

void ColumnStringEnum::to_dot(ostream& out, StringData title) const
{
    ref_type ref = m_keys.get_ref();
    out << "subgraph cluster_string_enum_column" << ref << " {" << endl;
    out << " label = \"String enum column";
    if (title.size() != 0)
        out << "\\n'" << title << "'";
    out << "\";" << endl;

    m_keys.to_dot(out, "keys");
    Column::to_dot(out, "values");

    out << "}" << endl;
}

namespace {

void leaf_dumper(MemRef mem, Allocator& alloc, ostream& out, int level)
{
    Array leaf(mem, 0, 0, alloc);
    int indent = level * 2;
    out << setw(indent) << "" << "String enumeration leaf (size: "<<leaf.size()<<")\n";
}

} // anonymous namespace

void ColumnStringEnum::dump_node_structure(ostream& out, int level) const
{
    m_array->dump_bptree_structure(out, level, &leaf_dumper);
}

#endif // TIGHTDB_DEBUG
<|endoftext|>


<commit_before>/*
 * spatial_displacement_leafless.cpp
 *
 *  Created on: Aug 12, 2010
 *      Author: Mark Christensen
 */

#include "spatial_displacement_leafless.h"
#include "../system/discursive_system.h"

using namespace std;

SpatialDisplacementLeafless::SpatialDisplacementLeafless(int width, int height):width(width), height(height), growthUnit(5.0)
{
}

void SpatialDisplacementLeafless::decorate(SurrogateTreeNode* tree)
{
	// Iterate over each node with children
	// First, "count".  Calculates child center of mass and subtree depth
	this->count(tree);
	// Second, float weighted surrogate nodes into position
	//this->expand(tree,(3.14159/2),0,0);
	// Max tree depth
	double maxDepth = tree->findMax(TreeNodeKey::DEPTH);
	this->expand(tree,(3.14159/2),0,0,(this->height)/maxDepth);
	// Third, transform coordinates
	this->transform(tree);
}

// Scale point values to fit within width and height give
// with root at (width/2, height)
// Transformation flips y values and shifts x values
// after scaling
void SpatialDisplacementLeafless::transform(SurrogateTreeNode* tree)
{
	// Calculate resize scaling factors
	double allowedWidth = this->width;
	double allowedHeight = this->height;
	double xMax = tree->findMax(TreeNodeKey::X);
	double xMin = tree->findMin(TreeNodeKey::X);
	double yMax = tree->findMax(TreeNodeKey::Y);
	double yMin = tree->findMin(TreeNodeKey::Y);
	DiscursiveDebugPrint("Mins: (%f,%f), Maxs: (%f,%f)\n",xMin,yMin,xMax,yMax);
	double currWidth = xMax - xMin;
	double currHeight = yMax - yMin;

	double minDim = min(allowedHeight, allowedWidth);

	double scalingFactorW = minDim/currWidth;
	double scalingFactorH = minDim/currHeight;

	// Transform points to look more "naturally tree-like"
	tree->scale(TreeNodeKey::Y, scalingFactorH);
	PropertyInverter inverter(this->height * 0.98);
	tree->transform(TreeNodeKey::Y,&inverter);

	PropertyShifter shifter(-1*((xMax + xMin) / 2));
	tree->transform(TreeNodeKey::X,&shifter);
	// Scale tree values
	tree->scale(TreeNodeKey::X, scalingFactorW);
	PropertyShifter shifter2(minDim * 1 / 2);
	tree->transform(TreeNodeKey::X,&shifter2);
}

// Sorted in increasing order
void SpatialDisplacementLeafless::insertOrderedBy(vector<SurrogateTreeNode*>* list, SurrogateTreeNode* tree, string property)
{
	//printf("Inserting '%s' from %s.  Current size: %d\n",property.c_str(),tree->data[TreeNodeKey::NAME].c_str(), list->size());
	SurrogateTreeNode* node;
	bool inserted = false;
	long comp = atol(tree->data[property].c_str());
	long curr = 0;
	for(vector<SurrogateTreeNode*>::iterator iter = list->begin(); iter != list->end(); ++iter)
	{
		node = *iter;
		curr = atol(node->data[property].c_str());
		//printf("%ld < %ld?\n",atol(tree->data[property].c_str()),atol(node->data[property].c_str()));
		if(comp < curr)
		{
			list->insert(iter,tree);
			inserted = true;
			break;
		}
	}
	if(!inserted)
	{
		//printf("Inserting at end\n");
		list->push_back(tree);
	}
}

void SpatialDisplacementLeafless::expand(SurrogateTreeNode* tree, double rootAngle, double rootX, double rootY, double allowedHeight)
{
	if(tree->children->size() > 0)
	{
		// Determine initial layout based on creation time of child nodes.
		// Files are split into different list than directories.
		vector<SurrogateTreeNode*> files;
		vector<SurrogateTreeNode*> dirs;
		SurrogateTreeNode* node = NULL;
		int maxChild = 1;
		int childSize;
		int mass = 0;
		int children = 0;
		double minChildMass = DBL_MAX;
		for(vector<SurrogateTreeNode*>::iterator iter = tree->children->begin(); iter != tree->children->end(); ++iter)
		{
			children++;
			node = *iter;
			// Directory
			if(node->children->size() > 0)
			{
				//printf("Inserting into dirs...\n");
				this->insertOrderedBy(&dirs,node,TreeNodeKey::SIZE);
				childSize = atoi(node->data[TreeNodeKey::SIZE].c_str());
				mass += childSize;
				if(childSize > maxChild )
				{
					maxChild = childSize;
				}
				if(childSize < minChildMass)
				{
					minChildMass = childSize;
				}
			}
			// File
			else
			{
				//printf("Inserting into files...\n");
				this->insertOrderedBy(&files,node,TreeNodeKey::CREATION_TIME);
				mass++;
				if(minChildMass > 1)
				{
					minChildMass = 1;
				}
			}
		}

		// Map for retrieving resutls
		unordered_map<SurrogateTreeNode*,int> pairs;
		double masses[children];
		double positions[children];
		memset(positions,0,children*sizeof(double));
		// Layout directories first
		// Start left if even number of items.
		bool left = children % 2 != 0;
		int center = children / 2;
		int dist = 0;
		//TreeDisplacementNode* treeNode;
		int i = 0;
		int location;
		int nodeMass;
		for(; i < (int)dirs.size(); i++)
		{
			dist = (i+1)/2;
			nodeMass = atoi(dirs[i]->data[TreeNodeKey::SIZE].c_str());
			if(left)
			{
				location = center - dist;
			}
			else
			{
				location = center + dist;
			}
			masses[location] = nodeMass;
			pairs[dirs[i]] = location;
			for(int k = location; k < children; k++)
			{
				positions[k] += nodeMass;
			}
			left = !left;
		}
		// Now layout files
		for(int j = 0; j < (int)files.size(); j++)
		{
			dist = ((i + j + 1)/2);
			nodeMass = 1;
			if(left)
			{
				location = center - dist;
			}
			else
			{
				location = center + dist;
			}
			masses[location] = nodeMass;
			pairs[files[j]] = location;
			for(int k = location; k < children; k++)
			{
				positions[k] += nodeMass;
			}
			left = !left;
		}

		// Calculate spacing to range [0,splay]
		double deltaSplay = 0;
		double splay = 3.14159 / 2;
		double com;
		if(mass > minChildMass)
		{
			deltaSplay = splay / (mass - minChildMass);
		}

		DiscursiveDebugPrint("Subtree mass effects [");
		for(i = 0; i < children; i++)
		{
			DiscursiveDebugPrint("%f,",positions[i]);
		}
		DiscursiveDebugPrint("] @ %f\n", deltaSplay);

		positions[0] = 0;
		com = 0;
		for(i = 1; i < children; i++)
		{
			positions[i] = (positions[i] - minChildMass)*deltaSplay;
			com += (positions[i] * masses[i]);
		}
		// Final part of CoM calculation
		com /= mass;

		// Transform positions to arc
		//double arcRadius = 10.0;
//		double deltaAngle = splay / children;
		int maxDepth = (int)tree->findMax(TreeNodeKey::DEPTH);
		int depth;
		double arcRadius = allowedHeight/maxDepth;
		double ratio;
		double angle;

		// Controls width of fan-out.  > 1 : Wide tree
		//							   < 1 : Narrow tree
		double widthHeightScaleFactor = 1.0;
		// Transform positions to arc and Update new positions
		// Dirs first
		for(i = 0; i < (int)dirs.size(); i++)
		{
			node = dirs[i];
			depth = atoi(node->data[TreeNodeKey::DEPTH].c_str());
			ratio = depth / (double)maxDepth;
			angle = rootAngle - (positions[pairs[node]] - com);
			double newX = rootX + (ratio * arcRadius * cos(angle));
			double newY = rootY + (ratio * arcRadius * widthHeightScaleFactor * sin(angle));
			node->set(TreeNodeKey::X, boost::lexical_cast<string>(newX));
			node->set(TreeNodeKey::Y, boost::lexical_cast<string>(newY));
			// Run expand on child
			double childRot = (3.14159/2);//angle;// + ((3.14159/2)-angle)/2;
			this->expand(node,childRot,newX,newY,allowedHeight - arcRadius);
		}
		// Then files
		for(int j = 0; j < (int)files.size(); j++)
		{
			node = files[j];
			depth = atoi(node->data[TreeNodeKey::DEPTH].c_str());
			ratio = depth / (double)maxDepth;
			angle = rootAngle - (positions[pairs[node]] - com);
			double newX = rootX + (ratio * arcRadius * cos(angle));
			double newY = rootY + (ratio * arcRadius * widthHeightScaleFactor * sin(angle));
			node->set(TreeNodeKey::X, boost::lexical_cast<string>(newX));
			node->set(TreeNodeKey::Y, boost::lexical_cast<string>(newY));
		}
//		for(vector<SurrogateTreeNode*>::iterator iter = tree->children->begin(); iter != tree->children->end(); ++iter)
//		{
//			node = *iter;
//			depth = atoi(node->data[TreeNodeKey::DEPTH].c_str());
//			ratio = depth / (double)maxDepth;
//			angle = rootAngle + ();
//			double newX = rootX + ();
//			double newY = rootY;
//			//printf("%s final position at (%f,%f)\n",node->data[TreeNodeKey::NAME].c_str(),newX, newY);
//			node->data[TreeNodeKey::X] = boost::lexical_cast<string>(newX);
//			node->data[TreeNodeKey::Y] = boost::lexical_cast<string>(newY);
//			printf("%s @ (%s,%s)\n",node->data[TreeNodeKey::NAME].c_str(),node->data[TreeNodeKey::X].c_str(),node->data[TreeNodeKey::Y].c_str());
//			// Run expand on child
//			double childRot = treeNode->getRotation() + ((3.14159/2)-treeNode->getRotation())/2;
//			this->expand(node,childRot,newX,newY,allowedHeight - arcRadius);
//		}
	}
	else
	{
		//printf("No children for node '%s'\n", tree->data[TreeNodeKey::NAME].c_str());
	}
}

int SpatialDisplacementLeafless::count(SurrogateTreeNode* tree)
{
	// Count all children plus ourselves (initial 1)
	int sum = 1;
	int maxDepth = 0;
	int childDepth = 0;
	for(vector<SurrogateTreeNode*>::iterator iter = tree->children->begin(); iter != tree->children->end(); ++iter)
	{
		sum += this->count(*iter);
		//childDepth = atoi((*iter)->data["count"].c_str());
		childDepth = atoi((*iter)->data[TreeNodeKey::DEPTH].c_str());
		if(childDepth > maxDepth)
		{
			maxDepth = childDepth;
		}
	}
	// Assign count to data
	tree->set(TreeNodeKey::SIZE, boost::lexical_cast<string>(sum));
	DiscursiveDebugPrint("Size of tree %s is %s in this and %d sublevels\n",tree->data[TreeNodeKey::NAME].c_str(),tree->data[TreeNodeKey::SIZE].c_str(),maxDepth);
	tree->set(TreeNodeKey::DEPTH, boost::lexical_cast<string>(maxDepth + 1));
	return sum;
}
<commit_msg>Fixed some scaling but the layout is wrong for small trees now.<commit_after>/*
 * spatial_displacement_leafless.cpp
 *
 *  Created on: Aug 12, 2010
 *      Author: Mark Christensen
 */

#include "spatial_displacement_leafless.h"
#include "../system/discursive_system.h"

using namespace std;

SpatialDisplacementLeafless::SpatialDisplacementLeafless(int width, int height):width(width), height(height), growthUnit(5.0)
{
}

void SpatialDisplacementLeafless::decorate(SurrogateTreeNode* tree)
{
	// Iterate over each node with children
	// First, "count".  Calculates child center of mass and subtree depth
	this->count(tree);
	// Second, float weighted surrogate nodes into position
	//this->expand(tree,(3.14159/2),0,0);
	// Max tree depth
	double maxDepth = tree->findMax(TreeNodeKey::DEPTH);
	this->expand(tree,(3.14159/2),0,0,(this->height)/maxDepth);
	// Third, transform coordinates
	this->transform(tree);
}

// Scale point values to fit within width and height give
// with root at (width/2, height)
// Transformation flips y values and shifts x values
// after scaling
void SpatialDisplacementLeafless::transform(SurrogateTreeNode* tree)
{
	// Calculate resize scaling factors
	double allowedWidth = this->width*0.95;
	double allowedHeight = this->height*0.95;
	double xMax = tree->findMax(TreeNodeKey::X);
	double xMin = tree->findMin(TreeNodeKey::X);
	double yMax = tree->findMax(TreeNodeKey::Y);
	double yMin = tree->findMin(TreeNodeKey::Y);
	DiscursiveDebugPrint("Mins: (%f,%f), Maxs: (%f,%f)\n",xMin,yMin,xMax,yMax);
	//double currWidth = xMax - xMin;
	double maxXDim = max(fabs(xMax),fabs(xMin));
	double currWidth = xMax - xMin;
	double currHeight = yMax - yMin;

	double minDim = min(allowedHeight, allowedWidth);

	double scalingFactorW = minDim/(2*maxXDim);
	double scalingFactorH = minDim/currHeight;

	// Transform points to look more "naturally tree-like"
	tree->scale(TreeNodeKey::Y, scalingFactorH * 0.98);
	PropertyInverter inverter(this->height * 0.98);
	tree->transform(TreeNodeKey::Y,&inverter);

//	PropertyShifter shifter(-1*((xMax + xMin) / 2));
	PropertyShifter shifter(-xMin);
	tree->transform(TreeNodeKey::X,&shifter);
	// Scale tree values
	tree->scale(TreeNodeKey::X, scalingFactorW);
//	PropertyShifter shifter2(minDim * 1 / 2);
	PropertyShifter shifter2(maxXDim * scalingFactorW);
	tree->transform(TreeNodeKey::X,&shifter2);
}

// Sorted in increasing order
void SpatialDisplacementLeafless::insertOrderedBy(vector<SurrogateTreeNode*>* list, SurrogateTreeNode* tree, string property)
{
	//printf("Inserting '%s' from %s.  Current size: %d\n",property.c_str(),tree->data[TreeNodeKey::NAME].c_str(), list->size());
	SurrogateTreeNode* node;
	bool inserted = false;
	long comp = atol(tree->data[property].c_str());
	long curr = 0;
	for(vector<SurrogateTreeNode*>::iterator iter = list->begin(); iter != list->end(); ++iter)
	{
		node = *iter;
		curr = atol(node->data[property].c_str());
		//printf("%ld < %ld?\n",atol(tree->data[property].c_str()),atol(node->data[property].c_str()));
		if(comp < curr)
		{
			list->insert(iter,tree);
			inserted = true;
			break;
		}
	}
	if(!inserted)
	{
		//printf("Inserting at end\n");
		list->push_back(tree);
	}
}

void SpatialDisplacementLeafless::expand(SurrogateTreeNode* tree, double rootAngle, double rootX, double rootY, double allowedHeight)
{
	if(tree->children->size() > 0)
	{
		// Determine initial layout based on creation time of child nodes.
		// Files are split into different list than directories.
		vector<SurrogateTreeNode*> files;
		vector<SurrogateTreeNode*> dirs;
		SurrogateTreeNode* node = NULL;
		int maxChild = 1;
		int childSize;
		int mass = 0;
		int children = 0;
		double minChildMass = DBL_MAX;
		for(vector<SurrogateTreeNode*>::iterator iter = tree->children->begin(); iter != tree->children->end(); ++iter)
		{
			children++;
			node = *iter;
			// Directory
			if(node->children->size() > 0)
			{
				//printf("Inserting into dirs...\n");
				this->insertOrderedBy(&dirs,node,TreeNodeKey::SIZE);
				childSize = atoi(node->data[TreeNodeKey::SIZE].c_str());
				mass += childSize;
				if(childSize > maxChild )
				{
					maxChild = childSize;
				}
				if(childSize < minChildMass)
				{
					minChildMass = childSize;
				}
			}
			// File
			else
			{
				//printf("Inserting into files...\n");
				this->insertOrderedBy(&files,node,TreeNodeKey::CREATION_TIME);
				mass++;
				if(minChildMass > 1)
				{
					minChildMass = 1;
				}
			}
		}

		// Map for retrieving resutls
		unordered_map<SurrogateTreeNode*,int> pairs;
		double masses[children];
		double positions[children];
		memset(positions,0,children*sizeof(double));
		// Layout directories first
		// Start left if even number of items.
		bool left = children % 2 != 0;
		int center = children / 2;
		int dist = 0;
		//TreeDisplacementNode* treeNode;
		int i = 0;
		int location;
		int nodeMass;
		for(; i < (int)dirs.size(); i++)
		{
			dist = (i+1)/2;
			nodeMass = atoi(dirs[i]->data[TreeNodeKey::SIZE].c_str());
			if(left)
			{
				location = center - dist;
			}
			else
			{
				location = center + dist;
			}
			masses[location] = nodeMass;
			pairs[dirs[i]] = location;
			for(int k = location; k < children; k++)
			{
				positions[k] += nodeMass;
			}
			left = !left;
		}
		// Now layout files
//		for(int j = 0; j < (int)files.size(); j++)
//		{
//			dist = ((i + j + 1)/2);
//			nodeMass = 1;
//			if(left)
//			{
//				location = center - dist;
//			}
//			else
//			{
//				location = center + dist;
//			}
//			masses[location] = nodeMass;
//			pairs[files[j]] = location;
//			for(int k = location; k < children; k++)
//			{
//				positions[k] += nodeMass;
//			}
//			left = !left;
//		}

		// Calculate spacing to range [0,splay]
		double deltaSplay = 0;
		double splay = 3.14159 / 2;
		double com;
		double max = 0;
		if(mass > minChildMass)
		{
			deltaSplay = splay / (mass - minChildMass);
		}

		DiscursiveDebugPrint("Subtree mass effects [");
		for(i = 0; i < children; i++)
		{
			DiscursiveDebugPrint("%f,",positions[i]);
		}
		DiscursiveDebugPrint("] @ %f\n", deltaSplay);

		// Convert masses to initial locations
		// Record bounds
		positions[0] = 0;
		com = 0;
		for(i = 1; i < children; i++)
		{
			positions[i] = (positions[i] - minChildMass)*deltaSplay;
			com += (positions[i] * masses[i]);

			if(positions[i] > max)
			{
				max = positions[i];
			}
		}
		// Final part of CoM calculation
		com /= mass;

		// Balance tree
		double balancedCom = com;
		double divergence = balancedCom - (max/2);
		double scale;
		bool shortenLeft;
		printf("CoM: %f, Max: %f, Divergence: %f\n", balancedCom, max, divergence);
		while(fabs(divergence) > 3.14159 / 50)
		{
			// Shorten left half
			if(divergence > 0)
			{
				shortenLeft = true;
				scale = (max - balancedCom)/balancedCom;
			}
			// Shorten right half
			else
			{
				shortenLeft = false;
				scale = balancedCom/(max - balancedCom);
			}
			com = 0;
			for(i = 1; i < children; i++)
			{
				// Adjust position
				if(shortenLeft && positions[i] < balancedCom)
				{
					positions[i] = (positions[i] - divergence) * scale;
				}
				else if(!shortenLeft && positions[i] > balancedCom)
				{
					positions[i] = ((positions[i] - balancedCom) * scale) + balancedCom;
				}
				else if(shortenLeft && positions[i] > balancedCom)
				{
					positions[i] -= (2 * balancedCom - max);
				}
//				else
//				{
//					positions[i] = ;
//				}

				// Aggregate new position to calc new CoM
				com += (positions[i] * masses[i]);
			}
			max = 2*(max - balancedCom);
			balancedCom = com/mass;
			divergence = balancedCom - (max/2);
//			printf("CoM: %f, Max: %f, Divergence: %f\n", balancedCom, max, divergence);
		}

		// Transform positions to arc
		//double arcRadius = 10.0;
//		double deltaAngle = splay / children;
		int maxDepth = (int)tree->findMax(TreeNodeKey::DEPTH);
		int depth;
		double arcRadius = allowedHeight/maxDepth;
		double ratio;
		double angle;

		// Controls width of fan-out.  > 1 : Wide tree
		//							   < 1 : Narrow tree
		double widthHeightScaleFactor = 1.0;
		// Transform positions to arc and Update new positions
		// Dirs first
		for(i = 0; i < (int)dirs.size(); i++)
		{
			node = dirs[i];
			depth = atoi(node->data[TreeNodeKey::DEPTH].c_str());
			ratio = depth / (double)maxDepth;
			angle = rootAngle - (positions[pairs[node]] - com);
			double newX = rootX + (ratio * arcRadius * cos(angle));
			double newY = rootY + (ratio * arcRadius * widthHeightScaleFactor * sin(angle));
			node->set(TreeNodeKey::X, boost::lexical_cast<string>(newX));
			node->set(TreeNodeKey::Y, boost::lexical_cast<string>(newY));
			// Run expand on child
			double childRot = (3.14159/2);//angle;// + ((3.14159/2)-angle)/2;
			this->expand(node,childRot,newX,newY,allowedHeight - arcRadius);
		}
		// Then files
		for(int j = 0; j < (int)files.size(); j++)
		{
			node = files[j];
			depth = atoi(node->data[TreeNodeKey::DEPTH].c_str());
			ratio = depth / (double)maxDepth;
			angle = rootAngle - (positions[pairs[node]] - com);
			double newX = rootX + (ratio * arcRadius * cos(angle));
			double newY = rootY + (ratio * arcRadius * widthHeightScaleFactor * sin(angle));
			node->set(TreeNodeKey::X, boost::lexical_cast<string>(newX));
			node->set(TreeNodeKey::Y, boost::lexical_cast<string>(newY));
		}
	}
}

int SpatialDisplacementLeafless::count(SurrogateTreeNode* tree)
{
	// Count all children plus ourselves (initial 1)
	int sum = 1;
	int maxDepth = 0;
	int childDepth = 0;
	for(vector<SurrogateTreeNode*>::iterator iter = tree->children->begin(); iter != tree->children->end(); ++iter)
	{
		sum += this->count(*iter);
		//childDepth = atoi((*iter)->data["count"].c_str());
		childDepth = atoi((*iter)->data[TreeNodeKey::DEPTH].c_str());
		if(childDepth > maxDepth)
		{
			maxDepth = childDepth;
		}
	}
	// Assign count to data
	tree->set(TreeNodeKey::SIZE, boost::lexical_cast<string>(sum));
	DiscursiveDebugPrint("Size of tree %s is %s in this and %d sublevels\n",tree->data[TreeNodeKey::NAME].c_str(),tree->data[TreeNodeKey::SIZE].c_str(),maxDepth);
	tree->set(TreeNodeKey::DEPTH, boost::lexical_cast<string>(maxDepth + 1));
	return sum;
}
<|endoftext|>


<commit_before>// Helper classes for writing unittests testing the entire asynchronous
// stack. Allows to send incoming messages (in gridconnect format) and set
// expectations on messages produced.
//
// Only include this file in unittests.

#ifndef _UTILS_ASYNC_IF_TEST_HELPER_HXX_
#define _UTILS_ASYNC_IF_TEST_HELPER_HXX_

#include "nmranet/AliasAllocator.hxx"
#include "nmranet/IfCan.hxx"
#include "nmranet/EventService.hxx"
#include "nmranet/DefaultNode.hxx"
#include "nmranet_config.h"
#include "utils/GridConnectHub.hxx"
#include "utils/test_main.hxx"

using ::testing::AtLeast;
using ::testing::AtMost;
using ::testing::Eq;
using ::testing::Field;
using ::testing::Invoke;
using ::testing::IsNull;
using ::testing::Mock;
using ::testing::NiceMock;
using ::testing::NotNull;
using ::testing::Pointee;
using ::testing::Return;
using ::testing::StrCaseEq;
using ::testing::StrictMock;
using ::testing::WithArg;
using ::testing::_;

class TrainTestHelper;

///@todo(balazs.racz) remove
// void (*g_invoke)(Notifiable *) = &InvokeNotification;

HubFlow gc_hub0(&g_service);
CanHubFlow can_hub0(&g_service);
GCAdapterBase *g_gc_adapter = nullptr;

HubFlow gc_hub1(&g_service);
CanHubFlow can_hub1(&g_service);
GCAdapterBase *g_gc_adapter1 = nullptr;

/** Helper class for setting expectation on the CANbus traffic in unit
 * tests. */
class MockSend : public HubPort
{
public:
    MockSend()
        : HubPort(&g_service)
    {
    }

    MOCK_METHOD1(mwrite, void(const string &s));

    virtual Action entry()
    {
        string s(message()->data()->data(), message()->data()->size());
        mwrite(s);
        return release_and_exit();
    }
};

void InvokeNotification(Notifiable *done)
{
    done->notify();
}

static void print_packet(const string &pkt)
{
    fprintf(stderr, "%s\n", pkt.c_str());
}

class AsyncCanTest : public testing::Test
{
public:
    static void SetUpTestCase()
    {
        g_gc_adapter =
            GCAdapterBase::CreateGridConnectAdapter(&gc_hub0, &can_hub0, false);
    }

    static void TearDownTestCase()
    {
        delete g_gc_adapter;
    }

protected:
    AsyncCanTest()
    {
        gc_hub0.register_port(&canBus_);
    }

    ~AsyncCanTest()
    {
        gc_hub0.unregister_port(&canBus_);
        if (printer_.get())
        {
            gc_hub0.unregister_port(printer_.get());
        }
    }

    /** Delays the current thread until we are certain that all asynchrnous
        processing has completed. */
    void wait()
    {
        wait_for_main_executor();
    }

/** Adds an expectation that the code will send a packet to the CANbus.

    Example:
    expect_packet(":X1954412DN05010101FFFF0000;");

    @param gc_packet the packet in GridConnect format, including the leading
    : and trailing ;
*/
#define expect_packet(gc_packet)                                               \
    EXPECT_CALL(canBus_, mwrite(StrCaseEq(gc_packet)))

    /** Ignores all produced packets.
     *
     *  Tihs can be used in tests where the expectations are tested in a higher
     *  level than monitoring the CANbus traffic.
    */
    void expect_any_packet()
    {
        EXPECT_CALL(canBus_, mwrite(_)).Times(AtLeast(0)).WillRepeatedly(
            WithArg<0>(Invoke(print_packet)));
    }

    /** Prints all packets sent to the canbus until the end of the current test
     * function.
    */
    void print_all_packets()
    {
        NiceMock<MockSend> *m = new NiceMock<MockSend>();
        EXPECT_CALL(*m, mwrite(_)).Times(AtLeast(0)).WillRepeatedly(
            WithArg<0>(Invoke(print_packet)));
        gc_hub0.register_port(m);
        printer_.reset(m);
    }

    /** Injects a packet to the interface. This acts as if a different node on
        the CANbus had sent that packet.

        Example:
        send_packet(":X195B4001N05010101FFFF0000;");

        @param gc_packet the packet in GridConnect format, including the leading
        : and trailing ;
    */
    void send_packet(const string &gc_packet)
    {
        Buffer<HubData> *packet;
        mainBufferPool->alloc(&packet);
        packet->data()->assign(gc_packet);
        packet->data()->skipMember_ = &canBus_;
        gc_hub0.send(packet);
    }

/** Injects an incoming packet to the interface and expects that the node
    will send out a response packet for it.

    As a side effect, clears all pending expectations on the CANbus.

    Example:
    send_packet_and_expect_response(":X198F4001N05010101FFFF0000;",
                                    ":X194C412DN05010101FFFF0000;");

    @param pkt is the packet to inject, in GridConnect format.
    @param resp is the response to expect, also in GridConnect format.
*/
#define send_packet_and_expect_response(pkt, resp)                             \
    do                                                                         \
    {                                                                          \
        expect_packet(resp);                                                   \
        send_packet_and_flush_expect(pkt);                                     \
    } while (0)

    void send_packet_and_flush_expect(const string &pkt)
    {
        send_packet(pkt);
        wait();
        Mock::VerifyAndClear(&canBus_);
    }

    /// Helper object for setting expectations on the packets sent on the bus.
    NiceMock<MockSend> canBus_;
    /// Object for debug-printing every packet (if requested).
    std::unique_ptr<HubPort> printer_;
};

namespace nmranet
{

/*
const char *Node::MANUFACTURER = "Stuart W. Baker";
const char *Node::HARDWARE_REV = "N/A";
const char *Node::SOFTWARE_REV = "0.1";

const size_t Datagram::POOL_SIZE = 10;
const size_t Datagram::THREAD_STACK_SIZE = 512;
const size_t Stream::CHANNELS_PER_NODE = 10;
const uint16_t Stream::MAX_BUFFER_SIZE = 512;*/

static const NodeID TEST_NODE_ID = 0x02010d000003ULL;

/** Test fixture base class with helper methods for exercising the asynchronous
 * interface code.
 *
 * Usage:
 *
 * Inherit your test fixture class from AsyncIfTest.
 */
class AsyncIfTest : public AsyncCanTest
{
protected:
    AsyncIfTest()
        : pendingAliasAllocation_(false)
    {
        ifCan_.reset(new IfCan(&g_executor, &can_hub0, 10, 10, 5));
        ifCan_->local_aliases()->add(TEST_NODE_ID, 0x22A);
    }

    ~AsyncIfTest()
    {
        wait();
        if (pendingAliasAllocation_)
        {
            ifCan_->alias_allocator()->TEST_finish_pending_allocation();
            wait();
        }
    }

    friend class ::TrainTestHelper;

    /** Creates an alias allocator flow, and injects an already allocated
     *  alias. */
    void create_allocated_alias()
    {
        ifCan_->set_alias_allocator(
            new AliasAllocator(TEST_NODE_ID, ifCan_.get()));
        Buffer<AliasInfo> *a;
        mainBufferPool->alloc(&a);
        a->data()->alias = 0x33A;
        a->data()->state = AliasInfo::STATE_RESERVED;
        ifCan_->local_aliases()->add(AliasCache::RESERVED_ALIAS_NODE_ID,
                                     a->data()->alias);
        ifCan_->alias_allocator()->reserved_aliases()->insert(a);
        aliasSeed_ = 0x44C;
        pendingAliasAllocation_ = false;
    }

    void expect_next_alias_allocation(NodeAlias a = 0)
    {
        pendingAliasAllocation_ = true;
        if (!a)
        {
            ifCan_->alias_allocator()->seed_ = aliasSeed_;
            a = aliasSeed_;
            aliasSeed_++;
        }
        EXPECT_CALL(canBus_, mwrite(StringPrintf(":X17020%03XN;", a)))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(canBus_, mwrite(StringPrintf(":X1610D%03XN;", a)))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(canBus_, mwrite(StringPrintf(":X15000%03XN;", a)))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(canBus_, mwrite(StringPrintf(":X14003%03XN;", a)))
            .Times(1)
            .RetiresOnSaturation();

        EXPECT_CALL(canBus_, mwrite(StringPrintf(":X10700%03XN;", a)))
            .Times(AtMost(1))
            .RetiresOnSaturation();
    }

    BarrierNotifiable *get_notifiable()
    {
        bn_.reset(&n_);
        return &bn_;
    }

    void wait_for_notification()
    {
        n_.wait_for_notification();
    }

    SyncNotifiable n_;
    BarrierNotifiable bn_;

    /// The interface under test.
    std::unique_ptr<IfCan> ifCan_;
    /** Temporary object used to send aliases around in the alias allocator
     *  flow. */
    AliasInfo testAlias_;
    /// The next alias we will make the allocator create.
    NodeAlias aliasSeed_;
    /// true if we have a pending async alias allocation task.
    bool pendingAliasAllocation_;
};

class AsyncNodeTest : public AsyncIfTest
{
protected:
    AsyncNodeTest()
        : eventService_(ifCan_.get())
    {
        EXPECT_CALL(canBus_, mwrite(":X1910022AN02010D000003;")).Times(1);
        ownedNode_.reset(new DefaultNode(ifCan_.get(), TEST_NODE_ID));
        node_ = ownedNode_.get();
        ifCan_->add_addressed_message_support();
        wait();
        Mock::VerifyAndClear(&canBus_);
        // AddEventHandlerToIf(ifCan_.get());
    }

    ~AsyncNodeTest()
    {
        wait_for_event_thread();
    }

    void wait_for_event_thread()
    {
        while (EventService::instance->event_processing_pending())
        {
            usleep(100);
        }
        AsyncIfTest::wait();
    }

    EventService eventService_;
    std::unique_ptr<DefaultNode> ownedNode_;
    Node *node_;
};

class MockMessageHandler : public MessageHandler
{
public:
    MOCK_METHOD2(handle_message,
                 void(NMRAnetMessage *message, unsigned priority));
    virtual void send(Buffer<NMRAnetMessage> *message, unsigned priority)
    {
        handle_message(message->data(), priority);
        message->unref();
    }
};

MATCHER_P(IsBufferValue, id, "")
{
    uint64_t value = htobe64(id);
    if (arg.size() != 8)
        return false;
    if (memcmp(&value, arg.data(), 8))
        return false;
    return true;
}

MATCHER_P(IsBufferValueString, expected, "")
{
    string s(expected);
    return arg == s;
}

MATCHER_P(IsBufferNodeValue, id, "")
{
    uint64_t value = htobe64(id);
    if (arg->used() != 6)
        return false;
    uint8_t *expected = reinterpret_cast<uint8_t *>(&value) + 2;
    uint8_t *actual = static_cast<uint8_t *>(arg->start());
    if (memcmp(expected, actual, 6))
    {
        for (int i = 0; i < 6; ++i)
        {
            if (expected[i] != actual[i])
            {
                LOG(INFO, "mismatch at position %d, expected %02x actual %02x",
                    i, expected[i], actual[i]);
            }
        }
        return false;
    }
    return true;
}

MATCHER_P(IsBufferNodeValueString, id, "")
{
    uint64_t value = htobe64(id);
    if (arg.size() != 6)
        return false;
    uint8_t *expected = reinterpret_cast<uint8_t *>(&value) + 2;
    uint8_t *actual = static_cast<uint8_t *>(arg->start());
    if (memcmp(expected, actual, 6))
    {
        for (int i = 0; i < 6; ++i)
        {
            if (expected[i] != actual[i])
            {
                LOG(INFO, "mismatch at position %d, expected %02x actual %02x",
                    i, expected[i], actual[i]);
            }
        }
        return false;
    }
    return true;
}

} // namespace nmranet

#endif // _UTILS_ASYNC_IF_TEST_HELPER_HXX_
<commit_msg>Adds an assertion guard against calling print_all_packets multiple times. It would lead to a crash anyway.<commit_after>// Helper classes for writing unittests testing the entire asynchronous
// stack. Allows to send incoming messages (in gridconnect format) and set
// expectations on messages produced.
//
// Only include this file in unittests.

#ifndef _UTILS_ASYNC_IF_TEST_HELPER_HXX_
#define _UTILS_ASYNC_IF_TEST_HELPER_HXX_

#include "nmranet/AliasAllocator.hxx"
#include "nmranet/IfCan.hxx"
#include "nmranet/EventService.hxx"
#include "nmranet/DefaultNode.hxx"
#include "nmranet_config.h"
#include "utils/GridConnectHub.hxx"
#include "utils/test_main.hxx"

using ::testing::AtLeast;
using ::testing::AtMost;
using ::testing::Eq;
using ::testing::Field;
using ::testing::Invoke;
using ::testing::IsNull;
using ::testing::Mock;
using ::testing::NiceMock;
using ::testing::NotNull;
using ::testing::Pointee;
using ::testing::Return;
using ::testing::StrCaseEq;
using ::testing::StrictMock;
using ::testing::WithArg;
using ::testing::_;

class TrainTestHelper;

///@todo(balazs.racz) remove
// void (*g_invoke)(Notifiable *) = &InvokeNotification;

HubFlow gc_hub0(&g_service);
CanHubFlow can_hub0(&g_service);
GCAdapterBase *g_gc_adapter = nullptr;

HubFlow gc_hub1(&g_service);
CanHubFlow can_hub1(&g_service);
GCAdapterBase *g_gc_adapter1 = nullptr;

/** Helper class for setting expectation on the CANbus traffic in unit
 * tests. */
class MockSend : public HubPort
{
public:
    MockSend()
        : HubPort(&g_service)
    {
    }

    MOCK_METHOD1(mwrite, void(const string &s));

    virtual Action entry()
    {
        string s(message()->data()->data(), message()->data()->size());
        mwrite(s);
        return release_and_exit();
    }
};

void InvokeNotification(Notifiable *done)
{
    done->notify();
}

static void print_packet(const string &pkt)
{
    fprintf(stderr, "%s\n", pkt.c_str());
}

class AsyncCanTest : public testing::Test
{
public:
    static void SetUpTestCase()
    {
        g_gc_adapter =
            GCAdapterBase::CreateGridConnectAdapter(&gc_hub0, &can_hub0, false);
    }

    static void TearDownTestCase()
    {
        delete g_gc_adapter;
    }

protected:
    AsyncCanTest()
    {
        gc_hub0.register_port(&canBus_);
    }

    ~AsyncCanTest()
    {
        gc_hub0.unregister_port(&canBus_);
        if (printer_.get())
        {
            gc_hub0.unregister_port(printer_.get());
        }
    }

    /** Delays the current thread until we are certain that all asynchrnous
        processing has completed. */
    void wait()
    {
        wait_for_main_executor();
    }

/** Adds an expectation that the code will send a packet to the CANbus.

    Example:
    expect_packet(":X1954412DN05010101FFFF0000;");

    @param gc_packet the packet in GridConnect format, including the leading
    : and trailing ;
*/
#define expect_packet(gc_packet)                                               \
    EXPECT_CALL(canBus_, mwrite(StrCaseEq(gc_packet)))

    /** Ignores all produced packets.
     *
     *  Tihs can be used in tests where the expectations are tested in a higher
     *  level than monitoring the CANbus traffic.
    */
    void expect_any_packet()
    {
        EXPECT_CALL(canBus_, mwrite(_)).Times(AtLeast(0)).WillRepeatedly(
            WithArg<0>(Invoke(print_packet)));
    }

    /** Prints all packets sent to the canbus until the end of the current test
     * function.
    */
    void print_all_packets()
    {
        HASSERT(!printer_ && "cannot have more than one print_all_packets call");
        NiceMock<MockSend> *m = new NiceMock<MockSend>();
        EXPECT_CALL(*m, mwrite(_)).Times(AtLeast(0)).WillRepeatedly(
            WithArg<0>(Invoke(print_packet)));
        gc_hub0.register_port(m);
        printer_.reset(m);
    }

    /** Injects a packet to the interface. This acts as if a different node on
        the CANbus had sent that packet.

        Example:
        send_packet(":X195B4001N05010101FFFF0000;");

        @param gc_packet the packet in GridConnect format, including the leading
        : and trailing ;
    */
    void send_packet(const string &gc_packet)
    {
        Buffer<HubData> *packet;
        mainBufferPool->alloc(&packet);
        packet->data()->assign(gc_packet);
        packet->data()->skipMember_ = &canBus_;
        gc_hub0.send(packet);
    }

/** Injects an incoming packet to the interface and expects that the node
    will send out a response packet for it.

    As a side effect, clears all pending expectations on the CANbus.

    Example:
    send_packet_and_expect_response(":X198F4001N05010101FFFF0000;",
                                    ":X194C412DN05010101FFFF0000;");

    @param pkt is the packet to inject, in GridConnect format.
    @param resp is the response to expect, also in GridConnect format.
*/
#define send_packet_and_expect_response(pkt, resp)                             \
    do                                                                         \
    {                                                                          \
        expect_packet(resp);                                                   \
        send_packet_and_flush_expect(pkt);                                     \
    } while (0)

    void send_packet_and_flush_expect(const string &pkt)
    {
        send_packet(pkt);
        wait();
        Mock::VerifyAndClear(&canBus_);
    }

    /// Helper object for setting expectations on the packets sent on the bus.
    NiceMock<MockSend> canBus_;
    /// Object for debug-printing every packet (if requested).
    std::unique_ptr<HubPort> printer_;
};

namespace nmranet
{

/*
const char *Node::MANUFACTURER = "Stuart W. Baker";
const char *Node::HARDWARE_REV = "N/A";
const char *Node::SOFTWARE_REV = "0.1";

const size_t Datagram::POOL_SIZE = 10;
const size_t Datagram::THREAD_STACK_SIZE = 512;
const size_t Stream::CHANNELS_PER_NODE = 10;
const uint16_t Stream::MAX_BUFFER_SIZE = 512;*/

static const NodeID TEST_NODE_ID = 0x02010d000003ULL;

/** Test fixture base class with helper methods for exercising the asynchronous
 * interface code.
 *
 * Usage:
 *
 * Inherit your test fixture class from AsyncIfTest.
 */
class AsyncIfTest : public AsyncCanTest
{
protected:
    AsyncIfTest()
        : pendingAliasAllocation_(false)
    {
        ifCan_.reset(new IfCan(&g_executor, &can_hub0, 10, 10, 5));
        ifCan_->local_aliases()->add(TEST_NODE_ID, 0x22A);
    }

    ~AsyncIfTest()
    {
        wait();
        if (pendingAliasAllocation_)
        {
            ifCan_->alias_allocator()->TEST_finish_pending_allocation();
            wait();
        }
    }

    friend class ::TrainTestHelper;

    /** Creates an alias allocator flow, and injects an already allocated
     *  alias. */
    void create_allocated_alias()
    {
        ifCan_->set_alias_allocator(
            new AliasAllocator(TEST_NODE_ID, ifCan_.get()));
        Buffer<AliasInfo> *a;
        mainBufferPool->alloc(&a);
        a->data()->alias = 0x33A;
        a->data()->state = AliasInfo::STATE_RESERVED;
        ifCan_->local_aliases()->add(AliasCache::RESERVED_ALIAS_NODE_ID,
                                     a->data()->alias);
        ifCan_->alias_allocator()->reserved_aliases()->insert(a);
        aliasSeed_ = 0x44C;
        pendingAliasAllocation_ = false;
    }

    void expect_next_alias_allocation(NodeAlias a = 0)
    {
        pendingAliasAllocation_ = true;
        if (!a)
        {
            ifCan_->alias_allocator()->seed_ = aliasSeed_;
            a = aliasSeed_;
            aliasSeed_++;
        }
        EXPECT_CALL(canBus_, mwrite(StringPrintf(":X17020%03XN;", a)))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(canBus_, mwrite(StringPrintf(":X1610D%03XN;", a)))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(canBus_, mwrite(StringPrintf(":X15000%03XN;", a)))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(canBus_, mwrite(StringPrintf(":X14003%03XN;", a)))
            .Times(1)
            .RetiresOnSaturation();

        EXPECT_CALL(canBus_, mwrite(StringPrintf(":X10700%03XN;", a)))
            .Times(AtMost(1))
            .RetiresOnSaturation();
    }

    BarrierNotifiable *get_notifiable()
    {
        bn_.reset(&n_);
        return &bn_;
    }

    void wait_for_notification()
    {
        n_.wait_for_notification();
    }

    SyncNotifiable n_;
    BarrierNotifiable bn_;

    /// The interface under test.
    std::unique_ptr<IfCan> ifCan_;
    /** Temporary object used to send aliases around in the alias allocator
     *  flow. */
    AliasInfo testAlias_;
    /// The next alias we will make the allocator create.
    NodeAlias aliasSeed_;
    /// true if we have a pending async alias allocation task.
    bool pendingAliasAllocation_;
};

class AsyncNodeTest : public AsyncIfTest
{
protected:
    AsyncNodeTest()
        : eventService_(ifCan_.get())
    {
        EXPECT_CALL(canBus_, mwrite(":X1910022AN02010D000003;")).Times(1);
        ownedNode_.reset(new DefaultNode(ifCan_.get(), TEST_NODE_ID));
        node_ = ownedNode_.get();
        ifCan_->add_addressed_message_support();
        wait();
        Mock::VerifyAndClear(&canBus_);
        // AddEventHandlerToIf(ifCan_.get());
    }

    ~AsyncNodeTest()
    {
        wait_for_event_thread();
    }

    void wait_for_event_thread()
    {
        while (EventService::instance->event_processing_pending())
        {
            usleep(100);
        }
        AsyncIfTest::wait();
    }

    EventService eventService_;
    std::unique_ptr<DefaultNode> ownedNode_;
    Node *node_;
};

class MockMessageHandler : public MessageHandler
{
public:
    MOCK_METHOD2(handle_message,
                 void(NMRAnetMessage *message, unsigned priority));
    virtual void send(Buffer<NMRAnetMessage> *message, unsigned priority)
    {
        handle_message(message->data(), priority);
        message->unref();
    }
};

MATCHER_P(IsBufferValue, id, "")
{
    uint64_t value = htobe64(id);
    if (arg.size() != 8)
        return false;
    if (memcmp(&value, arg.data(), 8))
        return false;
    return true;
}

MATCHER_P(IsBufferValueString, expected, "")
{
    string s(expected);
    return arg == s;
}

MATCHER_P(IsBufferNodeValue, id, "")
{
    uint64_t value = htobe64(id);
    if (arg->used() != 6)
        return false;
    uint8_t *expected = reinterpret_cast<uint8_t *>(&value) + 2;
    uint8_t *actual = static_cast<uint8_t *>(arg->start());
    if (memcmp(expected, actual, 6))
    {
        for (int i = 0; i < 6; ++i)
        {
            if (expected[i] != actual[i])
            {
                LOG(INFO, "mismatch at position %d, expected %02x actual %02x",
                    i, expected[i], actual[i]);
            }
        }
        return false;
    }
    return true;
}

MATCHER_P(IsBufferNodeValueString, id, "")
{
    uint64_t value = htobe64(id);
    if (arg.size() != 6)
        return false;
    uint8_t *expected = reinterpret_cast<uint8_t *>(&value) + 2;
    uint8_t *actual = static_cast<uint8_t *>(arg->start());
    if (memcmp(expected, actual, 6))
    {
        for (int i = 0; i < 6; ++i)
        {
            if (expected[i] != actual[i])
            {
                LOG(INFO, "mismatch at position %d, expected %02x actual %02x",
                    i, expected[i], actual[i]);
            }
        }
        return false;
    }
    return true;
}

} // namespace nmranet

#endif // _UTILS_ASYNC_IF_TEST_HELPER_HXX_
<|endoftext|>


<commit_before>
/*!
 * \file cuda_utilities_tests.cpp
 * \author Robert 'Bob' Caddy (rvc@pitt.edu), Helena Richie (helenarichie@pitt.edu)
 * \brief Tests for the contents of cuda_utilities.h and cuda_utilities.cpp
 *
 */

// STL Includes
#include <vector>
#include <string>
#include <iostream>

// External Includes
#include <gtest/gtest.h>    // Include GoogleTest and related libraries/headers

// Local Includes
#include "../utils/testing_utilities.h"
#include "../utils/cuda_utilities.h"
#include "../global/global.h"

/*
 PCM : n_ghost = 2
 PLMP : n_ghost = 2
 PLMC : n_ghost = 3
 PPMP : n_ghost = 4
 PPMC : n_ghost = 4
*/

// =============================================================================
// Local helper functions
namespace
{
    struct TestParams
    {
        std::vector<int> n_ghost {2, 2, 3, 4};
        std::vector<int> nx {1, 2048, 2048, 2048};
        std::vector<int> ny {1, 2048, 2048, 2048};
        std::vector<int> nz {1, 4096, 4096, 4096};
        std::vector<std::string> names {"Single-cell 3D PCM/PLMP case", "Large 3D PCM/PLMP case", "Large PLMC case", "Large PPMP/PPMC case"};

    };
}

TEST(tHYDROSYSTEMCudaUtilsGetRealIndices, CorrectInputExpectCorrectOutput) {
    TestParams parameters;
    std::vector<std::vector<int>> fiducial_indices {{1, 2, 3, 4, 5, 6},
                                               {4, 5, 6, 7, 8, 9},
                                               {7, 8, 9, 4, 5, 6},
                                               {7, 8, 9, 4, 5, 6}};

    for (size_t i = 0; i < parameters.names.size(); i++)
    {
        int is;
        int ie;
        int js;
        int je;
        int ks;
        int ke;
        cuda_utilities::Get_Real_Indices(parameters.n_ghost.at(i), parameters.nx.at(i), parameters.ny.at(i), parameters.nz.at(i), is, ie, js, je, ks, ke);

        std::vector<int> test_indices {is, ie, js, je, ks, ke};
        std::vector<std::string> index_names {"is", "ie", "js", "je", "ks", "ke"};

        for (int j = 0; j < test_indices.size(); j++)
        {
            testingUtilities::checkResults(fiducial_indices[i][j], test_indices[i], index_names[j] + " " + parameters.names[i]);
        }
    }
}<commit_msg>add test for real index utility function<commit_after>
/*!
 * \file cuda_utilities_tests.cpp
 * \author Robert 'Bob' Caddy (rvc@pitt.edu), Helena Richie (helenarichie@pitt.edu)
 * \brief Tests for the contents of cuda_utilities.h and cuda_utilities.cpp
 *
 */

// STL Includes
#include <vector>
#include <string>
#include <iostream>

// External Includes
#include <gtest/gtest.h>    // Include GoogleTest and related libraries/headers

// Local Includes
#include "../utils/testing_utilities.h"
#include "../utils/cuda_utilities.h"
#include "../global/global.h"

/*
 PCM : n_ghost = 2
 PLMP : n_ghost = 2
 PLMC : n_ghost = 3
 PPMP : n_ghost = 4
 PPMC : n_ghost = 4
*/

// =============================================================================
// Local helper functions
namespace
{
    struct TestParams
    {
        std::vector<int> n_ghost {2, 2, 3, 4};
        std::vector<int> nx {100, 2048, 2048, 2048};
        std::vector<int> ny {1, 2048, 2048, 2048};
        std::vector<int> nz {1, 4096, 4096, 4096};
        std::vector<std::string> names {"Single-cell 3D PCM/PLMP case", "Large 3D PCM/PLMP case", "Large PLMC case", "Large PPMP/PPMC case"};

    };
}

TEST(tHYDROSYSTEMCudaUtilsGetRealIndices, CorrectInputExpectCorrectOutput) {
    TestParams parameters;
    std::vector<std::vector<int>> fiducial_indices {{2, 98, 0, 1, 0, 1},
                                               {2, 2046, 2, 2046, 2, 2046},
                                               {3, 2045, 3, 2045, 3, 2045},
                                               {4, 2044, 4, 2044, 4, 2044}};

    for (size_t i = 0; i < parameters.names.size(); i++)
    {
        int is;
        int ie;
        int js;
        int je;
        int ks;
        int ke;
        cuda_utilities::Get_Real_Indices(parameters.n_ghost.at(i), parameters.nx.at(i), parameters.ny.at(i), parameters.nz.at(i), is, ie, js, je, ks, ke);

        std::vector<std::string> index_names {"is", "ie", "js", "je", "ks", "ke"};
        std::vector<int> test_indices {is, ie, js, je, ks, ke};

        std::cout >> test_indices << "\n"

        for (size_t j = 0; j < test_indices.size(); j++)
        {
            testingUtilities::checkResults(fiducial_indices[i][j], test_indices[j], index_names[j] + " " + parameters.names[i]);
        }
    }
}<|endoftext|>


<commit_before>/*ckwg +5
 * Copyright 2011 by Kitware, Inc. All Rights Reserved. Please refer to
 * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel,
 * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065.
 */

#include "homography.h"

#include "plane_ref.h"
#include "timestamp.h"
#include "utm.h"

/**
 * \file homography.cxx
 *
 * \brief Implementation of the homography classes.
 */

namespace vistk
{

homography_base
::homography_base(homography_base const& h)
  : m_transform(h.m_transform)
  , m_valid(h.m_valid)
  , m_new_reference(h.m_new_reference)
{
}

homography_base
::~homography_base()
{
}

homography_base::transform_t const&
homography_base
::transform() const
{
  return m_transform;
}

bool
homography_base
::is_valid() const
{
  return m_valid;
}

bool
homography_base
::is_new_reference() const
{
  return m_new_reference;
}

void
homography_base
::set_transform(transform_t const& trans)
{
  m_transform = trans;
}

void
homography_base
::set_identity()
{
  m_transform.set_identity();
}

void
homography_base
::set_valid(bool valid)
{
  m_valid = valid;
}

void
homography_base
::set_new_reference(bool new_reference)
{
  m_new_reference = new_reference;
}

bool
homography_base
::operator == (homography_base const& h) const
{
  return ((m_valid == h.m_valid) &&
          (m_new_reference == h.m_new_reference) &&
          (m_transform == h.m_transform));
}

homography_base
::homography_base()
  : m_valid(false)
  , m_new_reference(false)
{
  set_identity();
}

template <typename Source, typename Dest>
homography<Source, Dest>
::homography()
{
}

template <typename Source, typename Dest>
homography<Source, Dest>
::homography(self_t const& h)
  : homography_base(h)
  , m_source(h.m_source)
  , m_dest(h.m_dest)
{
}

template <typename Source, typename Dest>
homography<Source, Dest>
::~homography()
{
}

template <typename Source, typename Dest>
typename homography<Source, Dest>::source_t
homography<Source, Dest>
::source() const
{
  return m_source;
}

template <typename Source, typename Dest>
typename homography<Source, Dest>::dest_t
homography<Source, Dest>
::destination() const
{
  return m_dest;
}

template <typename Source, typename Dest>
void
homography<Source, Dest>
::set_source(source_t const& src)
{
  m_source = src;
}

template <typename Source, typename Dest>
void
homography<Source, Dest>
::set_destination(dest_t const& dest)
{
  m_dest = dest;
}

template <typename Source, typename Dest>
typename homography<Source, Dest>::inverse_t
homography<Source, Dest>
::inverse() const
{
  inverse_t result;

  result.set_transform(transform().get_inverse());
  result.set_source(m_dest);
  result.set_destination(m_source);
  result.set_valid(is_valid());
  result.set_new_reference(is_new_reference());

  return result;
}

template <typename Source, typename Dest>
bool
homography<Source, Dest>
::operator == (self_t const& h) const
{
  return ((homography::operator == (h)) &&
          (m_source == h.m_source) &&
          (m_dest == h.m_dest));
}

// Keep in sync with homography_types.h
template class homography<timestamp, timestamp>;
template class homography<timestamp, plane_ref_t>;
template class homography<plane_ref_t, timestamp>;
template class homography<timestamp, utm_zone_t>;
template class homography<utm_zone_t, timestamp>;
template class homography<plane_ref_t, utm_zone_t>;
template class homography<utm_zone_t, plane_ref_t>;

template <typename Source, typename Shared, typename Dest>
homography<Source, Dest>
operator * (homography<Shared, Dest> const& l, homography<Source, Shared> const& r)
{
  homography<Source, Dest> result;

  result.set_transform(l.transform() * r.transform());
  result.set_source(r.source());
  result.set_destination(l.dest());
  result.set_valid(l.is_valid() && r.is_valid());

  return result;
}

#ifndef DOXYGEN_IGNORE

/**
 * \def INSTANTIATE_SELF_MULT
 *
 * \brief Instantiates multiplication with homogeneous types.
 *
 * \param X The reference plane for the multiplication.
 */
#define INSTANTIATE_SELF_MULT(X) \
  template <> homography<X, X> operator * <X, X, X>(homography<X, X> const&, homography<X, X> const&)

/**
 * \def INSTANTIATE_DUAL_MULT_RAW
 *
 * \brief Instantiates multiplication with two distinct types.
 *
 * \param X The first type in the multiplication.
 * \param Y The second type in the multiplication.
 */
#define INSTANTIATE_DUAL_MULT_RAW(X, Y) \
  template <> homography<X, X> operator * <X, Y, X>(homography<Y, X> const&, homography<X, Y> const&); \
  template <> homography<X, Y> operator * <X, X, Y>(homography<X, Y> const&, homography<X, X> const&); \
  template <> homography<X, Y> operator * <X, Y, Y>(homography<Y, Y> const&, homography<X, Y> const&)

/**
 * \def INSTANTIATE_DUAL_MULT
 *
 * \brief Instantiates multiplication with two distinct types.
 *
 * This instantiates all permutations between the two types.
 *
 * \param X The first type in the multiplication.
 * \param Y The second type in the multiplication.
 */
#define INSTANTIATE_DUAL_MULT(X, Y) \
  INSTANTIATE_DUAL_MULT_RAW(X, Y);  \
  INSTANTIATE_DUAL_MULT_RAW(Y, X)

/**
 * \def INSTANTIATE_TRIP_MULT_RAW
 *
 * \brief Instantiates multiplication with three distinct types.
 *
 * \param X The source type in the multiplication.
 * \param Y The shared type in the multiplication.
 * \param Z The destination type in the multiplication.
 */
#define INSTANTIATE_TRIP_MULT_RAW(X,Y,Z) \
  template <> homography<X, Z> operator * <X, Y, Z>(homography<Y, Z> const&, homography<X, Y> const&)

/**
 * \def INSTANTIATE_TRIP_MULT
 *
 * \brief Instantiates multiplication with three distinct types.
 *
 * This instantiates all permutations between the three types.
 *
 * \param X The first type in the multiplication.
 * \param Y The second type in the multiplication.
 * \param Z The third type in the multiplication.
 */
#define INSTANTIATE_TRIP_MULT(X,Y,Z)  \
  INSTANTIATE_TRIP_MULT_RAW(X, Y, Z); \
  INSTANTIATE_TRIP_MULT_RAW(X, Z, Y); \
  INSTANTIATE_TRIP_MULT_RAW(Y, X, Z); \
  INSTANTIATE_TRIP_MULT_RAW(Y, Z, Y); \
  INSTANTIATE_TRIP_MULT_RAW(Z, X, Y); \
  INSTANTIATE_TRIP_MULT_RAW(Z, Y, X)

// Instantiate all allowable types
INSTANTIATE_SELF_MULT(timestamp);
INSTANTIATE_SELF_MULT(plane_ref_t);
INSTANTIATE_SELF_MULT(utm_zone_t);

INSTANTIATE_DUAL_MULT(timestamp,   plane_ref_t);
INSTANTIATE_DUAL_MULT(timestamp,   utm_zone_t);
INSTANTIATE_DUAL_MULT(plane_ref_t, utm_zone_t);

INSTANTIATE_TRIP_MULT(timestamp,   plane_ref_t, utm_zone_t);

#undef INSTANTIATE_SELF_MULT
#undef INSTANTIATE_DUAL_MULT_RAW
#undef INSTANTIATE_DUAL_MULT
#undef INSTANTIATE_TRIP_MULT_RAW
#undef INSTANTIATE_TRIP_MULT

#endif // DOXYGEN_IGNORE

}
<commit_msg>Call the proper operator ==<commit_after>/*ckwg +5
 * Copyright 2011-2012 by Kitware, Inc. All Rights Reserved. Please refer to
 * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel,
 * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065.
 */

#include "homography.h"

#include "plane_ref.h"
#include "timestamp.h"
#include "utm.h"

/**
 * \file homography.cxx
 *
 * \brief Implementation of the homography classes.
 */

namespace vistk
{

homography_base
::homography_base(homography_base const& h)
  : m_transform(h.m_transform)
  , m_valid(h.m_valid)
  , m_new_reference(h.m_new_reference)
{
}

homography_base
::~homography_base()
{
}

homography_base::transform_t const&
homography_base
::transform() const
{
  return m_transform;
}

bool
homography_base
::is_valid() const
{
  return m_valid;
}

bool
homography_base
::is_new_reference() const
{
  return m_new_reference;
}

void
homography_base
::set_transform(transform_t const& trans)
{
  m_transform = trans;
}

void
homography_base
::set_identity()
{
  m_transform.set_identity();
}

void
homography_base
::set_valid(bool valid)
{
  m_valid = valid;
}

void
homography_base
::set_new_reference(bool new_reference)
{
  m_new_reference = new_reference;
}

bool
homography_base
::operator == (homography_base const& h) const
{
  return ((m_valid == h.m_valid) &&
          (m_new_reference == h.m_new_reference) &&
          (m_transform == h.m_transform));
}

homography_base
::homography_base()
  : m_valid(false)
  , m_new_reference(false)
{
  set_identity();
}

template <typename Source, typename Dest>
homography<Source, Dest>
::homography()
{
}

template <typename Source, typename Dest>
homography<Source, Dest>
::homography(self_t const& h)
  : homography_base(h)
  , m_source(h.m_source)
  , m_dest(h.m_dest)
{
}

template <typename Source, typename Dest>
homography<Source, Dest>
::~homography()
{
}

template <typename Source, typename Dest>
typename homography<Source, Dest>::source_t
homography<Source, Dest>
::source() const
{
  return m_source;
}

template <typename Source, typename Dest>
typename homography<Source, Dest>::dest_t
homography<Source, Dest>
::destination() const
{
  return m_dest;
}

template <typename Source, typename Dest>
void
homography<Source, Dest>
::set_source(source_t const& src)
{
  m_source = src;
}

template <typename Source, typename Dest>
void
homography<Source, Dest>
::set_destination(dest_t const& dest)
{
  m_dest = dest;
}

template <typename Source, typename Dest>
typename homography<Source, Dest>::inverse_t
homography<Source, Dest>
::inverse() const
{
  inverse_t result;

  result.set_transform(transform().get_inverse());
  result.set_source(m_dest);
  result.set_destination(m_source);
  result.set_valid(is_valid());
  result.set_new_reference(is_new_reference());

  return result;
}

template <typename Source, typename Dest>
bool
homography<Source, Dest>
::operator == (self_t const& h) const
{
  return ((homography_base::operator == (h)) &&
          (m_source == h.m_source) &&
          (m_dest == h.m_dest));
}

// Keep in sync with homography_types.h
template class homography<timestamp, timestamp>;
template class homography<timestamp, plane_ref_t>;
template class homography<plane_ref_t, timestamp>;
template class homography<timestamp, utm_zone_t>;
template class homography<utm_zone_t, timestamp>;
template class homography<plane_ref_t, utm_zone_t>;
template class homography<utm_zone_t, plane_ref_t>;

template <typename Source, typename Shared, typename Dest>
homography<Source, Dest>
operator * (homography<Shared, Dest> const& l, homography<Source, Shared> const& r)
{
  homography<Source, Dest> result;

  result.set_transform(l.transform() * r.transform());
  result.set_source(r.source());
  result.set_destination(l.dest());
  result.set_valid(l.is_valid() && r.is_valid());

  return result;
}

#ifndef DOXYGEN_IGNORE

/**
 * \def INSTANTIATE_SELF_MULT
 *
 * \brief Instantiates multiplication with homogeneous types.
 *
 * \param X The reference plane for the multiplication.
 */
#define INSTANTIATE_SELF_MULT(X) \
  template <> homography<X, X> operator * <X, X, X>(homography<X, X> const&, homography<X, X> const&)

/**
 * \def INSTANTIATE_DUAL_MULT_RAW
 *
 * \brief Instantiates multiplication with two distinct types.
 *
 * \param X The first type in the multiplication.
 * \param Y The second type in the multiplication.
 */
#define INSTANTIATE_DUAL_MULT_RAW(X, Y) \
  template <> homography<X, X> operator * <X, Y, X>(homography<Y, X> const&, homography<X, Y> const&); \
  template <> homography<X, Y> operator * <X, X, Y>(homography<X, Y> const&, homography<X, X> const&); \
  template <> homography<X, Y> operator * <X, Y, Y>(homography<Y, Y> const&, homography<X, Y> const&)

/**
 * \def INSTANTIATE_DUAL_MULT
 *
 * \brief Instantiates multiplication with two distinct types.
 *
 * This instantiates all permutations between the two types.
 *
 * \param X The first type in the multiplication.
 * \param Y The second type in the multiplication.
 */
#define INSTANTIATE_DUAL_MULT(X, Y) \
  INSTANTIATE_DUAL_MULT_RAW(X, Y);  \
  INSTANTIATE_DUAL_MULT_RAW(Y, X)

/**
 * \def INSTANTIATE_TRIP_MULT_RAW
 *
 * \brief Instantiates multiplication with three distinct types.
 *
 * \param X The source type in the multiplication.
 * \param Y The shared type in the multiplication.
 * \param Z The destination type in the multiplication.
 */
#define INSTANTIATE_TRIP_MULT_RAW(X,Y,Z) \
  template <> homography<X, Z> operator * <X, Y, Z>(homography<Y, Z> const&, homography<X, Y> const&)

/**
 * \def INSTANTIATE_TRIP_MULT
 *
 * \brief Instantiates multiplication with three distinct types.
 *
 * This instantiates all permutations between the three types.
 *
 * \param X The first type in the multiplication.
 * \param Y The second type in the multiplication.
 * \param Z The third type in the multiplication.
 */
#define INSTANTIATE_TRIP_MULT(X,Y,Z)  \
  INSTANTIATE_TRIP_MULT_RAW(X, Y, Z); \
  INSTANTIATE_TRIP_MULT_RAW(X, Z, Y); \
  INSTANTIATE_TRIP_MULT_RAW(Y, X, Z); \
  INSTANTIATE_TRIP_MULT_RAW(Y, Z, Y); \
  INSTANTIATE_TRIP_MULT_RAW(Z, X, Y); \
  INSTANTIATE_TRIP_MULT_RAW(Z, Y, X)

// Instantiate all allowable types
INSTANTIATE_SELF_MULT(timestamp);
INSTANTIATE_SELF_MULT(plane_ref_t);
INSTANTIATE_SELF_MULT(utm_zone_t);

INSTANTIATE_DUAL_MULT(timestamp,   plane_ref_t);
INSTANTIATE_DUAL_MULT(timestamp,   utm_zone_t);
INSTANTIATE_DUAL_MULT(plane_ref_t, utm_zone_t);

INSTANTIATE_TRIP_MULT(timestamp,   plane_ref_t, utm_zone_t);

#undef INSTANTIATE_SELF_MULT
#undef INSTANTIATE_DUAL_MULT_RAW
#undef INSTANTIATE_DUAL_MULT
#undef INSTANTIATE_TRIP_MULT_RAW
#undef INSTANTIATE_TRIP_MULT

#endif // DOXYGEN_IGNORE

}
<|endoftext|>


<commit_before>#include "halley/core/graphics/sprite/animation.h"
#include "halley/core/graphics/sprite/sprite_sheet.h"
#include "halley/core/graphics/material/material.h"
#include "halley/core/graphics/material/material_definition.h"
#include "halley/core/graphics/material/material_parameter.h"
#include "halley/core/api/halley_api.h"
#include "resources/resources.h"
#include "halley/bytes/byte_serializer.h"
#include <gsl/gsl_assert>
#include <utility>

using namespace Halley;

AnimationFrame::AnimationFrame(int frameNumber, int duration, const String& imageName, const SpriteSheet& sheet, const Vector<AnimationDirection>& directions)
	: duration(duration)
{
	const size_t n = directions.size();
	sprites.resize(n);
	for (size_t i = 0; i < n; i++) {
		sprites[i] = &sheet.getSprite(directions[i].needsToProcessFrameName(imageName) ? directions[i].getFrameName(frameNumber, imageName) : imageName);
	}
}

int AnimationFrame::getDuration() const
{
	return duration;
}

AnimationFrameDefinition::AnimationFrameDefinition()
	: frameNumber(-1)
{}

AnimationFrameDefinition::AnimationFrameDefinition(int frameNumber, int duration, String imageName)
	: imageName(std::move(imageName))
	, frameNumber(frameNumber)
	, duration(duration)
{
}

AnimationFrame AnimationFrameDefinition::makeFrame(const SpriteSheet& sheet, const Vector<AnimationDirection>& directions) const
{
	return AnimationFrame(frameNumber, duration, imageName, sheet, directions);
}

void AnimationFrameDefinition::serialize(Serializer& s) const
{
	s << imageName;
	s << frameNumber;
	s << duration;
}

void AnimationFrameDefinition::deserialize(Deserializer& s)
{
	s >> imageName;
	s >> frameNumber;
	s >> duration;
}

AnimationSequence::AnimationSequence() {}

AnimationSequence::AnimationSequence(String name, bool loop, bool noFlip)
	: name(std::move(name))
	, loop(loop)
	, noFlip(noFlip)
{}

void AnimationSequence::serialize(Serializer& s) const
{
	s << frameDefinitions;
	s << name;
	s << loop;
	s << noFlip;
}

void AnimationSequence::deserialize(Deserializer& s)
{
	s >> frameDefinitions;
	s >> name;
	s >> loop;
	s >> noFlip;
}

void AnimationSequence::addFrame(const AnimationFrameDefinition& animationFrameDefinition)
{
	frameDefinitions.push_back(animationFrameDefinition);
}

Rect4i AnimationSequence::getBounds() const
{
	Vector2i topLeft;
	Vector2i bottomRight;
	for (auto& frame: frames) {
		auto& sprite = frame.getSprite(0);
		if (sprite.size.x >= 0.1f && sprite.size.y > 0.0f) {
			auto offset = Vector2i(Vector2f(sprite.pivot * sprite.size).round());
			const Vector2i tl = -offset;
			const Vector2i br = Vector2i(sprite.size) - offset;

			topLeft = Vector2i::min(topLeft, tl);
			bottomRight = Vector2i::max(bottomRight, br);
		}
	}

	return Rect4i(topLeft, bottomRight);
}

AnimationDirection::AnimationDirection()
	: id(-1)
	, flip(false)
{}

AnimationDirection::AnimationDirection(String name, String fileName, bool flip, int id)
	: name(std::move(name))
	, fileName(std::move(fileName))
	, id(id)
	, flip(flip)
{
}

bool AnimationDirection::needsToProcessFrameName(const String& baseName) const
{
	return baseName.find("%f") != std::string::npos || baseName.find("%dir%") != std::string::npos;
}

String AnimationDirection::getFrameName(int frameNumber, String baseName) const
{
	try {
		baseName = baseName.replaceAll("%dir%", fileName);

		size_t frameToken = baseName.find("%f");
		if (frameToken != std::string::npos) {
			size_t endToken = baseName.find('%', frameToken + 1);
			if (endToken != std::string::npos) {
				int minWidth = 0;
				if (baseName[frameToken + 2] == ':' && baseName[frameToken + 3] == '0') {
					minWidth = baseName.subToInteger(frameToken + 4, endToken);
				}

				std::stringstream ss;
				ss << std::setfill('0') << std::setw(minWidth) << frameNumber;
				baseName = baseName.left(frameToken) + ss.str() + baseName.mid(endToken + 1);
			}
		}

		return baseName;
	} catch (...) {
		throw Exception("Invalid frame name: " + baseName, HalleyExceptions::Graphics);
	}
}

void AnimationDirection::serialize(Serializer& s) const
{
	s << name;
	s << fileName;
	s << id;
	s << flip;
}

void AnimationDirection::deserialize(Deserializer& s)
{
	s >> name;
	s >> fileName;
	s >> id;
	s >> flip;
}

Animation::Animation()
{}

std::unique_ptr<Animation> Animation::loadResource(ResourceLoader& loader)
{
	auto result = std::make_unique<Animation>();
	auto sData = loader.getStatic();
	Deserializer s(sData->getSpan());
	s >> *result;
	result->loadDependencies(loader);
	return result;
}

void Animation::reload(Resource&& resource)
{
	*this = std::move(dynamic_cast<Animation&>(resource));
}

void Animation::loadDependencies(ResourceLoader& loader)
{
	spriteSheet = loader.getAPI().getResource<SpriteSheet>(spriteSheetName);

	auto matDef = loader.getAPI().getResource<MaterialDefinition>(materialName);
	material = std::make_shared<Material>(matDef);
	material->set("tex0", spriteSheet->getTexture());

	for (auto& s: sequences) {
		for (auto& f : s.frameDefinitions) {
			s.frames.emplace_back(f.makeFrame(*spriteSheet, directions));
		}
	}
}

void Animation::setName(const String& n)
{
	name = n;
}

void Animation::setMaterialName(const String& n)
{
	materialName = n;
}

void Animation::setSpriteSheetName(const String& n)
{
	spriteSheetName = n;
}

void Animation::addSequence(const AnimationSequence& sequence)
{
	sequences.push_back(sequence);
}

void Animation::addDirection(const AnimationDirection& direction)
{
	directions.push_back(direction);
}

const AnimationSequence& Animation::getSequence(const String& seqName) const
{
	for (auto& seq: sequences) {
		if (seq.name == seqName) {
			return seq;
		}
	}
	return sequences.at(0);
}

const AnimationDirection& Animation::getDirection(const String& dirName) const
{
	Expects(directions.size() > 0);

	for (auto& dir : directions) {
		if (dir.name == dirName) {
			return dir;
		}
	}
	return directions[0];
}

const AnimationDirection& Animation::getDirection(int id) const
{
	Expects(id >= 0);
	Expects(directions.size() > 0);

	if (id < int(directions.size())) {
		return directions[id];
	} else {
		return directions[0];
	}
}

Vector2i Animation::getPivot() const
{
	return sequences.at(0).getFrame(0).getSprite(0).origPivot;
}

bool Animation::hasSequence(const String& seqName) const
{
	for (auto& s: sequences) {
		if (s.getName() == seqName) {
			return true;
		}
	}
	return false;
}

void Animation::serialize(Serializer& s) const
{
	s << name;
	s << spriteSheetName;
	s << materialName;
	s << sequences;
	s << directions;
}

void Animation::deserialize(Deserializer& s)
{
	s >> name;
	s >> spriteSheetName;
	s >> materialName;
	s >> sequences;
	s >> directions;
}
<commit_msg>Fix bounds calculation<commit_after>#include "halley/core/graphics/sprite/animation.h"
#include "halley/core/graphics/sprite/sprite_sheet.h"
#include "halley/core/graphics/material/material.h"
#include "halley/core/graphics/material/material_definition.h"
#include "halley/core/graphics/material/material_parameter.h"
#include "halley/core/api/halley_api.h"
#include "resources/resources.h"
#include "halley/bytes/byte_serializer.h"
#include <gsl/gsl_assert>
#include <utility>

using namespace Halley;

AnimationFrame::AnimationFrame(int frameNumber, int duration, const String& imageName, const SpriteSheet& sheet, const Vector<AnimationDirection>& directions)
	: duration(duration)
{
	const size_t n = directions.size();
	sprites.resize(n);
	for (size_t i = 0; i < n; i++) {
		sprites[i] = &sheet.getSprite(directions[i].needsToProcessFrameName(imageName) ? directions[i].getFrameName(frameNumber, imageName) : imageName);
	}
}

int AnimationFrame::getDuration() const
{
	return duration;
}

AnimationFrameDefinition::AnimationFrameDefinition()
	: frameNumber(-1)
{}

AnimationFrameDefinition::AnimationFrameDefinition(int frameNumber, int duration, String imageName)
	: imageName(std::move(imageName))
	, frameNumber(frameNumber)
	, duration(duration)
{
}

AnimationFrame AnimationFrameDefinition::makeFrame(const SpriteSheet& sheet, const Vector<AnimationDirection>& directions) const
{
	return AnimationFrame(frameNumber, duration, imageName, sheet, directions);
}

void AnimationFrameDefinition::serialize(Serializer& s) const
{
	s << imageName;
	s << frameNumber;
	s << duration;
}

void AnimationFrameDefinition::deserialize(Deserializer& s)
{
	s >> imageName;
	s >> frameNumber;
	s >> duration;
}

AnimationSequence::AnimationSequence() {}

AnimationSequence::AnimationSequence(String name, bool loop, bool noFlip)
	: name(std::move(name))
	, loop(loop)
	, noFlip(noFlip)
{}

void AnimationSequence::serialize(Serializer& s) const
{
	s << frameDefinitions;
	s << name;
	s << loop;
	s << noFlip;
}

void AnimationSequence::deserialize(Deserializer& s)
{
	s >> frameDefinitions;
	s >> name;
	s >> loop;
	s >> noFlip;
}

void AnimationSequence::addFrame(const AnimationFrameDefinition& animationFrameDefinition)
{
	frameDefinitions.push_back(animationFrameDefinition);
}

Rect4i AnimationSequence::getBounds() const
{
	Vector2i topLeft(99999, 99999);
	Vector2i bottomRight;
	for (auto& frame: frames) {
		auto& sprite = frame.getSprite(0);
		if (sprite.size.x >= 0.1f && sprite.size.y > 0.0f) {
			auto offset = Vector2i(Vector2f(sprite.pivot * sprite.size).round());
			const Vector2i tl = -offset;
			const Vector2i br = tl + Vector2i(sprite.size);

			topLeft = Vector2i::min(topLeft, tl);
			bottomRight = Vector2i::max(bottomRight, br);
		}
	}

	return Rect4i(topLeft, bottomRight);
}

AnimationDirection::AnimationDirection()
	: id(-1)
	, flip(false)
{}

AnimationDirection::AnimationDirection(String name, String fileName, bool flip, int id)
	: name(std::move(name))
	, fileName(std::move(fileName))
	, id(id)
	, flip(flip)
{
}

bool AnimationDirection::needsToProcessFrameName(const String& baseName) const
{
	return baseName.find("%f") != std::string::npos || baseName.find("%dir%") != std::string::npos;
}

String AnimationDirection::getFrameName(int frameNumber, String baseName) const
{
	try {
		baseName = baseName.replaceAll("%dir%", fileName);

		size_t frameToken = baseName.find("%f");
		if (frameToken != std::string::npos) {
			size_t endToken = baseName.find('%', frameToken + 1);
			if (endToken != std::string::npos) {
				int minWidth = 0;
				if (baseName[frameToken + 2] == ':' && baseName[frameToken + 3] == '0') {
					minWidth = baseName.subToInteger(frameToken + 4, endToken);
				}

				std::stringstream ss;
				ss << std::setfill('0') << std::setw(minWidth) << frameNumber;
				baseName = baseName.left(frameToken) + ss.str() + baseName.mid(endToken + 1);
			}
		}

		return baseName;
	} catch (...) {
		throw Exception("Invalid frame name: " + baseName, HalleyExceptions::Graphics);
	}
}

void AnimationDirection::serialize(Serializer& s) const
{
	s << name;
	s << fileName;
	s << id;
	s << flip;
}

void AnimationDirection::deserialize(Deserializer& s)
{
	s >> name;
	s >> fileName;
	s >> id;
	s >> flip;
}

Animation::Animation()
{}

std::unique_ptr<Animation> Animation::loadResource(ResourceLoader& loader)
{
	auto result = std::make_unique<Animation>();
	auto sData = loader.getStatic();
	Deserializer s(sData->getSpan());
	s >> *result;
	result->loadDependencies(loader);
	return result;
}

void Animation::reload(Resource&& resource)
{
	*this = std::move(dynamic_cast<Animation&>(resource));
}

void Animation::loadDependencies(ResourceLoader& loader)
{
	spriteSheet = loader.getAPI().getResource<SpriteSheet>(spriteSheetName);

	auto matDef = loader.getAPI().getResource<MaterialDefinition>(materialName);
	material = std::make_shared<Material>(matDef);
	material->set("tex0", spriteSheet->getTexture());

	for (auto& s: sequences) {
		for (auto& f : s.frameDefinitions) {
			s.frames.emplace_back(f.makeFrame(*spriteSheet, directions));
		}
	}
}

void Animation::setName(const String& n)
{
	name = n;
}

void Animation::setMaterialName(const String& n)
{
	materialName = n;
}

void Animation::setSpriteSheetName(const String& n)
{
	spriteSheetName = n;
}

void Animation::addSequence(const AnimationSequence& sequence)
{
	sequences.push_back(sequence);
}

void Animation::addDirection(const AnimationDirection& direction)
{
	directions.push_back(direction);
}

const AnimationSequence& Animation::getSequence(const String& seqName) const
{
	for (auto& seq: sequences) {
		if (seq.name == seqName) {
			return seq;
		}
	}
	return sequences.at(0);
}

const AnimationDirection& Animation::getDirection(const String& dirName) const
{
	Expects(directions.size() > 0);

	for (auto& dir : directions) {
		if (dir.name == dirName) {
			return dir;
		}
	}
	return directions[0];
}

const AnimationDirection& Animation::getDirection(int id) const
{
	Expects(id >= 0);
	Expects(directions.size() > 0);

	if (id < int(directions.size())) {
		return directions[id];
	} else {
		return directions[0];
	}
}

Vector2i Animation::getPivot() const
{
	return sequences.at(0).getFrame(0).getSprite(0).origPivot;
}

bool Animation::hasSequence(const String& seqName) const
{
	for (auto& s: sequences) {
		if (s.getName() == seqName) {
			return true;
		}
	}
	return false;
}

void Animation::serialize(Serializer& s) const
{
	s << name;
	s << spriteSheetName;
	s << materialName;
	s << sequences;
	s << directions;
}

void Animation::deserialize(Deserializer& s)
{
	s >> name;
	s >> spriteSheetName;
	s >> materialName;
	s >> sequences;
	s >> directions;
}
<|endoftext|>