<commit_before>// Program.cpp

#include "stdafx.h"
#include "Program.h"
#include "PythonStuff.h"
#include "tinyxml/tinyxml.h"
#include "ProgramCanvas.h"
#include "NCCode.h"
#include "interface/MarkedObject.h"
#include "interface/PropertyString.h"
#include "interface/PropertyChoice.h"
#include "interface/Tool.h"
#include "Profile.h"
#include "Pocket.h"
#include "ZigZag.h"
#include "Adaptive.h"
#include "Op.h"
#include "CNCConfig.h"

bool COperations::CanAdd(HeeksObj* object)
{
	return object->GetType() == ProfileType || object->GetType() == PocketType || object->GetType() == ZigZagType || object->GetType() == AdaptiveType;
}

void COperations::WriteXML(TiXmlNode *root)
{
	TiXmlElement * element;
	element = new TiXmlElement( "Operations" );
	root->LinkEndChild( element );
	WriteBaseXML(element);
}

//static
HeeksObj* COperations::ReadFromXMLElement(TiXmlElement* pElem)
{
	COperations* new_object = new COperations;
	new_object->ReadBaseXML(pElem);
	return new_object;
}

static COperations* object_for_tools = NULL;

class SetAllActive: public Tool{
	// Tool's virtual functions
	const wxChar* GetTitle(){return _("Set All Operations Active");}
	void Run()
	{
		for(HeeksObj* object = object_for_tools->GetFirstChild(); object; object = object_for_tools->GetNextChild())
		{
			if(COp::IsAnOperation(object->GetType()))
			{
				((COp*)object)->m_active = true;
				heeksCAD->WasModified(object);
			}
		}
	}
	wxString BitmapPath(){ return _T("setactive");}
};

static SetAllActive set_all_active;

class SetAllInactive: public Tool{
	// Tool's virtual functions
	const wxChar* GetTitle(){return _("Set All Operations Inactive");}
	void Run()
	{
		for(HeeksObj* object = object_for_tools->GetFirstChild(); object; object = object_for_tools->GetNextChild())
		{
			if(COp::IsAnOperation(object->GetType()))
			{
				((COp*)object)->m_active = false;
				heeksCAD->WasModified(object);
			}
		}
	}
	wxString BitmapPath(){ return _T("setinactive");}
};

static SetAllInactive set_all_inactive;

void COperations::GetTools(std::list<Tool*>* t_list, const wxPoint* p)
{
	object_for_tools = this;
	t_list->push_back(&set_all_active);
	t_list->push_back(&set_all_inactive);

	ObjList::GetTools(t_list, p);
}

CProgram::CProgram():m_nc_code(NULL), m_operations(NULL), m_script_edited(false)
{
	CNCConfig config;
	config.Read(_T("ProgramMachine"), &m_machine, _T("nc.iso"));
	config.Read(_T("ProgramOutputFile"), &m_output_file, _T("/tmp/test.tap"));
	config.Read(_T("ProgramUnits"), &m_units, 1.0);
}

HeeksObj *CProgram::MakeACopy(void)const
{
	return new CProgram(*this);
}

void CProgram::CopyFrom(const HeeksObj* object)
{
	operator=(*((CProgram*)object));
}

static void on_set_machine(const wxChar* value, HeeksObj* object)
{
	((CProgram*)object)->m_machine = value;
	CNCConfig config;
	config.Write(_T("ProgramMachine"), ((CProgram*)object)->m_machine);
}

static void on_set_output_file(const wxChar* value, HeeksObj* object)
{
	((CProgram*)object)->m_output_file = value;
	CNCConfig config;
	config.Write(_T("ProgramOutputFile"), ((CProgram*)object)->m_output_file);
}

static void on_set_units(int value, HeeksObj* object)
{
	((CProgram*)object)->m_units = (value == 0) ? 1.0:25.4;
	CNCConfig config;
	config.Write(_T("ProgramUnits"), ((CProgram*)object)->m_units);

	// also change HeeksCAD's view units automatically
	heeksCAD->SetViewUnits(((CProgram*)object)->m_units, true);
}

void CProgram::GetProperties(std::list<Property *> *list)
{
	list->push_back(new PropertyString(_("machine"), m_machine, this, on_set_machine));
	list->push_back(new PropertyString(_("output file"), m_output_file, this, on_set_output_file));
	{
		std::list< wxString > choices;
		choices.push_back ( wxString ( _("mm") ) );
		choices.push_back ( wxString ( _("inch") ) );
		int choice = 0;
		if(m_units > 25.0)choice = 1;
		list->push_back ( new PropertyChoice ( _("units"),  choices, choice, this, on_set_units ) );
	}
	HeeksObj::GetProperties(list);
}

bool CProgram::CanAdd(HeeksObj* object)
{
	return object->GetType() == NCCodeType || object->GetType() == OperationsType;
}

bool CProgram::CanAddTo(HeeksObj* owner)
{
	return owner->GetType() == DocumentType;
}

void CProgram::SetClickMarkPoint(MarkedObject* marked_object, const double* ray_start, const double* ray_direction)
{
	if(marked_object->m_map.size() > 0)
	{
		MarkedObject* sub_marked_object = marked_object->m_map.begin()->second;
		if(sub_marked_object)
		{
			HeeksObj* object = sub_marked_object->m_map.begin()->first;
			if(object && object->GetType() == NCCodeType)
			{
				((CNCCode*)object)->SetClickMarkPoint(sub_marked_object, ray_start, ray_direction);
			}
		}
	}
}

void CProgram::WriteXML(TiXmlNode *root)
{
	TiXmlElement * element;
	element = new TiXmlElement( "Program" );
	root->LinkEndChild( element );
	element->SetAttribute("machine", Ttc(m_machine.c_str()));
	element->SetAttribute("output_file", Ttc(m_output_file.c_str()));
	element->SetAttribute("program", Ttc(theApp.m_program_canvas->m_textCtrl->GetValue()));
	element->SetDoubleAttribute("units", m_units);

	WriteBaseXML(element);
}

bool CProgram::Add(HeeksObj* object, HeeksObj* prev_object)
{
	if(object->GetType() == NCCodeType)m_nc_code = (CNCCode*)object;
	if(object->GetType() == OperationsType)m_operations = (COperations*)object;
	return ObjList::Add(object, prev_object);
}

void CProgram::Remove(HeeksObj* object)
{
	 // these shouldn't happen, though
	if(object == m_nc_code)m_nc_code = NULL;
	else if(object == m_operations)m_operations = NULL;

	ObjList::Remove(object);
}

// static member function
HeeksObj* CProgram::ReadFromXMLElement(TiXmlElement* pElem)
{
	CProgram* new_object = new CProgram;

	// get the attributes
	for(TiXmlAttribute* a = pElem->FirstAttribute(); a; a = a->Next())
	{
		std::string name(a->Name());
		if(name == "machine"){new_object->m_machine.assign(Ctt(a->Value()));}
		else if(name == "output_file"){new_object->m_output_file.assign(Ctt(a->Value()));}
		else if(name == "program"){theApp.m_program_canvas->m_textCtrl->SetValue(Ctt(a->Value()));}
		else if(name == "units"){new_object->m_units = a->DoubleValue();}
	}

	new_object->ReadBaseXML(pElem);
	theApp.m_program = new_object;

	return new_object;
}

void CProgram::RewritePythonProgram()
{
	theApp.m_program_canvas->m_textCtrl->Clear();
	CZigZag::number_for_stl_file = 1;
	CAdaptive::number_for_stl_file = 1;

	bool profile_op_exists = false;
	bool pocket_op_exists = false;
	bool zigzag_op_exists = false;
	bool adaptive_op_exists = false;
	for(HeeksObj* object = m_operations->GetFirstChild(); object; object = m_operations->GetNextChild())
	{
		if(object->GetType() == ProfileType)
		{
			if(((CProfile*)object)->m_active)profile_op_exists = true;
		}
		else if(object->GetType() == PocketType)
		{
			if(((CPocket*)object)->m_active)pocket_op_exists = true;
		}
		else if(object->GetType() == ZigZagType)
		{
			if(((CZigZag*)object)->m_active)zigzag_op_exists = true;
		}
		else if(object->GetType() == AdaptiveType)
		{
			if(((CAdaptive*)object)->m_active)adaptive_op_exists = true;
		}
	}

	// add standard stuff at the top
	//hackhack, make it work on unix with FHS
#ifndef WIN32
	theApp.m_program_canvas->m_textCtrl->AppendText(_T("import sys\n"));
	theApp.m_program_canvas->m_textCtrl->AppendText(_T("sys.path.insert(0,'/usr/lib/heekscnc/')\n"));
#endif
	// kurve related things
	if(profile_op_exists)
	{
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("import kurve\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("import kurve_funcs\n"));
	}

	// area related things
	if(pocket_op_exists)
	{
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("import area\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("import area_funcs\n"));
	}

	// pycam stuff
	if(zigzag_op_exists)
	{
#ifdef WIN32
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("import sys\n"));
#endif
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("sys.path.insert(0,'PyCam/trunk')\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("from pycam.Geometry import *\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("from pycam.Cutters.SphericalCutter import *\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("from pycam.Cutters.CylindricalCutter import *\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("from pycam.Cutters.ToroidalCutter import *\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("from pycam.Importers.STLImporter import ImportModel\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("from pycam.PathGenerators.DropCutter import DropCutter\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("from PyCamToHeeks import HeeksCNCExporter\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("\n"));
	}

	// actp
	if(adaptive_op_exists)
	{
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("import actp_funcs\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("import actp\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("\n"));
	}

	// machine general stuff
	theApp.m_program_canvas->m_textCtrl->AppendText(_T("from nc.nc import *\n"));

	// specific machine
	theApp.m_program_canvas->m_textCtrl->AppendText(_T("import ") + m_machine + _T("\n"));
	theApp.m_program_canvas->m_textCtrl->AppendText(_T("\n"));

	// output file
	theApp.m_program_canvas->m_textCtrl->AppendText(_T("output('") + m_output_file + _T("')\n"));

	// begin program
	theApp.m_program_canvas->m_textCtrl->AppendText(_T("program_begin(123, 'Test program')\n"));
	theApp.m_program_canvas->m_textCtrl->AppendText(_T("absolute()\n"));
	if(m_units > 25.0)
	{
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("imperial()\n"));
	}
	else
	{
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("metric()\n"));
	}
	theApp.m_program_canvas->m_textCtrl->AppendText(_T("set_plane(0)\n"));
	theApp.m_program_canvas->m_textCtrl->AppendText(_T("\n"));

	// write the operations
	for(HeeksObj* object = m_operations->GetFirstChild(); object; object = m_operations->GetNextChild())
	{
		switch(object->GetType())
		{
		case ProfileType:
			if(((CProfile*)object)->m_active)((CProfile*)object)->AppendTextToProgram();
			break;
		case PocketType:
			if(((CPocket*)object)->m_active)((CPocket*)object)->AppendTextToProgram();
			break;
		case ZigZagType:
			if(((CZigZag*)object)->m_active)((CZigZag*)object)->AppendTextToProgram();
			break;
		case AdaptiveType:
			if(((CAdaptive*)object)->m_active)((CAdaptive*)object)->AppendTextToProgram();
			break;
		}
	}
}

ProgramUserType CProgram::GetUserType()
{
	if(m_nc_code->m_user_edited)return ProgramUserTypeNC;
	if(m_script_edited)return ProgramUserTypeScript;
	if(m_operations->GetFirstChild())return ProgramUserTypeTree;
	return ProgramUserTypeUnkown;
}

void CProgram::UpdateFromUserType()
{
#if 0
	switch(GetUserType())
	{
	case ProgramUserTypeUnkown:

	case tree:
   Enable "Operations" icon in tree
     editing the tree, recreates the python script
   Read only "Program" window
     pressing "Post-process" creates the NC code ( and backplots it )
   Read only "Output" window
   Disable all buttons on "Output" window

	}
#endif
}
<commit_msg>added '/usr/local/lib' to make things coincide with PREFIX changes that were made earlier<commit_after>// Program.cpp

#include "stdafx.h"
#include "Program.h"
#include "PythonStuff.h"
#include "tinyxml/tinyxml.h"
#include "ProgramCanvas.h"
#include "NCCode.h"
#include "interface/MarkedObject.h"
#include "interface/PropertyString.h"
#include "interface/PropertyChoice.h"
#include "interface/Tool.h"
#include "Profile.h"
#include "Pocket.h"
#include "ZigZag.h"
#include "Adaptive.h"
#include "Op.h"
#include "CNCConfig.h"

bool COperations::CanAdd(HeeksObj* object)
{
	return object->GetType() == ProfileType || object->GetType() == PocketType || object->GetType() == ZigZagType || object->GetType() == AdaptiveType;
}

void COperations::WriteXML(TiXmlNode *root)
{
	TiXmlElement * element;
	element = new TiXmlElement( "Operations" );
	root->LinkEndChild( element );
	WriteBaseXML(element);
}

//static
HeeksObj* COperations::ReadFromXMLElement(TiXmlElement* pElem)
{
	COperations* new_object = new COperations;
	new_object->ReadBaseXML(pElem);
	return new_object;
}

static COperations* object_for_tools = NULL;

class SetAllActive: public Tool{
	// Tool's virtual functions
	const wxChar* GetTitle(){return _("Set All Operations Active");}
	void Run()
	{
		for(HeeksObj* object = object_for_tools->GetFirstChild(); object; object = object_for_tools->GetNextChild())
		{
			if(COp::IsAnOperation(object->GetType()))
			{
				((COp*)object)->m_active = true;
				heeksCAD->WasModified(object);
			}
		}
	}
	wxString BitmapPath(){ return _T("setactive");}
};

static SetAllActive set_all_active;

class SetAllInactive: public Tool{
	// Tool's virtual functions
	const wxChar* GetTitle(){return _("Set All Operations Inactive");}
	void Run()
	{
		for(HeeksObj* object = object_for_tools->GetFirstChild(); object; object = object_for_tools->GetNextChild())
		{
			if(COp::IsAnOperation(object->GetType()))
			{
				((COp*)object)->m_active = false;
				heeksCAD->WasModified(object);
			}
		}
	}
	wxString BitmapPath(){ return _T("setinactive");}
};

static SetAllInactive set_all_inactive;

void COperations::GetTools(std::list<Tool*>* t_list, const wxPoint* p)
{
	object_for_tools = this;
	t_list->push_back(&set_all_active);
	t_list->push_back(&set_all_inactive);

	ObjList::GetTools(t_list, p);
}

CProgram::CProgram():m_nc_code(NULL), m_operations(NULL), m_script_edited(false)
{
	CNCConfig config;
	config.Read(_T("ProgramMachine"), &m_machine, _T("nc.iso"));
	config.Read(_T("ProgramOutputFile"), &m_output_file, _T("/tmp/test.tap"));
	config.Read(_T("ProgramUnits"), &m_units, 1.0);
}

HeeksObj *CProgram::MakeACopy(void)const
{
	return new CProgram(*this);
}

void CProgram::CopyFrom(const HeeksObj* object)
{
	operator=(*((CProgram*)object));
}

static void on_set_machine(const wxChar* value, HeeksObj* object)
{
	((CProgram*)object)->m_machine = value;
	CNCConfig config;
	config.Write(_T("ProgramMachine"), ((CProgram*)object)->m_machine);
}

static void on_set_output_file(const wxChar* value, HeeksObj* object)
{
	((CProgram*)object)->m_output_file = value;
	CNCConfig config;
	config.Write(_T("ProgramOutputFile"), ((CProgram*)object)->m_output_file);
}

static void on_set_units(int value, HeeksObj* object)
{
	((CProgram*)object)->m_units = (value == 0) ? 1.0:25.4;
	CNCConfig config;
	config.Write(_T("ProgramUnits"), ((CProgram*)object)->m_units);

	// also change HeeksCAD's view units automatically
	heeksCAD->SetViewUnits(((CProgram*)object)->m_units, true);
}

void CProgram::GetProperties(std::list<Property *> *list)
{
	list->push_back(new PropertyString(_("machine"), m_machine, this, on_set_machine));
	list->push_back(new PropertyString(_("output file"), m_output_file, this, on_set_output_file));
	{
		std::list< wxString > choices;
		choices.push_back ( wxString ( _("mm") ) );
		choices.push_back ( wxString ( _("inch") ) );
		int choice = 0;
		if(m_units > 25.0)choice = 1;
		list->push_back ( new PropertyChoice ( _("units"),  choices, choice, this, on_set_units ) );
	}
	HeeksObj::GetProperties(list);
}

bool CProgram::CanAdd(HeeksObj* object)
{
	return object->GetType() == NCCodeType || object->GetType() == OperationsType;
}

bool CProgram::CanAddTo(HeeksObj* owner)
{
	return owner->GetType() == DocumentType;
}

void CProgram::SetClickMarkPoint(MarkedObject* marked_object, const double* ray_start, const double* ray_direction)
{
	if(marked_object->m_map.size() > 0)
	{
		MarkedObject* sub_marked_object = marked_object->m_map.begin()->second;
		if(sub_marked_object)
		{
			HeeksObj* object = sub_marked_object->m_map.begin()->first;
			if(object && object->GetType() == NCCodeType)
			{
				((CNCCode*)object)->SetClickMarkPoint(sub_marked_object, ray_start, ray_direction);
			}
		}
	}
}

void CProgram::WriteXML(TiXmlNode *root)
{
	TiXmlElement * element;
	element = new TiXmlElement( "Program" );
	root->LinkEndChild( element );
	element->SetAttribute("machine", Ttc(m_machine.c_str()));
	element->SetAttribute("output_file", Ttc(m_output_file.c_str()));
	element->SetAttribute("program", Ttc(theApp.m_program_canvas->m_textCtrl->GetValue()));
	element->SetDoubleAttribute("units", m_units);

	WriteBaseXML(element);
}

bool CProgram::Add(HeeksObj* object, HeeksObj* prev_object)
{
	if(object->GetType() == NCCodeType)m_nc_code = (CNCCode*)object;
	if(object->GetType() == OperationsType)m_operations = (COperations*)object;
	return ObjList::Add(object, prev_object);
}

void CProgram::Remove(HeeksObj* object)
{
	 // these shouldn't happen, though
	if(object == m_nc_code)m_nc_code = NULL;
	else if(object == m_operations)m_operations = NULL;

	ObjList::Remove(object);
}

// static member function
HeeksObj* CProgram::ReadFromXMLElement(TiXmlElement* pElem)
{
	CProgram* new_object = new CProgram;

	// get the attributes
	for(TiXmlAttribute* a = pElem->FirstAttribute(); a; a = a->Next())
	{
		std::string name(a->Name());
		if(name == "machine"){new_object->m_machine.assign(Ctt(a->Value()));}
		else if(name == "output_file"){new_object->m_output_file.assign(Ctt(a->Value()));}
		else if(name == "program"){theApp.m_program_canvas->m_textCtrl->SetValue(Ctt(a->Value()));}
		else if(name == "units"){new_object->m_units = a->DoubleValue();}
	}

	new_object->ReadBaseXML(pElem);
	theApp.m_program = new_object;

	return new_object;
}

void CProgram::RewritePythonProgram()
{
	theApp.m_program_canvas->m_textCtrl->Clear();
	CZigZag::number_for_stl_file = 1;
	CAdaptive::number_for_stl_file = 1;

	bool profile_op_exists = false;
	bool pocket_op_exists = false;
	bool zigzag_op_exists = false;
	bool adaptive_op_exists = false;
	for(HeeksObj* object = m_operations->GetFirstChild(); object; object = m_operations->GetNextChild())
	{
		if(object->GetType() == ProfileType)
		{
			if(((CProfile*)object)->m_active)profile_op_exists = true;
		}
		else if(object->GetType() == PocketType)
		{
			if(((CPocket*)object)->m_active)pocket_op_exists = true;
		}
		else if(object->GetType() == ZigZagType)
		{
			if(((CZigZag*)object)->m_active)zigzag_op_exists = true;
		}
		else if(object->GetType() == AdaptiveType)
		{
			if(((CAdaptive*)object)->m_active)adaptive_op_exists = true;
		}
	}

	// add standard stuff at the top
	//hackhack, make it work on unix with FHS
#ifndef WIN32
	theApp.m_program_canvas->m_textCtrl->AppendText(_T("import sys\n"));
	theApp.m_program_canvas->m_textCtrl->AppendText(_T("sys.path.insert(0,'/usr/local/lib/heekscnc/')\n"));
#endif
	// kurve related things
	if(profile_op_exists)
	{
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("import kurve\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("import kurve_funcs\n"));
	}

	// area related things
	if(pocket_op_exists)
	{
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("import area\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("import area_funcs\n"));
	}

	// pycam stuff
	if(zigzag_op_exists)
	{
#ifdef WIN32
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("import sys\n"));
#endif
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("sys.path.insert(0,'PyCam/trunk')\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("from pycam.Geometry import *\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("from pycam.Cutters.SphericalCutter import *\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("from pycam.Cutters.CylindricalCutter import *\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("from pycam.Cutters.ToroidalCutter import *\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("from pycam.Importers.STLImporter import ImportModel\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("from pycam.PathGenerators.DropCutter import DropCutter\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("from PyCamToHeeks import HeeksCNCExporter\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("\n"));
	}

	// actp
	if(adaptive_op_exists)
	{
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("import actp_funcs\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("import actp\n"));
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("\n"));
	}

	// machine general stuff
	theApp.m_program_canvas->m_textCtrl->AppendText(_T("from nc.nc import *\n"));

	// specific machine
	theApp.m_program_canvas->m_textCtrl->AppendText(_T("import ") + m_machine + _T("\n"));
	theApp.m_program_canvas->m_textCtrl->AppendText(_T("\n"));

	// output file
	theApp.m_program_canvas->m_textCtrl->AppendText(_T("output('") + m_output_file + _T("')\n"));

	// begin program
	theApp.m_program_canvas->m_textCtrl->AppendText(_T("program_begin(123, 'Test program')\n"));
	theApp.m_program_canvas->m_textCtrl->AppendText(_T("absolute()\n"));
	if(m_units > 25.0)
	{
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("imperial()\n"));
	}
	else
	{
		theApp.m_program_canvas->m_textCtrl->AppendText(_T("metric()\n"));
	}
	theApp.m_program_canvas->m_textCtrl->AppendText(_T("set_plane(0)\n"));
	theApp.m_program_canvas->m_textCtrl->AppendText(_T("\n"));

	// write the operations
	for(HeeksObj* object = m_operations->GetFirstChild(); object; object = m_operations->GetNextChild())
	{
		switch(object->GetType())
		{
		case ProfileType:
			if(((CProfile*)object)->m_active)((CProfile*)object)->AppendTextToProgram();
			break;
		case PocketType:
			if(((CPocket*)object)->m_active)((CPocket*)object)->AppendTextToProgram();
			break;
		case ZigZagType:
			if(((CZigZag*)object)->m_active)((CZigZag*)object)->AppendTextToProgram();
			break;
		case AdaptiveType:
			if(((CAdaptive*)object)->m_active)((CAdaptive*)object)->AppendTextToProgram();
			break;
		}
	}
}

ProgramUserType CProgram::GetUserType()
{
	if(m_nc_code->m_user_edited)return ProgramUserTypeNC;
	if(m_script_edited)return ProgramUserTypeScript;
	if(m_operations->GetFirstChild())return ProgramUserTypeTree;
	return ProgramUserTypeUnkown;
}

void CProgram::UpdateFromUserType()
{
#if 0
	switch(GetUserType())
	{
	case ProgramUserTypeUnkown:

	case tree:
   Enable "Operations" icon in tree
     editing the tree, recreates the python script
   Read only "Program" window
     pressing "Post-process" creates the NC code ( and backplots it )
   Read only "Output" window
   Disable all buttons on "Output" window

	}
#endif
}
<|endoftext|>


<commit_before>#include "RThread.h"
#include "REventLoop.h"
#include "RForwardList.h"
#include "RSpinLocker.h"

class RThreadPrivate
{
public:
  static void
  run(void *arg);
};

// FIXME: Thread list not guarded by mutex now !
static RForwardList<RThread *> sThreads;
void
RThreadPrivate::run(void *arg)
{
  RThread *thread = static_cast<RThread *>(arg);

  if(thread)
  {
    thread->started.emit();
    thread->run();
    thread->finished.emit();
  }
}

RThread::RThread(TaskHandle_t handle)
// So that RObject won't create another RThread lead infinite looping
  : RObject(this)
  , mStackSize(128)
  , mHandle(handle)
  , mIsOwnded(false)
{
}

RThread::RThread()
  : RObject(this)
  , mStackSize(128)
  , mHandle(NULL)
  , mIsOwnded(false)
{
  R_MAKE_SPINLOCKER();
  sThreads.pushFront(this);
}

RThread::~RThread()
{
  terminate();

  R_MAKE_SPINLOCKER();

  // Clear this thread pointer in thread list
  sThreads.remove(this);
}

void
RThread::exit(int returnCode)
{
  REventLoop::instance(this)->exit(returnCode);
}

bool
RThread::isFinished() const
{
  if(!mHandle)
  {
    return true;
  }

  return (eDeleted == eTaskGetState(mHandle));
}

bool
RThread::isRunning() const
{
  if(!mHandle)
  {
    return false;
  }

  return (eDeleted != eTaskGetState(mHandle));
}

RThread::Priority
RThread::priority() const
{
  if(!mHandle)
  {
    return IdlePriority;
  }

  return static_cast<RThread::Priority>(uxTaskPriorityGet(mHandle));
}

void
RThread::setPriority(RThread::Priority priority)
{
  if(!mHandle)
  {
    return;
  }

  vTaskPrioritySet(mHandle, static_cast<UBaseType_t>(priority));
}

void
RThread::setStackSize(size_t stackSize)
{
  mStackSize = stackSize;
}

size_t
RThread::stackSize() const
{
  return mStackSize;
}

bool
RThread::wait(unsigned long time)
{
  /// FIXME: Ugly way to wait for thread finished.

  /* Block for 100ms each time */
  const TickType_t ticksPerBlock = 100 / portTICK_PERIOD_MS;

  if(currentThreadId() == mHandle)
  {
    // You can't wait yourself in the samethread!
    return false;
  }

  while(true)
  {
    if(isFinished())
    {
      return true;
    }

    if(time < std::numeric_limits<unsigned long>::max())
    {
      if(time <= 100)
      {
        // Timeout!
        return false;
      }
      else
      {
        time -= 100;
      }
    }

    vTaskDelay(ticksPerBlock);
  }
}

void
RThread::quit()
{
  this->exit(0);
}

void
RThread::start(RThread::Priority priority)
{
  /* Create the task, storing the handle. */
  TaskHandle_t handle = NULL;

  if(isRunning())
  {
    return;
  }

  terminate();

  UBaseType_t targetPriority = IdlePriority;

  if(InheritPriority == priority)
  {
    targetPriority = uxTaskPriorityGet(currentThreadId());
  }

  auto ret = xTaskCreate(
    RThreadPrivate::run,  /* Function that implements the task. */
    "",                /* Text name for the task. */
    (stackSize() + 1) % sizeof(uint16_t), /* Stack size in words, not bytes. */
    static_cast<void *>(this),      /* Parameter passed into the task. */
    targetPriority,  /* Priority at which the task is created. */
    &handle);   /* Used to pass out the created task's handle. */

  if(ret == pdPASS)
  {
    mIsOwnded = true;
    mHandle   = handle;
  }
}

void
RThread::terminate()
{
  if(mIsOwnded && mHandle)
  {
    // Ensure no tasks and interrupts will be trigger during thread event loop
    // destruction.
    {
      R_MAKE_SPINLOCKER();
      REventLoop::_destroy(this);
      vTaskDelete(mHandle);
    }

    terminated.emit();
  }

  mHandle = NULL;
}

RThread *
RThread::currentThread()
{
  TaskHandle_t handle = currentThreadId();

  for(auto it = sThreads.begin(); it != sThreads.end(); ++it)
  {
    if(handle == (*it)->mHandle)
    {
      return *it;
    }
  }

  // FIXME: Here generated an orphan RThread object!
  return new RThread(handle);
}

TaskHandle_t
RThread::currentThreadId()
{
  return xTaskGetCurrentTaskHandle();
}

void
RThread::yieldCurrentThread()
{
  taskYIELD();
}

int
RThread::exec()
{
  REventLoop *loop = REventLoop::instance(this);

  return loop->exec();
}

void
RThread::run()
{
  exec();
}

void
RThread::msleep(unsigned long msecs)
{
  unsigned long blockTime = std::numeric_limits<unsigned long>::max() / 1000;

  while(msecs > 0)
  {
    if(msecs <= std::numeric_limits<unsigned long>::max())
    {
      usleep(msecs);
      return;
    }

    usleep(std::numeric_limits<unsigned long>::max());
    msecs -= blockTime;
  }
}

void
RThread::sleep(unsigned long secs)
{
  unsigned long blockTime = std::numeric_limits<unsigned long>::max() /
                            1000000l;

  while(secs > 0)
  {
    if(secs <= std::numeric_limits<unsigned long>::max())
    {
      usleep(secs);
      return;
    }

    usleep(std::numeric_limits<unsigned long>::max());
    secs -= blockTime;
  }
}

void
RThread::usleep(unsigned long usecs)
{
  unsigned long ticks = usecs / (1000 * portTICK_PERIOD_MS);

  while(ticks > 0)
  {
    if(ticks <= static_cast<decltype(usecs)>(portMAX_DELAY))
    {
      vTaskDelay(static_cast<decltype(portMAX_DELAY)>(ticks));
      return;
    }

    vTaskDelay(portMAX_DELAY);
    ticks -= static_cast<decltype(usecs)>(portMAX_DELAY);
  }
}
<commit_msg>Fixed task not running<commit_after>#include "RThread.h"
#include "REventLoop.h"
#include "RForwardList.h"
#include "RSpinLocker.h"

class RThreadPrivate
{
public:
  static void
  run(void *arg);
};

// FIXME: Thread list not guarded by mutex now !
static RForwardList<RThread *> sThreads;
void
RThreadPrivate::run(void *arg)
{
  RThread *thread = static_cast<RThread *>(arg);

  if(thread)
  {
    thread->started.emit();
    thread->run();
    thread->finished.emit();
  }
}

RThread::RThread(TaskHandle_t handle)
// So that RObject won't create another RThread lead infinite looping
  : RObject(this)
  , mStackSize(128)
  , mHandle(handle)
  , mIsOwnded(false)
{
}

RThread::RThread()
  : RObject(this)
  , mStackSize(128)
  , mHandle(NULL)
  , mIsOwnded(false)
{
  R_MAKE_SPINLOCKER();
  sThreads.pushFront(this);
}

RThread::~RThread()
{
  terminate();

  R_MAKE_SPINLOCKER();

  // Clear this thread pointer in thread list
  sThreads.remove(this);
}

void
RThread::exit(int returnCode)
{
  REventLoop::instance(this)->exit(returnCode);
}

bool
RThread::isFinished() const
{
  if(!mHandle)
  {
    return true;
  }

  return (eDeleted == eTaskGetState(mHandle));
}

bool
RThread::isRunning() const
{
  if(!mHandle)
  {
    return false;
  }

  return (eDeleted != eTaskGetState(mHandle));
}

RThread::Priority
RThread::priority() const
{
  if(!mHandle)
  {
    return IdlePriority;
  }

  return static_cast<RThread::Priority>(uxTaskPriorityGet(mHandle));
}

void
RThread::setPriority(RThread::Priority priority)
{
  if(!mHandle)
  {
    return;
  }

  vTaskPrioritySet(mHandle, static_cast<UBaseType_t>(priority));
}

void
RThread::setStackSize(size_t stackSize)
{
  mStackSize = stackSize;
}

size_t
RThread::stackSize() const
{
  return mStackSize;
}

bool
RThread::wait(unsigned long time)
{
  /// FIXME: Ugly way to wait for thread finished.

  /* Block for 100ms each time */
  const TickType_t ticksPerBlock = 100 / portTICK_PERIOD_MS;

  if(currentThreadId() == mHandle)
  {
    // You can't wait yourself in the samethread!
    return false;
  }

  while(true)
  {
    if(isFinished())
    {
      return true;
    }

    if(time < std::numeric_limits<unsigned long>::max())
    {
      if(time <= 100)
      {
        // Timeout!
        return false;
      }
      else
      {
        time -= 100;
      }
    }

    vTaskDelay(ticksPerBlock);
  }
}

void
RThread::quit()
{
  this->exit(0);
}

void
RThread::start(RThread::Priority priority)
{
  /* Create the task, storing the handle. */
  TaskHandle_t handle = NULL;

  if(isRunning())
  {
    return;
  }

  terminate();

  UBaseType_t targetPriority = IdlePriority;

  if(InheritPriority == priority)
  {
    targetPriority = uxTaskPriorityGet(currentThreadId());
  }

  auto ret = xTaskCreate(
    RThreadPrivate::run,  /* Function that implements the task. */
    "",                /* Text name for the task. */
    (stackSize() + 1) / sizeof(uint16_t), /* Stack size in words, not bytes. */
    static_cast<void *>(this),      /* Parameter passed into the task. */
    targetPriority,  /* Priority at which the task is created. */
    &handle);   /* Used to pass out the created task's handle. */

  if(ret == pdPASS)
  {
    mIsOwnded = true;
    mHandle   = handle;
  }
}

void
RThread::terminate()
{
  if(mIsOwnded && mHandle)
  {
    // Ensure no tasks and interrupts will be trigger during thread event loop
    // destruction.
    {
      R_MAKE_SPINLOCKER();
      REventLoop::_destroy(this);
      vTaskDelete(mHandle);
    }

    terminated.emit();
  }

  mHandle = NULL;
}

RThread *
RThread::currentThread()
{
  TaskHandle_t handle = currentThreadId();

  for(auto it = sThreads.begin(); it != sThreads.end(); ++it)
  {
    if(handle == (*it)->mHandle)
    {
      return *it;
    }
  }

  // FIXME: Here generated an orphan RThread object!
  return new RThread(handle);
}

TaskHandle_t
RThread::currentThreadId()
{
  return xTaskGetCurrentTaskHandle();
}

void
RThread::yieldCurrentThread()
{
  taskYIELD();
}

int
RThread::exec()
{
  REventLoop *loop = REventLoop::instance(this);

  return loop->exec();
}

void
RThread::run()
{
  exec();
}

void
RThread::msleep(unsigned long msecs)
{
  unsigned long blockTime = std::numeric_limits<unsigned long>::max() / 1000;

  while(msecs > 0)
  {
    if(msecs <= std::numeric_limits<unsigned long>::max())
    {
      usleep(msecs);
      return;
    }

    usleep(std::numeric_limits<unsigned long>::max());
    msecs -= blockTime;
  }
}

void
RThread::sleep(unsigned long secs)
{
  unsigned long blockTime = std::numeric_limits<unsigned long>::max() /
                            1000000l;

  while(secs > 0)
  {
    if(secs <= std::numeric_limits<unsigned long>::max())
    {
      usleep(secs);
      return;
    }

    usleep(std::numeric_limits<unsigned long>::max());
    secs -= blockTime;
  }
}

void
RThread::usleep(unsigned long usecs)
{
  unsigned long ticks = usecs / (1000 * portTICK_PERIOD_MS);

  while(ticks > 0)
  {
    if(ticks <= static_cast<decltype(usecs)>(portMAX_DELAY))
    {
      vTaskDelay(static_cast<decltype(portMAX_DELAY)>(ticks));
      return;
    }

    vTaskDelay(portMAX_DELAY);
    ticks -= static_cast<decltype(usecs)>(portMAX_DELAY);
  }
}
<|endoftext|>


<commit_before>#ifndef SAPLIST_HPP
#define SAPLIST_HPP 1

#include <functional>
#include <map>
#include <typeinfo>
#include <climits>
#include "CollisionManager.hpp"

class Collisionable {
public:
    virtual int getXMin() = 0;
    virtual int getYMin() = 0;
    virtual int getXMax() = 0;
    virtual int getYMax() = 0;
    virtual ~Collisionable() {};
};

class SAPList : public CollisionManager<Collisionable> {

    class EndPoint;

    class AABB {
    public:
        /** min/max
         *  - first is x axis
         *  - second is y axis */
        EndPoint * min[2];
        EndPoint * max[2];

        size_t typeId; /* Need to use std::type_info::hash_code() */
        void * owner;

        AABB (int minX, int minY, int maxX, int maxY, size_t t, void * o) :
            typeId(t), owner(o) {
            min[0] = new EndPoint(this, minX, true);
            min[1] = new EndPoint(this, minY, true);
            max[0] = new EndPoint(this, maxX, false);
            max[1] = new EndPoint(this, maxY, false);
        }

    };

    /**
     * TODO:
     * - Optimize merging isMin boolean into another member
     *   (as a flag)
     * - Using doubly-linked list could waste memory but is easier to implement
     *   than arrays.
     * - Implement pair manager
     */
    class EndPoint {
    public:
        AABB * owner;
        int value; //FIXME: use a pointer?
        bool isMin;

        EndPoint * prev;
        EndPoint * next;

        EndPoint (AABB* o,int v,bool m,EndPoint* p=NULL,EndPoint* n=NULL) :
            owner(o), value(v), isMin(m), prev(p), next(n) {}

        /** When and EndPoint is destroyed, it updates prev and next */
        ~EndPoint () {
            if (this->prev != NULL) {
                this->prev->next = this->next;
            }
            if (this->next != NULL)
                this->next->prev = this->prev;
        }
    };

    /**
     * Action manager stores actions to perform on collision or separation
     * of two objects depending on their type
     * It also stores default actions to execute if no specific action is
     * defined for two types.
     */
    class ActionManager {
    public:
        /**
         * action table stores 2 actions per types couple:
         * - first is onCollision
         * - second is onSeparation
         */
        std::map < std::pair < size_t,size_t >,
                   std::pair < std::function < void (void *, void *) >,
                               std::function < void (void *, void *) > > >
        actions;

        /**
         * @param t1 type id of first object
         * @param t2 type id of second object
         * @return function to call on t1/t2 collision
         */
        std::function < void (void *, void *) >
        onCollision(size_t t1, size_t t2) {
            return this
                ->actions[std::pair<size_t, size_t>(t1,t2)].first;
        }

        /**
         * @param t1 type id of first object
         * @param t2 type id of second object
         * @return function to call on t1/t2 separation
         */
        std::function < void (void *, void *) >
        onSeparation(size_t t1, size_t t2) {
            return this
                ->actions[std::pair<size_t, size_t>(t1,t2)].second;
        }
    };

private:

    EndPoint * xAxis;
    EndPoint * yAxis;
    ActionManager actions;

    /** Swap two EndPoint * */
    void swap(EndPoint * p1, EndPoint * p2) {
        p2->next->prev = p1;
        p1->next = p2->next;
        p1->prev->next = p2;
        p1->prev = p2;
        p2->next = p1;
    }

    /** A collision between two AABB on one axis */
    inline bool partialCollisionCheck(const AABB & b1,
                                      const AABB & b2,
                                      int dim) {
        return (b1.max[dim] <= b2.max[dim] && b1.max[dim] >= b2.min[dim])
            || (b1.min[dim] >= b2.min[dim] && b1.max[dim] <= b2.max[dim]);
    }

    /**
     * Check if two AABB collide using 2 axises
     * @see partialCollisionCheck
     */
    bool collisionCheck(const AABB & b1, const AABB & b2) {
        return partialCollisionCheck (b1, b2, 0)
            && partialCollisionCheck (b1, b2, 1);
    }

    /** Create the AABB corresponding to a collisionable object
     * @param c the object used to create the corresponding AABB
     * @return a pointer to freshly heap-allocated AABB
     */
    AABB * mk_AABB(Collisionable * c) {
        return new AABB(c->getXMin(), c->getYMin(),
                        c->getXMax(), c->getYMax(),
                        typeid(c).hash_code(), (void *) c);
    }

    /** Update EndPoint place and call the appropriate function in case
     * of collision/separation
     * @param pt the EndPoint to update
     */
    void updateEndPoint(EndPoint * pt) {

         auto aux =
            [&pt, this]
            (std::function<EndPoint*(EndPoint*)>succ,
             std::function<bool(int,int)>loop_cond,
             std::function<bool(EndPoint*,EndPoint*)>mustAdd,
             std::function<bool(EndPoint*,EndPoint*)>mustRm) {

            EndPoint * tmp = succ(pt);

            while (loop_cond(tmp->value, pt->value)) {
                this->swap(tmp, pt);
                if (mustAdd(pt, tmp)) {
                    if (this->collisionCheck(*(pt->owner), *(tmp->owner))) {
                    this->actions.onCollision
                        (pt->owner->typeId, tmp->owner->typeId)
                        (pt->owner->owner, tmp->owner->owner);
                    }
                } else if (mustRm(pt, tmp)) {
                    this->actions.onSeparation
                        (pt->owner->typeId,tmp->owner->typeId)
                        (pt->owner->owner, tmp->owner->owner);
                }
            }
        };

        /** TODO: use aux to update on x and y axis */

    }

public:

    SAPList () {
        /* not sure about the true/false values */
        xAxis = new EndPoint(NULL, INT_MIN, true, NULL, NULL);
        xAxis->next = new EndPoint(NULL, INT_MAX, false, xAxis, NULL);
        yAxis = new EndPoint(NULL, INT_MIN, true, NULL, NULL);
        yAxis->next = new EndPoint(NULL, INT_MAX, false, yAxis, NULL);
    }

    ~SAPList () {

        while (xAxis->next != NULL) {
            delete xAxis->next;
        } delete xAxis;

        while (yAxis->next != NULL) {
            delete yAxis->next;
        } delete yAxis;
    }

    void addObject(const Collisionable &) {}
    void updateObject(const Collisionable &) {}
    void removeObject(const Collisionable &) {}
};

#endif
<commit_msg>Added function to add actions in ActionManager.<commit_after>#ifndef SAPLIST_HPP
#define SAPLIST_HPP 1

#include <functional>
#include <map>
#include <typeinfo>
#include <climits>
#include "CollisionManager.hpp"

class Collisionable {
public:
    virtual int getXMin() = 0;
    virtual int getYMin() = 0;
    virtual int getXMax() = 0;
    virtual int getYMax() = 0;
    virtual ~Collisionable() {};
};

class SAPList : public CollisionManager<Collisionable> {

    class EndPoint;

    class AABB {
    public:
        /** min/max
         *  - first is x axis
         *  - second is y axis */
        EndPoint * min[2];
        EndPoint * max[2];

        size_t typeId; /* Need to use std::type_info::hash_code() */
        void * owner;

        AABB (int minX, int minY, int maxX, int maxY, size_t t, void * o) :
            typeId(t), owner(o) {
            min[0] = new EndPoint(this, minX, true);
            min[1] = new EndPoint(this, minY, true);
            max[0] = new EndPoint(this, maxX, false);
            max[1] = new EndPoint(this, maxY, false);
        }

    };

    /**
     * TODO:
     * - Optimize merging isMin boolean into another member
     *   (as a flag)
     * - Using doubly-linked list could waste memory but is easier to implement
     *   than arrays.
     * - Implement pair manager
     */
    class EndPoint {
    public:
        AABB * owner;
        int value; //FIXME: use a pointer?
        bool isMin;

        EndPoint * prev;
        EndPoint * next;

        EndPoint (AABB* o,int v,bool m,EndPoint* p=NULL,EndPoint* n=NULL) :
            owner(o), value(v), isMin(m), prev(p), next(n) {}

        /** When and EndPoint is destroyed, it updates prev and next */
        ~EndPoint () {
            if (this->prev != NULL) {
                this->prev->next = this->next;
            }
            if (this->next != NULL)
                this->next->prev = this->prev;
        }
    };

    /**
     * Action manager stores actions to perform on collision or separation
     * of two objects depending on their type
     * It also stores default actions to execute if no specific action is
     * defined for two types.
     */
    class ActionManager {
    public:
        /**
         * action table stores 2 actions per types couple:
         * - first is onCollision
         * - second is onSeparation
         */
        std::map < std::pair < size_t,size_t >,
                   std::pair < std::function < void (void *, void *) >,
                               std::function < void (void *, void *) > > >
        actions;

        ActionManager () {}

        /** Add an action to ActionManager
         * @param t1 first object type (hashcode)
         * @param t2 second  object type (hashcode)
         * @param onCollision function to call on collision of t1 and t2
         * @param onSeparation function to call on separation of t1 and t2
         */
        void addAction(size_t t1, size_t t2,
                  std::function <void (void *, void *)> onCollision,
                  std::function <void (void *, void *)> onSeparation) {
            this->actions[std::pair < size_t, size_t > (t1,t2)] =
                std::pair < std::function < void (void *, void *) >,
                            std::function < void (void *, void *) > >
                (onCollision, onSeparation);
        }
        /**
         * @param t1 type id of first object
         * @param t2 type id of second object
         * @return function to call on t1/t2 collision
         */
        std::function < void (void *, void *) >
        onCollision(size_t t1, size_t t2) {
            return this
                ->actions[std::pair<size_t, size_t>(t1,t2)].first;
        }

        /**
         * @param t1 type id of first object
         * @param t2 type id of second object
         * @return function to call on t1/t2 separation
         */
        std::function < void (void *, void *) >
        onSeparation(size_t t1, size_t t2) {
            return this
                ->actions[std::pair<size_t, size_t>(t1,t2)].second;
        }
    };

private:

    EndPoint * xAxis;
    EndPoint * yAxis;
    ActionManager actions;

    /** Swap two EndPoint * */
    void swap(EndPoint * p1, EndPoint * p2) {
        p2->next->prev = p1;
        p1->next = p2->next;
        p1->prev->next = p2;
        p1->prev = p2;
        p2->next = p1;
    }

    /** A collision between two AABB on one axis */
    inline bool partialCollisionCheck(const AABB & b1,
                                      const AABB & b2,
                                      int dim) {
        return (b1.max[dim] <= b2.max[dim] && b1.max[dim] >= b2.min[dim])
            || (b1.min[dim] >= b2.min[dim] && b1.max[dim] <= b2.max[dim]);
    }

    /**
     * Check if two AABB collide using 2 axises
     * @see partialCollisionCheck
     */
    bool collisionCheck(const AABB & b1, const AABB & b2) {
        return partialCollisionCheck (b1, b2, 0)
            && partialCollisionCheck (b1, b2, 1);
    }

    /** Create the AABB corresponding to a collisionable object
     * @param c the object used to create the corresponding AABB
     * @return a pointer to freshly heap-allocated AABB
     */
    AABB * mk_AABB(Collisionable * c) {
        return new AABB(c->getXMin(), c->getYMin(),
                        c->getXMax(), c->getYMax(),
                        typeid(c).hash_code(), (void *) c);
    }

    /** Update EndPoint place and call the appropriate function in case
     * of collision/separation
     * @param pt the EndPoint to update
     */
    void updateEndPoint(EndPoint * pt) {

         auto aux =
            [&pt, this]
            (std::function<EndPoint*(EndPoint*)>succ,
             std::function<bool(int,int)>loop_cond,
             std::function<bool(EndPoint*,EndPoint*)>mustAdd,
             std::function<bool(EndPoint*,EndPoint*)>mustRm) {

            EndPoint * tmp = succ(pt);

            while (loop_cond(tmp->value, pt->value)) {
                this->swap(tmp, pt);
                if (mustAdd(pt, tmp)) {
                    if (this->collisionCheck(*(pt->owner), *(tmp->owner))) {
                    this->actions.onCollision
                        (pt->owner->typeId, tmp->owner->typeId)
                        (pt->owner->owner, tmp->owner->owner);
                    }
                } else if (mustRm(pt, tmp)) {
                    this->actions.onSeparation
                        (pt->owner->typeId,tmp->owner->typeId)
                        (pt->owner->owner, tmp->owner->owner);
                }
            }
        };

        /** TODO: use aux to update on x and y axis */

    }

public:

    SAPList () {
        /* not sure about the true/false values */
        xAxis = new EndPoint(NULL, INT_MIN, true, NULL, NULL);
        xAxis->next = new EndPoint(NULL, INT_MAX, false, xAxis, NULL);
        yAxis = new EndPoint(NULL, INT_MIN, true, NULL, NULL);
        yAxis->next = new EndPoint(NULL, INT_MAX, false, yAxis, NULL);
    }

    ~SAPList () {

        while (xAxis->next != NULL) {
            delete xAxis->next;
        } delete xAxis;

        while (yAxis->next != NULL) {
            delete yAxis->next;
        } delete yAxis;
    }

    void addObject(const Collisionable &) {}
    void updateObject(const Collisionable &) {}
    void removeObject(const Collisionable &) {}
};

#endif
<|endoftext|>


<commit_before>/*
    This file is part of Magnum.

    Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included
    in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.
*/

#include "Sampler.h"

#include <Utility/Debug.h>

#include "Context.h"
#include "Implementation/State.h"
#include "Implementation/TextureState.h"
#include "Extensions.h"

namespace Magnum {

/* Check correctness of binary OR in setMinificationFilter(). If nobody fucks
   anything up, this assert should produce the same results on all dimensions,
   thus testing only on AbstractTexture. */
#define filter_or(filter, mipmap) (GLint(Sampler::Filter::filter)|GLint(Sampler::Mipmap::mipmap))
static_assert((filter_or(Nearest, Base) == GL_NEAREST) &&
              (filter_or(Nearest, Nearest) == GL_NEAREST_MIPMAP_NEAREST) &&
              (filter_or(Nearest, Linear) == GL_NEAREST_MIPMAP_LINEAR) &&
              (filter_or(Linear, Base) == GL_LINEAR) &&
              (filter_or(Linear, Nearest) == GL_LINEAR_MIPMAP_NEAREST) &&
              (filter_or(Linear, Linear) == GL_LINEAR_MIPMAP_LINEAR),
    "Unsupported constants for GL texture filtering");
#undef filter_or

Float Sampler::maxAnisotropy() {
    if(!Context::current()->isExtensionSupported<Extensions::GL::EXT::texture_filter_anisotropic>())
        return 0.0f;

    GLfloat& value = Context::current()->state().texture->maxAnisotropy;

    /** @todo Re-enable when extension header is available */
    #ifndef MAGNUM_TARGET_GLES
    /* Get the value, if not already cached */
    if(value == 0.0f)
        glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &value);
    #endif

    return value;
}

#ifndef DOXYGEN_GENERATING_OUTPUT
Debug operator<<(Debug debug, const Sampler::Filter value) {
    switch(value) {
        #define _c(value) case Sampler::Filter::value: return debug << "Sampler::Filter::" #value;
        _c(Nearest)
        _c(Linear)
        #undef _c
    }

    return debug << "Sampler::Filter::(invalid)";
}

Debug operator<<(Debug debug, const Sampler::Mipmap value) {
    switch(value) {
        #define _c(value) case Sampler::Mipmap::value: return debug << "Sampler::Mipmap::" #value;
        _c(Base)
        _c(Nearest)
        _c(Linear)
        #undef _c
    }

    return debug << "Sampler::Mipmap::(invalid)";
}

Debug operator<<(Debug debug, const Sampler::Wrapping value) {
    switch(value) {
        #define _c(value) case Sampler::Wrapping::value: return debug << "Sampler::Wrapping::" #value;
        _c(Repeat)
        _c(MirroredRepeat)
        _c(ClampToEdge)
        _c(ClampToBorder)
        #ifndef MAGNUM_TARGET_GLES
        _c(MirrorClampToEdge)
        #endif
        #undef _c
    }

    return debug << "Sampler::Wrapping::(invalid)";
}
#endif

}
<commit_msg>Sampler: remove old ES-specific workaround.<commit_after>/*
    This file is part of Magnum.

    Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included
    in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.
*/

#include "Sampler.h"

#include <Utility/Debug.h>

#include "Context.h"
#include "Implementation/State.h"
#include "Implementation/TextureState.h"
#include "Extensions.h"

namespace Magnum {

/* Check correctness of binary OR in setMinificationFilter(). If nobody fucks
   anything up, this assert should produce the same results on all dimensions,
   thus testing only on AbstractTexture. */
#define filter_or(filter, mipmap) (GLint(Sampler::Filter::filter)|GLint(Sampler::Mipmap::mipmap))
static_assert((filter_or(Nearest, Base) == GL_NEAREST) &&
              (filter_or(Nearest, Nearest) == GL_NEAREST_MIPMAP_NEAREST) &&
              (filter_or(Nearest, Linear) == GL_NEAREST_MIPMAP_LINEAR) &&
              (filter_or(Linear, Base) == GL_LINEAR) &&
              (filter_or(Linear, Nearest) == GL_LINEAR_MIPMAP_NEAREST) &&
              (filter_or(Linear, Linear) == GL_LINEAR_MIPMAP_LINEAR),
    "Unsupported constants for GL texture filtering");
#undef filter_or

Float Sampler::maxAnisotropy() {
    if(!Context::current()->isExtensionSupported<Extensions::GL::EXT::texture_filter_anisotropic>())
        return 0.0f;

    GLfloat& value = Context::current()->state().texture->maxAnisotropy;

    /* Get the value, if not already cached */
    if(value == 0.0f)
        glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &value);

    return value;
}

#ifndef DOXYGEN_GENERATING_OUTPUT
Debug operator<<(Debug debug, const Sampler::Filter value) {
    switch(value) {
        #define _c(value) case Sampler::Filter::value: return debug << "Sampler::Filter::" #value;
        _c(Nearest)
        _c(Linear)
        #undef _c
    }

    return debug << "Sampler::Filter::(invalid)";
}

Debug operator<<(Debug debug, const Sampler::Mipmap value) {
    switch(value) {
        #define _c(value) case Sampler::Mipmap::value: return debug << "Sampler::Mipmap::" #value;
        _c(Base)
        _c(Nearest)
        _c(Linear)
        #undef _c
    }

    return debug << "Sampler::Mipmap::(invalid)";
}

Debug operator<<(Debug debug, const Sampler::Wrapping value) {
    switch(value) {
        #define _c(value) case Sampler::Wrapping::value: return debug << "Sampler::Wrapping::" #value;
        _c(Repeat)
        _c(MirroredRepeat)
        _c(ClampToEdge)
        _c(ClampToBorder)
        #ifndef MAGNUM_TARGET_GLES
        _c(MirrorClampToEdge)
        #endif
        #undef _c
    }

    return debug << "Sampler::Wrapping::(invalid)";
}
#endif

}
<|endoftext|>


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

#include <Trigger.hpp>

void trigger(const unsigned int firstSynthesizedBeam, const Options &options, const unsigned int padding, const unsigned int integration, const AstroData::Observation &observation, const HostMemory &hostMemory, TriggeredEvents &triggeredEvents)
{
    unsigned int nrDMs = 0;

    if (options.subbandDedispersion)
    {
        nrDMs = observation.getNrDMs(true) * observation.getNrDMs();
    }
    else
    {
        nrDMs = observation.getNrDMs();
    }
    for (unsigned int beam = 0; beam < options.nrSynthesizedBeamsPerChunk; beam++)
    {
        for (unsigned int dm = 0; dm < nrDMs; dm++)
        {
            unsigned int maxIndex = 0;
            outputDataType maxSNR = 0;
            // outputDataType maxSNR_mad = 0;

            if (options.snrMode == SNRMode::Standard)
            {
                maxSNR = hostMemory.snrData.at((beam * isa::utils::pad(nrDMs, padding / sizeof(float))) + dm);
                maxIndex = hostMemory.snrSamples.at((beam * isa::utils::pad(nrDMs, padding / sizeof(unsigned int))) + dm);
            }
            else if (options.snrMode == SNRMode::Momad)
            {
                maxSNR = (hostMemory.maxValues.at((beam * isa::utils::pad(nrDMs, padding / sizeof(float))) + dm) - hostMemory.medianOfMedians.at((beam * isa::utils::pad(nrDMs, padding / sizeof(float))) + dm)) / (hostMemory.medianOfMediansAbsoluteDeviation.at((beam * isa::utils::pad(nrDMs, padding / sizeof(float))) + dm) * 1.48);
                maxIndex = hostMemory.maxIndices.at((beam * isa::utils::pad(nrDMs, padding / sizeof(unsigned int))) + dm);
            }
            else if ( options.snrMode == SNRMode::MomSigmaCut )
            {
                maxSNR = (hostMemory.maxValues.at((beam * isa::utils::pad(nrDMs, padding / sizeof(float))) + dm) - hostMemory.medianOfMedians.at((beam * isa::utils::pad(nrDMs, padding / sizeof(float))) + dm)) / (options.sigmaCorrectionFactor * hostMemory.stdevs.at((beam * isa::utils::pad(nrDMs, padding / sizeof(float))) + dm));
                maxIndex = hostMemory.maxIndices.at((beam * isa::utils::pad(nrDMs, padding / sizeof(float))) + dm);
            }

            if ( (std::isnormal(maxSNR)) && (maxSNR >= options.threshold) )
            {
                TriggeredEvent event;
                event.beam = firstSynthesizedBeam + beam;
                event.sample = maxIndex;
                event.integration = integration;
                event.DM = dm;
                event.SNR = maxSNR;
                try
                {
                    // Add event to its existing list
                    triggeredEvents.at(beam).at(dm).push_back(event);
                }
                catch (std::out_of_range &err)
                {
                    // Add event to new list
                    std::vector<TriggeredEvent> events;

                    events.push_back(event);
                    triggeredEvents.at(beam).insert(std::pair<unsigned int, std::vector<TriggeredEvent>>(dm, events));
                }
            }
        }
    }
}

void compact(const AstroData::Observation &observation, const TriggeredEvents &triggeredEvents, CompactedEvents &compactedEvents)
{
    CompactedEvents temporaryEvents(observation.getNrSynthesizedBeams());

    // Compact integration
    for (auto beamEvents = triggeredEvents.begin(); beamEvents != triggeredEvents.end(); ++beamEvents)
    {
        for (auto dmEvents = beamEvents->begin(); dmEvents != beamEvents->end(); ++dmEvents)
        {
            CompactedEvent event;

            for (auto dmEvent = dmEvents->second.begin(); dmEvent != dmEvents->second.end(); ++dmEvent)
            {
                if (dmEvent->SNR > event.SNR)
                {
                    event.beam = dmEvent->beam;
                    event.sample = dmEvent->sample;
                    event.integration = dmEvent->integration;
                    event.DM = dmEvent->DM;
                    event.SNR = dmEvent->SNR;
                }
            }
            event.compactedIntegration = dmEvents->second.size();
            temporaryEvents.at(event.beam).push_back(event);
        }
    }
    // Compact DM
    for (auto beamEvents = temporaryEvents.begin(); beamEvents != temporaryEvents.end(); ++beamEvents)
    {
        for (auto event = beamEvents->begin(); event != beamEvents->end(); ++event)
        {
            CompactedEvent finalEvent;
            unsigned int window = 0;

            while ( (event->DM + window) == (event + window)->DM && ((event + window) != beamEvents->end()) )
            {
                if ((event + window)->SNR > finalEvent.SNR)
                {
                    finalEvent.beam = (event + window)->beam;
                    finalEvent.sample = (event + window)->sample;
                    finalEvent.integration = (event + window)->integration;
                    finalEvent.compactedIntegration = (event + window)->compactedIntegration;
                    finalEvent.DM = (event + window)->DM;
                    finalEvent.SNR = (event + window)->SNR;
                }
                window++;
            }
            finalEvent.compactedDMs = window;
            compactedEvents.at(finalEvent.beam).push_back(finalEvent);
            // Move the iterator forward
            event += (window - 1);
        }
    }
}
<commit_msg>Fixed a bug in the trigger.<commit_after>// Copyright 2017 Netherlands Institute for Radio Astronomy (ASTRON)
// Copyright 2017 Netherlands eScience Center
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <Trigger.hpp>

void trigger(const unsigned int firstSynthesizedBeam, const Options &options, const unsigned int padding, const unsigned int integration, const AstroData::Observation &observation, const HostMemory &hostMemory, TriggeredEvents &triggeredEvents)
{
    unsigned int nrDMs = 0;

    if (options.subbandDedispersion)
    {
        nrDMs = observation.getNrDMs(true) * observation.getNrDMs();
    }
    else
    {
        nrDMs = observation.getNrDMs();
    }
    for (unsigned int beam = 0; beam < options.nrSynthesizedBeamsPerChunk; beam++)
    {
        for (unsigned int dm = 0; dm < nrDMs; dm++)
        {
            unsigned int maxIndex = 0;
            outputDataType maxSNR = 0;
            // outputDataType maxSNR_mad = 0;

            if (options.snrMode == SNRMode::Standard)
            {
                maxSNR = hostMemory.snrData.at((beam * isa::utils::pad(nrDMs, padding / sizeof(float))) + dm);
                maxIndex = hostMemory.snrSamples.at((beam * isa::utils::pad(nrDMs, padding / sizeof(unsigned int))) + dm);
            }
            else if (options.snrMode == SNRMode::Momad)
            {
                maxSNR = (hostMemory.maxValues.at((beam * isa::utils::pad(nrDMs, padding / sizeof(float))) + dm) - hostMemory.medianOfMedians.at((beam * isa::utils::pad(nrDMs, padding / sizeof(float))) + dm)) / (hostMemory.medianOfMediansAbsoluteDeviation.at((beam * isa::utils::pad(nrDMs, padding / sizeof(float))) + dm) * 1.48);
                maxIndex = hostMemory.maxIndices.at((beam * isa::utils::pad(nrDMs, padding / sizeof(unsigned int))) + dm);
            }
            else if ( options.snrMode == SNRMode::MomSigmaCut )
            {
                maxSNR = (hostMemory.maxValues.at((beam * isa::utils::pad(nrDMs, padding / sizeof(float))) + dm) - hostMemory.medianOfMedians.at((beam * isa::utils::pad(nrDMs, padding / sizeof(float))) + dm)) / (options.sigmaCorrectionFactor * hostMemory.stdevs.at((beam * isa::utils::pad(nrDMs, padding / sizeof(float))) + dm));
                maxIndex = hostMemory.maxIndices.at((beam * isa::utils::pad(nrDMs, padding / sizeof(float))) + dm);
            }

            if ( (std::isnormal(maxSNR)) && (maxSNR >= options.threshold) )
            {
                TriggeredEvent event;
                event.beam = firstSynthesizedBeam + beam;
                event.sample = maxIndex;
                event.integration = integration;
                event.DM = dm;
                event.SNR = maxSNR;
                try
                {
                    // Add event to its existing list
                    triggeredEvents.at(firstSynthesizedBeam + beam).at(dm).push_back(event);
                }
                catch (std::out_of_range &err)
                {
                    // Add event to new list
                    std::vector<TriggeredEvent> events;

                    events.push_back(event);
                    triggeredEvents.at(firstSynthesizedBeam + beam).insert(std::pair<unsigned int, std::vector<TriggeredEvent>>(dm, events));
                }
            }
        }
    }
}

void compact(const AstroData::Observation &observation, const TriggeredEvents &triggeredEvents, CompactedEvents &compactedEvents)
{
    CompactedEvents temporaryEvents(observation.getNrSynthesizedBeams());

    // Compact integration
    for (auto beamEvents = triggeredEvents.begin(); beamEvents != triggeredEvents.end(); ++beamEvents)
    {
        for (auto dmEvents = beamEvents->begin(); dmEvents != beamEvents->end(); ++dmEvents)
        {
            CompactedEvent event;

            for (auto dmEvent = dmEvents->second.begin(); dmEvent != dmEvents->second.end(); ++dmEvent)
            {
                if (dmEvent->SNR > event.SNR)
                {
                    event.beam = dmEvent->beam;
                    event.sample = dmEvent->sample;
                    event.integration = dmEvent->integration;
                    event.DM = dmEvent->DM;
                    event.SNR = dmEvent->SNR;
                }
            }
            event.compactedIntegration = dmEvents->second.size();
            temporaryEvents.at(event.beam).push_back(event);
        }
    }
    // Compact DM
    for (auto beamEvents = temporaryEvents.begin(); beamEvents != temporaryEvents.end(); ++beamEvents)
    {
        for (auto event = beamEvents->begin(); event != beamEvents->end(); ++event)
        {
            CompactedEvent finalEvent;
            unsigned int window = 0;

            while ( (event->DM + window) == (event + window)->DM && ((event + window) != beamEvents->end()) )
            {
                if ((event + window)->SNR > finalEvent.SNR)
                {
                    finalEvent.beam = (event + window)->beam;
                    finalEvent.sample = (event + window)->sample;
                    finalEvent.integration = (event + window)->integration;
                    finalEvent.compactedIntegration = (event + window)->compactedIntegration;
                    finalEvent.DM = (event + window)->DM;
                    finalEvent.SNR = (event + window)->SNR;
                }
                window++;
            }
            finalEvent.compactedDMs = window;
            compactedEvents.at(finalEvent.beam).push_back(finalEvent);
            // Move the iterator forward
            event += (window - 1);
        }
    }
}
<|endoftext|>


<commit_before>/*
 * Vector2.cpp
 *
 *  Created on: 21/12/2013
 *      Author: vitor
 */

#include "Vector2.h"

Vector2::Vector2(float x, float y) {
	this->x = x;
	this->y = y;
}

Vector2::~Vector2() {
}

Vector2 Vector2::operator +(Vector2 other) {
	return Vector2(this->x + other.x, this->y + other.y);
}

void Vector2::operator +=(Vector2 other) {
	this->x = this->x + other.x;
	this->y = this->y + other.y;
}

Vector2 Vector2::operator -(Vector2 other) {
	return Vector2(this->x - other.x, this->y - other.y);
}

void Vector2::operator -=(Vector2 other) {
	this->x = this->x - other.x;
	this->y = this->y - other.y;
}

Vector2 Vector2::operator *(float scalar) {
	return Vector2(this->x * scalar, this->y * scalar);
}

void Vector2::operator *=(float scalar) {
	this->x = this->x * scalar;
	this->y = this->y * scalar;
}

float Vector2::operator *(Vector2 other) {
	return this->x * other.x + this->y * other.y;
}

float Vector2::length() {
	return sqrt(this->x * this->x + this->y * this->y);
}

/*
 * Angle from -180 to 180, inclusive
 */
float Vector2::angle() {
	return ((M_PI / 180) * atan2(y, x));
}

Vector2 Vector2::normalize() {
	float vecLength = length();
	if (vecLength == 0.0) {
		throw std::logic_error("Attempted to normalize zero vector.\n");
	}
	return Vector2(this->x / vecLength, this->y / vecLength);
}

Vector2 Vector2::rotate(float angleInDegrees) {
	float angleInRadians = angleInDegrees * M_PI / 180;

	return Vector2(
			this->x * cos(angleInRadians) - this->y * sin(angleInRadians),
			this->x * sin(angleInRadians) + this->y * cos(angleInRadians));
}

Vector2 Vector2::normal_vector() {
	return rotate(90);
}

float Vector2::distance(Vector2 other) {
	float diffx = this->x - other.x;
	float diffy = this->y - other.y;
	return sqrt((diffx * diffx) + (diffy * diffy));
}
<commit_msg>Fixed bug with angle method.<commit_after>/*
 * Vector2.cpp
 *
 *  Created on: 21/12/2013
 *      Author: vitor
 */

#include "Vector2.h"

Vector2::Vector2(float x, float y) {
	this->x = x;
	this->y = y;
}

Vector2::~Vector2() {
}

Vector2 Vector2::operator +(Vector2 other) {
	return Vector2(this->x + other.x, this->y + other.y);
}

void Vector2::operator +=(Vector2 other) {
	this->x = this->x + other.x;
	this->y = this->y + other.y;
}

Vector2 Vector2::operator -(Vector2 other) {
	return Vector2(this->x - other.x, this->y - other.y);
}

void Vector2::operator -=(Vector2 other) {
	this->x = this->x - other.x;
	this->y = this->y - other.y;
}

Vector2 Vector2::operator *(float scalar) {
	return Vector2(this->x * scalar, this->y * scalar);
}

void Vector2::operator *=(float scalar) {
	this->x = this->x * scalar;
	this->y = this->y * scalar;
}

float Vector2::operator *(Vector2 other) {
	return this->x * other.x + this->y * other.y;
}

float Vector2::length() {
	return sqrt(this->x * this->x + this->y * this->y);
}

/*
 * Angle from -180 to 180, inclusive
 */
float Vector2::angle() {
	return ((180.0 / M_PI) * atan2(y, x));
}

Vector2 Vector2::normalize() {
	float vecLength = length();
	if (vecLength == 0.0) {
		throw std::logic_error("Attempted to normalize zero vector.\n");
	}
	return Vector2(this->x / vecLength, this->y / vecLength);
}

Vector2 Vector2::rotate(float angleInDegrees) {
	float angleInRadians = angleInDegrees * M_PI / 180;

	return Vector2(
			this->x * cos(angleInRadians) - this->y * sin(angleInRadians),
			this->x * sin(angleInRadians) + this->y * cos(angleInRadians));
}

Vector2 Vector2::normal_vector() {
	return rotate(90);
}

float Vector2::distance(Vector2 other) {
	float diffx = this->x - other.x;
	float diffy = this->y - other.y;
	return sqrt((diffx * diffx) + (diffy * diffy));
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 2017 2018 Jon Taylor
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "blockdb.h"
#include <sstream>
#include <unistd.h>   // open and close
#include "leveldb/db.h"
#include "platform.h"
#include "log.h";
#include <boost/lexical_cast.hpp>

using namespace std;

/**
 * getDatabase
 *
 * Description: Get a levelDB instance.
 */
leveldb::DB * CBlockDB::getDatabase(){
    CPlatform platform;
    CFunctions functions;
    std::string dbPath = platform.getSafirePath();
    //std::cout << dbPath << std::endl;
    leveldb::DB* db;
    leveldb::Options options;
    options.create_if_missing = true;
    leveldb::Status status = leveldb::DB::Open(options, "./blockdb", &db);
    if (false == status.ok())
    {
        cerr << "Unable to open/create test database './blockdb'" << endl;
        cerr << status.ToString() << endl;
        //return null;
    }
    leveldb::WriteOptions writeOptions;
    return db;
}

/**
* AddBlock
*
* Description: Add a block json to the database. Indexed by block number.
*
* @param: CFunctions::block_structure block - structure representing block information.
* @return bool returns 1 is successfull.
*/
bool CBlockDB::AddBlock(CFunctions::block_structure block){
    CFunctions functions;

    /*
    CPlatform platform;
    std::string dbPath = platform.getSafirePath();
    //std::cout << dbPath << std::endl;
    leveldb::DB* db;
    leveldb::Options options;
    options.create_if_missing = true;
    leveldb::Status status = leveldb::DB::Open(options, "./blockdb", &db);
    if (false == status.ok())
    {
        cerr << "Unable to open/create test database './blockdb'" << endl;
        cerr << status.ToString() << endl;
        return -1;
    }
    */
    leveldb::WriteOptions writeOptions;

    leveldb::DB* db = getDatabase();

    // Insert block record into leveldb
    ostringstream keyStream;
    keyStream << "b_" << boost::lexical_cast<std::string>(block.number);
    ostringstream valueStream;
    valueStream << functions.blockJSON(block);
    db->Put(writeOptions, keyStream.str(), valueStream.str());

    // Insert all sender_key->block_id records for fast lookup.
    for(int i = 0; i < block.records.size(); i++ ){
        CFunctions::record_structure record = block.records.at(i);
        ostringstream sendKeyStream;
        sendKeyStream << "s_" << record.sender_public_key;
        ostringstream sendValueStream;
        sendValueStream << functions.blockJSON(block);
        //db->Put(writeOptions, sendKeyStream.str(), sendValueStream.str());
    }

    // Insert all recipient_public_key->block_id records for fast lookup.
    for(int i = 0; i < block.records.size(); i++ ){

    }

    // Save index to next block
    if(block.previous_block_id > -1){
        ostringstream keyStream;
        keyStream << "next_block_" << block.previous_block_id;
        ostringstream valueStream;
        valueStream << block.number;
        db->Put(writeOptions, keyStream.str(), valueStream.str());
    }

    setLatestBlockId(block.number);

    // Close the database
    delete db;
    return true;
}

/**
 * setFirstBlockId
 *
 * Description: Set the dedicated entity recording the network wide first genesis block by id.
 *  Nodes that create a network will set this. Nodes that startup without this value need
 *  to retrieve past block data starting from this first block.
 */
void CBlockDB::setFirstBlockId(long number){
    leveldb::WriteOptions writeOptions;
    leveldb::DB* db = getDatabase();
    ostringstream keyStream;
    keyStream << "first_block_id";
    ostringstream valueStream;
    valueStream << boost::lexical_cast<std::string>(number);
    db->Put(writeOptions, keyStream.str(), valueStream.str());
    delete db;

    //std::cout << " set first " << number << "\n";
}

/**
 * getFirstBlockId
 *
 * Description: Retruen the stord first block id. Represents the start of the chain.
 *   If this is missing the node has to:
 * A) retrieve the chain from other nodes or
 * B) start a new network.
 */
long CBlockDB::getFirstBlockId(){
    leveldb::WriteOptions writeOptions;
    leveldb::DB* db = getDatabase();
    std::string key = "first_block_id";
    std::string firstBlockIdString;
    db->Get(leveldb::ReadOptions(), key, &firstBlockIdString);
    long result = -1;
    if(firstBlockIdString.length() > 0){
        //result = std::stol(firstBlockIdString);
        result = boost::lexical_cast<long>(firstBlockIdString);
        //std::cout << "   --- " << firstBlockIdString << "\n";
    }
    delete db;

    //std::cout << " get first " << result << "\n";

    return result;
}

void CBlockDB::setLatestBlockId(long number){
    leveldb::WriteOptions writeOptions;
    leveldb::DB* db = getDatabase();
    ostringstream keyStream;
    keyStream << "latest_block_id";
    ostringstream valueStream;
    valueStream << boost::lexical_cast<std::string>(number);
    db->Put(writeOptions, keyStream.str(), valueStream.str());
    delete db;
}

long CBlockDB::getLatestBlockId(){
    leveldb::WriteOptions writeOptions;
    leveldb::DB* db = getDatabase();
    std::string key = "latest_block_id"; // boost::lexical_cast<std::string>(number);
    std::string latestBlockIdString;
    db->Get(leveldb::ReadOptions(), key, &latestBlockIdString);
    delete db;
    long result = -1;
    //std::stol(firstBlockId);
    if(latestBlockIdString.length() > 0){
        result = boost::lexical_cast<long>(latestBlockIdString);
    }

    return result;
}

/**
 * getNextBlockId
 *
 * Description: Get a block id for the block that follows after a given block id.
 * Blocks are generated in sequance but there may be ommitions if a node does not generate
 * a block. Each block contains the previous block id and a db index stores these in an index
 * for fast lookup.
 */
long CBlockDB::getNextBlockId(long previousBlockId){
    leveldb::WriteOptions writeOptions;
    leveldb::DB* db = getDatabase();
    ostringstream keyStream;
    keyStream << "next_block_" <<  previousBlockId;
    std::string nextBlockIdString;
    db->Get(leveldb::ReadOptions(), keyStream.str(), &nextBlockIdString);
    delete db;
    long result = -1; //std::stol(nextBlockIdString);
    if(nextBlockIdString.length() > 0){
        result = boost::lexical_cast<long>(nextBlockIdString);
    }
    std::cout << " next " << result << "\n";

    return result;
}

/**
* GetBlocks
*
* Description: get a list of blocks from the DB.
*
* @return:
*/
void CBlockDB::GetBlocks(){
    leveldb::WriteOptions writeOptions;
    leveldb::DB* db = getDatabase();

    // Iterate over each item in the database and print them
    leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
    for (it->SeekToFirst(); it->Valid(); it->Next())
    {
        cout << it->key().ToString() << " : " << it->value().ToString() << endl;
    }
    if (false == it->status().ok())
    {
        cerr << "An error was found during the scan" << endl;
        cerr << it->status().ToString() << endl;
    }
    delete it;

    // Close the database
    delete db;
}

/**
 * getBlock
 *
 * Description: Get a block by number
 *
 * @param: number (long) id of block to look up as a key
 */
CFunctions::block_structure CBlockDB::getBlock(long number){
    CFunctions::block_structure block;
    block.number = -1;
    leveldb::WriteOptions writeOptions;
    leveldb::DB* db = getDatabase();

    std::string key = "b_" + boost::lexical_cast<std::string>(number);
    std::string blockJson;
    db->Get(leveldb::ReadOptions(), key, &blockJson);

    CFunctions functions;
    std::vector<CFunctions::block_structure> blocks = functions.parseBlockJson(blockJson);

    if(blocks.size() > 0){
        block = blocks.at(0);
    }

    // Close the database
    delete db;
    return block;
}

/**
 * getNextBlock
 *
 * Description: get the next block in the chain.
 */
CFunctions::block_structure CBlockDB::getNextBlock(CFunctions::block_structure block){
    long nextId = getNextBlockId(block.number);
    CFunctions::block_structure nextBlock = getBlock(nextId);
    return nextBlock;
}

/**
 * GetBlockWithSender
 *
 * Description: Return a block record that contains a sender key in it.
 *  If no block exists, return null.
 *  This can be used to look up a block record validating a sender has been approved by the network.
 *
 * @param sender_key string.
 * @param index int.
 * @return block_structure.
 */
CFunctions::block_structure CBlockDB::GetBlockWithSender( std::string sender_key, int index ){
    CFunctions::block_structure block;
    leveldb::WriteOptions writeOptions;
    leveldb::DB* db = getDatabase();

    // todo:

    delete db;
    return block;
}

/**
 * DeleteAll
 *
 * Description: Delete all data in the levelDB database.
 */
void CBlockDB::DeleteAll(){
    leveldb::WriteOptions writeOptions;
    leveldb::DB* db = getDatabase();
    // Iterate over each item in the database and print them
    leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
    for (it->SeekToFirst(); it->Valid(); it->Next())
    {
        //cout << it->key().ToString() << " : " << it->value().ToString() << endl;
        db->Delete(leveldb::WriteOptions(), it->key().ToString());
        cout << "Delete: " << it->key().ToString() << "\n";
    }
    if (false == it->status().ok())
    {
        cerr << "An error was found during the scan" << endl;
        cerr << it->status().ToString() << endl;
    }
    delete it;
    delete db;
}
<commit_msg>Bug fix.<commit_after>// Copyright (c) 2016 2017 2018 Jon Taylor
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "blockdb.h"
#include <sstream>
#include <unistd.h>   // open and close
#include "leveldb/db.h"
#include "platform.h"
#include "log.h";
#include <boost/lexical_cast.hpp>

using namespace std;

/**
 * getDatabase
 *
 * Description: Get a levelDB instance.
 */
leveldb::DB * CBlockDB::getDatabase(){
    CPlatform platform;
    CFunctions functions;
    std::string dbPath = platform.getSafirePath();
    //std::cout << dbPath << std::endl;
    leveldb::DB* db;
    leveldb::Options options;
    options.create_if_missing = true;
    leveldb::Status status = leveldb::DB::Open(options, "./blockdb", &db);
    if (false == status.ok())
    {
        cerr << "Unable to open/create test database './blockdb'" << endl;
        cerr << status.ToString() << endl;
        //return null;
    }
    leveldb::WriteOptions writeOptions;
    return db;
}

/**
* AddBlock
*
* Description: Add a block json to the database. Indexed by block number.
*
* @param: CFunctions::block_structure block - structure representing block information.
* @return bool returns 1 is successfull.
*/
bool CBlockDB::AddBlock(CFunctions::block_structure block){
    CFunctions functions;

    /*
    CPlatform platform;
    std::string dbPath = platform.getSafirePath();
    //std::cout << dbPath << std::endl;
    leveldb::DB* db;
    leveldb::Options options;
    options.create_if_missing = true;
    leveldb::Status status = leveldb::DB::Open(options, "./blockdb", &db);
    if (false == status.ok())
    {
        cerr << "Unable to open/create test database './blockdb'" << endl;
        cerr << status.ToString() << endl;
        return -1;
    }
    */
    leveldb::WriteOptions writeOptions;

    leveldb::DB* db = getDatabase();

    // Insert block record into leveldb
    ostringstream keyStream;
    keyStream << "b_" << boost::lexical_cast<std::string>(block.number);
    ostringstream valueStream;
    valueStream << functions.blockJSON(block);
    db->Put(writeOptions, keyStream.str(), valueStream.str());

    // Insert all sender_key->block_id records for fast lookup.
    for(int i = 0; i < block.records.size(); i++ ){
        CFunctions::record_structure record = block.records.at(i);
        ostringstream sendKeyStream;
        sendKeyStream << "s_" << record.sender_public_key;
        ostringstream sendValueStream;
        sendValueStream << functions.blockJSON(block);
        //db->Put(writeOptions, sendKeyStream.str(), sendValueStream.str());
    }

    // Insert all recipient_public_key->block_id records for fast lookup.
    for(int i = 0; i < block.records.size(); i++ ){

    }

    // Save index to next block
    if(block.previous_block_id > -1){
        ostringstream keyStream;
        keyStream << "next_block_" << block.previous_block_id;
        ostringstream valueStream;
        valueStream << block.number;
        db->Put(writeOptions, keyStream.str(), valueStream.str());
    }

    // Close the database
    delete db;

    setLatestBlockId(block.number);

    return true;
}

/**
 * setFirstBlockId
 *
 * Description: Set the dedicated entity recording the network wide first genesis block by id.
 *  Nodes that create a network will set this. Nodes that startup without this value need
 *  to retrieve past block data starting from this first block.
 */
void CBlockDB::setFirstBlockId(long number){
    leveldb::WriteOptions writeOptions;
    leveldb::DB* db = getDatabase();
    ostringstream keyStream;
    keyStream << "first_block_id";
    ostringstream valueStream;
    valueStream << boost::lexical_cast<std::string>(number);
    db->Put(writeOptions, keyStream.str(), valueStream.str());
    delete db;

    //std::cout << " set first " << number << "\n";
}

/**
 * getFirstBlockId
 *
 * Description: Retruen the stord first block id. Represents the start of the chain.
 *   If this is missing the node has to:
 * A) retrieve the chain from other nodes or
 * B) start a new network.
 */
long CBlockDB::getFirstBlockId(){
    leveldb::WriteOptions writeOptions;
    leveldb::DB* db = getDatabase();
    std::string key = "first_block_id";
    std::string firstBlockIdString;
    db->Get(leveldb::ReadOptions(), key, &firstBlockIdString);
    long result = -1;
    if(firstBlockIdString.length() > 0){
        //result = std::stol(firstBlockIdString);
        result = boost::lexical_cast<long>(firstBlockIdString);
        //std::cout << "   --- " << firstBlockIdString << "\n";
    }
    delete db;

    //std::cout << " get first " << result << "\n";

    return result;
}

void CBlockDB::setLatestBlockId(long number){
    leveldb::WriteOptions writeOptions;
    leveldb::DB* db = getDatabase();
    ostringstream keyStream;
    keyStream << "latest_block_id";
    ostringstream valueStream;
    valueStream << boost::lexical_cast<std::string>(number);
    db->Put(writeOptions, keyStream.str(), valueStream.str());
    delete db;
}

long CBlockDB::getLatestBlockId(){
    leveldb::WriteOptions writeOptions;
    leveldb::DB* db = getDatabase();
    std::string key = "latest_block_id"; // boost::lexical_cast<std::string>(number);
    std::string latestBlockIdString;
    db->Get(leveldb::ReadOptions(), key, &latestBlockIdString);
    delete db;
    long result = -1;
    //std::stol(firstBlockId);
    if(latestBlockIdString.length() > 0){
        result = boost::lexical_cast<long>(latestBlockIdString);
    }

    return result;
}

/**
 * getNextBlockId
 *
 * Description: Get a block id for the block that follows after a given block id.
 * Blocks are generated in sequance but there may be ommitions if a node does not generate
 * a block. Each block contains the previous block id and a db index stores these in an index
 * for fast lookup.
 */
long CBlockDB::getNextBlockId(long previousBlockId){
    leveldb::WriteOptions writeOptions;
    leveldb::DB* db = getDatabase();
    ostringstream keyStream;
    keyStream << "next_block_" <<  previousBlockId;
    std::string nextBlockIdString;
    db->Get(leveldb::ReadOptions(), keyStream.str(), &nextBlockIdString);
    delete db;
    long result = -1; //std::stol(nextBlockIdString);
    if(nextBlockIdString.length() > 0){
        result = boost::lexical_cast<long>(nextBlockIdString);
    }
    std::cout << " next " << result << "\n";

    return result;
}

/**
* GetBlocks
*
* Description: get a list of blocks from the DB.
*
* @return:
*/
void CBlockDB::GetBlocks(){
    leveldb::WriteOptions writeOptions;
    leveldb::DB* db = getDatabase();

    // Iterate over each item in the database and print them
    leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
    for (it->SeekToFirst(); it->Valid(); it->Next())
    {
        cout << it->key().ToString() << " : " << it->value().ToString() << endl;
    }
    if (false == it->status().ok())
    {
        cerr << "An error was found during the scan" << endl;
        cerr << it->status().ToString() << endl;
    }
    delete it;

    // Close the database
    delete db;
}

/**
 * getBlock
 *
 * Description: Get a block by number
 *
 * @param: number (long) id of block to look up as a key
 */
CFunctions::block_structure CBlockDB::getBlock(long number){
    CFunctions::block_structure block;
    block.number = -1;
    leveldb::WriteOptions writeOptions;
    leveldb::DB* db = getDatabase();

    std::string key = "b_" + boost::lexical_cast<std::string>(number);
    std::string blockJson;
    db->Get(leveldb::ReadOptions(), key, &blockJson);

    CFunctions functions;
    std::vector<CFunctions::block_structure> blocks = functions.parseBlockJson(blockJson);

    if(blocks.size() > 0){
        block = blocks.at(0);
    }

    // Close the database
    delete db;
    return block;
}

/**
 * getNextBlock
 *
 * Description: get the next block in the chain.
 */
CFunctions::block_structure CBlockDB::getNextBlock(CFunctions::block_structure block){
    long nextId = getNextBlockId(block.number);
    CFunctions::block_structure nextBlock = getBlock(nextId);
    return nextBlock;
}

/**
 * GetBlockWithSender
 *
 * Description: Return a block record that contains a sender key in it.
 *  If no block exists, return null.
 *  This can be used to look up a block record validating a sender has been approved by the network.
 *
 * @param sender_key string.
 * @param index int.
 * @return block_structure.
 */
CFunctions::block_structure CBlockDB::GetBlockWithSender( std::string sender_key, int index ){
    CFunctions::block_structure block;
    leveldb::WriteOptions writeOptions;
    leveldb::DB* db = getDatabase();

    // todo:

    delete db;
    return block;
}

/**
 * DeleteAll
 *
 * Description: Delete all data in the levelDB database.
 */
void CBlockDB::DeleteAll(){
    leveldb::WriteOptions writeOptions;
    leveldb::DB* db = getDatabase();
    // Iterate over each item in the database and print them
    leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
    for (it->SeekToFirst(); it->Valid(); it->Next())
    {
        //cout << it->key().ToString() << " : " << it->value().ToString() << endl;
        db->Delete(leveldb::WriteOptions(), it->key().ToString());
        cout << "Delete: " << it->key().ToString() << "\n";
    }
    if (false == it->status().ok())
    {
        cerr << "An error was found during the scan" << endl;
        cerr << it->status().ToString() << endl;
    }
    delete it;
    delete db;
}
<|endoftext|>


<commit_before>#include "bxsplit.h"

#include "bxcommon.h"
#include <string>
#include <getopt.h>
#include <iostream>
#include <sstream>

#include "SeqLib/BamReader.h"
#include "SeqLib/BamWriter.h"

struct BXTag {

  SeqLib::BamWriter w;
  size_t count = 0;
  SeqLib::BamRecordVector buff;
};

namespace opt {

  static std::string bam; // the bam to split
  static std::string analysis_id = "foo"; // unique prefix for output
  static bool verbose = false;
  static bool noop = false; // dont write bams, just count
  static int min = 0; // minimum number of reads before writing
  static std::string tag = "BX"; // tag to split by
}

static const char* shortopts = "hvxb:a:m:t:";
static const struct option longopts[] = {
  { "help",                    no_argument, NULL, 'h' },
  { "no-output",               no_argument, NULL, 'x' },
  { "analysis-id",             required_argument, NULL, 'a' },
  { "verbose",                 no_argument, NULL, 'v' },
  { "min-reads",               required_argument, NULL, 'm' },
  { "tag",                     required_argument, NULL, 't' },
  { NULL, 0, NULL, 0 }
};

static const char *SPLIT_USAGE_MESSAGE =
"Usage: bxtools split <BAM/SAM/CRAM> -a <id> > bxcounts.tsv\n"
"Description: Split / count a BAM into multiple BAMs, one BAM per unique BX tag\n"
"\n"
"  General options\n"
"  -v, --verbose                        Select verbosity level (0-4). Default: 0 \n"
"  -h, --help                           Display this help and exit\n"
"  -a, --analysis-id                    ID to prefix output files with [foo]\n"
"  -x, --no-output                      Don't output BAMs (count only) [off]\n"
"  -m, --min-reads                      Minumum reads of given tag to see before writing [0]\n"
"  -t, --tag                            Split by a tag other than BX (e.g. MI)\n"
"\n";

void parseSplitOptions(int argc, char** argv) {

  bool die = false;

  if (argc < 2)
    die = true;
  else
    opt::bam = std::string(argv[1]);

  bool help = false;
  std::stringstream ss;

  for (char c; (c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1;) {
    std::istringstream arg(optarg != NULL ? optarg : "");
    switch (c) {
    case 'a': arg >> opt::analysis_id; break;
    case 'v': opt::verbose = true; break;
    case 'x': opt::noop = true; break;
    case 'm': arg >> opt::min; break;
    case 't': arg >> opt::tag; break;
    }
  }

  if (die || help) {
    std::cerr << "\n" << SPLIT_USAGE_MESSAGE;
    die ? exit(EXIT_FAILURE) : exit(EXIT_SUCCESS);
  }
}

void runSplit(int argc, char** argv) {

  parseSplitOptions(argc, argv);

  // opeen the BAM
  SeqLib::BamReader reader;
  if (!reader.Open(opt::bam)) {
    std::cerr << "Failed to open bam: " << opt::bam << std::endl;
    exit(EXIT_FAILURE);
  }

  // make a collection of writers
  std::unordered_map<std::string, BXTag> tags;

  // loop and write
  SeqLib::BamRecord r;
  size_t count = 0;
  bool hit = false;
  while (reader.GetNextRecord(r)) {

    ++count;

    // sanity check
    BXLOOPCHECK(r, hit, opt::tag)

    std::string bx;
    r.GetTag(opt::tag, bx);
    if (bx.empty()) {
      continue;
    } else {
      hit = true;
    }

    ++tags[bx].count;

    if (opt::noop)
      continue;

    if (tags[bx].count < opt::min) {
      tags[bx].buff.push_back(r);
      continue;
    }

    // have a buffer to clear or hit first read with no min
    if (tags[bx].buff.size() || (opt::min <= 0 && tags[bx].count == 1)) {

      // need to establish a new writer?
      std::string bname = opt::analysis_id + "." + bx + ".bam";
      if (!tags[bx].w.Open(bname)) {
	std::cerr << "Could not open BAM: " << bname << std::endl;
	exit(EXIT_FAILURE);
      }

      std::cerr << "creating new output BAM: " << bname << std::endl;
      tags[bx].w.SetHeader(reader.Header());
      tags[bx].w.WriteHeader();
      for (const auto& rr : tags[bx].buff)
	tags[bx].w.WriteRecord(rr);
      tags[bx].buff.clear();
      continue;
    }

    if (!tags[bx].w.WriteRecord(r)) {
      std::cerr << "failed to write read " << r << " to BAM for " << bx << std::endl;
      exit(EXIT_FAILURE);
    }

  }

  // print the final counts to std::out
  for (const auto& b : tags)
    std::cout << b.first << "\t" << b.second.count << std::endl;

}
<commit_msg>added include_empty flag for issue #26<commit_after>#include "bxsplit.h"

#include "bxcommon.h"
#include <string>
#include <getopt.h>
#include <iostream>
#include <sstream>

#include "SeqLib/BamReader.h"
#include "SeqLib/BamWriter.h"

struct BXTag {

  SeqLib::BamWriter w;
  size_t count = 0;
  SeqLib::BamRecordVector buff;
};

namespace opt {

  static std::string bam; // the bam to split
  static std::string analysis_id = "foo"; // unique prefix for output
  static bool verbose = false;
  static bool noop = false; // dont write bams, just count
  static int min = 0; // minimum number of reads before writing
  static std::string tag = "BX"; // tag to split by
  static bool include_empty = false; // output BAM with empty reads
}

static const char* shortopts = "hvxeb:a:m:t:";
static const struct option longopts[] = {
  { "help",                    no_argument, NULL, 'h' },
  { "no-output",               no_argument, NULL, 'x' },
  { "analysis-id",             required_argument, NULL, 'a' },
  { "verbose",                 no_argument, NULL, 'v' },
  { "include-empty",           no_argument, NULL, 'e' },
  { "min-reads",               required_argument, NULL, 'm' },
  { "tag",                     required_argument, NULL, 't' },
  { NULL, 0, NULL, 0 }
};

static const char *SPLIT_USAGE_MESSAGE =
"Usage: bxtools split <BAM/SAM/CRAM> -a <id> > bxcounts.tsv\n"
"Description: Split / count a BAM into multiple BAMs, one BAM per unique BX tag\n"
"\n"
"  General options\n"
"  -v, --verbose                        Select verbosity level (0-4). Default: 0 \n"
"  -h, --help                           Display this help and exit\n"
"  -a, --analysis-id                    ID to prefix output files with [foo]\n"
"  -x, --no-output                      Don't output BAMs (count only) [off]\n"
"  -m, --min-reads                      Minumum reads of given tag to see before writing [0]\n"
"  -t, --tag                            Split by a tag other than BX (e.g. MI)\n"
"  -e, --include-empty                  Output a BAM with all of the reads with empty tag\n"
"\n";

void parseSplitOptions(int argc, char** argv) {

  bool die = false;

  if (argc < 2)
    die = true;
  else
    opt::bam = std::string(argv[1]);

  bool help = false;
  std::stringstream ss;

  for (char c; (c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1;) {
    std::istringstream arg(optarg != NULL ? optarg : "");
    switch (c) {
    case 'a': arg >> opt::analysis_id; break;
    case 'v': opt::verbose = true; break;
    case 'x': opt::noop = true; break;
    case 'e': opt::include_empty = true; break;
    case 'm': arg >> opt::min; break;
    case 't': arg >> opt::tag; break;
    }
  }

  if (die || help) {
    std::cerr << "\n" << SPLIT_USAGE_MESSAGE;
    die ? exit(EXIT_FAILURE) : exit(EXIT_SUCCESS);
  }
}

void runSplit(int argc, char** argv) {

  parseSplitOptions(argc, argv);

  // opeen the BAM
  SeqLib::BamReader reader;
  if (!reader.Open(opt::bam)) {
    std::cerr << "Failed to open bam: " << opt::bam << std::endl;
    exit(EXIT_FAILURE);
  }

  // make a collection of writers
  std::unordered_map<std::string, BXTag> tags;

  // loop and write
  SeqLib::BamRecord r;
  size_t count = 0;
  bool hit = false;
  while (reader.GetNextRecord(r)) {

    ++count;

    // sanity check
    BXLOOPCHECK(r, hit, opt::tag)

    std::string bx;
    r.GetTag(opt::tag, bx);
    if (bx.empty()) {
      if (!opt::include_empty)
	continue;
      bx="bxe"; // bxtools empty
    } else {
      hit = true;
    }

    ++tags[bx].count;

    if (opt::noop)
      continue;

    if (tags[bx].count < opt::min) {
      tags[bx].buff.push_back(r);
      continue;
    }

    // have a buffer to clear or hit first read with no min
    if (tags[bx].buff.size() || (opt::min <= 0 && tags[bx].count == 1)) {

      // need to establish a new writer?
      std::string bname = opt::analysis_id + "." + bx + ".bam";
      if (!tags[bx].w.Open(bname)) {
	std::cerr << "Could not open BAM: " << bname << std::endl;
	exit(EXIT_FAILURE);
      }

      std::cerr << "creating new output BAM: " << bname << std::endl;
      tags[bx].w.SetHeader(reader.Header());
      tags[bx].w.WriteHeader();
      for (const auto& rr : tags[bx].buff)
	tags[bx].w.WriteRecord(rr);
      tags[bx].buff.clear();
      continue;
    }

    if (!tags[bx].w.WriteRecord(r)) {
      std::cerr << "failed to write read " << r << " to BAM for " << bx << std::endl;
      exit(EXIT_FAILURE);
    }

  }

  // print the final counts to std::out
  for (const auto& b : tags)
    std::cout << b.first << "\t" << b.second.count << std::endl;

}
<|endoftext|>