// Copyright JGX Software Solutions, 2018.

// THIS 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 
// THIS SOFTWARE.

#include <cmath>
#include <vector>
#include <fstream>
#include <iostream>

using namespace std;

struct vertex {
	float x;
	float y;
	float z;
};

void createIcosahedron(vector<vertex> &);
void STLFacetOut(ofstream &, vector<vertex>::const_iterator, vertex);
void STLExport(ofstream &, const vector<vertex> &);

vertex operator ^ (const vertex &, const vertex &);

int main()
{
	// An icosahedron has 20 triangular facets ...
	const int numIcosahedronFacets = 20;

	// Each of which requires 3 vertices.
	vector<vertex> vertices(3 * numIcosahedronFacets);

	// Determine the locations of the icosahedron vertices.
	createIcosahedron(vertices);

	ofstream stlout("icosahedron.stl", ofstream::trunc);

	// Export the data in text-based STL format.
	STLExport(stlout, vertices);

	stlout.close();

    return 0;
}


vertex operator ^ (const vertex & a, const vertex & b) {
	// Cross product.
	vertex result;
	result.x = a.y * b.z - a.z * b.y;
	result.y = a.z * b.x - a.x * b.z;
	result.z = a.x * b.y - a.y * b.x;
	return(result);
}

vertex operator - (const vertex & a, const vertex & b) {
	// Subtraction.
	vertex result;
	result.x = a.x - b.x;
	result.y = a.y - b.y;
	result.z = a.z - b.z;
	return(result);
}

void unit(vertex & v) {
	// Normalize a vector.
	float vmod = pow(v.x, 2) + pow(v.y, 2) + pow(v.z, 2);
	vmod = sqrt(vmod);

	if (vmod > (float)1.0e-9) {
		v.x /= vmod;
		v.y /= vmod;
		v.z /= vmod;
	}
}


void STLExport(ofstream & stlout, const vector<vertex> & vertices) {
	// Export in STL format to an output stream, stlout.

	vertex normal;

	stlout << "solid STLExport" << endl;

	for (auto q = vertices.begin(); q < vertices.end(); q += 3) {

		normal = (q[1] - q[0]) ^ (q[2] - q[1]);
		unit(normal);

		STLFacetOut(stlout, q, normal);
	}

	stlout << "endsolid STLExport" << endl;

}

void STLFacetOut(ofstream & stlout, vector<vertex>::const_iterator vv, vertex normal) {

	// Export an STL facet to an output stream, stlout.

	stlout << "facet normal " << normal.x << " " << normal.y << " " << normal.z << endl;
	stlout << "   outer loop" << endl;
	stlout << "      vertex " << vv[0].x << " " << vv[0].y << " " << vv[0].z << endl;
	stlout << "      vertex " << vv[1].x << " " << vv[1].y << " " << vv[1].z << endl;
	stlout << "      vertex " << vv[2].x << " " << vv[2].y << " " << vv[2].z << endl;
	stlout << "   endloop" << endl;
	stlout << "endfacet" << endl;
}

void createIcosahedron(vector<vertex> & vertices) {

	// Determine the 60 vertices required to create an icosahedron with
	// unit side length.

	const float pi = 3.141592654f;

	float c36 = cos(36.0f * pi / 180.0f);
	float t36 = tan(36.0f * pi / 180.0f);

	float eta  = 0.5f / t36;
	float zeta = sqrt(0.25f + pow(eta, 2));
	float ksi  = sqrt(0.75f - pow(eta - zeta, 2));
	float alp  = sqrt(0.75f - pow(eta, 2));

	float aa = zeta * cos(18.0f * pi / 180.0f);
	float bb = zeta * sin(18.0f * pi / 180.0f);


	vertices = { 
		
	{  -0.5f,   0.0f,      -eta },
	{   0.5f,   0.0f,      -eta },
	{   0.0f,    alp,      0.0f },

	
	{	0.5f,   0.0f,      -eta },
	{     aa,   0.0f,        bb },
	{   0.0f,    alp,      0.0f },

	{ aa,       0.0f,        bb },
	{ 0.0f,     0.0f,      zeta },
	{ 0.0f,      alp,      0.0f },

	{ 0.0f,     0.0f,      zeta },
	{ -aa,      0.0f,        bb },
	{ 0.0f,      alp,      0.0f },

	{ -aa,      0.0f,        bb },
	{ -0.5f,    0.0f,      -eta },
	{ 0.0f,      alp,      0.0f },

	{ 0.0f,     -ksi,     -zeta },
	{ aa,       -ksi,       -bb },
	{ 0.0f,  -alp - ksi,   0.0f },

	{ aa,       -ksi,       -bb },
	{ 0.5f,     -ksi,       eta },
	{ 0.0f,  -alp - ksi,   0.0f },

	{ 0.5f,     -ksi,       eta },
	{ -0.5f,    -ksi,       eta },
	{ 0.0f,  -alp - ksi,   0.0f },

	{ -0.5f,    -ksi,       eta },
	{ -aa,      -ksi,       -bb },
	{ 0.0f,  -alp - ksi,   0.0f },

	{ -aa,      -ksi,       -bb },
	{ 0.0f,     -ksi,     -zeta },
	{ 0.0f,  -alp - ksi,   0.0f },

	{ 0.0f,     -ksi,     -zeta },
	{ 0.5f,     0.0f,      -eta },
	{ aa,       -ksi,       -bb },

	{ 0.5f,     0.0f,      -eta },
	{ aa,       -ksi,       -bb },
	{ aa,       0.0f,        bb },

	{ aa,       -ksi,       -bb },
	{ aa,       0.0f,        bb },
	{ 0.5f,     -ksi,       eta },

	{ aa,       0.0f,        bb },
	{ 0.5f,     -ksi,       eta },
	{ 0.0f,     0.0f,      zeta },

	{ 0.5f,     -ksi,       eta },
	{ 0.0f,     0.0f,      zeta },
	{ -0.5f,    -ksi,       eta },

	{ 0.0f,     0.0f,      zeta },
	{ -0.5f,    -ksi,       eta },
	{ -aa,      0.0f,        bb },

	{ -0.5f,    -ksi,       eta },
	{ -aa,      0.0f,        bb },
	{ -aa,      -ksi,       -bb },

	{ -aa,      0.0f,        bb },
	{ -aa,      -ksi,       -bb },
	{ -0.5f,    0.0f,      -eta },

	{ -aa,      -ksi,       -bb },
	{ -0.5f,    0.0f,      -eta },
	{ 0.0f,     -ksi,     -zeta },


	{ -0.5f,    0.0f,      -eta },
	{ 0.0f,     -ksi,     -zeta },
	{ 0.5f,     0.0f,      -eta }

	};

}

