mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-12-02 09:02:32 +00:00
68ca8c7016
According to enki (bivector community) when there are more than one null vector in a geometry, usually all vectors are null, and it was what to do with multiple null vectors that caused me to balk at using e0 for the null vector. However, using e0 for the null vector makes life much easier, especially as that's what most of the literature does. There are plenty of places, particularly in layout handling, that still need adjustment for the change, but things seem to work (minus duals, but they were broken in the first place, thus the discussion with enki).
472 lines
9.4 KiB
R
472 lines
9.4 KiB
R
#include <AutoreleasePool.h>
|
|
#include <hash.h>
|
|
#include <qfile.h>
|
|
#include <script.h>
|
|
#include <string.h>
|
|
|
|
#include "algebra.h"
|
|
#include "basisblade.h"
|
|
#include "basisgroup.h"
|
|
#include "metric.h"
|
|
#include "multivector.h"
|
|
#include "util.h"
|
|
|
|
@static AutoreleasePool *autorelease_pool;
|
|
@static void
|
|
arp_start (void)
|
|
{
|
|
autorelease_pool = [[AutoreleasePool alloc] init];
|
|
}
|
|
|
|
@static void
|
|
arp_end (void)
|
|
{
|
|
[autorelease_pool release];
|
|
autorelease_pool = nil;
|
|
}
|
|
|
|
static void
|
|
basic_test (void)
|
|
{
|
|
arp_start ();
|
|
|
|
BasisBlade *a = [[BasisBlade basis:1] retain];
|
|
BasisBlade *b = [[BasisBlade basis:2] retain];
|
|
BasisBlade *c = [[BasisBlade basis:4] retain];
|
|
BasisBlade *d = [[BasisBlade basis:8] retain];
|
|
BasisBlade *blades[] = {a, b, c, d};
|
|
static string names[] = {"a", "b", "c", "d"};
|
|
|
|
// printf ("a: %@\n", a);
|
|
// printf ("b: %@\n", b);
|
|
// printf ("c: %@\n", c);
|
|
// printf ("d: %@\n", d);
|
|
|
|
arp_end ();
|
|
#if 0
|
|
arp_start ();
|
|
for (int i = 0; i < 4; i++) {
|
|
arp_end ();
|
|
arp_start ();
|
|
BasisBlade *vec = blades[i];
|
|
printf ("%s: %@\n", names[i], vec);
|
|
for (int j = 0; j < 4; j++) {
|
|
BasisBlade *bvec = [vec outerProduct:blades[j]];
|
|
if (![bvec scale]) {
|
|
continue;
|
|
}
|
|
printf ("%s^%s: %@\n", names[i], names[j], bvec);
|
|
for (int k = 0; k < 4; k++) {
|
|
BasisBlade *tvec = [bvec outerProduct:blades[k]];
|
|
if (![tvec scale]) {
|
|
continue;
|
|
}
|
|
printf ("%s^%s^%s: %@\n", names[i], names[j], names[k],
|
|
tvec);
|
|
for (int l = 0; l < 4; l++) {
|
|
BasisBlade *qvec = [tvec outerProduct:blades[l]];
|
|
if (![qvec scale]) {
|
|
continue;
|
|
}
|
|
printf ("%s^%s^%s^%s: %@\n",
|
|
names[i], names[j], names[k], names[l],
|
|
qvec);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
arp_end ();
|
|
#endif
|
|
arp_start ();
|
|
|
|
Metric *m = [Metric R:3,0,1];
|
|
BasisBlade *ad = [a geometricProduct:d metric:m];
|
|
BasisBlade *prod = [ad geometricProduct:ad metric:m];
|
|
printf ("%s%s %s%s: %@\n",
|
|
names[0], names[3], names[0], names[3], prod);
|
|
|
|
Algebra *alg = [Algebra R:3, 0, 1];
|
|
double plane1_vals[4] = {1, 0, 0, 8};
|
|
double plane2_vals[4] = {0, 1, 0, 8};
|
|
double origin_vals[4] = {0, 0, 0, 1};
|
|
MultiVector *plane1 = [alg group:0 values:plane1_vals];
|
|
MultiVector *plane2 = [alg group:0 values:plane2_vals];
|
|
MultiVector *origin = [alg group:3 values:origin_vals];
|
|
|
|
MultiVector *line = [plane1 wedge:plane2];
|
|
MultiVector *point = [[line dot:origin] product:[line reverse]];
|
|
printf ("plane1:%@\nplane2:%@\nline:%@\norigin:%@\n", plane1, plane2, line, origin);
|
|
printf ("point:%@\n", point);
|
|
|
|
arp_end ();
|
|
}
|
|
|
|
typedef struct var_s {
|
|
string name;
|
|
MultiVector *value;
|
|
} var_t;
|
|
|
|
static hashtab_t *symtab;
|
|
|
|
typedef enum {
|
|
EOF, SEMI,
|
|
VAR, EQUAL,
|
|
ID, VALUE,
|
|
OPENP, CLOSEP,
|
|
OPENB, CLOSEB,
|
|
OPENS, CLOSES,
|
|
MUL, DIV, PLUS, MINUS,
|
|
WEDGE, ANTIWEDGE, DOT,
|
|
REVERSE, DUAL,
|
|
} token_e;
|
|
|
|
script_t script;
|
|
string token_str;
|
|
token_e lookahead = -1;
|
|
|
|
typedef struct token_s {
|
|
token_e id;
|
|
union {
|
|
MultiVector *value;
|
|
string name;
|
|
};
|
|
} token_t;
|
|
|
|
static token_t
|
|
get_token ()
|
|
{
|
|
if (!Script_TokenAvailable (script, 1)) {
|
|
return {EOF, nil};
|
|
}
|
|
Script_GetToken (script, 1);
|
|
switch (token_str) {
|
|
case "var": return {VAR, nil};
|
|
case "=": return {EQUAL, nil};
|
|
case ";": return {SEMI, nil};
|
|
case "(": return {OPENP, nil};
|
|
case ")": return {CLOSEP, nil};
|
|
case "[": return {OPENB, nil};
|
|
case "]": return {CLOSEB, nil};
|
|
case "{": return {OPENS, nil};
|
|
case "}": return {CLOSES, nil};
|
|
case "*": return {MUL, nil};
|
|
case "/": return {DIV, nil};
|
|
case "+": return {PLUS, nil};
|
|
case "-": return {MINUS, nil};
|
|
case "^": return {WEDGE, nil};
|
|
case "&": return {ANTIWEDGE, nil};
|
|
case ".": return {DOT, nil};
|
|
case "~": return {REVERSE, nil};
|
|
case "!": return {DUAL, nil};
|
|
}
|
|
return {ID, .name = token_str };
|
|
}
|
|
|
|
static void
|
|
syntax_error ()
|
|
{
|
|
obj_error (nil, 0, "syntax error before `%s': %d\n", token_str,
|
|
Script_GetLine (script));
|
|
}
|
|
|
|
static int
|
|
match (token_e token)
|
|
{
|
|
if (lookahead == -1) {
|
|
lookahead = get_token ().id;
|
|
}
|
|
return token == lookahead;
|
|
}
|
|
|
|
static void
|
|
advance ()
|
|
{
|
|
lookahead = get_token ().id;
|
|
}
|
|
|
|
static Algebra *algebra;
|
|
static MultiVector *minus_one;
|
|
static MultiVector *expression ();
|
|
|
|
static int
|
|
is_digit (string x)
|
|
{
|
|
return (x == "0" || x == "1" || x == "2" || x == "3" ||
|
|
x == "4" || x == "5" || x == "6" || x == "7" ||
|
|
x == "8" || x == "9");
|
|
}
|
|
|
|
static int
|
|
is_number (string x)
|
|
{
|
|
return x == "." || is_digit (x);
|
|
}
|
|
|
|
static MultiVector *
|
|
factor ()
|
|
{
|
|
MultiVector *vec;
|
|
if (match (REVERSE)) {
|
|
advance ();
|
|
vec = [factor () reverse];
|
|
} else if (match (DUAL)) {
|
|
advance ();
|
|
vec = [factor () dual];
|
|
} else if (match (MINUS)) {
|
|
advance ();
|
|
vec = [minus_one product:factor ()];
|
|
} else if (match (OPENP)) {
|
|
advance ();
|
|
vec = expression ();
|
|
if (!match (CLOSEP)) {
|
|
syntax_error ();
|
|
}
|
|
advance ();
|
|
} else if (match (ID)) {
|
|
if (is_digit (str_mid (token_str, 0, 1))) {
|
|
string num_str = nil;
|
|
string blade_str = nil;
|
|
int pos = 0;
|
|
while (is_number (str_mid (token_str, pos, pos + 1))) {
|
|
pos++;
|
|
}
|
|
num_str = str_mid (token_str, 0, pos);
|
|
if (str_mid (token_str, pos, pos + 1) == "e") {
|
|
blade_str = str_mid (token_str, ++pos);
|
|
}
|
|
BasisBlade *blade = [BasisBlade basis:0];
|
|
pos = 0;
|
|
while (is_digit (str_mid (blade_str, pos, pos + 1))) {
|
|
int x = str_char (blade_str, pos++) - '0';
|
|
BasisBlade *new = [BasisBlade basis:1 << x];
|
|
blade = [blade outerProduct:new];
|
|
}
|
|
|
|
double num = strtod (num_str, nil) * [blade scale];
|
|
vec = [algebra ofGrade:[blade grade]];
|
|
*[vec componentFor:blade] = num;
|
|
} else {
|
|
var_t *var = Hash_Find (symtab, token_str);
|
|
if (!var) {
|
|
syntax_error ();
|
|
}
|
|
vec = var.value;
|
|
}
|
|
advance ();
|
|
} else {
|
|
syntax_error ();
|
|
vec = nil;
|
|
}
|
|
return vec;
|
|
}
|
|
|
|
static MultiVector *
|
|
high_term ()
|
|
{
|
|
MultiVector *vec = nil;
|
|
SEL op = nil;
|
|
while (1) {
|
|
if (vec) {
|
|
vec = [vec performSelector:op withObject: factor()];
|
|
} else {
|
|
vec = factor ();
|
|
}
|
|
if (match (WEDGE)) {
|
|
op = @selector(wedge:);
|
|
advance ();
|
|
} else if (match (DOT)) {
|
|
op = @selector(dot:);
|
|
advance ();
|
|
} else {
|
|
return vec;
|
|
}
|
|
}
|
|
}
|
|
|
|
static MultiVector *
|
|
term ()
|
|
{
|
|
MultiVector *vec = nil;
|
|
SEL op = nil;
|
|
while (1) {
|
|
if (vec) {
|
|
vec = [vec performSelector:op withObject: high_term()];
|
|
} else {
|
|
vec = high_term ();
|
|
}
|
|
if (match (REVERSE) || match (DUAL) || match (OPENP) || match (ID)) {
|
|
op = @selector(product:);
|
|
} else if (match (DIV)) {
|
|
op = @selector(divide:);
|
|
advance ();
|
|
} else {
|
|
return vec;
|
|
}
|
|
}
|
|
}
|
|
|
|
static MultiVector *
|
|
expression ()
|
|
{
|
|
MultiVector *vec = nil;
|
|
SEL op = nil;
|
|
while (1) {
|
|
if (vec) {
|
|
vec = [vec performSelector:op withObject: term()];
|
|
} else {
|
|
vec = term ();
|
|
}
|
|
if (match (PLUS)) {
|
|
op = @selector(plus:);
|
|
advance ();
|
|
} else if (match (MINUS)) {
|
|
op = @selector(minus:);
|
|
advance ();
|
|
} else {
|
|
return vec;
|
|
}
|
|
}
|
|
}
|
|
|
|
static var_t *
|
|
assignment ()
|
|
{
|
|
if (!match (ID) || is_digit (str_mid (token_str, 0, 1))) {
|
|
printf ("%s\n", token_str);
|
|
syntax_error ();
|
|
}
|
|
var_t *var = Hash_Find (symtab, token_str);
|
|
if (!var) {
|
|
syntax_error ();
|
|
}
|
|
advance ();
|
|
if (!match (EQUAL)) {
|
|
syntax_error ();
|
|
}
|
|
advance ();
|
|
var.value = [expression () retain];
|
|
return var;
|
|
}
|
|
|
|
static var_t *
|
|
declaration ()
|
|
{
|
|
if (!match (ID) || is_digit (str_mid (token_str, 0, 1))) {
|
|
syntax_error ();
|
|
}
|
|
if (Hash_Find (symtab, token_str)) {
|
|
syntax_error ();
|
|
}
|
|
var_t *var = obj_malloc (sizeof (var_t));
|
|
var.name = str_hold (str_unmutable (token_str));
|
|
Hash_Add (symtab, var);
|
|
assignment ();
|
|
return var;
|
|
}
|
|
|
|
static int
|
|
parse_script (string name, QFile file)
|
|
{
|
|
script = Script_New ();
|
|
Script_SetSingle (script, "()[]{}/+-^&~=;!");
|
|
token_str = Script_FromFile (script, name, file);
|
|
|
|
while (!match (EOF)) {
|
|
arp_end ();
|
|
arp_start ();
|
|
var_t *var;
|
|
if (match (VAR)) {
|
|
advance ();
|
|
var = declaration ();
|
|
printf ("var %s = %@\n", var.name, var.value);
|
|
} else {
|
|
var = assignment ();
|
|
printf ("%s = %@\n", var.name, var.value);
|
|
}
|
|
advance ();
|
|
}
|
|
|
|
Script_Delete (script);
|
|
return 1;
|
|
}
|
|
|
|
static string
|
|
get_symtab_key (void *var, void *unused)
|
|
{
|
|
return ((var_t *) var).name;
|
|
}
|
|
|
|
static Algebra *
|
|
parse_algebra (string spec)
|
|
{
|
|
ivec3 R = {};
|
|
string s = spec;
|
|
if (is_digit (str_mid (spec, 0, 1))) {
|
|
for (int i = 0; i < 3; i++) {
|
|
int end = 0;
|
|
R[i] = strtol (s, &end, 0);
|
|
string e = str_mid (s, end, end + 1);
|
|
if (!e) {
|
|
break;
|
|
}
|
|
if (e != ",") {
|
|
goto bad_spec;
|
|
}
|
|
s = str_mid (s, end + 1);
|
|
}
|
|
if (!R[0] && !R[1] && !R[2]) {
|
|
goto bad_spec;
|
|
}
|
|
return [Algebra R:R[0], R[1], R[2]];
|
|
} else {
|
|
}
|
|
bad_spec:
|
|
printf ("bad algebra spec: %s\n", spec);
|
|
return nil;
|
|
}
|
|
|
|
int
|
|
main (int argc, string *argv)
|
|
{
|
|
symtab = Hash_NewTable (127, get_symtab_key, nil, nil);
|
|
if (argc < 2) {
|
|
basic_test ();
|
|
} else {
|
|
arp_start ();
|
|
algebra = [[Algebra PGA:3] retain];
|
|
arp_end ();
|
|
arp_start ();
|
|
double m1 = -1;
|
|
minus_one = [[algebra ofGrade:0 values:&m1] retain];
|
|
arp_end ();
|
|
arp_start ();
|
|
for (int i = 1; i < argc; i++) {
|
|
if (argv[i] == "-a") {
|
|
Algebra *a = [parse_algebra (argv[++i]) retain];
|
|
if (!a) {
|
|
return 1;
|
|
}
|
|
[algebra release];
|
|
algebra = a;
|
|
[minus_one release];
|
|
minus_one = [[algebra ofGrade:0 values:&m1] retain];
|
|
continue;
|
|
}
|
|
QFile file = Qopen (argv[i], "rt");
|
|
if (file) {
|
|
arp_end ();
|
|
arp_start ();
|
|
printf ("Using algebra %@\n", algebra);
|
|
int res = parse_script (argv[i], file);
|
|
Qclose (file);
|
|
if (!res) {
|
|
return 1;
|
|
}
|
|
} else {
|
|
printf ("%s: failed to open '%s'\n", argv[0], argv[i]);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|