diff --git a/CAVE/NOMADCaveT/CMakeLists.txt b/CAVE/NOMADCaveT/CMakeLists.txt
index fa9be2b500267b6f7e2b7c1f905ec2dd635c527d..e80bf158a84aa9933d998e8608b150d4792bf354 100644
--- a/CAVE/NOMADCaveT/CMakeLists.txt
+++ b/CAVE/NOMADCaveT/CMakeLists.txt
@@ -61,7 +61,8 @@ set(SOURCES
 	src/NOMADVRLib/polyhedron.cpp   src/NOMADVRLib/UnitCellShaders.cpp
 	src/NOMADVRLib/atomsGL.cpp	src/NOMADVRLib/ConfigFile.cpp	
 	src/NOMADVRLib/TessShaders.cpp	src/NOMADVRLib/IsosurfacesGL.cpp
-	src/NOMADVRLib/IsoShaders.cpp
+	src/NOMADVRLib/IsoShaders.cpp	src/NOMADVRLib/Grid.cpp
+	src/NOMADVRLib/markerShaders.cpp
 	src/happyhttp/happyhttp.cpp
 	src/rply/rply.c
 )
diff --git a/CAVE/NOMADCaveT/src/main.cpp b/CAVE/NOMADCaveT/src/main.cpp
index 96898c5ef56aaebd08f2283b604c7043501d6ec1..ba5d14a3243bba5ffae9867f500da56f614d3888 100644
--- a/CAVE/NOMADCaveT/src/main.cpp
+++ b/CAVE/NOMADCaveT/src/main.cpp
@@ -1,3 +1,4 @@
+#define GLM_SWIZZLE
 #include <string>
 
 #include <m3drenderer.h>
@@ -87,6 +88,8 @@ private:
 TextRendering::Text text;
 
 GLuint PointVAO, PointVBO;
+m3d::ShaderLoader sPoint;
+bool initGLSelectedPoints();
 
 std::shared_ptr<synchlib::SynchObject<int> > m_pCurrentDataPosSyncher;
 std::shared_ptr<synchlib::SynchObject<int> > m_pCurrentDataTimeSyncher;
@@ -106,7 +109,7 @@ int error=0;
 GLuint textures[2]; // white, atoms
 GLuint textDepthPeeling[ZLAYERS+2]; 
 GLuint peelingFramebuffer;
-unsigned int geo[0]; //window width, height
+unsigned int geo[2]; //window width, height
 	//if no tesselation is available, we still need the tess atoms for the trajectories!
 GLuint *AtomTVAO=nullptr, *AtomTBuffer=nullptr, BondIndices=0,
 	*AtomVAO=nullptr, *AtomBuffer=nullptr, *AtomIndices=nullptr,//[2], atoms, extraatoms
@@ -130,9 +133,36 @@ void RenderUnitCell(const glm::mat4 eyeViewProjection);
 void RenderAtomTrajectoriesUnitCell();
 void RenderAtomTrajectories(const glm::mat4 eyeViewProjection);
 void RenderIsos(const glm::mat4 eyeViewProjection, int curDataPos);
+void RenderText(const glm::mat4 pvmat, int curDataPos, int timestep,
+	const SelectedPoints& selectedPoints);
+void RenderPoints(const glm::mat4 pvmat, const glm::vec3 translation, 
+	const SelectedPoints& selectedPoints);
 };
 
 
+bool sceneManager::initGLSelectedPoints() {
+//for painting selected points 
+
+GLenum e;
+
+glGenVertexArrays(1, &PointVAO);
+glGenBuffers(1, &PointVBO);
+glBindVertexArray(PointVAO);
+glBindBuffer(GL_ARRAY_BUFFER, PointVBO);
+//x y z r g b (6), 5 points (wand + 4 selected points)
+glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 5, NULL, GL_DYNAMIC_DRAW);
+glEnableVertexAttribArray(0);
+glEnableVertexAttribArray(1);
+glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), 0);
+glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), 
+	(char*)0+3*sizeof(float));
+glBindBuffer(GL_ARRAY_BUFFER, 0);
+glBindVertexArray(0);    
+e = glGetError();
+return (e==GL_NO_ERROR); 
+}
+
+
 sceneManager::sceneManager(m3d::Renderer* ren, synchlib::renderNode* node,
 	const char *c, const unsigned int geometry[2]){
 GLenum err;
@@ -154,7 +184,12 @@ GLenum err;
    m_scalematSky = glm::scale(m_scalematSky,glm::vec3(0.05,0.05,0.05));
 	
 	std::string s(configFile);
-	chdir(s.substr(0, s.find_last_of("\\/")).c_str());
+   int e;
+	e=chdir(s.substr(0, s.find_last_of("\\/")).c_str());
+	if (e!=0) {
+		fprintf (stderr, "Could not change to directory of config "
+			"file, relative paths may fail\n");
+	}
 
 	if ((error=loadConfigFile(configFile))<0) {
 		if (-100<error) {
@@ -171,8 +206,23 @@ GLenum err;
 		}
 	}
 
+e=glGetError();
+if (e!=GL_NO_ERROR) {
+	eprintf ("OpenGL error after loading config file %d", e);
+	error=-409;
+}
 glGenTextures(2, textures);
+e=glGetError();
+if (e!=GL_NO_ERROR) {
+	eprintf ("Depth-peeling, glGenTextures 1 OpenGL error %d", e);
+	error=-410;
+}
 glGenTextures(2+ZLAYERS, textDepthPeeling);
+e=glGetError();
+if (e!=GL_NO_ERROR) {
+	eprintf ("Depth-peeling, glGenTextures 2 OpenGL error %d", e);
+	error=-411;
+}
 
     //white
     unsigned char data2[4]={255,255,255,255}; //white texture for non-textured meshes
@@ -201,8 +251,7 @@ glGenTextures(2+ZLAYERS, textDepthPeeling);
 	};
 
 	//atom texture
-	int e;
-	
+
 	e=atomTexture(textures[1]);
 	if (e!=GL_NO_ERROR) {
 		eprintf ("atomTexture error %d", e);
@@ -211,13 +260,11 @@ glGenTextures(2+ZLAYERS, textDepthPeeling);
 
 bool er;
 
-
 	e=SetupAtoms(&AtomTVAO, &AtomTBuffer, &BondIndices);
 	if (e!=GL_NO_ERROR) {
 		eprintf ("SetupAtoms error %d", e);
 		error=-404;
 	}
-
 	if (!hasTess)
 		e=SetupAtomsNoTess(&AtomVAO, &AtomBuffer, &AtomIndices);
 
@@ -230,7 +277,6 @@ bool er;
 		eprintf ("SetupUnitCell error %d", e);
 		error=-406;
 	}
-
 //now isosurfaces
 	if (ISOS) {
 		er=::SetupDepthPeeling(geo[0], geo[1], ZLAYERS, 
@@ -273,24 +319,50 @@ bool er;
 		for (int p = 0; p < TIMESTEPS*ISOS; p++) {
 			sprintf(tmpname, "%s%d-%s.ply", PATH, timestep, 
 				plyfiles[p % ISOS]);
-		//add the rotateX(-90)
-			glm::mat4 trans;
-			trans=glm::rotate(trans, (float)-M_PI_2, glm::vec3(1.f,0.f,0.f));
-			glm::mat4 matFinal;
-			matFinal=glm::translate(matFinal, 
-				glm::vec3(translations[p%ISOS][0],
-					translations[p%ISOS][1],
-					translations[p%ISOS][2]));
-			matFinal=trans*matFinal;
 			float mat[16];
-			for (int i=0;i<4;i++)
-				for (int j=0;j<4;j++)
-					mat[j*4+i]=matFinal[j][i];
+			glm::mat4 matFinal, matcubetrans, mvs, sc, sctrans;
+			if (voxelSize[0]!=-1) {
+//We scale in RenderUnitCell by 5 so that atoms look large enough on the cave. 
+//So we need to scale here as well.
+				mvs=glm::scale(mvs, glm::vec3(5.0f / (float)voxelSize[0], 
+						5.0f / (float)voxelSize[1], 
+						5.0f / (float)voxelSize[2]));
+				matcubetrans=glm::translate(matcubetrans,glm::vec3(cubetrans[0], 
+					cubetrans[1], cubetrans[2]));
+				glm::mat4 abcm (abc[0][0], abc[0][1], abc[0][2], 0,
+					abc[1][0], abc[1][1], abc[1][2], 0,
+					abc[2][0], abc[2][1], abc[2][2], 0,
+					0, 0, 0, 1);
+
+				sc=glm::scale(sc, glm::vec3(supercell[0], supercell[1], supercell[2]));
+				
+				sctrans=glm::translate(sctrans, glm::vec3(-translations[p%ISOS][2], 
+					-translations[p%ISOS][1], -translations[p%ISOS][0]));
+
+				matFinal = abcm*sctrans*sc*mvs;
+				for (int i=0;i<4;i++)
+					for (int j=0;j<4;j++)
+						mat[j*4+i]=matFinal[j][i];
+			} else {
+				glm::mat4 trans;
+				trans=glm::rotate(trans, (float)-M_PI_2, glm::vec3(1.f,0.f,0.f));
+				glm::mat4 matFinal;
+				matFinal=glm::translate(matFinal, 
+					glm::vec3(translations[p%ISOS][0],
+						translations[p%ISOS][1],
+						translations[p%ISOS][2]));
+				matFinal=trans*matFinal;
+				
+				for (int i=0;i<4;i++)
+					for (int j=0;j<4;j++)
+						mat[j*4+i]=matFinal[j][i];			
+			}
+
 			if (!AddModelToScene(mat, vertices, indices,
 				tmpname, false, isocolours[p%ISOS][0]<0, p%ISOS))
 			{
 				eprintf("Error loading ply file %s\n", tmpname);
-				//return; 
+				//return; //rgh: files may be missing if no isos are available
 			}
 #ifndef INDICESGL32
 			if (vertices.size() > 65535 * numComponents)
@@ -313,22 +385,13 @@ bool er;
 			}
 		}
 	}
-//for painting selected points 
-	glGenVertexArrays(1, &PointVAO);
-	glGenBuffers(1, &PointVBO);
-glBindVertexArray(PointVAO);
-glBindBuffer(GL_ARRAY_BUFFER, PointVBO);
-//x y z r g b (6), 5 points (wand + 4 selected points)
-glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 5, NULL, GL_DYNAMIC_DRAW);
-glEnableVertexAttribArray(0);
-glEnableVertexAttribArray(1);
-glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), 0);
-glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), 
-	(char*)0+3*sizeof(float));
-glBindBuffer(GL_ARRAY_BUFFER, 0);
-glBindVertexArray(0);      
 
-//end for painting selected points
+	if (!initGLSelectedPoints())
+		std::cout<< "initGLSelectedPoints failed" << std::endl;
+
+	if (!sPoint.loadShadersFromFiles(SHADERPATH "points.vert", SHADERPATH "points.frag"))
+	std::cerr << __FUNCTION__<<
+			"Loading of points shader failed" << std::endl;
 
 	M3DFileIO fileio;
 	std::shared_ptr<Node> UploadingRoot = Node::create();
@@ -409,22 +472,27 @@ m_node->getSceneTrafo(st);
 //rgh FIXME: cache these numbers, do not calculate twice per frame
 if(error)
 	return;
-if (has_abc) {
+
+if (ISOS) {
+	RenderIsos(pvmat*st, curDataPos);
+} else if (has_abc) {
 	RenderUnitCell(pvmat*st);
 } else {
 	//atom trajectories
 	RenderAtomTrajectories(pvmat*st);
 }
 
-if (ISOS)
-	RenderIsos(pvmat*st, curDataPos);
 
 
+RenderText(pvmat, curDataPos, m_oldTime, selectedPoints);
+
 if ((err = glGetError()) != GL_NO_ERROR) 
 	eprintf ("end of glDraw, error %d\n", err);
 
 }
 
+
+
 void sceneManager::displayFunction(){
 	GLenum err;
 	while ((err = glGetError()) != GL_NO_ERROR) {
@@ -886,6 +954,85 @@ for (unsigned int i=0;i<atomtrajectories.size();i++) {
 } //sceneManager::RenderAtomTrajectoriesUnitCell()
 
 
+void sceneManager::RenderText(const glm::mat4 pvmat, int curDataPos, int timestep,
+	const SelectedPoints& selectedPoints)
+{
+glm::mat4 wand;
+m_node->getWandTrafo(wand);
+//rgh FIXME this should be done only once.
+glm::vec3 scale;
+glm::quat rotation;
+glm::vec3 translation;
+glm::vec3 skew;
+glm::vec4 perspective;
+glm::decompose(wand, scale, rotation, translation, skew, perspective);
+
+//rgh FIXME: cache these numbers, do not calculate twice per frame
+
+glm::vec3 b1, b2, b3, n1, n2;
+
+if (selectedPoints.number>=2)
+	b1=selectedPoints.p[1]-selectedPoints.p[0];
+
+if (selectedPoints.number>=3) {
+	b2=selectedPoints.p[1]-selectedPoints.p[2];
+	n1=glm::normalize(b1*b2);
+}
+
+if (selectedPoints.number==4) {
+	b3=selectedPoints.p[3]-selectedPoints.p[2];
+	n2=glm::normalize(b2*b3);
+}
+
+
+
+std::string mystring=std::string("Timestep ")+std::to_string(timestep)+
+	"\nIso "+std::to_string(curDataPos)+"\n";
+
+if (selectedPoints.number==2)
+	mystring+="Distance: "+	std::to_string(glm::length(b1))+" unit cell length";
+else if (selectedPoints.number==3)
+	mystring+="Angle: "+
+		std::to_string(glm::degrees(glm::angle(glm::normalize(b1), glm::normalize(b2))))+"°";
+//http://chemistry.stackexchange.com/questions/40181/when-calculating-a-dihedral-angle-for-a-chemical-bond-how-is-the-direction-defi
+else if (selectedPoints.number==4)
+	mystring+="Dihedral angle: "+		
+		std::to_string(glm::degrees(glm::angle(n1, n2)))+"°";;
+
+text.render (mystring, 0,0, 0.02, glm::vec3(0,0,1), 
+	pvmat*glm::translate(translation));
+
+RenderPoints(pvmat, translation, selectedPoints);
+}
+
+void sceneManager::RenderPoints(const glm::mat4 pvmat, const glm::vec3 translation,
+	const SelectedPoints& selectedPoints)
+{
+glm::mat4 st;
+m_node->getSceneTrafo(st);
+glm::mat4 stinv=glm::inverse(st);
+glm::vec3 finalTranslation=(stinv*glm::vec4(translation, 1)).xyz();
+
+sPoint.begin();
+sPoint.setUniformMatrix("projection", false, pvmat*st);
+glBindVertexArray(PointVAO);
+glBindBuffer(GL_ARRAY_BUFFER, PointVBO);
+float verts[(1+selectedPoints.number)*6];
+float *vp=verts;
+for (int j=0;j<selectedPoints.number;j++) {
+	for(int i=0;i<3;i++)
+		*vp++=selectedPoints.p[j][i];
+	*vp++=0; *vp++=1; *vp++=0; //selected points colour
+}
+for (int i=0;i<3;i++)
+	*vp++=finalTranslation[i];
+*vp++=1; *vp++=0; *vp++=0; //wand point colour
+glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(verts), verts); 
+
+glDrawArrays(GL_LINE_STRIP, 0, 1+selectedPoints.number);
+sPoint.end();
+}
+
 void sceneManager::RenderIsos(const glm::mat4 eyeViewProjection, int curDataPos)
 {
 GLenum e;
@@ -900,9 +1047,15 @@ if (curDataPos!=ISOS) {
 	glUseProgram(ISOP);
 	glUniformMatrix4fv(ISOMatrixLoc, 1, GL_FALSE, t);
 	if ((e = glGetError()) != GL_NO_ERROR)
-		eprintf("Gl error after glUniform4fv 1 RenderUnitCell: %d\n", e);
+		eprintf("Gl error after glUniform4fv 1 RenderIsos: %d\n", e);
 	glBindVertexArray(ISOVAO[m_oldTime*ISOS+curDataPos]);
 	glDrawElements(GL_TRIANGLES,numISOIndices[m_oldTime*ISOS+curDataPos] , GL_UNSIGNED_INT, 0);
+		if (has_abc) {
+			RenderUnitCell(eyeViewProjection);
+		} else {
+			//atom trajectories
+			RenderAtomTrajectories(eyeViewProjection);
+		}
 } else {//transparency
 	glDisable(GL_BLEND);
 	glDepthMask(GL_TRUE);
@@ -918,18 +1071,20 @@ if ((e = glGetError()) != GL_NO_ERROR)
 		glUniformMatrix4fv(TransMatrixLoc, 1, GL_FALSE, t);
 		for (int i=0;i<ISOS;i++) {
 			glBindVertexArray(ISOVAO[m_oldTime*ISOS+i]);
-			glDrawElements(GL_TRIANGLES,numISOIndices[m_oldTime*ISOS+i] , GL_UNSIGNED_INT, 0);	
+			glDrawElements(GL_TRIANGLES,numISOIndices[m_oldTime*ISOS+i], 
+				GL_UNSIGNED_INT, 0);	
+		}
+		if (has_abc) {
+			RenderUnitCell(eyeViewProjection);
+		} else {
+			//atom trajectories
+			RenderAtomTrajectories(eyeViewProjection);
 		}
 	}
 	glUseProgram(BlendP);
 	glBindFramebuffer(GL_FRAMEBUFFER, dfb);
 	glBindVertexArray(BlendVAO);
 	BlendTextures(textDepthPeeling, ZLAYERS);
-//old code for no transparency
-//	for (int i=0;i<ISOS;i++) {
-//		glBindVertexArray(ISOVAO[m_oldTime*ISOS+i]);
-//		glDrawElements(GL_TRIANGLES,numISOIndices[m_oldTime*ISOS+i] , GL_UNSIGNED_INT, 0);		
-//	}
 }
 if ((e = glGetError()) != GL_NO_ERROR)
 	eprintf("Gl error after RenderIsos: %d\n", e);
@@ -985,15 +1140,16 @@ void sceneManager::RenderUnitCell(const glm::mat4 eyeViewProjection)
 					if ((e = glGetError()) != GL_NO_ERROR)
 						eprintf("Gl error after glUniform4fv 2 RenderUnitCell: %d\n", e);
 					glBindVertexArray(UnitCellVAO);
-	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, UnitCellIndexBuffer);
-	if ((e = glGetError()) != GL_NO_ERROR)
-		eprintf("1 Gl error RenderAtom timestep =%d: %d\n", m_oldTime, e);
-	glBindBuffer(GL_ARRAY_BUFFER, UnitCellBuffer);
-	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (const void *)(0));
-	glEnableVertexAttribArray(0);
-	glDisableVertexAttribArray(1);
-	glDisableVertexAttribArray(2);
-	glDisableVertexAttribArray(3);
+					glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, UnitCellIndexBuffer);
+					if ((e = glGetError()) != GL_NO_ERROR)
+						eprintf("1 Gl error RenderAtom timestep =%d: %d\n", m_oldTime, e);
+					glBindBuffer(GL_ARRAY_BUFFER, UnitCellBuffer);
+					glVertexAttribPointer(0, 3, GL_FLOAT, 
+						GL_FALSE, 3 * sizeof(float), (const void *)(0));
+					glEnableVertexAttribArray(0);
+					glDisableVertexAttribArray(1);
+					glDisableVertexAttribArray(2);
+					glDisableVertexAttribArray(3);
 					if ((e = glGetError()) != GL_NO_ERROR)
 						eprintf("Gl error after glBindVertexArray RenderUnitCell: %d\n", e);
 					glDrawElements(GL_LINES, 24, GL_UNSIGNED_INT, 0);
diff --git a/CAVE/NOMADCaveT/src/main_server.cpp b/CAVE/NOMADCaveT/src/main_server.cpp
index f38d018652de8ba754b347e2e8c79ab643b25f3a..c935d9bb77ed86c51661b1fff850380fb152f472 100644
--- a/CAVE/NOMADCaveT/src/main_server.cpp
+++ b/CAVE/NOMADCaveT/src/main_server.cpp
@@ -203,7 +203,7 @@ void sceneManager::buttonEval(){
                 m_swapIsoSurface = 0;
             }
         }
-        if((*it)[0] == 2){ // if button 2 (mode for isosurfaces: both, negative, positive)
+        if((*it)[0] == 2){ // if button 2 (mode for isosurfaces: all, one by one)
             if((*it)[1] == 1){ //if pressed
                 m_oldTimeForSwap = Clock::now();
             	m_pressB0 = true;
diff --git a/NOMADVRLib/atoms.cpp b/NOMADVRLib/atoms.cpp
index 957e559e64a4987cfcfd9ebabd45fc174f3a74ab..3d827964cc808ad9807dabd62010d153f013f84e 100644
--- a/NOMADVRLib/atoms.cpp
+++ b/NOMADVRLib/atoms.cpp
@@ -3,6 +3,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <vector>
+#include <math.h>
 
 #ifdef _MSC_VER
 #include <winsock2.h>