volmetrics
diff src/main.cc @ 36:1df14c5ffa71
conversion to Qt
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Fri, 06 Feb 2015 22:39:51 +0200 |
parents | df4a277adb82 |
children |
line diff
1.1 --- a/src/main.cc Fri Feb 06 21:15:23 2015 +0200 1.2 +++ b/src/main.cc Fri Feb 06 22:39:51 2015 +0200 1.3 @@ -1,588 +1,11 @@ 1.4 -#include "opengl.h" 1.5 +#include "mainwin.h" 1.6 +#include <QApplication> 1.7 1.8 -#include <math.h> 1.9 -#include <stdio.h> 1.10 -#include <assert.h> 1.11 +int main(int argc, char *argv[]) 1.12 +{ 1.13 + QApplication a(argc, argv); 1.14 + MainWin w; 1.15 + w.show(); 1.16 1.17 -#include <vector> 1.18 - 1.19 -#include "mesh.h" 1.20 -#include "sdr.h" 1.21 -#include "volume.h" 1.22 - 1.23 -static bool init(void); 1.24 -static GLUI *create_ui(void); 1.25 -static void display(void); 1.26 -static void reshape(int x, int y); 1.27 -static void keyboard(unsigned char key, int x, int y); 1.28 -static void keyboard_up(unsigned char key, int x, int y); 1.29 -static void mouse(int button, int state, int x, int y); 1.30 -static void motion(int x, int y); 1.31 -static bool init_xfer(void); 1.32 -static void display_xfer(void); 1.33 -static void reshape_xfer(int x, int y); 1.34 -static void mouse_xfer(int button, int state, int x, int y); 1.35 -static void motion_xfer(int x, int y); 1.36 -static void volume_preview(); 1.37 -static void draw_iso(); 1.38 -static void draw_volume(); 1.39 - 1.40 -static int mainwin_id, xferwin_id; 1.41 -//todo keyb esc 1.42 - 1.43 -static int win_xsz, win_ysz; 1.44 -static float cam_phi, cam_theta, cam_dist = 6; 1.45 -static std::vector<bool> key_state(256); 1.46 - 1.47 -static const char *vol_fname = "data/test1.vol"; 1.48 -static const char *vsdr_path = "data/shaders/transfer.v.glsl"; 1.49 -static const char *fsdr_path = "data/shaders/transfer.f.glsl"; 1.50 -static const char *vsdr_vol_path = "data/shaders/vol.v.glsl"; 1.51 -static const char *fsdr_vol_path = "data/shaders/vol.f.glsl"; 1.52 - 1.53 -static unsigned int sprog; 1.54 -static unsigned int sprog_vol; 1.55 - 1.56 -static Volume *vol; 1.57 -static Mesh *mesh; 1.58 -static float cur_z, thres = 0.5, thres2 = 1.0; 1.59 -static float slice_z = 0.5; 1.60 - 1.61 -static int use_orig_vol_res = 1; 1.62 -static int vol_res[3]; // volume sampling resolution x/y/z 1.63 -static float bound_scale = 1.42; 1.64 - 1.65 -static GLUI *ui; 1.66 - 1.67 -static int num_poly = 128; 1.68 - 1.69 -int main(int argc, char **argv) 1.70 -{ 1.71 - glutInitWindowSize(512, 512); 1.72 - glutInit(&argc, argv); 1.73 - if(argv[1]) 1.74 - vol_fname = argv[1]; 1.75 - 1.76 - if(!init()) { 1.77 - fprintf(stderr, "Failed to initialize program.\n"); 1.78 - return 1; 1.79 - } 1.80 - 1.81 - init_xfer(); 1.82 - 1.83 - glutMainLoop(); 1.84 - return 0; 1.85 + return a.exec(); 1.86 } 1.87 - 1.88 -static bool init() 1.89 -{ 1.90 - glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE); 1.91 - 1.92 - mainwin_id = glutCreateWindow("CT scan"); 1.93 - glutDisplayFunc(display); 1.94 - glutReshapeFunc(reshape); 1.95 - glutKeyboardFunc(keyboard); 1.96 - glutKeyboardUpFunc(keyboard_up); 1.97 - glutMouseFunc(mouse); 1.98 - glutMotionFunc(motion); 1.99 - 1.100 - glewInit(); 1.101 - 1.102 - glEnable(GL_DEPTH_TEST); 1.103 - glEnable(GL_NORMALIZE); 1.104 - 1.105 - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 1.106 - 1.107 - //FIXME shaders setup 1.108 - if(!(sprog = sdr_getprog(vsdr_path, fsdr_path))) 1.109 - { 1.110 - fprintf(stderr, "Failed to create shader program!\n"); 1.111 - return false; 1.112 - } 1.113 - 1.114 - if(!(sprog_vol = sdr_getprog(vsdr_vol_path, fsdr_vol_path))) 1.115 - { 1.116 - fprintf(stderr, "Failed to create shader program!\n"); 1.117 - return false; 1.118 - } 1.119 - 1.120 - vol = new Volume; 1.121 - if(!vol->load(vol_fname)) { 1.122 - fprintf(stderr, "Failed to load %s", vol_fname); 1.123 - return false; 1.124 - } 1.125 - mesh = new Mesh; 1.126 - cur_z = 0.5; 1.127 - 1.128 - vol_res[0] = vol->get_slice(0)->get_width(); 1.129 - vol_res[1] = vol->get_slice(0)->get_height(); 1.130 - vol_res[2] = vol->get_slice_count(); 1.131 - 1.132 - num_poly = std::max(vol_res[0], std::max(vol_res[1], vol_res[2])) * bound_scale; 1.133 - 1.134 - if(!(ui = create_ui())) { 1.135 - return false; 1.136 - } 1.137 - 1.138 - return true; 1.139 -} 1.140 - 1.141 -static GLUI_Spinner *res_spin[3]; 1.142 -static void toggle_use_orig(int id) 1.143 -{ 1.144 - for(int i=0; i<3; i++) { 1.145 - if(use_orig_vol_res) { 1.146 - res_spin[i]->disable(); 1.147 - } else { 1.148 - res_spin[i]->enable(); 1.149 - } 1.150 - } 1.151 -} 1.152 -static void thres_change(int id) 1.153 -{ 1.154 - static float prev_thres = thres; 1.155 - static float prev_thres2 = thres2; 1.156 - 1.157 - if(prev_thres != thres || prev_thres2 != thres2) { 1.158 - prev_thres = thres; 1.159 - prev_thres2 = thres2; 1.160 - 1.161 - mesh->clear(); 1.162 - 1.163 - glutSetWindow(xferwin_id); 1.164 - glutPostRedisplay(); 1.165 - glutSetWindow(mainwin_id); 1.166 - glutPostRedisplay(); 1.167 - } 1.168 -} 1.169 - 1.170 -static void res_change(int id) 1.171 -{ 1.172 - static float prev_resx = vol_res[0]; 1.173 - static float prev_resy = vol_res[1]; 1.174 - static float prev_resz = vol_res[2]; 1.175 - 1.176 - if(prev_resx != vol_res[0] || prev_resy != vol_res[1] || prev_resz != vol_res[2]) { 1.177 - prev_resx = vol_res[0]; 1.178 - prev_resy = vol_res[1]; 1.179 - prev_resz = vol_res[2]; 1.180 - mesh->clear(); 1.181 - } 1.182 -} 1.183 -static GLUI *create_ui() 1.184 -{ 1.185 - GLUI *ui = GLUI_Master.create_glui("ui"); 1.186 - assert(ui); 1.187 - 1.188 - ui->set_main_gfx_window(glutGetWindow()); 1.189 - 1.190 - GLUI_Panel *thres_panel = ui->add_panel("iso thresholds"); 1.191 - 1.192 - GLUI_Spinner *thres_spin = ui->add_spinner_to_panel(thres_panel, "T1", GLUI_SPINNER_FLOAT, &thres, 0, thres_change); 1.193 - thres_spin->set_float_limits(0, 1); 1.194 - 1.195 - GLUI_Spinner *thres2_spin = ui->add_spinner_to_panel(thres_panel, "T2", GLUI_SPINNER_FLOAT, &thres2, 0, thres_change); 1.196 - thres2_spin->set_float_limits(0, 1); 1.197 - 1.198 -#ifdef ISO 1.199 - GLUI_Panel *res_panel = ui->add_panel("volume resolution"); 1.200 - 1.201 - ui->add_checkbox_to_panel(res_panel, "original resolution", &use_orig_vol_res, 0, toggle_use_orig); 1.202 - 1.203 - static const char *res_spin_name[] = {"x", "y", "z"}; 1.204 - for(int i=0; i<3; i++) { 1.205 - res_spin[i] = ui->add_spinner_to_panel(res_panel, res_spin_name[i], GLUI_SPINNER_INT, vol_res + i, 0, res_change); 1.206 - res_spin[i]->set_int_limits(8, 256); // TODO limits as arguments or config 1.207 - res_spin[i]->disable(); 1.208 - } 1.209 -#endif 1.210 - GLUI_Panel *preview_panel = ui->add_panel("volume preview"); 1.211 - 1.212 - GLUI_Spinner *preview_spin = ui->add_spinner_to_panel(preview_panel, "slice z", GLUI_SPINNER_FLOAT, &slice_z, 0); 1.213 - preview_spin->set_float_limits(0, 1); 1.214 - 1.215 - return ui; 1.216 -} 1.217 - 1.218 -static void volume_preview () 1.219 -{ 1.220 - float aspect = win_xsz / win_ysz; 1.221 - 1.222 - glDisable(GL_DEPTH_TEST); 1.223 - 1.224 - glUseProgram(sprog); 1.225 - int tmin_loc = glGetUniformLocation(sprog, "tmin"); 1.226 - if(tmin_loc != -1) 1.227 - glUniform1f(tmin_loc, std::min(thres, thres2)); 1.228 - int tmax_loc = glGetUniformLocation(sprog, "tmax"); 1.229 - if(tmax_loc != -1) 1.230 - glUniform1f(tmax_loc, std::max(thres, thres2)); 1.231 - 1.232 - glMatrixMode(GL_MODELVIEW); 1.233 - glPushMatrix(); 1.234 - glLoadIdentity(); 1.235 - 1.236 - glMatrixMode(GL_PROJECTION); 1.237 - glPushMatrix(); 1.238 - glLoadIdentity(); 1.239 - glOrtho(0, aspect, 0, 1, -1, 1); 1.240 - 1.241 - glBindTexture(GL_TEXTURE_3D, vol->get_texture()); 1.242 - glEnable(GL_TEXTURE_3D); 1.243 - glBegin(GL_QUADS); 1.244 - glColor3f(1.0, 1.0, 1.0); 1.245 - glTexCoord3f(0, 0, slice_z); glVertex3f(0, 1, 0); 1.246 - glTexCoord3f(0, 1, slice_z); glVertex3f(0, 0.75, 0); 1.247 - glTexCoord3f(1, 1, slice_z); glVertex3f(0.25, 0.75, 0); 1.248 - glTexCoord3f(1, 0, slice_z); glVertex3f(0.25, 1, 0); 1.249 - glEnd(); 1.250 - glDisable(GL_TEXTURE_3D); 1.251 - 1.252 - glUseProgram(0); 1.253 - 1.254 - glLineWidth(2); 1.255 - glBegin(GL_LINE_LOOP); 1.256 - glColor3f(0.8, 0.3, 0.8); 1.257 - glVertex3f(0, 1, 0); 1.258 - glVertex3f(0, 0.75, 0); 1.259 - glVertex3f(0.25, 0.75, 0); 1.260 - glVertex3f(0.25, 1, 0); 1.261 - glEnd(); 1.262 - 1.263 - glMatrixMode(GL_PROJECTION); 1.264 - glPopMatrix(); 1.265 - 1.266 - glMatrixMode(GL_MODELVIEW); 1.267 - glPopMatrix(); 1.268 - 1.269 - glEnable(GL_DEPTH_TEST); 1.270 -} 1.271 - 1.272 -static void draw_iso() 1.273 -{ 1.274 - if(mesh->is_empty()) { 1.275 - printf("recalculating isosurface ... "); 1.276 - fflush(stdout); 1.277 - vol->create_mesh(mesh, thres, thres2, vol_res[0], vol_res[1], vol_res[2]); 1.278 - printf("done.\n"); 1.279 - } 1.280 - mesh->draw(); 1.281 -} 1.282 - 1.283 -static void draw_volume() 1.284 -{ 1.285 - glUseProgram(sprog_vol); 1.286 - 1.287 - int tmin_loc = glGetUniformLocation(sprog_vol, "tmin"); 1.288 - if(tmin_loc != -1) 1.289 - glUniform1f(tmin_loc, std::min(thres, thres2)); 1.290 - int tmax_loc = glGetUniformLocation(sprog_vol, "tmax"); 1.291 - if(tmax_loc != -1) 1.292 - glUniform1f(tmax_loc, std::max(thres, thres2)); 1.293 - 1.294 - int res_loc = glGetUniformLocation(sprog_vol, "res"); 1.295 - if(res_loc != -1) 1.296 - glUniform3f(res_loc, (float)vol_res[0], (float)vol_res[1], (float)vol_res[2]); 1.297 - 1.298 - glRotatef(-vol->get_volume_rotation(), 1, 0, 0); 1.299 - 1.300 - glDisable(GL_DEPTH_TEST); 1.301 - glBindTexture(GL_TEXTURE_3D, vol->get_texture()); 1.302 - glEnable(GL_TEXTURE_3D); 1.303 - glEnable(GL_BLEND); 1.304 - glBegin(GL_QUADS); 1.305 - for(int i=0; i<num_poly; i++) 1.306 - { 1.307 - float tex_z = (float)i / (float)num_poly; 1.308 - float z = 2 * tex_z - 1; 1.309 - 1.310 - glTexCoord3f(0, 0, tex_z); glVertex3f(-bound_scale, -bound_scale, z * bound_scale); 1.311 - glTexCoord3f(0, 1, tex_z); glVertex3f(-bound_scale, bound_scale, z * bound_scale); 1.312 - glTexCoord3f(1, 1, tex_z); glVertex3f(bound_scale, bound_scale, z * bound_scale); 1.313 - glTexCoord3f(1, 0, tex_z); glVertex3f(bound_scale, -bound_scale, z * bound_scale); 1.314 - } 1.315 - glEnd(); 1.316 - glDisable(GL_BLEND); 1.317 - glDisable(GL_TEXTURE_3D); 1.318 - glEnable(GL_DEPTH_TEST); 1.319 - 1.320 - glUseProgram(0); 1.321 -} 1.322 - 1.323 -static void display(void) 1.324 -{ 1.325 - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 1.326 - 1.327 - glMatrixMode(GL_MODELVIEW); 1.328 - glLoadIdentity(); 1.329 - glTranslatef(0, 0, -cam_dist); 1.330 - glRotatef(cam_phi, 1, 0, 0); 1.331 - glRotatef(cam_theta, 0, 1, 0); 1.332 - 1.333 - //draw_slices(); 1.334 - //draw_iso(); 1.335 - draw_volume(); 1.336 - 1.337 - volume_preview(); 1.338 - glutSwapBuffers(); 1.339 - assert(glGetError() == GL_NO_ERROR); 1.340 -} 1.341 - 1.342 -static void reshape(int x, int y) 1.343 -{ 1.344 - glViewport(0, 0, x, y); 1.345 - glMatrixMode(GL_PROJECTION); 1.346 - glLoadIdentity(); 1.347 - gluPerspective(45, (float)x / (float)y, 0.5, 500); 1.348 - 1.349 - win_xsz = x; 1.350 - win_ysz = y; 1.351 -} 1.352 - 1.353 -static void keyboard(unsigned char key, int x, int y) 1.354 -{ 1.355 - key_state[(int)key] = true; 1.356 - 1.357 - switch(key) { 1.358 - case 27: 1.359 - exit(0); 1.360 - case 'w': 1.361 - { 1.362 - static bool wire; 1.363 - wire = !wire; 1.364 - glPolygonMode(GL_FRONT_AND_BACK, wire ? GL_LINE : GL_FILL); 1.365 - } 1.366 - break; 1.367 - default: 1.368 - break; 1.369 - } 1.370 -} 1.371 - 1.372 -static void keyboard_up(unsigned char key, int x, int y) 1.373 -{ 1.374 - key_state[(int) key] = false; 1.375 -} 1.376 - 1.377 -static int prev_x, prev_y; 1.378 -static bool bn_state[32]; 1.379 -static float prev_thres, prev_thres2; 1.380 - 1.381 -static void mouse(int bn, int state, int x, int y) 1.382 -{ 1.383 - prev_x = x; 1.384 - prev_y = y; 1.385 - 1.386 - bn_state[bn - GLUT_LEFT_BUTTON] = (state == GLUT_DOWN); 1.387 - 1.388 - if(state == GLUT_DOWN) { 1.389 - prev_thres = thres; 1.390 - prev_thres2 = thres2; 1.391 - } 1.392 - else { 1.393 - if(thres != prev_thres || thres2 != prev_thres2) { 1.394 - mesh->clear(); 1.395 - } 1.396 - } 1.397 -} 1.398 - 1.399 -static void motion(int x, int y) 1.400 -{ 1.401 - int dx = x - prev_x; 1.402 - int dy = y - prev_y; 1.403 - 1.404 - if(!dx && !dy) 1.405 - return; 1.406 - 1.407 - prev_x = x; 1.408 - prev_y = y; 1.409 - 1.410 - if(key_state[(int)'t']) { 1.411 - thres = (float)x / (float)win_xsz; 1.412 - printf("threshold: %f\n", thres); 1.413 - 1.414 - glutPostRedisplay(); 1.415 - return; 1.416 - } 1.417 - 1.418 - // camera 1.419 - if(bn_state[0]) { 1.420 - if(key_state[(int)'z']) { 1.421 - //zoom the camera 1.422 - 1.423 - cam_dist += dy * 0.1; 1.424 - 1.425 - if(cam_dist < 0) 1.426 - cam_dist = 0; 1.427 - } 1.428 - else { 1.429 - //rotate the camera 1.430 - 1.431 - cam_phi += dy * 0.5; 1.432 - cam_theta += dx * 0.5; 1.433 - 1.434 - if(cam_phi > 90) 1.435 - cam_phi = 90; 1.436 - if(cam_phi < -90) 1.437 - cam_phi = -90; 1.438 - } 1.439 - 1.440 - glutPostRedisplay(); 1.441 - } 1.442 -} 1.443 - 1.444 -static bool init_xfer(void) 1.445 -{ 1.446 - glutSetWindow(mainwin_id); 1.447 - int x = glutGet(GLUT_WINDOW_X); 1.448 - int y = glutGet(GLUT_WINDOW_Y) + glutGet(GLUT_WINDOW_HEIGHT); 1.449 - 1.450 - glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); 1.451 - glutInitWindowSize(512, 100); 1.452 - glutInitWindowPosition(x, y); 1.453 - 1.454 - xferwin_id = glutCreateWindow("Transfer Function"); 1.455 - glutDisplayFunc(display_xfer); 1.456 - glutReshapeFunc(reshape_xfer); 1.457 - glutMouseFunc(mouse_xfer); 1.458 - glutMotionFunc(motion_xfer); 1.459 - 1.460 - return true; 1.461 -} 1.462 - 1.463 -static void display_xfer(void) 1.464 -{ 1.465 - glClear(GL_COLOR_BUFFER_BIT); 1.466 - 1.467 - int num_val = glutGet(GLUT_WINDOW_WIDTH) / 4.0; 1.468 - float x = 0; 1.469 - float dx = 1.0 / num_val; 1.470 - float low, high; 1.471 - if(thres < thres2) { 1.472 - low = thres; 1.473 - high = thres2; 1.474 - } 1.475 - else { 1.476 - low = thres2; 1.477 - high = thres; 1.478 - } 1.479 - 1.480 - 1.481 - //drawing the histogram 1.482 - float hmax = 1 / (float)vol->max_histogram_value; 1.483 - 1.484 - glBegin(GL_QUADS); 1.485 - for (int i=0; i<HIST_SIZE; i++) { 1.486 - float x0 = (float)i / HIST_SIZE; 1.487 - float x1 = (float)(i + 1) / HIST_SIZE; 1.488 - 1.489 - glColor3f(0.3, 0.3, 0.3); 1.490 - glVertex3f(x0, 0, 0); 1.491 - glVertex3f(x1, 0, 0); 1.492 - glVertex3f(x1, hmax * vol->histogram[i + 1], 0); 1.493 - glVertex3f(x0, hmax * vol->histogram[i], 0); 1.494 - } 1.495 - glEnd(); 1.496 - 1.497 - //drawing the transfer function curve 1.498 - 1.499 - glLineWidth(3); 1.500 - glBegin(GL_LINE_STRIP); 1.501 - glColor3f(0.8, 0.3, 0.8); 1.502 - for(int i=0; i<num_val; i++) { 1.503 - float val = transfer_function(x, low, high); 1.504 - glVertex2f(x, val); 1.505 - x += dx; 1.506 - } 1.507 - glEnd(); 1.508 - 1.509 - //threshold bars 1.510 - 1.511 - glLineWidth(2); 1.512 - glBegin(GL_LINES); 1.513 - glColor3f(0.4, 0.4, 0.8); 1.514 - glVertex2f(low, 0); 1.515 - glVertex2f(low, 1); 1.516 - glColor3f(0.4, 0.8, 0.4); 1.517 - glVertex2f(high, 0); 1.518 - glVertex2f(high, 1); 1.519 - glEnd(); 1.520 - 1.521 -/* 1.522 - * gradient 1.523 - */ 1.524 -/* glBegin(GL_QUADS); 1.525 - for(int i=0; i<num_val; i++) { 1.526 - float val = transfer_function(x, low, high); 1.527 - glColor3f(val, val, val); 1.528 - glVertex3f(x, 1.0, 0.0); 1.529 - glVertex3f(x, 0.0, 0.0); 1.530 - 1.531 - val = transfer_function(x + dx, low, high); 1.532 - glColor3f(val, val, val); 1.533 - glVertex3f(x + dx, 0.0, 0.0); 1.534 - glVertex3f(x + dx, 1.0, 0.0); 1.535 - x += dx; 1.536 - } 1.537 - glEnd(); */ 1.538 - 1.539 - glutSwapBuffers(); 1.540 - assert(glGetError() == GL_NO_ERROR); 1.541 -} 1.542 - 1.543 -static void reshape_xfer(int x, int y) 1.544 -{ 1.545 - glViewport(0.0, 0.0, x, y); 1.546 - glMatrixMode(GL_PROJECTION); 1.547 - glLoadIdentity(); 1.548 - glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0); 1.549 -} 1.550 - 1.551 -static float *select_thres; 1.552 -static float prev_select_thres; 1.553 -static void mouse_xfer(int button, int state, int x, int y) 1.554 -{ 1.555 - if(button == GLUT_LEFT_BUTTON) { 1.556 - if(state == GLUT_DOWN) { 1.557 - int width = glutGet(GLUT_WINDOW_WIDTH); 1.558 - float xpos = (float)x / (float)width; 1.559 - if(fabs(xpos - thres) <= fabs(xpos - thres2) && fabs(xpos - thres) < 0.1) { 1.560 - select_thres = &thres; 1.561 - } 1.562 - else if (fabs(xpos - thres2) < 0.1) { 1.563 - select_thres = &thres2; 1.564 - } 1.565 - else 1.566 - select_thres = 0; 1.567 - 1.568 - if (select_thres) 1.569 - prev_select_thres = *select_thres; 1.570 - } 1.571 - else { 1.572 - if(!prev_select_thres || !select_thres) { 1.573 - select_thres = 0; 1.574 - return; 1.575 - } 1.576 - if(fabs(*select_thres - prev_select_thres) > 0.001) { 1.577 - mesh->clear(); 1.578 - ui->sync_live(); 1.579 - } 1.580 - select_thres = 0; 1.581 - } 1.582 - } 1.583 -} 1.584 - 1.585 -static void motion_xfer(int x, int y) 1.586 -{ 1.587 - if(!select_thres) 1.588 - return; 1.589 - 1.590 - int width = glutGet(GLUT_WINDOW_WIDTH); 1.591 - float xpos = (float)x / (float)width; 1.592 - 1.593 - *select_thres = xpos; 1.594 - thres_change(0); 1.595 - 1.596 - glutPostRedisplay(); 1.597 - glutSetWindow(mainwin_id); 1.598 - glutPostRedisplay(); 1.599 -}