<commit_before>//
//  Mesh.m
//  OpenGLEditor
//
//  Created by <NAME> on 7/29/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//

#import "Mesh.h"

BOOL IsTriangleDegenerated(Triangle triangle)
{
	if (triangle.vertexIndices[0] == triangle.vertexIndices[1])
		return YES;
	if (triangle.vertexIndices[0] == triangle.vertexIndices[2])
		return YES;
	if (triangle.vertexIndices[1] == triangle.vertexIndices[2])
		return YES;

	return NO;
}

BOOL AreEdgesSame(Edge first, Edge second)
{
	if (first.vertexIndices[0] == second.vertexIndices[0] &&
		first.vertexIndices[1] == second.vertexIndices[1])
		return YES;
	
	if (first.vertexIndices[0] == second.vertexIndices[1] &&
		first.vertexIndices[1] == second.vertexIndices[0])
		return YES;
	
	return NO;
}

BOOL IsIndexInTriangle(Triangle triangle, uint index)
{
	for (uint i = 0; i < 3; i++)
	{
		if (triangle.vertexIndices[i] == index)
			return YES;
	}
	return NO;
}

BOOL IsEdgeInTriangle(Triangle triangle, Edge edge)
{
	if (IsIndexInTriangle(triangle, edge.vertexIndices[0]) &&
		IsIndexInTriangle(triangle, edge.vertexIndices[1]))
	{
		return YES;
	}
	return NO;
}

uint NonEdgeIndexInTriangle(Triangle triangle, Edge edge)
{
	for (uint i = 0; i < 3; i++)
	{
		if (triangle.vertexIndices[i] != edge.vertexIndices[0] &&
			triangle.vertexIndices[i] != edge.vertexIndices[1])
		{
			return triangle.vertexIndices[i];
		}
	}
	return 0;
}

Vector3D NormalFromTriangleVertices(Vector3D triangleVertices[3])
{
	// now is same as RedBook (OpenGL Programming Guide)
	Vector3D u = triangleVertices[0] - triangleVertices[1];
	Vector3D v = triangleVertices[1] - triangleVertices[2];
	return u.Cross(v);
}

Triangle MakeTriangle(uint first, uint second, uint third)
{
	Triangle triangle;
	triangle.vertexIndices[0] = first;
	triangle.vertexIndices[1] = second;
	triangle.vertexIndices[2] = third;
	return triangle;
}

Triangle FlipTriangle(Triangle triangle)
{
	Triangle opposite;
	opposite.vertexIndices[0] = triangle.vertexIndices[2];
	opposite.vertexIndices[1] = triangle.vertexIndices[1];
	opposite.vertexIndices[2] = triangle.vertexIndices[0];
	return opposite;
}

@implementation Mesh

@synthesize selectionMode;

- (uint)vertexCount
{
	return vertices->size();
}

- (uint)triangleCount
{
	return triangles->size();
}

- (uint)edgeCount
{
	return edges->size();
}

- (id)init
{
	self = [super init];
	if (self)
	{
		vertices = new vector<Vector3D>();
		triangles = new vector<Triangle>();
		edges = new vector<Edge>();
		selected = new vector<BOOL>();
		markedVertices = new vector<BOOL>();
		selectionMode = MeshSelectionModeVertices;
		float hue = (random() % 10) / 10.0f;
		color = [NSColor colorWithCalibratedHue:hue 
									 saturation:1.0f
									 brightness:0.7f 
										  alpha:1.0f];
		[color retain];
	}
	return self;
}

- (void)dealloc
{
	delete vertices;
	delete triangles;
	delete edges;
	delete selected;
	delete markedVertices;
	[color release];
	[super dealloc];
}

- (void)setSelectionMode:(enum MeshSelectionMode)value
{
	selectionMode = value;
	selected->clear();
	switch (selectionMode) 
	{
		case MeshSelectionModeVertices:
		{
			for (uint i = 0; i < vertices->size(); i++)
			{
				selected->push_back(NO);
			}
		} break;
		case MeshSelectionModeTriangles:
		{
			for (uint i = 0; i < triangles->size(); i++)
			{
				selected->push_back(NO);
			}
		} break;
		case MeshSelectionModeEdges:
		{
			[self makeEdges];
			for (uint i = 0; i < edges->size(); i++)
			{
				selected->push_back(NO);
			}
		} break;
	}
}

- (Vector3D)vertexAtIndex:(uint)anIndex
{
	return vertices->at(anIndex);
}

- (Triangle)triangleAtIndex:(uint)anIndex
{
	return triangles->at(anIndex);
}

- (Edge)edgeAtIndex:(uint)anIndex
{
	return edges->at(anIndex);
}

- (BOOL)isVertexMarkedAtIndex:(uint)anIndex
{
	return markedVertices->at(anIndex);
}

- (void)setVertexMarked:(BOOL)isMarked atIndex:(uint)anIndex
{
	markedVertices->at(anIndex) = isMarked;
}

- (void)addVertex:(Vector3D)aVertex
{
	vertices->push_back(aVertex);
	if (selectionMode == MeshSelectionModeVertices)
		selected->push_back(NO);
}

- (void)addTriangle:(Triangle)aTriangle
{
	triangles->push_back(aTriangle);
	if (selectionMode == MeshSelectionModeTriangles)
		selected->push_back(NO);
}

- (void)addTriangleWithIndex1:(uint)index1
					   index2:(uint)index2
					   index3:(uint)index3
{
	Triangle triangle;
	triangle.vertexIndices[0] = index1;
	triangle.vertexIndices[1] = index2;
	triangle.vertexIndices[2] = index3;
	[self addTriangle:triangle];
}

- (void)addQuadWithIndex1:(uint)index1
				   index2:(uint)index2
				   index3:(uint)index3 
				   index4:(uint)index4
{
	Triangle triangle1, triangle2;
	triangle1.vertexIndices[0] = index1;
	triangle1.vertexIndices[1] = index2;
	triangle1.vertexIndices[2] = index3;
	
	triangle2.vertexIndices[0] = index1;
	triangle2.vertexIndices[1] = index3;
	triangle2.vertexIndices[2] = index4;
	
	[self addTriangle:triangle1];
	[self addTriangle:triangle2];
}

- (void)addEdgeWithIndex1:(uint)index1
				   index2:(uint)index2
{
	Edge edge;
	edge.vertexIndices[0] = index1;
	edge.vertexIndices[1] = index2;
	edges->push_back(edge);
	
	if (selectionMode == MeshSelectionModeEdges)
		selected->push_back(NO);
}

- (void)drawFast
{
	// experimental fast drawing
	
	// Flat lighting is missing due to complexity of generating normals.
	// Normal must be different for each triangle and distributed on all
	// three vertices of this triangle.
	// Problem is that vertices is shared, but normals not.
	// Another possibility is using shared normals and doing smooth shading.
	// Each normal will be then averaged on each vertex.
	
	glEnableClientState(GL_VERTEX_ARRAY);
	
	uint *trianglePtr = (uint *)(&(*triangles)[0]);
	float *vertexPtr = (float *)(&(*vertices)[0]);
	
	glVertexPointer(3, GL_FLOAT, 0, vertexPtr);
	
	glDrawElements(GL_TRIANGLES, triangles->size() * 3, GL_UNSIGNED_INT, trianglePtr);
	
	glDisableClientState(GL_VERTEX_ARRAY);
}

- (void)drawFillWithScale:(Vector3D)scale
{	
	float frontDiffuse[] = { 0.4, 0.4, 0.4, 1 };
	CGFloat components[4];
	[color getComponents:components];
	float backDiffuse[4];
	float selectedDiffuse[4];
	
	for (uint i = 0; i < 4; i++)
	{
		backDiffuse[i] = components[i];
		if (i < 3)
			selectedDiffuse[i] = 1.0f - components[i];
		else
			selectedDiffuse[i] = components[i];
	}
	
	glMaterialfv(GL_BACK, GL_DIFFUSE, backDiffuse);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, frontDiffuse);
	
	Vector3D triangleVertices[3];
	
	float *lastDiffuse = frontDiffuse; 
	
	BOOL flip = scale.x < 0.0f || scale.y < 0.0f || scale.z < 0.0f;
	
	glBegin(GL_TRIANGLES);
	
	for (uint i = 0; i < triangles->size(); i++)
	{
		if (selectionMode == MeshSelectionModeTriangles) 
		{
			if (selected->at(i))
			{
				if (lastDiffuse == frontDiffuse)
				{
					glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, selectedDiffuse);
					lastDiffuse = selectedDiffuse;
				}
			}
			else if (lastDiffuse == selectedDiffuse)
			{
				glMaterialfv(GL_BACK, GL_DIFFUSE, backDiffuse);
				glMaterialfv(GL_FRONT, GL_DIFFUSE, frontDiffuse);
				lastDiffuse = frontDiffuse;
			}
		}
		Triangle currentTriangle = [self triangleAtIndex:i];
		if (flip)
			currentTriangle = FlipTriangle(currentTriangle);
		
		[self getTriangleVertices:triangleVertices fromTriangle:currentTriangle];
		for (uint j = 0; j < 3; j++)
		{
			for (uint k = 0; k < 3; k++)
			{
				triangleVertices[j][k] *= scale[k];
			}
		}
		Vector3D n = NormalFromTriangleVertices(triangleVertices);
		n.Normalize();
		for (uint j = 0; j < 3; j++)
		{
			glNormal3f(n.x, n.y, n.z);
			glVertex3f(triangleVertices[j].x, triangleVertices[j].y, triangleVertices[j].z);			
		}
	}
	
	glEnd();
}

- (void)drawWireWithScale:(Vector3D)scale selected:(BOOL)isSelected
{
	glDisable(GL_LIGHTING);
	if (isSelected)
		glColor3f(1, 1, 1);
	else
		glColor3f([color redComponent], [color greenComponent], [color blueComponent]); 
	if (selectionMode != MeshSelectionModeEdges)
	{
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		[self drawFillWithScale:scale];
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	}
	glEnable(GL_LIGHTING);
}

- (void)drawWithScale:(Vector3D)scale selected:(BOOL)isSelected
{	
	if (isSelected)
	{
		glEnable(GL_POLYGON_OFFSET_FILL);
		glPolygonOffset(1.0f, 1.0f);
		[self drawFillWithScale:scale];
		glDisable(GL_POLYGON_OFFSET_FILL);
		[self drawWireWithScale:scale selected:isSelected];
	}
	else
	{
		[self drawFillWithScale:scale];
	}
}

- (void)makeCube
{
	NSLog(@"makeCube");
	
	vertices->clear();
	triangles->clear();
	selected->clear();
	
	// back vertices
	vertices->push_back(Vector3D(-1, -1, -1)); // 0
	vertices->push_back(Vector3D( 1, -1, -1)); // 1
	vertices->push_back(Vector3D( 1,  1, -1)); // 2
	vertices->push_back(Vector3D(-1,  1, -1)); // 3
	
	// front vertices
	vertices->push_back(Vector3D(-1, -1,  1)); // 4
	vertices->push_back(Vector3D( 1, -1,  1)); // 5
	vertices->push_back(Vector3D( 1,  1,  1)); // 6
	vertices->push_back(Vector3D(-1,  1,  1)); // 7
	
	// back triangles
	[self addQuadWithIndex1:0 index2:1 index3:2 index4:3];
	
	// front triangles
	[self addQuadWithIndex1:7 index2:6 index3:5 index4:4];
	
	// bottom triangles
	[self addQuadWithIndex1:1 index2:0 index3:4 index4:5];
	
	// top triangles
	[self addQuadWithIndex1:3 index2:2 index3:6 index4:7];
	
	// left triangles
	[self addQuadWithIndex1:7 index2:4 index3:0 index4:3];
	
	// right triangles
	[self addQuadWithIndex1:2 index2:1 index3:5 index4:6];
	
	[self setSelectionMode:[self selectionMode]];
}

- (void)makeCylinderWithSteps:(uint)steps
{
	NSLog(@"makeCylinderWithSteps:%i", steps);
	
	vertices->clear();
	triangles->clear();
	selected->clear();
	
	vertices->push_back(Vector3D(0, -1, 0)); // 0
 	vertices->push_back(Vector3D(0,  1, 0)); // 1
	
	vertices->push_back(Vector3D(cosf(0.0f), -1, sinf(0.0f))); // 2
	vertices->push_back(Vector3D(cosf(0.0f),  1, sinf(0.0f))); // 3
		
	uint max = steps;
	float step = (FLOAT_PI * 2.0f) / max;
	float angle = step;
	for (uint i = 1; i < max; i++)
	{
		vertices->push_back(Vector3D(cosf(angle), -1, sinf(angle))); // 4
		vertices->push_back(Vector3D(cosf(angle),  1, sinf(angle))); // 5
		
		Triangle triangle1, triangle2;
		triangle1.vertexIndices[0] = vertices->size() - 3;
		triangle1.vertexIndices[1] = vertices->size() - 2;
		triangle1.vertexIndices[2] = vertices->size() - 1;
	
		triangle2.vertexIndices[0] = vertices->size() - 2;
		triangle2.vertexIndices[1] = vertices->size() - 3;
		triangle2.vertexIndices[2] = vertices->size() - 4;
		
		triangles->push_back(triangle1);
		triangles->push_back(triangle2);
		
		Triangle triangle3, triangle4;
		triangle3.vertexIndices[0] = vertices->size() - 4;
		triangle3.vertexIndices[1] = 0;
		triangle3.vertexIndices[2] = vertices->size() - 2;
		
		triangle4.vertexIndices[0] = vertices->size() - 3;
		triangle4.vertexIndices[1] = vertices->size() - 1;
		triangle4.vertexIndices[2] = 1;
		
		triangles->push_back(triangle3);
		triangles->push_back(triangle4);
		
		angle += step;
	}
	
	Triangle triangle1, triangle2;
	triangle1.vertexIndices[0] = 2;
	triangle1.vertexIndices[1] = 3;
	triangle1.vertexIndices[2] = vertices->size() - 1;
	
	triangle2.vertexIndices[0] = vertices->size() - 1;
	triangle2.vertexIndices[1] = vertices->size() - 2;
	triangle2.vertexIndices[2] = 2;
	
	triangles->push_back(triangle1);
	triangles->push_back(triangle2);
	
	Triangle triangle3, triangle4;
	triangle3.vertexIndices[0] = 0;
	triangle3.vertexIndices[1] = 2;
	triangle3.vertexIndices[2] = vertices->size() - 2;
	
	triangle4.vertexIndices[0] = 3;
	triangle4.vertexIndices[1] = 1;
	triangle4.vertexIndices[2] = vertices->size() - 1;
	
	triangles->push_back(triangle3);
	triangles->push_back(triangle4);
	
	[self setSelectionMode:[self selectionMode]];
}

- (void)makeSphereWithSteps:(uint)steps
{
	NSLog(@"makeSphereWithSteps:%i", steps);
	
	vertices->clear();
	triangles->clear();
	selected->clear();
		
	uint max = steps;
	
	vertices->push_back(Vector3D(0, 1, 0));
	vertices->push_back(Vector3D(0, -1, 0));
	
	float step = FLOAT_PI / max;
	
	for (uint i = 0; i < 2 * max; i++)
	{
		float beta = i * step;
		
		for (uint j = 1; j < max; j++)
		{
			float alpha = 0.5f * FLOAT_PI + j * step;
			float y0 = sinf(alpha);
			float w0 = cosf(alpha);                
	
			float x0 = sinf(beta) * w0;
			float z0 = cosf(beta) * w0;
						
			vertices->push_back(Vector3D(x0, y0, z0));
						
			if (i > 0 && j < max - 1)
			{
				int index = (i - 1) * (max - 1);
				[self addQuadWithIndex1:1 + max + j + index
								 index2:2 + j + index
								 index3:1 + j + index
								 index4:max + j + index];
			}
		}
		
		int index = i * (max - 1);
		if (i < 2 * max - 1)
		{
			[self addTriangleWithIndex1:0
								 index2:2 + index + max - 1
								 index3:2 + index];
			
			[self addTriangleWithIndex1:1
								 index2:index + max
								 index3:index + 2 * max - 1];
		}
		else 
		{
			[self addTriangleWithIndex1:0
								 index2:2
								 index3:2 + index];
			
			[self addTriangleWithIndex1:1
								 index2:index + max
								 index3:max];
		}
	}
	
	for (uint j = 1; j < max - 1; j++)
	{
		int index = (2 * max - 1) * (max - 1);
		[self addQuadWithIndex1:1 + j + index
						 index2:1 + j
						 index3:2 + j
						 index4:2 + j + index];
	}
		
	[self setSelectionMode:[self selectionMode]];
}

- (void)makeEdges
{
	edges->clear();
	for (uint i = 0; i < triangles->size(); i++)
	{
		Triangle triangle = triangles->at(i);
		Edge triangleEdges[3];<commit_msg>Sphere is now created with same count of steps in x and z.<commit_after>//
//  Mesh.m
//  OpenGLEditor
//
//  Created by <NAME> on 7/29/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//

#import "Mesh.h"

BOOL IsTriangleDegenerated(Triangle triangle)
{
	if (triangle.vertexIndices[0] == triangle.vertexIndices[1])
		return YES;
	if (triangle.vertexIndices[0] == triangle.vertexIndices[2])
		return YES;
	if (triangle.vertexIndices[1] == triangle.vertexIndices[2])
		return YES;

	return NO;
}

BOOL AreEdgesSame(Edge first, Edge second)
{
	if (first.vertexIndices[0] == second.vertexIndices[0] &&
		first.vertexIndices[1] == second.vertexIndices[1])
		return YES;
	
	if (first.vertexIndices[0] == second.vertexIndices[1] &&
		first.vertexIndices[1] == second.vertexIndices[0])
		return YES;
	
	return NO;
}

BOOL IsIndexInTriangle(Triangle triangle, uint index)
{
	for (uint i = 0; i < 3; i++)
	{
		if (triangle.vertexIndices[i] == index)
			return YES;
	}
	return NO;
}

BOOL IsEdgeInTriangle(Triangle triangle, Edge edge)
{
	if (IsIndexInTriangle(triangle, edge.vertexIndices[0]) &&
		IsIndexInTriangle(triangle, edge.vertexIndices[1]))
	{
		return YES;
	}
	return NO;
}

uint NonEdgeIndexInTriangle(Triangle triangle, Edge edge)
{
	for (uint i = 0; i < 3; i++)
	{
		if (triangle.vertexIndices[i] != edge.vertexIndices[0] &&
			triangle.vertexIndices[i] != edge.vertexIndices[1])
		{
			return triangle.vertexIndices[i];
		}
	}
	return 0;
}

Vector3D NormalFromTriangleVertices(Vector3D triangleVertices[3])
{
	// now is same as RedBook (OpenGL Programming Guide)
	Vector3D u = triangleVertices[0] - triangleVertices[1];
	Vector3D v = triangleVertices[1] - triangleVertices[2];
	return u.Cross(v);
}

Triangle MakeTriangle(uint first, uint second, uint third)
{
	Triangle triangle;
	triangle.vertexIndices[0] = first;
	triangle.vertexIndices[1] = second;
	triangle.vertexIndices[2] = third;
	return triangle;
}

Triangle FlipTriangle(Triangle triangle)
{
	Triangle opposite;
	opposite.vertexIndices[0] = triangle.vertexIndices[2];
	opposite.vertexIndices[1] = triangle.vertexIndices[1];
	opposite.vertexIndices[2] = triangle.vertexIndices[0];
	return opposite;
}

@implementation Mesh

@synthesize selectionMode;

- (uint)vertexCount
{
	return vertices->size();
}

- (uint)triangleCount
{
	return triangles->size();
}

- (uint)edgeCount
{
	return edges->size();
}

- (id)init
{
	self = [super init];
	if (self)
	{
		vertices = new vector<Vector3D>();
		triangles = new vector<Triangle>();
		edges = new vector<Edge>();
		selected = new vector<BOOL>();
		markedVertices = new vector<BOOL>();
		selectionMode = MeshSelectionModeVertices;
		float hue = (random() % 10) / 10.0f;
		color = [NSColor colorWithCalibratedHue:hue 
									 saturation:1.0f
									 brightness:0.7f 
										  alpha:1.0f];
		[color retain];
	}
	return self;
}

- (void)dealloc
{
	delete vertices;
	delete triangles;
	delete edges;
	delete selected;
	delete markedVertices;
	[color release];
	[super dealloc];
}

- (void)setSelectionMode:(enum MeshSelectionMode)value
{
	selectionMode = value;
	selected->clear();
	switch (selectionMode) 
	{
		case MeshSelectionModeVertices:
		{
			for (uint i = 0; i < vertices->size(); i++)
			{
				selected->push_back(NO);
			}
		} break;
		case MeshSelectionModeTriangles:
		{
			for (uint i = 0; i < triangles->size(); i++)
			{
				selected->push_back(NO);
			}
		} break;
		case MeshSelectionModeEdges:
		{
			[self makeEdges];
			for (uint i = 0; i < edges->size(); i++)
			{
				selected->push_back(NO);
			}
		} break;
	}
}

- (Vector3D)vertexAtIndex:(uint)anIndex
{
	return vertices->at(anIndex);
}

- (Triangle)triangleAtIndex:(uint)anIndex
{
	return triangles->at(anIndex);
}

- (Edge)edgeAtIndex:(uint)anIndex
{
	return edges->at(anIndex);
}

- (BOOL)isVertexMarkedAtIndex:(uint)anIndex
{
	return markedVertices->at(anIndex);
}

- (void)setVertexMarked:(BOOL)isMarked atIndex:(uint)anIndex
{
	markedVertices->at(anIndex) = isMarked;
}

- (void)addVertex:(Vector3D)aVertex
{
	vertices->push_back(aVertex);
	if (selectionMode == MeshSelectionModeVertices)
		selected->push_back(NO);
}

- (void)addTriangle:(Triangle)aTriangle
{
	triangles->push_back(aTriangle);
	if (selectionMode == MeshSelectionModeTriangles)
		selected->push_back(NO);
}

- (void)addTriangleWithIndex1:(uint)index1
					   index2:(uint)index2
					   index3:(uint)index3
{
	Triangle triangle;
	triangle.vertexIndices[0] = index1;
	triangle.vertexIndices[1] = index2;
	triangle.vertexIndices[2] = index3;
	[self addTriangle:triangle];
}

- (void)addQuadWithIndex1:(uint)index1
				   index2:(uint)index2
				   index3:(uint)index3 
				   index4:(uint)index4
{
	Triangle triangle1, triangle2;
	triangle1.vertexIndices[0] = index1;
	triangle1.vertexIndices[1] = index2;
	triangle1.vertexIndices[2] = index3;
	
	triangle2.vertexIndices[0] = index1;
	triangle2.vertexIndices[1] = index3;
	triangle2.vertexIndices[2] = index4;
	
	[self addTriangle:triangle1];
	[self addTriangle:triangle2];
}

- (void)addEdgeWithIndex1:(uint)index1
				   index2:(uint)index2
{
	Edge edge;
	edge.vertexIndices[0] = index1;
	edge.vertexIndices[1] = index2;
	edges->push_back(edge);
	
	if (selectionMode == MeshSelectionModeEdges)
		selected->push_back(NO);
}

- (void)drawFast
{
	// experimental fast drawing
	
	// Flat lighting is missing due to complexity of generating normals.
	// Normal must be different for each triangle and distributed on all
	// three vertices of this triangle.
	// Problem is that vertices is shared, but normals not.
	// Another possibility is using shared normals and doing smooth shading.
	// Each normal will be then averaged on each vertex.
	
	glEnableClientState(GL_VERTEX_ARRAY);
	
	uint *trianglePtr = (uint *)(&(*triangles)[0]);
	float *vertexPtr = (float *)(&(*vertices)[0]);
	
	glVertexPointer(3, GL_FLOAT, 0, vertexPtr);
	
	glDrawElements(GL_TRIANGLES, triangles->size() * 3, GL_UNSIGNED_INT, trianglePtr);
	
	glDisableClientState(GL_VERTEX_ARRAY);
}

- (void)drawFillWithScale:(Vector3D)scale
{	
	float frontDiffuse[] = { 0.4, 0.4, 0.4, 1 };
	CGFloat components[4];
	[color getComponents:components];
	float backDiffuse[4];
	float selectedDiffuse[4];
	
	for (uint i = 0; i < 4; i++)
	{
		backDiffuse[i] = components[i];
		if (i < 3)
			selectedDiffuse[i] = 1.0f - components[i];
		else
			selectedDiffuse[i] = components[i];
	}
	
	glMaterialfv(GL_BACK, GL_DIFFUSE, backDiffuse);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, frontDiffuse);
	
	Vector3D triangleVertices[3];
	
	float *lastDiffuse = frontDiffuse; 
	
	BOOL flip = scale.x < 0.0f || scale.y < 0.0f || scale.z < 0.0f;
	
	glBegin(GL_TRIANGLES);
	
	for (uint i = 0; i < triangles->size(); i++)
	{
		if (selectionMode == MeshSelectionModeTriangles) 
		{
			if (selected->at(i))
			{
				if (lastDiffuse == frontDiffuse)
				{
					glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, selectedDiffuse);
					lastDiffuse = selectedDiffuse;
				}
			}
			else if (lastDiffuse == selectedDiffuse)
			{
				glMaterialfv(GL_BACK, GL_DIFFUSE, backDiffuse);
				glMaterialfv(GL_FRONT, GL_DIFFUSE, frontDiffuse);
				lastDiffuse = frontDiffuse;
			}
		}
		Triangle currentTriangle = [self triangleAtIndex:i];
		if (flip)
			currentTriangle = FlipTriangle(currentTriangle);
		
		[self getTriangleVertices:triangleVertices fromTriangle:currentTriangle];
		for (uint j = 0; j < 3; j++)
		{
			for (uint k = 0; k < 3; k++)
			{
				triangleVertices[j][k] *= scale[k];
			}
		}
		Vector3D n = NormalFromTriangleVertices(triangleVertices);
		n.Normalize();
		for (uint j = 0; j < 3; j++)
		{
			glNormal3f(n.x, n.y, n.z);
			glVertex3f(triangleVertices[j].x, triangleVertices[j].y, triangleVertices[j].z);			
		}
	}
	
	glEnd();
}

- (void)drawWireWithScale:(Vector3D)scale selected:(BOOL)isSelected
{
	glDisable(GL_LIGHTING);
	if (isSelected)
		glColor3f(1, 1, 1);
	else
		glColor3f([color redComponent], [color greenComponent], [color blueComponent]); 
	if (selectionMode != MeshSelectionModeEdges)
	{
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		[self drawFillWithScale:scale];
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	}
	glEnable(GL_LIGHTING);
}

- (void)drawWithScale:(Vector3D)scale selected:(BOOL)isSelected
{	
	if (isSelected)
	{
		glEnable(GL_POLYGON_OFFSET_FILL);
		glPolygonOffset(1.0f, 1.0f);
		[self drawFillWithScale:scale];
		glDisable(GL_POLYGON_OFFSET_FILL);
		[self drawWireWithScale:scale selected:isSelected];
	}
	else
	{
		[self drawFillWithScale:scale];
	}
}

- (void)makeCube
{
	NSLog(@"makeCube");
	
	vertices->clear();
	triangles->clear();
	selected->clear();
	
	// back vertices
	vertices->push_back(Vector3D(-1, -1, -1)); // 0
	vertices->push_back(Vector3D( 1, -1, -1)); // 1
	vertices->push_back(Vector3D( 1,  1, -1)); // 2
	vertices->push_back(Vector3D(-1,  1, -1)); // 3
	
	// front vertices
	vertices->push_back(Vector3D(-1, -1,  1)); // 4
	vertices->push_back(Vector3D( 1, -1,  1)); // 5
	vertices->push_back(Vector3D( 1,  1,  1)); // 6
	vertices->push_back(Vector3D(-1,  1,  1)); // 7
	
	// back triangles
	[self addQuadWithIndex1:0 index2:1 index3:2 index4:3];
	
	// front triangles
	[self addQuadWithIndex1:7 index2:6 index3:5 index4:4];
	
	// bottom triangles
	[self addQuadWithIndex1:1 index2:0 index3:4 index4:5];
	
	// top triangles
	[self addQuadWithIndex1:3 index2:2 index3:6 index4:7];
	
	// left triangles
	[self addQuadWithIndex1:7 index2:4 index3:0 index4:3];
	
	// right triangles
	[self addQuadWithIndex1:2 index2:1 index3:5 index4:6];
	
	[self setSelectionMode:[self selectionMode]];
}

- (void)makeCylinderWithSteps:(uint)steps
{
	NSLog(@"makeCylinderWithSteps:%i", steps);
	
	vertices->clear();
	triangles->clear();
	selected->clear();
	
	vertices->push_back(Vector3D(0, -1, 0)); // 0
 	vertices->push_back(Vector3D(0,  1, 0)); // 1
	
	vertices->push_back(Vector3D(cosf(0.0f), -1, sinf(0.0f))); // 2
	vertices->push_back(Vector3D(cosf(0.0f),  1, sinf(0.0f))); // 3
		
	uint max = steps;
	float step = (FLOAT_PI * 2.0f) / max;
	float angle = step;
	for (uint i = 1; i < max; i++)
	{
		vertices->push_back(Vector3D(cosf(angle), -1, sinf(angle))); // 4
		vertices->push_back(Vector3D(cosf(angle),  1, sinf(angle))); // 5
		
		Triangle triangle1, triangle2;
		triangle1.vertexIndices[0] = vertices->size() - 3;
		triangle1.vertexIndices[1] = vertices->size() - 2;
		triangle1.vertexIndices[2] = vertices->size() - 1;
	
		triangle2.vertexIndices[0] = vertices->size() - 2;
		triangle2.vertexIndices[1] = vertices->size() - 3;
		triangle2.vertexIndices[2] = vertices->size() - 4;
		
		triangles->push_back(triangle1);
		triangles->push_back(triangle2);
		
		Triangle triangle3, triangle4;
		triangle3.vertexIndices[0] = vertices->size() - 4;
		triangle3.vertexIndices[1] = 0;
		triangle3.vertexIndices[2] = vertices->size() - 2;
		
		triangle4.vertexIndices[0] = vertices->size() - 3;
		triangle4.vertexIndices[1] = vertices->size() - 1;
		triangle4.vertexIndices[2] = 1;
		
		triangles->push_back(triangle3);
		triangles->push_back(triangle4);
		
		angle += step;
	}
	
	Triangle triangle1, triangle2;
	triangle1.vertexIndices[0] = 2;
	triangle1.vertexIndices[1] = 3;
	triangle1.vertexIndices[2] = vertices->size() - 1;
	
	triangle2.vertexIndices[0] = vertices->size() - 1;
	triangle2.vertexIndices[1] = vertices->size() - 2;
	triangle2.vertexIndices[2] = 2;
	
	triangles->push_back(triangle1);
	triangles->push_back(triangle2);
	
	Triangle triangle3, triangle4;
	triangle3.vertexIndices[0] = 0;
	triangle3.vertexIndices[1] = 2;
	triangle3.vertexIndices[2] = vertices->size() - 2;
	
	triangle4.vertexIndices[0] = 3;
	triangle4.vertexIndices[1] = 1;
	triangle4.vertexIndices[2] = vertices->size() - 1;
	
	triangles->push_back(triangle3);
	triangles->push_back(triangle4);
	
	[self setSelectionMode:[self selectionMode]];
}

- (void)makeSphereWithSteps:(uint)steps
{
	NSLog(@"makeSphereWithSteps:%i", steps);
	
	vertices->clear();
	triangles->clear();
	selected->clear();
		
	uint max = steps;
	
	vertices->push_back(Vector3D(0, 1, 0));
	vertices->push_back(Vector3D(0, -1, 0));
	
	float step = FLOAT_PI / max;
	
	for (uint i = 0; i < max; i++)
	{
		float beta = i * step * 2.0f;
		
		for (uint j = 1; j < max; j++)
		{
			float alpha = 0.5f * FLOAT_PI + j * step;
			float y0 = sinf(alpha);
			float w0 = cosf(alpha);                
	
			float x0 = sinf(beta) * w0;
			float z0 = cosf(beta) * w0;
						
			vertices->push_back(Vector3D(x0, y0, z0));
						
			if (i > 0 && j < max - 1)
			{
				int index = (i - 1) * (max - 1);
				[self addQuadWithIndex1:1 + max + j + index
								 index2:2 + j + index
								 index3:1 + j + index
								 index4:max + j + index];
			}
		}
		
		int index = i * (max - 1);
		if (i < max - 1)
		{
			[self addTriangleWithIndex1:0
								 index2:2 + index + max - 1
								 index3:2 + index];
			
			[self addTriangleWithIndex1:1
								 index2:index + max
								 index3:index + 2 * max - 1];
		}
		else 
		{
			[self addTriangleWithIndex1:0
								 index2:2
								 index3:2 + index];
			
			[self addTriangleWithIndex1:1
								 index2:index + max
								 index3:max];
		}
	}
	
	for (uint j = 1; j < max - 1; j++)
	{
		int index = (max - 1) * (max - 1);
		[self addQuadWithIndex1:1 + j + index
						 index2:1 + j
						 index3:2 + j
						 index4:2 + j + index];
	}
		
	[self setSelectionMode:[self selectionMode]];
}

- (void)makeEdges
{
	edges->clear();
	for (uint i = 0; i < triangles->size(); i++)
	{
		Triangle triangle = triangles->at(i);
		Edge triangleEdges[3];
{'fraction_non_alphanumeric': 0.1496268656716418, 'fraction_numerical': 0.033619402985074626, 'mean_word_length': 3.9131072410632446, 'pattern_counts': {'":': 0, '<': 57, '<?xml version=': 0, '>': 169, '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': '2660993', 'n_tokens_mistral': 11460, 'n_tokens_neox': 10028, 'n_words': 1985}