Fix xyz loading, allow loading from web.

Cleanup fixes.
OpenVR: messages on extra thread to avoid interrupting VR.
Option "disablereloadreset" to keep user state on ncfg reload. By default,
resets to timestep 0, userpos from ncfg, etc.
parent f7b78a93
...@@ -86,6 +86,8 @@ int secret; ...@@ -86,6 +86,8 @@ int secret;
const char * server; const char * server;
int port; int port;
bool resetTimestepOnReload;
const char * loadConfigFileErrors[] = const char * loadConfigFileErrors[] =
{ {
"All Ok",//0 "All Ok",//0
...@@ -115,12 +117,34 @@ const char * loadConfigFileErrors[] = ...@@ -115,12 +117,34 @@ const char * loadConfigFileErrors[] =
"Error loading analytics json file, add 400 to see the error",//<-400 "Error loading analytics json file, add 400 to see the error",//<-400
}; };
void cleanMarkers()
{
for (int i=0;i<TIMESTEPS;i++)
delete markers[i];
delete[] markers;
markers=nullptr;
}
void cleanMarkerColours() {
for (int i=0;i<TIMESTEPS;i++)
delete markercolours[i];
delete[] markercolours;
markercolours=nullptr;
}
void updateTIMESTEPS (int timesteps) void updateTIMESTEPS (int timesteps)
{ {
if (TIMESTEPS==0) if (TIMESTEPS==0) {
TIMESTEPS=timesteps; TIMESTEPS=timesteps;
else } else {
TIMESTEPS=std::min(TIMESTEPS, timesteps); if (TIMESTEPS!=timesteps) {
if (markers)
cleanMarkers();
if (markercolours)
cleanMarkerColours();
TIMESTEPS=std::min(TIMESTEPS, timesteps);
}
}
} }
int readString(FILE *f, char *s) int readString(FILE *f, char *s)
...@@ -155,6 +179,9 @@ int readString(FILE *f, char *s) ...@@ -155,6 +179,9 @@ int readString(FILE *f, char *s)
void fixFile(char * file) void fixFile(char * file)
{ {
if (!strncmp (file, "http://", 7) || !strncmp(file, "https://", 8))
return;
#ifdef WIN32 #ifdef WIN32
const char c='\\'; const char c='\\';
#else #else
...@@ -190,13 +217,11 @@ void cleanConfig() ...@@ -190,13 +217,11 @@ void cleanConfig()
SCREENSHOT=nullptr; SCREENSHOT=nullptr;
if (markers) { if (markers) {
cleanMarkers();
for (int i=0;i<TIMESTEPS;i++) { for (int i=0;i<TIMESTEPS;i++) {
delete[] markers[i];
delete[] markercolours[i]; delete[] markercolours[i];
} }
delete[] markers;
delete[] markercolours; delete[] markercolours;
markers=nullptr;
markercolours=nullptr; markercolours=nullptr;
} }
for (int i=0;i<info.size();i++) { for (int i=0;i<info.size();i++) {
...@@ -301,8 +326,10 @@ void initState() ...@@ -301,8 +326,10 @@ void initState()
secret=0; secret=0;
server=nullptr; server=nullptr;
port=-1; port=-1;
resetTimestepOnReload=true;
} }
int loadConfigFile(const char * f) int loadConfigFile(const char * f)
{ {
//default values //default values
...@@ -335,10 +362,7 @@ int loadConfigFile(const char * f) ...@@ -335,10 +362,7 @@ int loadConfigFile(const char * f)
if (!strcmp(s, "timesteps")) { if (!strcmp(s, "timesteps")) {
int timesteps; int timesteps;
r = fscanf(F, "%d", &timesteps); r = fscanf(F, "%d", &timesteps);
if (TIMESTEPS==0) updateTIMESTEPS(timesteps);
TIMESTEPS=timesteps;
else
TIMESTEPS=std::min(TIMESTEPS, timesteps);
} }
else if (!strcmp(s, "isos")) { else if (!strcmp(s, "isos")) {
if (ISOS!=0) if (ISOS!=0)
...@@ -572,6 +596,8 @@ int loadConfigFile(const char * f) ...@@ -572,6 +596,8 @@ int loadConfigFile(const char * f)
fclose(F); fclose(F);
return -17; return -17;
} }
if (markers)
cleanMarkers();
markers=new float* [TIMESTEPS]; markers=new float* [TIMESTEPS];
for (int i=0;i<TIMESTEPS;i++) { for (int i=0;i<TIMESTEPS;i++) {
markers[i]=new float[3]; markers[i]=new float[3];
...@@ -755,6 +781,8 @@ int loadConfigFile(const char * f) ...@@ -755,6 +781,8 @@ int loadConfigFile(const char * f)
#endif #endif
} else if (!strcmp (s, "\x0d")) { //discard windows newline (problem in Sebastian Kokott's phone (?!) } else if (!strcmp (s, "\x0d")) { //discard windows newline (problem in Sebastian Kokott's phone (?!)
continue; continue;
} else if (!strcmp (s, "disablereloadreset")) {
resetTimestepOnReload=false;
} else if (!strcmp (s, "server")) { //multiuser support } else if (!strcmp (s, "server")) { //multiuser support
int r; int r;
if (server) if (server)
......
...@@ -88,6 +88,8 @@ extern int secret; ...@@ -88,6 +88,8 @@ extern int secret;
extern const char * server; extern const char * server;
extern int port; extern int port;
extern bool resetTimestepOnReload;
struct information { struct information {
float pos[3]; float pos[3];
float size; float size;
......
...@@ -38,7 +38,7 @@ grid::~grid() ...@@ -38,7 +38,7 @@ grid::~grid()
void grid::coordinates(const float pos[3], int c[3]) void grid::coordinates(const float pos[3], int c[3])
{ {
for (int i=0;i<3;i++) { for (int i=0;i<3;i++) {
c[i]=floor((pos[i]-m[i])/dif[i]*dims); c[i]=static_cast<int>(floor((pos[i]-m[i])/dif[i]*dims));
if (c[i]>=dims) if (c[i]>=dims)
c[i]=dims-1; c[i]=dims-1;
else if (c[i]<0) else if (c[i]<0)
...@@ -52,7 +52,7 @@ void grid::add (float *p) //compatible with the atoms xyzr ...@@ -52,7 +52,7 @@ void grid::add (float *p) //compatible with the atoms xyzr
coordinates (p, pos); coordinates (p, pos);
content[pos[0]*dims*dims + pos[1]*dims+pos[2]].push_back(p); content[pos[0]*dims*dims + pos[1]*dims+pos[2]].push_back(p);
float ar=atomRadius(p[3]); float ar=atomRadius(static_cast<int>(p[3]));
if (ar>maxradius) if (ar>maxradius)
maxradius=ar; maxradius=ar;
} }
...@@ -61,7 +61,7 @@ bool grid::compare (float *a, float *b) ...@@ -61,7 +61,7 @@ bool grid::compare (float *a, float *b)
{ {
if (a<=b) //already returned when searching a beforehand if (a<=b) //already returned when searching a beforehand
return false; return false;
float sqd=atomRadius(a[3])+atomRadius(b[3]); float sqd=atomRadius(static_cast<int>(a[3]))+atomRadius(static_cast<int>(b[3]));
sqd*=sqd; sqd*=sqd;
float d=0; float d=0;
for (int i=0;i<3;i++) for (int i=0;i<3;i++)
...@@ -82,8 +82,8 @@ std::vector<float*> grid::find (float *p) ...@@ -82,8 +82,8 @@ std::vector<float*> grid::find (float *p)
float mp[3]; float mp[3];
float Mp[3]; float Mp[3];
for (int i=0;i<3;i++) { for (int i=0;i<3;i++) {
mp[i]=p[i]-(atomRadius(p[3])+maxradius)/scale; mp[i]=p[i]-(atomRadius(static_cast<int>(p[3]))+maxradius)/scale;
Mp[i]=p[i]+(atomRadius(p[3])+maxradius)/scale; Mp[i]=p[i]+(atomRadius(static_cast<int>(p[3]))+maxradius)/scale;
} }
coordinates(mp, mc); coordinates(mp, mc);
coordinates(Mp, Mc); coordinates(Mp, Mc);
......
...@@ -227,7 +227,7 @@ int findAtom(const char *const s) ...@@ -227,7 +227,7 @@ int findAtom(const char *const s)
strcpy (x, s); strcpy (x, s);
char *p=x+strlen(x); char *p=x+strlen(x);
p--; p--;
while (*p > '0' && *p <= '9') { while (*p >= '0' && *p <= '9') {
*p='\0'; *p='\0';
*p--; *p--;
if (p==x) if (p==x)
...@@ -260,7 +260,8 @@ const char * readAtomsXYZErrors[] = { ...@@ -260,7 +260,8 @@ const char * readAtomsXYZErrors[] = {
"Could not open file", //-1 "Could not open file", //-1
"Error loading atom type and position line", //-2 "Error loading atom type and position line", //-2
"Atom type unknown", //-3 "Atom type unknown", //-3
"Corrupt xyz file" //-4 "Corrupt xyz file", //-4
"Unable to download xyz file" //-5
}; };
void cleanAtoms (int **numatoms, int timesteps, float ***pos) void cleanAtoms (int **numatoms, int timesteps, float ***pos)
...@@ -276,10 +277,27 @@ void cleanAtoms (int **numatoms, int timesteps, float ***pos) ...@@ -276,10 +277,27 @@ void cleanAtoms (int **numatoms, int timesteps, float ***pos)
int readAtomsXYZ(const char *const file, int **numatoms, int *timesteps, float ***pos) int readAtomsXYZ(const char *const file, int **numatoms, int *timesteps, float ***pos)
{ {
const char *myfile=nullptr;
const char *webdownload="material.xyz";
//add http support
if (!strncmp(file, "http:", 5) || !strncmp(file, "https:", 6)) {
char cmd[2048];
int ret;
sprintf (cmd, "wget %s -O %s", file, webdownload);
ret=system(cmd);
if (ret!=0) {
*numatoms=nullptr;
*pos=nullptr;
return (-5);
}
myfile=webdownload;
} else {
myfile = file;
}
int mynumatoms; int mynumatoms;
std::vector<float*> mypos; std::vector<float*> mypos;
std::vector<int> mynum; std::vector<int> mynum;
FILE *f=fopen (file, "r"); FILE *f=fopen (myfile, "r");
int r; int r;
char s[100]; char s[100];
if (f==0) { if (f==0) {
...@@ -294,6 +312,7 @@ int readAtomsXYZ(const char *const file, int **numatoms, int *timesteps, float * ...@@ -294,6 +312,7 @@ int readAtomsXYZ(const char *const file, int **numatoms, int *timesteps, float *
blanklines++; blanklines++;
if (blanklines>3) { if (blanklines>3) {
eprintf("Corrupt xyz file %s. Error at %d atoms\n", file, mynumatoms); eprintf("Corrupt xyz file %s. Error at %d atoms\n", file, mynumatoms);
fclose(f);
return -4; return -4;
} }
continue; //there may be a blank line at the end of the file continue; //there may be a blank line at the end of the file
...@@ -311,8 +330,10 @@ int readAtomsXYZ(const char *const file, int **numatoms, int *timesteps, float * ...@@ -311,8 +330,10 @@ int readAtomsXYZ(const char *const file, int **numatoms, int *timesteps, float *
char line[512]; char line[512];
fgets(line, 512, f); fgets(line, 512, f);
r=sscanf (line, "%s %f %f %f %f", s, mypos.back()+4*i+0, mypos.back()+4*i+1,mypos.back()+4*i+2, &unused); r=sscanf (line, "%s %f %f %f %f", s, mypos.back()+4*i+0, mypos.back()+4*i+1,mypos.back()+4*i+2, &unused);
if (r<4) if (r<4) {
fclose (f);
return -2; return -2;
}
int a=findAtom(s); int a=findAtom(s);
if (a==-1) { if (a==-1) {
eprintf ("Read atoms xyz, atom type unknown: %s", s); eprintf ("Read atoms xyz, atom type unknown: %s", s);
...@@ -331,7 +352,7 @@ int readAtomsXYZ(const char *const file, int **numatoms, int *timesteps, float * ...@@ -331,7 +352,7 @@ int readAtomsXYZ(const char *const file, int **numatoms, int *timesteps, float *
(*numatoms)[i]=mynum[i]; (*numatoms)[i]=mynum[i];
//eprintf ("Getting atoms, numatoms=%d",(*numatoms)[i]); //eprintf ("Getting atoms, numatoms=%d",(*numatoms)[i]);
} }
fclose (f);
return 0; return 0;
} }
...@@ -367,9 +388,10 @@ int readAtomsCube(const char *const file, int **numatoms, int *timesteps, float ...@@ -367,9 +388,10 @@ int readAtomsCube(const char *const file, int **numatoms, int *timesteps, float
discardline(f); //two comments discardline(f); //two comments
discardline(f); discardline(f);
r = fscanf(f, "%d %f %f %f", *numatoms, cubetrans + 0, cubetrans + 1, cubetrans + 2); r = fscanf(f, "%d %f %f %f", *numatoms, cubetrans + 0, cubetrans + 1, cubetrans + 2);
if (r < 4) if (r < 4) {
fclose (f);
return -2; return -2;
}
//rgh FIXME. Is this always bohr? //rgh FIXME. Is this always bohr?
for (int i=0;i<3;i++) for (int i=0;i<3;i++)
cubetrans[i]*= 0.52918f; cubetrans[i]*= 0.52918f;
......
...@@ -227,6 +227,9 @@ void CleanAtoms (GLuint **AtomVAO /*[4]*/, GLuint **AtomVertBuffer /*[3]*/, GLui ...@@ -227,6 +227,9 @@ void CleanAtoms (GLuint **AtomVAO /*[4]*/, GLuint **AtomVertBuffer /*[3]*/, GLui
if (!numAtoms) if (!numAtoms)
return; return;
if (*AtomVAO==nullptr)
return;
glDeleteVertexArrays(4, *AtomVAO); glDeleteVertexArrays(4, *AtomVAO);
glDeleteBuffers(3, *AtomVertBuffer); glDeleteBuffers(3, *AtomVertBuffer);
glDeleteBuffers(1, BondIndices); glDeleteBuffers(1, BondIndices);
...@@ -764,6 +767,7 @@ GLenum SetupUnitCell(GLuint *UnitCellVAO, GLuint *UnitCellVertBuffer, GLuint *Un ...@@ -764,6 +767,7 @@ GLenum SetupUnitCell(GLuint *UnitCellVAO, GLuint *UnitCellVertBuffer, GLuint *Un
if ((e = glGetError()) != GL_NO_ERROR) if ((e = glGetError()) != GL_NO_ERROR)
eprintf( "opengl error %d, glBufferData index, l %d\n", e, __LINE__); eprintf( "opengl error %d, glBufferData index, l %d\n", e, __LINE__);
glBindVertexArray(0); glBindVertexArray(0);
delete[] tmp;
return e; return e;
} }
......
...@@ -351,6 +351,7 @@ private: // OpenGL bookkeeping ...@@ -351,6 +351,7 @@ private: // OpenGL bookkeeping
char *pixels, *pixels2; //for saving screenshots to disk char *pixels, *pixels2; //for saving screenshots to disk
int framecounter; int framecounter;
bool savetodisk; bool savetodisk;
bool showInfoBox;
int selectedAtom; int selectedAtom;
...@@ -370,7 +371,7 @@ private: // OpenGL bookkeeping ...@@ -370,7 +371,7 @@ private: // OpenGL bookkeeping
void UDP(); void UDP();
int UDPContr(SOCKET s, char c, int device); int UDPContr(SOCKET s, char c, int device);
int UDPHead(SOCKET s); int UDPHead(SOCKET s);
int sock, s; SOCKET sock, s;
struct sockaddr_in serv_addr; struct sockaddr_in serv_addr;
void Send(char c, int32_t value); void Send(char c, int32_t value);
void Send(char c, bool value); void Send(char c, bool value);
...@@ -417,6 +418,10 @@ void dprintf( const char *fmt, ... ) ...@@ -417,6 +418,10 @@ void dprintf( const char *fmt, ... )
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "Warning", buffer, 0); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "Warning", buffer, 0);
} }
void message (char *buffer)
{
MessageBoxA (0, buffer, "Warning", 0);
}
//pure windows, no sdl //pure windows, no sdl
void eprintf( const char *fmt, ... ) void eprintf( const char *fmt, ... )
...@@ -439,7 +444,7 @@ void eprintf( const char *fmt, ... ) ...@@ -439,7 +444,7 @@ void eprintf( const char *fmt, ... )
if ( g_bPrintf ) if ( g_bPrintf )
printf( "%s", buffer ); printf( "%s", buffer );
MessageBoxA(0, buffer, "Warning", 0); std::thread(message, buffer).detach();
} }
int CMainApplication::LoadConfigFile (const char *c) int CMainApplication::LoadConfigFile (const char *c)
...@@ -689,7 +694,10 @@ void CMainApplication::connectTCP() ...@@ -689,7 +694,10 @@ void CMainApplication::connectTCP()
eprintf ("short read at socket\n"); eprintf ("short read at socket\n");
return; return;
} }
currentset=ntohl(tmp)%TIMESTEPS; if (TIMESTEPS!=0)
currentset=ntohl(tmp)%TIMESTEPS;
else
currentset=ntohl(tmp);
break; break;
case 'i': case 'i':
n=recv (sock, (char*)&tmp, sizeof(tmp), 0); n=recv (sock, (char*)&tmp, sizeof(tmp), 0);
...@@ -726,13 +734,6 @@ void CMainApplication::connectTCP() ...@@ -726,13 +734,6 @@ void CMainApplication::connectTCP()
std::unique_lock<std::mutex> lk(cleanupmutex); std::unique_lock<std::mutex> lk(cleanupmutex);
cleanupcond.wait(lk); cleanupcond.wait(lk);
//OpenGL commands only in main thread
//CleanScene();
//LoadConfigFile(myargv[currentConfig]);
//SetupScene();
//cleanupmutex.lock();
//cleanup=0;
//cleanupmutex.unlock();
} }
break; break;
case 'X': case 'X':
...@@ -850,6 +851,8 @@ CMainApplication::CMainApplication(int argc, char *argv[]) ...@@ -850,6 +851,8 @@ CMainApplication::CMainApplication(int argc, char *argv[])
, identifier(0) , identifier(0)
, whenDrag (3) , whenDrag (3)
, cleanup (0) , cleanup (0)
, showInfoBox (true)
, m_unInfoVAO (0)
{ {
LoadConfigFile(argv[currentConfig]); LoadConfigFile(argv[currentConfig]);
for (int j=0;j<3;j++) for (int j=0;j<3;j++)
...@@ -1134,6 +1137,7 @@ void CMainApplication::Shutdown() ...@@ -1134,6 +1137,7 @@ void CMainApplication::Shutdown()
if (tcpconn) { if (tcpconn) {
closesocket(sock); closesocket(sock);
sock=INVALID_SOCKET; sock=INVALID_SOCKET;
cleanupcond.notify_one(); //in case we are waiting for update
tcpconn->join(); tcpconn->join();
delete tcpconn; delete tcpconn;
tcpconn=nullptr; tcpconn=nullptr;
...@@ -1357,7 +1361,11 @@ void CMainApplication::Send(char c, bool value) ...@@ -1357,7 +1361,11 @@ void CMainApplication::Send(char c, bool value)
void CMainApplication::SendConfigFile() void CMainApplication::SendConfigFile()
{ {
Send('n', currentConfig); if (sock != INVALID_SOCKET) {
Send('n', currentConfig);
} else {//notify for local execution
cleanup=1;
}
} }
void CMainApplication::SendTimestep() void CMainApplication::SendTimestep()
...@@ -1501,19 +1509,12 @@ bool CMainApplication::HandleInput() ...@@ -1501,19 +1509,12 @@ bool CMainApplication::HandleInput()
if (currentConfig>=myargc) if (currentConfig>=myargc)
currentConfig=1; currentConfig=1;
SendConfigFile(); SendConfigFile();
//we will soon receive an order to reload the scene, so avoid double-reloading + race here
//CleanScene();
//LoadConfigFile(myargv[currentConfig]);
//SetupScene();
} else if (state.rAxis[0].y < -0.7 && state.rAxis[0].x > -0.4 && state.rAxis[0].x < 0.4) { } else if (state.rAxis[0].y < -0.7 && state.rAxis[0].x > -0.4 && state.rAxis[0].x < 0.4) {
//prev config file //prev config file
currentConfig--; currentConfig--;
if (currentConfig<=0) if (currentConfig<=0)
currentConfig=myargc-1; currentConfig=myargc-1;
SendConfigFile(); SendConfigFile();
//CleanScene();
//LoadConfigFile(myargv[currentConfig]);
//SetupScene();
} }
} }
} else { //Drag and Drop } else { //Drag and Drop
...@@ -1552,8 +1553,12 @@ bool CMainApplication::HandleInput() ...@@ -1552,8 +1553,12 @@ bool CMainApplication::HandleInput()
else if(unDevice !=firstdevice && seconddevice==-1) else if(unDevice !=firstdevice && seconddevice==-1)
seconddevice=unDevice; seconddevice=unDevice;
*/ */
if (firstdevice==unDevice) if (firstdevice==unDevice) {
savetodisk = !savetodisk; if (menubutton==Record)
savetodisk = !savetodisk;
else if (menubutton==Infobox)
showInfoBox= !showInfoBox;
}
else { else {
showAtoms= !showAtoms; showAtoms= !showAtoms;
SendShowAtoms(); SendShowAtoms();
...@@ -1793,7 +1798,6 @@ void CMainApplication::RenderFrame() ...@@ -1793,7 +1798,6 @@ void CMainApplication::RenderFrame()
cleanup=0; cleanup=0;
cleanupmutex.unlock(); cleanupmutex.unlock();
cleanupcond.notify_one(); cleanupcond.notify_one();
//return;
} }
int e; int e;
...@@ -2089,7 +2093,8 @@ void CMainApplication::CleanScene() ...@@ -2089,7 +2093,8 @@ void CMainApplication::CleanScene()
CleanMarker(&m_unMarkerVAO, &m_glMarkerVertBuffer); CleanMarker(&m_unMarkerVAO, &m_glMarkerVertBuffer);
} }
//infocube //infocube
::CleanInfoCube(&m_unInfoVAO, &m_unInfoVertBuffer, &m_unInfoIndexBuffer); if (m_unInfoVAO != 0)
::CleanInfoCube(&m_unInfoVAO, &m_unInfoVertBuffer, &m_unInfoIndexBuffer);
cleanConfig(); cleanConfig();
ISOS=0; ISOS=0;
} }
...@@ -2108,6 +2113,14 @@ void CMainApplication::SetupScene() ...@@ -2108,6 +2113,14 @@ void CMainApplication::SetupScene()
SetupInfoCube(); SetupInfoCube();
movementspeed/=scaling; movementspeed/=scaling;
SetupInfoBoxTexture(); SetupInfoBoxTexture();
SetupCameras(); //near and far plane may have changed
if (resetTimestepOnReload) {
currentset=0;
currentiso=ISOS;
UserPosition=Vector3(-userpos[0], -userpos[1], -userpos[2]);
SendUserPos();
selectedAtom=-1;
}
} }
void CMainApplication::SetupInfoCube() void CMainApplication::SetupInfoCube()
...@@ -2155,7 +2168,7 @@ Matrix4 i = matDeviceToTracking; ...@@ -2155,7 +2168,7 @@ Matrix4 i = matDeviceToTracking;
Matrix4 trans; Matrix4 trans;
Vector3 iPos = (*pos)+Vector3(0,0.02,0); //raise glyph Vector3 iPos = (*pos)+Vector3(0.0f,0.02f,0.0f); //raise glyph
int e; int e;
trans.scale(0.05).translate(iPos); //translate(0,0.1,0); trans.scale(0.05).translate(iPos); //translate(0,0.1,0);
...@@ -2178,31 +2191,31 @@ return true; ...@@ -2178,31 +2191,31 @@ return true;
void FillVerticesGlyph (float * const vert, const int i, const float u) void FillVerticesGlyph (float * const vert, const int i, const float u)
{ {
for (int j=0;j<4;j++) { for (int j=0;j<4;j++) {
vert[i*9*4+j*9+2]=0; //z vert[i*9*4+j*9+2]=0.0f; //z
vert[i*9*4+j*9+3]=1; //w vert[i*9*4+j*9+3]=1.0f; //w
vert[i*9*4+j*9+4]=0; //nx vert[i*9*4+j*9+4]=0.0f; //nx
vert[i*9*4+j*9+5]=0; //ny vert[i*9*4+j*9+5]=0.0f; //ny
vert[i*9*4+j*9+6]=-1; //nz vert[i*9*4+j*9+6]=-1.0f; //nz
}