#include <cstdio>
#include <fstream>
#include <iostream>
#include "loader.h"
class Loader::Internal {
public:
Track &track;
const std::string &filename;
std::ifstream f;
int row;
int col;
bool valid_point;
bool newline;
Point point;
Internal(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 && isdigit(c)) { number = number*10 + (c - '0')*sign; sign = 1; mode = 1; }
else if (mode == 2 && isdigit(c)) { number += (c - '0')*(frac *= 0.1); }
else if (mode == 0) { error("expected number"); return c >= 0; }
else { break; }
get();
}
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);
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);
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;
}
};
bool Loader::load(Track &track, const std::string &filename) {
return Internal(track, filename).load();
}