<commit_before>// See unity.cpp for build instructions

#include <cstdio>
#include <cstdlib>
#include <cstring>

#include "env.hpp"
#include "gl.hpp"

namespace qm {
struct Shader {
	GLuint vs;
	GLuint fs;
	GLuint prog;
	GLuint phaseLoc;
	GLuint colorLoc;
};

const GLchar* g_vs= "\
#version 120\n\
varying vec3 v_pos; \
varying vec3 v_normal; \
void main() \
{ \
    gl_Position= vec4(gl_Vertex.xy, 0.0, 1.0); \
	v_pos= (gl_ModelViewMatrix*gl_Vertex).xyz; \
	v_normal= mat3(gl_ModelViewMatrix)*vec3(gl_Vertex.xy, -1.0); \
} \
\n";

const GLchar* g_fs= "\
#version 120\n\
uniform float u_phase; \
uniform vec3 u_color; \
varying vec3 v_pos; \
varying vec3 v_normal; \
/* x emission, y absorption */ \
vec2 sample(vec3 p) \
{ \
	p.z += 0.2; \
	float beam_e= \
		abs(cos(p.z*10.0 - sign(p.z)*u_phase)*0.5 + 1.0)* \
			0.001/(p.x*p.x + p.y*p.y + 0.001); \
	float disc_e= \
		0.01/((p.z*p.z + 0.01)*(p.x*p.x + p.y*p.y)*100.0 + 0.1); \
	float hole_a= pow(min(1.0, 0.1/(dot(p, p))), 10.0); \
	float a= clamp(hole_a, 0.0, 1.0); \
    return vec2((disc_e + beam_e)*(1 - a), a + disc_e*7.0)*25.0; \
} \
void main() \
{ \
	vec3 n= normalize(v_normal); \
	vec3 color= u_color; \
	float intensity= 0.0; \
	const int steps= 45; \
	const float dl= 2.0/steps; \
	for (int i= 0; i < steps; ++i) { \
		vec2 s= sample(v_pos + n*2.0*float(steps - i - 1)/steps); \
		intensity= max(0, intensity + (s.x - s.y*intensity)*dl); \
	} \
	gl_FragColor= vec4(color*intensity, 1.0); \
} \
\n";

void init(Env& env, Shader& shd)
{
	env= envInit();
	queryGlFuncs();

	{ // Create shaders
		{ // Vertex
			shd.vs= glCreateShader(GL_VERTEX_SHADER);
			glShaderSource(shd.vs, 1, &g_vs, NULL);
			glCompileShader(shd.vs);
			checkShaderStatus(shd.vs);
		}
		{ // Fragment
			shd.fs= glCreateShader(GL_FRAGMENT_SHADER);
			glShaderSource(shd.fs, 1, &g_fs, NULL);
			glCompileShader(shd.fs);
			checkShaderStatus(shd.fs);
		}
		{ // Shader program
			shd.prog= glCreateProgram();
			glAttachShader(shd.prog, shd.vs);
			glAttachShader(shd.prog, shd.fs);
			glLinkProgram(shd.prog);
			checkProgramStatus(shd.prog);

			shd.phaseLoc= glGetUniformLocation(shd.prog, "u_phase");
			shd.colorLoc= glGetUniformLocation(shd.prog, "u_color");
		}
	}

	{ // Setup initial GL state
		glClearColor(0.0, 0.0, 0.0, 0.0);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	}
}

void quit(Env& env, const Shader& shd)
{
	glDetachShader(shd.prog, shd.vs);
	glDeleteShader(shd.vs);

	glDetachShader(shd.prog, shd.fs);
	glDeleteShader(shd.fs);

	glDeleteProgram(shd.prog);

	envQuit(env);
}

void frame(const Env& env, const Shader& shd)
{
	static float setting_r= 0.3;
	static float setting_g= 0.8;
	static float setting_b= 1.0;

	struct Slider {
		const char* title;
		float min;
		float max;
		float& value;
		int decimals;
		bool hover;

		static float height() { return 0.05; }
		static float width() { return 0.4; }
		static float top(std::size_t i) { return 1.0 - i*height(); }
		static float bottom(std::size_t i) { return top(i + 1); }
		bool pointInside(std::size_t i, Vec2f p) const
		{
			return	p.x  >= -1.0	&& p.x < width() - 1.0 &&
					p.y > bottom(i)	&& p.y < top(i);
		}
		float fraction() const
		{ return (value - min)/(max - min); }
		float coordToValue(float x) const
		{
			return clamp((1.0 + x)/width()*(max - min) + min, min, max);
		}
	};

	static Slider sliders[]= {
		{ "r",		0.0,	2.0, setting_r,	2,	false },
		{ "g",		0.0,	2.0, setting_g,	2,	false },
		{ "b",		0.0,	2.0, setting_b,	2,	false },
	};
	const std::size_t slider_count= sizeof(sliders)/sizeof(*sliders);

	static Vec2f prev_delta;
	static Vec2f rot;

	{ // User interaction
		bool slider_activity= false;
		for (std::size_t i= 0; i < slider_count; ++i) {
			Slider& s= sliders[i];
			if (s.pointInside(i, env.anchorPos)) {
				if (env.lmbDown)
					s.value= s.coordToValue(env.cursorPos.x);
				s.hover= true;
				slider_activity= true;
			} else {
				s.hover= false;
			}
		}

		if (env.lmbDown && !slider_activity) {
			Vec2f smooth_delta= prev_delta*0.5 + (env.cursorDelta)*0.5;
			prev_delta= smooth_delta;

			rot += smooth_delta*2;
			rot.y= clamp(rot.y, -tau/4, tau/4);
		}
	}

	glClear(GL_COLOR_BUFFER_BIT);
	glViewport(0, 0, env.winSize.x, env.winSize.y);

	{ // Draw volume
		static float phase;

		/// @todo *dt
		phase += 0.3;

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();

		glMatrixMode(GL_MODELVIEW);
		// Trackball-style rotation
		glRotatef(rot.y*radToDeg, cos(rot.x), 0.0, sin(rot.x));
		glRotatef(rot.x*radToDeg, 0.0, -1.0, 0.0);

		glUseProgram(shd.prog);
		glUniform1f(shd.phaseLoc, phase);
		glUniform3f(shd.colorLoc, setting_r, setting_g, setting_b);

		glBegin(GL_QUADS);
			glVertex3f(-1, -1, 1);
			glVertex3f(1, -1, 1);
			glVertex3f(1,  1, 1);
			glVertex3f(-1, 1, 1);
		glEnd();
	}

	{ // Draw gui
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		glUseProgram(0);

		glBegin(GL_QUADS);
			// Background
			glColor4f(0.0, 0.0, 0.0, 0.3);
			glVertex2f(-1.0, 1.0);
			glVertex2f(-1.0, 1.0 - slider_count*Slider::height());
			glVertex2f(Slider::width() - 1.0, 1.0 - slider_count*Slider::height());
			glVertex2f(Slider::width() - 1.0, 1.0);

			for (std::size_t i= 0; i < slider_count; ++i) {
				Slider& s= sliders[i];
				float top= s.top(i);
				float bottom= s.bottom(i);
				float width= s.width()*s.fraction();

				if (!s.hover)
					glColor4f(0.3, 0.3, 0.3, 0.6);
				else
					glColor4f(1.0, 1.0, 1.0, 0.8);

				glVertex2f(-1.0, top);
				glVertex2f(-1.0, bottom);
				glVertex2f(-1.0 + width, bottom);
				glVertex2f(-1.0 + width, top);
			}
		glEnd();
	}
}

} // qm

int main()
{
	qm::Env env;
	qm::Shader shd;
	qm::init(env, shd);

	while (!env.quitRequested) {
		qm::envUpdate(env);
		qm::frame(env, shd);
	}

	qm::quit(env, shd);
}

<commit_msg>Added noise<commit_after>// See unity.cpp for build instructions

#include <cstdio>
#include <cstdlib>
#include <cstring>

#include "env.hpp"
#include "gl.hpp"

namespace qm {
struct Shader {
	GLuint vs;
	GLuint fs;
	GLuint prog;
	GLuint phaseLoc;
	GLuint colorLoc;
};

const GLchar* g_vs= "\
#version 120\n\
varying vec3 v_pos; \
varying vec3 v_normal; \
void main() \
{ \
    gl_Position= vec4(gl_Vertex.xy, 0.0, 1.0); \
	v_pos= (gl_ModelViewMatrix*gl_Vertex).xyz; \
	v_normal= mat3(gl_ModelViewMatrix)*vec3(gl_Vertex.xy, -1.0); \
} \
\n";

const GLchar* g_fs= "\
#version 120\n\
uniform float u_phase; \
uniform vec3 u_color; \
varying vec3 v_pos; \
varying vec3 v_normal; \
float rand(vec2 co){ \
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); \
} \
/* x emission, y absorption */ \
vec2 sample(vec3 p) \
{ \
	p.z += 0.2; \
	float beam_e= \
		abs(cos(p.z*10.0 - sign(p.z)*u_phase)*0.5 + 1.0)* \
			0.001/(p.x*p.x + p.y*p.y + 0.001); \
	float disc_e= \
		0.01/((p.z*p.z + 0.01)*(p.x*p.x + p.y*p.y)*100.0 + 0.1); \
	float hole_a= pow(min(1.0, 0.1/(dot(p, p))), 10.0); \
	float a= clamp(hole_a, 0.0, 1.0); \
    return vec2((disc_e + beam_e)*(1 - a), a + disc_e*7.0)*25.0; \
} \
void main() \
{ \
	vec3 n= normalize(v_normal); \
	vec3 color= u_color; \
	float intensity= 0.0; \
	const int steps= 45; \
	const float dl= 2.0/steps; \
	for (int i= 0; i < steps; ++i) { \
		vec2 s= sample(v_pos + n*2.0*float(steps - i - 1)/steps); \
		intensity= max(0, intensity + (s.x - s.y*intensity)*dl); \
	} \
	intensity += rand(v_pos.xy + vec2(u_phase, 0))*0.02; \
	gl_FragColor= vec4(color*intensity, 1.0); \
} \
\n";

void init(Env& env, Shader& shd)
{
	env= envInit();
	queryGlFuncs();

	{ // Create shaders
		{ // Vertex
			shd.vs= glCreateShader(GL_VERTEX_SHADER);
			glShaderSource(shd.vs, 1, &g_vs, NULL);
			glCompileShader(shd.vs);
			checkShaderStatus(shd.vs);
		}
		{ // Fragment
			shd.fs= glCreateShader(GL_FRAGMENT_SHADER);
			glShaderSource(shd.fs, 1, &g_fs, NULL);
			glCompileShader(shd.fs);
			checkShaderStatus(shd.fs);
		}
		{ // Shader program
			shd.prog= glCreateProgram();
			glAttachShader(shd.prog, shd.vs);
			glAttachShader(shd.prog, shd.fs);
			glLinkProgram(shd.prog);
			checkProgramStatus(shd.prog);

			shd.phaseLoc= glGetUniformLocation(shd.prog, "u_phase");
			shd.colorLoc= glGetUniformLocation(shd.prog, "u_color");
		}
	}

	{ // Setup initial GL state
		glClearColor(0.0, 0.0, 0.0, 0.0);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	}
}

void quit(Env& env, const Shader& shd)
{
	glDetachShader(shd.prog, shd.vs);
	glDeleteShader(shd.vs);

	glDetachShader(shd.prog, shd.fs);
	glDeleteShader(shd.fs);

	glDeleteProgram(shd.prog);

	envQuit(env);
}

void frame(const Env& env, const Shader& shd)
{
	static float setting_r= 0.3;
	static float setting_g= 0.8;
	static float setting_b= 1.0;

	struct Slider {
		const char* title;
		float min;
		float max;
		float& value;
		int decimals;
		bool hover;

		static float height() { return 0.05; }
		static float width() { return 0.4; }
		static float top(std::size_t i) { return 1.0 - i*height(); }
		static float bottom(std::size_t i) { return top(i + 1); }
		bool pointInside(std::size_t i, Vec2f p) const
		{
			return	p.x  >= -1.0	&& p.x < width() - 1.0 &&
					p.y > bottom(i)	&& p.y < top(i);
		}
		float fraction() const
		{ return (value - min)/(max - min); }
		float coordToValue(float x) const
		{
			return clamp((1.0 + x)/width()*(max - min) + min, min, max);
		}
	};

	static Slider sliders[]= {
		{ "r",		0.0,	2.0, setting_r,	2,	false },
		{ "g",		0.0,	2.0, setting_g,	2,	false },
		{ "b",		0.0,	2.0, setting_b,	2,	false },
	};
	const std::size_t slider_count= sizeof(sliders)/sizeof(*sliders);

	static Vec2f prev_delta;
	static Vec2f rot;

	{ // User interaction
		bool slider_activity= false;
		for (std::size_t i= 0; i < slider_count; ++i) {
			Slider& s= sliders[i];
			if (s.pointInside(i, env.anchorPos)) {
				if (env.lmbDown)
					s.value= s.coordToValue(env.cursorPos.x);
				s.hover= true;
				slider_activity= true;
			} else {
				s.hover= false;
			}
		}

		if (env.lmbDown && !slider_activity) {
			Vec2f smooth_delta= prev_delta*0.5 + (env.cursorDelta)*0.5;
			prev_delta= smooth_delta;

			rot += smooth_delta*2;
			rot.y= clamp(rot.y, -tau/4, tau/4);
		}
	}

	glClear(GL_COLOR_BUFFER_BIT);
	glViewport(0, 0, env.winSize.x, env.winSize.y);

	{ // Draw volume
		static float phase;

		/// @todo *dt
		phase += 0.3;

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();

		glMatrixMode(GL_MODELVIEW);
		// Trackball-style rotation
		glRotatef(rot.y*radToDeg, cos(rot.x), 0.0, sin(rot.x));
		glRotatef(rot.x*radToDeg, 0.0, -1.0, 0.0);

		glUseProgram(shd.prog);
		glUniform1f(shd.phaseLoc, phase);
		glUniform3f(shd.colorLoc, setting_r, setting_g, setting_b);

		glBegin(GL_QUADS);
			glVertex3f(-1, -1, 1);
			glVertex3f(1, -1, 1);
			glVertex3f(1,  1, 1);
			glVertex3f(-1, 1, 1);
		glEnd();
	}

	{ // Draw gui
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		glUseProgram(0);

		glBegin(GL_QUADS);
			// Background
			glColor4f(0.0, 0.0, 0.0, 0.3);
			glVertex2f(-1.0, 1.0);
			glVertex2f(-1.0, 1.0 - slider_count*Slider::height());
			glVertex2f(Slider::width() - 1.0, 1.0 - slider_count*Slider::height());
			glVertex2f(Slider::width() - 1.0, 1.0);

			for (std::size_t i= 0; i < slider_count; ++i) {
				Slider& s= sliders[i];
				float top= s.top(i);
				float bottom= s.bottom(i);
				float width= s.width()*s.fraction();

				if (!s.hover)
					glColor4f(0.3, 0.3, 0.3, 0.6);
				else
					glColor4f(1.0, 1.0, 1.0, 0.8);

				glVertex2f(-1.0, top);
				glVertex2f(-1.0, bottom);
				glVertex2f(-1.0 + width, bottom);
				glVertex2f(-1.0 + width, top);
			}
		glEnd();
	}
}

} // qm

int main()
{
	qm::Env env;
	qm::Shader shd;
	qm::init(env, shd);

	while (!env.quitRequested) {
		qm::envUpdate(env);
		qm::frame(env, shd);
	}

	qm::quit(env, shd);
}

<|endoftext|>


<commit_before>#include <QGLWidget>
#include "Mesh.h"
#include "Matrix44.h"
#include "Common.h"

Mesh::Mesh() : triangleColouring(MESH_COLOUR_SAME), geomType(MESH_GEOM_TRIANGLES),
	surfaceTexture(NULL), useSurfaceNormals(false)
{
}

Mesh::~Mesh()
{
}

const VertexList& Mesh::vertices() const
{
	return verts;
}

void Mesh::setVertices(const VertexList& newVerts)
{
	verts = newVerts;
}

const TriangleList& Mesh::triangles() const
{
	return tris;
}

void Mesh::setTriangles(const TriangleList& newTris)
{
	tris = newTris;
}

Mesh::Colouring Mesh::colouring() const
{
	return triangleColouring;
}

void Mesh::setColouring(Mesh::Colouring newColouring)
{
	triangleColouring = newColouring;
}

Mesh::GeometryType Mesh::geometryType() const
{
	return geomType;
}

void Mesh::setGeometryType(Mesh::GeometryType newGeomType)
{
	geomType = newGeomType;
}

bool Mesh::showingNormals() const
{
	return drawNormals;
}

void Mesh::showNormals(bool willShow)
{
	drawNormals = willShow;
}

Texture* Mesh::texture() const
{
	return surfaceTexture;
}

void Mesh::setTexture(Texture* newTexture)
{
	surfaceTexture = newTexture;
}

bool Mesh::usingPerFaceNormals() const
{
	return useSurfaceNormals;
}

void Mesh::setPerFaceNormals(bool usePerFace)
{
	useSurfaceNormals = usePerFace;
}

void Mesh::renderVertex(const Vertex& v)
{
	glTexCoord2f(v.texCoord.s, v.texCoord.t);
	glNormal3f(v.normal.x, v.normal.y, v.normal.z);
	glVertex3f(v.position.x, v.position.y, v.position.y);
}

void Mesh::renderTriangle(const VertexList& verticesToUse, const Triangle& tri)
{
	renderVertex( verticesToUse[tri.v1] );
	renderVertex( verticesToUse[tri.v2] );
	renderVertex( verticesToUse[tri.v3] );
}

void Mesh::renderPoints(const VertexList& vertices)
{
	glColor3f(1.0f, 1.0f, 1.0f);
	glBegin(GL_POINTS);
		for (VertexList::const_iterator it = vertices.begin(); (it != vertices.end()); it++)
			glVertex3f(it->position.x, it->position.y, it->position.x);
	glEnd();
}

void Mesh::renderLines(const VertexList& vertices, const TriangleList& triangles)
{
	glColor3f(1.0f, 1.0f, 1.0f);
	glBegin(GL_LINES);
		for (TriangleList::const_iterator it = triangles.begin(); (it != triangles.end()); it++)
		{
			const Vertex& v1 = vertices[it->v1];
			const Vertex& v2 = vertices[it->v2];
			const Vertex& v3 = vertices[it->v3];
			glVertex3f(v1.position.x, v1.position.y, v1.position.x);
			glVertex3f(v2.position.x, v2.position.y, v2.position.x);
			glVertex3f(v2.position.x, v2.position.y, v2.position.x);
			glVertex3f(v3.position.x, v3.position.y, v3.position.x);
			glVertex3f(v3.position.x, v3.position.y, v3.position.x);
			glVertex3f(v1.position.x, v1.position.y, v1.position.x);
		}
	glEnd();
}

void Mesh::renderTriangles(const VertexList& vertices, const TriangleList& triangles)
{
	glBegin(GL_TRIANGLES);
	if (triangleColouring == MESH_ALTERNATING_TRIANGLES)
	{
		// Whole for-loop put it if-statement so there isn't a branch every iteration (costly operation
		for (unsigned int i = 0; (i < triangles.size()); i++)
		{
			const Vector3& col = ALTERNATING_TRIANGLE_COLOURS[i % NUM_ALTERNATING_COLOURS];
			glColor3f(col.x, col.y, col.y);
			renderTriangle(vertices, tris[i]);
		}
	}
	else
	{
		for (TriangleList::const_iterator it = triangles.begin(); (it != triangles.end()); it++)
			renderTriangle(vertices, *it);
	}
	glEnd();
}

void Mesh::renderNormals(const VertexList& vertices)
{
	glColor3f(1.0f, 1.0f, 1.0f);
	glBegin(GL_LINES);
		for (VertexList::const_iterator it = vertices.begin(); (it != vertices.end()); it++)
		{
			const Vector3& position = it->position;
			const Vector3& normal = it->normal;
			Vector3 end = position + (normal * NORMAL_SCALING_FACTOR);
			glVertex3f(it->position.x, it->position.y, it->position.z);
			glVertex3f(end.x, end.y, end.y);
		}
	glEnd();
}

void Mesh::render()
{
	// If texturing has been enabled, ensure we set the correct OpenGL state
	if (triangleColouring == MESH_TEXTURE)
	{
		// If texture hasn't been loaded yet - load it now!
		// This is lazy initialisation
		if (!surfaceTexture)
			surfaceTexture = new Texture("resources/world_texture.jpg");
		glEnable(GL_TEXTURE_2D);
		surfaceTexture->bind();
	}

	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	// Perform local model transformations on mesh
	Matrix44 transformation =  Matrix44::translation(pos.x, pos.y, pos.z);
	transformation = transformation * Matrix44::xyzRotation(rot);
	// Transform vertices using position and orientation of drawable
	VertexList transformedVerts = verts;
	// If using per-face normals and not per-vertex normals, compute surface normals now
	if (useSurfaceNormals)
	{
		Vector3List surfaceNormals = computeSurfaceNormals(transformedVerts, tris);
		for (int i = 0; (i < surfaceNormals.size()); i++)
		{
			transformedVerts[i].normal = surfaceNormals[i];
		}
	}
	for (VertexList::iterator it = transformedVerts.begin(); (it != transformedVerts.end()); it++)
	{
		it->position = transformation * it->position;
		it->normal = (transformation * it->normal).normalise();
	}

	// How mesh is drawn depends on geometry type chosen
	switch (geomType)
	{
	case MESH_GEOM_POINTS:
		renderPoints(transformedVerts);
		break;
	case MESH_GEOM_LINES:
		renderLines(transformedVerts, tris);
		break;
	case MESH_GEOM_TRIANGLES:
		renderTriangles(transformedVerts, tris);
		break;
	}
	// Also draw lines reprensenting vertex normals
	if (drawNormals)
	{
		renderNormals(transformedVerts);
	}

	glPopMatrix();

	// Make sure the mesh's texture is unbinded after rendering
	if (triangleColouring == MESH_TEXTURE)
	{
		if (surfaceTexture)
			surfaceTexture->unbind();
		glDisable(GL_TEXTURE_2D);
	}
}

Vector3List Mesh::computeSurfaceNormals(const VertexList& vertices, const TriangleList& triangles)
{
	Vector3List normals;
	normals.resize(vertices.size());

	for (TriangleList::const_iterator it = triangles.begin();
		(it != triangles.end()); it++)
	{
		// Get two points of the triangle and create two vectors from them (sides of triangle)
		Vector3 s1 = vertices[it->v2].position - vertices[it->v1].position;
		Vector3 s2 = vertices[it->v3].position - vertices[it->v1].position;
		// Compute cross product of sides to get surface normal
		Vector3 surfaceNormal = s1.cross(s2).normalise();
		// Assign this surface normal to all v
		normals[it->v1] = surfaceNormal;
		normals[it->v2] = surfaceNormal;
		normals[it->v3] = surfaceNormal;
	}

	return normals;
}
<commit_msg>Guarantee white being uswed as colour for "same triangle colouring"<commit_after>#include <QGLWidget>
#include "Mesh.h"
#include "Matrix44.h"
#include "Common.h"

Mesh::Mesh() : triangleColouring(MESH_COLOUR_SAME), geomType(MESH_GEOM_TRIANGLES),
	surfaceTexture(NULL), useSurfaceNormals(false)
{
}

Mesh::~Mesh()
{
}

const VertexList& Mesh::vertices() const
{
	return verts;
}

void Mesh::setVertices(const VertexList& newVerts)
{
	verts = newVerts;
}

const TriangleList& Mesh::triangles() const
{
	return tris;
}

void Mesh::setTriangles(const TriangleList& newTris)
{
	tris = newTris;
}

Mesh::Colouring Mesh::colouring() const
{
	return triangleColouring;
}

void Mesh::setColouring(Mesh::Colouring newColouring)
{
	triangleColouring = newColouring;
}

Mesh::GeometryType Mesh::geometryType() const
{
	return geomType;
}

void Mesh::setGeometryType(Mesh::GeometryType newGeomType)
{
	geomType = newGeomType;
}

bool Mesh::showingNormals() const
{
	return drawNormals;
}

void Mesh::showNormals(bool willShow)
{
	drawNormals = willShow;
}

Texture* Mesh::texture() const
{
	return surfaceTexture;
}

void Mesh::setTexture(Texture* newTexture)
{
	surfaceTexture = newTexture;
}

bool Mesh::usingPerFaceNormals() const
{
	return useSurfaceNormals;
}

void Mesh::setPerFaceNormals(bool usePerFace)
{
	useSurfaceNormals = usePerFace;
}

void Mesh::renderVertex(const Vertex& v)
{
	glTexCoord2f(v.texCoord.s, v.texCoord.t);
	glNormal3f(v.normal.x, v.normal.y, v.normal.z);
	glVertex3f(v.position.x, v.position.y, v.position.y);
}

void Mesh::renderTriangle(const VertexList& verticesToUse, const Triangle& tri)
{
	renderVertex( verticesToUse[tri.v1] );
	renderVertex( verticesToUse[tri.v2] );
	renderVertex( verticesToUse[tri.v3] );
}

void Mesh::renderPoints(const VertexList& vertices)
{
	glColor3f(1.0f, 1.0f, 1.0f);
	glBegin(GL_POINTS);
		for (VertexList::const_iterator it = vertices.begin(); (it != vertices.end()); it++)
			glVertex3f(it->position.x, it->position.y, it->position.x);
	glEnd();
}

void Mesh::renderLines(const VertexList& vertices, const TriangleList& triangles)
{
	glColor3f(1.0f, 1.0f, 1.0f);
	glBegin(GL_LINES);
		for (TriangleList::const_iterator it = triangles.begin(); (it != triangles.end()); it++)
		{
			const Vertex& v1 = vertices[it->v1];
			const Vertex& v2 = vertices[it->v2];
			const Vertex& v3 = vertices[it->v3];
			glVertex3f(v1.position.x, v1.position.y, v1.position.x);
			glVertex3f(v2.position.x, v2.position.y, v2.position.x);
			glVertex3f(v2.position.x, v2.position.y, v2.position.x);
			glVertex3f(v3.position.x, v3.position.y, v3.position.x);
			glVertex3f(v3.position.x, v3.position.y, v3.position.x);
			glVertex3f(v1.position.x, v1.position.y, v1.position.x);
		}
	glEnd();
}

void Mesh::renderTriangles(const VertexList& vertices, const TriangleList& triangles)
{
	glBegin(GL_TRIANGLES);
	if (triangleColouring == MESH_ALTERNATING_TRIANGLES)
	{
		// Whole for-loop put it if-statement so there isn't a branch every iteration (costly operation
		for (unsigned int i = 0; (i < triangles.size()); i++)
		{
			const Vector3& col = ALTERNATING_TRIANGLE_COLOURS[i % NUM_ALTERNATING_COLOURS];
			glColor3f(col.x, col.y, col.y);
			renderTriangle(vertices, tris[i]);
		}
	}
	else
	{
		if (triangleColouring == MESH_COLOUR_SAME)
			glColor3f(1.0f, 1.0f, 1.0f);
		for (TriangleList::const_iterator it = triangles.begin(); (it != triangles.end()); it++)
			renderTriangle(vertices, *it);
	}
	glEnd();
}

void Mesh::renderNormals(const VertexList& vertices)
{
	glColor3f(1.0f, 1.0f, 1.0f);
	glBegin(GL_LINES);
		for (VertexList::const_iterator it = vertices.begin(); (it != vertices.end()); it++)
		{
			const Vector3& position = it->position;
			const Vector3& normal = it->normal;
			Vector3 end = position + (normal * NORMAL_SCALING_FACTOR);
			glVertex3f(it->position.x, it->position.y, it->position.z);
			glVertex3f(end.x, end.y, end.y);
		}
	glEnd();
}

void Mesh::render()
{
	// If texturing has been enabled, ensure we set the correct OpenGL state
	if (triangleColouring == MESH_TEXTURE)
	{
		// If texture hasn't been loaded yet - load it now!
		// This is lazy initialisation
		if (!surfaceTexture)
			surfaceTexture = new Texture("resources/world_texture.jpg");
		glEnable(GL_TEXTURE_2D);
		surfaceTexture->bind();
	}

	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	// Perform local model transformations on mesh
	Matrix44 transformation =  Matrix44::translation(pos.x, pos.y, pos.z);
	transformation = transformation * Matrix44::xyzRotation(rot);
	// Transform vertices using position and orientation of drawable
	VertexList transformedVerts = verts;
	// If using per-face normals and not per-vertex normals, compute surface normals now
	if (useSurfaceNormals)
	{
		Vector3List surfaceNormals = computeSurfaceNormals(transformedVerts, tris);
		for (int i = 0; (i < surfaceNormals.size()); i++)
		{
			transformedVerts[i].normal = surfaceNormals[i];
		}
	}
	for (VertexList::iterator it = transformedVerts.begin(); (it != transformedVerts.end()); it++)
	{
		it->position = transformation * it->position;
		it->normal = (transformation * it->normal).normalise();
	}

	// How mesh is drawn depends on geometry type chosen
	switch (geomType)
	{
	case MESH_GEOM_POINTS:
		renderPoints(transformedVerts);
		break;
	case MESH_GEOM_LINES:
		renderLines(transformedVerts, tris);
		break;
	case MESH_GEOM_TRIANGLES:
		renderTriangles(transformedVerts, tris);
		break;
	}
	// Also draw lines reprensenting vertex normals
	if (drawNormals)
	{
		renderNormals(transformedVerts);
	}

	glPopMatrix();

	// Make sure the mesh's texture is unbinded after rendering
	if (triangleColouring == MESH_TEXTURE)
	{
		if (surfaceTexture)
			surfaceTexture->unbind();
		glDisable(GL_TEXTURE_2D);
	}
}

Vector3List Mesh::computeSurfaceNormals(const VertexList& vertices, const TriangleList& triangles)
{
	Vector3List normals;
	normals.resize(vertices.size());

	for (TriangleList::const_iterator it = triangles.begin();
		(it != triangles.end()); it++)
	{
		// Get two points of the triangle and create two vectors from them (sides of triangle)
		Vector3 s1 = vertices[it->v2].position - vertices[it->v1].position;
		Vector3 s2 = vertices[it->v3].position - vertices[it->v1].position;
		// Compute cross product of sides to get surface normal
		Vector3 surfaceNormal = s1.cross(s2).normalise();
		// Assign this surface normal to all v
		normals[it->v1] = surfaceNormal;
		normals[it->v2] = surfaceNormal;
		normals[it->v3] = surfaceNormal;
	}

	return normals;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2007-2008 Digital Bazaar, Inc.  All rights reserved.
 */
#include "db/modest/OperationDispatcher.h"
#include "db/modest/Engine.h"

using namespace std;
using namespace db::modest;
using namespace db::rt;

OperationDispatcher::OperationDispatcher(Engine* e) :
   ThreadPool(200),
   JobDispatcher(this, false)
{
   mEngine = e;
   mDispatch = false;

   // set thread expire time to 2 minutes (120000 milliseconds) by default
   getThreadPool()->setThreadExpireTime(120000);
}

OperationDispatcher::~OperationDispatcher()
{
   // stop dispatching
   stopDispatching();

   // terminate all running operations
   terminateRunningOperations();

   // clear all queued operations
   clearQueuedOperations();
}

bool OperationDispatcher::canDispatch()
{
   return mDispatch;
}

void OperationDispatcher::dispatchJobs()
{
   OperationImpl* impl = NULL;

   // get engine state
   ImmutableState* state = mEngine->getState();

   lock();
   {
      // turn off dispatching until an Operation executes or is canceled
      mDispatch = false;

      // execute all Operations that can be executed
      int guardCheck;
      for(list<Runnable*>::iterator i = mJobQueue.begin();
          impl == NULL && i != mJobQueue.end();)
      {
         impl = (OperationImpl*)*i;

         // lock engine state
         state->lock();
         {
            // check the Operation's guard restrictions
            guardCheck = 0;
            if(impl->getGuard() != NULL)
            {
               Operation& op = mOpMap[impl];
               if(!impl->getGuard()->canExecuteOperation(state, op))
               {
                  if(!impl->isInterrupted() &&
                     !impl->getGuard()->mustCancelOperation(state, op))
                  {
                     // operation can wait
                     guardCheck = 1;
                  }
                  else
                  {
                     // operation must be canceled
                     guardCheck = 2;
                  }
               }
            }

            switch(guardCheck)
            {
               case 0:
                  // Operation is executable, enable dispatching and unqueue
                  mDispatch = true;
                  i = mJobQueue.erase(i);

                  // do pre-execution state mutation
                  if(impl->getStateMutator() != NULL)
                  {
                     impl->getStateMutator()->mutatePreExecutionState(
                        (State*)state, mOpMap[impl]);
                  }

                  // try to run the operation
                  if(getThreadPool()->tryRunJob(*impl))
                  {
                     // Operation executed, no need to run it outside of loop
                     impl = NULL;
                  }
                  break;
               case 1:
                  // move to next Operation
                  impl = NULL;
                  i++;
                  break;
               case 2:
                  // Operation is canceled, stop, unmap and unqueue
                  impl->stop();
                  mOpMap.erase(impl);
                  i = mJobQueue.erase(i);
                  impl = NULL;
                  break;
            }
         }
         // unlock engine state
         state->unlock();
      }
   }
   unlock();

   if(impl != NULL)
   {
      // execute Operation, allow thread blocking
      getThreadPool()->runJob(*impl);
   }
}

void OperationDispatcher::queueOperation(Operation& op)
{
   lock();
   {
      // ensure to enable dispatching, then add operation to queue and map
      mDispatch = true;
      mJobQueue.push_back(&(*op));
      mOpMap.insert(make_pair(&(*op), op));
   }
   unlock();

   // wake up dispatcher
   wakeup();
}

void OperationDispatcher::clearQueuedOperations()
{
   lock();
   {
      // remove all job queue entries from the map
      for(list<Runnable*>::iterator i = mJobQueue.begin();
          i != mJobQueue.end(); i++)
      {
         mOpMap.erase((OperationImpl*)*i);
      }

      // clear queue
      mJobQueue.clear();
   }
   unlock();

   // wake up dispatcher
   wakeup();
}

void OperationDispatcher::terminateRunningOperations()
{
   JobDispatcher::terminateAllRunningJobs();

   // wake up dispatcher
   wakeup();
}

void OperationDispatcher::jobCompleted(PooledThread* t)
{
   lock();
   {
      // Note: this method is executed by a PooledThread, external to an
      // Operation, so that the Operation can be safely garbage-collected
      // here if the map happens to hold the last reference to it

      // get operation reference
      OperationImpl* impl = (OperationImpl*)t->getJob();
      Operation& op = mOpMap[impl];

      // do post-execution state mutation
      if(op->getStateMutator() != NULL)
      {
         mEngine->getState()->lock();
         {
            op->getStateMutator()->mutatePostExecutionState(
               (State*)mEngine->getState(), op);
         }
         mEngine->getState()->unlock();
      }

      // stop operation
      op->stop();

      // wake up dispatcher
      mDispatch = true;
      wakeup();

      // remove operation reference from map
      mOpMap.erase(impl);
   }
   unlock();

   // call parent method to release thread back into pool
   ThreadPool::jobCompleted(t);
}

Operation OperationDispatcher::getCurrentOperation()
{
   // get the current thread's OperationImpl
   Thread* thread = Thread::currentThread();
   OperationImpl* impl = (OperationImpl*)thread->getUserData();
   Operation op = mOpMap[impl];
   return op;
}

ThreadPool* OperationDispatcher::getThreadPool()
{
   return JobDispatcher::getThreadPool();
}

unsigned int OperationDispatcher::getQueuedOperationCount()
{
   return JobDispatcher::getQueuedJobCount();
}

unsigned int OperationDispatcher::getTotalOperationCount()
{
   return JobDispatcher::getTotalJobCount();
}
<commit_msg>Made job cleanup code faster.<commit_after>/*
 * Copyright (c) 2007-2008 Digital Bazaar, Inc.  All rights reserved.
 */
#include "db/modest/OperationDispatcher.h"
#include "db/modest/Engine.h"

using namespace std;
using namespace db::modest;
using namespace db::rt;

OperationDispatcher::OperationDispatcher(Engine* e) :
   ThreadPool(200),
   JobDispatcher(this, false)
{
   mEngine = e;
   mDispatch = false;

   // set thread expire time to 2 minutes (120000 milliseconds) by default
   getThreadPool()->setThreadExpireTime(120000);
}

OperationDispatcher::~OperationDispatcher()
{
   // stop dispatching
   stopDispatching();

   // terminate all running operations
   terminateRunningOperations();

   // clear all queued operations
   clearQueuedOperations();
}

bool OperationDispatcher::canDispatch()
{
   return mDispatch;
}

void OperationDispatcher::dispatchJobs()
{
   OperationImpl* impl = NULL;

   // get engine state
   ImmutableState* state = mEngine->getState();

   lock();
   {
      // turn off dispatching until an Operation executes or is canceled
      mDispatch = false;

      // execute all Operations that can be executed
      int guardCheck;
      for(list<Runnable*>::iterator i = mJobQueue.begin();
          impl == NULL && i != mJobQueue.end();)
      {
         impl = (OperationImpl*)*i;

         // lock engine state
         state->lock();
         {
            // check the Operation's guard restrictions
            guardCheck = 0;
            if(impl->getGuard() != NULL)
            {
               Operation& op = mOpMap[impl];
               if(!impl->getGuard()->canExecuteOperation(state, op))
               {
                  if(!impl->isInterrupted() &&
                     !impl->getGuard()->mustCancelOperation(state, op))
                  {
                     // operation can wait
                     guardCheck = 1;
                  }
                  else
                  {
                     // operation must be canceled
                     guardCheck = 2;
                  }
               }
            }

            switch(guardCheck)
            {
               case 0:
                  // Operation is executable, enable dispatching and unqueue
                  mDispatch = true;
                  i = mJobQueue.erase(i);

                  // do pre-execution state mutation
                  if(impl->getStateMutator() != NULL)
                  {
                     impl->getStateMutator()->mutatePreExecutionState(
                        (State*)state, mOpMap[impl]);
                  }

                  // try to run the operation
                  if(getThreadPool()->tryRunJob(*impl))
                  {
                     // Operation executed, no need to run it outside of loop
                     impl = NULL;
                  }
                  break;
               case 1:
                  // move to next Operation
                  impl = NULL;
                  i++;
                  break;
               case 2:
                  // Operation is canceled, stop, unmap and unqueue
                  impl->stop();
                  mOpMap.erase(impl);
                  i = mJobQueue.erase(i);
                  impl = NULL;
                  break;
            }
         }
         // unlock engine state
         state->unlock();
      }
   }
   unlock();

   if(impl != NULL)
   {
      // execute Operation, allow thread blocking
      getThreadPool()->runJob(*impl);
   }
}

void OperationDispatcher::queueOperation(Operation& op)
{
   lock();
   {
      // ensure to enable dispatching, then add operation to queue and map
      mDispatch = true;
      mJobQueue.push_back(&(*op));
      mOpMap.insert(make_pair(&(*op), op));
   }
   unlock();

   // wake up dispatcher
   wakeup();
}

void OperationDispatcher::clearQueuedOperations()
{
   lock();
   {
      // remove all job queue entries from the map
      for(list<Runnable*>::iterator i = mJobQueue.begin();
          i != mJobQueue.end(); i++)
      {
         mOpMap.erase((OperationImpl*)*i);
      }

      // clear queue
      mJobQueue.clear();
   }
   unlock();

   // wake up dispatcher
   wakeup();
}

void OperationDispatcher::terminateRunningOperations()
{
   JobDispatcher::terminateAllRunningJobs();

   // wake up dispatcher
   wakeup();
}

void OperationDispatcher::jobCompleted(PooledThread* t)
{
   lock();
   {
      // Note: this method is executed by a PooledThread, external to an
      // Operation, so that the Operation can be safely garbage-collected
      // here if the map happens to hold the last reference to it

      // get operation reference
      OperationImpl* impl = (OperationImpl*)t->getJob();
      OperationMap::iterator i = mOpMap.find(impl);
      Operation& op = i->second;

      // do post-execution state mutation
      if(op->getStateMutator() != NULL)
      {
         mEngine->getState()->lock();
         {
            op->getStateMutator()->mutatePostExecutionState(
               (State*)mEngine->getState(), op);
         }
         mEngine->getState()->unlock();
      }

      // stop operation
      op->stop();

      // wake up dispatcher
      mDispatch = true;
      wakeup();

      // remove operation reference from map
      mOpMap.erase(i);
   }
   unlock();

   // call parent method to release thread back into pool
   ThreadPool::jobCompleted(t);
}

Operation OperationDispatcher::getCurrentOperation()
{
   // get the current thread's OperationImpl
   Thread* thread = Thread::currentThread();
   OperationImpl* impl = (OperationImpl*)thread->getUserData();
   Operation op = mOpMap[impl];
   return op;
}

ThreadPool* OperationDispatcher::getThreadPool()
{
   return JobDispatcher::getThreadPool();
}

unsigned int OperationDispatcher::getQueuedOperationCount()
{
   return JobDispatcher::getQueuedJobCount();
}

unsigned int OperationDispatcher::getTotalOperationCount()
{
   return JobDispatcher::getTotalJobCount();
}
<|endoftext|>


<commit_before>#include "luaSemanticAnalyzerTest.h"

#include "qrtext/lua/ast/assignment.h"
#include "qrtext/lua/ast/block.h"
#include "qrtext/lua/ast/functionCall.h"
#include "qrtext/lua/ast/unaryMinus.h"

#include "qrtext/lua/types/integer.h"
#include "qrtext/lua/types/float.h"
#include "qrtext/lua/types/string.h"

#include "gtest/gtest.h"

using namespace qrTest;
using namespace qrtext;
using namespace qrtext::lua;
using namespace qrtext::lua::details;
using namespace qrtext::lua::types;

void LuaSemanticAnalyzerTest::SetUp()
{
	mAnalyzer.reset(new LuaSemanticAnalyzer(mErrors));
	mParser.reset(new LuaParser(mErrors));
	mLexer.reset(new LuaLexer(mErrors));
}

QSharedPointer<qrtext::core::ast::Node> LuaSemanticAnalyzerTest::parse(QString const &code)
{
	return mParser->parse(mLexer->tokenize(code));
}

TEST_F(LuaSemanticAnalyzerTest, sanityCheck)
{
	auto tree = parse("123");
	mAnalyzer->analyze(tree);
	EXPECT_TRUE(mErrors.empty());
	EXPECT_TRUE(mAnalyzer->type(tree)->is<Integer>());
	EXPECT_TRUE(mAnalyzer->type(tree)->is<Number>());
}

TEST_F(LuaSemanticAnalyzerTest, assignment)
{
	auto tree = parse("a = 123");

	auto variable = as<ast::Assignment>(tree)->variable();
	auto value = as<ast::Assignment>(tree)->value();

	mAnalyzer->analyze(tree);

	EXPECT_TRUE(mErrors.empty());
	EXPECT_TRUE(mAnalyzer->type(variable)->is<types::Integer>());
	EXPECT_TRUE(mAnalyzer->type(value)->is<types::Integer>());
}

TEST_F(LuaSemanticAnalyzerTest, unaryOperator)
{
	auto tree = parse("-123");

	mAnalyzer->analyze(tree);

	EXPECT_TRUE(mErrors.empty());
	EXPECT_TRUE(mAnalyzer->type(as<ast::UnaryMinus>(tree))->is<types::Integer>());
}

TEST_F(LuaSemanticAnalyzerTest, propagation)
{
	auto tree = parse("a = -123; b = a");

	mAnalyzer->analyze(tree);

	EXPECT_TRUE(mErrors.empty());

	auto block = as<ast::Block>(tree);
	auto firstAssignment = as<ast::Assignment>(block->children()[0]);
	auto secondAssignment = as<ast::Assignment>(block->children()[1]);

	auto firstVariable = firstAssignment->variable();
	auto firstValue = firstAssignment->value();

	EXPECT_TRUE(mAnalyzer->type(firstVariable)->is<types::Integer>());
	EXPECT_TRUE(mAnalyzer->type(firstValue)->is<types::Integer>());

	auto secondVariable = secondAssignment->variable();
	auto secondValue = secondAssignment->value();

	EXPECT_TRUE(mAnalyzer->type(secondVariable)->is<types::Integer>());
	EXPECT_TRUE(mAnalyzer->type(secondValue)->is<types::Integer>());
}

TEST_F(LuaSemanticAnalyzerTest, integerFloatCoercion)
{
	auto tree = parse("a = -123; a = 1.0");

	mAnalyzer->analyze(tree);

	EXPECT_TRUE(mErrors.isEmpty());

	auto block = as<ast::Block>(tree);
	auto firstAssignment = as<ast::Assignment>(block->children()[0]);
	auto secondAssignment = as<ast::Assignment>(block->children()[1]);

	auto firstVariable = firstAssignment->variable();
	auto firstValue = firstAssignment->value();

	EXPECT_TRUE(mAnalyzer->type(firstVariable)->is<types::Float>());
	EXPECT_TRUE(mAnalyzer->type(firstValue)->is<types::Integer>());

	auto secondVariable = secondAssignment->variable();
	auto secondValue = secondAssignment->value();

	EXPECT_TRUE(mAnalyzer->type(secondVariable)->is<types::Float>());
	EXPECT_TRUE(mAnalyzer->type(secondValue)->is<types::Float>());
}

TEST_F(LuaSemanticAnalyzerTest, functionReturnType)
{
	auto tree = parse("a = f(1)");

	mAnalyzer->addIntrinsicFunction("f", QSharedPointer<types::Function>(new types::Function(
			QSharedPointer<core::types::TypeExpression>(new types::Float()),
			{QSharedPointer<core::types::TypeExpression>(new types::Integer())}
			)));

	mAnalyzer->analyze(tree);

	EXPECT_TRUE(mErrors.isEmpty());

	auto assignment = as<ast::Assignment>(tree);

	auto a = assignment->variable();
	auto f = assignment->value();

	EXPECT_TRUE(mAnalyzer->type(a)->is<types::Float>());
	EXPECT_TRUE(mAnalyzer->type(f)->is<types::Float>());
}

TEST_F(LuaSemanticAnalyzerTest, functionParameters)
{
	auto tree = parse("a = f(b, c)");

	mAnalyzer->addIntrinsicFunction("f", QSharedPointer<types::Function>(new types::Function(
			QSharedPointer<core::types::TypeExpression>(new types::Float()),
			{QSharedPointer<core::types::TypeExpression>(new types::Integer())
					, QSharedPointer<core::types::TypeExpression>(new types::String())}
			)));

	mAnalyzer->analyze(tree);

	EXPECT_TRUE(mErrors.isEmpty());

	auto assignment = as<ast::Assignment>(tree);

	auto f = as<ast::FunctionCall>(assignment->value());

	auto b = f->arguments()[0];
	auto c = f->arguments()[1];

	EXPECT_TRUE(mAnalyzer->type(b)->is<types::Integer>());
	EXPECT_TRUE(mAnalyzer->type(c)->is<types::String>());
}

TEST_F(LuaSemanticAnalyzerTest, invalidFunctionParameters)
{
	auto tree = parse("a = f(0.5, 'a')");

	mAnalyzer->addIntrinsicFunction("f", QSharedPointer<types::Function>(new types::Function(
			QSharedPointer<core::types::TypeExpression>(new types::Float()),
			{QSharedPointer<core::types::TypeExpression>(new types::Integer())
					, QSharedPointer<core::types::TypeExpression>(new types::String())}
			)));

	mAnalyzer->analyze(tree);

	ASSERT_FALSE(mErrors.isEmpty());
}
<commit_msg>added test for invalid use of integer-float coercion where it breaks existing type constraints<commit_after>#include "luaSemanticAnalyzerTest.h"

#include "qrtext/core/types/any.h"

#include "qrtext/lua/ast/assignment.h"
#include "qrtext/lua/ast/block.h"
#include "qrtext/lua/ast/functionCall.h"
#include "qrtext/lua/ast/unaryMinus.h"

#include "qrtext/lua/types/integer.h"
#include "qrtext/lua/types/float.h"
#include "qrtext/lua/types/string.h"

#include "gtest/gtest.h"

using namespace qrTest;
using namespace qrtext;
using namespace qrtext::lua;
using namespace qrtext::lua::details;
using namespace qrtext::lua::types;

void LuaSemanticAnalyzerTest::SetUp()
{
	mAnalyzer.reset(new LuaSemanticAnalyzer(mErrors));
	mParser.reset(new LuaParser(mErrors));
	mLexer.reset(new LuaLexer(mErrors));
}

QSharedPointer<qrtext::core::ast::Node> LuaSemanticAnalyzerTest::parse(QString const &code)
{
	return mParser->parse(mLexer->tokenize(code));
}

TEST_F(LuaSemanticAnalyzerTest, sanityCheck)
{
	auto tree = parse("123");
	mAnalyzer->analyze(tree);
	EXPECT_TRUE(mErrors.empty());
	EXPECT_TRUE(mAnalyzer->type(tree)->is<Integer>());
	EXPECT_TRUE(mAnalyzer->type(tree)->is<Number>());
}

TEST_F(LuaSemanticAnalyzerTest, assignment)
{
	auto tree = parse("a = 123");

	auto variable = as<ast::Assignment>(tree)->variable();
	auto value = as<ast::Assignment>(tree)->value();

	mAnalyzer->analyze(tree);

	EXPECT_TRUE(mErrors.empty());
	EXPECT_TRUE(mAnalyzer->type(variable)->is<types::Integer>());
	EXPECT_TRUE(mAnalyzer->type(value)->is<types::Integer>());
}

TEST_F(LuaSemanticAnalyzerTest, unaryOperator)
{
	auto tree = parse("-123");

	mAnalyzer->analyze(tree);

	EXPECT_TRUE(mErrors.empty());
	EXPECT_TRUE(mAnalyzer->type(as<ast::UnaryMinus>(tree))->is<types::Integer>());
}

TEST_F(LuaSemanticAnalyzerTest, propagation)
{
	auto tree = parse("a = -123; b = a");

	mAnalyzer->analyze(tree);

	EXPECT_TRUE(mErrors.empty());

	auto block = as<ast::Block>(tree);
	auto firstAssignment = as<ast::Assignment>(block->children()[0]);
	auto secondAssignment = as<ast::Assignment>(block->children()[1]);

	auto firstVariable = firstAssignment->variable();
	auto firstValue = firstAssignment->value();

	EXPECT_TRUE(mAnalyzer->type(firstVariable)->is<types::Integer>());
	EXPECT_TRUE(mAnalyzer->type(firstValue)->is<types::Integer>());

	auto secondVariable = secondAssignment->variable();
	auto secondValue = secondAssignment->value();

	EXPECT_TRUE(mAnalyzer->type(secondVariable)->is<types::Integer>());
	EXPECT_TRUE(mAnalyzer->type(secondValue)->is<types::Integer>());
}

TEST_F(LuaSemanticAnalyzerTest, integerFloatCoercion)
{
	auto tree = parse("a = -123; a = 1.0");

	mAnalyzer->analyze(tree);

	EXPECT_TRUE(mErrors.isEmpty());

	auto block = as<ast::Block>(tree);
	auto firstAssignment = as<ast::Assignment>(block->children()[0]);
	auto secondAssignment = as<ast::Assignment>(block->children()[1]);

	auto firstVariable = firstAssignment->variable();
	auto firstValue = firstAssignment->value();

	EXPECT_TRUE(mAnalyzer->type(firstVariable)->is<types::Float>());
	EXPECT_TRUE(mAnalyzer->type(firstValue)->is<types::Integer>());

	auto secondVariable = secondAssignment->variable();
	auto secondValue = secondAssignment->value();

	EXPECT_TRUE(mAnalyzer->type(secondVariable)->is<types::Float>());
	EXPECT_TRUE(mAnalyzer->type(secondValue)->is<types::Float>());
}

TEST_F(LuaSemanticAnalyzerTest, functionReturnType)
{
	auto tree = parse("a = f(1)");

	mAnalyzer->addIntrinsicFunction("f", QSharedPointer<types::Function>(new types::Function(
			QSharedPointer<core::types::TypeExpression>(new types::Float()),
			{QSharedPointer<core::types::TypeExpression>(new types::Integer())}
			)));

	mAnalyzer->analyze(tree);

	EXPECT_TRUE(mErrors.isEmpty());

	auto assignment = as<ast::Assignment>(tree);

	auto a = assignment->variable();
	auto f = assignment->value();

	EXPECT_TRUE(mAnalyzer->type(a)->is<types::Float>());
	EXPECT_TRUE(mAnalyzer->type(f)->is<types::Float>());
}

TEST_F(LuaSemanticAnalyzerTest, functionParameters)
{
	auto tree = parse("a = f(b, c)");

	mAnalyzer->addIntrinsicFunction("f", QSharedPointer<types::Function>(new types::Function(
			QSharedPointer<core::types::TypeExpression>(new types::Float()),
			{QSharedPointer<core::types::TypeExpression>(new types::Integer())
					, QSharedPointer<core::types::TypeExpression>(new types::String())}
			)));

	mAnalyzer->analyze(tree);

	EXPECT_TRUE(mErrors.isEmpty());

	auto assignment = as<ast::Assignment>(tree);

	auto f = as<ast::FunctionCall>(assignment->value());

	auto b = f->arguments()[0];
	auto c = f->arguments()[1];

	EXPECT_TRUE(mAnalyzer->type(b)->is<types::Integer>());
	EXPECT_TRUE(mAnalyzer->type(c)->is<types::String>());
}

TEST_F(LuaSemanticAnalyzerTest, invalidFunctionParameters)
{
	auto tree = parse("a = f(0.5, 'a')");

	mAnalyzer->addIntrinsicFunction("f", QSharedPointer<types::Function>(new types::Function(
			QSharedPointer<core::types::TypeExpression>(new types::Float()),
			{QSharedPointer<core::types::TypeExpression>(new types::Integer())
					, QSharedPointer<core::types::TypeExpression>(new types::String())}
			)));

	mAnalyzer->analyze(tree);

	ASSERT_FALSE(mErrors.isEmpty());
}

TEST_F(LuaSemanticAnalyzerTest, invalidCoercion)
{
	auto tree = parse("a = f(b, c); b = 0.5");

	mAnalyzer->addIntrinsicFunction("f", QSharedPointer<types::Function>(new types::Function(
			QSharedPointer<core::types::TypeExpression>(new types::Float()),
			{QSharedPointer<core::types::TypeExpression>(new types::Integer())
					, QSharedPointer<core::types::TypeExpression>(new types::String())}
			)));

	mAnalyzer->analyze(tree);

	EXPECT_FALSE(mErrors.isEmpty());

	auto block = as<ast::Block>(tree);
	auto firstAssignment = as<ast::Assignment>(block->children()[0]);

	auto a = firstAssignment->variable();
	auto f = as<ast::FunctionCall>(firstAssignment->value());

	EXPECT_TRUE(mAnalyzer->type(a)->is<types::Float>());

	auto b = f->arguments()[0];
	auto c = f->arguments()[1];

	EXPECT_TRUE(mAnalyzer->type(b)->is<qrtext::core::types::Any>());
	EXPECT_TRUE(mAnalyzer->type(c)->is<types::String>());
}
<|endoftext|>


<commit_before>/*
 * opencog/atoms/core/PutLink.cc
 *
 * Copyright (C) 2015 Linas Vepstas
 * All Rights Reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <opencog/atoms/base/atom_types.h>
#include <opencog/atoms/base/ClassServer.h>
#include "DefineLink.h"
#include "FreeLink.h"
#include "LambdaLink.h"
#include "PutLink.h"

using namespace opencog;

PutLink::PutLink(const HandleSeq& oset, Type t)
    : RewriteLink(oset, t)
{
	init();
}

PutLink::PutLink(const Link& l)
    : RewriteLink(l)
{
	init();
}

/* ================================================================= */

/// PutLink expects a very strict format: an arity-2 link, with
/// the first part being a pattern, and the second a list or set
/// of values. If the pattern has N variables, then the seccond
/// part must have N values.  Furthermore, any type restrictions on
/// the variables must be satisfied by the values.
///
/// The following formats are understood:
///
///    PutLink
///       <pattern with 1 variable>
///       <any single atom>
///
///    PutLink
///       <pattern with N variables>
///       ListLink     ;; must have arity N
///          <atom 1>
///          ...
///          <atom N>
///
/// The below is a handy-dandy easy-to-use form. When it is reduced,
/// it will result in the creation of a set of reduced forms, not
/// just one (the two sets haveing the same arity). Unfortunately,
/// this trick cannot work for N=1 unless the variable is cosntrained
/// to not be a set.
///
///    PutLink
///       <pattern with N variables>
///       SetLink        ;; Must hold a set of ListLinks
///          ListLink    ;; must have arity N
///             <atom 1>
///             ...
///             <atom N>
///
void PutLink::init(void)
{
	if (not classserver().isA(get_type(), PUT_LINK))
		throw InvalidParamException(TRACE_INFO, "Expecting a PutLink");

	size_t sz = _outgoing.size();
	if (2 != sz and 3 != sz)
		throw InvalidParamException(TRACE_INFO,
			"Expecting an outgoing set size of two or three, got %d; %s",
			sz, to_string().c_str());

	ScopeLink::extract_variables(_outgoing);

	if (2 == sz)
	{
		// If the body is just a single variable, and there are no
		// type declarations for it, then ScopeLink gets confused.
		_vardecl = Handle::UNDEFINED;
		_body = _outgoing[0];
		_values = _outgoing[1];
	}
	else
		_values = _outgoing[2];

	static_typecheck_values();
}

/// Check that the values in the PutLink obey the type constraints.
/// This only performs "static" typechecking, at construction-time;
/// since the values may be dynamically obtained at run-time, we cannot
/// check these here.
void PutLink::static_typecheck_values(void)
{
	// Cannot typecheck at this pont in time, because the schema
	// might not be defined yet...
	Type btype = _body->get_type();
	if (DEFINED_SCHEMA_NODE == btype)
		return;
	if (DEFINED_PREDICATE_NODE == btype)
		return;

	// If its part of a signature, there is nothing to do.
	if (classserver().isA(btype, TYPE_NODE) or TYPE_CHOICE == btype)
		return;

	size_t sz = _varlist.varseq.size();
	Type vtype = _values->get_type();

	if (1 == sz)
	{
		if (not _varlist.is_type(_values)
		    and SET_LINK != vtype
		    and PUT_LINK != vtype
		    and not (classserver().isA(vtype, SATISFYING_LINK)))
		{
				throw InvalidParamException(TRACE_INFO,
					"PutLink mismatched type!");
		}
		return;
	}

	// Cannot typecheck naked FunctionLinks.  For example:
	// (cog-execute! (Put (Plus) (List (Number 2) (Number 2))))
	if (0 == sz and classserver().isA(btype, FUNCTION_LINK))
		return;

	// The standard, default case is to get a ListLink as an argument.
	if (LIST_LINK == vtype)
	{
		if (not _varlist.is_type(_values->getOutgoingSet()))
		{
			if (_vardecl)
				throw SyntaxException(TRACE_INFO,
					"PutLink has mismatched value list! vardecl=%s\nvals=%s",
					_vardecl->to_string().c_str(),
					_values->to_string().c_str());
			else
				throw SyntaxException(TRACE_INFO,
					"PutLink has mismatched value list! body=%s\nvals=%s",
					_body->to_string().c_str(),
					_values->to_string().c_str());
		}
		return;
	}

	// GetLinks (and the like) are evaluated dynamically, later.
	if (classserver().isA(vtype, SATISFYING_LINK))
		return;

	// If its part of a signature, there is nothing to do.
	if (TYPE_NODE == vtype or TYPE_CHOICE == vtype)
		return;

	// The only remaining possibility is that there is set of ListLinks.
	if (SET_LINK != vtype)
		throw InvalidParamException(TRACE_INFO,
			"PutLink was expecting a ListLink, SetLink or GetLink!");

	if (1 < sz)
	{
		for (const Handle& h : _values->getOutgoingSet())
		{
			// If the arity is greater than one, then the values must be in a list.
		   if (h->get_type() != LIST_LINK)
				throw InvalidParamException(TRACE_INFO,
					"PutLink expected value list!");

			if (not _varlist.is_type(h->getOutgoingSet()))
				throw InvalidParamException(TRACE_INFO,
					"PutLink bad value list!");
		}
		return;
	}

	// If the arity is one, the values must obey type constraint.
	for (const Handle& h : _values->getOutgoingSet())
	{
		if (not _varlist.is_type(h))
			throw InvalidParamException(TRACE_INFO,
					"PutLink bad type!");
	}
}

/* ================================================================= */

static inline Handle reddy(RewriteLinkPtr subs, const HandleSeq& oset)
{
	subs->make_silent(true);
	return subs->beta_reduce(oset);
}

/**
 * Perform the actual beta reduction --
 *
 * Substitute values for the variables in the pattern tree.
 * This is a lot like applying the function fun to the argument list
 * args, except that no actual evaluation is performed; only
 * substitution.  The resulting tree is NOT placed into any atomspace,
 * either. If you want that, you must do it youself.  If you want
 * evaluation or execution to happen during or after sustitution, use
 * either the EvaluationLink, the ExecutionOutputLink, or the Instantiator.
 *
 * So, for example, if this PutLink looks like this:
 *
 *   PutLink
 *      EvaluationLink
 *         PredicateNode "is a kind of"
 *         ListLink
 *            VariableNode $a
 *            ConceptNode "hot patootie"
 *      ConceptNode "cowpie"
 *
 * then the reduced value will be
 *
 *   EvaluationLink
 *      PredicateNode "is a kind of"
 *      ListLink
 *         ConceptNode "cowpie"
 *         ConceptNode "hot patootie"
 *
 * Type checking is performed during substitution; if the values fail to
 * have the desired types, no substitution is performed.  In this case,
 * an undefined handle is returned. For set substitutions, this acts as
 * a filter, removing (filtering out) the mismatched types.
 *
 * Again, only a substitution is performed, there is no execution or
 * evaluation.  Note also that the resulting tree is NOT placed into
 * any atomspace!
 */
Handle PutLink::do_reduce(void) const
{
	Handle bods(_body);
	Variables vars(_varlist);
	RewriteLinkPtr subs(RewriteLinkCast(get_handle()));

	// Resolve the body, if needed. That is, if the body is
	// given in a defintion, get that defintion.
	Type btype = _body->get_type();
	if (DEFINED_SCHEMA_NODE == btype or
	    DEFINED_PREDICATE_NODE == btype)
	{
		bods = DefineLink::get_definition(bods);
		btype = bods->get_type();
		// XXX TODO we should perform a type-check on the function.
		if (not classserver().isA(btype, LAMBDA_LINK))
			throw InvalidParamException(TRACE_INFO,
					"Expecting a LambdaLink, got %s",
			      bods->to_string().c_str());
	}

	// If the body is a lambda, work with that.
	if (classserver().isA(btype, LAMBDA_LINK))
	{
		LambdaLinkPtr lam(LambdaLinkCast(bods));
		bods = lam->get_body();
		vars = lam->get_variables();
		btype = bods->get_type();
		subs = lam;
	}

	// Now get the values that we will plug into the body.
	Type vtype = _values->get_type();

	size_t nvars = vars.varseq.size();

	// FunctionLinks behave like combinators; that is, one can create
	// valid beta-redexes with them. We handle that here.
	//
	// XXX At this time, we don't know the number of arguments any
	// given FunctionLink might take.  Atomese does have the mechanisms
	// to declare these, including arbitrary-arity functions, its
	// just that its currently not declared anywhere for any of the
	// FunctionLinks.  So we just punt.  Example usage:
	// (cog-execute! (Put (Plus) (List (Number 2) (Number 2))))
	// (cog-execute! (Put (Plus (Number 9)) (List (Number 2) (Number 2))))
	if (0 == nvars and classserver().isA(btype, FUNCTION_LINK))
	{
		if (LIST_LINK == vtype)
		{
			HandleSeq oset(bods->getOutgoingSet());
			const HandleSeq& rest = _values->getOutgoingSet();
			oset.insert(oset.end(), rest.begin(), rest.end());
			return createLink(oset, btype);
		}

		if (SET_LINK != vtype)
		{
			HandleSeq oset(bods->getOutgoingSet());
			oset.emplace_back(_values);
			return createLink(oset, btype);
		}

		// If the values are given in a set, then iterate over the set...
		HandleSeq bset;
		for (const Handle& h : _values->getOutgoingSet())
		{
			if (LIST_LINK == h->get_type())
			{
				HandleSeq oset(bods->getOutgoingSet());
				const HandleSeq& rest = h->getOutgoingSet();
				oset.insert(oset.end(), rest.begin(), rest.end());
				bset.emplace_back(createLink(oset, btype));
			}
			else
			{
				HandleSeq oset(bods->getOutgoingSet());
				oset.emplace_back(h);
				bset.emplace_back(createLink(oset, btype));
			}
		}
		return createLink(bset, SET_LINK);
	}

	// If there is only one variable in the PutLink body...
	if (1 == nvars)
	{
		if (SET_LINK != vtype)
		{
			HandleSeq oset;
			oset.emplace_back(_values);
			try
			{
				// return vars.substitute(bods, oset, /* silent */ true);
				return reddy(subs, oset);
			}
			catch (const TypeCheckException& ex)
			{
				return Handle::UNDEFINED;
			}
		}

		// If the values are given in a set, then iterate over the set...
		HandleSeq bset;
		for (const Handle& h : _values->getOutgoingSet())
		{
			HandleSeq oset;
			oset.emplace_back(h);
			try
			{
				// bset.emplace_back(vars.substitute(bods, oset, /* silent */ true));
				bset.emplace_back(reddy(subs, oset));
			}
			catch (const TypeCheckException& ex) {}
		}
		return createLink(bset, SET_LINK);
	}

	// If we are here, then there are multiple variables in the body.
	// See how many values there are.  If the values are a ListLink,
	// then assume that there is only a single set of values to plug in.
	if (LIST_LINK == vtype)
	{
		const HandleSeq& oset = _values->getOutgoingSet();
		try
		{
			// return vars.substitute(bods, oset, /* silent */ true);
			return reddy(subs, oset);
		}
		catch (const TypeCheckException& ex)
		{
			return Handle::UNDEFINED;
		}
	}

	// If we are here, then there are multiple values.
	// These  MUST be given to us as a SetLink.
	OC_ASSERT(SET_LINK == vtype,
		"Should have caught this earlier, in the ctor");

	HandleSeq bset;
	for (const Handle& h : _values->getOutgoingSet())
	{
		const HandleSeq& oset = h->getOutgoingSet();
		try
		{
			// bset.emplace_back(vars.substitute(bods, oset, /* silent */ true));
			bset.emplace_back(reddy(subs, oset));
		}
		catch (const TypeCheckException& ex) {}
	}
	return createLink(bset, SET_LINK);
}

Handle PutLink::reduce(void)
{
	return do_reduce();
}

DEFINE_LINK_FACTORY(PutLink, PUT_LINK)

/* ===================== END OF FILE ===================== */
<commit_msg>support eta conversion<commit_after>/*
 * opencog/atoms/core/PutLink.cc
 *
 * Copyright (C) 2015 Linas Vepstas
 * All Rights Reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <opencog/atoms/base/atom_types.h>
#include <opencog/atoms/base/ClassServer.h>
#include "DefineLink.h"
#include "FreeLink.h"
#include "LambdaLink.h"
#include "PutLink.h"

using namespace opencog;

PutLink::PutLink(const HandleSeq& oset, Type t)
    : RewriteLink(oset, t)
{
	init();
}

PutLink::PutLink(const Link& l)
    : RewriteLink(l)
{
	init();
}

/* ================================================================= */

/// PutLink expects a very strict format: an arity-2 link, with
/// the first part being a pattern, and the second a list or set
/// of values. If the pattern has N variables, then the seccond
/// part must have N values.  Furthermore, any type restrictions on
/// the variables must be satisfied by the values.
///
/// The following formats are understood:
///
///    PutLink
///       <pattern with 1 variable>
///       <any single atom>
///
///    PutLink
///       <pattern with N variables>
///       ListLink     ;; must have arity N
///          <atom 1>
///          ...
///          <atom N>
///
/// The below is a handy-dandy easy-to-use form. When it is reduced,
/// it will result in the creation of a set of reduced forms, not
/// just one (the two sets haveing the same arity). Unfortunately,
/// this trick cannot work for N=1 unless the variable is cosntrained
/// to not be a set.
///
///    PutLink
///       <pattern with N variables>
///       SetLink        ;; Must hold a set of ListLinks
///          ListLink    ;; must have arity N
///             <atom 1>
///             ...
///             <atom N>
///
void PutLink::init(void)
{
	if (not classserver().isA(get_type(), PUT_LINK))
		throw InvalidParamException(TRACE_INFO, "Expecting a PutLink");

	size_t sz = _outgoing.size();
	if (2 != sz and 3 != sz)
		throw InvalidParamException(TRACE_INFO,
			"Expecting an outgoing set size of two or three, got %d; %s",
			sz, to_string().c_str());

	ScopeLink::extract_variables(_outgoing);

	if (2 == sz)
	{
		// If the body is just a single variable, and there are no
		// type declarations for it, then ScopeLink gets confused.
		_vardecl = Handle::UNDEFINED;
		_body = _outgoing[0];
		_values = _outgoing[1];
	}
	else
		_values = _outgoing[2];

	static_typecheck_values();
}

/// Check that the values in the PutLink obey the type constraints.
/// This only performs "static" typechecking, at construction-time;
/// since the values may be dynamically obtained at run-time, we cannot
/// check these here.
void PutLink::static_typecheck_values(void)
{
	// Cannot typecheck at this pont in time, because the schema
	// might not be defined yet...
	Type btype = _body->get_type();
	if (DEFINED_SCHEMA_NODE == btype)
		return;
	if (DEFINED_PREDICATE_NODE == btype)
		return;

	// If its part of a signature, there is nothing to do.
	if (classserver().isA(btype, TYPE_NODE) or TYPE_CHOICE == btype)
		return;

	size_t sz = _varlist.varseq.size();

	Handle valley = _values;
	Type vtype = valley->get_type();

	// If its a LambdaLink, try to see if its eta-reducible. If
	// so, then eta-reduce it immediately. A LambdaLink is
	// eta-reducible when it's body is a ListLink.
	if (LAMBDA_LINK == vtype)
	{
		LambdaLinkPtr lam(LambdaLinkCast(_values));
		Handle body = lam->get_body();
		Type bt = body->get_type();
		if (LIST_LINK == bt)
		{
			valley = body;
			vtype = bt;
		}
	}

	if (1 == sz)
	{
		if (not _varlist.is_type(valley)
		    and SET_LINK != vtype
		    and PUT_LINK != vtype
		    and not (classserver().isA(vtype, SATISFYING_LINK)))
		{
				throw InvalidParamException(TRACE_INFO,
					"PutLink mismatched type!");
		}
		return;
	}

	// Cannot typecheck naked FunctionLinks.  For example:
	// (cog-execute! (Put (Plus) (List (Number 2) (Number 2))))
	if (0 == sz and classserver().isA(btype, FUNCTION_LINK))
		return;

	// The standard, default case is to get a ListLink as an argument.
	if (LIST_LINK == vtype)
	{
		if (not _varlist.is_type(valley->getOutgoingSet()))
		{
			if (_vardecl)
				throw SyntaxException(TRACE_INFO,
					"PutLink has mismatched value list! vardecl=%s\nvals=%s",
					_vardecl->to_string().c_str(),
					_values->to_string().c_str());
			else
				throw SyntaxException(TRACE_INFO,
					"PutLink has mismatched value list! body=%s\nvals=%s",
					_body->to_string().c_str(),
					_values->to_string().c_str());
		}
		return;
	}

	// GetLinks (and the like) are evaluated dynamically, later.
	if (classserver().isA(vtype, SATISFYING_LINK))
		return;

	// If its part of a signature, there is nothing to do.
	if (TYPE_NODE == vtype or TYPE_CHOICE == vtype)
		return;

	// The only remaining possibility is that there is set of ListLinks.
	if (SET_LINK != vtype)
		throw InvalidParamException(TRACE_INFO,
			"PutLink was expecting a ListLink, SetLink or GetLink!");

	if (1 < sz)
	{
		for (const Handle& h : valley->getOutgoingSet())
		{
			// If the arity is greater than one, then the values must be in a list.
		   if (h->get_type() != LIST_LINK)
				throw InvalidParamException(TRACE_INFO,
					"PutLink expected value list!");

			if (not _varlist.is_type(h->getOutgoingSet()))
				throw InvalidParamException(TRACE_INFO,
					"PutLink bad value list!");
		}
		return;
	}

	// If the arity is one, the values must obey type constraint.
	for (const Handle& h : valley->getOutgoingSet())
	{
		if (not _varlist.is_type(h))
			throw InvalidParamException(TRACE_INFO,
					"PutLink bad type!");
	}
}

/* ================================================================= */

static inline Handle reddy(RewriteLinkPtr subs, const HandleSeq& oset)
{
	subs->make_silent(true);
	return subs->beta_reduce(oset);
}

/**
 * Perform the actual beta reduction --
 *
 * Substitute values for the variables in the pattern tree.
 * This is a lot like applying the function fun to the argument list
 * args, except that no actual evaluation is performed; only
 * substitution.  The resulting tree is NOT placed into any atomspace,
 * either. If you want that, you must do it youself.  If you want
 * evaluation or execution to happen during or after sustitution, use
 * either the EvaluationLink, the ExecutionOutputLink, or the Instantiator.
 *
 * So, for example, if this PutLink looks like this:
 *
 *   PutLink
 *      EvaluationLink
 *         PredicateNode "is a kind of"
 *         ListLink
 *            VariableNode $a
 *            ConceptNode "hot patootie"
 *      ConceptNode "cowpie"
 *
 * then the reduced value will be
 *
 *   EvaluationLink
 *      PredicateNode "is a kind of"
 *      ListLink
 *         ConceptNode "cowpie"
 *         ConceptNode "hot patootie"
 *
 * Type checking is performed during substitution; if the values fail to
 * have the desired types, no substitution is performed.  In this case,
 * an undefined handle is returned. For set substitutions, this acts as
 * a filter, removing (filtering out) the mismatched types.
 *
 * Again, only a substitution is performed, there is no execution or
 * evaluation.  Note also that the resulting tree is NOT placed into
 * any atomspace!
 */
Handle PutLink::do_reduce(void) const
{
	Handle bods(_body);
	Variables vars(_varlist);
	RewriteLinkPtr subs(RewriteLinkCast(get_handle()));

	// Resolve the body, if needed. That is, if the body is
	// given in a defintion, get that defintion.
	Type btype = _body->get_type();
	if (DEFINED_SCHEMA_NODE == btype or
	    DEFINED_PREDICATE_NODE == btype)
	{
		bods = DefineLink::get_definition(bods);
		btype = bods->get_type();
		// XXX TODO we should perform a type-check on the function.
		if (not classserver().isA(btype, LAMBDA_LINK))
			throw InvalidParamException(TRACE_INFO,
					"Expecting a LambdaLink, got %s",
			      bods->to_string().c_str());
	}

	// If the body is a lambda, work with that.
	if (classserver().isA(btype, LAMBDA_LINK))
	{
		LambdaLinkPtr lam(LambdaLinkCast(bods));
		bods = lam->get_body();
		vars = lam->get_variables();
		btype = bods->get_type();
		subs = lam;
	}

	// Now get the values that we will plug into the body.
	Handle valley = _values;
	Type vtype = valley->get_type();

	// If the value is a LambdaLink, try to see if its eta-reducible.
	// If so, then eta-reduce it immediately. A LambdaLink is
	// eta-reducible when it's body is a ListLink.
	if (LAMBDA_LINK == vtype)
	{
		LambdaLinkPtr lam(LambdaLinkCast(_values));
		Handle eta = lam->get_body();
		Type bt = eta->get_type();
		if (LIST_LINK == bt)
		{
			valley = eta;
			vtype = bt;
		}
	}

	size_t nvars = vars.varseq.size();

	// FunctionLinks behave like combinators; that is, one can create
	// valid beta-redexes with them. We handle that here.
	//
	// XXX At this time, we don't know the number of arguments any
	// given FunctionLink might take.  Atomese does have the mechanisms
	// to declare these, including arbitrary-arity functions, its
	// just that its currently not declared anywhere for any of the
	// FunctionLinks.  So we just punt.  Example usage:
	// (cog-execute! (Put (Plus) (List (Number 2) (Number 2))))
	// (cog-execute! (Put (Plus (Number 9)) (List (Number 2) (Number 2))))
	if (0 == nvars and classserver().isA(btype, FUNCTION_LINK))
	{
		if (LIST_LINK == vtype)
		{
			HandleSeq oset(bods->getOutgoingSet());
			const HandleSeq& rest = valley->getOutgoingSet();
			oset.insert(oset.end(), rest.begin(), rest.end());
			return createLink(oset, btype);
		}

		if (SET_LINK != vtype)
		{
			HandleSeq oset(bods->getOutgoingSet());
			oset.emplace_back(valley);
			return createLink(oset, btype);
		}

		// If the values are given in a set, then iterate over the set...
		HandleSeq bset;
		for (const Handle& h : valley->getOutgoingSet())
		{
			if (LIST_LINK == h->get_type())
			{
				HandleSeq oset(bods->getOutgoingSet());
				const HandleSeq& rest = h->getOutgoingSet();
				oset.insert(oset.end(), rest.begin(), rest.end());
				bset.emplace_back(createLink(oset, btype));
			}
			else
			{
				HandleSeq oset(bods->getOutgoingSet());
				oset.emplace_back(h);
				bset.emplace_back(createLink(oset, btype));
			}
		}
		return createLink(bset, SET_LINK);
	}

	// If there is only one variable in the PutLink body...
	if (1 == nvars)
	{
		if (SET_LINK != vtype)
		{
			HandleSeq oset;
			oset.emplace_back(valley);
			try
			{
				// return vars.substitute(bods, oset, /* silent */ true);
				return reddy(subs, oset);
			}
			catch (const TypeCheckException& ex)
			{
				return Handle::UNDEFINED;
			}
		}

		// If the values are given in a set, then iterate over the set...
		HandleSeq bset;
		for (const Handle& h : valley->getOutgoingSet())
		{
			HandleSeq oset;
			oset.emplace_back(h);
			try
			{
				// bset.emplace_back(vars.substitute(bods, oset, /* silent */ true));
				bset.emplace_back(reddy(subs, oset));
			}
			catch (const TypeCheckException& ex) {}
		}
		return createLink(bset, SET_LINK);
	}

	// If we are here, then there are multiple variables in the body.
	// See how many values there are.  If the values are a ListLink,
	// then assume that there is only a single set of values to plug in.
	if (LIST_LINK == vtype)
	{
		const HandleSeq& oset = valley->getOutgoingSet();
		try
		{
			// return vars.substitute(bods, oset, /* silent */ true);
			return reddy(subs, oset);
		}
		catch (const TypeCheckException& ex)
		{
			return Handle::UNDEFINED;
		}
	}

	// If we are here, then there are multiple values.
	// These  MUST be given to us as a SetLink.
	OC_ASSERT(SET_LINK == vtype,
		"Should have caught this earlier, in the ctor");

	HandleSeq bset;
	for (const Handle& h : valley->getOutgoingSet())
	{
		const HandleSeq& oset = h->getOutgoingSet();
		try
		{
			// bset.emplace_back(vars.substitute(bods, oset, /* silent */ true));
			bset.emplace_back(reddy(subs, oset));
		}
		catch (const TypeCheckException& ex) {}
	}
	return createLink(bset, SET_LINK);
}

Handle PutLink::reduce(void)
{
	return do_reduce();
}

DEFINE_LINK_FACTORY(PutLink, PUT_LINK)

/* ===================== END OF FILE ===================== */
<|endoftext|>


<commit_before>/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4      *
*                (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS                    *
*                                                                             *
* This library is free software; you can redistribute it and/or modify it     *
* under the terms of the GNU Lesser General Public License as published by    *
* the Free Software Foundation; either version 2.1 of the License, or (at     *
* your option) any later version.                                             *
*                                                                             *
* This library is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
* for more details.                                                           *
*                                                                             *
* You should have received a copy of the GNU Lesser General Public License    *
* along with this library; if not, write to the Free Software Foundation,     *
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.          *
*******************************************************************************
*                               SOFA :: Modules                               *
*                                                                             *
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/
#include <sofa/helper/system/config.h>
#include <sofa/component/initComponentMisc.h>

namespace sofa
{

namespace component
{

void initComponentMisc()
{
    static bool first = true;
    if (first)
    {
        first = false;
    }
}

//SOFA_LINK_CLASS(ParallelCollisionPipeline)
//SOFA_LINK_CLASS(TriangleModelInRegularGrid)
//SOFA_LINK_CLASS(ContinuousTriangleIntersection)
//SOFA_LINK_CLASS(RigidContactMapper)
//SOFA_LINK_CLASS(RuleBasedContactMapper)
SOFA_LINK_CLASS(DiscreteBeamBsplineMapping)
SOFA_LINK_CLASS(TreeCollisionGroupManager)
SOFA_LINK_CLASS(MatrixMass)
SOFA_LINK_CLASS(MeshMatrixMass)
SOFA_LINK_CLASS(FastTetrahedralCorotationalForceField)
SOFA_LINK_CLASS(LennardJonesForceField)
SOFA_LINK_CLASS(TetrahedralTensorMassForceField)
SOFA_LINK_CLASS(WashingMachineForceField)
SOFA_LINK_CLASS(BeamLinearMapping)
SOFA_LINK_CLASS(CenterPointMechanicalMapping)
SOFA_LINK_CLASS(CenterOfMassMapping)
SOFA_LINK_CLASS(CenterOfMassMultiMapping)
SOFA_LINK_CLASS(CenterOfMassMulti2Mapping)
SOFA_LINK_CLASS(CurveMapping)
SOFA_LINK_CLASS(ExternalInterpolationMapping)
SOFA_LINK_CLASS(SubsetMultiMapping)
SOFA_LINK_CLASS(TubularMapping)
SOFA_LINK_CLASS(VoidMapping)
SOFA_LINK_CLASS(Distances)
SOFA_LINK_CLASS(ManifoldEdgeSetGeometryAlgorithms)
SOFA_LINK_CLASS(ManifoldEdgeSetTopologyAlgorithms)
SOFA_LINK_CLASS(ManifoldEdgeSetTopologyContainer)
SOFA_LINK_CLASS(ManifoldEdgeSetTopologyModifier)
SOFA_LINK_CLASS(ManifoldTriangleSetTopologyContainer)
SOFA_LINK_CLASS(ManifoldTriangleSetTopologyModifier)
SOFA_LINK_CLASS(ManifoldTriangleSetTopologyAlgorithms)
//SOFA_LINK_CLASS(ManifoldTetrahedronSetTopologyContainer)
//SOFA_LINK_CLASS(IndexedMapTopology)
SOFA_LINK_CLASS(MeshTetraStuffing)
SOFA_LINK_CLASS(TopologicalChangeProcessor)
//SOFA_LINK_CLASS(Triplet)
//SOFA_LINK_CLASS(SubdivisionCell)
//SOFA_LINK_CLASS(TopologyChangeTest)
//SOFA_LINK_CLASS(BoundingBox)
//SOFA_LINK_CLASS(TagRule)
//SOFA_LINK_CLASS(ParallelCGLinearSolver)
//SOFA_LINK_CLASS(DampVelocitySolver)
SOFA_LINK_CLASS(NewmarkImplicitSolver)

#ifdef SOFA_DEV
SOFA_LINK_CLASS(ContinuousIntersection)
SOFA_LINK_CLASS(TestDetection)
SOFA_LINK_CLASS(BarycentricStickContact)
SOFA_LINK_CLASS(BglCollisionGroupManager)
SOFA_LINK_CLASS(ArborisDescription)
SOFA_LINK_CLASS(ShapeMatchingForceField)
SOFA_LINK_CLASS(VectorField)
SOFA_LINK_CLASS(ArborisMapping)
SOFA_LINK_CLASS(CircumcenterMapping)
SOFA_LINK_CLASS(DeformableOnRigidFrameMapping)
SOFA_LINK_CLASS(PCAOnRigidFrameMapping)
SOFA_LINK_CLASS(Triangle2DFEMForceField)
SOFA_LINK_CLASS(TriangleBendingFEMForceField)
SOFA_LINK_CLASS(FluidSolidInteractionForceField)
SOFA_LINK_CLASS(DistanceOnGrid)
SOFA_LINK_CLASS(Edge2TetraTopologicalMapping)
SOFA_LINK_CLASS(TriangleSubdivisionTopologicalMapping)
SOFA_LINK_CLASS(SpringIt)
SOFA_LINK_CLASS(GraphScenePartionner)
SOFA_LINK_CLASS(MappedBeamToTetraForceField)
SOFA_LINK_CLASS(FlowVisualModel)
SOFA_LINK_CLASS(NewtonEulerImplicit)
#endif

} // namespace component

} // namespace sofa
<commit_msg>r11157/sofa-dev : CHANGE: desactive components: FluidSolidInteractionForceField, FlowVisualModel and VectorField for the moment as manifold topologies are going to be moved in a plugin.<commit_after>/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4      *
*                (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS                    *
*                                                                             *
* This library is free software; you can redistribute it and/or modify it     *
* under the terms of the GNU Lesser General Public License as published by    *
* the Free Software Foundation; either version 2.1 of the License, or (at     *
* your option) any later version.                                             *
*                                                                             *
* This library is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
* for more details.                                                           *
*                                                                             *
* You should have received a copy of the GNU Lesser General Public License    *
* along with this library; if not, write to the Free Software Foundation,     *
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.          *
*******************************************************************************
*                               SOFA :: Modules                               *
*                                                                             *
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/
#include <sofa/helper/system/config.h>
#include <sofa/component/initComponentMisc.h>

namespace sofa
{

namespace component
{

void initComponentMisc()
{
    static bool first = true;
    if (first)
    {
        first = false;
    }
}

//SOFA_LINK_CLASS(ParallelCollisionPipeline)
//SOFA_LINK_CLASS(TriangleModelInRegularGrid)
//SOFA_LINK_CLASS(ContinuousTriangleIntersection)
//SOFA_LINK_CLASS(RigidContactMapper)
//SOFA_LINK_CLASS(RuleBasedContactMapper)
SOFA_LINK_CLASS(DiscreteBeamBsplineMapping)
SOFA_LINK_CLASS(TreeCollisionGroupManager)
SOFA_LINK_CLASS(MatrixMass)
SOFA_LINK_CLASS(MeshMatrixMass)
SOFA_LINK_CLASS(FastTetrahedralCorotationalForceField)
SOFA_LINK_CLASS(LennardJonesForceField)
SOFA_LINK_CLASS(TetrahedralTensorMassForceField)
SOFA_LINK_CLASS(WashingMachineForceField)
SOFA_LINK_CLASS(BeamLinearMapping)
SOFA_LINK_CLASS(CenterPointMechanicalMapping)
SOFA_LINK_CLASS(CenterOfMassMapping)
SOFA_LINK_CLASS(CenterOfMassMultiMapping)
SOFA_LINK_CLASS(CenterOfMassMulti2Mapping)
SOFA_LINK_CLASS(CurveMapping)
SOFA_LINK_CLASS(ExternalInterpolationMapping)
SOFA_LINK_CLASS(SubsetMultiMapping)
SOFA_LINK_CLASS(TubularMapping)
SOFA_LINK_CLASS(VoidMapping)
SOFA_LINK_CLASS(Distances)
//SOFA_LINK_CLASS(IndexedMapTopology)
SOFA_LINK_CLASS(MeshTetraStuffing)
SOFA_LINK_CLASS(TopologicalChangeProcessor)
//SOFA_LINK_CLASS(Triplet)
//SOFA_LINK_CLASS(SubdivisionCell)
//SOFA_LINK_CLASS(TopologyChangeTest)
//SOFA_LINK_CLASS(BoundingBox)
//SOFA_LINK_CLASS(TagRule)
//SOFA_LINK_CLASS(ParallelCGLinearSolver)
//SOFA_LINK_CLASS(DampVelocitySolver)
SOFA_LINK_CLASS(NewmarkImplicitSolver)

#ifdef SOFA_DEV
SOFA_LINK_CLASS(ContinuousIntersection)
SOFA_LINK_CLASS(TestDetection)
SOFA_LINK_CLASS(BarycentricStickContact)
SOFA_LINK_CLASS(BglCollisionGroupManager)
SOFA_LINK_CLASS(ArborisDescription)
SOFA_LINK_CLASS(ShapeMatchingForceField)
//SOFA_LINK_CLASS(VectorField)
SOFA_LINK_CLASS(ArborisMapping)
SOFA_LINK_CLASS(CircumcenterMapping)
SOFA_LINK_CLASS(DeformableOnRigidFrameMapping)
SOFA_LINK_CLASS(PCAOnRigidFrameMapping)
SOFA_LINK_CLASS(Triangle2DFEMForceField)
SOFA_LINK_CLASS(TriangleBendingFEMForceField)
//SOFA_LINK_CLASS(FluidSolidInteractionForceField)
SOFA_LINK_CLASS(DistanceOnGrid)
SOFA_LINK_CLASS(Edge2TetraTopologicalMapping)
SOFA_LINK_CLASS(TriangleSubdivisionTopologicalMapping)
SOFA_LINK_CLASS(SpringIt)
SOFA_LINK_CLASS(GraphScenePartionner)
SOFA_LINK_CLASS(MappedBeamToTetraForceField)
//SOFA_LINK_CLASS(FlowVisualModel)
SOFA_LINK_CLASS(NewtonEulerImplicit)
#endif

} // namespace component

} // namespace sofa
<|endoftext|>