<commit_before>/*

Copyright (c) 2007-2009, Python File Format Interface
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

   * Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.

   * Redistributions in binary form must reproduce the above
     copyright notice, this list of conditions and the following
     disclaimer in the documentation and/or other materials provided
     with the distribution.

   * Neither the name of the Python File Format Interface
     project nor the names of its contributors may be used to endorse
     or promote products derived from this software without specific
     prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

// Based on:
// http://techgame.net/projects/Runeblade/browser/trunk/RBRapier/RBRapier/Tools/Geometry/Analysis/TriangleMesh.py?rev=760

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~ License of original code:
//~
//- The RuneBlade Foundation library is intended to ease some
//- aspects of writing intricate Jabber, XML, and User Interface (wxPython, etc.)
//- applications, while providing the flexibility to modularly change the
//- architecture. Enjoy.
//~
//~ Copyright (C) 2002  TechGame Networks, LLC.
//~
//~ This library is free software; you can redistribute it and/or
//~ modify it under the terms of the BSD style License as found in the
//~ LICENSE file included with this distribution.
//~
//~ TechGame Networks, LLC can be reached at:
//~ 3578 E. Hartsel Drive #211
//~ Colorado Springs, Colorado, USA, 80920
//~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~ Imports
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#include <iostream> // for dump
#include <stdexcept>

#include <boost/foreach.hpp>

#include "trianglemesh.hpp"

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~ Definitions
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Edge::Edge(int _ev0, int _ev1) {
	// ensure it is not degenerate
	if (_ev0 == _ev1)
		throw std::runtime_error("Degenerate edge.");
	// order indices
	if (_ev0 < _ev1) {
		ev0 = _ev0;
		ev1 = _ev1;
	} else {
		ev0 = _ev1;
		ev1 = _ev0;
	}
};

bool Edge::operator<(const Edge & otheredge) const {
	if (ev0 < otheredge.ev0) return true;
	if (ev1 < otheredge.ev1) return true;
	return false;
};

bool Edge::operator==(const Edge & otheredge) const {
	return ((ev0 == otheredge.ev0) && (ev1 == otheredge.ev1));
};

std::vector<int> Edge::get_common_vertices(const Edge & otheredge) const {
	// return [v for v in otheredge.ev if v in self.ev]
	std::vector<int> result;
	if ((ev0 == otheredge.ev0) || (ev0 == otheredge.ev1))
		result.push_back(ev0);
	if ((ev1 == otheredge.ev0) || (ev1 == otheredge.ev1))
		result.push_back(ev1);
	return result;
};

MEdge::MEdge(const Edge & edge) : Edge(edge), faces() {
	// nothing to do: faces are set in Mesh::add_face.
};

MFacePtr MEdge::get_next_face(Faces::const_iterator & face_iter) const {
	// if there is no next face: we are done
	if (faces.size() <= 1) {
		return MFacePtr();
	}
	// advance the iterator
	do {
		face_iter++;
		if (face_iter == faces.end()) face_iter = faces.begin();
	} while (face_iter->expired());
	// return result to new pointer
	return MFacePtr(*face_iter);
};

void MEdge::dump() {
	std::cout << "  edge " << ev0 << "," << ev1 << std::endl;
	BOOST_FOREACH(boost::weak_ptr<MFace> face, faces) {
		std::cout << "    face " << face.lock()->v0 << "," << face.lock()->v1 << "," << face.lock()->v2 << std::endl;
	};
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Face::Face(int _v0, int _v1, int _v2) {
	// ensure it is not degenerate
	if ((_v0 == _v1) || (_v1 == _v2) || (_v0 == _v2))
		throw std::runtime_error("Degenerate face.");
	// order vertex indices
	if ((_v0 < _v1) && (_v0 < _v2)) {
		v0 = _v0;
		v1 = _v1;
		v2 = _v2;
	} else if ((_v1 < _v0) && (_v1 < _v2)) {
		v0 = _v1;
		v1 = _v2;
		v2 = _v0;
	} else if ((_v2 < _v0) && (_v2 < _v1)) {
		v0 = _v2;
		v1 = _v0;
		v2 = _v1;
	} else {
		throw std::runtime_error("Oops. Face construction bug!");
	}
};

bool Face::operator<(const Face & otherface) const {
	if (v0 < otherface.v0) return true;
	if (v1 < otherface.v1) return true;
	if (v2 < otherface.v2) return true;
	return false;
};

bool Face::operator==(const Face & otherface) const {
	return ((v0 == otherface.v0) && (v1 == otherface.v1) && (v2 == otherface.v2));
};

int Face::get_vertex_winding(int pv0, int pv1) const {
	if (((pv0 == v0) && (pv1 == v1))
	        ||
	        ((pv0 == v1) && (pv1 == v2))
	        ||
	        ((pv0 == v2) && (pv1 == v0))) return 0;
	else if (((pv1 == v0) && (pv0 == v1))
	         ||
	         ((pv1 == v1) && (pv0 == v2))
	         ||
	         ((pv1 == v2) && (pv0 == v0))) return 1;
	// bug!
	throw std::runtime_error("Invalid vertex index.");
}

int Face::get_next_vertex(int vi) const {
	if (vi == v0) return v1;
	else if (vi == v1) return v2;
	else if (vi == v2) return v0;
	// bug!
	throw std::runtime_error("Invalid vertex index.");
}

int Face::get_other_vertex(int pv0, int pv1) const {
	if (pv0 == pv1)
		throw std::runtime_error("Vertex indices shouldn't be identical.");
	if (((pv0 == v0) && (pv1 == v1)) || ((pv0 == v1) && (pv1 == v0)))
		return v2;
	if (((pv0 == v1) && (pv1 == v2)) || ((pv0 == v2) && (pv1 == v1)))
		return v0;
	if (((pv0 == v2) && (pv1 == v0)) || ((pv0 == v0) && (pv1 == v2)))
		return v1;
	// bug!
	throw std::runtime_error("Invalid vertex index.");
}

MFace::MFace(const Face & face)
		: Face(face), edges(),
		strip_id(-1), test_strip_id(-1), experiment_id(-1) {
	// nothing to do
};

MEdgePtr MFace::get_edge(int ev0, int ev1) const {
	if (ev0 == ev1)
		throw std::runtime_error("Vertex indices shouldn't be identical.");
	if (((ev0 == v0) && (ev1 == v1)) || ((ev0 == v1) && (ev1 == v0)))
		return edges[0];
	if (((ev0 == v1) && (ev1 == v2)) || ((ev0 == v2) && (ev1 == v1)))
		return edges[1];
	if (((ev0 == v2) && (ev1 == v0)) || ((ev0 == v0) && (ev1 == v2)))
		return edges[2];
	// bug!
	throw std::runtime_error("Invalid vertex index.");
}

std::vector<MEdgePtr> MFace::get_common_edges(const MFace & otherface) const {
	//return [edge for edge in otherface.edges if edge in self.edges]
	std::vector<MEdgePtr> result;
	for (int i=0; i<3; i++) {
		for (int j=0; j<3; j++) {
			if (edges[i] == otherface.edges[j]) {
				result.push_back(edges[i]);
				break; // no otherface edges can match, so break j loop
			}
		}
	}
	return result;
}

MFacePtr MFace::get_next_face(int ev0, int ev1) {
	// Note: Original name was FindOtherFace.
	// Note: implementation is slightly simpler compared to the
	// original RuneBlade FindOtherFace algorithm but in most use
	// cases it gives identical results.
	MEdgePtr edge = get_edge(ev0, ev1);
	for (MEdge::Faces::const_iterator face_iter = edge->faces.begin();
	        face_iter != edge->faces.end(); face_iter++) {
		MFacePtr otherface = face_iter->lock();
		// skip expired faces
		// (lock() returns MFacePtr() on expired faces!)
		if (!otherface) continue;
		// skip given face
		if (otherface.get() == this) continue;
		// return other face if it has different winding along the edge
		if (get_vertex_winding(ev0, ev1)
		        != otherface->get_vertex_winding(ev0, ev1)) {
			return otherface;
		};
	};
	return MFacePtr();
}

void MFace::dump() {
	std::cout << "  face " << v0 << "," << v1 << "," << v2 << std::endl;
	std::cout << "    edge 0 " << edges[0]->ev0 << "," << edges[0]->ev1 << std::endl;
	std::cout << "    edge 1 " << edges[1]->ev0 << "," << edges[1]->ev1 << std::endl;
	std::cout << "    edge 2 " << edges[2]->ev0 << "," << edges[2]->ev1 << std::endl;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

MEdgePtr Mesh::add_edge(int ev0, int ev1) {
	// create edge index and search if edge already exists in mesh
	Edge edge_index(ev0, ev1);
	EdgeMap::const_iterator edge_iter = edges.find(edge_index);
	if ((edge_iter != edges.end()) && (!edge_iter->second.expired())) {
		// edge already exists!
		return MEdgePtr(edge_iter->second);
	} else {
		// create edge<commit_msg>Bugfix in edge and face comparison (is now full ordering, this fixes issues with mapping).<commit_after>/*

Copyright (c) 2007-2009, Python File Format Interface
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

   * Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.

   * Redistributions in binary form must reproduce the above
     copyright notice, this list of conditions and the following
     disclaimer in the documentation and/or other materials provided
     with the distribution.

   * Neither the name of the Python File Format Interface
     project nor the names of its contributors may be used to endorse
     or promote products derived from this software without specific
     prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

// Based on:
// http://techgame.net/projects/Runeblade/browser/trunk/RBRapier/RBRapier/Tools/Geometry/Analysis/TriangleMesh.py?rev=760

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~ License of original code:
//~
//- The RuneBlade Foundation library is intended to ease some
//- aspects of writing intricate Jabber, XML, and User Interface (wxPython, etc.)
//- applications, while providing the flexibility to modularly change the
//- architecture. Enjoy.
//~
//~ Copyright (C) 2002  TechGame Networks, LLC.
//~
//~ This library is free software; you can redistribute it and/or
//~ modify it under the terms of the BSD style License as found in the
//~ LICENSE file included with this distribution.
//~
//~ TechGame Networks, LLC can be reached at:
//~ 3578 E. Hartsel Drive #211
//~ Colorado Springs, Colorado, USA, 80920
//~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~ Imports
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#include <iostream> // for dump
#include <stdexcept>

#include <boost/foreach.hpp>

#include "trianglemesh.hpp"

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~ Definitions
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Edge::Edge(int _ev0, int _ev1) {
	// ensure it is not degenerate
	if (_ev0 == _ev1)
		throw std::runtime_error("Degenerate edge.");
	// order indices
	if (_ev0 < _ev1) {
		ev0 = _ev0;
		ev1 = _ev1;
	} else {
		ev0 = _ev1;
		ev1 = _ev0;
	}
};

bool Edge::operator<(const Edge & otheredge) const {
	if (ev0 < otheredge.ev0) return true;
	if (ev0 > otheredge.ev0) return false;
	// ev0 == otheredge.ev0, so use ev1 for comparing
	if (ev1 < otheredge.ev1) return true;
	return false;
};

bool Edge::operator==(const Edge & otheredge) const {
	return ((ev0 == otheredge.ev0) && (ev1 == otheredge.ev1));
};

std::vector<int> Edge::get_common_vertices(const Edge & otheredge) const {
	// return [v for v in otheredge.ev if v in self.ev]
	std::vector<int> result;
	if ((ev0 == otheredge.ev0) || (ev0 == otheredge.ev1))
		result.push_back(ev0);
	if ((ev1 == otheredge.ev0) || (ev1 == otheredge.ev1))
		result.push_back(ev1);
	return result;
};

MEdge::MEdge(const Edge & edge) : Edge(edge), faces() {
	// nothing to do: faces are set in Mesh::add_face.
};

MFacePtr MEdge::get_next_face(Faces::const_iterator & face_iter) const {
	// if there is no next face: we are done
	if (faces.size() <= 1) {
		return MFacePtr();
	}
	// advance the iterator
	do {
		face_iter++;
		if (face_iter == faces.end()) face_iter = faces.begin();
	} while (face_iter->expired());
	// return result to new pointer
	return MFacePtr(*face_iter);
};

void MEdge::dump() {
	std::cout << "  edge " << ev0 << "," << ev1 << std::endl;
	BOOST_FOREACH(boost::weak_ptr<MFace> face, faces) {
		std::cout << "    face " << face.lock()->v0 << "," << face.lock()->v1 << "," << face.lock()->v2 << std::endl;
	};
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Face::Face(int _v0, int _v1, int _v2) {
	// ensure it is not degenerate
	if ((_v0 == _v1) || (_v1 == _v2) || (_v0 == _v2))
		throw std::runtime_error("Degenerate face.");
	// order vertex indices
	if ((_v0 < _v1) && (_v0 < _v2)) {
		v0 = _v0;
		v1 = _v1;
		v2 = _v2;
	} else if ((_v1 < _v0) && (_v1 < _v2)) {
		v0 = _v1;
		v1 = _v2;
		v2 = _v0;
	} else if ((_v2 < _v0) && (_v2 < _v1)) {
		v0 = _v2;
		v1 = _v0;
		v2 = _v1;
	} else {
		throw std::runtime_error("Oops. Face construction bug!");
	}
};

bool Face::operator<(const Face & otherface) const {
	if (v0 < otherface.v0) return true;
	if (v0 > otherface.v0) return false;
	if (v1 < otherface.v1) return true;
	if (v1 > otherface.v1) return false;
	if (v2 < otherface.v2) return true;
	return false;
};

bool Face::operator==(const Face & otherface) const {
	return ((v0 == otherface.v0) && (v1 == otherface.v1) && (v2 == otherface.v2));
};

int Face::get_vertex_winding(int pv0, int pv1) const {
	if (((pv0 == v0) && (pv1 == v1))
	        ||
	        ((pv0 == v1) && (pv1 == v2))
	        ||
	        ((pv0 == v2) && (pv1 == v0))) return 0;
	else if (((pv1 == v0) && (pv0 == v1))
	         ||
	         ((pv1 == v1) && (pv0 == v2))
	         ||
	         ((pv1 == v2) && (pv0 == v0))) return 1;
	// bug!
	throw std::runtime_error("Invalid vertex index.");
}

int Face::get_next_vertex(int vi) const {
	if (vi == v0) return v1;
	else if (vi == v1) return v2;
	else if (vi == v2) return v0;
	// bug!
	throw std::runtime_error("Invalid vertex index.");
}

int Face::get_other_vertex(int pv0, int pv1) const {
	if (pv0 == pv1)
		throw std::runtime_error("Vertex indices shouldn't be identical.");
	if (((pv0 == v0) && (pv1 == v1)) || ((pv0 == v1) && (pv1 == v0)))
		return v2;
	if (((pv0 == v1) && (pv1 == v2)) || ((pv0 == v2) && (pv1 == v1)))
		return v0;
	if (((pv0 == v2) && (pv1 == v0)) || ((pv0 == v0) && (pv1 == v2)))
		return v1;
	// bug!
	throw std::runtime_error("Invalid vertex index.");
}

MFace::MFace(const Face & face)
		: Face(face), edges(),
		strip_id(-1), test_strip_id(-1), experiment_id(-1) {
	// nothing to do
};

MEdgePtr MFace::get_edge(int ev0, int ev1) const {
	if (ev0 == ev1)
		throw std::runtime_error("Vertex indices shouldn't be identical.");
	if (((ev0 == v0) && (ev1 == v1)) || ((ev0 == v1) && (ev1 == v0)))
		return edges[0];
	if (((ev0 == v1) && (ev1 == v2)) || ((ev0 == v2) && (ev1 == v1)))
		return edges[1];
	if (((ev0 == v2) && (ev1 == v0)) || ((ev0 == v0) && (ev1 == v2)))
		return edges[2];
	// bug!
	throw std::runtime_error("Invalid vertex index.");
}

std::vector<MEdgePtr> MFace::get_common_edges(const MFace & otherface) const {
	//return [edge for edge in otherface.edges if edge in self.edges]
	std::vector<MEdgePtr> result;
	for (int i=0; i<3; i++) {
		for (int j=0; j<3; j++) {
			if (edges[i] == otherface.edges[j]) {
				result.push_back(edges[i]);
				break; // no otherface edges can match, so break j loop
			}
		}
	}
	return result;
}

MFacePtr MFace::get_next_face(int ev0, int ev1) {
	// Note: Original name was FindOtherFace.
	// Note: implementation is slightly simpler compared to the
	// original RuneBlade FindOtherFace algorithm but in most use
	// cases it gives identical results.
	MEdgePtr edge = get_edge(ev0, ev1);
	for (MEdge::Faces::const_iterator face_iter = edge->faces.begin();
	        face_iter != edge->faces.end(); face_iter++) {
		MFacePtr otherface = face_iter->lock();
		// skip expired faces
		// (lock() returns MFacePtr() on expired faces!)
		if (!otherface) continue;
		// skip given face
		if (otherface.get() == this) continue;
		// return other face if it has different winding along the edge
		if (get_vertex_winding(ev0, ev1)
		        != otherface->get_vertex_winding(ev0, ev1)) {
			return otherface;
		};
	};
	return MFacePtr();
}

void MFace::dump() {
	std::cout << "  face " << v0 << "," << v1 << "," << v2 << std::endl;
	std::cout << "    edge " << edges[0]->ev0 << "," << edges[0]->ev1 << std::endl;
	std::cout << "    edge " << edges[1]->ev0 << "," << edges[1]->ev1 << std::endl;
	std::cout << "    edge " << edges[2]->ev0 << "," << edges[2]->ev1 << std::endl;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

MEdgePtr Mesh::add_edge(int ev0, int ev1) {
	// create edge index and search if edge already exists in mesh
	Edge edge_index(ev0, ev1);
	EdgeMap::const_iterator edge_iter = edges.find(edge_index);
	if ((edge_iter != edges.end()) && (!edge_iter->second.expired())) {
		// edge already exists!
		return MEdgePtr(edge_iter->second);
	} else {
		// create edge
{'fraction_non_alphanumeric': 0.20012131906915187, 'fraction_numerical': 0.02779309584206463, 'mean_word_length': 3.7723684210526316, 'pattern_counts': {'":': 0, '<': 189, '<?xml version=': 0, '>': 54, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4852616', 'n_tokens_mistral': 6525, 'n_tokens_neox': 6181, 'n_words': 1940}