#include <cstdio>
#include <fstream>
#include <iostream>
#include <iomanip>
#include "loader.h"
namespace {
class LoaderTrack {
public:
Track &track;
const std::string &filename;
std::ifstream f;
int row;
int col;
bool valid_point;
bool newline;
TrackPoint point;
LoaderTrack(Track &track, const std::string &filename):
track(track),
filename(filename),
row(1),
col(),
valid_point(),
newline()
{ }
void error(const std::string &msg) {
std::cerr << "Error while loading track: " << filename
<< ":" << row
<< ":" << col
<< ": " << msg
<< std::endl;
}
int get() {
int c = f.get();
if (c == '\n') ++row, col = 0, newline = true; else ++col;
return c;
}
bool read_pair(int &command, Real &number) {
command = 0; number = 0;
char c = f.get(); ++row;
if (c < 0) return false;
while(std::isspace(c)) c = get();
if (c == '(') while(c != ')') c = get();
while(std::isspace(c)) c = get();
newline = false;
if (c < 0) { return false; }
command = c;
while(std::isspace(f.peek())) get();
if (newline) return true;
Real sign = 1;
Real frac = 1;
int mode = 0;
while(true) {
c = f.peek();
if (mode == 0 && c == '-') { sign = -1; mode = 1; }
else if (mode == 0 && c == '+') { sign = +1; mode = 1; }
else if (mode <= 1 && c == '.') { mode = 2; }
else if (mode <= 1 && c >= '0' && c <= '9') { number = number*10 + (c - '0'); mode = 1; }
else if (mode == 2 && c >= '0' && c <= '9') { number += (c - '0')*(frac *= 0.1); }
else if (mode == 0) { error("expected number"); return c >= 0; }
else { break; }
get();
}
number *= sign;
while(std::isspace(f.peek())) get();
return true;
}
void read_point_data(Real &data, int command, Real number) {
if (!valid_point)
error("arg " + std::string(1, command) + " was set in unknown command");
data = number;
}
bool load() {
track.points.clear();
std::cout << "loading: " << filename << std::endl;
f.open(filename);
if (!f) {
error("cannot open file");
f.close();
return false;
}
int command;
Real number;
point.speed = 600/60.0;
while(read_pair(command, number)) {
int inumber = (int)round(number);
//debug: std::cout << std::string(1, command) << " " << number << std::endl;
bool arg = true;
if (command == 'X') {
read_point_data(point.position.x, command, number);
} else
if (command == 'Y') {
read_point_data(point.position.y, command, number);
} else
if (command == 'Z') {
read_point_data(point.position.z, command, number);
} else
if (command == 'A') {
read_point_data(point.angle, command, number);
} else
if (command == 'F') {
if (number < 1e-3) {
error("feed rate too small");
number = 1e-3;
}
read_point_data(point.speed, command, number/60.0);
} else {
arg = false;
}
if (newline || !arg) {
if (valid_point) track.points.push_back(point);
if (!arg) {
valid_point = false;
if (command == 'G' && (inumber == 0 || inumber == 1))
valid_point = true;
else
error("Unknown command: " + std::string(1, command));
}
}
}
if (valid_point) track.points.push_back(point);
track.split(1, 0, 0);
track.calc_length();
track.split(0, 1, 0);
track.calc_length();
std::cout << "track length: " << ((track.points.empty() ? 0.0 : track.points.back().time)/60.0) << " min" << std::endl;
f.close();
std::cout << "loaded: " << filename << std::endl;
return true;
}
}; // LoaderTrack
class LoaderModel {
public:
Model &model;
const std::string &filename;
FILE *f;
LoaderModel(Model &model, const std::string &filename):
model(model),
filename(filename),
f()
{ }
void error(const std::string &msg) {
std::cerr << "Error while loading model: " << filename
<< ": offset: " << (f ? ftell(f) : 0)
<< ": " << msg
<< std::endl;
}
template<typename T>
bool read(T &data) {
if (fread(&data, 1, sizeof(data), f) != sizeof(data)) {
error("fread failed");
return false;
}
return true;
}
bool load() {
model.triangles.clear();
std::cout << "loading: " << filename << std::endl;
f = fopen(filename.c_str(), "rb");
if (!f) {
error("cannot open file");
return false;
}
char header[80] = {};
unsigned int count = 0;
float normal[3] = {};
float vertices[3][3] = {};
unsigned short attribute = 0;
Triangle t;
bool success = false;
int degenerated_count = 0;
if (read(header) && read(count)) {
success = true;
for(unsigned int i = 0; i < count; ++i) {
if (!read(normal) || !read(vertices) || !read(attribute))
{ success = false; break; }
for(int j = 0; j < 3; ++j)
for(int k = 0; k < 3; ++k)
t.vertices[j].c[k] = vertices[j][k];
if (t.check(1e-10))
model.triangles.push_back(t);
else
++degenerated_count;
}
}
fclose(f);
if (degenerated_count)
std::cout << "skipped degenerated triangles: " << degenerated_count << std::endl;
std::cout << "loaded triangles: " << model.triangles.size() << std::endl;
model.normalize();
model.transform(Matrix4::scaling(Vector3(1000, 1000, 1000)));
std::cout << "loaded: " << filename << std::endl;
return success;
}
}; // LoaderModel
} // namespace
bool Loader::load(Track &track, const std::string &filename) {
return LoaderTrack(track, filename).load();
}
bool Loader::load(Model &model, const std::string &filename) {
return LoaderModel(model, filename).load();
}
bool Loader::save(const Track &track, const std::string &filename) {
std::cout << "saving: " << filename << std::endl;
std::ofstream f(filename);
if (!f) {
std::cerr << "cannot open file for write: " << filename << std::endl;
return false;
}
for(TrackPointList::const_iterator i = track.points.begin(); i != track.points.end(); ++i) {
f << std::fixed
<< std::setprecision(8)
<< "G01"
<< " X" << i->position.x
<< " Y" << i->position.y
<< " Z" << i->position.z
<< " A" << i->angle
<< " F" << (int)round(i->speed*60.0)
<< std::endl;
}
f.flush();
if (!f) {
std::cerr << "write error" << std::endl;
return false;
}
f.close();
std::cerr << "saved: " << filename << std::endl;
return true;
}