From: John Tsiombikas Date: Tue, 11 Oct 2016 13:06:16 +0000 (+0300) Subject: Merge branch 'master' of goat:git/antikythera X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=antikythera;a=commitdiff_plain;h=403be7663d7412d0937b1f611b63363213bb3766;hp=1548fea8c64ebdfde728f33fd2d1537954ebf38b Merge branch 'master' of goat:git/antikythera --- diff --git a/src/gear.cc b/src/gear.cc index e6a5491..6a34815 100644 --- a/src/gear.cc +++ b/src/gear.cc @@ -9,14 +9,14 @@ Gear::Gear() { pdist = 0; angle = 0; - nteeth = 42; - radius = 80; teeth_length = 5; thickness = 5; bevel = 1.5; init_angle = 0; xform_valid = false; + set_teeth(42, 10); + supergear = 0; mesh = 0; @@ -79,6 +79,11 @@ float Gear::get_angular_offset() const void Gear::set_teeth(int nt, float tooth_pitch) { + if(tooth_pitch <= 0) { + tooth_pitch = this->tooth_pitch; + } else { + this->tooth_pitch = tooth_pitch; + } float circ = tooth_pitch * nt; radius = circ / (2.0 * M_PI); nteeth = nt; diff --git a/src/gear.h b/src/gear.h index f6058a1..fac245c 100644 --- a/src/gear.h +++ b/src/gear.h @@ -58,6 +58,7 @@ public: float angle; // current angle of the gear int nteeth; // number of teeth + float tooth_pitch; float radius; // total radius of the gear, including teeth float teeth_length; // how far teeth extend past the radius @@ -87,7 +88,7 @@ public: // sets the supplied number of teeth, and calculates the radius // of the gear, to achieve the required tooth pitch - void set_teeth(int nt, float tooth_pitch); + void set_teeth(int nt, float tooth_pitch = 0.0f); void set_axis(const Vec3 &axis); const Vec3 &get_axis() const; void set_position(const Vec3 &pos); diff --git a/src/machine.cc b/src/machine.cc index 3d4dd73..441826e 100644 --- a/src/machine.cc +++ b/src/machine.cc @@ -47,6 +47,15 @@ void Machine::add_motor(int gearidx, float speed_hz) motors.push_back(m); } +int Machine::get_gear_index(Gear *g) const +{ + std::map::const_iterator it = gearidx.find(g); + if(it == gearidx.end()) { + return -1; + } + return it->second; +} + void Machine::invalidate_meshing() { meshing_valid = false; diff --git a/src/machine.h b/src/machine.h index e0f2155..097157d 100644 --- a/src/machine.h +++ b/src/machine.h @@ -29,6 +29,8 @@ public: void add_gear(Gear *g); /* takes ownership */ void add_motor(int gearidx, float speed_hz); + int get_gear_index(Gear *g) const; + void invalidate_meshing(); void calc_meshing(); diff --git a/src/main.cc b/src/main.cc index 772b63c..6a296b9 100644 --- a/src/main.cc +++ b/src/main.cc @@ -13,6 +13,7 @@ #include "texture.h" #include "machine.h" #include "meshgen.h" +#include "mparser.h" static bool init(); static void cleanup(); @@ -88,7 +89,12 @@ static bool init() Mesh::use_custom_sdr_attr = false; machine = new Machine; + if(!parse_machine(machine, "data/test.machine")) { + fprintf(stderr, "failed to parse machine\n"); + return false; + } + /* const float pitch = 10.0f; Gear *gear1 = new Gear; @@ -119,8 +125,9 @@ static bool init() gear2->attach(subgear); machine->add_gear(subgear); subgear->color = Vec3(0.8, 0.7, 0.5); + */ - machine->add_motor(0, 1.0); + //machine->add_motor(0, 1.0); // shadows init_shadow(2048); diff --git a/src/mparser.cc b/src/mparser.cc new file mode 100644 index 0000000..3bfe8ce --- /dev/null +++ b/src/mparser.cc @@ -0,0 +1,819 @@ +#include +#include +#include +#include +#include +#include +#include "mparser.h" +#include "machine.h" + +enum ValueType { VAL_NUM, VAL_VEC, VAL_STR }; +struct Value { + ValueType type; + std::string s; + float v[4]; +}; + +struct ParserState; + +typedef bool (*Func)(ParserState*); + +struct FuncDesc { + Func func; + int arity; +}; + +struct ParserState { + FILE *fp; + const char *fname; + int nline, nerrors; + Machine *mcn; + + int nextc; + int savedc; + + std::stack val; + std::map var; + std::map func; + + std::map gears; + std::vector cur_gear; + Motor *cur_motor; +}; + +#define MAX_ID_LEN 31 + +static bool machine(ParserState *ps); +static bool expect(ParserState *ps, char c); +static bool statement(ParserState *ps); +static bool expression(ParserState *ps); +static bool term(ParserState *ps); +static bool vector(ParserState *ps); +static bool function(ParserState *ps, const char *name); + +static char *get_ident(ParserState *ps, char *buf, int bsz); +static bool get_number(ParserState *ps, float *num); +static char *get_string(ParserState *ps, char *buf, int bsz); + +static bool nextchar(ParserState *ps); +static void putback(ParserState *ps, char c); + +static void errmsg(ParserState *ps, const char *fmt, ...); +static void expected(ParserState *ps, const char *str); + +static void set_var(ParserState *ps, const char *name, const Value &value); +static bool have_var(ParserState *ps, const char *name); +static const Value &get_var(ParserState *ps, const char *name); + +static void set_func(ParserState *ps, const char *name, Func func, int nargs = 0); +static bool have_func(ParserState *ps, const char *name); +static Func get_func(ParserState *ps, const char *name); +static int get_func_arity(ParserState *ps, const char *name); + +static Gear *begin_gear(ParserState *ps); +static bool end_gear(ParserState *ps); +static Gear *this_gear(ParserState *ps); +static Gear *find_gear(ParserState *ps, const char *name); +static void update_gear_vars(ParserState *ps, Gear *gear); +static bool set_gear_var(Gear *gear, const char *name, const Value &val); + +static void update_motor_vars(ParserState *ps, Motor *motor); +static bool set_motor_var(ParserState *ps, Motor *motor, const char *name, const Value &val); + +// built-in functions +static bool func_adjacent(ParserState *ps); +static bool func_coaxial(ParserState *ps); + + +bool parse_machine(Machine *mcn, const char *fname) +{ + FILE *fp = fopen(fname, "rb"); + if(!fp) return false; + + ParserState pstate; + pstate.fname = fname; + pstate.nline = 1; + pstate.nerrors = 0; + pstate.fp = fp; + pstate.mcn = mcn; + pstate.nextc = 0; + pstate.savedc = -1; + pstate.cur_motor = 0; + + // init built-in function table + set_func(&pstate, "adjacent", func_adjacent, 2); + set_func(&pstate, "coaxial", func_coaxial, 2); + + nextchar(&pstate); + bool res = machine(&pstate); + fclose(fp); + return res; +} + +static bool machine(ParserState *ps) +{ + char tok[MAX_ID_LEN + 1]; + if(strcmp(get_ident(ps, tok, sizeof tok), "machine") != 0) { + expected(ps, "machine"); + return false; + } + expect(ps, '{'); + + while(ps->nextc != '}') { + if(!statement(ps)) { + return false; + } + } + expect(ps, '}'); + return true; +} + +static bool expect(ParserState *ps, char c) +{ + if(c != ps->nextc) { + char buf[2] = {0, 0}; + buf[0] = c; + expected(ps, buf); + return false; + } + nextchar(ps); + return true; +} + +static bool statement(ParserState *ps) +{ + char id[MAX_ID_LEN + 1]; + if(!get_ident(ps, id, sizeof id)) { + return false; + } + + if(strcmp(id, "gear") == 0) { + if(!expect(ps, '{')) { + return false; + } + + Gear *gear = begin_gear(ps); + if(!gear) return false; + + update_gear_vars(ps, gear); + + while(ps->nextc != '}') { + if(!statement(ps)) { + return false; + } + } + expect(ps, '}'); + + if(!end_gear(ps)) { + return false; + } + return true; + } + + if(strcmp(id, "motor") == 0) { + if(!expect(ps, '{')) { + return false; + } + if(ps->cur_motor) { + errmsg(ps, "nested motors not allowed"); + return false; + } + + ps->cur_motor = new Motor; + update_motor_vars(ps, ps->cur_motor); + + while(ps->nextc != '}') { + if(!statement(ps)) { + return false; + } + } + expect(ps, '}'); + + ps->mcn->add_motor(ps->cur_motor->drive, ps->cur_motor->speed); + delete ps->cur_motor; + ps->cur_motor = 0; + return true; + } + + if(ps->nextc == '=') { + expect(ps, '='); + if(!expression(ps)) { + return false; + } + expect(ps, ';'); + + set_var(ps, id, ps->val.top()); + ps->val.pop(); + return true; + } + + expected(ps, "expression"); + return false; +} + +static bool expression(ParserState *ps) +{ + return term(ps); +} + +static bool term(ParserState *ps) +{ + if(ps->nextc == '+') { + expect(ps, '+'); + return expression(ps); + } + if(ps->nextc == '-') { + expect(ps, '-'); + if(!expression(ps)) { + return false; + } + + Value &v = ps->val.top(); + switch(v.type) { + case VAL_VEC: + v.v[3] = -v.v[3]; + v.v[2] = -v.v[2]; + v.v[1] = -v.v[1]; + case VAL_NUM: + v.v[0] = -v.v[0]; + break; + + default: + expected(ps, "number or vector after an unary -"); + return false; + } + return true; + } + if(isalpha(ps->nextc)) { + char buf[MAX_ID_LEN]; + if(!get_ident(ps, buf, sizeof buf)) { + return false; + } + + if(ps->nextc == '(') { + if(!function(ps, buf)) { + return false; + } + } else if(have_var(ps, buf)) { + ps->val.push(get_var(ps, buf)); + } else { + errmsg(ps, "unknown identifier: %s", buf); + return false; + } + return true; + } + if(isdigit(ps->nextc)) { + float num; + if(!get_number(ps, &num)) { + return false; + } + + Value v; + v.type = VAL_NUM; + v.v[0] = num; + ps->val.push(v); + return true; + } + if(ps->nextc == '[') { + return vector(ps); + } + if(ps->nextc == '"') { + char buf[MAX_ID_LEN]; + if(!get_string(ps, buf, sizeof buf)) { + return false; + } + + Value v; + v.type = VAL_STR; + v.s = buf; + ps->val.push(v); + return true; + } + + expected(ps, "term"); + return false; +} + +static bool vector(ParserState *ps) +{ + int nelem; + if(!expect(ps, '[') || + !expression(ps) || + !expect(ps, ',') || + !expression(ps) || + !expect(ps, ',') || + !expression(ps)) { + return false; + } + + if(ps->nextc == ']') { + expect(ps, ']'); + nelem = 3; + } else { + if(!expect(ps, ',') || !expression(ps) || !expect(ps, ']')) { + return false; + } + nelem = 4; + } + + Value vec; + vec.type = VAL_VEC; + vec.v[3] = 1.0f; + + for(int i=0; ival.top(); + if(tmp.type != VAL_NUM) { + expected(ps, "numbers as vector elements"); + return false; + } + vec.v[nelem - i - 1] = tmp.v[0]; + ps->val.pop(); + } + + ps->val.push(vec); + return true; +} + +static bool function(ParserState *ps, const char *name) +{ + if(!expect(ps, '(')) { + return false; + } + + if(!have_func(ps, name)) { + errmsg(ps, "unknown function: %s", name); + return false; + } + + int nargs = 0; + while(ps->nextc != ')') { + if(nargs && !expect(ps, ',')) { + return false; + } + if(!expression(ps)) { + return false; + } + ++nargs; + } + expect(ps, ')'); + + int arity = get_func_arity(ps, name); + if(arity != nargs) { + errmsg(ps, "function %s expects %d arguments", name, arity); + return false; + } + Func func = get_func(ps, name); + assert(func); + + if(!func(ps)) { + errmsg(ps, "function %s failed", name); + return false; + } + return true; +} + +static char *get_ident(ParserState *ps, char *buf, int bsz) +{ + char *ptr = buf; + if(!isalpha(ps->nextc)) { + expected(ps, "identifier"); + return 0; + } + while(isalnum(ps->nextc) || ps->nextc == '_') { + if(bsz > 1) { + *ptr++ = ps->nextc; + --bsz; + } + nextchar(ps); + } + *ptr = 0; + printf("get_ident -> \"%s\"\n", buf); + return buf; +} + +static bool get_number(ParserState *ps, float *num) +{ + char buf[256], *ptr = buf, *end = buf + sizeof buf; + bool found_point = false; + + if(!isdigit(ps->nextc)) { + expected(ps, "number"); + return false; + } + + while(isdigit(ps->nextc) || (ps->nextc == '.' && !found_point)) { + if(ptr < end) { + *ptr++ = ps->nextc; + } + if(ps->nextc == '.') { + found_point = true; + } + nextchar(ps); + } + *ptr = 0; + + *num = atof(buf); + printf("get_number -> %f\n", *num); + return true; +} + +static char *get_string(ParserState *ps, char *buf, int bsz) +{ + char *ptr = buf; + if(!expect(ps, '"')) { + return 0; + } + + while(ps->nextc != -1 && ps->nextc != '"') { + if(bsz > 1) { + *ptr++ = ps->nextc; + --bsz; + } + if((ps->nextc = fgetc(ps->fp)) == '\n') { + ++ps->nline; + } + } + *ptr = 0; + + if(ps->nextc == -1) { + return 0; + } + nextchar(ps); + + printf("get_string -> \"%s\"\n", buf); + return buf; +} + +static bool skip_line(ParserState *ps) +{ + int c; + while((c = fgetc(ps->fp)) != -1 && c != '\n'); + if(c != -1) { + ps->nextc = fgetc(ps->fp); + return true; + } + return false; +} + +static bool nextchar(ParserState *ps) +{ + if(ps->savedc != -1) { + ps->nextc = ps->savedc; + ps->savedc = -1; + return true; + } + + while((ps->nextc = fgetc(ps->fp)) != -1) { + if(ps->nextc == '#') { + if(!skip_line(ps)) { + return false; + } + ++ps->nline; + } + if(!isspace(ps->nextc)) { + break; + } + if(ps->nextc == '\n') { + ++ps->nline; + } + } + + /*if(ps->nextc != -1) { + printf("DBG: nextchar -> %c\n", ps->nextc); + } else { + printf("DBG: nextchar -> EOF\n"); + }*/ + return ps->nextc != -1; +} + +static void putback(ParserState *ps, char c) +{ + ps->savedc = ps->nextc; + ps->nextc = c; +} + +static void errmsg(ParserState *ps, const char *fmt, ...) +{ + fprintf(stderr, "%s line %d error: ", ps->fname, ps->nline); + + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + fputc('\n', stderr); +} + +static void expected(ParserState *ps, const char *str) +{ + errmsg(ps, "expected: %s", str); + ++ps->nerrors; +} + +static void set_var(ParserState *ps, const char *name, const Value &value) +{ + ps->var[name] = value; + + Gear *gear = this_gear(ps); + if(gear) { + set_gear_var(gear, name, value); + } + + if(ps->cur_motor) { + set_motor_var(ps, ps->cur_motor, name, value); + } +} + +static bool have_var(ParserState *ps, const char *name) +{ + return ps->var.find(name) != ps->var.end(); +} + +static const Value &get_var(ParserState *ps, const char *name) +{ + return ps->var[name]; +} + +static void set_func(ParserState *ps, const char *name, Func func, int nargs) +{ + FuncDesc fdesc; + fdesc.func = func; + fdesc.arity = nargs; + ps->func[name] = fdesc; +} + +static bool have_func(ParserState *ps, const char *name) +{ + return ps->func.find(name) != ps->func.end(); +} + +static Func get_func(ParserState *ps, const char *name) +{ + return ps->func[name].func; +} + +static int get_func_arity(ParserState *ps, const char *name) +{ + return ps->func[name].arity; +} + +static void print_value(const Value &val, FILE *fp) +{ + switch(val.type) { + case VAL_NUM: + fprintf(fp, "%f", val.v[0]); + break; + + case VAL_VEC: + fprintf(fp, "[%f %f %f %f]", val.v[0], val.v[1], val.v[2], val.v[3]); + break; + + case VAL_STR: + fprintf(fp, "\"%s\"", val.s.c_str()); + break; + + default: + fprintf(fp, ""); + } +} + +// Gear-specific stuff + +static Gear *begin_gear(ParserState *ps) +{ + Gear *res = new Gear; + ps->cur_gear.push_back(res); + return res; +} + +static bool end_gear(ParserState *ps) +{ + if(ps->cur_gear.empty()) { + errmsg(ps, "parser error: unbalanced end_gear"); + return false; + } + Gear *gear = ps->cur_gear.back(); + ps->cur_gear.pop_back(); + + printf("DBG: end_gear: %s\n", gear->name.c_str()); + + if(gear->name.empty() || ps->gears[gear->name]) { + char buf[32]; + sprintf(buf, "gear%04d", (int)ps->gears.size()); + gear->name = buf; + } + printf("DBG: adding gear: %s\n", gear->name.c_str()); + ps->gears[gear->name] = gear; + ps->mcn->add_gear(gear); + + if(!ps->cur_gear.empty()) { + ps->cur_gear.back()->attach(gear); + } + return true; +} + +static Gear *this_gear(ParserState *ps) +{ + return ps->cur_gear.empty() ? 0 : ps->cur_gear.back(); +} + +static Gear *find_gear(ParserState *ps, const char *name) +{ + // search progressively wider lexical scopes + std::vector::const_reverse_iterator it = ps->cur_gear.rbegin(); + while(it != ps->cur_gear.rend()) { + Gear *g = *it++; + if(g->name == std::string(name)) { + return g; + } + } + + return ps->gears[name]; +} + +static void update_gear_vars(ParserState *ps, Gear *gear) +{ + std::map::const_iterator it = ps->var.begin(); + while(it != ps->var.end()) { + set_gear_var(gear, it->first.c_str(), it->second); + ++it; + } +} + +#define ASSERT_TYPE(v, t) \ + do { \ + if((v).type != (t)) { \ + fprintf(stderr, "type mismatch while trying to set %s to value: ", name); \ + print_value(val, stderr); \ + fputc('\n', stderr); \ + return false; \ + } \ + } while(0) + +#define VVEC3(val) Vec3((val).v[0], (val).v[1], (val).v[2]) +#define VVEC4(val) Vec4((val).v[0], (val).v[1], (val).v[2], (val).v[3]) + +static bool set_gear_var(Gear *gear, const char *name, const Value &val) +{ + if(strcmp(name, "name") == 0) { + ASSERT_TYPE(val, VAL_STR); + gear->name = val.s; + + } else if(strcmp(name, "position") == 0) { + ASSERT_TYPE(val, VAL_VEC); + gear->pos = VVEC3(val); + + } else if(strcmp(name, "plane") == 0) { + ASSERT_TYPE(val, VAL_VEC); + gear->axis = VVEC3(val); + gear->pdist = val.v[3]; + printf("setting plane eqn: %f %f %f %f\n", val.v[0], val.v[1], val.v[2], val.v[3]); + + } else if(strcmp(name, "thickness") == 0) { + ASSERT_TYPE(val, VAL_NUM); + gear->thickness = val.v[0]; + + } else if(strcmp(name, "teeth") == 0) { + ASSERT_TYPE(val, VAL_NUM); + gear->set_teeth(val.v[0]); // set teeth and recalc radius + + } else if(strcmp(name, "teeth_length") == 0) { + ASSERT_TYPE(val, VAL_NUM); + gear->teeth_length = val.v[0]; + + } else if(strcmp(name, "pitch") == 0) { + ASSERT_TYPE(val, VAL_NUM); + gear->set_teeth(gear->nteeth, val.v[0]); // set pitch and recalc radius + + } else if(strcmp(name, "color") == 0) { + ASSERT_TYPE(val, VAL_VEC); + gear->color = VVEC3(val); + + } else if(strcmp(name, "roughness") == 0) { + ASSERT_TYPE(val, VAL_NUM); + gear->roughness = val.v[0]; + + } else { + return false; + } + + return true; +} + + +// motor stuff + +static void update_motor_vars(ParserState *ps, Motor *motor) +{ + std::map::const_iterator it = ps->var.begin(); + while(it != ps->var.end()) { + set_motor_var(ps, motor, it->first.c_str(), it->second); + ++it; + } +} + +static bool set_motor_var(ParserState *ps, Motor *motor, const char *name, const Value &val) +{ + if(strcmp(name, "drive") == 0) { + ASSERT_TYPE(val, VAL_STR); + Gear *gear = ps->gears[val.s]; + if(!gear) { + errmsg(ps, "undefined gear: %s", val.s.c_str()); + return false; + } + int idx = ps->mcn->get_gear_index(gear); + assert(idx >= 0); + motor->drive = idx; + + } else if(strcmp(name, "speed") == 0) { + ASSERT_TYPE(val, VAL_NUM); + motor->speed = val.v[0]; + + } else { + return false; + } + return true; +} + + +// built-in functions +static bool func_adjacent(ParserState *ps) +{ + Value val_angle = ps->val.top(); ps->val.pop(); + Value val_gear_name = ps->val.top(); ps->val.pop(); + + if(val_gear_name.type != VAL_STR) { + expected(ps, "gear name (string) as 1st arg to adjacent"); + return false; + } + if(val_angle.type != VAL_NUM) { + expected(ps, "angle as 2nd arg to adjacent"); + return false; + } + + Gear *gthis = this_gear(ps); + if(!gthis) { + errmsg(ps, "adjacent: called outside of a gear block"); + return false; + } + + Gear *gother = ps->gears[val_gear_name.s]; + if(!gother) { + errmsg(ps, "adjacent: gear \"%s\" not found", val_gear_name.s.c_str()); + return false; + } + + Mat4 xform; + xform.rotation(deg_to_rad(val_angle.v[0]), gother->axis); + Vec3 dir = xform * Vec3(0, 1, 0); + + float sum_radii = gthis->radius + gother->radius; + float avg_teeth_len = (gthis->teeth_length + gother->teeth_length) * 0.5; + Vec3 pos = gother->pos + dir * (sum_radii - avg_teeth_len * 0.75); + + Value res; + res.type = VAL_VEC; + res.v[0] = pos.x; + res.v[1] = pos.y; + res.v[2] = pos.z; + ps->val.push(res); + return true; +} + +static bool func_coaxial(ParserState *ps) +{ + Value val_dist = ps->val.top(); ps->val.pop(); + Value val_gear_name = ps->val.top(); ps->val.pop(); + + if(val_gear_name.type != VAL_STR) { + expected(ps, "gear name (string) as 1st arg to func_coaxial"); + return false; + } + if(val_dist.type != VAL_NUM) { + expected(ps, "stacking distance as 2nd arg to func_coaxial"); + return false; + } + + Gear *gthis = this_gear(ps); + if(!gthis) { + errmsg(ps, "coaxial: called outside of a gear block"); + return false; + } + + Gear *gother = find_gear(ps, val_gear_name.s.c_str()); + if(!gother) { + errmsg(ps, "coaxial: gear \"%s\" not found", val_gear_name.s.c_str()); + return false; + } + + float avg_thickness = (gthis->thickness + gother->thickness) * 0.5; + Vec3 pos = gother->pos + gother->axis * val_dist.v[0] * avg_thickness; + + Value res; + res.type = VAL_VEC; + res.v[0] = pos.x; + res.v[1] = pos.y; + res.v[2] = pos.z; + ps->val.push(res); + return true; +} diff --git a/src/mparser.h b/src/mparser.h new file mode 100644 index 0000000..eb64f1b --- /dev/null +++ b/src/mparser.h @@ -0,0 +1,9 @@ +#ifndef MPARSER_H_ +#define MPARSER_H_ + +#include +#include "machine.h" + +bool parse_machine(Machine *mcn, const char *fname); + +#endif /* MPARSER_H_ */