Check-in [2b421a41f8]
Not logged in
Bounty program for improvements to Tcl and certain Tcl packages.
Tcl 2018 Conference, Houston/TX, US, Oct 15-19
Send your abstracts to tclconference@googlegroups.com or submit via the online form
by Aug 20.

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Loosing the cmodules that have been sent over to odielib
Timelines: family | ancestors | descendants | both | autosetup
Files: files | file ages | folders
SHA1: 2b421a41f857a0badc273159f67d78860791f90f
User & Date: hypnotoad 2015-03-18 16:16:19
Context
2015-03-18
21:54
Adding an -x-includes flag for Macosx to point it to where Xquartz stores its includes Typo fix in codebale check-in: f825c3ba37 user: hypnotoad tags: autosetup
16:16
Loosing the cmodules that have been sent over to odielib check-in: 2b421a41f8 user: hypnotoad tags: autosetup
15:11
Fixes to the boot process for installed autosetup check-in: 4f8d078657 user: hypnotoad tags: autosetup
Changes
Unified Diff Ignore Whitespace Patch
Deleted cmodules/btree/cthulhu.ini.
1
2
3
set here [file dirname [file normalize [info script]]]
::cthulhu::add_directory $here {
}
<
<
<






Deleted cmodules/btree/tree.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
/*
** This file implements an in-memory balanced binary tree.  This code
** was originally brought in to implement an efficient priority queue
** for the Dijkstra's shortest path algorithm in crewroute.c.
**
** This file contains code imported into READI from another project.
** The text of the original header comment follows:
*/
/*
** A package of routines for handling balanced binary trees.
** Copyright (c) 1990 by D. Richard Hipp
*/

#include <stdlib.h>
#include "odieInt.h"


/* Each node in the binary tree is represented by a single instance
** of the following structure
*/
typedef struct TreeElem TreeElem;
struct TreeElem {
  void *data;             /* Pointer to the user's data */
  void *key;              /* The key associated with this element */
  TreeElem *left;         /* Left daughter */
  TreeElem *right;        /* Right daughter */
  int weight;             /* Weight of this node */
};

/* Turn bulk memory into a Tree structure
*/
void TreeInit(
  Tree *tree,                                 /* Tree object to initialize */
  int (*xCompare)(const void*, const void*),  /* Comparison function */
  void *(*xCopy)(const void*),                /* Key copy function or NULL */
  void (*xFree)(void*)                        /* Key delete function or NULL */
){
  tree->xCompare = xCompare;
  tree->xCopy = xCopy;
  tree->xFree = xFree;
  tree->top = 0;
}

/* Return the number of elements in the tree.
*/
int TreeCount(Tree *pTree){
  if( pTree && pTree->top ){
    return pTree->top->weight;
  }else{
    return 0;
  }
}

/* Delete a single node of the binary tree and all of its children
*/
static void TreeClearNode(TreeElem *p, void (*xFree)(void*)){
  if( p==0 ) return;
  if( p->left ) TreeClearNode(p->left, xFree);
  if( p->right ) TreeClearNode(p->right, xFree);
  if( xFree ){
    xFree(p->key);
  }
  Odie_Free((char *)p);
}

/* Remove all nodes from a tree
*/
void TreeClear(Tree *tree){
  if( tree->top ){
    TreeClearNode(tree->top, tree->xFree);
  }
  tree->top = 0;
}

/* Find the element of the tree (if any) whose key matches "key".
** Return a pointer to the data for that element.  If no match
** is found, return NULL.
*/
void *TreeFind(Tree *tree, const void *key){
  TreeElem *p;

  p = tree->top;
  while( p ){
    int c = tree->xCompare(p->key, key);
    if( c==0 ){
      return p->data;
    }else if( c<0 ){
      p = p->right;
    }else{
      p = p->left;
    }
  }
  return 0;
}

/* If the node with key "key" is the left-most element of the tree, 
** return 0.  If it is the second to the left, return 1.  And so
** forth.
**
** If there is no node in the tree with the key "key", then return
** the number that would have been returned if such a node were
** inserted.
*/
int TreeRank(Tree *tree, const void *key){
  TreeElem *p;
  int rank = 0;

  p = tree->top;
  while( p ){
    int c = tree->xCompare(p->key, key);
    if( c==0 ){
      rank += p->left ? p->left->weight: 0;
      break;
    }else if( c<0 ){
      rank += (p->left ? p->left->weight: 0) + 1;
      p = p->right;
    }else{
      p = p->left;
    }
  }
  return rank;
}

/* Return a pointer to the N-th element of a tree.  (The left-most
** element is number 0, the next is number 1 and so forth.)
*/
static TreeElem *TreeFindNthElem(Tree *tree, int n){
  TreeElem *p;

  p = tree->top;
  while( p ){
    int c = p->left ? p->left->weight : 0;
    if( n==c ){
      return p;
    }else if( n>c ){
      n -= c+1;
      p = p->right;
    }else{
      p = p->left;
    }
  }
  return 0;
}

/* Return the data associated with the N-th element of the tree.  Return
** NULL if there is no N-th element.
*/
void *TreeData(Tree *tree, int n){
  TreeElem *p = TreeFindNthElem(tree,n);
  return p ? p->data : 0;
}

/* Return the key associated with the N-th element of the tree.  Return
** NULL if there is no N-th element.
*/
const void *TreeKey(Tree *tree, int n){
  TreeElem *p = TreeFindNthElem(tree,n);
  if( p ){
    return p->key;
  }else{
    return 0;
  }
}

/*
** Definitions:
** WEIGHT
**   The weight of a node is the total number of children for the node
**   plus 1.  Leaf nodes have a weight of 1.  The root node has a weight
**   which equals the number of nodes in the tree.
**
** BALANCE
**   A node is balanced if the weight of the one child is not more than
**   twice the weight of the other child.
*/

/* The following routine rebalances the tree rooted at *ppElem after
** the insertion or deletion of a single ancestor.
*/
static void TreeBalance(TreeElem **ppElem){
  TreeElem *n;     /* Pointer to self */
  int l,r;         /* Weight of left and right daughters */
  int a,b;         /* Weights of various nodes */

  if( ppElem==0 || (n=*ppElem)==0 ) return;
  l = n->left ? n->left->weight: 0;
  r = n->right ? n->right->weight: 0;
  if( l>r*2 ){    /* Too heavy on the left side */
    TreeElem *nl;     /* Pointer to left daughter */
    TreeElem *nr;     /* Pointer to right daughter */
    int ll, lr;       /* Weight of left daughter's left and right daughter */
    nl = n->left;
    ll = nl->left ? nl->left->weight: 0;
    lr = nl->right ? nl->right->weight: 0;
    if( ll>lr || nl->right==0 ){
      /*
      ** Convert from:  n     to:  nl
      **               / \        /  \
      **              nl  c      a    n
      **             /  \            / \
      **            a    b          b   c
      */
      n->left = nl->right;
      nl->right = n;
      n->weight = a = r + lr + 1;
      nl->weight = a + ll + 1;
      *ppElem = nl;
    }else{
      /*
      ** Convert from:  n        to:  nr
      **               / \           /  \
      **             nl   d        nl    n
      **            /  \          / \   / \
      **           a    nr       a   b c   d
      **               /  \
      **              b    c
      */
      int lrl, lrr;    /* Weight of Great-granddaughter nodes */
      nr = nl->right;
      lrl = nr->left ? nr->left->weight: 0;
      lrr = nr->right ? nr->right->weight: 0;
      nl->right = nr->left;
      nr->left = nl;
      n->left = nr->right;
      nr->right = n;
      n->weight = a = lrr + r + 1;
      nl->weight = b = ll + lrl + 1;
      nr->weight = a + b + 1;
      *ppElem = nr;
    }
  }else if( r>l*2 ){/* Too deep on the right side */
    TreeElem *nl;     /* Pointer to left daughter */
    TreeElem *nr;     /* Pointer to right daughter */
    int rl, rr;       /* Weight of right daughter's left and right daughter */
    nr = n->right;
    rl = nr->left ? nr->left->weight: 0;
    rr = nr->right ? nr->right->weight: 0;
    if( rr>rl || nr->left==0 ){
      /*
      ** Convert from:  n         to:  nr
      **               / \            /  \
      **              a   nr         n    c 
      **                 /  \       / \
      **                b    c     a   b
      */
      n->right = nr->left;
      nr->left = n;
      n->weight = a = l + rl + 1;
      nr->weight = a + rr + 1;
      *ppElem = nr;
    }else{
      /*
      ** Convert from:  n         to:  nl
      **               / \            /  \
      **              a   nr         n    nr
      **                 /  \       / \   / \
      **               nl    d     a   b c   d
      **              /  \
      **             b    c
      */
      int rll,rlr;    /* Weights of great-granddaughter nodes */
      nl = nr->left;
      rll = nl->left ? nl->left->weight: 0;
      rlr = nl->right ? nl->right->weight: 0;
      nr->left = nl->right;
      nl->right = nr;
      n->right = nl->left;
      nl->left = n;
      n->weight = a = l + rll + 1;
      nr->weight = b = rr + rlr + 1;
      nl->weight = a + b + 1;
      *ppElem = nl;
    }
  }else{ /* Node is already balanced.  Just recompute its weight. */
    n->weight = l + r + 1;
  }
}

/* This routine either changes the data on an existing node in the tree,
** or inserts a new node.  "key" identifies the node.  If the data on
** an existing node is changed, then the function returns the old data.
** If a new node is created, NULL is returned.
*/
static void *TreeInsertElement(
  Tree *pTree,                /* The root of the tree */
  void *key,                  /* Insert data at this key */
  void *data                  /* Data to be inserted */
){
  TreeElem *n;
  void *old = 0;
  TreeElem **h[100];  /* Sufficient for a tree with up to 4.0E+17 nodes */
  int level = 0;


  h[0] = &pTree->top;
  level = 1;
  n = pTree->top;
  while( n ){
    int c;
    c = pTree->xCompare(key, n->key);
    if( c<0 ){
      h[level++] = &(n->left);
      n = n->left;
    }else if( c>0 ){
      h[level++] = &(n->right);
      n = n->right;
    }else{
      old = n->data;
      n->data = data;                /* Replace data in an existing node */
      break;
    }
  }
  if( n==0 ){                     /* Insert a leaf node */
    level--;
    n = *h[level] = (TreeElem *)Odie_Alloc( sizeof(TreeElem) );
    if( n==0 ){
      return data;
    }
    n->data = data;
    if( pTree->xCopy ){
      n->key = pTree->xCopy(key);
    }else{
      n->key = key;
    }
    n->left = n->right = 0;
    while( level>=0 ) TreeBalance(h[level--]);
  }
  return old;
}

/* Unlink the N-th node of the tree and return a pointer to that
** node.  (The left-most node is 0, the next node to the right is
** 1 and so forth.)
*/
static TreeElem *TreeDeleteNthElem(TreeElem **ppTop, int N){
  TreeElem *p;        /* For walking the tree */
  int level = 0;      /* Depth of the blancing stack */
  TreeElem **h[100];  /* Balance stack.  100 is sufficient for balancing
                      ** a tree with up to 4.0E+17 nodes */

  h[0] = ppTop;
  level = 1;
  p = *ppTop;
  while( p ){
    int w;
    w = (p->left ? p->left->weight: 0);
    if( N>w ){
      h[level++] = &(p->right);
      p = p->right;
      N -= w+1;
    }else if( N<w ){
      h[level++] = &(p->left);
      p = p->left;
    }else{
      break;
    }
  }
  if( p ){
    level--;
    if( p->left==0 ){
      *h[level] = p->right;
      level--;
    }else if( p->right==0 ){
      *h[level] = p->left;
      level--;
    }else{
      TreeElem *x;
      x = TreeDeleteNthElem(&(p->right),0);
      x->right = p->right;
      x->left = p->left;
      *h[level] = x;
    }
    while( level>=0 ) TreeBalance(h[level--]);
  }
  return p;
}

/* Unlink the node of the tree corresponding to key and return a pointer 
** to that node.
*/
static TreeElem *TreeDeleteElem(Tree *tree, const void *key){
  TreeElem *p;        /* For walking the tree */
  int level = 0;      /* Depth of the blancing stack */
  TreeElem **h[100];  /* Balance stack.  100 is sufficient for balancing
                      ** a tree with up to 4.0E+17 nodes */

  h[0] = &tree->top;
  level = 1;
  p = tree->top;
  while( p ){
    int w;
    w = tree->xCompare(p->key, key);
    if( w<0 ){
      h[level++] = &(p->right);
      p = p->right;
    }else if( w>0 ){
      h[level++] = &(p->left);
      p = p->left;
    }else{
      break;
    }
  }
  if( p ){
    level--;
    if( p->left==0 ){
      *h[level] = p->right;
      level--;
    }else if( p->right==0 ){
      *h[level] = p->left;
      level--;
    }else{
      TreeElem *x;
      x = TreeDeleteNthElem(&(p->right),0);
      x->right = p->right;
      x->left = p->left;
      *h[level] = x;
    }
    while( level>=0 ) TreeBalance(h[level--]);
  }
  return p;
}

/* Insert new data into a node of the tree.  The node is identified
** by "key".
**
** If the new data is NULL, then node is deleted.
**
** If the node aready exists, the new data overwrites the old and
** the old data is returned.  If the node doesn't already exist, then
** a new node is created and the function returns NULL.
*/
void *TreeInsert(Tree *tree, void *key, void *data){
  void *old;
  if( data==0 ){
    TreeElem *elem = TreeDeleteElem(tree, key);
    if( elem ){
      if( tree->xFree ){
        tree->xFree(elem->key);
      }
      old = elem->data;
      Odie_Free((char *)elem);
    }else{
      old = 0;
    }
  }else{
    old = TreeInsertElement(tree,key,data);
  }
  return old;
}

/* Change the data on the n-th node of the tree.  The old data
** is returned.
**
** If data==NULL, then the n-th node of the tree is deleted.  (The
** data associated with that node is still returned.)
**
** If the value N is out-of-bounds, then no new node is created.
** Instead, the "data" parameter is returned.
*/
void *TreeChangeNth(Tree *tree, int n, void *data){
  void *old;
  if( data==0 ){
    TreeElem *elem = TreeDeleteNthElem(&tree->top,n);
    if( elem ){
      if( tree->xFree ){
        tree->xFree(elem->key);
      }
      old = elem->data;
      Odie_Free((char *)elem);
    }else{
      old = 0;
    }
  }else{
    TreeElem *elem = TreeFindNthElem(tree,n);
    if( elem ){
      old = elem->data;
      elem->data = data;
    }else{
      old = data;
    }
  }
  return old;
}

int DLLEXPORT Tree_Init(Tcl_Interp *interp){
  #if IRM_MEM_DEBUG
  Tcl_LinkVar(interp, "module_malloc(tree)", (char*)&nMalloc,
        TCL_LINK_INT | TCL_LINK_READ_ONLY);
  #endif
  return TCL_OK;
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted cmodules/btree/tree.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*
** Original code by D. Richard Hipp, circa 1990.
** Modified for READI, 2005-11-21.
*/

/* A complete binary tree is defined by an instance of the following
** structure
*/
typedef struct Tree Tree;
struct Tree {
  int (*xCompare)(const void*, const void*); /* Comparison function */
  void *(*xCopy)(const void*);               /* Key copy function, or NULL */
  void (*xFree)(void*);                      /* Key delete function */
  struct TreeElem *top;                      /* The top-most node of the tree */
};

void TreeInit(
  Tree *tree,                                 /* Tree object to initialize */
  int (*xCompare)(const void*, const void*),  /* Comparison function */
  void *(*xCopy)(const void*),                /* Key copy function or NULL */
  void (*xFree)(void*)                        /* Key delete function or NULL */
);
void TreeClear(Tree *tree);
void *TreeChangeNth(Tree *tree, int n, void *data);
void *TreeInsert(Tree *tree, void *key, void *data);
void *TreeFind(Tree *tree, const void *key);
int TreeRank(Tree *tree, const void *key);
void *TreeData(Tree *tree, int n);
const void *TreeKey(Tree *tree, int n);
int TreeCount(Tree*);
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























































Deleted cmodules/geometry/cthulhu.ini.
1
2
3
4
5
set here [file dirname [file normalize [info script]]]
foreach file {plotter.c slicer.c wallset.c} {
  ::cthulhu::detect_cases [file join $here generic $file]
}
::cthulhu::add_directory [file join $here generic] {}
<
<
<
<
<










Deleted cmodules/geometry/generic/geometry.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
/*
** Load the entire geometry module
*/
#include "odieInt.h"

/*
** Declare our Tcl Obj Types
*/

const Tcl_ObjType polygon_tclobjtype = {
  "polygon", /* name */
  &PolygonObj_freeIntRepProc, /* freeIntRepProc */
  &PolygonObj_dupIntRepProc, /* dupIntRepProc */
  &PolygonObj_updateStringProc, /* updateStringProc */
  &PolygonObj_setFromAnyProc /* setFromAnyProc */
};

const Tcl_ObjType segmentset_tclobjtype = {
  "segmentset", /* name */
  &SegmentSetObj_freeIntRepProc, /* freeIntRepProc */
  &SegmentSetObj_dupIntRepProc, /* dupIntRepProc */
  &SegmentSetObj_updateStringProc, /* updateStringProc */
  &SegmentSetObj_setFromAnyProc /* setFromAnyProc */
};

Tcl_Obj *Odie_NewPolygonObj(Poly *pPoly) {
  Tcl_Obj *pResult;
  pResult=Tcl_NewObj();
  Tcl_InvalidateStringRep(pResult);
  pResult->internalRep.otherValuePtr=pPoly;
  pResult->typePtr=&polygon_tclobjtype;
  return pResult;
}

int Odie_GetPolygonFromObj(Tcl_Interp *interp,Tcl_Obj *objPtr,Poly **ptr,int *created) {
  Poly *p;
  *created=0;

  if(objPtr->typePtr) {
    if(objPtr->typePtr==&polygon_tclobjtype && objPtr->internalRep.otherValuePtr) {
      /*
      ** Object is already of the type requested
      */
      *ptr=objPtr->internalRep.otherValuePtr;
      return TCL_OK;
    }
  }
  int k,i;
  if( Tcl_ListObjLength(interp, objPtr, &k) ) return TCL_ERROR;
  if( k<6 ){
    Tcl_AppendResult(interp, "need at least 3 vertices", 0);
    return TCL_ERROR;
  }
  if( k&1 ){
    Tcl_AppendResult(interp, "coordinates should come in pairs", 0);
    return TCL_ERROR;
  }
  p=(Poly *)Odie_Alloc(sizeof(*p)+(k+2)*sizeof(p->v[0]));
  p->nVertex=k/2;
  for(i=0; i<p->nVertex; i++){
    Tcl_Obj *pElem;
    double d;
    Tcl_ListObjIndex(0, objPtr, i*2, &pElem);
    if(Tcl_GetDoubleFromObj(interp, pElem, &d)) goto createfail;
    p->v[i][X_IDX] = d;
    Tcl_ListObjIndex(0, objPtr, i*2+1, &pElem);
    if(Tcl_GetDoubleFromObj(interp, pElem, &d)) goto createfail;
    p->v[i][Y_IDX] = d;
  }

  if(Odie_PolygonComputeArea(interp,p)==TCL_OK) {
    *ptr=p;
    if(Tcl_IsShared(objPtr)) {
      *created=1;
    } else {
      /* Shimmer this object to the requested type */
      if(objPtr->typePtr && objPtr->typePtr->freeIntRepProc) {
        Tcl_FreeInternalRepProc *freeIntRepProc=objPtr->typePtr->freeIntRepProc;
        freeIntRepProc(objPtr);
      }
      Tcl_InvalidateStringRep(objPtr);
      objPtr->internalRep.otherValuePtr=p;
      objPtr->typePtr=&polygon_tclobjtype;
    }
    return TCL_OK;
  }
  
createfail:
  Odie_Free((char *)p);
  return TCL_ERROR;
}

int PolygonObj_setFromAnyProc(Tcl_Interp *interp,Tcl_Obj *objPtr) {
  if(objPtr->typePtr) {
    if(objPtr->typePtr==&polygon_tclobjtype) {
      /*
      ** Object is already of the type requested
      */
      return TCL_OK;
    }
  }
  Poly *p;
  int created=0;
  if(Odie_GetPolygonFromObj(interp,objPtr,&p,&created)) {
    return TCL_ERROR;
  }
  Tcl_InvalidateStringRep(objPtr);
  objPtr->internalRep.otherValuePtr=p;
  objPtr->typePtr=&polygon_tclobjtype;
  return TCL_OK;
}

void PolygonObj_updateStringProc(Tcl_Obj *objPtr) {
  char outbuffer[128];
  Tcl_DString result;
  Poly *p=objPtr->internalRep.otherValuePtr;
  int i,j;
  Tcl_DStringInit(&result);
  j=p->nVertex-1;
  if(p->v[0][X_IDX]==p->v[j][X_IDX] && p->v[0][Y_IDX]==p->v[j][Y_IDX]) {
    j=p->nVertex-2;
  }
  for(i=0; i<=j; i++){
    sprintf(outbuffer,"%g %g",(float)p->v[i][X_IDX],(float)p->v[i][Y_IDX]);
    Tcl_DStringAppendElement(&result,outbuffer);
  }
  objPtr->length=Tcl_DStringLength(&result);
  objPtr->bytes=Odie_Alloc(objPtr->length+1);
  strcpy(objPtr->bytes,Tcl_DStringValue(&result));
  Tcl_DStringFree(&result);
}

void PolygonObj_dupIntRepProc(Tcl_Obj *srcPtr,Tcl_Obj *dupPtr) {
  Poly *src=srcPtr->internalRep.otherValuePtr;
  int size=sizeof(*src)+src->nVertex*sizeof(src->v[0]);
  Poly *copy=(Poly *)Odie_Alloc(size);

  memcpy(copy,src,size);
  Tcl_InvalidateStringRep(dupPtr);
  dupPtr->typePtr=&polygon_tclobjtype;
  dupPtr->internalRep.otherValuePtr=copy;
}

void PolygonObj_freeIntRepProc(Tcl_Obj *objPtr) {
  Odie_Free(objPtr->internalRep.otherValuePtr);
  objPtr->internalRep.otherValuePtr=NULL;
  objPtr->typePtr=NULL;
}

void Segset_Insert_Polygon(SegSet *pSet,Poly *p,int fill) {
  int i;
  if(p->nVertex>0) {
    VECTORXY *P;
    P=&p->v[0];
    for(i=1; i<p->nVertex; i++){
      SegSetInsert(pSet,*P,p->v[i],1);
      P=&p->v[i];
    }
    SegSetInsert(pSet,*P,p->v[0],1);
  }
}

void SegSetCopy(SegSet *dest,SegSet *src) {
  SegSetClear(dest);
  memset(dest, 0, sizeof(SegSet));

  Link *pLoop, *pNext;
  for(pLoop=src->pAll; pLoop; pLoop=pNext){
    Segment *pAB;
    pAB = pLoop->pLinkNode;
    pNext = pLoop->pNext;
    SegSetInsert(dest,pAB->from,pAB->to,pAB->isBoundary);
  }
}


/*
** Find and return the line segment that goes from A to B.  Return NULL
** if there is not such line segment
*/
Segment *SegSetFind(SegSet *pSet, VectorXY A, VectorXY B){
  Link *pX;
  Segment *p;
  int h;
  h = hashVectorXY(A);
  for(pX=pSet->hashFrom[h]; pX; pX=pX->pNext){
    p = pX->pLinkNode;
    if( sameVectorXY(p->from, A) && sameVectorXY(p->to, B) ){
      return p;
    }
  }
  return 0;
}

Tcl_Obj *Odie_NewSegmentSetObj(SegSet *pSegSet) {
  Tcl_Obj *pResult;
  pResult=Tcl_NewObj();
  Tcl_InvalidateStringRep(pResult);
  pResult->internalRep.otherValuePtr=pSegSet;
  pResult->typePtr=&segmentset_tclobjtype;
  return pResult;
}

int Odie_GetSegmentSetFromObj(
  Tcl_Interp *interp,
  Tcl_Obj *objPtr,
  SegSet **ptr,
  int *created
) {
  SegSet *pSet=NULL;

  *created=0;  
  if(objPtr->typePtr && objPtr->typePtr->setFromAnyProc==&SegmentSetObj_setFromAnyProc) {
    /*
    ** Object is already of the type requested
    */
    *ptr=objPtr->internalRep.otherValuePtr;
    return TCL_OK;
  }
  if(objPtr->typePtr && objPtr->typePtr->setFromAnyProc==&PolygonObj_setFromAnyProc) {
    *created=1;
    /*
    ** Convert from a polygon
    */
    pSet=(SegSet *)Odie_Alloc(sizeof(SegSet));
    Poly *p=objPtr->internalRep.otherValuePtr;
    Segset_Insert_Polygon(pSet,p,1);
    *ptr=pSet;
    return TCL_OK;
  }

  int i,n;
  if( Tcl_ListObjLength(interp, objPtr, &n) ) return TCL_ERROR;
  if( n%4!=0 ){
    Tcl_AppendResult(interp, "VECTORS argument should contain a multiple of 4 values", 0);
    return TCL_ERROR;
  }
  pSet=(SegSet *)Odie_Alloc(sizeof(SegSet));

  for(i=0; i<n; i+=4){
    int j;
    Segment *found;
    double x[4];
    VECTORXY A,B;

    for(j=0; j<4; j++){
      Tcl_Obj *pObj;
      Tcl_ListObjIndex(0, objPtr, i+j, &pObj);
      if( Tcl_GetDoubleFromObj(interp, pObj, &x[j]) ){
        goto createfail;
      }
    }
    A[X_IDX]=x[0];
    A[Y_IDX]=x[1];
    B[X_IDX]=x[2];
    B[Y_IDX]=x[3];
    /*
    ** Do not insert a vector into the wallset if it
    ** matches a vector already given. It's either redundent
    ** or the edge of a hole
    */
    found=SegSetFind(pSet,A,B);
    if(found) continue;
    SegSetInsert(pSet, A, B, 1);
  }
  
  //Link *pLoop, *pNext;
  //for(pLoop=pSet->pAll; pLoop; pLoop=pNext){
  //  Segment *pAB;
  //  pAB = pLoop->pLinkNode;
  //  pNext = pLoop->pNext;
  //  int h = hashPoint(pAB->from);
  //}
  
  *created=1;
  *ptr=pSet;
  return TCL_OK;
  
createfail:
  SegSetClear(pSet);

  Odie_Free((char *)pSet);
  return TCL_ERROR;
}

int SegmentSetObj_setFromAnyProc(Tcl_Interp *interp,Tcl_Obj *objPtr) {
  if(objPtr->typePtr) {
    if(objPtr->typePtr==&segmentset_tclobjtype) {
      /*
      ** Object is already of the type requested
      */
      return TCL_OK;
    }
  }
  SegSet *p;
  int created=0;
  if(Odie_GetSegmentSetFromObj(interp,objPtr,&p,&created)) {
    return TCL_ERROR;
  }
  Tcl_InvalidateStringRep(objPtr);
  objPtr->internalRep.otherValuePtr=p;
  objPtr->typePtr=&segmentset_tclobjtype;
  return TCL_OK;
}

void SegmentSetObj_updateStringProc(Tcl_Obj *objPtr) {
  char outbuffer[128];
  Tcl_DString result;
  SegSet *pSet=objPtr->internalRep.otherValuePtr;
  Tcl_DStringInit(&result);
  Link *pLoop, *pNext;
  for(pLoop=pSet->pAll; pLoop; pLoop=pNext){
    Segment *pAB;
    pAB = pLoop->pLinkNode;
    pNext = pLoop->pNext;
    sprintf(outbuffer,"%g %g %g %g %d",(float)pAB->from[X_IDX],(float)pAB->from[Y_IDX],(float)pAB->to[X_IDX],(float)pAB->to[Y_IDX],pAB->isBoundary);
    Tcl_DStringAppendElement(&result,outbuffer);
  }
  objPtr->length=Tcl_DStringLength(&result);
  objPtr->bytes=Odie_Alloc(objPtr->length+1);
  strcpy(objPtr->bytes,Tcl_DStringValue(&result));
  Tcl_DStringFree(&result);
}

void SegmentSetObj_dupIntRepProc(Tcl_Obj *srcPtr,Tcl_Obj *dupPtr) {
  SegSet *src=(SegSet*)srcPtr->internalRep.otherValuePtr;
  SegSet *dest=(SegSet*)Odie_Alloc(sizeof(SegSet));

  SegSetCopy(dest,src);

  dupPtr->typePtr=srcPtr->typePtr;
  dupPtr->internalRep.otherValuePtr=dest;
}

void SegmentSetObj_freeIntRepProc(Tcl_Obj *objPtr) {
  SegSet *set=(SegSet *)objPtr->internalRep.otherValuePtr;
  SegSetClear(set);
  Odie_Free(objPtr->internalRep.otherValuePtr);
}


int Odie_GetSegmentGetFromVar(
  Tcl_Interp *interp,
  Tcl_Obj *varName,
  SegSet **dest
) {
  Tcl_Obj *objPtr=Tcl_ObjGetVar2(interp,varName,NULL,0);
  if(!objPtr) {
    Tcl_ResetResult(interp);
    *dest=(SegSet *)Odie_Alloc(sizeof(SegSet));
    return TCL_OK;
  }
  int created;
  SegSet *src;
  if( Odie_GetSegmentSetFromObj(interp, objPtr, &src, &created) ) return TCL_ERROR;
  *dest=(SegSet *)Odie_Alloc(sizeof(SegSet));
  SegSetCopy(*dest,src);
  return TCL_OK;
}

DLLEXPORT int Odie_Geometry_Init(Tcl_Interp *interp) {

  Tcl_RegisterObjType(&polygon_tclobjtype);
  Tcl_RegisterObjType(&segmentset_tclobjtype);
  
  if(Odie_Segset_Init(interp)) return TCL_ERROR;
  if(Odie_Polygon_Init(interp)) return TCL_ERROR;
  if(Odie_Shapes_Init(interp)) return TCL_ERROR;
  if(Odie_Mathtools_Init(interp)) return TCL_ERROR;
  
  Tcl_CreateObjCommand(interp, "plotter", Odie_PlotterCreateProc, 0, 0);
  Tcl_CreateObjCommand(interp, "slicer", Odie_SlicerCreateProc, 0, 0);
  Tcl_CreateObjCommand(interp, "wallset", Odie_WallsetCreateProc, 0, 0);
  return TCL_OK;
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<














































































































































































































































































































































































































































































































































































































































































































































































Deleted cmodules/geometry/generic/geometry.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*
** A bounding box.
*/
typedef unsigned char u8;

/*
** Size of the hash tables
*/
#define SZ_HASH  419

/*
** Grain size for rounding
*/
#define GRAIN 2.5


/*
** An instance of the following structure is used as an entry in
** a doubly linked list.
*/
typedef struct Link Link;
struct Link {
  Link *pNext, **ppPrev; /* Next and prior nodes on the list */
  void *pLinkNode;    /* Structure that this link is a part of */
};

typedef struct Box Box;
struct Box {
  double t, b, l, r;
};

/*
** Every polygon is an instance of the following structure:
*/
typedef struct Point Vertex;
typedef struct Point Point;

struct Point { double x, y; };

typedef struct Poly Poly;
struct Poly {
  int id;            /* Numeric ID of the polygon */
  int nVertex;        /* Number of verticies */
  double area;        /* Area contained within the polygon */
  Box bbox;           /* The bounding box */
  double v[][2];        /* Set of vertices */          
};

/*
** Each straight line in the wallset is recorded as an instance in the
** following structure.
**
** Coordinates are always stored as integers in a right-handed coordinate
** system.  Multiply external coordinates by Wallset.rXZoom and
** Wallset.rYZoom to get the internal coordinates stored here.  Divide
** the coordinates stored here by the zoom factors to get external
** coordinates.
*/

typedef struct Segment Segment;
struct Segment {
  int id;
  double from[2];       /* Beginning coordinate */
  double to[2];        /* Ending coordinate */
  int idLC, idRC;   /* ID numbers of compartments to the left and right */

  Link pAll;         /* All segments */
  Link pHash;        /* All segments with the same hash on id */
  Link pFrom;        /* All segments with the same hash on from */
  Link pTo;          /* All segments with the same hash on to */
  Link pSet;         /* A temporary subset of segments */

  double score;     /* Candidate vertex score */
  unsigned int notOblique:1; /* True if next segment is not at an oblique angle */
  unsigned int isBoundary:8; /* True if this is a boundary segment */
  unsigned int ignore:1;        /* Causes this segment to be ignored on some operations */
  unsigned int midpoint:1;        /* Causes this segment to be ignored on some operations */
  int isRight:4; /* -1 isleft - 0 straight - 1 right */
};

/*
** A complete set of segments
*/
typedef struct SegSet SegSet;
struct SegSet {
  int shared;
  int nSeg;              /* Number of segments in the set */
  Segment *pCurrent;     /* Current segment */
  Link *pAll;            /* All segments connected by Segment.all */
  Link *hashFrom[SZ_HASH];  /* Hash on Segment.orig */
};

/*
** A boundary consists of three or more segments.  Each segment contains
** a direction flag.  The following structure holds a single element
** of a boundary.
*/
typedef struct Boundary Boundary;
struct Boundary {
  Segment *pSeg;   /* The segment of the boundary */
  int backwards;   /* True if the boundary moves backwards on the segment */
};

/*
** Instances of the following structure are used to speed up the
** "WS primary" method.  Each instance of this structure records
** a primary wall for a compartment and a rectangular bounding box
** for that compartment.  When searching for a compartment that
** contains a point, we can do a quick scan of the bounding boxes
** in linear time.
*/
typedef struct ComptBox ComptBox;
struct ComptBox {
  ComptBox *pNext;     /* Next ComptBox in a list of them all */
  Box bbox;            /* The bounding box */
  double area;         /* Area of the bounding box, used for sorting */
  Boundary prim;       /* Primary boundary wall for the compartment */
};

/*
** A wallset is an instance of this structure
*/
typedef struct Wallset Wallset;
struct Wallset {
  int busy;                 /* Do not delete or insert if true */
  double rXZoom, rYZoom;    /* Zoom for input and output */
  ComptBox *pComptBox;      /* Cache of compartment boxes */
  Link *pAll;               /* Any of the segments in the Segment.all ring */
  Link *hashId[SZ_HASH];    /* Hash table for Segment.id */
  Link *hashFrom[SZ_HASH];  /* Hash table for Segment.x0,Segment.y0 */
  Link *hashTo[SZ_HASH];    /* Hash table for Segment.x1,Segment.y1 */
};

/*
** A slicer is an instance of the following structure.
*/
typedef struct Slicer Slicer;
struct Slicer {
  int nSlice;        /* Number of slices */
  struct OneSlice {  /* One entry per slice */
    int idx;            /* Index of this slice in a[] */
    int did;            /* Integer deck id */
    int above;            /* Integer deck id above */
    int below;            /* Integer deck id below */
    
    char *zName;        /* Name of this slice */
    double z;           /* Z coordinate of this slice */
    int nXZ;            /* Number of entries in xz[] */
    double rXShift;     /* Change X values by this amount times rZoom */
    double *xz;         /* Alternating X and Z profile values */
    double mnX, mxX;    /* Min and max Y for the slice (actual coord space) */
    double mnY, mxY;    /* Min and max Y for the slice (actual coord space) */
    double top, btm;    /* Top and bottom of slice in canvas coords. top<btm */
    double upperbound;  /* Half-way from a[i].top to a[i+1].btm */
  } *a;              /* Entries malloced and sorted by increasing z */
  double upper_height;  /* Height to assume for top level if given negative coords */
  double rZoom;      /* Multiply canvas coord by this to get actual coord */
  double rXOffset;   /* X-Shift amount per unit of height */
};

/*
** Initialize a new list element so that it is on a list all by itself
** and so that the ring is a member of structure pContainer.
*/
#define LinkInit(R,C)  ((R).pNext = 0, (R).ppPrev = 0, (R).pLinkNode = (void *)(C))
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<










































































































































































































































































































































Deleted cmodules/geometry/generic/linklist.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/*
** Routines for handling a doubly linked list
*/
#include "odieInt.h"

/*
** Remove a link element from its list.
*/
CTHULHU_INLINE void LinkRemove(Link *p){
  assert( p->pLinkNode !=0 );
  if( p->ppPrev ){
    assert( *p->ppPrev==p );
    *p->ppPrev = p->pNext;
  }
  if( p->pNext ){
    assert( p->pNext->ppPrev == &p->pNext );
    p->pNext->ppPrev = p->ppPrev;
  }
  p->pNext = 0;
  p->ppPrev = 0;
}

/* 
** Add a link to a list
*/
CTHULHU_INLINE void LinkInsert(Link **ppRoot, Link *pLink){
  assert( pLink->ppPrev==0 );
  assert( pLink->pNext==0 );
  assert( pLink->pLinkNode!=0 );
  pLink->ppPrev = ppRoot;
  pLink->pNext = *ppRoot;
  if( pLink->pNext ){
    pLink->pNext->ppPrev = &pLink->pNext;
  }
  *ppRoot = pLink;
}

/*
** Return the number of elements on a linked list
*/

CTHULHU_INLINE int LinkCount(Link *pRoot){
  int cnt = 0;
  while( pRoot ){
    cnt++;
    pRoot = pRoot->pNext;
  }
  return cnt;
}

/*
** Compute a hash on an integer.
*/
CTHULHU_INLINE int hashInt(int x){
  return (x & 0x7fffffff) % SZ_HASH;
}

/*
** Round a coordinate to its nearest grain boundary
*/
CTHULHU_INLINE long intCoord(double x) {
  long idxX = x/GRAIN + (x>0.0 ? 0.5 : -0.5);
  return idxX*GRAIN;
}

/*
** Compute a hash on a point.
*/
CTHULHU_INLINE int hashPoint(VECTORXY p){
  int idxX = p[X_IDX]/GRAIN;
  int idxY = p[Y_IDX]/GRAIN;
  return hashInt(idxX+idxY);
}

CTHULHU_INLINE int hashVectorXY(VECTORXY p){
  int idxX = p[X_IDX]/GRAIN;
  int idxY = p[Y_IDX]/GRAIN;
  return hashInt(idxX+idxY);
}


CTHULHU_INLINE double roundCoord(double x){
    return intCoord(x);
}

/*
** Compute a hash on a pair of floating point number.
*/
CTHULHU_INLINE int hashCoord(double x, double y){
  long idxX = intCoord(x);
  long idxY = intCoord(y);
  return hashInt(idxX+idxY);
}

/*
** Compare to floating point values and return negative, zero, or
** positive if the first value is less than, equal to, or greater
** than the second.
*/
CTHULHU_INLINE int floatCompare(double x0, double x1){
  double diff = x1 - x0;
  if( diff>-GRAIN && diff<GRAIN ){
    diff = 0.0;
  }
  return (int)diff;
}

/*
** Return TRUE if x0,y0 is the same point as x1,y1
*/
CTHULHU_INLINE int samePoint(double x0, double y0, double x1, double y1){
  return floatCompare(x0,x1)==0 && floatCompare(y0,y1)==0;
}

CTHULHU_INLINE int sameVectorXY(VectorXY A, VectorXY B){
  return floatCompare(A[X_IDX],B[X_IDX])==0 && floatCompare(A[Y_IDX],B[Y_IDX])==0;
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<










































































































































































































































Deleted cmodules/geometry/generic/mathtools.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611

/*
** This file is machine generated. Changes will
** be overwritten on the next run of cstruct.tcl
*/
#include "odieInt.h"
#define ODIE_REAL_TOLERANCE 0.001

CTHULHU_INLINE int ODIE_Real_Is_Zero(double x) {
  if(fabs(x) < __FLT_EPSILON__) {
    return 1;
  }
  return 0;
}

int ODIE_Fuzzy_Compare_TclObj(Tcl_Obj *avalue,Tcl_Obj *bvalue) {
  int result;
  double ax,bx;
  if(Tcl_GetDoubleFromObj(NULL,avalue,&ax)) {
    char *a,*b;
    a=Tcl_GetString(avalue);
    b=Tcl_GetString(bvalue);
    result=strcmp(a,b);
  } else if(Tcl_GetDoubleFromObj(NULL,bvalue,&bx)) {
    char *a,*b;
    a=Tcl_GetString(avalue);
    b=Tcl_GetString(bvalue);
    result=strcmp(a,b);
  } else {
    result=ODIE_Fuzzy_Compare(ax,bx);
  }
  return result;
}

Tcl_Obj *ODIE_NewFuzzyObj(double ax) {
  double bx;
  const char *use_format="%g";
  char newstring[128];
  
  if(ODIE_Real_Is_Zero(ax)) {
    return ODIE_REAL_ZERO();
  }
  bx=abs(ax);
  if(bx<1e-4 || bx>1e5) {
    use_format="%g";
  } else if(bx>10.0) {
    use_format="%.1f";
  } else if (bx>1.0) {
    use_format="%.2f";
  } else {
    use_format="%.3f";
  }
  sprintf(newstring,use_format,ax);
  return Tcl_NewStringObj(newstring,-1);
}

CTHULHU_INLINE int ODIE_Fuzzy_Compare(double avalue,double bvalue) {
  /* Handle the simple cases */
  double c=bvalue-avalue;
  if(fabs(c) < __FLT_EPSILON__) return 0;
  if(avalue>bvalue) return 1;
  return -1;
}

CTHULHU_INLINE int ODIE_Fuzzy_GTE(double avalue,double bvalue) {
  /* Handle the simple cases */
  if (avalue==bvalue) return 1;
  if (avalue<=ODIE_REAL_TOLERANCE && bvalue>ODIE_REAL_TOLERANCE) return 0;
  if (avalue>bvalue) return 1;

  /* Add epsilon to the send */
  avalue+=ODIE_REAL_TOLERANCE;
  if (avalue>=bvalue) return 2;

  /* For large quantities, loose the decimal points 
  if(avalue>100.0 && bvalue>100.0) {
    avalue=ceil(avalue);
    bvalue=floor(bvalue);
    if (avalue>=bvalue) return 2;
  }
  */
  return 0;
}

/* Detect of two lines are colinear */
CTHULHU_INLINE int Odie_IsColinear(double x1,double y1,double x2,double y2,double x3,double y3) {
  double c=(x3-x1)*(y2-y1)-(y3-y1)*(x2-x1);
  if(fabs(c) < __FLT_EPSILON__) return 1;
  return 0;
}

/*
** Detect the intersection of two lines
** Returns:
** 0 - no overlap
** 1 - AX1 is on line BX1-BY1
** 2 - AX2 is on line BX1-BY1
** 4 - BX1 is on line AX1-AX2
** 8 - BX2 is on line AX1-AX2
*/
CTHULHU_INLINE int ODIE_Math_LineLineCoincident(
double ax1, double ay1,
double ax2, double ay2,
double bx1, double by1,
double bx2, double by2)
{
  double denom,numera,numerb;

  denom  = (by2-by1) * (ax2-ax1) - (bx2-bx1) * (ay2-ay1);   
  /* Are the line parallel */
  if (!ODIE_Real_Is_Zero(denom)) {
   return 0;
  }
  numera = (bx2-bx1) * (ay1-by1) - (by2-by1) * (ax1-bx1);
  numerb = (ax2-ax1) * (ay1-by1) - (ay2-ay1) * (ax1-bx1);
   
  if (!ODIE_Real_Is_Zero(numera) || !ODIE_Real_Is_Zero(numerb)) {
    return 0;
  }
  return 1;
#ifdef NEVER
  VectorXY A,B,C,D;
  A[X_IDX]=ax1;
  A[Y_IDX]=ay1;
  B[X_IDX]=ax2;
  B[Y_IDX]=ay2;
  C[X_IDX]=bx1;
  C[Y_IDX]=by1;
  D[X_IDX]=bx2;
  D[Y_IDX]=by2;

  int result=0;
  if(ODIE_Math_PointOnSegment(C,D,A)) {
    result|=1;
  }
  if(ODIE_Math_PointOnSegment(C,D,B)) {
    result|=2;
  }
  if(ODIE_Math_PointOnSegment(A,B,C)) {
    result|=4;
  }
  if(ODIE_Math_PointOnSegment(A,B,D)) {
    result|=8;
  }
  return result;
#endif
}

/*
** Detect the intersection of two lines
** Adapted from: http://paulbourke.net/geometry/lineline2d/pdb.c
*/
int ODIE_Math_LineLineIntersect(
double ax1, double ay1,
double ax2, double ay2,
double bx1, double by1,
double bx2, double by2,
double *x, double *y)
{
   double mua,mub;
   double denom,numera,numerb;


   denom  = (by2-by1) * (ax2-ax1) - (bx2-bx1) * (ay2-ay1);
   numera = (bx2-bx1) * (ay1-by1) - (by2-by1) * (ax1-bx1);
   numerb = (ax2-ax1) * (ay1-by1) - (ay2-ay1) * (ax1-bx1);
   
   /* Are the line parallel */
   if (ODIE_Real_Is_Zero(denom)) {
    if (ODIE_Real_Is_Zero(numera) && ODIE_Real_Is_Zero(numerb)) {
      /* Are the line coincident? */
      int within=1;
      if(ax2>ax1) {
        if(bx1>ax2 && bx2>ax2) {
          within=0;
        } else if(bx1<ax1 && bx2<ax1) {
          within=0;
        }
      } else {
        if(bx1>ax1 && bx2>ax1) {
          within=0;
        } else if(bx1<ax2 && bx2<ax2) {
          within=0;
        }
      }
      if(ay2>ay1) {
        if(by1>ay2 && by2>ay2) {
          within=0;
        } else if(by1<ay1 && by2<ay1) {
          within=0;
        }
      } else {
        if(by1>ay1 && by2>ay1) {
          within=0;
        } else if(by1<ay2 && by2<ay2) {
          within=0;
        }
      }
      if(within) {
        *x = (ax1 + ax2) / 2;
        *y = (ay1 + ay2) / 2;
        return(1);
      }
    }
    *x = 0;
    *y = 0;
    return(0);
   }

   /* Is the intersection along the the segments */
   mua = numera / denom;
   mub = numerb / denom;
   if (mua < 0 || mua > 1 || mub < 0 || mub > 1) {
    *x = 0;
    *y = 0;
    return(0);
   }
   *x = ax1 + mua * (ax2 - ax1);
   *y = ay1 + mua * (ay2 - ay1);
   return(1);
}

/*
** Detect the intersection of a line and a sphere
** Adapted from: http://http://paulbourke.net/geometry/circlesphere/raysphere.c
*/
int ODIE_Math_LineSphereIntersect(
double p1_x, double p1_y, double p1_z,
double p2_x, double p2_y, double p2_z,
double sc_x, double sc_y, double sc_z,
double r,
double *mu1, double *mu2)
{
   double a,b,c;
   double bb4ac;
   double dp_x,dp_y,dp_z;
   *mu1 = 0;
   *mu2 = 0;

   dp_x = p2_x - p1_x;
   dp_y = p2_y - p1_y;
   dp_z = p2_z - p1_z;
   a = dp_x * dp_x + dp_y * dp_y + dp_z * dp_z;
   b = 2 * (dp_x * (p1_x - sc_x) + dp_y * (p1_y - sc_y) + dp_z * (p1_z - sc_z));
   c = sc_x * sc_x + sc_y * sc_y + sc_z * sc_z;
   c += p1_x * p1_x + p1_y * p1_y + p1_z * p1_z;
   c -= 2 * (sc_x * p1_x + sc_y * p1_y + sc_z * p1_z);
   c -= r * r;
   bb4ac = b * b - 4 * a * c;
   if (ODIE_Real_Is_Zero(a) || bb4ac < 0) {
      return(0);
   }

   *mu1 = (-b + sqrt(bb4ac)) / (2 * a);
   *mu2 = (-b - sqrt(bb4ac)) / (2 * a);

   return(1);
}
/*
** Detect the intersection of a line and a circle
** Adapted from: http://http://paulbourke.net/geometry/circlesphere/raysphere.c
*/
int ODIE_Math_LineCircleIntersect(
double p1_x, double p1_y,
double p2_x, double p2_y,
double sc_x, double sc_y,
double r,
double *mu1, double *mu2)
{
   double a,b,c;
   double bb4ac;
   double dp_x,dp_y;
   *mu1 = 0;
   *mu2 = 0;

   dp_x = p2_x - p1_x;
   dp_y = p2_y - p1_y;
   a = dp_x * dp_x + dp_y * dp_y;
   b = 2 * (dp_x * (p1_x - sc_x) + dp_y * (p1_y - sc_y));
   c = sc_x * sc_x + sc_y * sc_y;
   c += p1_x * p1_x + p1_y * p1_y;
   c -= 2 * (sc_x * p1_x + sc_y * p1_y);
   c -= r * r;
   bb4ac = b * b - 4 * a * c;
   if (ODIE_Real_Is_Zero(a) || bb4ac < 0) {
      return(0);
   }

   *mu1 = (-b + sqrt(bb4ac)) / (2 * a);
   *mu2 = (-b - sqrt(bb4ac)) / (2 * a);

   return(1);
}

/*
** This file is copyright Test and Evaluation Solutions, LLC
** See license.terms for details of usage
*/


static int  odiemath_method_colinear (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  if( objc != 7 ){
      Tcl_WrongNumArgs(interp, 1, objv, "colinear x1 y1 x2 y2 x3 y3");
      return TCL_ERROR;
  }
  double x1,y1,x2,y2,x3,y3;
  if (Tcl_GetDoubleFromObj(interp,objv[1],&x1)) return TCL_ERROR;
  if (Tcl_GetDoubleFromObj(interp,objv[2],&y1)) return TCL_ERROR;
  if (Tcl_GetDoubleFromObj(interp,objv[3],&x2)) return TCL_ERROR;
  if (Tcl_GetDoubleFromObj(interp,objv[4],&y2)) return TCL_ERROR;
  if (Tcl_GetDoubleFromObj(interp,objv[5],&x3)) return TCL_ERROR;
  if (Tcl_GetDoubleFromObj(interp,objv[6],&y3)) return TCL_ERROR;
  Tcl_SetObjResult(interp, Tcl_NewBooleanObj(Odie_IsColinear(x1,y1,x2,y2,x3,y3)));
  return TCL_OK;
}

/*
** This file implements several math and drawing functions used
** to accellerate the IRM gui
*/

static int  odiemath_method_double_to_fuzzy (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  double ax;
  if(Tcl_GetDoubleFromObj(interp,objv[1],&ax)) {
    Tcl_SetObjResult(interp,objv[1]);
    return TCL_OK;
  }
  Tcl_SetObjResult(interp,ODIE_NewFuzzyObj(ax));
  return TCL_OK;
}

static int  odiemath_method_fuzzy_compare (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  int result;
  if(objc!=3) {
    Tcl_WrongNumArgs(interp, 1, objv, "avalue bvalue");
  }
  result=ODIE_Fuzzy_Compare_TclObj(objv[1],objv[2]);
  Tcl_SetObjResult(interp,Tcl_NewIntObj(result));
  return TCL_OK;
}

static int  odiemath_method_fuzzy_is_zero (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  int result;
  if(objc!=2) {
    Tcl_WrongNumArgs(interp, 1, objv, "avalue");
  }
  double value;
  if(Tcl_GetDoubleFromObj(NULL,objv[1],&value)) {
    result=0;
  } else {
    result=ODIE_Real_Is_Zero(value);
  }
  Tcl_SetObjResult(interp,Tcl_NewBooleanObj(result));
  return TCL_OK;
}

static int  odiemath_method_grid_hex (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  double grid, x, y;
  int gx,gy;
  Tcl_Obj *pResult;
 
  if( objc != 4 ){
    Tcl_WrongNumArgs(interp, 1, objv, "gridsize x y");
    return TCL_ERROR;
  }
  if(Tcl_GetDoubleFromObj(interp,objv[1],&grid)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&x)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[3],&y)) return TCL_ERROR;
  pResult=Tcl_NewObj();
  gy=(int)round(y/grid);
  if(gy%2==1){
    gx=(int)round((x-grid/2)/grid);
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(grid*gx+grid/2));
  } else {
    gx=(int)round(x/grid);
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(grid*gx));
  }
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(grid*gy));
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

static int  odiemath_method_grid_square (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  double grid;
  double x;
  double y;
  Tcl_Obj *pResult;
  if( objc != 4 ){
    Tcl_WrongNumArgs(interp, 1, objv, "gridsize x y");
    return TCL_ERROR;
  }
  if(Tcl_GetDoubleFromObj(interp,objv[1],&grid)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&x)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[3],&y)) return TCL_ERROR;
  pResult=Tcl_NewObj();
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(grid*round(x/grid)));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(grid*round(y/grid)));
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

static int  odiemath_method_line_circle_intersect (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  double ax1,ax2,ay1,ay2;
  double bx1,by1,brad;
  double ix,iy;

  if(objc<8) {
    Tcl_WrongNumArgs(interp, 1, objv, "ax1 ay1 ax2 ay2 bx1 by1 brad");
    return TCL_ERROR;
  }

  if( Tcl_GetDoubleFromObj(interp, objv[1], &ax1) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[2], &ay1) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[3], &ax2) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[4], &ay2) ) return TCL_ERROR;

  if( Tcl_GetDoubleFromObj(interp, objv[5], &bx1) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[6], &by1) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[7], &brad) ) return TCL_ERROR;
  
  if(ODIE_Math_LineCircleIntersect(ax1,ay1,ax2,ay2,bx1,by1,brad,&ix,&iy)) {
    Tcl_Obj *pResult;
    pResult = Tcl_NewObj();
    Tcl_ListObjAppendElement(interp, pResult, Tcl_NewDoubleObj(ix));
    Tcl_ListObjAppendElement(interp, pResult, Tcl_NewDoubleObj(iy));
    Tcl_SetObjResult(interp, pResult);
  }
  return TCL_OK;
}

static int  odiemath_method_line_intersect (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  double ax1,ax2,ay1,ay2;
  double bx1,bx2,by1,by2;
  double ix,iy;

  if(objc<9) {
    Tcl_WrongNumArgs(interp, 1, objv, "ax1 ay1 ax2 ay2 bx1 by1 bx2 by2");
    return TCL_ERROR;
  }

  if( Tcl_GetDoubleFromObj(interp, objv[1], &ax1) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[2], &ay1) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[3], &ax2) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[4], &ay2) ) return TCL_ERROR;

  if( Tcl_GetDoubleFromObj(interp, objv[5], &bx1) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[6], &by1) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[7], &bx2) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[8], &by2) ) return TCL_ERROR;
  
  if(ODIE_Math_LineLineIntersect(ax1,ay1,ax2,ay2,bx1,by1,bx2,by2,&ix,&iy)) {
    Tcl_Obj *pResult;
    pResult = Tcl_NewObj();
    Tcl_ListObjAppendElement(interp, pResult, Tcl_NewDoubleObj(ix));
    Tcl_ListObjAppendElement(interp, pResult, Tcl_NewDoubleObj(iy));
    Tcl_SetObjResult(interp, pResult);
  }
  return TCL_OK;
}

static int  odiemath_method_line_overlap (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  Tcl_Obj *pResult;
  double ax1,ax2,ay1,ay2;
  double bx1,bx2,by1,by2;

  if(objc<9) {
    Tcl_WrongNumArgs(interp, 1, objv, "ax1 ay1 ax2 ay2 bx1 by1 bx2 by2");
    return TCL_ERROR;
  }

  if( Tcl_GetDoubleFromObj(interp, objv[1], &ax1) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[2], &ay1) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[3], &ax2) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[4], &ay2) ) return TCL_ERROR;

  if( Tcl_GetDoubleFromObj(interp, objv[5], &bx1) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[6], &by1) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[7], &bx2) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[8], &by2) ) return TCL_ERROR;
  
  /*
  ** ignore if the segments connect at endpoints
  if(ax1==bx1 && ay1==by1) return TCL_OK;
  if(ax1==bx2 && ay1==by2) return TCL_OK;
  if(ax2==bx1 && ay2==by1) return TCL_OK;
  if(ax2==bx2 && ay2==by2) return TCL_OK;
  */
    
  pResult = Tcl_NewIntObj(ODIE_Math_LineLineCoincident(ax1,ay1,ax2,ay2,bx1,by1,bx2,by2));
  Tcl_SetObjResult(interp, pResult);

  return TCL_OK;
}

static int  odiemath_method_list_round (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  int i, n;
  double factor;
  if(Tcl_GetDoubleFromObj(interp,objv[1],&factor)) return TCL_ERROR;
  if( Tcl_ListObjLength(interp, objv[2], &n) ) return TCL_ERROR;

  Tcl_Obj *pResult=Tcl_NewObj();
  for(i=0;i<n;i++) {
    double thisval;
    Tcl_Obj *pObj;
    Tcl_ListObjIndex(0, objv[2], i, &pObj);
    if(Tcl_GetDoubleFromObj(interp,pObj,&thisval)) {
      Tcl_DecrRefCount(pResult);
      return TCL_ERROR;
    }
    Tcl_ListObjAppendElement(interp, pResult, Tcl_NewDoubleObj(round(thisval/factor)*factor));
  }
  Tcl_SetObjResult(interp,pResult);
  return TCL_OK;
}

static int  odiemath_method_list_to_int (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  Tcl_Obj *pResult=Tcl_NewObj();
  int i;
  for(i=1;i<objc;i++) {
    double thisval;
    if(Tcl_GetDoubleFromObj(interp,objv[i],&thisval)) return TCL_ERROR;
    Tcl_ListObjAppendElement(interp, pResult, Tcl_NewIntObj((int)thisval));
  }
  Tcl_SetObjResult(interp,pResult);
  return TCL_OK;
}

int Odie_Mathtools_Init(Tcl_Interp *interp) {
  Tcl_Namespace *modPtr;

  modPtr=Tcl_FindNamespace(interp,"odiemath",NULL,TCL_NAMESPACE_ONLY);
  if(!modPtr) {
    modPtr = Tcl_CreateNamespace(interp, "odiemath", NULL, NULL);
  }

  Tcl_CreateObjCommand(interp,"::odiemath::colinear",(Tcl_ObjCmdProc *)odiemath_method_colinear,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::odiemath::double_to_fuzzy",(Tcl_ObjCmdProc *)odiemath_method_double_to_fuzzy,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::odiemath::fuzzy_compare",(Tcl_ObjCmdProc *)odiemath_method_fuzzy_compare,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::odiemath::fuzzy_is_zero",(Tcl_ObjCmdProc *)odiemath_method_fuzzy_is_zero,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::odiemath::grid_hex",(Tcl_ObjCmdProc *)odiemath_method_grid_hex,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::odiemath::grid_square",(Tcl_ObjCmdProc *)odiemath_method_grid_square,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::odiemath::line_circle_intersect",(Tcl_ObjCmdProc *)odiemath_method_line_circle_intersect,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::odiemath::line_intersect",(Tcl_ObjCmdProc *)odiemath_method_line_intersect,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::odiemath::line_overlap",(Tcl_ObjCmdProc *)odiemath_method_line_overlap,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::odiemath::list_round",(Tcl_ObjCmdProc *)odiemath_method_list_round,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::odiemath::list_to_int",(Tcl_ObjCmdProc *)odiemath_method_list_to_int,NULL,NULL);

  Tcl_CreateEnsemble(interp, modPtr->fullName, modPtr, TCL_ENSEMBLE_PREFIX);
  Tcl_Export(interp, modPtr, "[a-z]*", 1);
  return TCL_OK;
}





<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted cmodules/geometry/generic/plotter.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
/*
** This widget translates 3-D coordinates onto a flat canvas by splitting
** the 3-D space into layers and stacking the layers on the canvas.
**
** The layers are decks of the ship.  The highest layer (or deck) is drawn
** at the top of the page.  The next layer down is drawn below the top layer.
** and so forth down the canvas.  In other words, the 3D object is drawn
** by showing a set of 2D slices where each slice is viewed from above.
**
** The original 3D coordinates are called "actual" coordinates.  When
** translated into the 2D canvas they are called "canvas" coordinates.
**
** The actual coordinate system is right-handed.  The X axis increases to
** the right.  The Y axis increases going up.  The Z axis comes out of the
** page at the viewer.  The canvas coordinate system is left-handed.  The
** X axis increase to the right but the Y axis increases going down.
**
** A plotter is a object with methods.  The details of the available
** methods and what each does are described in comments before the
** implementation of each method.
*/
#include "odieInt.h"
#include <stdarg.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include <string.h>

/*
** A plotter is an instance of the following structure.
*/
typedef struct Plotter Plotter;
struct Plotter {
  double rZoom;      /* Multiply canvas coord by this to get actual coord */
  double rXOffset;   /* X-Shift amount */
  double rYOffset;   /* Y-Shift amount */
};

/*
** This routine is called when a plotter is deleted.  All the memory and
** other resources allocated by this plotter is recovered.
*/
static void destroyPlotter(void *pArg){
  Plotter *p = (Plotter*)pArg;
  Odie_Free((char *)p);
}

static inline double xCanvasToActual(Plotter *p,double cx){
  return (cx+p->rXOffset)*p->rZoom;
}

static inline double yCanvasToActual(Plotter *p,double cy){
  return -1.0*(cy+p->rYOffset)*p->rZoom;
}

/*
** Convert a Y coordinate from actual to canvas coordinates for a
** given deck.
*/
static inline double xActualToCanvas(Plotter *p,double ax){
  return (ax/p->rZoom)-p->rXOffset;
}

/*
** Convert a Y coordinate from actual to canvas coordinates for a
** given deck.
*/
static inline double yActualToCanvas(Plotter *p,double ay){
  return -1.0*(ay/p->rZoom)-p->rYOffset;
}

/*
** This routine runs when a method is executed against a plotter
*/
static int plotterMethodProc(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  Plotter *p = (Plotter*)pArg;

#if 0
  /* For debugging....
  ** Print each wallset command before it is executed.
  */
  { int i;
    for(i=0; i<objc; i++){
      printf("%s%c", Tcl_GetStringFromObj(objv[i], 0), i<objc-1 ? ' ' : '\n');
    }
  }
#endif

  /* The following bit of magic implements a switch() statement that
  ** invokes the correct code based on the value of objv[1].  The
  ** mktclopts.tcl script scans this source file looking for "case"
  ** statements then generates the "wallset.h" include file that contains
  ** all of the code necessary to implement the switch.
  **
  ** In this way, we can add new methods to the command simply by adding
  ** new cases within the switch.  All the related switch code is
  ** regenerated automatically.
  */
  {
#include "plotter_cases.h"
  {
  /*
  ** tclmethod:  PLOTTER actualcoords CANVAS-COORD-LIST
  ** title:   Convert from canvas to actual coordinate space
  */
  case PLOTTER_ACTUALCOORDS: {
    int i, n;
    Tcl_Obj *pResult;
    MATOBJ *M;
    if( objc!=3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "CANVAS-COORD-LIST");
      return TCL_ERROR;
    }
    if( Tcl_ListObjLength(interp, objv[2], &n) ) return TCL_ERROR;
    if( n%2!=0 ){
      Tcl_AppendResult(interp, "coordinate list must contain a multiple "
         "of 2 values", 0);
      return TCL_ERROR;
    }

    pResult = Tcl_NewObj();
    for(i=0; i<n-1; i+=2){
      double cx, cy;
      Tcl_Obj *pObj;

      Tcl_ListObjIndex(0, objv[2], i, &pObj);
      if( Tcl_GetDoubleFromObj(interp, pObj, &cx) ) break;
      Tcl_ListObjIndex(0, objv[2], i+1, &pObj);
      if( Tcl_GetDoubleFromObj(interp, pObj, &cy) ) break;
      M=Matrix_NewObj();
      Matrix_Alloc(M,MATFORM_vectorxy);

      /* Original Formula 
      ** ax = cx*p->rZoom - pS->rXShift;
      ** ay = pS->mxY + (pS->mnY-pS->mxY)*(cy-pS->top)/(pS->btm-pS->top);
      */
      *(M->matrix+X_IDX)=xCanvasToActual(p,cx);
      *(M->matrix+Y_IDX)=yCanvasToActual(p,cy);
      
      Tcl_ListObjAppendElement(interp, pResult, Matrix_To_TclObj(M));
    }
    if( i<n-1 ){
      Tcl_DecrRefCount(pResult);
      return TCL_ERROR;
    }
    Tcl_SetObjResult(interp, pResult);
    break;
  }
 
  /*
  ** tclmethod:  PLOTTER canvascoords VECTOR-LIST ?xvar? ?yvar?
  ** title:   Convert from actual (a list of vectors) to canvas coordinate space
  **
  */
  case PLOTTER_CANVASCOORDS: {
    int i,n,m;
    Tcl_Obj *pResult=NULL;
    double cx, cy, ax, ay;
    Tcl_Obj *pObj;
    int error=0;
    int singlearg=0;

    if( objc!=3 && objc!=5 ){
      Tcl_WrongNumArgs(interp, 2, objv, "VECTOR-LIST ?xvar yvar?");
      return TCL_ERROR;
    }
    
    
    if(objv[2]->typePtr==&matrix_tclobjtype) {
      singlearg=1;
    } else {
      if( Tcl_ListObjLength(interp, objv[2], &n) ) return TCL_ERROR;
      Tcl_ListObjIndex(0, objv[2], 0, &pObj);
      if( Tcl_ListObjLength(interp, pObj, &m) ) return TCL_ERROR;
      if(m==1) {
        singlearg=1;
      }
    }
    
    if(singlearg) {
      /*
      ** Accept a single vector as an argument
      ** Do this to ensure we don't interpret the
      ** value as a list
      */
      MATOBJ *M;
      M=Odie_GetMatrixFromTclObj(interp,objv[2],MATFORM_vectorxy);
      if(!M) return TCL_ERROR;
      ax=*(M->matrix+X_IDX);
      ay=*(M->matrix+Y_IDX);
      cx = xActualToCanvas(p,ax);       
      cy = yActualToCanvas(p,ay);
      if(objc==5) {
        Tcl_ObjSetVar2(interp,objv[3],NULL,Tcl_NewDoubleObj(cx),0);
        Tcl_ObjSetVar2(interp,objv[4],NULL,Tcl_NewDoubleObj(cy),0);
      }
      pResult = Tcl_NewObj();
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(cx));
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(cy));
      Tcl_SetObjResult(interp, pResult);  
      return TCL_OK;
    }

    if(objc != 5) {
      pResult = Tcl_NewObj();
    }

    for(i=0; i<n; i++){
      MATOBJ *M;
      Tcl_ListObjIndex(0, objv[2], i, &pObj);
      M=Odie_GetMatrixFromTclObj(interp,pObj,MATFORM_vectorxy);
      if(!M) return TCL_ERROR;
      ax=*(M->matrix+X_IDX);
      ay=*(M->matrix+Y_IDX);
      cx = xActualToCanvas(p,ax);       
      cy = yActualToCanvas(p,ay);
      if(objc==5) {
        Tcl_ObjSetVar2(interp,objv[3],NULL,Tcl_NewDoubleObj(cx),0);
        Tcl_ObjSetVar2(interp,objv[4],NULL,Tcl_NewDoubleObj(cy),0);
        return TCL_OK;
      } else {    
        Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(cx));
        Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(cy));
      }
    }
    if(error) {
      if(pResult) {
        Tcl_DecrRefCount(pResult);
      }
      return TCL_ERROR;
    }
    if( i<n-3 ){
        Tcl_AppendResult(interp, "Did not reach the end", 0);
      Tcl_DecrRefCount(pResult);
      return TCL_ERROR;
    }

    Tcl_SetObjResult(interp, pResult);
    break;
  }

  /*
  ** tclmethod:  PLOTTER destroy
  ** title:   Destroy this plotter
  */
  case PLOTTER_DESTROY: {
    Tcl_DeleteCommand(interp,Tcl_GetString(objv[0]));
    break;
  }

  /*
  ** tclmethod:  PLOTTER objinfo obj
  ** title:   Return TK to draw a line on a canvas
  */
  case PLOTTER_OBJINFO: {
    Tcl_Obj *tmp;
    tmp = objv[2];
    printf("INFO: %s Ref: %d Type: %p \n", Tcl_GetStringFromObj(tmp, NULL),
        tmp->refCount, tmp->typePtr);
    fflush (stdout);
    break;
  }
  
  /*
  ** tclmethod:  PLOTTER centerset zoom width height
  ** title:   Set all settings for plotter in one go
  ** description: Sets the center of the screen based on the width
  ** and height (0,0 = width/2 height/2)
  */
  case PLOTTER_CENTERSET: {
    double rZoom,rXOffset,rYOffset;

    if(objc!=5) {
      printf("%d\n",objc);
      Tcl_WrongNumArgs(interp, 2, objv, "ZOOM XOFFSET YOFFSET");
      return TCL_ERROR;
    }
    if( Tcl_GetDoubleFromObj(interp, objv[2], &rZoom) ) return TCL_ERROR;
    p->rZoom = rZoom;
    if( Tcl_GetDoubleFromObj(interp, objv[3], &rXOffset) ) return TCL_ERROR;
    p->rXOffset = -rXOffset/2.0;
    if( Tcl_GetDoubleFromObj(interp, objv[4], &rYOffset) ) return TCL_ERROR;
    p->rYOffset = -rYOffset/2.0;
    return TCL_OK;
  }
  
  /*
  ** tclmethod:  PLOTTER xoffset ?AMT?
  ** title:   Change the X-Offset
  */
  case PLOTTER_XOFFSET: {
    double rXOffset;
    if( objc!=2 && objc!=3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "?ZOOM?");
      return TCL_ERROR;
    }
    if( objc==2 ){
      Tcl_SetObjResult(interp, Tcl_NewDoubleObj(p->rXOffset));
      return TCL_OK;
    }
    if( Tcl_GetDoubleFromObj(interp, objv[2], &rXOffset) ) return TCL_ERROR;
    p->rXOffset = rXOffset;
    break;
  }

  /*
  ** tclmethod:  PLOTTER yoffset ?AMT?
  ** title:   Change the Y-Offset
  */
  case PLOTTER_YOFFSET: {
    double rYOffset;
    if( objc!=2 && objc!=3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "?ZOOM?");
      return TCL_ERROR;
    }
    if( objc==2 ){
      Tcl_SetObjResult(interp, Tcl_NewDoubleObj(p->rYOffset));
      return TCL_OK;
    }
    if( Tcl_GetDoubleFromObj(interp, objv[2], &rYOffset) ) return TCL_ERROR;
    p->rYOffset = rYOffset;
    break;
  }

  /*
  ** tclmethod:  PLOTTER zoom ?ZOOM?
  ** title:   Query or change the zoom factor.
  */
  case PLOTTER_ZOOM: {
    Tcl_Obj *pResult;
    if( objc!=2 && objc!=3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "?ZOOM?");
      return TCL_ERROR;
    }
    if( objc==3 ){
      double r;
      if( Tcl_GetDoubleFromObj(interp, objv[2], &r) ) return TCL_ERROR;
      p->rZoom = r;
    }
    pResult = Tcl_NewDoubleObj(p->rZoom);
    Tcl_SetObjResult(interp, pResult);
    break;
  }

  /* End of the command methods.  The brackets that follow terminate the
  ** automatically generated switch.
  ****************************************************************************/
  }
  }
  return TCL_OK;
}

/*
** tclcmd: plotter PLOTTER
** title: creates a plotter object
** This routine runs when the "plotter" command is invoked to create a
** new plotter.
*/
int Odie_PlotterCreateProc(
  void *NotUsed,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  char *zCmd;
  Plotter *p;
  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "PLOTTER");
    return TCL_ERROR;
  }
  zCmd = Tcl_GetStringFromObj(objv[1], 0);
  p = (Plotter *)Odie_Alloc( sizeof(*p) );
  p->rZoom = 1.0;
  p->rXOffset = 0.0;
  p->rYOffset = 0.0;
  Tcl_CreateObjCommand(interp, zCmd, plotterMethodProc, p, destroyPlotter);
  return TCL_OK;
}

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
































































































































































































































































































































































































































































































































































































































































































































































































Deleted cmodules/geometry/generic/plotter_cases.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*** Automatically Generated Header File - Do Not Edit ***/
  const static char *PLOTTER_strs[] = {
    "actualcoords",       "canvascoords",      "centerset",
    "destroy",            "objinfo",           "xoffset",
    "yoffset",            "zoom",              0
  };
  enum PLOTTER_enum {
    PLOTTER_ACTUALCOORDS, PLOTTER_CANVASCOORDS,PLOTTER_CENTERSET,
    PLOTTER_DESTROY,      PLOTTER_OBJINFO,     PLOTTER_XOFFSET,
    PLOTTER_YOFFSET,      PLOTTER_ZOOM,        
  };
 int index;
  if( objc<2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "METHOD ?ARG ...?");
    return TCL_ERROR;
  }
  if( Tcl_GetIndexFromObj(interp, objv[1], PLOTTER_strs, "option", 0, &index)){
    return TCL_ERROR;
  }
  switch( (enum PLOTTER_enum)index )
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








































Deleted cmodules/geometry/generic/polygon.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971

/*
** This file is machine generated. Changes will
** be overwritten on the next run of cstruct.tcl
*/
#include "odieInt.h"

/*
** This file implements a TCL object used for tracking polygons.  A
** single new TCL command named "poly" is defined.  This command
** has methods for creating, deleting, and taking the intersection
** of 2-D polygons.  There are comments on the implementation of each
** method to describe what the method does.
**
** This module was originally developed to aid in computing the
** shared surface area between two compartments on separate decks.
** The shared surface area is needed in initializing the fire model
** since heat conduction between the two compartments is proportional
** to their shared area.
*/
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <math.h>

/*
** Return the distance between two points
*/
static inline double dist(double x0, double y0, double x1, double y1){
  double dx = x1 - x0;
  double dy = y1 - y0;
  return sqrt(dx*dx + dy*dy);
}

/*
** Return -1, 0, or 1 if the point x,y is outside, on, or within
** the polygon p.
*/
static inline int within(Poly *p, double x, double y){
  int res, i;
  res = -1;
  for(i=0; i<p->nVertex-1; i++){
    double x0, y0, x1, y1, yP;
    x0 = p->v[i][X_IDX];
    y0 = p->v[i][Y_IDX];
    x1 = p->v[i+1][X_IDX];
    y1 = p->v[i+1][Y_IDX];
    if( x0==x1 ){
      if( x0==x && ((y0<=y && y1>=y) || (y1<=y && y0>=y)) ){
        res = 0;
        break;
      }
      continue;
    }
    if( x0>x1 ){
      int t = x0;
      x0 = x1;
      x1 = t;
      t = y0;
      y0 = y1;
      y1 = t;
    }
    if( x>=x1 || x<x0 ) continue;
    yP = y1 - (x1-x)*(y1-y0)/(x1-x0);
    if( yP == y ){ res = 0; break; }
    if( yP > y ){ res = -res; }
  }
  return res;
}

int Odie_PolygonComputeArea(Tcl_Interp *interp,Poly *p) {
  double area=0.0;
  double areacomp=0.0;
  int i;

  if( p->v[p->nVertex-1][X_IDX]!=p->v[0][X_IDX] || p->v[p->nVertex-1][Y_IDX]!=p->v[0][Y_IDX] ){
    p->v[p->nVertex][X_IDX] = p->v[0][X_IDX];
    p->v[p->nVertex][Y_IDX] = p->v[0][Y_IDX];
    p->nVertex++;
  }

  for(i=0; i<p->nVertex-1; i++){
    area += 0.5*(p->v[i][Y_IDX] + p->v[i+1][Y_IDX])*(p->v[i+1][X_IDX] - p->v[i][X_IDX]);
  }
  if( area<0.0 ){
    int b, e;
    for(b=0, e=p->nVertex-1; b<e; b++, e--){
      double t;
      t = p->v[b][X_IDX];
      p->v[b][X_IDX] = p->v[e][X_IDX];
      p->v[e][X_IDX] = t;
      t = p->v[b][Y_IDX];
      p->v[b][Y_IDX] = p->v[e][Y_IDX];
      p->v[e][Y_IDX] = t;
    }
    area = -area;
  }
  p->area = area;
  p->bbox.l = p->bbox.r = p->v[0][X_IDX];
  p->bbox.t = p->bbox.b = p->v[0][Y_IDX];
  for(i=1; i<p->nVertex-1; i++){
    double x, y;
    x = p->v[i][X_IDX];
    if( x<p->bbox.l ) p->bbox.l = x;
    if( x>p->bbox.r ) p->bbox.r = x;
    y = p->v[i][Y_IDX];
    if( y>p->bbox.t ) p->bbox.t = y;
    if( y<p->bbox.b ) p->bbox.b = y;
  }
  areacomp=(p->bbox.r - p->bbox.l)*(p->bbox.t-p->bbox.b)*1.00001;
  
  if(area<=areacomp) {
    return TCL_OK;
  } else {
    char errstr[256];
    sprintf(errstr,"Area: %g Calculated: %g\n",area,areacomp);
    Tcl_AppendResult(interp, "Area of polygon wonky ", errstr, 0);
    return TCL_ERROR;
  }
}

static int  polygon_method_create (
  ClientData *dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "N");
    return TCL_ERROR;
  }
  int isnew;
  Poly *p;
  if( Odie_GetPolygonFromObj(interp, objv[1], &p, &isnew) ) return TCL_ERROR;
  if(isnew) {
    Tcl_SetObjResult(interp, Odie_NewPolygonObj(p));
  } else {
    Tcl_SetObjResult(interp, objv[1]);
  }
  return TCL_OK;
}

static int  polygon_method_simplify (
  ClientData *dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "N");
    return TCL_ERROR;
  }
  Poly *pPoly,*pNewPoly;
  int i,isnew;
  int colinear;
  double ax,ay,bx,by,cx,cy;
  if( Odie_GetPolygonFromObj(interp, objv[1], &pPoly, &isnew) ) return TCL_ERROR;

  pNewPoly=(Poly *)Odie_Alloc(sizeof(*pNewPoly)+(pPoly->nVertex+2)*sizeof(pNewPoly->v[0]));
  pNewPoly->nVertex=0;
  
  ax=pPoly->v[pPoly->nVertex-1][X_IDX];
  ay=pPoly->v[pPoly->nVertex-1][Y_IDX];
  bx=pPoly->v[0][X_IDX];
  by=pPoly->v[0][Y_IDX];
  if(ax==bx && ay==by) {
    ax=pPoly->v[pPoly->nVertex-2][X_IDX];
    ay=pPoly->v[pPoly->nVertex-2][Y_IDX];
  }
  for(i=1;i<pPoly->nVertex;i++) {
    cx=pPoly->v[i][X_IDX];
    cy=pPoly->v[i][Y_IDX];
    colinear=Odie_IsColinear(ax,ay,bx,by,cx,cy);
    if(!colinear) {
      pNewPoly->v[pNewPoly->nVertex][X_IDX]=bx;
      pNewPoly->v[pNewPoly->nVertex][Y_IDX]=by;
      pNewPoly->nVertex++;
    }
    ax=bx;
    ay=by;
    bx=cx;
    by=cy;
  }
  cx=pPoly->v[0][X_IDX];
  cy=pPoly->v[0][Y_IDX];
  colinear=Odie_IsColinear(ax,ay,bx,by,cx,cy);
  if(!Odie_IsColinear(ax,ay,bx,by,cx,cy)) {
    pNewPoly->v[pNewPoly->nVertex][X_IDX]=bx;
    pNewPoly->v[pNewPoly->nVertex][Y_IDX]=by;
    pNewPoly->nVertex++;
  }
  if( pNewPoly->v[pNewPoly->nVertex-1][X_IDX]!=pNewPoly->v[0][X_IDX] || pNewPoly->v[pNewPoly->nVertex-1][Y_IDX]!=pNewPoly->v[0][Y_IDX] ){
    pNewPoly->v[pNewPoly->nVertex][X_IDX] = pNewPoly->v[0][X_IDX];
    pNewPoly->v[pNewPoly->nVertex][Y_IDX] = pNewPoly->v[0][Y_IDX];
    pNewPoly->nVertex++;
  }
  Odie_PolygonComputeArea(interp,pNewPoly);
  Tcl_SetObjResult(interp, Odie_NewPolygonObj(pNewPoly));
  if(isnew) Odie_Free((char *)pPoly);
  return TCL_OK;
}

static int  polygon_method_area (
  ClientData *dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  Poly *p;
  int isnew;
  if( objc<2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "N ?N...?");
    return TCL_ERROR;
  }
  if( Odie_GetPolygonFromObj(interp, objv[1], &p, &isnew) ) return TCL_ERROR;
  double area=p->area;
  if(isnew) Odie_Free((char *)p);
  int i;
  for(i=2;i<objc;i++) {
    if( Odie_GetPolygonFromObj(interp, objv[i], &p, &isnew) ) return TCL_ERROR;
    area+=p->area;
    if(isnew) Odie_Free((char *)p);
  }
  Tcl_SetObjResult(interp, Tcl_NewDoubleObj(area));
  return TCL_OK;
}

static int  polygon_method_bbox (
  ClientData *dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  /*    poly bbox N
  **
  ** Return the bounding box for a polygon
  */
  Poly *p;
  int isnew;
  Tcl_Obj *pResult;
  if( objc<2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "N ?N...?");
    return TCL_ERROR;
  }
  if( Odie_GetPolygonFromObj(interp, objv[1], &p, &isnew) ) return TCL_ERROR;
  double left=p->bbox.l;
  double top=p->bbox.t;
  double right=p->bbox.r;
  double bottom=p->bbox.b;
  int i;
  if(isnew) Odie_Free((char *)p);
  for(i=2;i<objc;i++) {
    if( Odie_GetPolygonFromObj(interp, objv[i], &p, &isnew) ) return TCL_ERROR;
    if(p->bbox.l < left) left=p->bbox.l;
    if(p->bbox.t > top)  top=p->bbox.t;
    if(p->bbox.r > right) right=p->bbox.r;
    if(p->bbox.b < bottom)  bottom=p->bbox.b;
    if(isnew) Odie_Free((char *)p);
  }
  pResult = Tcl_NewObj();
  Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(left));
  Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(top));
  Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(right));
  Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(bottom));
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

static int  polygon_method_info (
  ClientData *dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  /*    poly info N
  **
  ** Return the coordinates for a polygon
  */
  Tcl_Obj *pResult;
  Poly *p;
  int i,isnew;
  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "N");
    return TCL_ERROR;
  }
  if( Odie_GetPolygonFromObj(interp, objv[1], &p, &isnew) ) return TCL_ERROR;
  pResult = Tcl_NewObj();
  for(i=0; i<p->nVertex-1; i++){
    Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(p->v[i][X_IDX]));
    Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(p->v[i][Y_IDX]));
  }
  if(isnew) Odie_Free((char *)p);
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

static int  polygon_method_intersect (
  ClientData *dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  /*    poly intersect N1 N2
  **
  ** Return a list of 3 elements where the first element is the
  ** area of intersection between polygons N1 and N2 and the remaining
  ** 3 elements are the X and Y coordinates of a point within both
  ** polygons. 
  **
  ** The current implementation returns an approximation.  We might
  ** change it to compute the exact intersection later.
  */    
  Poly *p1, *p2;
  int isnew1,isnew2;
  double area;
  double xInside = 0.0, yInside = 0.0;
  Tcl_Obj *pResult;
  if( objc!=3 ){
    Tcl_WrongNumArgs(interp, 1, objv, "N1 N2");
    return TCL_ERROR;
  }
  if( Odie_GetPolygonFromObj(interp, objv[1], &p1, &isnew1) ) return TCL_ERROR;
  if( Odie_GetPolygonFromObj(interp, objv[2], &p2, &isnew2) ) {
    if(isnew1) Odie_Free((char *)p1);
    return TCL_ERROR;
  }
  if( p1->bbox.r<=p2->bbox.l || p1->bbox.l>=p2->bbox.r
                || p1->bbox.t<=p2->bbox.b  || p1->bbox.b>=p2->bbox.t ){
    area = 0.0;
  }else if( p1->area==0.0 || p2->area==0.0 ){
    area = 0.0;
  }else{
    double x0, y0, x1, y1, dx, dy, xP, yP, xC, yC;
    int i, j, cnt;
    int score, bestScore;
    static const int n = 50;
    char hit[50][50];

    /* Compute the overlap of the bounding boxes of the two polygons. */
    x0 = p1->bbox.l < p2->bbox.l ? p2->bbox.l : p1->bbox.l;
    y0 = p1->bbox.t > p2->bbox.t ? p2->bbox.t : p1->bbox.t;
    x1 = p1->bbox.r > p2->bbox.r ? p2->bbox.r : p1->bbox.r;
    y1 = p1->bbox.b < p2->bbox.b ? p2->bbox.b : p1->bbox.b;

    /* Divide the intersection of the bounding boxes into a n-by-n grid
    ** and count the number of elements in this grid whose centers fall
    ** within both polygons.  This will be our approximation for the
    ** intersection of the polygons themselves.
    */
    dx = (x1-x0)/n;
    dy = (y1-y0)/n;
    cnt = 0;
    xC = yC = 0.0;
    for(i=0; i<n; i++){
      xP = x0 + dx*(i+0.5);
      for(j=0; j<n; j++){
        yP = y0 + dy*(j+0.5);
        if( within(p1, xP, yP)>0 && within(p2, xP, yP)>0 ){
          cnt++;
          hit[i][j] = 1;
          xC += xP;
          yC += yP;
        }else{
          hit[i][j] = 0;
        }
      }
    }

    /* We need to find a good approximation for the center of the
    ** overlap.  Begin by computing the center of mass for the
    ** overlapping region.  Then find the point inside the intersection
    ** that is nearest the center of mass.
    */
    if( cnt>0 ){
      area = cnt*(x1-x0)*(y0-y1)/(n*n);
      xC /= cnt;
      yC /= cnt;
      bestScore = -1.0;
      for(i=0; i<n; i++){
        xP = x0 + dx*(i+0.5);
        for(j=0; j<n; j++){
          if( !hit[i][j] ) continue;
          yP = y0 + dy*(j+0.5);
          score = dist(xP,yP,xC,yC);
          if( score<bestScore || bestScore<0.0 ){
            xInside = xP;
            yInside = yP;
            bestScore = score;
          }
        }
      }
    } else {
      area=0.0;
    }
  }
  if(isnew1) Odie_Free((char *)p1);
  if(isnew2) Odie_Free((char *)p2);

  pResult = Tcl_NewObj();
  Tcl_ListObjAppendElement(interp, pResult, Tcl_NewDoubleObj(area));
  Tcl_ListObjAppendElement(interp, pResult, Tcl_NewDoubleObj(xInside));
  Tcl_ListObjAppendElement(interp, pResult, Tcl_NewDoubleObj(yInside));
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

static int  polygon_method_within (
  ClientData *dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  /*    poly within X Y N ?N...?
  **
  ** Return -1, 0, or +1 if the point X,Y is outside, on, or inside
  ** polygon N.
  */
  Poly *p;
  int res,isnew;
  double x, y;
  if( objc<4 ){
    Tcl_WrongNumArgs(interp, 1, objv, "X Y ?N...?");
    return TCL_ERROR;
  }
  if( Tcl_GetDoubleFromObj(interp, objv[1], &x) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[2], &y) ) return TCL_ERROR;
  if(objc==4) {
    if( Odie_GetPolygonFromObj(interp, objv[3], &p, &isnew) ) return TCL_ERROR;
    res = within(p, x, y);
    if(isnew) Odie_Free((char *)p);
    Tcl_SetObjResult(interp, Tcl_NewIntObj(res));
    return TCL_OK;      
  } else {
    int i;
    for(i=3;i<objc;i++) {
      if( Odie_GetPolygonFromObj(interp, objv[i], &p, &isnew) ) return TCL_ERROR;
      res = within(p, x, y);
      if(isnew) Odie_Free((char *)p);
      if(res>=0) {
        Tcl_SetObjResult(interp, Tcl_NewBooleanObj(1));
        return TCL_OK;      
      }
    }
    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0));
    return TCL_OK;
  }
}

static int  polygon_method_segments (
  ClientData *dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  Tcl_Obj *pResult;
  int isnew;
  Poly *p;
  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "N");
    return TCL_ERROR;
  }
  if( Odie_GetPolygonFromObj(interp, objv[1], &p, &isnew) ) return TCL_ERROR;
  int i;
  double px,py;
  pResult=Tcl_NewObj();
  px=p->v[0][X_IDX];
  py=p->v[0][Y_IDX];
  for(i=1; i<p->nVertex-1; i++){
    Tcl_Obj *segment=Tcl_NewObj();
    Tcl_ListObjAppendElement(interp,segment, Tcl_NewDoubleObj(px));
    Tcl_ListObjAppendElement(interp,segment, Tcl_NewDoubleObj(py));
    Tcl_ListObjAppendElement(interp,segment, Tcl_NewDoubleObj(p->v[i][X_IDX]));
    Tcl_ListObjAppendElement(interp,segment, Tcl_NewDoubleObj(p->v[i][Y_IDX]));
    Tcl_ListObjAppendElement(interp,pResult, segment);
    px=p->v[i][X_IDX];
    py=p->v[i][Y_IDX];
  }
  Tcl_Obj *segment=Tcl_NewObj();
  Tcl_ListObjAppendElement(interp,segment, Tcl_NewDoubleObj(px));
  Tcl_ListObjAppendElement(interp,segment, Tcl_NewDoubleObj(py));
  Tcl_ListObjAppendElement(interp,segment, Tcl_NewDoubleObj(p->v[0][X_IDX]));
  Tcl_ListObjAppendElement(interp,segment, Tcl_NewDoubleObj(p->v[0][Y_IDX]));
  Tcl_ListObjAppendElement(interp,pResult, segment);
  Tcl_SetObjResult(interp, pResult);
  if(isnew) Odie_Free((char *)p);
  return TCL_OK;
}

static int  polygon_method_rectangle (
  ClientData *dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  double cx, cy, radx,rady;
  Tcl_Obj *pResult=Tcl_NewObj();

  if( objc != 5 ){
    Tcl_WrongNumArgs(interp, 1, objv, "cx cy dimx dimy");
    return TCL_ERROR;
  }
  
  if(Tcl_GetDoubleFromObj(interp,objv[1],&cx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&cy)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[3],&radx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[4],&rady)) return TCL_ERROR;
  radx=radx/2.0;
  rady=rady/2.0;
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx-radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy-rady));

  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx+radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy-rady));
  
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx+radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy+rady));

  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx-radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy+rady));

  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}


static int  polygon_method_vector_place (
  ClientData *dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  /*
  ** Apply Matrices
  */
  Tcl_Obj *pResult=Tcl_NewObj();
  int i;
  double zoom;
  double matA[6] = {1.0,0.0,0.0,1.0,0.0,0.0};
  double centerx,centery,normalx,normaly,angle;

  if( objc < 8 ){
      Tcl_WrongNumArgs(interp, 1, objv, "zoom centerx centery normalx normaly x1 y1 ?x2 y2?...");
      return TCL_ERROR;
  }   
  if(Tcl_GetDoubleFromObj(interp,objv[1],&zoom)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&centerx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[3],&centery)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[4],&normalx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[5],&normaly)) return TCL_ERROR;

  angle=atan2(normaly,normalx);
  matA[0]=cos(angle);
  matA[1]=sin(angle);
  matA[2]=-sin(angle);
  matA[3]=cos(angle);
  matA[4]=0.0;
  matA[5]=0.0;

  
  for(i=6;i<objc;i+=2) {
      double x,y,sx,sy,newx,newy;
      if(Tcl_GetDoubleFromObj(interp,objv[i],&x)) return TCL_ERROR;
      if(Tcl_GetDoubleFromObj(interp,objv[i+1],&y)) return TCL_ERROR;
      
      sx=(x/zoom);
      sy=(y/zoom);
      newx=matA[0]*sx+matA[1]*sy+matA[4]+centerx;
      newy=matA[2]*sx+matA[3]*sy+matA[5]+centery;

      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(newx));
      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(newy));
  }
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}


static int  polygon_method_hexagon (
  ClientData *dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  int i,flip=0;
  double cx, cy, radx,rady;

  Tcl_Obj *pResult=Tcl_NewObj();
  double coords[7][2]= {
    {1.00, 0.00} , {0.50, M_SQRT3_2} , 
    {-0.50, M_SQRT3_2} , {-1.00, -0.00} , 
    {-0.50, -M_SQRT3_2} , {0.50, -M_SQRT3_2},
    {1.00, 0.00} 
  };
  if( objc != 5 && objc != 6){
    Tcl_WrongNumArgs(interp, 1, objv, "cx cy dimx dimy ?flip?");
    return TCL_ERROR;
  }
  
  if(Tcl_GetDoubleFromObj(interp,objv[1],&cx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&cy)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[3],&radx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[4],&rady)) return TCL_ERROR;
  if(objc==6) {
    if(Tcl_GetBooleanFromObj(interp,objv[5],&flip)) return TCL_ERROR;
  }
  radx=radx/2.0;
  rady=rady/2.0;

  for(i=0;i<6;i++) {
    if(flip) {
      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx+radx*coords[i][1]));
      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy+rady*coords[i][0]));
      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx+radx*coords[i+1][1]));
      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy+rady*coords[i+1][0]));
    } else {
      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx+radx*coords[i][0]));
      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy+rady*coords[i][1]));
      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx+radx*coords[i+1][0]));
      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy+rady*coords[i+1][1]));
    }
  }

  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

static int  polygon_method_poly_place (
  ClientData *dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  /*
  ** Apply Matrices
  */
  Tcl_Obj *pResult=Tcl_NewObj();
  int i;
  double zoom;
  double matA[6] = {1.0,0.0,0.0,1.0,0.0,0.0};
  double centerx,centery,normalx,normaly,angle;

  if( objc < 8 ){
      Tcl_WrongNumArgs(interp, 1, objv, "zoom centerx centery normalx normaly x1 y1 ?x2 y2?...");
      return TCL_ERROR;
  }   
  if(Tcl_GetDoubleFromObj(interp,objv[1],&zoom)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&centerx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[3],&centery)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[4],&normalx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[5],&normaly)) return TCL_ERROR;

  angle=atan2(normaly,normalx);
  matA[0]=cos(angle);
  matA[1]=sin(angle);
  matA[2]=-sin(angle);
  matA[3]=cos(angle);
  matA[4]=0.0;
  matA[5]=0.0;
  double startx,starty,prevx,prevy;

  i=6;
  {
    double x,y,sx,sy,newx,newy;
    if(Tcl_GetDoubleFromObj(interp,objv[i],&x)) return TCL_ERROR;
    if(Tcl_GetDoubleFromObj(interp,objv[i+1],&y)) return TCL_ERROR;
    
    sx=(x/zoom);
    sy=(y/zoom);
    newx=matA[0]*sx+matA[1]*sy+matA[4]+centerx;
    newy=matA[2]*sx+matA[3]*sy+matA[5]+centery;

    startx=newx;
    starty=newy;
    prevx=newx;
    prevy=newy;
  }
  
  for(i=8;i<objc;i+=2) {
    double x,y,sx,sy,newx,newy;
    if(Tcl_GetDoubleFromObj(interp,objv[i],&x)) return TCL_ERROR;
    if(Tcl_GetDoubleFromObj(interp,objv[i+1],&y)) return TCL_ERROR;
    
    sx=(x/zoom);
    sy=(y/zoom);
    newx=matA[0]*sx+matA[1]*sy+matA[4]+centerx;
    newy=matA[2]*sx+matA[3]*sy+matA[5]+centery;

    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(prevx));
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(prevy));  
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(newx));
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(newy));       

    prevx=newx;
    prevy=newy;
  }
  if(startx != prevx && starty!= prevy) {
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(prevx));
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(prevy));  
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(startx));
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(starty));  
  }
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

static int  polygon_method_drawobj_orientation (
  ClientData *dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  Tcl_Obj *temp;
  int len;
  double nx=100,ny=0;
  if( objc !=4 ){
    Tcl_WrongNumArgs(interp, 1, objv, "orientation nxvar nyvar");
    return TCL_ERROR;
  }
  
  if(Tcl_ListObjLength(interp,objv[1],&len)) return TCL_ERROR;
  if(len>0) {
    if(Tcl_ListObjIndex(interp, objv[1], 0, &temp)) return TCL_ERROR;
    if(Tcl_GetDoubleFromObj(interp,temp,&nx)) return TCL_ERROR;
  }
  if(len>1) {
    if(Tcl_ListObjIndex(interp, objv[1], 1, &temp)) return TCL_ERROR;
    if(Tcl_GetDoubleFromObj(interp,temp,&ny)) return TCL_ERROR;
  }
  Tcl_ObjSetVar2(interp,objv[2],NULL,Tcl_NewDoubleObj(nx),0);
  Tcl_ObjSetVar2(interp,objv[3],NULL,Tcl_NewDoubleObj(ny),0);
  return TCL_OK;
}

static int  polygon_method_corners (
  ClientData *dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  double cx, cy, radx,rady;
  
  if( objc != 5 && objc != 9 ){
    Tcl_WrongNumArgs(interp, 1, objv, "cx cy dimx dimy ?x0var y0var x1var y1var?");
    return TCL_ERROR;
  }
  
  if(Tcl_GetDoubleFromObj(interp,objv[1],&cx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&cy)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[3],&radx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[4],&rady)) return TCL_ERROR;
  if (objc == 5) {
    Tcl_Obj *pResult=Tcl_NewObj();
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx+radx));
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy-rady));
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx-radx));
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy+rady));
    Tcl_SetObjResult(interp, pResult);
    return TCL_OK;
  }
  /*
    Replaces
    set x0 [expr {$cx-$d}]
    set y0 [expr {$cy-$d}]
    set x1 [expr {$cx+$d}]
    set y1 [expr {$cy+$d}]
  */
  
  Tcl_ObjSetVar2(interp,objv[5],NULL,Tcl_NewDoubleObj(cx+radx),0);
  Tcl_ObjSetVar2(interp,objv[6],NULL,Tcl_NewDoubleObj(cy-rady),0);
  Tcl_ObjSetVar2(interp,objv[7],NULL,Tcl_NewDoubleObj(cx-radx),0);
  Tcl_ObjSetVar2(interp,objv[8],NULL,Tcl_NewDoubleObj(cy+rady),0);
  
  return TCL_OK; 
}



static int  polygon_method_hexgrid_create (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {

  /*    poly hexgrid
  **
  ** Reduce the polygons to a series
  ** of grid coordinates
  */
  Tcl_Obj *pResult;
  Poly *p;
  int isnew;
  pResult = Tcl_NewObj();
  double gridsize=500.0;
  double x,y;

  if(objc < 3) {
    Tcl_WrongNumArgs(interp, 1, objv, "GRIDSIZE ?POLY ...?");
    return TCL_ERROR;
  }
  if( Tcl_GetDoubleFromObj(interp, objv[1], &gridsize) ) return TCL_ERROR;
  
  if( Odie_GetPolygonFromObj(interp, objv[2], &p, &isnew) ) return TCL_ERROR;
  double left=p->bbox.l;
  double top=p->bbox.t;
  double right=p->bbox.r;
  double bottom=p->bbox.b;
  int i;
  if(isnew) Odie_Free((char *)p);
  for(i=3;i<objc;i++) {
    if( Odie_GetPolygonFromObj(interp, objv[i], &p, &isnew) ) return TCL_ERROR;
    if(p->bbox.l < left) left=p->bbox.l;
    if(p->bbox.t > top)  top=p->bbox.t;
    if(p->bbox.r > right) right=p->bbox.r;
    if(p->bbox.b < bottom)  bottom=p->bbox.b;
    if(isnew) Odie_Free((char *)p);
  }
  
  pResult = Tcl_NewObj();
  left-=gridsize;
  top+=gridsize;
  right+=gridsize;
  bottom-=gridsize;
  int row=0;
  for(y=bottom;y<=top;y+=gridsize) {
    double lstartx=left;
    double gy=floor(y/gridsize)*gridsize;
    row++;
    if(row%2==1) {
      lstartx-=gridsize/2;
    }
    for(x=lstartx;x<=right;x+=gridsize) {
      double gx=floor(x/gridsize)*gridsize;
      int found=0;
      for(i=2;i<objc;i++) {
        int isnew,isWithin;
        Odie_GetPolygonFromObj(interp, objv[i], &p, &isnew);
        isWithin=within(p,gx,gy);
        if(isnew) Odie_Free((char *)p);
        if(isWithin>0) {
          found=1;
          break;
        }
      }
      if(found) {
        Tcl_Obj *coord=Tcl_NewObj();
        Tcl_ListObjAppendElement(interp, coord, Tcl_NewDoubleObj(gx));
        Tcl_ListObjAppendElement(interp, coord, Tcl_NewDoubleObj(gy));
        Tcl_ListObjAppendElement(interp, pResult, coord);
      }
    }
  }
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}


static int  polygon_method_squaregrid_create (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {

  /*    poly hexgrid
  **
  ** Reduce the polygons to a series
  ** of grid coordinates
  */
  Tcl_Obj *pResult;
  Poly *p;
  int isnew;
  pResult = Tcl_NewObj();
  double gridsize=500.0;
  double x,y;
  if(objc < 3) {
    Tcl_WrongNumArgs(interp, 1, objv, "GRIDSIZE ?POLY ...?");
    return TCL_ERROR;
  }
  if( Tcl_GetDoubleFromObj(interp, objv[1], &gridsize) ) return TCL_ERROR;
  
  if( Odie_GetPolygonFromObj(interp, objv[2], &p, &isnew) ) return TCL_ERROR;
  double left=p->bbox.l;
  double top=p->bbox.t;
  double right=p->bbox.r;
  double bottom=p->bbox.b;
  if(isnew) Odie_Free((char *)p);

  int i;
  for(i=3;i<objc;i++) {
    if( Odie_GetPolygonFromObj(interp, objv[i], &p, &isnew) ) return TCL_ERROR;
    if(p->bbox.l < left) left=p->bbox.l;
    if(p->bbox.t > top)  top=p->bbox.t;
    if(p->bbox.r > right) right=p->bbox.r;
    if(p->bbox.b < bottom)  bottom=p->bbox.b;
    if(isnew) Odie_Free((char *)p);
  }
  
  pResult = Tcl_NewObj();
  left-=gridsize;
  top+=gridsize;
  right+=gridsize;
  bottom-=gridsize;

  for(y=bottom;y<=top;y+=gridsize) {
    double gy=floor(y/gridsize)*gridsize;
    for(x=left;x<=right;x+=gridsize) {
      double gx=floor(x/gridsize)*gridsize;
      int found=0;
      for(i=2;i<objc;i++) {
        int isnew,isWithin;
        Odie_GetPolygonFromObj(interp, objv[i], &p, &isnew);
        isWithin=within(p,gx,gy);
        if(isnew) Odie_Free((char *)p);
        if(isWithin>0) {
          found=1;
          break;
        }
      }
      if(found) {
        Tcl_Obj *coord=Tcl_NewObj();
        Tcl_ListObjAppendElement(interp, coord, Tcl_NewDoubleObj(gx));
        Tcl_ListObjAppendElement(interp, coord, Tcl_NewDoubleObj(gy));
        Tcl_ListObjAppendElement(interp, pResult, coord);
      }
    }
  }
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

static int  polygon_method_grid_nearest (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  return TCL_OK;
}

int Odie_Polygon_Init(Tcl_Interp *interp) {
  Tcl_Namespace *modPtr;
  
  modPtr=Tcl_FindNamespace(interp,"polygon",NULL,TCL_NAMESPACE_ONLY);
  if(!modPtr) {
    modPtr = Tcl_CreateNamespace(interp, "polygon", NULL, NULL);
  }
  
  Tcl_CreateObjCommand(interp,"::polygon::area",(Tcl_ObjCmdProc *)polygon_method_area,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::polygon::bbox",(Tcl_ObjCmdProc *)polygon_method_bbox,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::polygon::corners",(Tcl_ObjCmdProc *)polygon_method_corners,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::polygon::create",(Tcl_ObjCmdProc *)polygon_method_create,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::polygon::drawobj_orientation",(Tcl_ObjCmdProc *)polygon_method_drawobj_orientation,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::polygon::grid_nearest",(Tcl_ObjCmdProc *)polygon_method_grid_nearest,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::polygon::hexgrid_create",(Tcl_ObjCmdProc *)polygon_method_hexgrid_create,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::polygon::hexagon",(Tcl_ObjCmdProc *)polygon_method_hexagon,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::polygon::info",(Tcl_ObjCmdProc *)polygon_method_info,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::polygon::intersect",(Tcl_ObjCmdProc *)polygon_method_intersect,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::polygon::poly_place",(Tcl_ObjCmdProc *)polygon_method_poly_place,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::polygon::rectangle",(Tcl_ObjCmdProc *)polygon_method_rectangle,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::polygon::simplify",(Tcl_ObjCmdProc *)polygon_method_simplify,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::polygon::segments",(Tcl_ObjCmdProc *)polygon_method_segments,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::polygon::squaregrid_create",(Tcl_ObjCmdProc *)polygon_method_squaregrid_create,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::polygon::vector_place",(Tcl_ObjCmdProc *)polygon_method_vector_place,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::polygon::within",(Tcl_ObjCmdProc *)polygon_method_within,NULL,NULL);

  Tcl_CreateEnsemble(interp, modPtr->fullName, modPtr, TCL_ENSEMBLE_PREFIX);
  Tcl_Export(interp, modPtr, "[a-z]*", 1);
  
  return TCL_OK;
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted cmodules/geometry/generic/segset.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
#include "odieInt.h"
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <math.h>

#ifndef IRM_EPSILON
#define IRM_EPSILON 1.19E-07
#endif

#ifndef M_PI
# define M_PI 3.1415926535898
#endif


/*
** LEFT OFF IMPLEMENTING SEGMENT SET AS A TCL OBJECT
** TODO: Write routine to detect a polygon and convert
** to segset and vice versa
**
*/

static Tcl_Interp *local_interp;

#if 0
/*
** Print all vectors in the SegSet.  Used for debugging purposes only.
*/
static void SegPrint(Segment *p, const char *zText){
  printf("%s ", zText);
  if( p ){
    printf("%g,%g -> %g,%g\n", p->from[X_IDX], p->from[Y_IDX], p->to[X_IDX], p->to[Y_IDX]);
  }else{
    printf(" (null)\n");
  }
}
static void SegSetPrint(SegSet *pSet){
  Link *pLink;
  printf("%d vectors:\n", pSet->nSeg);
  for(pLink=pSet->pAll; pLink; pLink=pLink->pNext){
    SegPrint(pLink->pLinkNode, "  ");
  }
}
#endif


/*
** This section implements code for breaking up compartment floorplans
** into triangles.
**
** A floorplan is defined by vectors (x0,y0,x1,y1) which define the
** parameter of each compartment.  The interior of the compartment
** is always to the right of the vector.   Thus the outer boundary
** of the compartment rotates clockwise when viewed from above.
** Compartments may contain holes which are interior voids surrounded 
** by counter-clockwise rotating boundaries.
*/

/*
** Given line segment AB, locate segment BC and return a Vector to
** it.  If there is not BC return NULL.  If there is more than one
** BC return the one that minimizes the angle ABC.
*/
static Segment *SegSetNext(SegSet *pSet, Segment *pAB){
  Link *pX;
  Segment *pBest = 0;
  double angle, bestAngle;
  int cnt = 0;
  int h;
  h = hashVectorXY(pAB->to);

  for(pX=pSet->hashFrom[h]; pX; pX=pX->pNext){
    Segment *pSeg = pX->pLinkNode;
    if( !sameVectorXY(pSeg->from,pAB->to) ) continue;
    /* if(pAB->isBoundary > 1 && pSeg->isBoundary!=0 && pSeg->isBoundary!=pAB->isBoundary) continue; */
    if( cnt==0 ){
      pBest = pSeg;
      bestAngle = fabs(VectorXY_angleOf(pAB->from, pAB->to, pBest->to));
    }else{
      angle = fabs(VectorXY_angleOf(pAB->from, pAB->to, pSeg->to));
      if( angle<bestAngle ){
        bestAngle = angle;
        pBest = pSeg;
      }
    }
    cnt++;
  }
  return pBest;
}

/*
** Remove all segments whose length is less than minLength.  For
** each segment removed, coalesce the inputs into a single new
** VectorXY at the center of the segment.
*/
static void Segset_Bisect_Edges(SegSet *pSet, double minLength){
  Link *pLoop, *pNext;
  double minLen2;

  /* Step 1 - Reduce short edges */
  minLen2 = minLength*minLength;
  
  
  for(pLoop=pSet->pAll; pLoop; pLoop=pNext){
    Segment *p;
    VectorXY from, to, center;
    double vec_dist_sq;

    p = pLoop->pLinkNode;
    pNext = pLoop->pNext;
    if(p->ignore) continue;
    p->ignore=1;
    vec_dist_sq = VectorXY_distance_squared(p->from, p->to);
    VectorXY_Set(from,p->from);
    VectorXY_Set(to,p->to);
    center[X_IDX] = rint(0.5*(from[X_IDX] + to[X_IDX]));
    center[Y_IDX] = rint(0.5*(from[Y_IDX] + to[Y_IDX]));
    
    if(vec_dist_sq>(minLen2*3)) {
      Segment *q;
      /* BISECT */
      p->to[X_IDX]=center[X_IDX];
      p->to[Y_IDX]=center[Y_IDX];
      q=SegSetInsert(pSet,center,to,p->isBoundary);
      if(q) {
        q->midpoint=1;
        q->ignore=1;
      }
    } else if(vec_dist_sq<minLen2 ){
      /* SHRINK */
      SegSetRemove(pSet, p);
      Odie_Free((char *)p);
      for(pLoop=pSet->pAll; pLoop; pLoop=pNext){
        pNext = pLoop->pNext;
        p = pLoop->pLinkNode;
        if( sameVectorXY(p->to,from) || sameVectorXY(p->to,to) ) {
          VectorXY_Set(p->to,center);
        }
        if( sameVectorXY(p->from,from) || sameVectorXY(p->from,to) ) {
          VectorXY_Set(p->from,center);
          SegRelink(pSet, p);
        }
        if( sameVectorXY(p->from,p->to) ) {
          SegSetRemove(pSet, p);
          Odie_Free((char *)p);
        }
      }
      pNext = pSet->pAll;
    }
  }
}

int Segset_Insert_Vectors(Tcl_Interp *interp,SegSet *pSet,int fill,int listLen,Tcl_Obj **listObjPtrs) {
  VECTORXY A,B;
  int i;
  /* Import a flat list, every 4 coordinates x0 y0 x1 y1*/
  if(listLen % 4) {
    Tcl_AppendResult(interp, "Could not interpret coordinates", 0);
    return TCL_ERROR;
  }
  for(i=0;i<listLen;i+=4) {      
    if(Tcl_GetDoubleFromObj(interp, listObjPtrs[i], &A[X_IDX])) return TCL_ERROR;
    if(Tcl_GetDoubleFromObj(interp, listObjPtrs[i+1], &A[Y_IDX])) return TCL_ERROR;
    if(Tcl_GetDoubleFromObj(interp, listObjPtrs[i+2], &B[X_IDX])) return TCL_ERROR;
    if(Tcl_GetDoubleFromObj(interp, listObjPtrs[i+3], &B[Y_IDX])) return TCL_ERROR;
    SegSetInsert(pSet,A,B,fill);
  }
  return TCL_OK;
}

/*
** Internal Utilities
*/

/*
** Routines for handling segments
*/

/*
** Add a new segment to the set
*/
CTHULHU_INLINE Segment *SegSetInsert(
  SegSet *pSet,
  VECTORXY from,
  VECTORXY to,
  u8 isBoundary
){
  int h;
  VectorXY_Round(from);
  VectorXY_Round(to);

  if( sameVectorXY(from,to) ){
    return NULL;
  }
  Segment *p = (Segment *)Odie_Alloc( sizeof(*p) );
  if( p==0 ) {
    return NULL;
  }
  VectorXY_Set(p->from,from);
  VectorXY_Set(p->to,to);

  p->isBoundary = isBoundary;
  p->notOblique = 0;
  LinkInit(p->pAll, p);
  LinkInit(p->pFrom, p);
  LinkInit(p->pSet, p);
  
  LinkInsert(&pSet->pAll, &p->pAll);
  h = hashPoint(p->from);
  LinkInsert(&pSet->hashFrom[h], &p->pFrom);

  pSet->nSeg++;
  pSet->pCurrent = p;
  return p;
}

/*
** Remove a segment from the segment set
*/
CTHULHU_INLINE void SegSetRemove(SegSet *pSet, Segment *p){
  LinkRemove(&p->pAll);
  LinkRemove(&p->pFrom);
  pSet->nSeg--;
  if( pSet->pCurrent==p ){
    pSet->pCurrent = pSet->pAll ? pSet->pAll->pLinkNode : 0;
  }
}

/*
** Call this routine to relink into a segment when the
** Seg.from vector changes.
*/
CTHULHU_INLINE void SegRelink(SegSet *pSet, Segment *p){
  int h;
  LinkRemove(&p->pFrom);
  h = hashPoint(p->from);
  LinkInsert(&pSet->hashFrom[h], &p->pFrom);
}

/*
** Remove all segments from a segment set
*/
CTHULHU_INLINE void SegSetClear(SegSet *pSet){
  while( pSet->pAll ){
    Segment *p;
    assert( pSet->nSeg>0 );
    p=pSet->pAll->pLinkNode;
    SegSetRemove(pSet, p);
    Odie_Free((char *)p);
  }
  assert( pSet->nSeg==0 );
}

/*
** Advance the pSet->pAll pointer so that it is pointing to a different
** segment.
*/
CTHULHU_INLINE void SegSetStep(SegSet *pSet){
  if( pSet->pCurrent ){
    Link *pNext = pSet->pCurrent->pAll.pNext;
    pSet->pCurrent = pNext ? pNext->pLinkNode : 0;
  }
  if( pSet->pCurrent==0 ){
    pSet->pCurrent = pSet->pAll ? pSet->pAll->pLinkNode : 0;
  }
}

static int  segset_method_create (
  ClientData *dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  local_interp=interp;
  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "N");
    return TCL_ERROR;
  }
  int isnew;
  SegSet *p;
  if( Odie_GetSegmentSetFromObj(interp, objv[1], &p, &isnew) ) return TCL_ERROR;
  if(isnew) {
    Tcl_SetObjResult(interp, Odie_NewSegmentSetObj(p));
  } else {
    Tcl_SetObjResult(interp, objv[1]);
  }
  return TCL_OK;
}


static int  segset_method_add (
  ClientData *dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  local_interp=interp;
  if( objc<2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "N X0 Y0 X1 Y1");
    return TCL_ERROR;
  }
  int i,j;
  SegSet *pSet;
  VECTORXY A,B;
  double x[4];
  
  if( Odie_GetSegmentGetFromVar(interp, objv[1], &pSet) ) return TCL_ERROR;
  for(i=2;i<objc;i+=4) {
    Segment *found;
    if((objc-i)<4) break;
    for(j=0; j<4; j++){
      if( Tcl_GetDoubleFromObj(interp, objv[i+j], &x[j]) ){
        goto createfail;
      }
    }
    A[X_IDX]=x[0];
    A[Y_IDX]=x[1];
    B[X_IDX]=x[2];
    B[Y_IDX]=x[3];
    /*
    ** Do not insert a vector into the wallset if it
    ** matches a vector already given. It's either redundent
    ** or the edge of a hole
    */
    found=SegSetFind(pSet,A,B);
    if(found) {
      if(found->isBoundary<1) {
        found->isBoundary=1;
      }
    } else {
      SegSetInsert(pSet, A, B, 1);
    }
  }

  Tcl_Obj *objPtr=Odie_NewSegmentSetObj(pSet);
  Tcl_ObjSetVar2(interp,objv[1],NULL,objPtr,0);
  Tcl_SetObjResult(interp, objPtr);

  return TCL_OK;

createfail:
  if(pSet) {
    SegSetClear(pSet);
  }

  return TCL_ERROR;
}

static int  segset_method_subtract (
  ClientData *dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  local_interp=interp;
  if( objc<2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "N X0 Y0 X1 Y1");
    return TCL_ERROR;
  }
  int i,j;
  SegSet *pSet=NULL;
  VECTORXY A,B;
  double x[4];

  if( Odie_GetSegmentGetFromVar(interp, objv[1], &pSet) ) return TCL_ERROR;
  for(i=2;i<objc;i+=4) {
    Segment *found;
    if((objc-i)<4) break;
    for(j=0; j<4; j++){
      Tcl_Obj *pObj;
      if( Tcl_GetDoubleFromObj(interp, objv[i+j], &x[j]) ){
        goto createfail;
      }
    }
    A[X_IDX]=x[0];
    A[Y_IDX]=x[1];
    B[X_IDX]=x[2];
    B[Y_IDX]=x[3];
    /*
    ** Do not insert a vector into the wallset if it
    ** matches a vector already given. It's either redundent
    ** or the edge of a hole
    */
    found=SegSetFind(pSet,A,B);
    if(found) {
      if(found->isBoundary<3) {
        found->isBoundary=3;
      }
    } else {
      SegSetInsert(pSet, A, B, 3);
    }
  }
  
  /*
  ** Meander through and clip all segments of isBoundary=1 that touch
  ** an isBoundary=3
  */
  
  Tcl_Obj *objPtr=Odie_NewSegmentSetObj(pSet);
  Tcl_ObjSetVar2(interp,objv[1],NULL,objPtr,0);
  Tcl_SetObjResult(interp, objPtr);
  return TCL_OK;

createfail:
  if(pSet) {
    SegSetClear(pSet);
  }

  return TCL_ERROR;
}


static int  segset_method_difference (
  ClientData *dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
#ifdef NEVER
  local_interp=interp;
  if( objc!=3 ){
    Tcl_WrongNumArgs(interp, 1, objv, "SEG_POSITIVE SEG_NEGATIVE");
    return TCL_ERROR;
  }
  int created;
  int i,j;
  SegSet *pSet=NULL;
  VECTORXY A,B;
  double x[4];

  Tcl_Obj **varv;
  int varc;
  if(Odie_GetSegmentSetFromObj(interp,objv[1],&pSet,&created)) return TCL_ERROR;

  if (Tcl_ListObjGetElements(interp, objv[2], &varc, &varv) != TCL_OK) {
    goto createfail;
  }
  if(Segset_Insert_Vectors(interp,pSet,3,varc,varv)) {
    goto createfail;
  }
  Link *pLoop, *pNext;
  Link *qLoop, *qNext;

  for(pLoop=pSet->pAll; pLoop; pLoop=pNext){
    Segment *pAB;
    pAB = pLoop->pLinkNode;
    pNext = pLoop->pNext;
    if(pAB->isBoundary>1) continue;

    for(qLoop=pSet->pAll; qLoop; qLoop=pNext){
      Segment *pCD;
      int incident=0;
      pCD = qLoop->pLinkNode;
      qNext = qLoop->pNext;
      if(pCD->isBoundary<3) continue;
      incident=ODIE_Math_LineLineCoincident(
        pAB->from[X_IDX],pAB->from[Y_IDX],
        pAB->to[X_IDX],pAB->to[Y_IDX],
        pCD->from[X_IDX],pCD->from[Y_IDX],
        pCD->to[X_IDX],pCD->to[Y_IDX]
      );
      switch(incident) {
        case 0: continue; /* No overlap */
        case 3:
          /* pAB fits entirely in the range of pBC*/
          SegSetRemove(pSet,pAB);
          continue;
        case 12:
          /* pCD fits entirely in the range of pAB*/
          /* Shorten the first side, and add a stub to represent the other */
          /* THIS CODE IS NOT FINISHED */
          if(VectorXY_distance_squared(pAB->from, pCD->from) < VectorXY_distance_squared(pAB->from, pCD->to)) {
            VectorXY_Set(pAB->from,pCD->from);
            SegRelink(pSet,pAB);
          } else {
            VectorXY_Set(pAB->to,pCD->to);
            SegSetInsert(pSet,pCD->from,pAB->to,1);
          }
          break;
        case 1: {
          /* A is along CD */
          if(VectorXY_distance_squared(pAB->from, pCD->from) < VectorXY_distance_squared(pAB->from, pCD->to)) {
            VectorXY_Set(pAB->from,pCD->from);
          } else {
            VectorXY_Set(pAB->from,pCD->to);
          }
          SegRelink(pSet,pAB);
          break;
        }
        case 2: {
          /* B is along CD */
          /* A is along CD */
          if(VectorXY_distance_squared(pAB->to, pCD->from) < VectorXY_distance_squared(pAB->to, pCD->to)) {
            VectorXY_Set(pAB->to,pCD->from);
          } else {
            VectorXY_Set(pAB->to,pCD->to);
          }
          break;
        }
        case 4: {
          /* C is along AB */
          if(VectorXY_distance_squared(pCD->from, pAB->from) < VectorXY_distance_squared(pCD->from, pAB->to)) {
            VectorXY_Set(pAB->from,pCD->from);
            SegRelink(pSet,pAB);
          } else {
            VectorXY_Set(pAB->to,pCD->from);
          }
          break;
        }
        case 8: {
          /* D is along AB */
          if(VectorXY_distance_squared(pCD->to, pAB->from) < VectorXY_distance_squared(pCD->to, pAB->to)) {
            VectorXY_Set(pAB->from,pCD->to);
            SegRelink(pSet,pAB);
          } else {
            VectorXY_Set(pAB->to,pCD->to);
          }
          break;
        }
      }
    }
  }
  
  
  if(isnew) {
    Tcl_SetObjResult(interp, Odie_NewSegmentSetObj(pSet));
  } else {
    Tcl_InvalidateStringRep(objv[1]);
    Tcl_SetObjResult(interp, objv[1]);
  }  
  return TCL_OK;

createfail:
  if(pSet) {
    SegSetClear(pSet);
  }

  return TCL_ERROR;
#else
  return TCL_OK;
#endif
}

static int  segset_method_rectangle (
  ClientData *dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  double cx, cy, radx,rady;
  Tcl_Obj *pResult=Tcl_NewObj();
  local_interp=interp;

  if( objc != 5 ){
    Tcl_WrongNumArgs(interp, 1, objv, "cx cy dimx dimy");
    return TCL_ERROR;
  }
  
  if(Tcl_GetDoubleFromObj(interp,objv[1],&cx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&cy)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[3],&radx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[4],&rady)) return TCL_ERROR;
  radx=radx/2.0;
  rady=rady/2.0;
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx-radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy-rady));

  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx+radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy-rady));

  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx+radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy-rady));
  
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx+radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy+rady));

  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx+radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy+rady));
  
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx-radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy+rady));

  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx-radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy+rady));

  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx-radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy-rady));
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}


static int WalkSegments(SegSet *pSet) {
  Link *pLoop, *pNext;
  int changed=0;
  for(pLoop=pSet->pAll; pLoop; pLoop=pNext){
    Segment *pAB, *pBC;
    Link *pRight, *pL2;
    int c;
    /* Find an oblique angle ABC */
    pAB = pLoop->pLinkNode;
    pNext = pLoop->pNext;

    /*
    ** If we are at an oblique for a non boundary
    ** segment, continue
    */
    if( pAB->notOblique ) continue;
    pBC = SegSetNext(pSet, pAB);
    if( pBC==0 ) {
      /*
      ** Remove an orphan wall
      */
      SegSetRemove(pSet, pAB);
      Odie_Free((char *)pAB);
      changed=1;
      continue;
    }

    if( (c = VectorXY_rightOf(pAB->from, pAB->to, pBC->to))>=0 ){
      if( c>0 || !sameVectorXY(pAB->from,pBC->to) ){
        pAB->notOblique = 1;
        continue;
      }
    }

    /* If we reach here, it means that ABC is an oblique angle.
    ** Locate all vertices to the right of AB.
    */
    pRight = 0;
    for(pL2=pSet->pAll; pL2; pL2=pL2->pNext){
      Segment *pX = pL2->pLinkNode;
      if( VectorXY_strictlyRightOf(pAB->from, pAB->to, pX->from)<0 ) continue;
      if( sameVectorXY(pAB->to,pX->from) ) continue;
      pX->score = VectorXY_distance_squared(pAB->to, pX->from);
      pX->isRight = VectorXY_rightOf(pBC->from, pBC->to, pX->from);
      LinkInit(pX->pSet, pX);
      LinkInsert(&pRight, &pX->pSet);
    }
    if( pRight==0 ){
      return TCL_ERROR;
    }
 
    /* pRight is a list of vertices to the right of AB.  Find the
    ** closest vertex X on this list where the line BX does not intersect
    ** any other segment in the polygon.  Then add segments BX and XB.
    */
    while( pRight ){
      Link *pBest=NULL;
      double bestScore;
      int bestRight;
      Segment *pThis,*pX, *pQ;


      /* Search for the "best" vertex.   The best vertex is the
      ** one that is closest.  Though if the vertex is to the left
      ** of BC (and thus would create another oblique angle) then
      ** artificially reduce its score because we would prefer not
      ** to use it.
      */
      pBest = pRight;
      pThis=pBest->pLinkNode;
      bestScore = pThis->score;
      bestRight = pThis->isRight;
      for(pL2=pBest->pNext; pL2; pL2=pL2->pNext){
        int better=0;
        pX = pL2->pLinkNode;
        if( pX->isRight>0 && bestRight <=0 ) {
          better=1;
        } else if ( pX->isRight<=0 && bestRight>0 ) {
          better=0;
        } else if( pX->score<bestScore ){
          better=1;
        }
        if(better) {
          bestScore = pX->score;
          bestRight = pX->isRight;
          pBest = pL2;
        }
      }
      
      

      /* The best vertex is pX */
      pX = pBest->pLinkNode;
      LinkRemove(pBest);

      /* Check to see if BX intersects any segment.  If it does, then
      ** go back and search for a different X
      */
      for(pL2=pSet->pAll; pL2; pL2=pL2->pNext){
        pQ = pL2->pLinkNode;
        if( pQ!=pAB && pQ!=pX
            && VectorXY_intersect(pAB->to, pX->from, pQ->from, pQ->to) ){
          break;
        }
      }
      if( pL2 ) continue;

      /* It did not intersect.  So add BX and XB to the pSet->
      */
      SegSetInsert(pSet, pAB->to, pX->from, 0);
      SegSetInsert(pSet, pX->from, pAB->to, 0);
      pRight = 0;
    }
    changed=1;
    if(!pAB->isBoundary) {
      pNext = pSet->pAll;
    }
  }
  if(changed) {
    return TCL_CONTINUE;
  }
  return TCL_OK;
}


/*
** tclcmd:   convex_subpolygons VECTORS ?MINLENGTH? ?HOLE? ?HOLE? ...
**
** VECTORS is a list of floating-VectorXY values.  Each group of four values
** forms a vector X0,Y0[X_IDX]1,Y1.  The vectors are in no particular order,
** but together they form one or more loops.  Space to the right of each
** vector is within the loop and space to the left is outside.
**
** Loops can be nested.  The outer boundary is formed by a clockwise loop
** of vectors.  Interior holes are formed by counter-clockwise loops.
**
** The output is a list polygons.  Each polygon is a list of 3 or more
** X,Y coordinate pairs.  All polygons are convex and disjoint and they
** together cover the input polygon.
**
** Optionally, the user can specify a series of polygons to be subtracted
** from the main polygon. These are given as an XY list suitable for
** producing a polygon on the tkcanvas
*/
TCL_COMMAND int  segset_method_decompose (
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  Tcl_Obj *pSub;   /* A sublist for a single polygon */
  int i, idx, cnt, created;
  SegSet *set;
  double minLen = 0.0;
  local_interp=interp;

  if( objc!=2 && objc!=3 && objc<4 ){
    Tcl_WrongNumArgs(interp, 1, objv, "VECTORS ?MINLENGTH? ?HOLE? ?HOLE? ...");
    return TCL_ERROR;
  }
  if(Odie_GetSegmentSetFromObj(interp,objv[1],&set,&created)) return TCL_ERROR;
  if( objc>2 ){
    if( Tcl_GetDoubleFromObj(interp, objv[2], &minLen) ) return TCL_ERROR;
  }
  /*
  ** Insert the polygons the user specified as
  ** the shape of the holes first
  */
  for(idx=3;idx<objc;idx++) {
    Poly *p;
    int created;
    if(Odie_GetPolygonFromObj(interp,objv[idx],&p,&created)) goto createrror;
    Segset_Insert_Polygon(set,p,idx);
    if(created) Odie_Free((char *)p);
  }

  if( minLen>0.0 ){
    Segset_Bisect_Edges(set,minLen);
  }
  
  cnt=0;
  i=TCL_CONTINUE;
  while(i==TCL_CONTINUE) {
    i=WalkSegments(set);
    cnt++;
    if(cnt>10) {
      break;
    }
  }
  if(i==TCL_CONTINUE) {
    Tcl_AppendResult(interp, "boundary too complex", 0);
    SegSetClear(set);
    return TCL_ERROR;
  }
  if(i==TCL_ERROR) {
    Tcl_AppendResult(interp, "boundary does not enclose a finite space", 0);
    SegSetClear(set);
    return TCL_ERROR;
  }

  /* Now all polygons should be convex.  We just have to generate them. */
  int obtuseangles=0;
  Tcl_Obj *pOut=Tcl_NewObj(); /* The output list */
  //Odie_trace_printf(interp,"NSEG %d\n",set->nSeg);
  while( set->nSeg ){
    VectorXY start;
    
    Segment *pAB, *pBC;
    int valid = 0;
    int cnt = 0;
    
    pAB = set->pAll->pLinkNode;
    start[X_IDX]=pAB->from[X_IDX];
    start[Y_IDX]=pAB->from[Y_IDX];

    /*
    ** Walk along the wallsets, filter out
    ** any that do not include one of the
    ** vectors given as an input of the first
    ** argument
    */
    pSub = Tcl_NewObj();
    while( pAB ){
      pBC = SegSetNext(set, pAB);
      if(pAB->isBoundary < 2) valid=1;
      cnt++;
      if( minLen>=1.0 ){
        Tcl_ListObjAppendElement(0, pSub, Tcl_NewIntObj(pAB->to[X_IDX]));
        Tcl_ListObjAppendElement(0, pSub, Tcl_NewIntObj(pAB->to[Y_IDX]));
      } else {
        Tcl_ListObjAppendElement(0, pSub, Tcl_NewDoubleObj(pAB->to[X_IDX]));
        Tcl_ListObjAppendElement(0, pSub, Tcl_NewDoubleObj(pAB->to[Y_IDX]));
      }
      SegSetRemove(set, pAB);
      if( sameVectorXY(pAB->to,start) ) {
        break;
      }
      Odie_Free((char *)pAB);
      pAB = pBC;
    }
    if( pAB==0 || cnt<3 || !valid){
      Tcl_DecrRefCount(pSub);
    }else{
      Tcl_ListObjAppendElement(0, pOut, pSub);
      //Odie_trace_printf(local_interp,"NEWPOLY %s\n",Tcl_GetString(pSub));
    }
  }

  if(created) {
    SegSetClear(set);
    Odie_Free((char *)set);
  }

  Tcl_SetObjResult(interp, pOut);
  return TCL_OK;

createrror:
  SegSetClear(set);
  return TCL_ERROR;
}

int Odie_Segset_Init(Tcl_Interp *interp) {
  Tcl_Namespace *modPtr;
  
  modPtr=Tcl_FindNamespace(interp,"segset",NULL,TCL_NAMESPACE_ONLY);
  if(!modPtr) {
    modPtr = Tcl_CreateNamespace(interp, "segset", NULL, NULL);
  }
  
  Tcl_CreateObjCommand(interp,"::segset::add",(Tcl_ObjCmdProc *)segset_method_add,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::segset::create",(Tcl_ObjCmdProc *)segset_method_create,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::segset::subtract",(Tcl_ObjCmdProc *)segset_method_subtract,NULL,NULL);
  //Tcl_CreateObjCommand(interp,"::segset::vectors",(Tcl_ObjCmdProc *)segset_method_vectors,NULL,NULL);


  Tcl_CreateObjCommand(interp,"::segset::decompose",(Tcl_ObjCmdProc *)segset_method_decompose,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::segset::rectangle",(Tcl_ObjCmdProc *)segset_method_rectangle,NULL,NULL);

  Tcl_CreateEnsemble(interp, modPtr->fullName, modPtr, TCL_ENSEMBLE_PREFIX);
  Tcl_Export(interp, modPtr, "[a-z]*", 1);
  
  return TCL_OK;
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted cmodules/geometry/generic/shapes.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475

/*
** This file is machine generated. Changes will
** be overwritten on the next run of cstruct.tcl
*/
#include "odieInt.h"

/*
** Functions provided by the template
*/


static int  shapes_method_corners (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {


  double cx, cy, radx,rady;
  
  if( objc != 5 && objc != 9 ){
    Tcl_WrongNumArgs(interp, 1, objv, "cx cy dimx dimy ?x0var y0var x1var y1var?");
    return TCL_ERROR;
  }
  
  if(Tcl_GetDoubleFromObj(interp,objv[1],&cx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&cy)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[3],&radx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[4],&rady)) return TCL_ERROR;
  if (objc == 5) {
    Tcl_Obj *pResult=Tcl_NewObj();
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx+radx));
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy-rady));
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx-radx));
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy+rady));
    Tcl_SetObjResult(interp, pResult);
    return TCL_OK;
  }
  /*
    Replaces
    set x0 [expr {$cx-$d}]
    set y0 [expr {$cy-$d}]
    set x1 [expr {$cx+$d}]
    set y1 [expr {$cy+$d}]
  */
  
  Tcl_ObjSetVar2(interp,objv[5],NULL,Tcl_NewDoubleObj(cx+radx),0);
  Tcl_ObjSetVar2(interp,objv[6],NULL,Tcl_NewDoubleObj(cy-rady),0);
  Tcl_ObjSetVar2(interp,objv[7],NULL,Tcl_NewDoubleObj(cx-radx),0);
  Tcl_ObjSetVar2(interp,objv[8],NULL,Tcl_NewDoubleObj(cy+rady),0);
  
  return TCL_OK; 
}

static int  shapes_method_drawobj_orientation (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {


  Tcl_Obj *temp;
  int len;
  double nx=100,ny=0;
  if( objc !=4 ){
    Tcl_WrongNumArgs(interp, 1, objv, "orientation nxvar nyvar");
    return TCL_ERROR;
  }
  
  if(Tcl_ListObjLength(interp,objv[1],&len)) return TCL_ERROR;
  if(len>0) {
    if(Tcl_ListObjIndex(interp, objv[1], 0, &temp)) return TCL_ERROR;
    if(Tcl_GetDoubleFromObj(interp,temp,&nx)) return TCL_ERROR;
  }
  if(len>1) {
    if(Tcl_ListObjIndex(interp, objv[1], 1, &temp)) return TCL_ERROR;
    if(Tcl_GetDoubleFromObj(interp,temp,&ny)) return TCL_ERROR;
  }
  Tcl_ObjSetVar2(interp,objv[2],NULL,Tcl_NewDoubleObj(nx),0);
  Tcl_ObjSetVar2(interp,objv[3],NULL,Tcl_NewDoubleObj(ny),0);
  return TCL_OK;
}


static int  shapes_method_poly_hex (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {


  int i,flip=0;
  double cx, cy, radx,rady;

  Tcl_Obj *pResult=Tcl_NewObj();
  double coords[7][2]= {
    {1.00, 0.00} , {0.50, M_SQRT3_2} , 
    {-0.50, M_SQRT3_2} , {-1.00, -0.00} , 
    {-0.50, -M_SQRT3_2} , {0.50, -M_SQRT3_2},
    {1.00, 0.00} 
  };
  if( objc != 5 && objc != 6){
    Tcl_WrongNumArgs(interp, 1, objv, "cx cy dimx dimy ?flip?");
    return TCL_ERROR;
  }
  
  if(Tcl_GetDoubleFromObj(interp,objv[1],&cx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&cy)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[3],&radx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[4],&rady)) return TCL_ERROR;
  if(objc==6) {
    if(Tcl_GetBooleanFromObj(interp,objv[5],&flip)) return TCL_ERROR;
  }
  radx=radx/2.0;
  rady=rady/2.0;

  for(i=0;i<6;i++) {
    if(flip) {
      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx+radx*coords[i][1]));
      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy+rady*coords[i][0]));
      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx+radx*coords[i+1][1]));
      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy+rady*coords[i+1][0]));
    } else {
      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx+radx*coords[i][0]));
      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy+rady*coords[i][1]));
      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx+radx*coords[i+1][0]));
      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy+rady*coords[i+1][1]));
    }
  }

  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

static int  shapes_method_poly_place (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {


  /*
  ** Apply Matrices
  */
  Tcl_Obj *pResult=Tcl_NewObj();
  int i;
  double zoom;
  double matA[6] = {1.0,0.0,0.0,1.0,0.0,0.0};
  double centerx,centery,normalx,normaly,angle;

  if( objc < 8 ){
      Tcl_WrongNumArgs(interp, 1, objv, "zoom centerx centery normalx normaly x1 y1 ?x2 y2?...");
      return TCL_ERROR;
  }   
  if(Tcl_GetDoubleFromObj(interp,objv[1],&zoom)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&centerx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[3],&centery)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[4],&normalx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[5],&normaly)) return TCL_ERROR;

  angle=atan2(normaly,normalx);
  matA[0]=cos(angle);
  matA[1]=sin(angle);
  matA[2]=-sin(angle);
  matA[3]=cos(angle);
  matA[4]=0.0;
  matA[5]=0.0;
  double startx,starty,prevx,prevy;

  i=6;
  {
    double x,y,sx,sy,newx,newy;
    if(Tcl_GetDoubleFromObj(interp,objv[i],&x)) return TCL_ERROR;
    if(Tcl_GetDoubleFromObj(interp,objv[i+1],&y)) return TCL_ERROR;
    
    sx=(x/zoom);
    sy=(y/zoom);
    newx=matA[0]*sx+matA[1]*sy+matA[4]+centerx;
    newy=matA[2]*sx+matA[3]*sy+matA[5]+centery;

    startx=newx;
    starty=newy;
    prevx=newx;
    prevy=newy;
  }
  
  for(i=8;i<objc;i+=2) {
    double x,y,sx,sy,newx,newy;
    if(Tcl_GetDoubleFromObj(interp,objv[i],&x)) return TCL_ERROR;
    if(Tcl_GetDoubleFromObj(interp,objv[i+1],&y)) return TCL_ERROR;
    
    sx=(x/zoom);
    sy=(y/zoom);
    newx=matA[0]*sx+matA[1]*sy+matA[4]+centerx;
    newy=matA[2]*sx+matA[3]*sy+matA[5]+centery;

    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(prevx));
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(prevy));  
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(newx));
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(newy));       

    prevx=newx;
    prevy=newy;
  }
  if(startx != prevx && starty!= prevy) {
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(prevx));
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(prevy));  
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(startx));
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(starty));  
  }
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

static int  shapes_method_polygon_to_vectors (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {


  Tcl_Obj *pResult=Tcl_NewObj();
  int i;
  double px,py,fx,fy;

  if(objc<6 || (objc-1)%2!=0) {
      Tcl_AppendResult(interp, "arguments should contain at least 6 and "
           " a multiple of 2 values", 0);
      return TCL_ERROR;

  }
  if(Tcl_GetDoubleFromObj(interp,objv[1],&px)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&py)) return TCL_ERROR;
  fx=px;
  fy=py;
  for(i=3;i<objc;i+=2) {
    double x,y;
    if(i+1>objc) break;
    if(Tcl_GetDoubleFromObj(interp,objv[i],&x)) return TCL_ERROR;
    if(Tcl_GetDoubleFromObj(interp,objv[i+1],&y)) return TCL_ERROR;
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(px));
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(py));
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(x));
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(y));
    px=x;
    py=y;
  }
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(px));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(py));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(fx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(fy));
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

static int  shapes_method_rectangle_as_polygon (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {


  double cx, cy, radx,rady;
  Tcl_Obj *pResult=Tcl_NewObj();

  if( objc != 5 ){
    Tcl_WrongNumArgs(interp, 1, objv, "cx cy dimx dimy");
    return TCL_ERROR;
  }
  
  if(Tcl_GetDoubleFromObj(interp,objv[1],&cx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&cy)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[3],&radx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[4],&rady)) return TCL_ERROR;
  radx=radx/2.0;
  rady=rady/2.0;
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx-radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy-rady));

  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx+radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy-rady));
  
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx+radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy+rady));

  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx-radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy+rady));

  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

static int  shapes_method_rectangle_as_vectors (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {


  double cx, cy, radx,rady;
  Tcl_Obj *pResult=Tcl_NewObj();

  if( objc != 5 ){
    Tcl_WrongNumArgs(interp, 1, objv, "cx cy dimx dimy");
    return TCL_ERROR;
  }
  
  if(Tcl_GetDoubleFromObj(interp,objv[1],&cx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&cy)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[3],&radx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[4],&rady)) return TCL_ERROR;
  radx=radx/2.0;
  rady=rady/2.0;
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx-radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy-rady));

  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx+radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy-rady));

  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx+radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy-rady));
  
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx+radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy+rady));

  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx+radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy+rady));
  
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx-radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy+rady));

  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx-radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy+rady));

  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cx-radx));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cy-rady));
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

static int  shapes_method_vector_place (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  /*
  ** Apply Matrices
  */
  Tcl_Obj *pResult=Tcl_NewObj();
  int i;
  double zoom;
  double matA[6] = {1.0,0.0,0.0,1.0,0.0,0.0};
  double centerx,centery,normalx,normaly,angle;

  if( objc < 8 ){
      Tcl_WrongNumArgs(interp, 1, objv, "zoom centerx centery normalx normaly x1 y1 ?x2 y2?...");
      return TCL_ERROR;
  }   
  if(Tcl_GetDoubleFromObj(interp,objv[1],&zoom)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&centerx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[3],&centery)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[4],&normalx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[5],&normaly)) return TCL_ERROR;

  angle=atan2(normaly,normalx);
  matA[0]=cos(angle);
  matA[1]=sin(angle);
  matA[2]=-sin(angle);
  matA[3]=cos(angle);
  matA[4]=0.0;
  matA[5]=0.0;

  
  for(i=6;i<objc;i+=2) {
      double x,y,sx,sy,newx,newy;
      if(Tcl_GetDoubleFromObj(interp,objv[i],&x)) return TCL_ERROR;
      if(Tcl_GetDoubleFromObj(interp,objv[i+1],&y)) return TCL_ERROR;
      
      sx=(x/zoom);
      sy=(y/zoom);
      newx=matA[0]*sx+matA[1]*sy+matA[4]+centerx;
      newy=matA[2]*sx+matA[3]*sy+matA[5]+centery;

      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(newx));
      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(newy));
  }
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}


static int  shapes_method_canvas (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
   /*
  ** Apply Matrices
  */
  Tcl_Obj *pResult=Tcl_NewObj();
  int i;
  double zoom;
  double matA[6] = {1.0,0.0,0.0,1.0,0.0,0.0};
  double centerx,centery,width,height,angle=0.0;

  if( objc < 6 || objc>7 ){
      Tcl_WrongNumArgs(interp, 1, objv, "zoom centerx centery ?angle?");
      return TCL_ERROR;
  }   
  if(Tcl_GetDoubleFromObj(interp,objv[1],&zoom)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&centerx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[3],&centery)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&width)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[3],&height)) return TCL_ERROR;
  if(objc==7) {
    if(Tcl_GetDoubleFromObj(interp,objv[4],&angle)) return TCL_ERROR;
    
  }
  matA[0]=cos(angle);
  matA[1]=sin(angle);
  matA[2]=-sin(angle);
  matA[3]=cos(angle);
  matA[4]=0.0;
  matA[5]=0.0;

  
  for(i=6;i<objc;i+=2) {
      double x,y,sx,sy,newx,newy;
      if(Tcl_GetDoubleFromObj(interp,objv[i],&x)) return TCL_ERROR;
      if(Tcl_GetDoubleFromObj(interp,objv[i+1],&y)) return TCL_ERROR;
      
      sx=(x/zoom);
      sy=(y/zoom);
      newx=matA[0]*sx+matA[1]*sy+matA[4]+centerx;
      newy=matA[2]*sx+matA[3]*sy+matA[5]+centery;

      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(newx));
      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(newy));
  }
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

int Odie_Shapes_Init(Tcl_Interp *interp) {
  Tcl_Namespace *modPtr;

  modPtr=Tcl_FindNamespace(interp,"shapes",NULL,TCL_NAMESPACE_ONLY);
  if(!modPtr) {
    modPtr = Tcl_CreateNamespace(interp, "shapes", NULL, NULL);
  }

  Tcl_CreateObjCommand(interp,"::shapes::corners",(Tcl_ObjCmdProc *)shapes_method_corners,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::shapes::drawobj_orientation",(Tcl_ObjCmdProc *)shapes_method_drawobj_orientation,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::shapes::poly_hex",(Tcl_ObjCmdProc *)shapes_method_poly_hex,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::shapes::poly_place",(Tcl_ObjCmdProc *)shapes_method_poly_place,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::shapes::polygon_to_vectors",(Tcl_ObjCmdProc *)shapes_method_polygon_to_vectors,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::shapes::rectangle_as_polygon",(Tcl_ObjCmdProc *)shapes_method_rectangle_as_polygon,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::shapes::rectangle_as_vectors",(Tcl_ObjCmdProc *)shapes_method_rectangle_as_vectors,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::shapes::vector_place",(Tcl_ObjCmdProc *)shapes_method_vector_place,NULL,NULL);

  Tcl_CreateEnsemble(interp, modPtr->fullName, modPtr, TCL_ENSEMBLE_PREFIX);
  Tcl_Export(interp, modPtr, "[a-z]*", 1);
  
  return TCL_OK;
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted cmodules/geometry/generic/slicer.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
/*
** This widget translates 3-D coordinates onto a flat canvas by splitting
** the 3-D space into layers and stacking the layers on the canvas.
**
** The layers are decks of the ship.  The highest layer (or deck) is drawn
** at the top of the page.  The next layer down is drawn below the top layer.
** and so forth down the canvas.  In other words, the 3D object is drawn
** by showing a set of 2D slices where each slice is viewed from above.
**
** The original 3D coordinates are called "actual" coordinates.  When
** translated into the 2D canvas they are called "canvas" coordinates.
**
** The actual coordinate system is right-handed.  The X axis increases to
** the right.  The Y axis increases going up.  The Z axis comes out of the
** page at the viewer.  The canvas coordinate system is left-handed.  The
** X axis increase to the right but the Y axis increases going down.
**
** A slicer is a object with methods.  The details of the available
** methods and what each does are described in comments before the
** implementation of each method.
*/
#include "odieInt.h"
#include <stdarg.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include <string.h>

/*
** This routine is called when a slicer is deleted.  All the memory and
** other resources allocated by this slicer is recovered.
*/
static void destroySlicer(void *pArg){
  Slicer *p = (Slicer*)pArg;
  int i;
  for(i=0; i<p->nSlice; i++){
    Odie_Free((char *)p->a[i].zName);
    Odie_Free((char *)p->a[i].xz);
  }
  Odie_Free((char *)p->a);
  Odie_Free((char *)p);
}

static int Location_FromTclObj(Tcl_Interp *interp, Tcl_Obj *pList,int *did,double *x,double *y) {
  int listlen;
  Tcl_Obj **elist;
  double z;
  if(Tcl_ListObjGetElements(interp,pList,&listlen,&elist)) {
    return TCL_ERROR;
  }
  if(listlen < 3 || listlen > 4) {
    Tcl_AppendResult(interp, "Could not interpret location ", Tcl_GetString(pList), 0);
    return TCL_ERROR;
  }
  if( Tcl_GetIntFromObj(interp, elist[0], did) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, elist[1], x) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, elist[2], y) ) return TCL_ERROR;
  return TCL_OK;
}


static double xCanvasToActual(Slicer *p,struct OneSlice *pS, double cx){
   double ax;
  if(p->nSlice==1) {
    ax = cx*p->rZoom;
  } else {
    ax = cx*p->rZoom - pS->rXShift;
  }   
  return ax;   
}

static double yCanvasToActual(Slicer *p,struct OneSlice *pS, double cy){
  double ay;
  if(p->nSlice==1) {
    ay=-cy*p->rZoom;
  } else {
    ay = pS->mxY + (pS->mnY-pS->mxY)*(cy-pS->top)/(pS->btm-pS->top);
  }
  return ay;
}

/*
** Convert a Y coordinate from actual to canvas coordinates for a
** given deck.
*/
static double xActualToCanvas(Slicer *p,struct OneSlice *pS, double ax){
  double cx;
  if(p->nSlice==1) {
    cx = ax/p->rZoom;
  } else {
    cx = (ax+pS->rXShift)/p->rZoom;
  }
  return cx;
}

/*
** Convert a Y coordinate from actual to canvas coordinates for a
** given deck.
*/
static double yActualToCanvas(Slicer *p,struct OneSlice *pS, double ay){
  double cy;
  if(p->nSlice==1) {
    cy=-ay/p->rZoom;
  } else {
    cy = pS->top + (pS->btm-pS->top)*(ay-pS->mxY)/(pS->mnY-pS->mxY);
  }
  return cy;
}

/*
** Return the height above baseline for the deck location rX.
*/
static double deckHeight(struct OneSlice *p, double rX){
  int i;
  double *xz;
  if(!p) {
    return 0.0;
  }
  if( p->nXZ<4 ){
    return p->z;
  }
  xz = p->xz;
  if( rX<=xz[0] ){
    return xz[1];
  }
  for(i=2; i<p->nXZ; i+=2){
    if( rX<=xz[i] ){
      assert( xz[i]>xz[i-2] );
      return xz[i-1] + (xz[i+1]-xz[i-1])*(rX-xz[i-2])/(xz[i]-xz[i-2]);
    }
  }
  return xz[p->nXZ-1];
}

/*
** Return a pointer to the particular deck at actual coordinates
** X, Z
*/
static struct OneSlice *deckAt(Slicer *p, double rX, double rZ){
  int i;
  struct OneSlice *pBest;
  double bestHeight;
  if( p->nSlice==0 ) return 0;
  pBest = &p->a[0];
  bestHeight = deckHeight(pBest, rX);
  for(i=1; i<p->nSlice; i++){
    double dh = deckHeight(&p->a[i],rX);
    if( dh>bestHeight && dh<=rZ ){
      pBest = &p->a[i];
      bestHeight = dh;
    }
  }
  return pBest;
}


/*
** Return a pointer to the deck immediately above the given deck.
** Return NULL if the given deck is topmost.
*/
static struct OneSlice *deckAbove(Slicer *p, struct OneSlice *pRef){
  int i;
  struct OneSlice *pBest = 0;
  if( p->nSlice==0 ) return 0;
  for(i=0; i<p->nSlice; i++){
    struct OneSlice *pTest = &p->a[i];
    if( pTest->z<=pRef->z ) continue;
    if( pBest==0 || pBest->z>pTest->z ){
      pBest = pTest;
    }
  }
  return pBest;
}
/*
** Return a pointer to the deck immediately above the given deck.
** Return NULL if the given deck is topmost.
*/
static struct OneSlice *deckBelow(Slicer *p, struct OneSlice *pRef){
  int i;
  struct OneSlice *pBest = 0;
  if( p->nSlice==0 ) return 0;
  for(i=0; i<p->nSlice; i++){
    struct OneSlice *pTest = &p->a[i];
    if( pTest->z>=pRef->z ) continue;
    if( pBest==0 || pBest->z<pTest->z ){
      pBest = pTest;
    }
  }
  return pBest;
}


/*
** Recompute the values of p->a[].top and p->a[].btm for all slices in
** the given slicer.
*/
static void computeTopAndBottom(Slicer *p){
  int i;
  double rY = 0.0;
  double rBound = -9.9e99;
  for(i=p->nSlice-1; i>=0; i--){
    double h = (p->a[i].mxY - p->a[i].mnY)/p->rZoom;
    p->a[i].upperbound = rBound;
    p->a[i].top = rY;
    p->a[i].btm = rY + h;
    rY = p->a[i].btm + 0.3*h;
    rBound = p->a[i].btm + 0.15*h;
  }
    
  if( p->nSlice==0 ) return;

  /* Calculate the above and below for each deck */

  for(i=0; i<p->nSlice; i++){
    struct OneSlice *pThis = &p->a[i];
    struct OneSlice *pBest = 0;

    pBest=deckAbove(p,pThis);
    if(pBest) {
      pThis->above=pBest->did;
    } else {
      pThis->above=0;
    }
    pBest=deckBelow(p,pThis);
    if(pBest) {
      pThis->below=pBest->did;
    } else {
      pThis->below=0;
    }
  }
}

/*
** pObj is either the name of a deck or a Z coordinate.  If it is a
** deck name, find the deck and write a pointer to it in *ppS.  If
** it is a Z coordinate, use that coordinate together with rX to 
** find the deck and write it into *ppS.  If an error occurs, put
** an error message on the TCL interpreter and return TCL_ERROR.
** Return TCL_OK on success.
*/
static int getDeck(
  Tcl_Interp *interp,     /* Put error messages here */
  Slicer *p,              /* The slicer */
  double rX,              /* X coord used to find deck if pObj is a Z coord */
  Tcl_Obj *pObj,          /* Either a deck name or a Z coordinate */
  struct OneSlice **ppS   /* Write the slice pointer here */
){
  double rZ;
  const char *zName;
  int i;
  if(p->nSlice==1) {
    *ppS=&p->a[0];
    return TCL_OK;
  }
  if( Tcl_GetDoubleFromObj(0, pObj, &rZ)==TCL_OK ){
    *ppS = deckAt(p, rX, rZ);
    return TCL_OK;
  }
  zName = Tcl_GetStringFromObj(pObj, 0);
  for(i=0; i<p->nSlice; i++){
    if( strcmp(zName, p->a[i].zName)==0 ){
      *ppS = &p->a[i];
      return TCL_OK;
    }
  }
  Tcl_AppendResult(interp, "no such deck: ", zName, 0);
  return TCL_ERROR;
}


/*
** pObj is either the name of a deck or a Z coordinate.  If it is a
** deck name, find the deck and write a pointer to it in *ppS.  If
** it is a Z coordinate, use that coordinate together with rX to 
** find the deck and write it into *ppS.  If an error occurs, put
** an error message on the TCL interpreter and return TCL_ERROR.
** Return TCL_OK on success.
*/
static int getDeckId(
  Tcl_Interp *interp,     /* Put error messages here */
  Slicer *p,              /* The slicer */
  Tcl_Obj *pObj,          /* Either a deck name or a Z coordinate */
  struct OneSlice **ppS   /* Write the slice pointer here */
){
  int did;
  const char *zName;
  int i;
  if(p->nSlice==1) {
    *ppS=&p->a[0];
    return TCL_OK;
  }
  if( Tcl_GetIntFromObj(interp, pObj, &did)==TCL_OK ){
    for(i=0; i<p->nSlice; i++){
      if( did == p->a[i].did ){
        *ppS = &p->a[i];
        return TCL_OK;
      }
    }
    Tcl_AppendResult(interp, "no such deckid: ", 0);
    return TCL_ERROR;
  }
  zName = Tcl_GetStringFromObj(pObj, 0);
  for(i=0; i<p->nSlice; i++){
    if( strcmp(zName, p->a[i].zName)==0 ){
      *ppS = &p->a[i];
      return TCL_OK;
    }
  }
  Tcl_AppendResult(interp, "no such deck: ", zName, 0);
  return TCL_ERROR;
}

static inline struct OneSlice *getDeck_FromInt(
  Slicer *p,
  int did
) {
  int i;
  if(p->nSlice==1) {
    return &p->a[0];
  }
  for(i=0; i<p->nSlice; i++){
    if( did == p->a[i].did ){
      return &p->a[i];
    }
  }
  return NULL;
}

/*
** Methods
*/

static int slicer_drawline_do(
  Tcl_Interp *interp,
  Slicer *p,
  Tcl_Obj *canvas,
  Tcl_Obj *tagname,
  Tcl_Obj *tagname_transdeck,
  Tcl_Obj *tagname_penetration,
  int coord_count,
  int *deckCoord,double *xCoord,double *yCoord,
  struct OneSlice **apDeck
) {
  int i;

  Tcl_Obj *pVTag;             /* The "sNNN" tag added to all line segments */
  const char *zXTag;          /* Trans-deck tag (dashed lines) */
  const char *zPTag;          /* Deck-penetraction tag */
  Tcl_Obj *aLineArg[20];      /* Element of "create line" TCL command */
  int nLineArg;               /* Number of used entries in aLineArg[] */
  Tcl_Obj *aPenArg[20];       /* Cmd to draw deck penetractions */
  int nPenArg;
  zXTag = Tcl_GetStringFromObj(tagname_transdeck, 0);
  zPTag = Tcl_GetStringFromObj(tagname_penetration, 0);
  
  aLineArg[0] = canvas;
  aLineArg[1] = ODIE_CONSTANT_STRING("create");
  aLineArg[2] = ODIE_CONSTANT_STRING("line");
  for(i=3; i<=6; i++){
    aLineArg[i] = Tcl_NewObj();
  }
  aLineArg[7] = ODIE_CONSTANT_STRING("-tags");
  for(i=0; i<=7; i++){
    Tcl_IncrRefCount(aLineArg[i]);
  }
  nLineArg = 9;

  if( zPTag[0]==0 ){
    nPenArg = 0;
  }else{
    aPenArg[0] = canvas;
    aPenArg[1] = ODIE_CONSTANT_STRING("create");
    aPenArg[2] = ODIE_CONSTANT_STRING("oval");
    for(i=3; i<=6; i++){
      aPenArg[i] = Tcl_NewObj();
    }
    aPenArg[7] = ODIE_CONSTANT_STRING("-tags");
    for(i=0; i<=7; i++){
      Tcl_IncrRefCount(aPenArg[i]);
    }
    nPenArg = 9;
  }

  for(i=1; i<coord_count; i++){
    char zBuf[30];
    double x0, y0, x1, y1, Y0, Y1;
    sprintf(zBuf, "s%d", i);
    pVTag = Tcl_NewStringObj(zBuf, -1);
    Tcl_IncrRefCount(pVTag);
    if( apDeck[i]!=apDeck[i-1] ){
      x0 = xActualToCanvas(p,apDeck[i-1],xCoord[i-1]);
      x1 = xActualToCanvas(p,apDeck[i],xCoord[i]);
      
      y0 = yActualToCanvas(p,apDeck[i-1], Y0=yCoord[i-1]);
      y1 = yActualToCanvas(p,apDeck[i], Y1=yCoord[i]);
      if( zXTag[0]!=0 ){
        Tcl_SetDoubleObj(aLineArg[3], x0);
        Tcl_SetDoubleObj(aLineArg[4], y0);
        Tcl_SetDoubleObj(aLineArg[5], x1);
        Tcl_SetDoubleObj(aLineArg[6], y1);
        aLineArg[8] = Tcl_DuplicateObj(tagname_transdeck);
        Tcl_ListObjAppendElement(0, aLineArg[8], pVTag);
        Tcl_IncrRefCount(aLineArg[8]);
        Tcl_EvalObjv(interp, nLineArg, aLineArg, 0);
        Tcl_DecrRefCount(aLineArg[8]);
      }
      continue;
    }
    
    x0 = xActualToCanvas(p,apDeck[i],xCoord[i-1]);
    x1 = xActualToCanvas(p,apDeck[i],xCoord[i]);
    
    y0 = yActualToCanvas(p,apDeck[i], yCoord[i-1]);
    y1 = yActualToCanvas(p,apDeck[i], yCoord[i]);
    Tcl_SetDoubleObj(aLineArg[3], x0);
    Tcl_SetDoubleObj(aLineArg[4], y0);
    Tcl_SetDoubleObj(aLineArg[5], x1);
    Tcl_SetDoubleObj(aLineArg[6], y1);
    aLineArg[8] = Tcl_DuplicateObj(tagname);
    Tcl_ListObjAppendElement(0, aLineArg[8], pVTag);
    Tcl_DecrRefCount(pVTag);
    Tcl_IncrRefCount(aLineArg[8]);
    Tcl_EvalObjv(interp, nLineArg, aLineArg, 0);
    Tcl_DecrRefCount(aLineArg[8]);
  }
  for(i=0; i<=7; i++){
    Tcl_DecrRefCount(aLineArg[i]);
    if( i<nPenArg ) Tcl_DecrRefCount(aPenArg[i]);
  }
  for(i=9; i<nLineArg; i++){
    Tcl_DecrRefCount(aLineArg[i]);
  }
  for(i=9; i<nPenArg; i++){
    Tcl_DecrRefCount(aPenArg[i]);
  }
  return TCL_OK;
}

/*
** tclmethod:  SLICER drawline_dxyz CANVAS START PATH END TAG TRANSDECK-TAG P-TAG
** title:   Return TK to draw a line on a canvas
*/ 
static int slicer_method_drawline(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  Slicer *p = (Slicer*)pArg;

  int i, j=0, totalN, n, mode;
  double *xCoord,*yCoord;             /* Actual coordinates of line to draw */
  int *deckCoord;
  struct OneSlice **apDeck;   /* Array of all decks */
  
  if( objc!=6 ){
    Tcl_WrongNumArgs(interp, 1, objv, 
        "CANVAS PATH TAG TRANSDECK-TAG PENETRATION-TAG");
    return TCL_ERROR;
  }
  if( Tcl_ListObjLength(interp, objv[2], &n) ) return TCL_ERROR;

  totalN=n+2;
  xCoord = (double *)Odie_Alloc(sizeof(xCoord[0])*totalN);
  yCoord = (double *)Odie_Alloc(sizeof(yCoord[0])*totalN);
  deckCoord = (int *)Odie_Alloc(sizeof(deckCoord[0])*(totalN));
  apDeck = (struct OneSlice **)Odie_Alloc(sizeof(apDeck[0])*(totalN));
  
  if( xCoord==0 || yCoord == 0 || deckCoord == 0 || apDeck == 0 ) {
    return TCL_ERROR;
  }
  
  j=0;
  for(i=0; i<n; i++){
    Tcl_Obj *deckObj,*xObj,*yObj;
    Tcl_ListObjIndex(0, objv[2], i, &deckObj);
    if( Tcl_GetIntFromObj(interp, deckObj, &deckCoord[j]) || i>(n-4) ) {
      if(Location_FromTclObj(interp,deckObj,&deckCoord[j],&xCoord[j],&yCoord[j])) {
        goto badRoute;
      }
    } else {
      if(i++ >= n) goto badRoute; 
      Tcl_ListObjIndex(0, objv[2], i, &xObj);
      if(i++ >= n) goto badRoute; 
      Tcl_ListObjIndex(0, objv[2], i, &yObj);
      i++;
      if( Tcl_GetDoubleFromObj(interp, xObj, &xCoord[j]) ) goto badRoute;
      if( Tcl_GetDoubleFromObj(interp, yObj, &yCoord[j]) )  goto badRoute;
    }
    if(deckCoord[j]==deckCoord[j-1]) {
      apDeck[j]=apDeck[j-1];
    } else {
      apDeck[j]=getDeck_FromInt(p,deckCoord[j]); if(!apDeck[j]) goto badRoute;
    }
    j++;
  }
  slicer_drawline_do(interp,p,objv[1],objv[3],objv[4],objv[5],j,deckCoord,xCoord,yCoord,apDeck);
  Odie_Free((char *)xCoord);
  Odie_Free((char *)yCoord);
  Odie_Free((char *)deckCoord);
  Odie_Free((char *)apDeck);
  return TCL_OK;

badRoute:
  Odie_Free((char *)xCoord);
  Odie_Free((char *)yCoord);
  Odie_Free((char *)deckCoord);
  Odie_Free((char *)apDeck);
  return TCL_ERROR;
}


/*
** tclmethod:  SLICER drawline_dxyz START PATH END
** title:   Return TK to draw a line on a canvas
*/ 
static int slicer_method_link_coords(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  Slicer *p = (Slicer*)pArg;

  int i, j=0, totalN, n, mode;
  double *xCoord,*yCoord;             /* Actual coordinates of line to draw */
  int *deckCoord;
  struct OneSlice **apDeck;   /* Array of all decks */

  if( objc!=8 ){
    Tcl_WrongNumArgs(interp, 1, objv, 
        "START PATH END");
    return TCL_ERROR;
  }
  if( Tcl_ListObjLength(interp, objv[3], &n) ) return TCL_ERROR;

  totalN=n+2;
  xCoord = (double *)Odie_Alloc(sizeof(xCoord[0])*totalN);
  yCoord = (double *)Odie_Alloc(sizeof(yCoord[0])*totalN);
  deckCoord = (int *)Odie_Alloc(sizeof(deckCoord[0])*(totalN));
  apDeck = (struct OneSlice **)Odie_Alloc(sizeof(apDeck[0])*(totalN));
  
  if( xCoord==0 || yCoord == 0 || deckCoord == 0 || apDeck == 0 ) {
    return TCL_ERROR;
  }
  
  j=0;
  if(Location_FromTclObj(interp,objv[2],&deckCoord[j],&xCoord[j],&yCoord[j])) {
    goto badRoute;
  }
  apDeck[j]=getDeck_FromInt(p,deckCoord[j]); if(!apDeck[j]) goto badRoute;

  j++;

  for(i=0; i<n; i++){
    Tcl_Obj *deckObj,*xObj,*yObj;
    Tcl_ListObjIndex(0, objv[3], i, &deckObj);
    if( Tcl_GetIntFromObj(interp, deckObj, &deckCoord[j]) || i>(n-4) ) {
      if(Location_FromTclObj(interp,deckObj,&deckCoord[j],&xCoord[j],&yCoord[j])) {
        goto badRoute;
      }
    } else {
      Tcl_ListObjIndex(0, objv[3], i+1, &xObj);
      Tcl_ListObjIndex(0, objv[3], i+2, &yObj);
      if( Tcl_GetDoubleFromObj(interp, xObj, &xCoord[j]) ) goto badRoute;
      if( Tcl_GetDoubleFromObj(interp, yObj, &yCoord[j]) )  goto badRoute;
      i+=3;
    }
    if(deckCoord[j]==deckCoord[j-1]) {
      apDeck[j]=apDeck[j-1];
    } else {
      apDeck[j]=getDeck_FromInt(p,deckCoord[j]); if(!apDeck[j]) goto badRoute;
    }
    j++;
  }

  if(Location_FromTclObj(interp,objv[4],&deckCoord[j],&xCoord[j],&yCoord[j])) {
    goto badRoute;
  }
  apDeck[j]=getDeck_FromInt(p,deckCoord[j]); if(!apDeck[j]) goto badRoute;
  j++;
  slicer_drawline_do(interp,p,objv[1],objv[5],objv[6],objv[7],j,deckCoord,xCoord,yCoord,apDeck);
  Odie_Free((char *)xCoord);
  Odie_Free((char *)yCoord);
  Odie_Free((char *)deckCoord);
  Odie_Free((char *)apDeck);
  return TCL_OK;

badRoute:
  Odie_Free((char *)xCoord);
  Odie_Free((char *)yCoord);
  Odie_Free((char *)deckCoord);
  Odie_Free((char *)apDeck);
  return TCL_ERROR;
}

static inline double Location_zabs(Slicer *p,struct OneSlice *pS,double x0,double dheight) {
  if(dheight >= 0) {
    return dheight+deckHeight(pS,x0);
  }
  struct OneSlice *pAbove;
  pAbove=deckAbove(p,pS);
  if(pAbove) {
    return deckHeight(pAbove,x0)+dheight;
  }
  return deckHeight(pS,x0)+p->upper_height+dheight;
}

static inline double Location_zdeck(Slicer *p,struct OneSlice *pS,double x0,double dheight) {
  if(dheight >= 0) {
    return dheight;
  }
  struct OneSlice *pAbove;
  pAbove=deckAbove(p,pS);
  if(pAbove) {
    return deckHeight(pAbove,x0)-deckHeight(pS,x0)+dheight;
  }
  return p->upper_height+dheight;
}

/*
** This routine runs when a method is executed against a slicer
*/
static int slicerMethodProc(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  Slicer *p = (Slicer*)pArg;

#if 0
  /* For debugging....
  ** Print each wallset command before it is executed.
  */
  { int i;
    for(i=0; i<objc; i++){
      printf("%s%c", Tcl_GetStringFromObj(objv[i], 0), i<objc-1 ? ' ' : '\n');
    }
  }
#endif

  /* The following bit of magic implements a switch() statement that
  ** invokes the correct code based on the value of objv[1].  The
  ** mktclopts.tcl script scans this source file looking for "case"
  ** statements then generates the "wallset.h" include file that contains
  ** all of the code necessary to implement the switch.
  **
  ** In this way, we can add new methods to the command simply by adding
  ** new cases within the switch.  All the related switch code is
  ** regenerated automatically.
  */
  {
#include "slicer_cases.h"
  {

  /* tclmethod:  SLICER above NAME
  ** title:   Return the deck above NAME
  */
  case SLICER_ABOVE: {
    struct OneSlice *pDeck;
    if( objc!=3 &&  objc!=5 && objc!=6){
      Tcl_WrongNumArgs(interp, 2, objv, "NAME ?x y? ?zoff?");
      return TCL_ERROR;
    }
    if( getDeckId(interp,p,objv[2],&pDeck) ) return TCL_ERROR;
    if(objc==3) {
      Tcl_SetObjResult(interp, Tcl_NewIntObj(pDeck->above));
    } else {
      Tcl_Obj *pResult=Tcl_NewObj();

      Tcl_ListObjAppendElement(interp, pResult, Tcl_NewIntObj(pDeck->above));
      Tcl_ListObjAppendElement(interp, pResult, Tcl_DuplicateObj(objv[3]));
      Tcl_ListObjAppendElement(interp, pResult, Tcl_DuplicateObj(objv[4]));
      if(objc==6) {
        Tcl_ListObjAppendElement(interp, pResult, Tcl_DuplicateObj(objv[5]));
      }
      Tcl_SetObjResult(interp, pResult);
    }
    return TCL_OK;
  }
  
  /* tclmethod:  SLICER below NAME
  ** title:   Return the deck below NAME
  */
  case SLICER_BELOW: {
    struct OneSlice *pDeck;
    if( objc!=3 &&  objc!=5 && objc!=6){
      Tcl_WrongNumArgs(interp, 2, objv, "NAME ?x y? ?zoff?");
      return TCL_ERROR;
    }
    if( getDeckId(interp,p,objv[2],&pDeck) ) return TCL_ERROR;
    if(objc==3) {
      Tcl_SetObjResult(interp, Tcl_NewIntObj(pDeck->below));
    } else {
      Tcl_Obj *pResult=Tcl_NewObj();

      Tcl_ListObjAppendElement(interp, pResult, Tcl_NewIntObj(pDeck->below));
      Tcl_ListObjAppendElement(interp, pResult, Tcl_DuplicateObj(objv[3]));
      Tcl_ListObjAppendElement(interp, pResult, Tcl_DuplicateObj(objv[4]));
      if(objc==6) {
        Tcl_ListObjAppendElement(interp, pResult, Tcl_DuplicateObj(objv[5]));
      }
      Tcl_SetObjResult(interp, pResult);
    }
    return TCL_OK;
  }
  case SLICER_DECKID_TO_NAME: {
    struct OneSlice *pDeck;
    if( objc!=3 ) {
      Tcl_WrongNumArgs(interp, 2, objv, "ID");
      return TCL_ERROR;
    }
    if( getDeckId(interp,p,objv[2],&pDeck) ) return TCL_ERROR;
    Tcl_SetObjResult(interp, ODIE_CONSTANT_STRING(pDeck->zName));
    return TCL_OK;
  }
  case SLICER_DECKNAME_TO_ID: {
    struct OneSlice *pDeck;
    if( objc!=3 ) {
      Tcl_WrongNumArgs(interp, 2, objv, "NAME");
      return TCL_ERROR;
    }
    if( getDeckId(interp,p,objv[2],&pDeck) ) return TCL_ERROR;
    Tcl_SetObjResult(interp, Tcl_NewIntObj(pDeck->did));
    return TCL_OK;
  }

  /*
  ** tclmethod:  SLICER xyz_to_location X Y Z
  ** title:   Convert from X Y Z to IRM Coordinates
  **
  ** The ABOVE-DECK parameter, if present, is the height above the deck.
  */  
  case SLICER_XYZ_TO_LOCATION: {
    double x0, y0, z0, dheight;
    Tcl_Obj *pResult;
    struct OneSlice *pS;
    int i;
    if(objc==2) {
      Tcl_SetObjResult(interp, Tcl_NewObj());
      return TCL_OK;
    }
    if( objc < 5 ){
      Tcl_WrongNumArgs(interp, 2, objv, "X Y Z ?x y z?");
      return TCL_ERROR;
    }
    if( p->nSlice<=0 ){
      Tcl_AppendResult(interp, "no slices defined", 0);
      return TCL_ERROR;
    }
    pResult = Tcl_NewObj();

    for(i=2;i<objc;i+=3) {
      if( Tcl_GetDoubleFromObj(interp, objv[i], &x0) ) return TCL_ERROR;
      if( Tcl_GetDoubleFromObj(interp, objv[i+1], &y0) ) return TCL_ERROR;
      if( Tcl_GetDoubleFromObj(interp, objv[i+2], &z0) ) return TCL_ERROR;
      pS = deckAt(p, x0, z0);
      dheight=z0-deckHeight(pS,x0);
    
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(pS->did));
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(x0));
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(y0));
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(dheight));
    }

    Tcl_SetObjResult(interp, pResult);
    break;
  }
  case SLICER_LOCATION_TO_XYZ: {
    int i;
    double x0, y0, z0, dheight;
    Tcl_Obj *pResult;
    struct OneSlice *pS,*pAbove;
    if(objc==2) {
      Tcl_SetObjResult(interp, Tcl_NewObj());
      return TCL_OK;
    }
    if( objc!= 5 && objc < 6 ){
      Tcl_WrongNumArgs(interp, 2, objv, "DECK X Y ?ZOFF? ...");
      return TCL_ERROR;
    }
    if( p->nSlice<=0 ){
      Tcl_AppendResult(interp, "no slices defined", 0);
      return TCL_ERROR;
    }
    if(objc<7) {
      if( getDeckId(interp,p,objv[2],&pS) ) return TCL_ERROR;
      if( Tcl_GetDoubleFromObj(interp, objv[3], &x0) ) return TCL_ERROR;
      if( Tcl_GetDoubleFromObj(interp, objv[4], &y0) ) return TCL_ERROR;
      if(objc==6) {
        if( Tcl_GetDoubleFromObj(NULL, objv[5], &dheight) ) {
          dheight=0.0;
        }
      } else {
        dheight=0.0;
      }
      pResult = Tcl_NewObj();
      z0=Location_zabs(p,pS,x0,dheight);
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(x0));
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(y0));
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(z0));
      Tcl_SetObjResult(interp, pResult);
      return TCL_OK;
    }
    pResult = Tcl_NewObj();
    for(i=2;i<objc;i+=4) {
      if( getDeckId(interp,p,objv[i],&pS) ) return TCL_ERROR;
      if( Tcl_GetDoubleFromObj(interp, objv[i+1], &x0) ) return TCL_ERROR;
      if( Tcl_GetDoubleFromObj(interp, objv[i+2], &y0) ) return TCL_ERROR;
      if( Tcl_GetDoubleFromObj(NULL, objv[i+3], &dheight) ) return TCL_ERROR;
      
      z0=Location_zabs(p,pS,x0,dheight);
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(x0));
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(y0));
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(z0));
    }
    Tcl_SetObjResult(interp, pResult);
    break;
  }
  case SLICER_LOCATION_Z: {
    int i;
    double x0, y0, z0, dheight;
    Tcl_Obj *pResult;
    struct OneSlice *pS,*pAbove;
    if( objc!= 5 && objc!=6 ){
      Tcl_WrongNumArgs(interp, 2, objv, "DECK X Y ?ZOFF? ...");
      return TCL_ERROR;
    }
    if( p->nSlice<=0 ){
      Tcl_AppendResult(interp, "no slices defined", 0);
      return TCL_ERROR;
    }
    if( getDeckId(interp,p,objv[2],&pS) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[3], &x0) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[4], &y0) ) return TCL_ERROR;
    if(objc==6) {
      if( Tcl_GetDoubleFromObj(NULL, objv[5], &dheight) ) {
        dheight=0.0;
      }
    } else {
      dheight=0.0;
    }
    z0=Location_zabs(p,pS,x0,dheight);
    Tcl_SetObjResult(interp, Tcl_NewDoubleObj(z0));
    return TCL_OK;
  }
  /*
  ** tclmethod:  SLICER actualcoords CANVAS-COORD-LIST  ?ABOVE-DECK?
  ** title:   Convert from canvas to actual coordinate space
  **
  ** The ABOVE-DECK parameter, if present, is the height above the deck.
  */
  case SLICER_ACTUALCOORDS: {
    int i, n;
    double aboveDeck;
    Tcl_Obj *pResult;
    if( objc!=3 && objc!=4 ){
      Tcl_WrongNumArgs(interp, 2, objv, "CANVAS-COORD-LIST ?ABOVE-DECK?");
      return TCL_ERROR;
    }
    if( Tcl_ListObjLength(interp, objv[2], &n) ) return TCL_ERROR;
    if( n%2!=0 ){
      Tcl_AppendResult(interp, "coordinate list must contain a multiple "
         "of 2 values", 0);
      return TCL_ERROR;
    }
    if( objc==4 ){
      if( Tcl_GetDoubleFromObj(interp, objv[3], &aboveDeck) ) return TCL_ERROR;
    }else{
      aboveDeck = 0.0;
    }
    if( p->nSlice<=0 ){
      Tcl_AppendResult(interp, "no slices defined", 0);
      return TCL_ERROR;
    }
    pResult = Tcl_NewObj();
    for(i=0; i<n-1; i+=2){
      double ax, ay;
      double cx, cy;
      Tcl_Obj *pObj;
      int j;
      struct OneSlice *pS;
      Tcl_ListObjIndex(0, objv[2], i, &pObj);
      if( Tcl_GetDoubleFromObj(interp, pObj, &cx) ) break;
      Tcl_ListObjIndex(0, objv[2], i+1, &pObj);
      if( Tcl_GetDoubleFromObj(interp, pObj, &cy) ) break;
      for(j=0; j<p->nSlice-1 && p->a[j].upperbound>cy; j++){}
      pS = &p->a[j];

      /* Original Formula 
      ** ax = cx*p->rZoom - pS->rXShift;
      ** ay = pS->mxY + (pS->mnY-pS->mxY)*(cy-pS->top)/(pS->btm-pS->top);
      */
      ax=xCanvasToActual(p,pS,cx);
      ay=yCanvasToActual(p,pS,cy);
      
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(pS->did));
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(round(ax)));
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(round(ay)));
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(round(aboveDeck)));
    }
    if( i<n-1 ){
      Tcl_DecrRefCount(pResult);
      return TCL_ERROR;
    }
    Tcl_SetObjResult(interp, pResult);
    break;
  }
 
  /*
  ** tclmethod:  SLICER canvascoords ACTUAL-COORD-LIST ?xvar? ?yvar?
  ** title:   Convert from actual (in DID X Y) to canvas coordinate space
  **
  ** The coordinates are considered to form a line.  If the actual
  ** coordinates make a transition from one slice to another, an extra
  ** set of canvas coordinates may be inserted to show the point of
  ** this transition.
  **
  */
  case SLICER_CANVASCOORDS: {
    int i, n;
    Tcl_Obj *pResult=NULL;
    double ax, ay;
    double cx, cy;
    Tcl_Obj *pObj;
    struct OneSlice *pS;
    int error=0;

    if( objc!=3 && objc!=5 ){
      Tcl_WrongNumArgs(interp, 2, objv, "ACTUAL-COORD-LIST ?xvar yvar?");
      return TCL_ERROR;
    }
    if( Tcl_ListObjLength(interp, objv[2], &n) ) return TCL_ERROR;
    if(n==3) {
      /* Goose a 3 length to a 4 length */
      n=4;
    }
    if(n<4 || n%4!=0) {
      Tcl_AppendResult(interp, "coordinate list must contain a multiple "
           "of 4 values", 0);
      return TCL_ERROR;
    }

    if( p->nSlice<=0 ){
      Tcl_AppendResult(interp, "no slices defined", 0);
      return TCL_ERROR;
    }
    if(objc != 5) {
      pResult = Tcl_NewObj();
    }

    for(i=0; i<n-3; i+=4){
      Tcl_ListObjIndex(0, objv[2], i+1, &pObj);
      if( Tcl_GetDoubleFromObj(interp, pObj, &ax) ) {
        error=1;
        break;
      }
      Tcl_ListObjIndex(0, objv[2], i+2, &pObj);
      if( Tcl_GetDoubleFromObj(interp, pObj, &ay) ) {
        error=1;
        break;
      }
      Tcl_ListObjIndex(0, objv[2], i, &pObj);
      if( getDeckId(interp, p, pObj, &pS) ) {
        error=1;
        break;
      }
      
      /* Old direct formula
      **
      ** cx = (ax+pS->rXShift)/p->rZoom;
      ** cy = pS->top + (pS->btm-pS->top)*(ay-pS->mxY)/(pS->mnY-pS->mxY);
      */
      cx = xActualToCanvas(p,pS,ax);       
      cy = yActualToCanvas(p,pS,ay);

      if(objc==5) {
        Tcl_ObjSetVar2(interp,objv[3],NULL,Tcl_NewDoubleObj(cx),0);
        Tcl_ObjSetVar2(interp,objv[4],NULL,Tcl_NewDoubleObj(cy),0);
        return TCL_OK;
      } else {    
        Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(cx));
        Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(cy));
      }
    }
    if(error) {
      if(pResult) {
        Tcl_DecrRefCount(pResult);
      }
      return TCL_ERROR;
    }
    if( i<n-3 ){
        Tcl_AppendResult(interp, "Did not reach the end", 0);
      Tcl_DecrRefCount(pResult);
      return TCL_ERROR;
    }

    Tcl_SetObjResult(interp, pResult);
    break;
  }

  /*
  ** tclmethod:  SLICER create DID NAME Z MIN-Y MAX-Y
  ** title:   Create a new slice
  */
  case SLICER_CREATE: {
    double z, mnY, mxY;
    const char *zNameOrig;
    char *zName;
    int did;
    int i, nName;
    if( objc!=7 ){
      Tcl_WrongNumArgs(interp, 2, objv, "DID NAME Z MIN-Y MAX-Y");
      return TCL_ERROR;
    }
    if( Tcl_GetIntFromObj(interp, objv[2], &did) ) return TCL_ERROR;
    zNameOrig = Tcl_GetStringFromObj(objv[3], &nName);
    if( Tcl_GetDoubleFromObj(interp, objv[4], &z) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[5], &mnY) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[6], &mxY) ) return TCL_ERROR;
    if( mnY>=mxY ){
      Tcl_AppendResult(interp, "MIN-Y must be less than MAX-Y", 0);
      return TCL_ERROR;
    }
    zName = Odie_Alloc( nName+1 );
    if( zName==0 ) return TCL_ERROR;
    memcpy(zName, zNameOrig, nName+1);
    for(i=0; i<p->nSlice; i++){
      if( p->a[i].did==did ){
        Tcl_AppendResult(interp, "Deckid for slice ", zName, " is the "
           "same as existing slice ", p->a[i].zName, 0);
        return TCL_ERROR;
      }
      if( p->a[i].z==z ){
        Tcl_AppendResult(interp, "Z coordinate for slice ", zName, " is the "
           "same as existing slice ", p->a[i].zName, 0);
        return TCL_ERROR;
      }
    }
    p->nSlice++;
    p->a = Odie_Realloc((char *)p->a, sizeof(p->a[0])*p->nSlice);
    if( p->a==0 ){
      p->nSlice = 0;
      return TCL_ERROR;
    }
    for(i=p->nSlice-1; i>0 && p->a[i-1].z>z; i--){
      p->a[i] = p->a[i-1];
      p->a[i].idx = i;
    }
    p->a[i].did = did;
    p->a[i].idx = i;
    p->a[i].zName = zName;
    p->a[i].nXZ = 0;
    p->a[i].xz = 0;
    p->a[i].z = z;
    p->a[i].mnY = mnY;
    p->a[i].mxY = mxY;
    p->a[i].rXShift = p->rXOffset*z;
    computeTopAndBottom(p);
    break;
  }

  /* tclmethod:  SLICER deck X Y Z
  ** title:   Return the name of the deck at actual coordinates X,Y,Z
  **
  ** See also: find
  */
  case SLICER_DECK: {
    double x0, y0, z0;
    Tcl_Obj *pResult;
    struct OneSlice *pS;
    if( objc!=5 ){
      Tcl_WrongNumArgs(interp, 2, objv, "X Y Z");
      return TCL_ERROR;
    }
    if( p->nSlice<=0 ){
      Tcl_AppendResult(interp, "no slices defined", 0);
      return TCL_ERROR;
    }
    if( Tcl_GetDoubleFromObj(interp, objv[2], &x0) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[3], &y0) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[4], &z0) ) return TCL_ERROR;
    pResult = Tcl_NewObj();
    pS = deckAt(p, x0, z0);
    pResult = ODIE_CONSTANT_STRING(pS->zName);
    Tcl_SetObjResult(interp, pResult);
    break;
  }


  /* tclmethod:  SLICER did X Y Z
  ** title:   Return the id of the deck at actual coordinates X,Y,Z
  **
  ** See also: find
  */
  case SLICER_DECKID: 
  case SLICER_DID: {
    double x0, y0, z0;
    Tcl_Obj *pResult;
    struct OneSlice *pS;
    if( objc!=5 ){
      Tcl_WrongNumArgs(interp, 2, objv, "X Y Z");
      return TCL_ERROR;
    }
    if( p->nSlice<=0 ){
      Tcl_AppendResult(interp, "no slices defined", 0);
      return TCL_ERROR;
    }
    if( Tcl_GetDoubleFromObj(interp, objv[2], &x0) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[3], &y0) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[4], &z0) ) return TCL_ERROR;
    pResult = Tcl_NewObj();
    pS = deckAt(p, x0, z0);
    pResult = Tcl_NewIntObj(pS->did);
    Tcl_SetObjResult(interp, pResult);
    break;
  }

  /*
  ** tclmethod:  SLICER delete NAME
  ** title:   Remove a slice from the slicer
  */
  case SLICER_DELETE: {
    int i, j;
    const char *zName;
    if( objc!=3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "NAME");
      return TCL_ERROR;
    }
    zName = Tcl_GetStringFromObj(objv[2], 0);
    for(i=0; i<p->nSlice && strcmp(p->a[i].zName,zName)!=0; i++){}
    if( i<p->nSlice ){
      Odie_Free((char *)p->a[i].zName);
      p->nSlice--;
      for(j=i; j<p->nSlice; j++){
        p->a[j] = p->a[j+1];
      }
      computeTopAndBottom(p);
    }
    break;
  }

  /*
  ** tclmethod:  SLICER destroy
  ** title:   Destroy this slicer
  */
  case SLICER_DESTROY: {
    Tcl_DeleteCommand(interp,Tcl_GetString(objv[0]));
    break;
  }

  case SLICER_DRAWLINE: {
    int result;
    result=slicer_method_drawline(pArg,interp,objc-1,objv+1);
    return result;
  }
  
  /*
  ** tclmethod:  SLICER objinfo obj
  ** title:   Return TK to draw a line on a canvas
  */
  case SLICER_OBJINFO: {
    Tcl_Obj *tmp;
    tmp = objv[2];
    printf("INFO: %s Ref: %d Type: %p \n", Tcl_GetStringFromObj(tmp, NULL),
        tmp->refCount, tmp->typePtr);
    fflush (stdout);
    break;
  }
  /*
  ** tclmethod:  SLICER makedlist CANVAS ACTUAL-PATH TAG TRANSDECK-TAG P-TAG ?COLOR?
  ** title:   Return TK to draw a line on a canvas
  */
  case SLICER_MAKEDLIST: {
    int i, n;
    double *aCoord;             /* Actual coordinates of line to draw */
    struct OneSlice **apDeck;   /* Array of all decks */
    Tcl_Obj *pVTag;             /* The "sNNN" tag added to all line segments */
    const char *zXTag;          /* Trans-deck tag (dashed lines) */
    Tcl_Obj *rtnList ;          /* List object to return */
    Tcl_Obj *coordList ;        /* List of coords to return */
    Tcl_Obj *configList ;       /* configuration string to return */
    Tcl_Obj *tmpObj;            /* Cheater for filling lists */

    if( objc!=5 && objc!=6 ){
      Tcl_WrongNumArgs(interp, 2, objv, 
          "ACTUAL-PATH TAG TRANSDECK-TAG ?COLOR?");
      return TCL_ERROR;
    }
// slicer makedlist  Path TagList Transdeck-TagList ?COLOR?
//  0     1          2    3       4                  5

    zXTag = Tcl_GetStringFromObj(objv[4], 0);

    if( Tcl_ListObjLength(interp, objv[2], &n) ) return TCL_ERROR;

    if( n%3!=0 || n<6 ){
      Tcl_AppendResult(interp, "coordinate list must contain a multiple "
         "of 3 values with a minimum of 6", 0);
      return TCL_ERROR;
    }

    rtnList = Tcl_NewListObj(0, NULL);

    aCoord = (double *)Odie_Alloc( sizeof(aCoord[0])*n + sizeof(apDeck[0])*(n/3) );
    if( aCoord==0 ){
      return TCL_ERROR;
    }
    
    // Move coords from Tcl objv[3] int "C" aCoord array

    apDeck = (struct OneSlice **)&aCoord[n];
    for(i=0; i<n; i++){
      Tcl_Obj *pObj;
      Tcl_ListObjIndex(0, objv[2], i, &pObj);
      if( Tcl_GetDoubleFromObj(interp, pObj, &aCoord[i]) ){
        Odie_Free((char *)aCoord);
        return TCL_ERROR;
      }
    }
    n /= 3;

    for(i=0; i<n; i++){
      double z = aCoord[i*3+2];
      double x = aCoord[i*3];
      apDeck[i] = deckAt(p, x, z);
    }

    for(i=1; i<n; i++){
      char zBuf[30];
      double x0, y0, x1, y1, z0, z1, Y0, Y1;

      coordList = Tcl_NewListObj(0, NULL);
      configList = Tcl_NewListObj(0, NULL);

      sprintf(zBuf, "s%d", i);
      pVTag = Tcl_NewStringObj(zBuf, -1);
      if( apDeck[i]!=apDeck[i-1] ) {

        /* Old direct formula
        **
        ** x0=(aCoord[i*3-3]+apDeck[i-1]->rXShift)/p->rZoom;
        ** x1 = (aCoord[i*3]+apDeck[i]->rXShift)/p->rZoom;
        */
        x0 = xActualToCanvas(p,apDeck[i-1],aCoord[i*3-3]);
        x1 = xActualToCanvas(p,apDeck[i],aCoord[i*3]);
        y0 = yActualToCanvas(p,apDeck[i-1], Y0=aCoord[i*3-2]);
        y1 = yActualToCanvas(p,apDeck[i], Y1=aCoord[i*3+1]);
        z0 = aCoord[i*3-1];
        z1 = aCoord[i*3+2];
        if( zXTag[0]!=0 ){
          Tcl_ListObjAppendElement(interp, coordList,Tcl_NewDoubleObj(x0));
          Tcl_ListObjAppendElement(interp, coordList,Tcl_NewDoubleObj(y0));
          Tcl_ListObjAppendElement(interp, coordList,Tcl_NewDoubleObj(x1));
          Tcl_ListObjAppendElement(interp, coordList,Tcl_NewDoubleObj(y1));

          Tcl_ListObjAppendElement(interp, configList,
	      ODIE_CONSTANT_STRING("-tags"));

          tmpObj = Tcl_DuplicateObj(objv[4]);
          Tcl_ListObjAppendElement(interp, tmpObj, pVTag);
          Tcl_ListObjAppendElement(interp, configList, tmpObj);

        }
      } else {
        /* Old direct formula
        **
        ** x0 = (aCoord[i*3-3]+apDeck[i]->rXShift)/p->rZoom;
        ** x1 = (aCoord[i*3]+apDeck[i]->rXShift)/p->rZoom;
        */
        x0 = xActualToCanvas(p,apDeck[i],aCoord[i*3-3]);
        x1 = xActualToCanvas(p,apDeck[i],aCoord[i*3]);
        
        y0 = yActualToCanvas(p,apDeck[i], aCoord[i*3-2]);
        y1 = yActualToCanvas(p,apDeck[i], aCoord[i*3+1]);

        Tcl_ListObjAppendElement(interp, coordList,Tcl_NewDoubleObj(x0));
        Tcl_ListObjAppendElement(interp, coordList,Tcl_NewDoubleObj(y0));
        Tcl_ListObjAppendElement(interp, coordList,Tcl_NewDoubleObj(x1));
        Tcl_ListObjAppendElement(interp, coordList,Tcl_NewDoubleObj(y1));


          Tcl_ListObjAppendElement(interp, configList,\
	      ODIE_CONSTANT_STRING("-tags"));

          tmpObj = Tcl_DuplicateObj(objv[3]);
          Tcl_ListObjAppendElement(interp, tmpObj, pVTag);

          Tcl_ListObjAppendElement(interp, configList, tmpObj);

      }
      if( objc>=6 ) {
         Tcl_ListObjAppendElement(interp, configList,\
           ODIE_CONSTANT_STRING("-fill"));
         Tcl_ListObjAppendElement(interp, configList,\
	   Tcl_DuplicateObj(objv[5]));
      }


    Tcl_ListObjAppendElement(interp, rtnList, coordList);
    Tcl_ListObjAppendElement(interp, rtnList, configList);
    }
    Odie_Free((char *)aCoord);

    Tcl_SetObjResult(interp, rtnList);

    break;
  }

  /* tclmethod:  SLICER find X Y
  ** title:   Return the name of the deck at canvas coordinates X,Y
  **
  ** The "deck" command works similarly except that it uses actual
  ** coordinates as inputs.
  */
  case SLICER_FIND: {
    int i;
    double x0, y0;
    Tcl_Obj *pResult;
    struct OneSlice *pS;
    if( objc!=4 ){
      Tcl_WrongNumArgs(interp, 2, objv, "X Y");
      return TCL_ERROR;
    }
    if( p->nSlice<=0 ){
      Tcl_AppendResult(interp, "no slices defined", 0);
      return TCL_ERROR;
    }
    if( Tcl_GetDoubleFromObj(interp, objv[2], &x0) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[3], &y0) ) return TCL_ERROR;
    pResult = Tcl_NewObj();
    for(i=0; i<p->nSlice-1 && p->a[i].upperbound>y0; i++){}
    pS = &p->a[i];
    pResult = ODIE_CONSTANT_STRING(pS->zName);
    Tcl_SetObjResult(interp, pResult);
    break;
  }

 /* tclmethod:  SLICER finddid X Y
  ** title:   Return the name of the deck at canvas coordinates X,Y
  **
  ** The "deck" command works similarly except that it uses actual
  ** coordinates as inputs.
  */
  case SLICER_FINDDID: {
    int i;
    double x0, y0;
    Tcl_Obj *pResult;
    struct OneSlice *pS;
    if( objc!=4 ){
      Tcl_WrongNumArgs(interp, 2, objv, "X Y");
      return TCL_ERROR;
    }
    if( p->nSlice<=0 ){
      Tcl_AppendResult(interp, "no slices defined", 0);
      return TCL_ERROR;
    }
    if( Tcl_GetDoubleFromObj(interp, objv[2], &x0) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[3], &y0) ) return TCL_ERROR;
    pResult = Tcl_NewObj();
    for(i=0; i<p->nSlice-1 && p->a[i].upperbound>y0; i++){}
    pS = &p->a[i];
    pResult = Tcl_NewIntObj(pS->did);
    Tcl_SetObjResult(interp, pResult);
    break;
  }

  /* tclmethod:  SLICER height NAME X
  ** title:   Return the height of slice NAME at actual position X
  **            Assuming the deck were flat
  **
  ** See also "headroom".
  */
  case  SLICER_FLATHEIGHT: {
    struct OneSlice *ppS;
    double x0;
    
    if( objc!=4 ){
      Tcl_WrongNumArgs(interp, 2, objv, "NAME X");
      return TCL_ERROR;
    }
    if( Tcl_GetDoubleFromObj(interp, objv[3], &x0) ) return TCL_ERROR;
    if( getDeckId(interp,p,objv[2],&ppS) ) return TCL_ERROR;

    Tcl_SetObjResult(interp, Tcl_NewDoubleObj(ppS->z));
    break;
  }

  /* tclmethod:  SLICER headroom NAME X
  ** title:   Return the headroom (Z from deck) of slice NAME at actual position X
  **
  ** See also "height"
  */
  case SLICER_HEADROOM: {
    double x0;
    struct OneSlice *pDeck, *pAbove;
    if( objc!=4 ){
      Tcl_WrongNumArgs(interp, 2, objv, "NAME X");
      return TCL_ERROR;
    }
    if( Tcl_GetDoubleFromObj(interp, objv[3], &x0) ) return TCL_ERROR;

    if( getDeckId(interp,p,objv[2],&pDeck) ) return TCL_ERROR;
    pAbove = deckAbove(p, pDeck);
    if( pAbove ){
      Tcl_SetObjResult(interp,Tcl_NewDoubleObj(deckHeight(pAbove,x0)-deckHeight(pDeck,x0)));
    }else{
      Tcl_SetObjResult(interp,Tcl_NewDoubleObj(p->upper_height));
    }
    break;
  }

  /* tclmethod:  SLICER ceiling NAME X ?DEFAULT?
  ** title:   Return the Z (absolute) of the ceiling of slice NAME at actual position X
  **
  ** See also "height"
  */
  case SLICER_CEILING: {
    double x0;
    struct OneSlice *pDeck, *pAbove;
    if( objc!=4 ){
      Tcl_WrongNumArgs(interp, 2, objv, "NAME X");
      return TCL_ERROR;
    }
    if( Tcl_GetDoubleFromObj(interp, objv[3], &x0) ) return TCL_ERROR;

    if( getDeckId(interp,p,objv[2],&pDeck) ) return TCL_ERROR;
    pAbove = deckAbove(p, pDeck);
    if( pAbove ){
      Tcl_SetObjResult(interp,Tcl_NewDoubleObj(deckHeight(pAbove,x0)));
    }else{
      Tcl_SetObjResult(interp,Tcl_NewDoubleObj(deckHeight(pDeck,x0)+p->upper_height));
    }
    break;
  }
  
  /* tclmethod:  SLICER height NAME X ?zoff?
  ** title:   Return the height (absolute) of slice NAME at actual position X
  ** description:
  **   NOTE if the top deck is asked for, and zoff is negative the
  **   system will assume a 2000mm ceiling
  ** See also "headroom".
  */
  case  SLICER_HEIGHT: {
    struct OneSlice *ppS,*pAbove;
    double x0,zoff=0,zresult=0.0;
    if( objc!=4 && objc!=5 ){
      Tcl_WrongNumArgs(interp, 2, objv, "NAME X ?ZOFF?");
      return TCL_ERROR;
    }
    if( Tcl_GetDoubleFromObj(interp, objv[3], &x0) ) return TCL_ERROR;
    if( getDeckId(interp,p,objv[2],&ppS) ) return TCL_ERROR;
    if( objc==5 ) {
      if( Tcl_GetDoubleFromObj(NULL, objv[4], &zoff) ) {
        zoff=0.0;
      }
    }
    if(zoff < 0.0) {
      pAbove = deckAbove(p, ppS);
      if(pAbove) {
        zresult=deckHeight(pAbove,x0)+zoff;
      } else {
        zresult=deckHeight(ppS,x0)+2000+zoff;
      }
    } else {
      zresult=deckHeight(ppS,x0)+zoff;
    }
    Tcl_SetObjResult(interp, Tcl_NewDoubleObj(zresult));
    break;
  }

  /* tclmethod:  SLICER deckheight NAME X ?zoff?
  ** title:   Return the height (mm from floor) of slice NAME at actual position X
  ** description:
  **   NOTE if the top deck is asked for, and zoff is negative the
  **   system will assume a 2000mm ceiling
  ** See also "headroom".
  */
  case  SLICER_DECKHEIGHT: {
    int i;
    double x0, z0, dheight;
    Tcl_Obj *pResult;
    struct OneSlice *pS,*pAbove;
    if( objc!= 4 && objc!=5 ){
      Tcl_WrongNumArgs(interp, 2, objv, "DECK X ?ZOFF?");
      return TCL_ERROR;
    }
    if( p->nSlice<=0 ){
      Tcl_AppendResult(interp, "no slices defined", 0);
      return TCL_ERROR;
    }
    if( getDeckId(interp,p,objv[2],&pS) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[3], &x0) ) return TCL_ERROR;
    if(objc==5) {
      if( Tcl_GetDoubleFromObj(NULL, objv[4], &dheight) ) {
        dheight=0.0;
      }
    } else {
      dheight=0.0;
    }
    z0=Location_zdeck(p,pS,x0,dheight);
    Tcl_SetObjResult(interp, Tcl_NewDoubleObj(z0));
    return TCL_OK;
  }

  /*
  ** tclmethod:  SLICER info NAME
  ** title:   Return information about a particular slice
  */
  case SLICER_INFO: {
    struct OneSlice *ppS;
    
    if( objc!=3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "NAME");
      return TCL_ERROR;
    }
    if( getDeckId(interp,p,objv[2],&ppS) ) {
      return TCL_ERROR;
    } else {
      Tcl_Obj *pResult = Tcl_NewObj();
      
      Tcl_ListObjAppendElement(interp, pResult, ODIE_CONSTANT_STRING("name"));
      Tcl_ListObjAppendElement(interp, pResult, ODIE_CONSTANT_STRING(ppS->zName));  
 
      Tcl_ListObjAppendElement(interp, pResult, ODIE_CONSTANT_STRING("did"));
      Tcl_ListObjAppendElement(interp, pResult, Tcl_NewIntObj(ppS->did));  
 
    
      Tcl_ListObjAppendElement(interp, pResult, ODIE_CONSTANT_STRING("z"));
      Tcl_ListObjAppendElement(interp, pResult, Tcl_NewDoubleObj(ppS->z));

      Tcl_ListObjAppendElement(interp, pResult, ODIE_CONSTANT_STRING("miny"));
      Tcl_ListObjAppendElement(interp, pResult, Tcl_NewDoubleObj(ppS->mnY));
      
      Tcl_ListObjAppendElement(interp, pResult, ODIE_CONSTANT_STRING("maxy"));
      Tcl_ListObjAppendElement(interp, pResult, Tcl_NewDoubleObj(ppS->mxY));
      
      Tcl_ListObjAppendElement(interp, pResult, ODIE_CONSTANT_STRING("top"));
      Tcl_ListObjAppendElement(interp, pResult, Tcl_NewDoubleObj(ppS->top));
      
      Tcl_ListObjAppendElement(interp, pResult, ODIE_CONSTANT_STRING("bottom"));
      Tcl_ListObjAppendElement(interp, pResult, Tcl_NewDoubleObj(ppS->btm));

      Tcl_ListObjAppendElement(interp, pResult, ODIE_CONSTANT_STRING("above"));
      Tcl_ListObjAppendElement(interp, pResult, Tcl_NewDoubleObj(ppS->above));

      Tcl_ListObjAppendElement(interp, pResult, ODIE_CONSTANT_STRING("below"));
      Tcl_ListObjAppendElement(interp, pResult, Tcl_NewDoubleObj(ppS->below));
      
      Tcl_SetObjResult(interp, pResult);
    }
    break;
  }

  /* 
  ** tclmethod:  SLICER list
  ** title:   List the names of all defined slices in the slicer
  */
  case SLICER_LIST: {
    Tcl_Obj *pResult = Tcl_NewObj();
    int i;
    for(i=0; i<p->nSlice; i++){
      Tcl_ListObjAppendElement(0, pResult, ODIE_CONSTANT_STRING(p->a[i].zName));
    }
    Tcl_SetObjResult(interp, pResult);
    break;
  }


  /*
  ** tclmethod:  SLICER profile DECK ?X Z ...?
  ** title:   Create an inboard profile for a deck
  */
  case SLICER_PROFILE: {
    int i, j, min;
    struct OneSlice *pS;
    if( objc<3 || (objc>3 && objc<7) || (objc & 1)==0 ){
      Tcl_WrongNumArgs(interp, 2, objv, "NAME ?X Z X Z...?");
      return TCL_ERROR;
    }
    
    if(getDeckId(interp,p,objv[2],&pS)) {
      return TCL_ERROR;
    }
    if( objc==3 ){
      Tcl_Obj *pResult = Tcl_NewObj();
      for(i=0; i<pS->nXZ; i++){
        Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(pS->xz[i]));
      }
      Tcl_SetObjResult(interp, pResult);
    }else{
      double *xz;
      if( pS->xz ) Odie_Free((char *)pS->xz);
      pS->nXZ = objc - 3;
      xz = pS->xz = (double *)Odie_Alloc( sizeof(pS->xz[0])*pS->nXZ );
      if( xz==0 ){
        pS->nXZ = 0;
        break;
      }
      for(i=0; i<pS->nXZ; i++){
        if( Tcl_GetDoubleFromObj(interp, objv[i+3], &xz[i]) ){
          Odie_Free((char *)xz);
          pS->nXZ = 0;
          pS->xz = 0;
          return TCL_ERROR;
        }
      }

      /* Put the profile in increasing X order.  An N**2 sort is used because
      ** it is convenient and the list will usually be short. */
      for(i=0; i<pS->nXZ-2; i+=2){
        for(min=i, j=i+2; j<pS->nXZ; j+=2){
          if( xz[j]<xz[min] ) min = j;
        }
        if( min>i ){
          double t = xz[min];
          xz[min] = xz[i];
          xz[i] = t;
          t = xz[min+1];
          xz[min+1] = xz[i+1];
          xz[i+1] = t;
        }
      }

      /* Remove duplidate X coordinates */
      for(i=j=0; i<pS->nXZ; i+=2){
        if( i<pS->nXZ-2 && xz[i+2]==xz[i] ){
          /* Ignore the duplicate */
        }else{
          xz[j++] = xz[i];
          xz[j++] = xz[i+1];
        }
      }
      pS->nXZ = j;
      if( j<4 ){
        pS->nXZ = 0;
        Odie_Free((char *)pS->xz);
        pS->xz = 0;
      }
    }        
    break;
  }

  /*
  ** tclmethod:  SLICER xoffset ?AMT?
  ** title:   Change the X-Offset as a function of deck height
  */
  case SLICER_XOFFSET: {
    double rXOffset;
    int i;
    if( objc!=2 && objc!=3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "?ZOOM?");
      return TCL_ERROR;
    }
    if( objc==2 ){
      Tcl_SetObjResult(interp, Tcl_NewDoubleObj(p->rXOffset));
      return TCL_OK;
    }
    if( Tcl_GetDoubleFromObj(interp, objv[2], &rXOffset) ) return TCL_ERROR;
    for(i=0; i<p->nSlice; i++){
      p->a[i].rXShift = rXOffset*p->a[i].z;
    }
    p->rXOffset = rXOffset;
    break;
  }

  /*
  ** tclmethod:  SLICER zoom ?ZOOM?
  ** title:   Query or change the zoom factor.
  */
  case SLICER_ZOOM: {
    if( objc!=2 && objc!=3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "?ZOOM?");
      return TCL_ERROR;
    }
    if( objc==3 ){
      double r;
      if( Tcl_GetDoubleFromObj(interp, objv[2], &r) ) return TCL_ERROR;
      p->rZoom = r;
    }
    Tcl_SetObjResult(interp, Tcl_NewDoubleObj(p->rZoom));
    computeTopAndBottom(p);
    break;
  }

  /*
  ** tclmethod:  SLICER upper_height MM
  ** title:   Set the headroom on the top level of the slicer (defaults to 2000)
  */
  case SLICER_UPPER_HEIGHT: {
    if( objc != 2 && objc!=3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "Z");
      return TCL_ERROR;
    }
    if( objc==3 ){
      double r;
      if( Tcl_GetDoubleFromObj(interp, objv[2], &r) ) return TCL_ERROR;
      p->upper_height=r;
    }
    Tcl_SetObjResult(interp, Tcl_NewDoubleObj(p->upper_height));
    break;
  }

  /* End of the command methods.  The brackets that follow terminate the
  ** automatically generated switch.
  ****************************************************************************/
  }
  }
  return TCL_OK;
}

/*
** tclcmd: slicer SLICER
** title: creates a slicer object
** This routine runs when the "slicer" command is invoked to create a
** new slicer.
*/
int Odie_SlicerCreateProc(
  void *NotUsed,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  char *zCmd;
  Slicer *p;
  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "SLICER");
    return TCL_ERROR;
  }
  zCmd = Tcl_GetStringFromObj(objv[1], 0);
  p = (Slicer *)Odie_Alloc( sizeof(*p) );
  p->rZoom = 100;
  p->upper_height=2000;
  Tcl_CreateObjCommand(interp, zCmd, slicerMethodProc, p, destroySlicer);
  return TCL_OK;
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted cmodules/geometry/generic/slicer_cases.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/*** Automatically Generated Header File - Do Not Edit ***/
  const static char *SLICER_strs[] = {
    "above",              "actualcoords",      "below",
    "canvascoords",       "ceiling",           "create",
    "deck",               "deckheight",        "deckid",
    "deckid_to_name",     "deckname_to_id",    "delete",
    "destroy",            "did",               "drawline",
    "find",               "finddid",           "flatheight",
    "headroom",           "height",            "info",
    "list",               "location_to_xyz",   "location_z",
    "makedlist",          "objinfo",           "profile",
    "upper_height",       "xoffset",           "xyz_to_location",
    "zoom",               0                    
  };
  enum SLICER_enum {
    SLICER_ABOVE,         SLICER_ACTUALCOORDS, SLICER_BELOW,
    SLICER_CANVASCOORDS,  SLICER_CEILING,      SLICER_CREATE,
    SLICER_DECK,          SLICER_DECKHEIGHT,   SLICER_DECKID,
    SLICER_DECKID_TO_NAME, SLICER_DECKNAME_TO_ID,SLICER_DELETE,
    SLICER_DESTROY,       SLICER_DID,          SLICER_DRAWLINE,
    SLICER_FIND,          SLICER_FINDDID,      SLICER_FLATHEIGHT,
    SLICER_HEADROOM,      SLICER_HEIGHT,       SLICER_INFO,
    SLICER_LIST,          SLICER_LOCATION_TO_XYZ,SLICER_LOCATION_Z,
    SLICER_MAKEDLIST,     SLICER_OBJINFO,      SLICER_PROFILE,
    SLICER_UPPER_HEIGHT,  SLICER_XOFFSET,      SLICER_XYZ_TO_LOCATION,
    SLICER_ZOOM,         
  };
 int index;
  if( objc<2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "METHOD ?ARG ...?");
    return TCL_ERROR;
  }
  if( Tcl_GetIndexFromObj(interp, objv[1], SLICER_strs, "option", 0, &index)){
    return TCL_ERROR;
  }
  switch( (enum SLICER_enum)index )
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








































































Deleted cmodules/geometry/generic/wallset.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
/*
** This file implements a TCL object that keeps track of the walls and
** bulkheads on a single deck of a ship.
**
** This widget assumes a right-handed coordinate system if zoom is positive
** and a left-handed coordinate system is zoom is negative.  The Tk canvas
** widget uses a left-handed coordinate system all the time.  The READI
** database uses a right-handed coordinate system all the time.  This module
** can be used to translate by setting zoom to +1.0 for database I/O and
** to -$g(zoom) for canvas I/O.
**
** This module uses a purely 2-D model.  It can only handle a single
** deck at a time.  If a multi-deck model needs to be displayed then
** that multi-deck model should first be flattened into a stack of
** individual decks in the same plane using the separate "slicer" object.
**
** This file implements a single new constructor tcl command named "wallset".
** The wallset command creates a new wallset object.  Methods on this
** wallset object are used to manage the object.
**
** The details of the various methods and what they do are provided in
** header comments above the implementation of each method.
*/
const char wallset_c_version[] = "$Header: /readi/code/tobe/wallset.c,v 1.34 2007/02/22 15:04:04 drh Exp $";
#include "odieInt.h"
#include <stdarg.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <math.h>

#ifndef M_PI
# define M_PI 3.1415926535898
#endif

/*
** Remove all of the ComptBox entries from the wallset.
*/
static void clearComptBoxCache(Wallset *pWS){
  ComptBox *p = pWS->pComptBox;
  while( p ){
    ComptBox *pNext = p->pNext;
    Odie_Free((char *)p);
    p = pNext;
  }
  pWS->pComptBox = 0;
}

/*
** This routine is invoked when the TCL command that implements a
** wallset is deleted.  Free all memory associated with that
** wallset.
*/
static void destroyWallset(void *pArg){
  Wallset *p = (Wallset*)pArg;
  Link *pLink = p->pAll;
  clearComptBoxCache(p);
  while( pLink ){
    Segment *pSeg = pLink->pLinkNode;
    pLink = pSeg->pAll.pNext;
    Odie_Free((char *) pSeg );
  }
  Odie_Free((char *) p );
}

/*
** Clear the Segment.ignore flag on all segments within a wallset.
*/
static void ignoreNone(Wallset *p){
#if 0  
  Link *pLink;
  for(pLink=p->pAll; pLink; pLink=pLink->pNext){
    pLink->pSeg->ignore = 0;
  }
#endif
}

/*
** Return a pointer to the segment with the given ID.  Return NULL
** if there is no such segment.
*/
static Segment *findSegment(Wallset *p, int id){
  int h;
  Link *pLink;

  h = hashInt(id);
  for(pLink = p->hashId[h]; pLink; pLink=pLink->pNext){
    Segment *pSeg=pLink->pLinkNode;
    if( pSeg->id==id ) return pSeg;
  }
  return 0;
}

#if 0 /* NOT USED */
/*
** Make a copy of a string.
*/
static char *stringDup(const char *z){
  int len = strlen(z);
  char *zNew = Odie_Alloc( len+1 );
  if( zNew ){
    memcpy(zNew, z, len+1);
  }
  return zNew;
}
#endif

/*
** Scan all segments looking for the vertex or vertices that are nearest
** to x,y.  Return a pointer to a Segment.set that is the list of matching
** segments.  Also write the nearest point into *pX,*pY.
**
** The returned list uses the Segment.set link.
*/
static Link *nearestVertex(
  Wallset *p,               /* The wallset to be scanned */
  double x, double y,       /* Search for points near to this point */
  double *pX, double *pY    /* Write nearest vertex here */
){
  double nx, ny;
  double min = -1.0;
  Link *pList = 0;
  Link *pI;

  x = roundCoord(x);
  y = roundCoord(y);
  for(pI=p->pAll; pI; pI=pI->pNext){
    double dx, dy, dist;
    Segment *pSeg = pI->pLinkNode;
    dx = x - pSeg->from[X_IDX];
    dy = y - pSeg->from[Y_IDX];
    dist = dx*dx + dy*dy;
    if( min<0.0 || dist<=min ){
      if( min<0.0 || nx!=pSeg->from[X_IDX] || ny!=pSeg->from[Y_IDX] ){
        pList = 0;
        nx = pSeg->from[X_IDX];
        ny = pSeg->from[Y_IDX];
        min = dist;
      }
      LinkInit(pSeg->pSet, pSeg);
      LinkInsert(&pList, &pSeg->pSet);
    }
    dx = x - pSeg->to[X_IDX];
    dy = y - pSeg->to[Y_IDX];
    dist = dx*dx + dy*dy;
    if( dist<=min ){
     if( nx!=pSeg->to[X_IDX] || ny!=pSeg->to[Y_IDX] ){
        pList = 0;
        nx = pSeg->to[X_IDX];
        ny = pSeg->to[Y_IDX];
        min = dist;
      }
      LinkInit(pSeg->pSet, pSeg);
      LinkInsert(&pList, &pSeg->pSet);
    }
  }
  *pX = nx;
  *pY = ny;
  return pList;
}

/*
** Scan all segments looking for the point on a segment that is nearest
** to x,y.  Return a pointer to a Segment.set that is the list of matching
** segments.  This set might contain multiple members if the nearest point
** is actually a vertex shared by two or more segments.  Write the nearest
** point into *pX, *pY.
**
** /// Ignore any segment that has its Segment.ignore flag set. -- removed
**
** The returned list uses the Segment.set list.
*/
static Link *nearestPoint(
  Wallset *p,               /* The wallset to be scanned */
  double x, double y,       /* Search for points near to this point */
  double *pX, double *pY    /* Write nearest vertex here */
){
  double nx, ny;
  double min = -1.0;
  Link *pList = 0;
  Link *pI;

  x = roundCoord(x);
  y = roundCoord(y);
  for(pI=p->pAll; pI; pI=pI->pNext){
    double dx, dy, dist;
    Segment *pSeg;
    double acx, acy;   /* Vector from x0,y0 to x,y */
    double abx, aby;   /* Vector from x0,y0 to x1,y1 */
    double rx, ry;     /* Nearest point on x0,y0->to[X_IDX],y1 to x,y */
    double r;

    pSeg = pI->pLinkNode;
    /* if( pSeg->ignore ) continue; */
    acx = x - pSeg->from[X_IDX];
    acy = y - pSeg->from[Y_IDX];
    abx = pSeg->to[X_IDX] - pSeg->from[X_IDX];
    aby = pSeg->to[Y_IDX] - pSeg->from[Y_IDX];
    r = (acx*abx + acy*aby)/(abx*abx + aby*aby);
    if( r<=0 ){
      rx = pSeg->from[X_IDX];
      ry = pSeg->from[Y_IDX];
    }else if( r>=1 ){
      rx = pSeg->to[X_IDX];
      ry = pSeg->to[Y_IDX];
    }else{
      rx = pSeg->from[X_IDX] + abx*r;
      ry = pSeg->from[Y_IDX] + aby*r;
    }
    rx = roundCoord(rx);
    ry = roundCoord(ry);
    dx = x - rx;
    dy = y - ry;
    dist = dx*dx + dy*dy;
    if( min<0.0 || dist<=min ){
      if( min<0.0 || nx!=rx || ny!=ry ){
        pList = 0;
        nx = rx;
        ny = ry;
        min = dist;
      }
      LinkInit(pSeg->pSet, pSeg);
      LinkInsert(&pList, &pSeg->pSet);
    }
  }
  *pX = nx;
  *pY = ny;
  return pList;
}

/*
** Return TRUE if the value x is in between x1 and x2.
*/
static int between(double x, double x1, double x2){
  if( x1<x2 ){
    return x>=x1 && x<=x2;
  }else{
    return x>=x2 && x<=x1;
  }
}

/*
** Return TRUE if the given segment is on the given list
*/
static int segmentOnList(Segment *pSeg, Link *pList){
  while( pList ){
    if( pList->pLinkNode==pSeg ) return 1;
    pList = pList->pNext;
  }
  return 0;
}

/*
** Return a list of all segments which have an end at the given vertex.
** The returned list uses Segment.set
*/
static Link *segmentsAtVertex(Wallset *p, double x, double y){
  Link *pList = 0;
  Link *pI;
  int h;

  x = roundCoord(x);
  y = roundCoord(y);
  h = hashCoord(x, y);
  for(pI=p->hashFrom[h]; pI; pI=pI->pNext){
    Segment *pSeg = pI->pLinkNode;
    /* if( pSeg->ignore ) continue; */
    if( floatCompare(x, pSeg->from[X_IDX])==0 && floatCompare(y, pSeg->from[Y_IDX])==0 ){
      assert( !segmentOnList(pSeg, pList) );
      LinkInit(pSeg->pSet, pSeg);
      LinkInsert(&pList, &pSeg->pSet);
    }
  }
  for(pI=p->hashTo[h]; pI; pI=pI->pNext){
    Segment *pSeg = pI->pLinkNode;
    /* if( pSeg->ignore ) continue; */
    if( floatCompare(x, pSeg->to[X_IDX])==0 && floatCompare(y, pSeg->to[Y_IDX])==0 ){
      assert( !segmentOnList(pSeg, pList) );
      LinkInit(pSeg->pSet, pSeg);
      LinkInsert(&pList, &pSeg->pSet);
    }
  }
  return pList;
}

/*
** The point xV,yV is a vertex in the wallset.  This routine locates
** a segment connected to that vertex which is the first segment in
** a clockwise direction from xR,yR->xV,yV.  A pointer to the segment
** is written into *ppSeg.  If the output segment moves backwards
** (in other words if x1,y1 of the segment is connected at xV,yV)
** then *pfBack is true.
**
** If a suitable segment is found, 0 is returned.  Non-zero is returned
** if no suitable segment could be found.
**
** This routine uses the Segment.set list internally.
*/
static int nextCwSegment(
  Wallset *p,              /* The wallset */
  double xR, double yR,    /* Remote end of input segment */
  double xV, double yV,    /* Vertex (near end of input segment) */
  Segment **ppSeg,         /* OUT: First segment clockwise from xR,yR->xV,yV */
  int *pfBack              /* OUT: True if output segment goes backwards */
){
  Link *pList, *pI;
  double rRef, rBest;
  int i, nSeg, iBest;
  Segment *pSeg;
  struct {
    Segment *pSeg;
    int isBack;
    double rAngle;
  } *aSeg, aSegStatic[20];

  /* Find all segments at xV,yV */
  pList = segmentsAtVertex(p, xV, yV);
  for(pI=pList, nSeg=0; pI; nSeg++, pI=pI->pNext){}
  if( nSeg==0 ) return 1;
  if( nSeg<=sizeof(aSegStatic)/sizeof(aSegStatic[0]) ){
    aSeg = aSegStatic;
  }else{
    aSeg = (void *)Odie_Alloc( nSeg*sizeof(*aSeg) );
  }
  for(pI=pList, i=0; pI; i++, pI=pI->pNext){ 
    aSeg[i].pSeg = pSeg = pI->pLinkNode;
    aSeg[i].isBack = floatCompare(xV, pSeg->to[X_IDX])==0 
                          && floatCompare(yV, pSeg->to[Y_IDX])==0;
  }
  
  /* Find the reference angle */
  rRef = atan2(yR-yV, xR-xV)*180.0/M_PI;

  /* Find angles on all segments */
  for(i=0; i<nSeg; i++){
    pSeg = aSeg[i].pSeg;
    if( aSeg[i].isBack ){
      aSeg[i].rAngle = atan2(pSeg->from[Y_IDX]-pSeg->to[Y_IDX], pSeg->from[X_IDX]-pSeg->to[X_IDX])*180.0/M_PI;
    }else{
      aSeg[i].rAngle = atan2(pSeg->to[Y_IDX]-pSeg->from[Y_IDX], pSeg->to[X_IDX]-pSeg->from[X_IDX])*180.0/M_PI;
    }
  }

  /* Subtract 360 to any segment angle that is less than the reference angle */
  for(i=0; i<nSeg; i++){
    if( aSeg[i].rAngle<rRef ) aSeg[i].rAngle += 360;
  }

  /* Choose the segment with the largest angle */
  rBest = aSeg[0].rAngle;
  iBest = 0;
  for(i=1; i<nSeg; i++){
    if( aSeg[i].rAngle>rBest ){
      iBest = i;
      rBest = aSeg[i].rAngle;
    }
  }
  *ppSeg = aSeg[iBest].pSeg;
  *pfBack = aSeg[iBest].isBack;
  if( aSeg!=aSegStatic ){
    Odie_Free((char *) aSeg );
  }

  return 0;
}

/*
** Consider a line beginning at x0,y0 then going from x1,y1 to x2,y2.
** x1,y1 is an elbow in the line.  This routine returns -1 if the
** elbow bends to the right, and +1 if it bends to the left.  zero is
** returned if the elbow does not bend at all.
*/
static int bendDirection(
  double x0, double y0,
  double x1, double y1,
  double x2, double y2
){
  /* Algorithm:  Rotate x0,y0->to[X_IDX],y1 90 degrees counter-clockwise.  Take
  ** the dot product with x1,y1->x2,y2.  The dot produce will be the product
  ** of two (non-negative) magnitudes and the cosine of the angle.  So if
  ** the dot product is positive, the bend is to the left, or to the right if
  ** the dot product is negative.
  */
  double r = (y0-y1)*(x2-x1) + (x1-x0)*(y2-y1);
  return r<0.0 ? +1 : (r>0.0 ? -1 : 0);
}

/*
** Given an interior point xI,yI, this routine finds a segment on the
** boundary that contains the interior point.  That segment is returned
** in *ppSeg.  *pfLeft is set to true if the interior point is to the left
** of the segment and false if it is to the right.
**
** Zero is returned on success.  Non-zero is returned if no suitable
** boundary could be located.  Non-zero might be returned, for example,
** if xI,yI is positioned directly on top of a wall or if there are no
** walls in the wallset.
**
** // Any segment marked with Segment.ignore is ignored for purposes of
** // this routine.  -- removed
**
** This routine uses the Segment.set list internally.
*/
static int firstBoundarySegment(
  Wallset *p,              /* The wallset */
  double xI, double yI,    /* An interior point */
  Segment **ppSeg,         /* OUT: A segment on the boundary containing xI,yI */
  int *pfLeft              /* OUT: True if xI,yI is to the left side *ppSeg */
){
  Link *pList;
  double xN, yN;

  /* Find nearest point, xN,yN */
  pList = nearestPoint(p, xI, yI, &xN, &yN);
  if( pList==0 ) return 1;
  if( pList->pNext ){
    /* xN,yN is a vertex...
    ** Locate the first segment clockwise from xI,yI->xN,yN and return
    */
    return nextCwSegment(p, xI, yI, xN, yN, ppSeg, pfLeft);
  }else{
    /* xN,yN is a point on single line segment...
    */
    Segment *pSeg;
    pSeg = *ppSeg = pList->pLinkNode;
    *pfLeft = bendDirection(pSeg->from[X_IDX], pSeg->from[Y_IDX], xN, yN, xI, yI)>0;
  }
  return 0;
}

/*
** Fill the given Boundary array with a list of segments (with
** Segment.ignore set to false) that form a closed circuit.  The
** first entry in aBound[] has already been filled in by the
** calling function and is used to seed the search.
**
** At most nBound slots in aBound[] will be used.  The return value
** is the number of slots in aBound[] that would have been used if those
** slots had been available.  A return of 0 indicates that no boundary
** is available.
**
** If the checkIsPrimary flag is true and the aBound[0] entry is not
** the primary segment for the compartment, then the aBound[] is not
** completely filled in and the routine returns 0;
*/
static int completeBoundary(
  Wallset *p,             /* The wallset */
  int checkIsPrimary,     /* Abort if aBound[0] is not the primary segment */
  int nBound,             /* Number of slots available in aBound[] */
  Boundary *aBound        /* IN-OUT: Write results into aBound[1...] */
){
  int cnt = 1;
  Segment *pSeg, *pS;
  int isLeft;
  int isBack;
  double xR, yR, xV, yV;

  pS = pSeg = aBound[0].pSeg;
  isLeft = aBound[0].backwards;
  if( !isLeft ){
    xR = pSeg->from[X_IDX];
    yR = pSeg->from[Y_IDX];
    xV = pSeg->to[X_IDX];
    yV = pSeg->to[Y_IDX];
  }else{
    xV = pSeg->from[X_IDX];
    yV = pSeg->from[Y_IDX];
    xR = pSeg->to[X_IDX];
    yR = pSeg->to[Y_IDX];
  }
  while( nextCwSegment(p,xR,yR,xV,yV,&pS,&isBack)==0 &&
              (isBack!=isLeft || pS!=pSeg) ){
    if( checkIsPrimary ){
      if( pS->id<pSeg->id ) return 0;
      if( pS->id==pSeg->id && !isLeft ) return 0;
    }
    if( isBack ){
      xV = pS->from[X_IDX];
      yV = pS->from[Y_IDX];
      xR = pS->to[X_IDX];
      yR = pS->to[Y_IDX];
    }else{
      xR = pS->from[X_IDX];
      yR = pS->from[Y_IDX];
      xV = pS->to[X_IDX];
      yV = pS->to[Y_IDX];
    }
    if( nBound>cnt ){ 
      aBound[cnt].pSeg = pS;
      aBound[cnt].backwards = isBack;
    }
    cnt++;
    if( cnt>1000 /* 00 */ ) return -cnt;   /* Avoid an infinite loop */
  }
  return cnt;
}

/*
** Compute the "spin" on a boundary.  A positive value means the
** circulation is to counter-clockwise and a negative value means the 
** circulation is clockwise.  For boundaries, a positive
** value means the region is internal and a negative value means
** the region is external.
*/
static double spin(Boundary *aBound, int nBound){
  double sum = 0;
  int i;
  for(i=0; i<nBound; i++){
    double x0, y0, x1, y1;
    double dx, dy;
    Segment *pSeg = aBound->pSeg;
    if( aBound->backwards ){
      x0 = pSeg->to[X_IDX];
      y0 = pSeg->to[Y_IDX];
      x1 = pSeg->from[X_IDX];
      y1 = pSeg->from[Y_IDX];
    }else{
      x0 = pSeg->from[X_IDX];
      y0 = pSeg->from[Y_IDX];
      x1 = pSeg->to[X_IDX];
      y1 = pSeg->to[Y_IDX];
    }
    aBound++;
    dx = x1-x0;
    dy = y1-y0;
    sum += x0*dy - y0*dx;
  }
  return sum;
}

/*
** The input is two linked lists of ComptBox structures where each
** list is sorted by increasing area.  Combine these two lists into
** a single sorted linked list.
*/
static ComptBox *mergeComptBox(ComptBox *p1, ComptBox *p2){
  ComptBox head;
  ComptBox *pTail = &head;
  ComptBox *p;
  while( p1 && p2 ){
    if( p1->area<=p2->area ){
      p = p1->pNext;
      pTail->pNext = p1;
      pTail = p1;
      p1 = p;
    }else{
      p = p2->pNext;
      pTail->pNext = p2;
      pTail = p2;
      p2 = p;
    }
  }
  if( p1 ){
    pTail->pNext = p1;
  }else{
    pTail->pNext = p2;
  }
  return head.pNext;
}

/*
** Construct the ComptBox cache.  For each compartment (where a compartment
** is a closed circuit of Segments) make an entry on the Wallset.pComptBox
** list.
**
** If the ComptBox cache already exists, this routine is a no-op.
*/
static void buildComptBoxCache(Wallset *p){
  Link *pI;
  int i;
  ComptBox *aSort[30];

  /* Return immediately if the cache already exists */
  if( p->pComptBox ) return;

  /* Compute a linked list of all compartment boxes */
  for(pI=p->pAll; pI; pI=pI->pNext){
    int i, j, n;
    Boundary aBound[1000];

    aBound[0].pSeg = pI->pLinkNode;
    for(j=0; j<2; j++){
      aBound[0].backwards = j;
      n = completeBoundary(p, 1, sizeof(aBound)/sizeof(aBound[0]), aBound);
      if( n>0 && spin(aBound,n)>0.0 ){
        double dx, dy;
        Segment *pSeg = pI->pLinkNode;
        ComptBox *pNew = (ComptBox *)Odie_Alloc( sizeof(*pNew) );
        pNew->pNext = p->pComptBox;
        pNew->bbox.l = pNew->bbox.r = pSeg->from[X_IDX];
        pNew->bbox.t = pNew->bbox.b = pSeg->from[Y_IDX];
        pNew->prim = aBound[0];
        for(i=1; i<n; i++){
          Segment *pSeg = aBound[i].pSeg;
          if( pSeg->from[X_IDX]<pNew->bbox.l ) pNew->bbox.l = pSeg->from[X_IDX];
          if( pSeg->from[X_IDX]>pNew->bbox.r ) pNew->bbox.r = pSeg->from[X_IDX];
          if( pSeg->from[Y_IDX]<pNew->bbox.b ) pNew->bbox.b = pSeg->from[Y_IDX];
          if( pSeg->from[Y_IDX]>pNew->bbox.t ) pNew->bbox.t = pSeg->from[Y_IDX];
          if( pSeg->to[X_IDX]<pNew->bbox.l ) pNew->bbox.l = pSeg->to[X_IDX];
          if( pSeg->to[X_IDX]>pNew->bbox.r ) pNew->bbox.r = pSeg->to[X_IDX];
          if( pSeg->to[Y_IDX]<pNew->bbox.b ) pNew->bbox.b = pSeg->to[Y_IDX];
          if( pSeg->to[Y_IDX]>pNew->bbox.t ) pNew->bbox.t = pSeg->to[Y_IDX];
        }
        dx = pNew->bbox.r - pNew->bbox.l;
        dy = pNew->bbox.t - pNew->bbox.b;
        pNew->area = sqrt(dx*dx+dy*dy);
        p->pComptBox = pNew;
      }
    }
  }

  /* Sort the list into order of increasing area */
  for(i=0; i<sizeof(aSort)/sizeof(aSort[0]); i++) aSort[i] = 0;
  while( p->pComptBox ){
    ComptBox *pBox = p->pComptBox;
    p->pComptBox = pBox->pNext;
    pBox->pNext = 0;
    for(i=0; i<sizeof(aSort)/sizeof(aSort[0])-1 && aSort[i]!=0; i++){
      pBox = mergeComptBox(aSort[i], pBox);
      aSort[i] = 0;
    }
    aSort[i] = mergeComptBox(aSort[i], pBox);
  }
  for(i=0; i<sizeof(aSort)/sizeof(aSort[0]); i++){
    p->pComptBox = mergeComptBox(aSort[i], p->pComptBox);
  }
}

/*
** Test to see if the point x,y is contained within the given
** boundary or is on the outside of the boundary.
*/
static int pointWithinBoundary(
  Boundary *aBound,      /* The boundary */
  int nBound,            /* Number of segments in the boundary */
  double x, double y     /* The point to test */
){
  int inside = 0;
  int i;
  for(i=0; i<nBound; i++){
    double x0, y0, x1, y1;
    Segment *p = aBound[i].pSeg;
    x0 = p->from[X_IDX];
    y0 = p->from[Y_IDX];
    x1 = p->to[X_IDX];
    y1 = p->to[Y_IDX];
    if( x0==x1 ) continue;
    if( (x0>x && x1>x) || (x0<x && x1<x) ) continue;
    if( y1 - (x1-x)*(y1-y0)/(x1-x0) >= y ) inside = !inside;
  }
  return inside;
}

/*
** Find a boundary which contains xI, yI.  If the size of the boundary
** is set to 0, that means no such boundary exists.
*/
static int findBoundary(
  Wallset *p,             /* The wallset */
  double xI, double yI,   /* A point that the boundary should be near */
  int nBound,             /* Number of slots available in aBound[] */
  Boundary *aBound        /* OUT: Write results here */
){
  int n = 0;
  ComptBox *pBox;

  buildComptBoxCache(p);
  for(pBox=p->pComptBox; pBox; pBox=pBox->pNext){
    if( xI<pBox->bbox.l || xI>pBox->bbox.r || yI<pBox->bbox.b || yI>pBox->bbox.t ) continue;
    aBound[0] = pBox->prim;
    n = completeBoundary(p, 0, nBound, aBound);
    if( n>0 && pointWithinBoundary(aBound, n, xI, yI) ) break;
    n = 0;
  }
  return n;
}


/*
** Do an check of the integrity of the internal data structures.  If
** a problem is found, leave an error message in interp->result and
** return TCL_ERROR.  Return TCL_OK if everything is OK.
*/
static int selfCheck(Tcl_Interp *interp, Wallset *p){
  Link *pLink;
  Segment *pSeg;
  int h;
  char zErr[200];

  for(pLink=p->pAll; pLink; pLink=pLink->pNext){
    pSeg = pLink->pLinkNode;
    h = hashInt(pSeg->id);
    if(!segmentOnList(pSeg, p->hashId[h]) ){
      sprintf(zErr, "segment %d missing from hashId[%d]", pSeg->id, h);
      Tcl_SetResult(interp, zErr, TCL_VOLATILE);
      return TCL_ERROR;
    }
    h = hashCoord(pSeg->from[X_IDX], pSeg->from[Y_IDX]);
    if(!segmentOnList(pSeg, p->hashFrom[h]) ){
      sprintf(zErr, "segment %d missing from hashFrom[%d]", pSeg->id, h);
      Tcl_SetResult(interp, zErr, TCL_VOLATILE);
      return TCL_ERROR;
    }
    h = hashCoord(pSeg->to[X_IDX], pSeg->to[Y_IDX]);
    if(!segmentOnList(pSeg, p->hashTo[h]) ){
      sprintf(zErr, "segment %d missing from hashTo[%d]", pSeg->id, h);
      Tcl_SetResult(interp, zErr, TCL_VOLATILE);
      return TCL_ERROR;
    }
  }
  return TCL_OK;
}

/*
** The maximum number of segments in a boundary
*/
#define MX_BOUND 1000


/*
** This routine runs when a method is executed against a wallset.
*/
static int wallsetMethodProc(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  Wallset *p = (Wallset*)pArg;
  Boundary aBound[MX_BOUND];

#if 0
  /* For debugging....
  ** Print each wallset command before it is executed.
  */
  { int i;
    for(i=0; i<objc; i++){
      printf("%s%c", Tcl_GetStringFromObj(objv[i], 0), i<objc-1 ? ' ' : '\n');
    }
  }
#endif


  /* The following bit of magic implements a switch() statement that
  ** invokes the correct code based on the value of objv[1].  The
  ** mktclopts.tcl script scans this source file looking for "case"
  ** statements then generates the "wallset.h" include file that contains
  ** all of the code necessary to implement the switch.
  **
  ** In this way, we can add new methods to the command simply by adding
  ** new cases within the switch.  All the related switch code is
  ** regenerated automatically.
  */
  {
#include "wallset_cases.h"
  {

  /***************************************************************************
  ** Command methods
  */

  /*
  ** tclmethod:  WALLSET atvertex X Y
  ** title:   Return a list of wall IDs that connect to vertex X,Y
  */
  case WALLSET_ATVERTEX: {
    Link *pList;
    double x, y;
    Tcl_Obj *pResult;
    if( objc!=4 ){
      Tcl_WrongNumArgs(interp, 2, objv, "X Y");
      return TCL_ERROR;
    }
    if( Tcl_GetDoubleFromObj(interp, objv[2], &x) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[3], &y) ) return TCL_ERROR;
    pResult = Tcl_NewObj();
    ignoreNone(p);
    pList = segmentsAtVertex(p, x*p->rXZoom, y*p->rYZoom);
    while( pList ){
      Segment *pSeg=pList->pLinkNode;
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(pSeg->id));
      pList = pList->pNext;
    }
    Tcl_SetObjResult(interp, pResult);
    break;
  }

  /*
  ** tclmethod:  WALLSET boundary X Y
  ** title:   Return indices of segments forming a boundary around X Y
  */
  case WALLSET_BOUNDARY: {
    int nBound;
    double x, y;
    Tcl_Obj *pResult;
    int i;
    int showDetail = 0;

    if( objc==5 && strcmp(Tcl_GetStringFromObj(objv[2],0),"-detail")==0 ){
      showDetail = 1;
      objc--;
      objv++;
    }
    if( objc!=4 ){
      Tcl_WrongNumArgs(interp, 2, objv, "?-detail? X Y");
      return TCL_ERROR;
    }
    if( Tcl_GetDoubleFromObj(interp, objv[2], &x) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[3], &y) ) return TCL_ERROR; 
    nBound = findBoundary(p, x*p->rXZoom, y*p->rYZoom, MX_BOUND, aBound);
    if( nBound>MX_BOUND ) nBound = 0;
    pResult = Tcl_NewObj();
    for(i=0; i<nBound; i++){
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(aBound[i].pSeg->id));
      if( showDetail ){
        Tcl_ListObjAppendElement(0, pResult,
           ODIE_CONSTANT_STRING(aBound[i].backwards ? "right" : "left"));
      }
    }
    Tcl_SetObjResult(interp, pResult);
    break;
  }

  /*
  ** tclmethod:  WALLSET closure WALLID BACKWARDS CHECKPRIMARY ?-coords?
  ** title:   Return the closure of a wall
  **
  ** The closure is a path of walls going clockwise from the wall given.
  ** The return value is a list consisting of wall IDs alternating with
  ** keywords "left" or "right" indicating which side of the wall applies.
  ** If the CHECKPRIMARY flag is true and the WALLID/BACKWARDS is not the
  ** primary wall id for the closure, then return an empty string.  The
  ** primary wall id is the wall id with the lowest id number, or if
  ** two walls in the closure have the same id, then the one that goes
  ** on the right side of the wall.
  */
  case WALLSET_CLOSURE: {
    int id;
    int nBound, i, checkPrim;
    Tcl_Obj *pResult;
    int coordsFlag = 0;
    int noerrFlag = 0;
    if( objc!=5 && objc!=6 ){
      Tcl_WrongNumArgs(interp, 2, objv, 
           "WALLID BACKWARDS CHECKPRIMARY ?-coords? ?-noerr?");
      return TCL_ERROR;
    }
    if( objc==6 ){
      const char *zOpt = Tcl_GetStringFromObj(objv[5],0);
      if( strcmp(zOpt,"-coords")==0 ){
        coordsFlag = 1;
      }else if( strcmp(zOpt,"-noerr")==0 ){
        noerrFlag = 1;
      }else{
        Tcl_AppendResult(interp, "unknown option: ", zOpt, 0);
        return TCL_ERROR;
      }
    }
    if( Tcl_GetIntFromObj(interp, objv[2], &id) ) return TCL_ERROR;
    if( (aBound[0].pSeg = findSegment(p, id))==0 ){
      Tcl_AppendResult(interp, "segment ",
        Tcl_GetStringFromObj(objv[2],0), " does not exist", 0);
      return TCL_ERROR;
    }
    if( Tcl_GetBooleanFromObj(interp, objv[3], &aBound[0].backwards) ){
      return TCL_ERROR;
    }
    if( Tcl_GetBooleanFromObj(interp, objv[4], &checkPrim) ){
      return TCL_ERROR;
    }
    ignoreNone(p);
    nBound = completeBoundary(p, checkPrim, MX_BOUND, aBound);  
    pResult = Tcl_NewObj();
    if( nBound<0 && noerrFlag ) nBound = -nBound;
    for(i=0; i<nBound; i++){
      if( coordsFlag ){
        double x, y;
        Segment *pSeg = aBound[i].pSeg;
        if( aBound[i].backwards ){
          x = pSeg->to[X_IDX];
          y = pSeg->to[Y_IDX];
        }else{
          x = pSeg->from[X_IDX];
          y = pSeg->from[Y_IDX];
        }
        Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(x/p->rXZoom));
        Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(y/p->rYZoom));
      }else{
        Tcl_ListObjAppendElement(0, pResult,
                                       Tcl_NewIntObj(aBound[i].pSeg->id));
        Tcl_ListObjAppendElement(0, pResult,
             ODIE_CONSTANT_STRING(aBound[i].backwards ? "right" : "left"));
      }
    }
    Tcl_SetObjResult(interp, pResult);
    break;
  }

  /*
  ** tclmethod:  WALLSET comptlist
  ** title:   Return a list of all compartments
  **
  ** A compartment is a closed circuit of walls.  This routine returns
  ** a list of all compartments.  Each element of the list consists of
  ** the primary wall for the compartment followed by a bounding box
  ** for the compartment.
  */
  case WALLSET_COMPTLIST: {
    ComptBox *pBox;
    Tcl_Obj *pResult = Tcl_NewObj();
    buildComptBoxCache(p);
    for(pBox=p->pComptBox; pBox; pBox=pBox->pNext){
      Tcl_Obj *pElem = Tcl_NewObj();
      Tcl_ListObjAppendElement(0, pElem,Tcl_NewIntObj(pBox->prim.pSeg->id));
      Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pBox->prim.backwards));
      Tcl_ListObjAppendElement(0, pElem, Tcl_NewDoubleObj(pBox->bbox.l/p->rXZoom));
      Tcl_ListObjAppendElement(0, pElem, Tcl_NewDoubleObj(pBox->bbox.b/p->rYZoom));
      Tcl_ListObjAppendElement(0, pElem, Tcl_NewDoubleObj(pBox->bbox.r/p->rXZoom));
      Tcl_ListObjAppendElement(0, pElem, Tcl_NewDoubleObj(pBox->bbox.t/p->rYZoom));
      Tcl_ListObjAppendElement(0, pResult, pElem);
    }
    Tcl_SetObjResult(interp, pResult);
    break;
  }

  /*
  ** tclmethod:  WALLSET primary X Y
  ** title:   Return the primary segment of the compartment enclosing X,Y
  **
  ** The primary segment is the segment with the smallest ID.  If the
  ** same segment occurs twice on the list (in other words, if the
  ** same compartment is on both sides of a wall), then the right side
  ** (as measured facing the direction of travel from x0,y0 -> x1,y1) 
  ** is used.
  */
  case WALLSET_PRIMARY: {
    int nBound;
    double x, y;
    int i, sideSmallest;
    int idSmallest;
    Tcl_Obj *pResult;
    if( objc!=4 ){
      Tcl_WrongNumArgs(interp, 2, objv, "X Y");
      return TCL_ERROR;
    }
    if( Tcl_GetDoubleFromObj(interp, objv[2], &x) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[3], &y) ) return TCL_ERROR; 
    nBound = findBoundary(p, x*p->rXZoom, y*p->rYZoom, MX_BOUND, aBound);
    if( nBound>0 && nBound<MX_BOUND ){
      idSmallest = aBound[0].pSeg->id;
      sideSmallest = aBound[0].backwards;
      for(i=1; i<nBound; i++){
        if( aBound[i].pSeg->id>idSmallest ) continue;
        if( aBound[i].pSeg->id<idSmallest || !sideSmallest ){
          idSmallest = aBound[i].pSeg->id;
          sideSmallest = aBound[i].backwards;
        }
      }
      pResult = Tcl_NewObj();
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(idSmallest));
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(sideSmallest));
      Tcl_SetObjResult(interp, pResult);
    }
    break;
  }

  /*
  ** tclmethod:  WALLSET corners X Y
  ** title:   Return vertices of compartment containing X,Y
  */
  case WALLSET_CORNERS: {
    int nBound, i;
    double x, y;
    Tcl_Obj *pResult;
    if( objc!=4 ){
      Tcl_WrongNumArgs(interp, 2, objv, "X Y");
      return TCL_ERROR;
    }
    if( Tcl_GetDoubleFromObj(interp, objv[2], &x) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[3], &y) ) return TCL_ERROR; 
    nBound = findBoundary(p, x*p->rXZoom, y*p->rYZoom, MX_BOUND, aBound);
    if( nBound>MX_BOUND ) nBound = 0;
    pResult = Tcl_NewObj();
    for(i=0; i<nBound; i++){
      Segment *pSeg = aBound[i].pSeg;
      if( aBound[i].backwards ){
        x = pSeg->to[X_IDX];
        y = pSeg->to[Y_IDX];
      }else{
        x = pSeg->from[X_IDX];
        y = pSeg->from[Y_IDX];
      }
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(x/p->rXZoom));
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(y/p->rYZoom));
    }
    Tcl_SetObjResult(interp, pResult);
    break;
  }

  /*
  ** tclmethod:  WALLSET delete ID
  ** title:   Delete a single segment of a wall given by ID
  */
  case WALLSET_DELETE: {
    int id;
    Segment *pSeg;
    if( objc!=3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "ID");
      return TCL_ERROR;
    }
    if( p->busy ){
      Tcl_AppendResult(interp, "cannot \"delete\" from within a \"foreach\"",0);
      return TCL_ERROR;
    }
    if( Tcl_GetIntFromObj(interp, objv[2], &id) ) return TCL_ERROR;
    if( (pSeg = findSegment(p, id))==0 ){
      Tcl_AppendResult(interp, "segment ",
        Tcl_GetStringFromObj(objv[2],0), " does not exist", 0);
      return TCL_ERROR;
    }
    clearComptBoxCache(p);
    LinkRemove(&pSeg->pAll);
    /* We intentionally do not remove pSeg->pSet because it might not be
    ** a well-formed list */
    LinkRemove(&pSeg->pHash);
    LinkRemove(&pSeg->pFrom);
    LinkRemove(&pSeg->pTo);
    Odie_Free((char *)pSeg);
    break;
  }

  /*
  ** tclmethod:  WALLSET destroy
  ** title:   Destroy this wallset
  */
  case WALLSET_DESTROY: {
    Tcl_DeleteCommand(interp,Tcl_GetString(objv[0]));
    break;
  }
  
  /*
  ** tclmethod:  WALLSET firstboundary X Y
  ** title:   Find a wall on the boundary of compartment containing X Y
  **
  ** Returns a list of two elements on success or an empty list if no
  ** suitable boundary could be found.  The first element is the ID of a
  ** wall that forms part of the boundary for the compartment containing
  ** point X,Y.  The second element is TRUE if X,Y is to the right of the
  ** wall and false if it is to the left.
  **
  ** The right/left designation assumes a right-handed coordinate system.
  */
  case WALLSET_FIRSTBOUNDARY: {
    int isBack;
    Segment *pSeg;
    double x, y;
    int rc;
    if( objc!=4 ){
      Tcl_WrongNumArgs(interp, 2, objv, "X Y");
      return TCL_ERROR;
    }
    if( Tcl_GetDoubleFromObj(interp, objv[2], &x) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[3], &y) ) return TCL_ERROR;
    ignoreNone(p);
    rc = firstBoundarySegment(p, x*p->rXZoom, y*p->rYZoom, &pSeg, &isBack);
    if( rc==0 ){
      Tcl_Obj *pResult = Tcl_NewObj();
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(pSeg->id));
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(isBack));
      Tcl_SetObjResult(interp, pResult);
    }
    break;
  }

  /*
  ** tclmethod: WALLSET foreach CODE
  ** title:  Run CODE for each segment of the wallset
  */
  case WALLSET_FOREACH: {
    Link *pLink;
    int rc = TCL_OK;
    if( objc!=3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "CODE");
      return TCL_ERROR;
    }
    p->busy++;
    for(pLink=p->pAll; pLink && rc==TCL_OK; pLink=pLink->pNext){
      Segment *pSeg = pLink->pLinkNode;
      Tcl_SetVar2Ex(interp, "x0", 0, Tcl_NewDoubleObj(pSeg->from[X_IDX]/p->rXZoom), 0);
      Tcl_SetVar2Ex(interp, "y0", 0, Tcl_NewDoubleObj(pSeg->from[Y_IDX]/p->rYZoom), 0);
      Tcl_SetVar2Ex(interp, "x1", 0, Tcl_NewDoubleObj(pSeg->to[X_IDX]/p->rXZoom), 0);
      Tcl_SetVar2Ex(interp, "y1", 0, Tcl_NewDoubleObj(pSeg->to[Y_IDX]/p->rYZoom), 0);
      Tcl_SetVar2Ex(interp, "id", 0, Tcl_NewIntObj(pSeg->id), 0);
      Tcl_SetVar2Ex(interp, "lc", 0, Tcl_NewIntObj(pSeg->idLC), 0);
      Tcl_SetVar2Ex(interp, "rc", 0, Tcl_NewIntObj(pSeg->idRC), 0);
      Tcl_SetVar2Ex(interp, "virtual", 0, Tcl_NewIntObj(pSeg->isBoundary), 0);
      rc = Tcl_EvalObjEx(interp, objv[2], 0);
    }
    if( rc==TCL_BREAK ) rc = TCL_OK;
    p->busy--;
    return rc;
  }

  /*
  ** tclmethod: WALLSET info ID
  ** title:  Return information about a single wall segment
  */
  case WALLSET_INFO: {
    int id;
    Segment *pSeg;
    Tcl_Obj *pResult;
    if( objc!=3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "ID");
      return TCL_ERROR;
    }
    if( Tcl_GetIntFromObj(interp, objv[2], &id) ) return TCL_ERROR;
    if( (pSeg = findSegment(p, id))==0 ){
      Tcl_AppendResult(interp, "segment ",
        Tcl_GetStringFromObj(objv[2],0), " does not exist", 0);
      return TCL_ERROR;
    }
    pResult = Tcl_NewObj();
    Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(pSeg->from[X_IDX]/p->rXZoom));
    Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(pSeg->from[Y_IDX]/p->rYZoom));
    Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(pSeg->to[X_IDX]/p->rXZoom));
    Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(pSeg->to[Y_IDX]/p->rYZoom));
    Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(pSeg->id));
    Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(pSeg->idLC));
    Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(pSeg->idRC));
    Tcl_SetObjResult(interp, pResult);
    break;
  }

  /* tclmethod:  WALLSET insert X0 Y0 X1 Y1 ID LC RC VIRTUAL
  ** title:   Create a new wall within the wallset
  */
  case WALLSET_INSERT: {
    int id;
    int h,virtual=0;
    double x0, y0, x1, y1;
    int idLC, idRC;
    Segment *pSeg;
    if( objc!=9 && objc!=10){
      Tcl_WrongNumArgs(interp, 2, objv, "X0 Y0 X1 Y1 ID LC RC ?1|0?");
      return TCL_ERROR;
    }
    if( p->busy ){
      Tcl_AppendResult(interp, "cannot \"insert\" from within a \"foreach\"",0);
      return TCL_ERROR;
    }
    if( Tcl_GetDoubleFromObj(interp, objv[2], &x0) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[3], &y0) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[4], &x1) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[5], &y1) ) return TCL_ERROR;
    if( Tcl_GetIntFromObj(interp, objv[6], &id) ) return TCL_ERROR;
    if( Tcl_GetIntFromObj(interp, objv[7], &idLC) ) return TCL_ERROR;
    if( Tcl_GetIntFromObj(interp, objv[8], &idRC) ) return TCL_ERROR;
    if(objc==10) {
      if( Tcl_GetIntFromObj(interp, objv[8], &virtual) ) return TCL_ERROR;
    }
    x0 = roundCoord(x0*p->rXZoom);
    y0 = roundCoord(y0*p->rYZoom);
    x1 = roundCoord(x1*p->rXZoom);
    y1 = roundCoord(y1*p->rYZoom);
    if( findSegment(p, id) ){
      Tcl_AppendResult(interp, "segment ",
        Tcl_GetStringFromObj(objv[6],0), " already exists", 0);
      return TCL_ERROR;
    }
    if( floatCompare(x0,x1)==0 && floatCompare(y0,y1)==0 ){
      /* Tcl_AppendResult(interp, "endpoints must be distinct", 0); */
      /* return TCL_ERROR; */
      return TCL_OK;  /* Not an error.  Just a no-op. */
    }
    clearComptBoxCache(p);
    pSeg = (Segment *)Odie_Alloc( sizeof(*pSeg) );
    if( pSeg==0 ) return TCL_ERROR;
    pSeg->id = id;
    pSeg->idLC = idLC;
    pSeg->idRC = idRC;
    pSeg->from[X_IDX] = x0;
    pSeg->from[Y_IDX] = y0;
    pSeg->to[X_IDX] = x1;
    pSeg->to[Y_IDX] = y1;
    pSeg->isBoundary=virtual;

    LinkInit(pSeg->pAll, pSeg);
    LinkInit(pSeg->pSet, pSeg);
    LinkInit(pSeg->pHash, pSeg);
    LinkInit(pSeg->pFrom, pSeg);
    LinkInit(pSeg->pTo, pSeg);
    LinkInsert(&p->pAll, &pSeg->pAll);
    h = hashInt(id);
    LinkInsert(&p->hashId[h], &pSeg->pHash);
    h = hashCoord(pSeg->from[X_IDX], pSeg->from[Y_IDX]);
    LinkInsert(&p->hashFrom[h], &pSeg->pFrom);
    h = hashCoord(pSeg->to[X_IDX], pSeg->to[Y_IDX]);
    LinkInsert(&p->hashTo[h], &pSeg->pTo);
    break;
  }

  /*
  ** tclmethod:  WALLSET intersect X0 Y0 X1 Y1
  ** title:   Find the intersection of X0,Y0->X1,Y1 with a segment
  **
  ** Scan all segments in the wallset looking for one that intersects with
  ** a line from X0,Y0 to X1,Y1.  If the intersection occurs at x0,y0, it
  ** is ignored, but intersections at x1,y1 count.  If no such intersection
  ** exists, return the empty string.  If there are one or more intersections,
  ** return the ID of the segment and the X and Y coordinates of the nearest
  ** intersection to X0,Y0.  
  */
  case WALLSET_INTERSECT: {
    double x0,y0,x1,y1;
    double adx, ady;
    Link *pI;
    int id;
    double nx, ny;
    double mindist2 = -1.0;
    if( objc!=6 ){
      Tcl_WrongNumArgs(interp, 2, objv, "X0 Y0 X1 Y1");
      return TCL_ERROR;
    }
    if( Tcl_GetDoubleFromObj(interp, objv[2], &x0) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[3], &y0) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[4], &x1) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[5], &y1) ) return TCL_ERROR;
    x0 = roundCoord(x0*p->rXZoom);
    y0 = roundCoord(y0*p->rYZoom);
    x1 = roundCoord(x1*p->rXZoom);
    y1 = roundCoord(y1*p->rYZoom);
    adx = x1-x0;
    ady = y1-y0;
    if( adx==0.0 && ady==0.0 ) break;
    for(pI=p->pAll; pI; pI=pI->pNext){
      double bdx, bdy, denom, num1;
      Segment *pSeg;
      pSeg = pI->pLinkNode;
      bdx = pSeg->to[X_IDX] - pSeg->from[X_IDX];
      bdy = pSeg->to[Y_IDX] - pSeg->from[Y_IDX];
      denom = adx*bdy - ady*bdx;
      num1 = (y0-pSeg->from[Y_IDX])*bdx - (x0-pSeg->from[X_IDX])*bdy;
      if( denom==0.0 ){
        /* The reference line and segment are parallel */
        if( num1==0.0 ){
          /* The reference line and segment are colinear */
          if( samePoint(x0,y0,pSeg->from[X_IDX],pSeg->from[Y_IDX])
                  && adx*bdx<=0.0 && ady*bdy<=0.0 ){
            continue;
          }
          if( samePoint(x0,y0,pSeg->to[X_IDX],pSeg->to[Y_IDX])
                  && adx*bdx>=0.0 && ady*bdy>=0.0 ){
            continue;
          }
          if( between(pSeg->from[Y_IDX],y0,y1) && between(pSeg->from[X_IDX],x0,x1) ){
            double dx, dy, dist2;
            dx = pSeg->from[X_IDX] - x0;
            dy = pSeg->from[Y_IDX] - y0;
            dist2 = dx*dx + dy*dy;
            if( mindist2<0 || mindist2>dist2 ){
              mindist2 = dist2;
              nx = pSeg->from[X_IDX];
              ny = pSeg->from[Y_IDX];
              id = pSeg->id;
            }
          }
          if( between(pSeg->to[Y_IDX],y0,y1) && between(pSeg->to[X_IDX],x0,x1) ){
            double dx, dy, dist2;
            dx = pSeg->to[X_IDX] - x0;
            dy = pSeg->to[Y_IDX] - y0;
            dist2 = dx*dx + dy*dy;
            if( mindist2<0 || mindist2>dist2 ){
              mindist2 = dist2;
              nx = pSeg->to[X_IDX];
              ny = pSeg->to[Y_IDX];
              id = pSeg->id;
            }
          }
          if( between(y0,pSeg->from[Y_IDX],pSeg->to[Y_IDX]) && between(x0,pSeg->from[X_IDX],pSeg->to[X_IDX]) ){
            if( mindist2<0 || mindist2>0.0 ){
              mindist2 = 0.0;
              nx = x0;
              ny = y0;
              id = pSeg->id;
            }
          }
        }
      }else{
        /* The reference line and segment are not parallel */
        double r, s;
        r = num1/denom;
        s = ((y0-pSeg->from[Y_IDX])*adx - (x0-pSeg->from[X_IDX])*ady)/denom;
        if( r>0 && r<=1.0 && s>=0.0 && s<=1.0 ){
          double dx, dy, dist2;
          dx = r*adx;
          dy = r*ady;
          dist2 = dx*dx + dy*dy;
          if( dist2>=GRAIN && (mindist2<0 || mindist2>dist2) ){
            mindist2 = dist2;
            nx = x0 + dx;
            ny = y0 + dy;
            id = pSeg->id;
          }
        }
      }
    }
    if( mindist2>=0.0 ){
      Tcl_Obj *pResult;
      pResult = Tcl_NewObj();
      nx = roundCoord(nx);
      ny = roundCoord(ny);
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(nx/p->rXZoom));
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(ny/p->rYZoom));
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(id));
      Tcl_SetObjResult(interp, pResult);
    }
    break;
  }

  /*
  ** tclmethod:  WALLSET left ID LC
  ** title:   Change the left compartment of a line segment
  */
  case WALLSET_LEFT: {
    int id, idLC;
    Segment *pSeg;
    if( objc!=4 ){
      Tcl_WrongNumArgs(interp, 2, objv, "ID LC");
      return TCL_ERROR;
    }
    if( Tcl_GetIntFromObj(interp, objv[2], &id) ) return TCL_ERROR;
    if( Tcl_GetIntFromObj(interp, objv[3], &idLC) ) return TCL_ERROR;
    if( (pSeg = findSegment(p, id))==0 ){
      Tcl_AppendResult(interp, "segment ",
        Tcl_GetStringFromObj(objv[2],0), " does not exist", 0);
      return TCL_ERROR;
    }
    pSeg->idLC = idLC;
    break;
  }

  /*
  ** tclmethod: WALLSET list
  ** title:  Return a list of all wall segment identifiers
  */
  case WALLSET_LIST: {
    Link *pLink;
    Tcl_Obj *pResult;
    pResult = Tcl_NewObj();
    for(pLink=p->pAll; pLink; pLink=pLink->pNext){
      Segment *pSeg=pLink->pLinkNode;
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(pSeg->id));
    }
    Tcl_SetObjResult(interp, pResult);
    break;
  }

  /*
  ** tclmethod:  WALLSET looseends
  ** title:   Return a list of walls that are have unconnected ends
  **
  ** For each unconnected end, the list contains four elements:
  **    1.  The wallid
  **    2.  0 for the "from" end, "1" for the "to" end
  **    3.  The X coordinate of the loose end
  **    4.  The Y coordinate of the loose end
  */
  case WALLSET_LOOSEENDS: {
    Segment *pSeg;
    Link *pAll, *pList;
    Tcl_Obj *pRes = Tcl_NewObj();
    for(pAll=p->pAll; pAll; pAll=pAll->pNext){
      pSeg = pAll->pLinkNode;
      pList = segmentsAtVertex(p, pSeg->from[X_IDX], pSeg->from[Y_IDX]);
      if( LinkCount(pList)==1 ){
        Tcl_ListObjAppendElement(0, pRes, Tcl_NewIntObj(pSeg->id));
        Tcl_ListObjAppendElement(0, pRes, ODIE_INT_ZERO());
        Tcl_ListObjAppendElement(0, pRes, Tcl_NewDoubleObj(pSeg->from[X_IDX]/p->rXZoom));
        Tcl_ListObjAppendElement(0, pRes, Tcl_NewDoubleObj(pSeg->from[Y_IDX]/p->rYZoom));
      }
      pList = segmentsAtVertex(p, pSeg->to[X_IDX], pSeg->to[Y_IDX]);
      if( LinkCount(pList)==1 ){
        Tcl_ListObjAppendElement(0, pRes, Tcl_NewIntObj(pSeg->id));
        Tcl_ListObjAppendElement(0, pRes, ODIE_INT_ONE());
        Tcl_ListObjAppendElement(0, pRes, Tcl_NewDoubleObj(pSeg->to[X_IDX]/p->rXZoom));
        Tcl_ListObjAppendElement(0, pRes, Tcl_NewDoubleObj(pSeg->to[Y_IDX]/p->rYZoom));
      }
    }
    Tcl_SetObjResult(interp, pRes);
    break;
  }

  /*
  ** tclmethod:  WALLSET nearest vertex|point X Y
  ** title:   Find the nearest vertex or point to a point in the plan
  */
  case WALLSET_NEAREST: {
    int type;
    double x, y, near_x, near_y;
    static const char *NEAR_strs[] = { "point", "vertex", 0 };
    enum NEAR_enum { NEAR_POINT, NEAR_VERTEX, };
    Link *pLink;
    Tcl_Obj *pResult;
    double dx, dy, dist;

    if( objc!=5 ){
      Tcl_WrongNumArgs(interp, 2, objv, "point|vertex X Y");
      return TCL_ERROR;
    }
    if( Tcl_GetIndexFromObj(interp, objv[2], NEAR_strs, "option", 0, &type) ){
      return TCL_ERROR;
    }
    if( Tcl_GetDoubleFromObj(interp, objv[3], &x) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[4], &y) ) return TCL_ERROR;
    x *= p->rXZoom;
    y *= p->rYZoom;
    ignoreNone(p);
    if( type==NEAR_POINT ){
      pLink = nearestPoint(p, x, y, &near_x, &near_y);
    }else if( type==NEAR_VERTEX ){
      pLink = nearestVertex(p, x, y, &near_x, &near_y);
    }else{
      /* Cannot happen */ return TCL_ERROR;
    }
    if( pLink==0 ) return TCL_OK;  /* There are not segments in the wallset */
    pResult = Tcl_NewObj();
    Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(near_x/p->rXZoom));
    Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(near_y/p->rYZoom));
    dx = x - near_x;
    dy = y - near_y;
    dist = sqrt(dx*dx + dy*dy);
    Tcl_ListObjAppendElement(0, pResult, Tcl_NewDoubleObj(dist/p->rXZoom));
    Tcl_ListObjAppendElement(0, pResult, Tcl_NewObj());
    while( pLink ){
      Segment *pSeg=pLink->pLinkNode;
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(pSeg->id));
      pLink = pLink->pNext;
    }
    Tcl_SetObjResult(interp, pResult);
    break;
  }

  /*
  ** tclmethod:  WALLSET nextcwwall X0 Y0 X1 Y1
  ** title:   Find a wall on X1,Y1 clockwise from X0,Y0->X1,Y1
  */
  case WALLSET_NEXTCWWALL: {
    int isBack;
    Segment *pSeg;
    double x0, y0, x1, y1;
    int rc;
    if( objc!=6 ){
      Tcl_WrongNumArgs(interp, 2, objv, "X0 Y0 X1 Y1");
      return TCL_ERROR;
    }
    if( Tcl_GetDoubleFromObj(interp, objv[2], &x0) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[3], &y0) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[4], &x1) ) return TCL_ERROR;
    if( Tcl_GetDoubleFromObj(interp, objv[5], &y1) ) return TCL_ERROR;
    x0 = roundCoord(x0*p->rXZoom);
    y0 = roundCoord(y0*p->rYZoom);
    x1 = roundCoord(x1*p->rXZoom);
    y1 = roundCoord(y1*p->rYZoom);
    rc = nextCwSegment(p, x0, y0, x1, y1, &pSeg, &isBack);
    if( rc==0 ){
      Tcl_Obj *pResult = Tcl_NewObj();
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(pSeg->id));
      Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(isBack));
      Tcl_SetObjResult(interp, pResult);
    }
    break;
  }

  /*
  ** tclmethod:  WALLSET right ID RC
  ** title:   Change the right compartment of a line segment
  */
  case WALLSET_RIGHT: {
    int id, idRC;
    Segment *pSeg;
    if( objc!=4 ){
      Tcl_WrongNumArgs(interp, 2, objv, "ID RC");
      return TCL_ERROR;
    }
    if( Tcl_GetIntFromObj(interp, objv[2], &id) ) return TCL_ERROR;
    if( Tcl_GetIntFromObj(interp, objv[3], &idRC) ) return TCL_ERROR;
    if( (pSeg = findSegment(p, id))==0 ){
      Tcl_AppendResult(interp, "segment ",
        Tcl_GetStringFromObj(objv[2],0), " does not exist", 0);
      return TCL_ERROR;
    }
    pSeg->idRC = idRC;
    break;
  }

  /*
  ** tclmethod: WALLSET segments
  ** title:  Write all wall segments out into the segset native datatype
  */
  case WALLSET_SEGMENTS: {
    
  }

  /*
  ** tclmethod:  WALLSET selfcheck
  ** title:   Verify the integrity of internal data structures
  */
  case WALLSET_SELFCHECK: {
    return selfCheck(interp, p);
  }

  /*
  ** tclmethod:  WALLSET zoom ?ZOOM?
  ** title:   Query or change the zoom factor.
  */
  case WALLSET_ZOOM: {
    Tcl_Obj *pResult;
    if( objc!=2 && objc!=3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "?ZOOM?");
      return TCL_ERROR;
    }
    if( objc==3 ){
      double r;
      if( Tcl_GetDoubleFromObj(interp, objv[2], &r) ) return TCL_ERROR;
      if( r==0.0 ){
        Tcl_AppendResult(interp, "zoom must be non-zero", 0);
        return TCL_ERROR;
      }
      p->rYZoom = r;
      p->rXZoom = fabs(r);
    }
    pResult = Tcl_NewDoubleObj(p->rYZoom);
    Tcl_SetObjResult(interp, pResult);
    break;
  }

  /* End of the command methods.  The brackets that follow terminate the
  ** automatically generated switch.
  ****************************************************************************/
  }
  }

#if 0
  /* Sanity checking for debugging */
  if( selfCheck(interp, p) ){
    return TCL_ERROR;
  }
#endif
  return TCL_OK;
}

/*
** tclcmd: wallset WALLSET
** title: Create a new wallset object
** This routine runs when the "wallset" command is invoked to create a
** new wallset.
*/
int Odie_WallsetCreateProc(
  void *NotUsed,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  char *zCmd;
  Wallset *p;
  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "WALLSET");
    return TCL_ERROR;
  }
  zCmd = Tcl_GetStringFromObj(objv[1], 0);
  p = (Wallset *)Odie_Alloc( sizeof(*p) );
  p->rXZoom = 100.0;
  p->rYZoom = -100.0;
  Tcl_CreateObjCommand(interp, zCmd, wallsetMethodProc, p, destroyWallset);
  return TCL_OK;
}

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted cmodules/geometry/generic/wallset_cases.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*** Automatically Generated Header File - Do Not Edit ***/
  const static char *WALLSET_strs[] = {
    "atvertex",           "boundary",          "closure",
    "comptlist",          "corners",           "delete",
    "destroy",            "firstboundary",     "foreach",
    "info",               "insert",            "intersect",
    "left",               "list",              "looseends",
    "nearest",            "nextcwwall",        "primary",
    "right",              "segments",          "selfcheck",
    "zoom",               0                    
  };
  enum WALLSET_enum {
    WALLSET_ATVERTEX,     WALLSET_BOUNDARY,    WALLSET_CLOSURE,
    WALLSET_COMPTLIST,    WALLSET_CORNERS,     WALLSET_DELETE,
    WALLSET_DESTROY,      WALLSET_FIRSTBOUNDARY,WALLSET_FOREACH,
    WALLSET_INFO,         WALLSET_INSERT,      WALLSET_INTERSECT,
    WALLSET_LEFT,         WALLSET_LIST,        WALLSET_LOOSEENDS,
    WALLSET_NEAREST,      WALLSET_NEXTCWWALL,  WALLSET_PRIMARY,
    WALLSET_RIGHT,        WALLSET_SEGMENTS,    WALLSET_SELFCHECK,
    WALLSET_ZOOM,        
  };
 int index;
  if( objc<2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "METHOD ?ARG ...?");
    return TCL_ERROR;
  }
  if( Tcl_GetIndexFromObj(interp, objv[1], WALLSET_strs, "option", 0, &index)){
    return TCL_ERROR;
  }
  switch( (enum WALLSET_enum)index )
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























































Deleted cmodules/geometry/geometry.man.
Deleted cmodules/logicset/cthulhu.ini.
1
2
3
set here [file dirname [file normalize [info script]]]
::cthulhu::add_directory $here {
}
<
<
<






Deleted cmodules/logicset/logicset.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328

/*
** This file is machine generated. Changes will
** be overwritten on the next run of cstruct.tcl
*/
#include "odieInt.h"
#include <strings.h>
#include <ctype.h>
#define UCHAR(c) ((unsigned char) (c))
#define TclFormatInt(buf, n)		sprintf((buf), "%ld", (long)(n))

#define MEM_DEBUG 0

/*
 * Macros used to cast between pointers and integers (e.g. when storing an int
 * in ClientData), on 64-bit architectures they avoid gcc warning about "cast
 * to/from pointer from/to integer of different size".
 */

#if !defined(INT2PTR) && !defined(PTR2INT)
#   if defined(HAVE_INTPTR_T) || defined(intptr_t)
#	define INT2PTR(p) ((void *)(intptr_t)(p))
#	define PTR2INT(p) ((int)(intptr_t)(p))
#   else
#	define INT2PTR(p) ((void *)(p))
#	define PTR2INT(p) ((int)(p))
#   endif
#endif
#if !defined(UINT2PTR) && !defined(PTR2UINT)
#   if defined(HAVE_UINTPTR_T) || defined(uintptr_t)
#	define UINT2PTR(p) ((void *)(uintptr_t)(p))
#	define PTR2UINT(p) ((unsigned int)(uintptr_t)(p))
#   else
#	define UINT2PTR(p) ((void *)(p))
#	define PTR2UINT(p) ((unsigned int)(p))
#   endif
#endif

#define VERSION "1.0"

/*
** Internal call required for munging integers
*/

/*
 * The structure used as the internal representation of Tcl list objects. This
 * struct is grown (reallocated and copied) as necessary to hold all the
 * list's element pointers. The struct might contain more slots than currently
 * used to hold all element pointers. This is done to make append operations
 * faster.
 */

typedef struct List {
    int refCount;
    int maxElemCount;		/* Total number of element array slots. */
    int elemCount;		/* Current number of list elements. */
    int canonicalFlag;		/* Set if the string representation was
				 * derived from the list representation. May
				 * be ignored if there is no string rep at
				 * all.*/
    Tcl_Obj *elements;		/* First list element; the struct is grown to
				 * accomodate all elements. */
} List;

/*
 * During execution of the "lsort" command, structures of the following type
 * are used to arrange the objects being sorted into a collection of linked
 * lists.
 */

typedef struct SortElement {
    union {
	char *strValuePtr;
	long intValue;
	double doubleValue;
	Tcl_Obj *objValuePtr;
    } index;
    Tcl_Obj *objPtr;	        /* Object being sorted, or its index. */
    struct SortElement *nextPtr;/* Next element in the list, or NULL for end
				 * of list. */
} SortElement;


typedef struct SortInfo {
  int isIncreasing;		/* Nonzero means sort in increasing order. */
  int sortMode;		/* The sort mode. One of SORTMODE_* values  * defined below. */
  Tcl_Obj *compareCmdPtr;	/* The Tcl comparison command when sortMode is
                                 * SORTMODE_COMMAND. Pre-initialized to hold
                                * base of command. */
  int *indexv;		/* If the -index option was specified, this
                         * holds the indexes contained in the list
                         * 
                         * supplied as an argument to that option.
                         * NULL if no indexes supplied, and points to
                         * singleIndex field when only one
                         * supplied.
                         */
  int indexc;		/* Number of indexes in indexv array. */
  int singleIndex;	/* Static space for common index case. */
  int unique;
  int numElements;
  Tcl_Interp *interp;   /* The interpreter in which the sort is being
                         * done.
                         */
  int resultCode;       /* Completion code for the lsort command. If
                         * an error occurs during the sort this is
                         * changed from TCL_OK to TCL_ERROR. */
} SortInfo;

/*
 * These function pointer types are used with the "lsearch" and "lsort"
 * commands to facilitate the "-nocase" option.
 */

typedef int (*SortStrCmpFn_t) (const char *, const char *);
typedef int (*SortMemCmpFn_t) (const void *, const void *, size_t);

/*
 * The "lsort" command needs to pass certain information down to the function
 * that compares two list elements, and the comparison function needs to pass
 * success or failure information back up to the top-level "lsort" command.
 * The following structure is used to pass this information.
 */


/*
 * The "sortMode" field of the SortInfo structure can take on any of the
 * following values.
 */

#define SORTMODE_ASCII		0
#define SORTMODE_INTEGER	1
#define SORTMODE_REAL		2
#define SORTMODE_COMMAND	3
#define SORTMODE_DICTIONARY	4
#define SORTMODE_ASCII_NC	8

/*
 * Magic values for the index field of the SortInfo structure. Note that the
 * index "end-1" will be translated to SORTIDX_END-1, etc.
 */

#define SORTIDX_NONE	-1	/* Not indexed; use whole value. */
#define SORTIDX_END	-2	/* Indexed from end. */

/*
 * Forward declarations for procedures defined in this file:
 */

static int		DictionaryCompare(char *left, char *right);
static SortElement *    MergeLists(SortElement *leftPtr, SortElement *rightPtr,
			    SortInfo *infoPtr);
static int		SortCompare(SortElement *firstPtr, SortElement *second,
			    SortInfo *infoPtr);

/*
 *----------------------------------------------------------------------
 *
 * MergeLists -
 *
 *	This procedure combines two sorted lists of SortElement structures
 *	into a single sorted list.
 *
 * Results:
 *	The unified list of SortElement structures.
 *
 * Side effects:
 *	If infoPtr->unique is set then infoPtr->numElements may be updated.
 *	Possibly others, if a user-defined comparison command does something
 *	weird.
 *
 * Note:
 *	If infoPtr->unique is set, the merge assumes that there are no
 *	"repeated" elements in each of the left and right lists. In that case,
 *	if any element of the left list is equivalent to one in the right list
 *	it is omitted from the merged list.
 *	This simplified mechanism works because of the special way
 *	our MergeSort creates the sublists to be merged and will fail to
 *	eliminate all repeats in the general case where they are already
 *	present in either the left or right list. A general code would need to
 *	skip adjacent initial repeats in the left and right lists before
 *	comparing their initial elements, at each step.
 *----------------------------------------------------------------------
 */

static SortElement *
MergeLists(
    SortElement *leftPtr,	/* First list to be merged; may be NULL. */
    SortElement *rightPtr,	/* Second list to be merged; may be NULL. */
    SortInfo *infoPtr)		/* Information needed by the comparison
				 * operator. */
{
    SortElement *headPtr, *tailPtr;
    int cmp;

    if (leftPtr == NULL) {
	return rightPtr;
    }
    if (rightPtr == NULL) {
	return leftPtr;
    }
    cmp = SortCompare(leftPtr, rightPtr, infoPtr);
    if (cmp > 0 || (cmp == 0 && infoPtr->unique)) {
	if (cmp == 0) {
	    infoPtr->numElements--;
	    leftPtr = leftPtr->nextPtr;
	}
	tailPtr = rightPtr;
	rightPtr = rightPtr->nextPtr;
    } else {
	tailPtr = leftPtr;
	leftPtr = leftPtr->nextPtr;
    }
    headPtr = tailPtr;
    if (!infoPtr->unique) {
	while ((leftPtr != NULL) && (rightPtr != NULL)) {
	    cmp = SortCompare(leftPtr, rightPtr, infoPtr);
	    if (cmp > 0) {
		tailPtr->nextPtr = rightPtr;
		tailPtr = rightPtr;
		rightPtr = rightPtr->nextPtr;
	    } else {
		tailPtr->nextPtr = leftPtr;
		tailPtr = leftPtr;
		leftPtr = leftPtr->nextPtr;
	    }
	}
    } else {
	while ((leftPtr != NULL) && (rightPtr != NULL)) {
	    cmp = SortCompare(leftPtr, rightPtr, infoPtr);
	    if (cmp >= 0) {
		if (cmp == 0) {
		    infoPtr->numElements--;
		    leftPtr = leftPtr->nextPtr;
		}
		tailPtr->nextPtr = rightPtr;
		tailPtr = rightPtr;
		rightPtr = rightPtr->nextPtr;
	    } else {
		tailPtr->nextPtr = leftPtr;
		tailPtr = leftPtr;
		leftPtr = leftPtr->nextPtr;
	    }
	}
    }
    if (leftPtr != NULL) {
	tailPtr->nextPtr = leftPtr;
    } else {
	tailPtr->nextPtr = rightPtr;
    }
    return headPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * SortCompare --
 *
 *	This procedure is invoked by MergeLists to determine the proper
 *	ordering between two elements.
 *
 * Results:
 *	A negative results means the the first element comes before the
 *	second, and a positive results means that the second element should
 *	come first. A result of zero means the two elements are equal and it
 *	doesn't matter which comes first.
 *
 * Side effects:
 *	None, unless a user-defined comparison command does something weird.
 *
 *----------------------------------------------------------------------
 */

static int
SortCompare(
    SortElement *elemPtr1, SortElement *elemPtr2,
				/* Values to be compared. */
    SortInfo *infoPtr)		/* Information passed from the top-level
				 * "lsort" command. */
{
    int order = 0;

    if (infoPtr->sortMode == SORTMODE_ASCII) {
	order = strcmp(elemPtr1->index.strValuePtr,
		elemPtr2->index.strValuePtr);
    } else if (infoPtr->sortMode == SORTMODE_ASCII_NC) {
	order = strcasecmp(elemPtr1->index.strValuePtr,
		elemPtr2->index.strValuePtr);
    } else if (infoPtr->sortMode == SORTMODE_DICTIONARY) {
	order = DictionaryCompare(elemPtr1->index.strValuePtr,
		elemPtr2->index.strValuePtr);
    } else if (infoPtr->sortMode == SORTMODE_INTEGER) {
	long a, b;

	a = elemPtr1->index.intValue;
	b = elemPtr2->index.intValue;
	order = ((a >= b) - (a <= b));
    } else if (infoPtr->sortMode == SORTMODE_REAL) {
	double a, b;

	a = elemPtr1->index.doubleValue;
	b = elemPtr2->index.doubleValue;
	order = ((a >= b) - (a <= b));
    } else {
	Tcl_Obj **objv, *paramObjv[2];
	int objc;
	Tcl_Obj *objPtr1, *objPtr2;

	if (infoPtr->resultCode != TCL_OK) {
	    /*
	     * Once an error has occurred, skip any future comparisons so as
	     * to preserve the error message in sortInterp->result.
	     */

	    return 0;
	}


	objPtr1 = elemPtr1->index.objValuePtr;
	objPtr2 = elemPtr2->index.objValuePtr;

	paramObjv[0] = objPtr1;
	paramObjv[1] = objPtr2;

	/*
	 * We made space in the command list for the two things to compare.
	 * Replace them and evaluate the result.
	 */

	Tcl_ListObjLength(infoPtr->interp, infoPtr->compareCmdPtr, &objc);
	Tcl_ListObjReplace(infoPtr->interp, infoPtr->compareCmdPtr, objc - 2,
		2, 2, paramObjv);
	Tcl_ListObjGetElements(infoPtr->interp, infoPtr->compareCmdPtr,
		&objc, &objv);

	infoPtr->resultCode = Tcl_EvalObjv(infoPtr->interp, objc, objv, 0);

	if (infoPtr->resultCode != TCL_OK) {
	    Tcl_AddErrorInfo(infoPtr->interp,
		    "\n    (-compare command)");
	    return 0;
	}

	/*
	 * Parse the result of the command.
	 */

	if (Tcl_GetIntFromObj(infoPtr->interp,
		Tcl_GetObjResult(infoPtr->interp), &order) != TCL_OK) {
	    Tcl_ResetResult(infoPtr->interp);
	    Tcl_AppendResult(infoPtr->interp,
		    "-compare command returned non-integer result", NULL);
	    infoPtr->resultCode = TCL_ERROR;
	    return 0;
	}
    }
    if (!infoPtr->isIncreasing) {
	order = -order;
    }
    return order;
}

/*
 *----------------------------------------------------------------------
 *
 * DictionaryCompare
 *
 *	This function compares two strings as if they were being used in an
 *	index or card catalog. The case of alphabetic characters is ignored,
 *	except to break ties. Thus "B" comes before "b" but after "a". Also,
 *	integers embedded in the strings compare in numerical order. In other
 *	words, "x10y" comes after "x9y", not * before it as it would when
 *	using strcmp().
 *
 * Results:
 *	A negative result means that the first element comes before the
 *	second, and a positive result means that the second element should
 *	come first. A result of zero means the two elements are equal and it
 *	doesn't matter which comes first.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
DictionaryCompare(
    char *left, char *right)	/* The strings to compare. */
{
    Tcl_UniChar uniLeft, uniRight, uniLeftLower, uniRightLower;
    int diff, zeros;
    int secondaryDiff = 0;

    while (1) {
	if (isdigit(UCHAR(*right))		/* INTL: digit */
		&& isdigit(UCHAR(*left))) {	/* INTL: digit */
	    /*
	     * There are decimal numbers embedded in the two strings. Compare
	     * them as numbers, rather than strings. If one number has more
	     * leading zeros than the other, the number with more leading
	     * zeros sorts later, but only as a secondary choice.
	     */

	    zeros = 0;
	    while ((*right == '0') && (isdigit(UCHAR(right[1])))) {
		right++;
		zeros--;
	    }
	    while ((*left == '0') && (isdigit(UCHAR(left[1])))) {
		left++;
		zeros++;
	    }
	    if (secondaryDiff == 0) {
		secondaryDiff = zeros;
	    }

	    /*
	     * The code below compares the numbers in the two strings without
	     * ever converting them to integers. It does this by first
	     * comparing the lengths of the numbers and then comparing the
	     * digit values.
	     */

	    diff = 0;
	    while (1) {
		if (diff == 0) {
		    diff = UCHAR(*left) - UCHAR(*right);
		}
		right++;
		left++;
		if (!isdigit(UCHAR(*right))) {		/* INTL: digit */
		    if (isdigit(UCHAR(*left))) {	/* INTL: digit */
			return 1;
		    } else {
			/*
			 * The two numbers have the same length. See if their
			 * values are different.
			 */

			if (diff != 0) {
			    return diff;
			}
			break;
		    }
		} else if (!isdigit(UCHAR(*left))) {	/* INTL: digit */
		    return -1;
		}
	    }
	    continue;
	}

	/*
	 * Convert character to Unicode for comparison purposes. If either
	 * string is at the terminating null, do a byte-wise comparison and
	 * bail out immediately.
	 */

	if ((*left != '\0') && (*right != '\0')) {
	    left += Tcl_UtfToUniChar(left, &uniLeft);
	    right += Tcl_UtfToUniChar(right, &uniRight);

	    /*
	     * Convert both chars to lower for the comparison, because
	     * dictionary sorts are case insensitve. Covert to lower, not
	     * upper, so chars between Z and a will sort before A (where most
	     * other interesting punctuations occur).
	     */

	    uniLeftLower = Tcl_UniCharToLower(uniLeft);
	    uniRightLower = Tcl_UniCharToLower(uniRight);
	} else {
	    diff = UCHAR(*left) - UCHAR(*right);
	    break;
	}

	diff = uniLeftLower - uniRightLower;
	if (diff) {
	    return diff;
	}
	if (secondaryDiff == 0) {
	    if (Tcl_UniCharIsUpper(uniLeft) && Tcl_UniCharIsLower(uniRight)) {
		secondaryDiff = -1;
	    } else if (Tcl_UniCharIsUpper(uniRight)
		    && Tcl_UniCharIsLower(uniLeft)) {
		secondaryDiff = 1;
	    }
	}
    }
    if (diff == 0) {
	diff = secondaryDiff;
    }
    return diff;
}


static int Odie_SortElement_FromObj(
  Tcl_Interp *interp,
  int sortMode,
  Tcl_Obj *valuePtr,
  SortElement *elementPtr
) {
  /*
   * Determine the "value" of this object for sorting purposes
   */
  if (sortMode == SORTMODE_ASCII) {
      elementPtr->index.strValuePtr = Tcl_GetString(valuePtr);
  } else if (sortMode == SORTMODE_INTEGER) {
      long a;

      if (Tcl_GetLongFromObj(interp, valuePtr, &a) != TCL_OK) {
        return TCL_ERROR;
      }
      elementPtr->index.intValue = a;
  } else if (sortMode == SORTMODE_REAL) {
      double a;

      if (Tcl_GetDoubleFromObj(interp, valuePtr, &a) != TCL_OK) {
        return TCL_ERROR;
      }
      elementPtr->index.doubleValue = a;
  } else {
      elementPtr->index.objValuePtr = valuePtr;
  }
  elementPtr->objPtr = valuePtr;
  return TCL_OK;
}

/*
** Converts a linked list of structures into
** a Tcl list object
*/

static Tcl_Obj *Odie_MergeList_ToObj(SortElement *elementPtr) {     
  SortElement *loopPtr;
  Tcl_Obj **newArray;
  int i,len=0;
  loopPtr=elementPtr;
  for (len=0; loopPtr != NULL ; loopPtr = loopPtr->nextPtr) {
    len++;
  }
  newArray = (Tcl_Obj **)Odie_Alloc(sizeof(Tcl_Obj *)*len);
  loopPtr=elementPtr;
  for (i=0; loopPtr != NULL ; loopPtr = loopPtr->nextPtr) {
      Tcl_Obj *objPtr = loopPtr->objPtr;
      newArray[i] = objPtr;
      i++;
      //Tcl_IncrRefCount(objPtr);
  }
  return Tcl_NewListObj(len,newArray);
}


STUB_EXPORT int Odie_Lsearch(int listLength,Tcl_Obj **listObjPtrs,Tcl_Obj *element) {
  int i;
  Tcl_Obj *o;
  if(element==NULL) {
    return -1;
  }

  int matchLen;
  char *match=Tcl_GetStringFromObj(element,&matchLen);

  int s2len,found;
  const char *s2;

  if(matchLen < 0) {
    return -1;
  }

  found = 0;
  for(i=0;i<listLength && !found;i++) {
    o=listObjPtrs[i];
    if (o != NULL) {
        s2 = Tcl_GetStringFromObj(o, &s2len);
    } else {
        s2 = "";
    }
    if (matchLen == s2len) {
      found = (strcmp(match, s2) == 0);
      if(found) {
        return i;
      }
    }
  }
  return -1;
}


STUB_EXPORT Tcl_Obj *Logicset_To_TclObj(Tcl_Obj *set) {
  if(set) {
    return Tcl_DuplicateObj(set);
  } else {
    return Tcl_NewObj();
  }
}
  
STUB_EXPORT void Logicset_LIST_RESET(Tcl_Obj **set) {
  if(*set) {
    Tcl_DecrRefCount(*set);
  }
  *set=Tcl_NewObj();
}

STUB_EXPORT int Logicset_LIST_CONTAINS(Tcl_Obj **aPtrs,int aLength,Tcl_Obj *element) {
  if(element==NULL) return 0;
  if(aLength<1) return 0;
  
  int matchIdx=Odie_Lsearch(aLength,aPtrs,element);
  if(matchIdx>=0) {
    return 1;
  }
  return 0;
}

STUB_EXPORT void Logicset_Sanitize_List(char *value,int len) {
  int i,skipped=0;
  for(i=0;i<len;i++) {
    unsigned char x=value[i];
    skipped++;
    /* Anything outside this range in non-printable, whitespace, or a delimeter */
    if(x<0x30 || x>0x80 || x==0x7D || x==0x7B) {
      value[i]=0x20;
      continue;
    }
    skipped--;
  }
}

STUB_EXPORT Tcl_Obj *Logicset_FromObj(Tcl_Obj *rawlist) {
  if(!rawlist) {
    return NULL;
  }
  Tcl_Obj *result;
  int len;
  char *rawvalue=Tcl_GetStringFromObj(rawlist,&len);
  if(len==0) {
    return NULL;
  }
  Logicset_Sanitize_List(rawvalue,len);
  Tcl_Obj *tempString=Tcl_NewStringObj(rawvalue,len);
  
  int listLength,i;
  Tcl_Obj **listObjPtrs;
  if(Tcl_ListObjGetElements(NULL, tempString, &listLength, &listObjPtrs)) {
    Tcl_DecrRefCount(tempString);
    return NULL;
  }
  if(listLength <= 0) {
    Tcl_DecrRefCount(tempString);
    return NULL;
  }
  Tcl_Obj *listObj=Tcl_NewObj();
  for(i=0;i<listLength;i++) {
    Logicset_Add(listObj,listObjPtrs[i]);
  }
  Tcl_DecrRefCount(tempString);
  return listObj;
}

STUB_EXPORT int Logicset_Add(Tcl_Obj *aset,Tcl_Obj *element) {
  int listLength;
  int i;
  Tcl_Obj **listObjPtrs;
  
  if(element==NULL) {
    return TCL_OK;
  }
  int matchLen;
  char *match=Tcl_GetStringFromObj(element,&matchLen);

  int s2len;
  const char *s2;

  if(matchLen < 0) {
    return TCL_ERROR;
  }
  if(Tcl_ListObjGetElements(NULL, aset, &listLength, &listObjPtrs)) {
    return TCL_ERROR;
  }
  /* Check that item isn't in list already */
  int found = 0;
  for(i=0;i<listLength && !found;i++) {
    Tcl_Obj *o=listObjPtrs[i];
    if (o != NULL) {
        s2 = Tcl_GetStringFromObj(o, &s2len);
    } else {
        s2 = "";
    }
    if (matchLen == s2len) {
      found = (strcmp(match, s2) == 0);
      if(found) {
        return TCL_OK;
      }
    }
  }
  for(i=0;i<listLength;i++) {
    int cmp = 0;
    Tcl_Obj *o=listObjPtrs[i];
    if (o != NULL) {
        s2 = Tcl_GetStringFromObj(o, &s2len);
    } else {
        s2 = "";
    }
    cmp = strcmp(match, s2);
    if(cmp==0) {
      return TCL_OK;
    } else if (cmp<0) {
      Tcl_Obj *NewVals[1];
      NewVals[0]=element;
      /* Add the new element here */
      return Tcl_ListObjReplace(NULL,aset,i,0,1,NewVals);
    }
  }
  Tcl_ListObjAppendElement(NULL,aset,element);
  return TCL_OK;
}
/*
** Add all elements of A and B together into a new set
*/
STUB_EXPORT void Logicset_Include(Tcl_Obj *aset,Tcl_Obj *bset) {
  int aLength;
  if(!aset || !bset) {
    return;
  }
  int bLength;
  Tcl_Obj **bPtrs;
  if(Tcl_ListObjGetElements(0, bset, &bLength, &bPtrs)) {
    return;
  }
  if(bLength==0) {
    return;
  }
  if(bLength>1) {
    int i;
    for(i=0;i<bLength;i++) {
      Logicset_Include(aset,bPtrs[i]);
    }
  } else {
    if(Logicset_Add(aset,bPtrs[0])) {
      return;
    }
  }
  return;
}

/*
** Returns 1 if all elements in B are contained in A
*/
STUB_EXPORT int Logicset_EXPR_AND(Tcl_Obj *aset,Tcl_Obj *bset) {
  int aLength;
  Tcl_Obj **aPtrs;
  if(!aset || !bset) return 0;
  if(Tcl_ListObjGetElements(0, aset, &aLength, &aPtrs)) {
    return 0;
  }
  int bLength;
  Tcl_Obj **bPtrs;
  if(Tcl_ListObjGetElements(0, bset, &bLength, &bPtrs)) {
    return 0;
  }
  int i;
  for(i=0;i<bLength;i++) {
    int found=Logicset_LIST_CONTAINS(aPtrs,aLength,bPtrs[i]);
    if(!found) {
      return 0;
    }
  }
  return 1;
}

STUB_EXPORT int Logicset_EXPR_OR(Tcl_Obj *aset,Tcl_Obj *bset) {
  int aLength;
  Tcl_Obj **aPtrs;
  if(!aset || !bset) return 0;
  if(Tcl_ListObjGetElements(0, aset, &aLength, &aPtrs)) {
    return 0;
  }
  int bLength;
  Tcl_Obj **bPtrs;
  if(Tcl_ListObjGetElements(0, bset, &bLength, &bPtrs)) {
    return 0;
  }
  int i;
  for(i=0;i<bLength;i++) {
    int found=Logicset_LIST_CONTAINS(aPtrs,aLength,bPtrs[i]);
    if(found) {
      return 1;
    }
  }
  return 0;
}


STUB_EXPORT Tcl_Obj *Logicset_PRODUCT_INTERSECT(Tcl_Obj *aset,Tcl_Obj *bset) {
  int aLength;
  Tcl_Obj **aPtrs;
  if(!aset) {
    return bset;
  }
  if(!bset) {
    return aset;
  }
  if(Tcl_ListObjGetElements(0, aset, &aLength, &aPtrs)) return bset;
  int bLength;
  Tcl_Obj **bPtrs;
  if(Tcl_ListObjGetElements(0, bset, &bLength, &bPtrs)) return aset;
  int i,resultlen=0;
  Tcl_Obj *result=Tcl_NewObj();
  for(i=0;i<bLength;i++) {
    int found=Logicset_LIST_CONTAINS(aPtrs,aLength,bPtrs[i]);
    if(found) {
      resultlen++;
      Logicset_Add(result,bPtrs[i]);
    }
  }
  if(!resultlen) {
    Tcl_DecrRefCount(result);
    return NULL;
  }
  return result;
}

STUB_EXPORT Tcl_Obj *Logicset_PRODUCT_UNION(Tcl_Obj *aset,Tcl_Obj *bset) {
  if(!aset) {
    return bset;
  }
  if(!bset) {
    return aset;
  }
  Tcl_Obj *listObj=Tcl_NewObj();
  Logicset_Include(listObj,aset);
  Logicset_Include(listObj,bset);
  return listObj;
}

STUB_EXPORT Tcl_Obj *Logicset_PRODUCT_XOR(Tcl_Obj *aset,Tcl_Obj *bset) {
  int aLength;
  Tcl_Obj **aPtrs;
  if(!aset) {
    return bset;
  }
  if(!bset) {
    return aset;
  }
  if(Tcl_ListObjGetElements(0, aset, &aLength, &aPtrs)) return bset;
  int bLength;
  Tcl_Obj **bPtrs;
  if(Tcl_ListObjGetElements(0, bset, &bLength, &bPtrs)) return aset;
  int i,resultlen=0;
  Tcl_Obj *result=Tcl_NewObj();
  for(i=0;i<bLength;i++) {
    int found=Logicset_LIST_CONTAINS(aPtrs,aLength,bPtrs[i]);
    if(!found) {
      resultlen++;
      Logicset_Add(result,bPtrs[i]);
    }
  }
  for(i=0;i<aLength;i++) {
    int found=Logicset_LIST_CONTAINS(bPtrs,bLength,aPtrs[i]);
    if(!found) {
      resultlen++;
      Logicset_Add(result,aPtrs[i]);
    }
  }
  if(!resultlen) {
    Tcl_DecrRefCount(result);
    return NULL;
  }
  Tcl_Obj *resultPtr=Odie_ListObj_Sort(result);
  if(resultPtr!=result) {
    Tcl_DecrRefCount(result);
  }
  return resultPtr;
}

STUB_EXPORT Tcl_Obj *Logicset_PRODUCT_MISSING(Tcl_Obj *aset,Tcl_Obj *bset) {
  int aLength;
  Tcl_Obj **aPtrs;
  if(!aset) {
    return NULL;
  }
  if(!bset) {
    return aset;
  }
  if(Tcl_ListObjGetElements(0, aset, &aLength, &aPtrs)) return bset;
  int bLength;
  Tcl_Obj **bPtrs;
  if(Tcl_ListObjGetElements(0, bset, &bLength, &bPtrs)) return aset;
  int i,resultlen=0;
  Tcl_Obj *result=Tcl_NewObj();
  for(i=0;i<aLength;i++) {
    int found=Logicset_LIST_CONTAINS(bPtrs,bLength,aPtrs[i]);
    if(!found) {
      resultlen++;
      Logicset_Add(result,aPtrs[i]);
    }
  }
  if(!resultlen) {
    Tcl_DecrRefCount(result);
    return NULL;
  }
  return result;
}

STUB_EXPORT Tcl_Obj *Odie_ListObj_Sort(Tcl_Obj *listObj) {
  Tcl_Obj *resultPtr=NULL;
  int i, j, length, sortMode;
  int idx;
  Tcl_Obj **listObjPtrs, *indexPtr;
  SortElement *elementArray, *elementPtr;
  SortInfo sortInfo;

  sortInfo.isIncreasing = 1;
  sortInfo.sortMode = SORTMODE_DICTIONARY;
  sortInfo.indexv = NULL;
  sortInfo.unique = 1;
  sortInfo.interp = NULL;
  sortInfo.resultCode = TCL_OK;

  /*
   * The subList array below holds pointers to temporary lists built during
   * the merge sort. Element i of the array holds a list of length 2**i.
   */
  #   define NUM_LISTS 30
  SortElement *subList[NUM_LISTS+1];


  sortInfo.resultCode = Tcl_ListObjGetElements(sortInfo.interp, listObj,
          &length, &listObjPtrs);

  if(length<2) {
    /*
    ** If the list is zero length, just return
    ** the original pointer
    */
    return listObj;
  }

  if (sortInfo.resultCode != TCL_OK || length <= 0) {
    goto done;
  }

  sortInfo.numElements = length;

  sortMode = sortInfo.sortMode;
  if ((sortMode == SORTMODE_ASCII_NC)
          || (sortMode == SORTMODE_DICTIONARY)) {
      /*
       * For this function's purpose all string-based modes are equivalent
       */
      sortMode = SORTMODE_ASCII;
  }

  /*
   * Initialize the sublists. After the following loop, subList[i] will
   * contain a sorted sublist of length 2**i. Use one extra subList at the
   * end, always at NULL, to indicate the end of the lists.
   */

  for (j=0 ; j<=NUM_LISTS ; j++) {
      subList[j] = NULL;
  }

  /*
   * The following loop creates a SortElement for each list element and
   * begins sorting it into the sublists as it appears.
   */

  elementArray = (SortElement *) Odie_Alloc( length * sizeof(SortElement));

  for (i=0; i < length; i++){
    idx = i;
    indexPtr = listObjPtrs[idx];
    sortInfo.resultCode=Odie_SortElement_FromObj(sortInfo.interp,sortMode,indexPtr,&elementArray[i]);
    if(sortInfo.resultCode!=TCL_OK) {
      goto done1;
    }
  }
  
  for (i=0; i < length; i++){
      /*
       * Merge this element in the pre-existing sublists (and merge together
       * sublists when we have two of the same size).
       */

      elementArray[i].nextPtr = NULL;
      elementPtr = &elementArray[i];
      for (j=0 ; subList[j] ; j++) {
          elementPtr = MergeLists(subList[j], elementPtr, &sortInfo);
          subList[j] = NULL;
      }
      if (j >= NUM_LISTS) {
          j = NUM_LISTS-1;
      }
      subList[j] = elementPtr;
  }

  /*
   * Merge all sublists
   */

  elementPtr = subList[0];
  for (j=1 ; j<NUM_LISTS ; j++) {
      elementPtr = MergeLists(subList[j], elementPtr, &sortInfo);
  }
  
  /*
   * Now store the sorted elements in the result list.
   */

  if (sortInfo.resultCode == TCL_OK) {
    resultPtr=Odie_MergeList_ToObj(elementPtr);

  }

  done1:
    Odie_Free((char *) elementArray);

  done:
    if (sortInfo.sortMode == SORTMODE_COMMAND) {
	Tcl_DecrRefCount(sortInfo.compareCmdPtr);
	Tcl_DecrRefCount(listObj);
	sortInfo.compareCmdPtr = NULL;
    }
    if (sortInfo.resultCode != TCL_OK) {
      return NULL;
    }
    return resultPtr;
}

static int logicset_method_union (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  Tcl_Obj *varPtr,*listObj,*resultPtr;
  int created=0;
  if(objc < 2) {
      Tcl_WrongNumArgs(interp, 1, objv, "varname element ...");
      return TCL_ERROR;
  }
  listObj=Tcl_ObjGetVar2(interp,objv[1],NULL,0);
  if(!listObj) {
    created=1;
    Tcl_ResetResult(interp);
    listObj=Tcl_NewObj();
  } else {
    if(Tcl_IsShared(listObj)) {
      created=1;
      listObj=Tcl_DuplicateObj(listObj);
    }
  }
  int i;
  for(i=2;i<objc;i++) {
    Logicset_Include(listObj,objv[i]);
  }
  Tcl_ObjSetVar2(interp,objv[1],NULL,listObj,0);
  Tcl_SetObjResult(interp,listObj);
  return TCL_OK;
}


static int logicset_method_create (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  Tcl_Obj *stringObj,*resultPtr;
  Tcl_DString parse;
  Tcl_DStringInit(&parse);
  if(objc < 2) {
    Tcl_SetObjResult(interp,Tcl_NewObj());
    return TCL_OK;
  }
  int i;
  for(i=1;i<objc;i++) {
    Tcl_DStringAppendElement(&parse,Tcl_GetString(objv[i]));
  }
  int len=Tcl_DStringLength(&parse);
  char *rawvalue=Tcl_DStringValue(&parse);
  Logicset_Sanitize_List(rawvalue,len);
  stringObj=Tcl_NewStringObj(rawvalue,len);
  Tcl_DStringFree(&parse);
  
  int listLength;
  Tcl_Obj **listObjPtrs;
  if(Tcl_ListObjGetElements(interp, stringObj, &listLength, &listObjPtrs)) {
    return TCL_ERROR;
  }
  Tcl_Obj *listObj=Tcl_NewObj();
  for(i=0;i<listLength;i++) {
    Logicset_Add(listObj,listObjPtrs[i]);
  }
  Tcl_DecrRefCount(stringObj);
  Tcl_SetObjResult(interp,listObj);
  return TCL_OK;
}


static int logicset_method_contains (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  int listLength, idx, match=1;
  Tcl_Obj **listObjPtrs;

  if (objc < 2) {
    Tcl_WrongNumArgs(interp, 1, objv, "varlist element ...");
    return TCL_ERROR;
  }
  if(Tcl_ListObjGetElements(interp, objv[1], &listLength, &listObjPtrs)) {
    return TCL_ERROR;
  }
  
  for(idx=2;idx<objc && match;idx++) {
    int matchIdx=Odie_Lsearch(listLength,listObjPtrs,objv[idx]);
    if(matchIdx < 0) {
      match=0;
      break;
    }
  }
  Tcl_SetObjResult(interp,Tcl_NewBooleanObj(match));
  return TCL_OK;
}

static int logicset_method_empty (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  int length;
  Tcl_Obj **data;
  
  if(objc != 2) {
      Tcl_WrongNumArgs(interp, 1, objv, "varlist");
  }
  /*
  ** Make sure we have well formed list
  */
  if(Tcl_ListObjGetElements(interp,objv[1],&length,&data)!=TCL_OK) {
    Tcl_ResetResult(interp);
    Tcl_SetObjResult(interp,Tcl_NewBooleanObj(0));
    return TCL_OK;
  }
  if(length) {
    Tcl_SetObjResult(interp,Tcl_NewBooleanObj(0));
  } else {
    Tcl_SetObjResult(interp,Tcl_NewBooleanObj(1));
  }
  return TCL_OK;
}


static int logicset_method_expr_and (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  if (objc != 3) {
    Tcl_WrongNumArgs(interp, 1, objv, "A B");
    return TCL_ERROR;
  }
  int match=Logicset_EXPR_AND(objv[1],objv[2]);
  Tcl_SetObjResult(interp,Tcl_NewBooleanObj(match));
  return TCL_OK;
}

static int logicset_method_expr_or (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  if (objc != 3) {
    Tcl_WrongNumArgs(interp, 1, objv, "A B");
    return TCL_ERROR;
  }
  int match=Logicset_EXPR_OR(objv[1],objv[2]);
  Tcl_SetObjResult(interp,Tcl_NewBooleanObj(match));
  return TCL_OK;
}

static int logicset_method_product_intersect (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  if (objc != 3) {
    Tcl_WrongNumArgs(interp, 1, objv, "A B");
    return TCL_ERROR;
  }
  Tcl_Obj *product=Logicset_PRODUCT_INTERSECT(objv[1],objv[2]);
  if(product) {
    Tcl_SetObjResult(interp,product);
  } else {
    Tcl_SetObjResult(interp,Tcl_NewObj());
  }
  return TCL_OK;
}

static int logicset_method_product_union (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  if (objc != 3) {
    Tcl_WrongNumArgs(interp, 1, objv, "A B");
    return TCL_ERROR;
  }
  Tcl_Obj *product=Logicset_PRODUCT_UNION(objv[1],objv[2]);
  if(product) {
    Tcl_SetObjResult(interp,product);
  } else {
    Tcl_SetObjResult(interp,Tcl_NewObj());
  }
  return TCL_OK;
}

static int logicset_method_product_xor (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  if (objc != 3) {
    Tcl_WrongNumArgs(interp, 1, objv, "A B");
    return TCL_ERROR;
  }
  Tcl_Obj *product=Logicset_PRODUCT_XOR(objv[1],objv[2]);
  if(product) {
    Tcl_SetObjResult(interp,product);
  } else {
    Tcl_SetObjResult(interp,Tcl_NewObj());
  }
  return TCL_OK;
}

static int logicset_method_product_missing (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  if (objc != 3) {
    Tcl_WrongNumArgs(interp, 1, objv, "A B");
    return TCL_ERROR;
  }
  Tcl_Obj *product=Logicset_PRODUCT_MISSING(objv[1],objv[2]);
  if(product) {
    Tcl_SetObjResult(interp,product);
  } else {
    Tcl_SetObjResult(interp,Tcl_NewObj());
  }
  return TCL_OK;
}

static int logicset_method_remove (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  int listLength, idx;
  Tcl_Obj *resultPtr,*listPtr;
  Tcl_Obj **listObjPtrs;

  if (objc < 2) {
    Tcl_WrongNumArgs(interp, 1, objv, "variable element ...");
    return TCL_ERROR;
  }
  listPtr=Tcl_ObjGetVar2(interp,objv[1],NULL,0);
  if(!listPtr) {
    Tcl_ResetResult(interp);
    listPtr=Tcl_NewObj();
  } else {
    listPtr=Tcl_DuplicateObj(listPtr);
  }
  
  if(Tcl_ListObjGetElements(interp, listPtr, &listLength, &listObjPtrs)) {
    return TCL_ERROR;
  }

  resultPtr=Tcl_NewObj();
  for(idx=0;idx<listLength;idx++) {
    int matchIdx=Odie_Lsearch((objc-2),(Tcl_Obj **)(objv+2),listObjPtrs[idx]);
    if(matchIdx < 0) {
      Logicset_Add(resultPtr,listObjPtrs[idx]);
    }
  }
  Tcl_ObjSetVar2(interp,objv[1],NULL,resultPtr,0);
  Tcl_SetObjResult(interp,resultPtr);
  return TCL_OK;
}


DLLEXPORT int Logicset_Init(Tcl_Interp *interp) {
  Tcl_Namespace *modPtr;
  modPtr=Tcl_FindNamespace(interp,"logicset",NULL,TCL_NAMESPACE_ONLY);
  if(!modPtr) {
    modPtr = Tcl_CreateNamespace(interp, "logicset", NULL, NULL);
  }
  
  Tcl_CreateObjCommand(interp,"::logicset::add",(Tcl_ObjCmdProc *)logicset_method_union,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::logicset::contains",(Tcl_ObjCmdProc *)logicset_method_contains,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::logicset::create",(Tcl_ObjCmdProc *)logicset_method_create,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::logicset::empty",(Tcl_ObjCmdProc *)logicset_method_empty,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::logicset::expr_and",(Tcl_ObjCmdProc *)logicset_method_expr_and,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::logicset::expr_or",(Tcl_ObjCmdProc *)logicset_method_expr_or,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::logicset::product_intersect",(Tcl_ObjCmdProc *)logicset_method_product_intersect,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::logicset::product_missing",(Tcl_ObjCmdProc *)logicset_method_product_missing,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::logicset::product_union",(Tcl_ObjCmdProc *)logicset_method_product_union,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::logicset::product_xor",(Tcl_ObjCmdProc *)logicset_method_product_xor,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::logicset::remove",(Tcl_ObjCmdProc *)logicset_method_remove,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::logicset::union",(Tcl_ObjCmdProc *)logicset_method_union,NULL,NULL);

  Tcl_CreateEnsemble(interp, modPtr->fullName, modPtr, TCL_ENSEMBLE_PREFIX);
  Tcl_Export(interp, modPtr, "[a-z]*", 1);
  return TCL_OK; 
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted cmodules/logicset/logicset.man.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
[comment {-*- tao -*-}]
[manpage_begin odielib::math n 2.0]
[keywords odielib]
[copyright {2000-2014 Sean Woods <yoda@etoyoc.com>}]
[moddesc {The Odielib Accellerated Math Module}]
[titledesc {The Odielib Accellerated Math Module}]
[category {Mathematics}]
[require odielib 2.0]
[description]

[para]

The [package logicset] package is included with [package odielib]. It contains
a series of C-accellerated routines for managing logical sets. 

[section COMMANDS]
[list_begin definitions]
[call [cmd affine2d::combine] [arg "transform"] [arg "transform"] [opt [arg "transform..."]]]
Accepts N 3x3 affine matrices, and returns a 3x3 matrix which is the combination of them all.

[call [cmd affine2d::rotation_from_angle] [arg "theta"] [opt [arg "units"]]]
Computes a 2d affine rotation (a 3x3 matrix) from an angle [arg theta].
[para]
Valid units r - radians (2pi = one turn), g - gradian (400 = one turn), d - degree (360 = 1 turn)
 
[call [cmd affine2d::rotation_from_normal] [arg "normalx"] [arg "normaly"]]
Computes a 2d affine rotation (a 3x3 matrix) from a directional normal, given
my %of travel in X and Y.



[list_end]
[section "REFERENCES"]


[section AUTHORS]
Sean Woods

[vset CATEGORY tao]
[include scripts/feedback.inc]

[manpage_end]
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




















































































Deleted cmodules/math/cthulhu.ini.
1
2
3
4
set here [file dirname [file normalize [info script]]]
::cthulhu::add_directory [file join $here generic] {
build-ignore-cfiles quaternion.c
}
<
<
<
<








Deleted cmodules/math/generic/affine2d.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#include "odieInt.h"

static int  affine2d_method_apply (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  Tcl_Obj *pResult=Tcl_NewObj();
  int i;
  double matA[6] = {0.0,0.0,0.0,0.0,0.0,0.0};

  if( objc < 4 ){
      Tcl_WrongNumArgs(interp, 1, objv, "matrix x1 y1 ?x2 y2?...");
      return TCL_ERROR;
  }   
  for(i=0;i<6;i++) {
      Tcl_Obj *temp;
      if(Tcl_ListObjIndex(interp, objv[1], i, &temp)) return TCL_ERROR;
      if(Tcl_GetDoubleFromObj(interp,temp,&matA[i])) return TCL_ERROR;
  }

  for(i=2;i<objc;i+=2) {
      double x,y,newx,newy;
      if(Tcl_GetDoubleFromObj(interp,objv[i],&x)) return TCL_ERROR;
      if(Tcl_GetDoubleFromObj(interp,objv[i+1],&y)) return TCL_ERROR;
      newx=matA[0]*x+matA[1]*y+matA[4];
      newy=matA[2]*x+matA[3]*y+matA[5];
      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(newx));
      Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(newy));
  }
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}


static int  affine2d_method_combine (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  int i,idx;
  Tcl_Obj *pResult=Tcl_NewObj();
  double matA[6] = {0.0,0.0,0.0,0.0,0.0,0.0};
  double matB[6] = {0.0,0.0,0.0,0.0,0.0,0.0};
  
  if( objc <  2){
    Tcl_WrongNumArgs(interp, 1, objv, "transformA transformB ?transformC?...");
    return TCL_ERROR;
  }
  
  for(i=0;i<6;i++) {
    Tcl_Obj *temp;
    if(Tcl_ListObjIndex(interp, objv[1], i, &temp)) return TCL_ERROR;
    if(Tcl_GetDoubleFromObj(interp,temp,&matA[i])) return TCL_ERROR;
  }
  for(idx=2;idx<objc;idx++) {
    for(i=0;i<6;i++) {
        Tcl_Obj *temp;
        if(Tcl_ListObjIndex(interp, objv[idx], i, &temp)) return TCL_ERROR;
        if(Tcl_GetDoubleFromObj(interp,temp,&matB[i])) return TCL_ERROR;
    }
    matA[0]=matA[0]*matB[0]+matA[2]*matB[2]; /* [expr {$a*$i+$c*$j}] */
    matA[1]=matA[1]*matB[0]+matA[3]*matB[2]; /* [expr {$b*$i+$d*$j}] */
    matA[2]=matA[0]*matB[2]+matA[2]*matB[3]; /* [expr {$a*$k+$c*$l}] */
    matA[3]=matA[1]*matB[2]+matA[3]*matB[3]; /* [expr {$b*$k+$d*$l}] */
    matA[4]=matA[4]*matB[0]+matA[5]*matB[1]+matB[4]; /* [expr {$e*$i+$f*$j+$m}] */
    matA[5]=matA[4]*matB[2]+matA[5]*matB[3]+matB[5]; /*  [expr {$e*$k+$f*$l+$n}]] */
  }
  for(i=0;i<6;i++) {
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(matA[i]));
  }
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

static int  affine2d_method_rotation_from_angle (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  Tcl_Obj *pResult=Tcl_NewObj();
  double angle;
  if( (objc != 2) && (objc != 3) ){
    Tcl_WrongNumArgs(interp, 1, objv, "angle ?units?");
    return TCL_ERROR;
  }
  if(Tcl_GetDoubleFromObj(interp,objv[1],&angle)) return TCL_ERROR;
  if(objc==3) {
    /* Scale by the unit */
    char *units;
    units=Tcl_GetString(objv[2]);
    if(units[0]=='d') {
        angle=angle/180.0*M_PI;
    } else if (units[0]=='g') {
        angle=angle/200.0*M_PI;
    } else if (units[0]=='r') {
    } else {
        Tcl_AppendResult(interp, "Unknown unit ", units, " use d[egrees], r[adians], or g[radients]. Radians are assumed",(char*)0);
        return TCL_ERROR;
    }
  } 
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cos(angle)));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(sin(angle)));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(-sin(angle)));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cos(angle)));
  Tcl_ListObjAppendElement(interp,pResult,ODIE_REAL_ZERO());
  Tcl_ListObjAppendElement(interp,pResult,ODIE_REAL_ZERO());
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

static int  affine2d_method_rotation_from_normal (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
 Tcl_Obj *pResult=Tcl_NewObj();
  double nx,ny,angle;
  
  if( objc != 3 ){
    Tcl_WrongNumArgs(interp, 1, objv, "nx ny");
    return TCL_ERROR;
  }
  if(Tcl_GetDoubleFromObj(interp,objv[1],&nx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&ny)) return TCL_ERROR;
  angle=atan2(ny,nx);
  
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cos(angle)));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(sin(angle)));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(-sin(angle)));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(cos(angle)));
  Tcl_ListObjAppendElement(interp,pResult,ODIE_REAL_ZERO());
  Tcl_ListObjAppendElement(interp,pResult,ODIE_REAL_ZERO());
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

DLLEXPORT int Affine2d_Init(Tcl_Interp *interp) {
  Tcl_Namespace *modPtr;
  modPtr=Tcl_FindNamespace(interp,"affine2d",NULL,TCL_NAMESPACE_ONLY);
  if(!modPtr) {
    modPtr = Tcl_CreateNamespace(interp, "affine2d", NULL, NULL);
  }

  Tcl_CreateObjCommand(interp,"::affine2d::apply",(Tcl_ObjCmdProc *)affine2d_method_apply,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::affine2d::combine",(Tcl_ObjCmdProc *)affine2d_method_combine,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::affine2d::rotation_from_angle",(Tcl_ObjCmdProc *)affine2d_method_rotation_from_angle,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::affine2d::rotation_from_normal",(Tcl_ObjCmdProc *)affine2d_method_rotation_from_normal,NULL,NULL);

  Tcl_CreateEnsemble(interp, modPtr->fullName, modPtr, TCL_ENSEMBLE_PREFIX);
  Tcl_Export(interp, modPtr, "[a-z]*", 1);
  return TCL_OK;
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


























































































































































































































































































































Deleted cmodules/math/generic/affine3d.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
#include "odieInt.h"

void affine_Copy(AFFINE A,AFFINE B)
{
  int i,j;
  for(i=0;i<4;i++)
    for(j=0;j<4;j++)
      B[i][j]=A[i][j];
}

/*
 * Affine Operations
 * Must be performed on a 4x4 matrix
 */

void affine_ZeroMatrix(AFFINE A)
{
  register int i,j;
  for (i=0;i<4;i++)
    for (j=0;j<4;j++)
      A[i][j]=0;

}

void affine_IdentityMatrix(AFFINE A)
{
  register int i;

  affine_ZeroMatrix(A);

  for (i=0;i<4;i++)
    A[i][i]=1;

}

void affine_Translate(VECTOR A,AFFINE B)
{
  affine_IdentityMatrix(B);
  B[0][3]=-A[0];
  B[1][3]=-A[1];
  B[2][3]=-A[2];
}

void affine_Scale(VECTOR A,AFFINE B)
{
  affine_ZeroMatrix(B);
  B[0][0]=A[X_IDX];
  B[1][1]=A[Y_IDX];
  B[2][2]=A[Z_IDX];
  B[3][3]=1.0;
}

void affine_RotateX(SCALER angle,AFFINE A)
{
  double c,s;
  c=cos(angle);
  s=sin(angle);

  affine_ZeroMatrix(A);

  A[0][0]=1.0;
  A[3][3]=1.0;

  A[1][1]=c;
  A[2][2]=c;
  A[1][2]=s;
  A[2][1]=0.0-s;
}

void affine_RotateY(SCALER angle,AFFINE A)
{
  double c,s;
  c=cos(angle);
  s=sin(angle);

  affine_ZeroMatrix(A);

  A[1][1]=1.0;
  A[3][3]=1.0;

  A[0][0]=c;
  A[2][2]=c;
  A[0][2]=0.0-s;
  A[2][0]=s;
}


void affine_RotateZ(SCALER angle,AFFINE A)
{
  double c,s;
  c=cos(angle);
  s=sin(angle);

  affine_ZeroMatrix(A);

  A[2][2]=1.0;
  A[3][3]=1.0;

  A[0][0]=c;
  A[1][1]=c;
  A[0][1]=s;
  A[1][0]=0.0-s;

}


void affine_Multiply(AFFINE A,AFFINE B,AFFINE R)
{
  int i,j,k;
  AFFINE temp_matrix;
  for (i=0;i<4;i++)
  {
    for (j=0;j<4;j++)
    {
      temp_matrix[i][j]=0.0;
      for (k=0;k<4;k++) temp_matrix[i][j]+=A[i][k]*B[k][j];
    }
  }
  affine_Copy(temp_matrix,R);
}


void affine_Rotate(VECTOR rotate,AFFINE R)
{
  AFFINE OP;
  
  affine_RotateX(rotate[X_IDX],R);
  affine_RotateY(rotate[Y_IDX],OP);

  affine_Multiply(OP,R,R);
  affine_RotateZ(rotate[Z_IDX],OP);
  affine_Multiply(OP,R,R);

}

void affine_ComputeTransform(VECTOR trans,VECTOR rotate,AFFINE R)
{
  AFFINE M1,M2,M3,M4,M5,M6,M7,M8,M9;
  //VECTOR scale = {1.0, 1.0, 1.0};
  //affine_Scale(scale,M1);

  affine_IdentityMatrix(M1);

  affine_RotateX(rotate[X_IDX],M2);
  affine_RotateY(rotate[Y_IDX],M3);
  affine_RotateZ(rotate[Z_IDX],M4);
  affine_Translate(trans,M5);

  affine_Multiply(M2,M1,M6);
  affine_Multiply(M3,M6,M7);
  affine_Multiply(M4,M7,M8);
  affine_Multiply(M5,M8,M9);
  affine_Copy(M9,R);
}

int affine_Inverse(AFFINE r, AFFINE m)
{
  double d00, d01, d02, d03;
  double d10, d11, d12, d13;
  double d20, d21, d22, d23;
  double d30, d31, d32, d33;
  double m00, m01, m02, m03;
  double m10, m11, m12, m13;
  double m20, m21, m22, m23;
  double m30, m31, m32, m33;
  double D;

  m00 = m[0][0];  m01 = m[0][1];  m02 = m[0][2];  m03 = m[0][3];
  m10 = m[1][0];  m11 = m[1][1];  m12 = m[1][2];  m13 = m[1][3];
  m20 = m[2][0];  m21 = m[2][1];  m22 = m[2][2];  m23 = m[2][3];
  m30 = m[3][0];  m31 = m[3][1];  m32 = m[3][2];  m33 = m[3][3];

  d00 = m11*m22*m33 + m12*m23*m31 + m13*m21*m32 - m31*m22*m13 - m32*m23*m11 - m33*m21*m12;
  d01 = m10*m22*m33 + m12*m23*m30 + m13*m20*m32 - m30*m22*m13 - m32*m23*m10 - m33*m20*m12;
  d02 = m10*m21*m33 + m11*m23*m30 + m13*m20*m31 - m30*m21*m13 - m31*m23*m10 - m33*m20*m11;
  d03 = m10*m21*m32 + m11*m22*m30 + m12*m20*m31 - m30*m21*m12 - m31*m22*m10 - m32*m20*m11;

  d10 = m01*m22*m33 + m02*m23*m31 + m03*m21*m32 - m31*m22*m03 - m32*m23*m01 - m33*m21*m02;
  d11 = m00*m22*m33 + m02*m23*m30 + m03*m20*m32 - m30*m22*m03 - m32*m23*m00 - m33*m20*m02;
  d12 = m00*m21*m33 + m01*m23*m30 + m03*m20*m31 - m30*m21*m03 - m31*m23*m00 - m33*m20*m01;
  d13 = m00*m21*m32 + m01*m22*m30 + m02*m20*m31 - m30*m21*m02 - m31*m22*m00 - m32*m20*m01;

  d20 = m01*m12*m33 + m02*m13*m31 + m03*m11*m32 - m31*m12*m03 - m32*m13*m01 - m33*m11*m02;
  d21 = m00*m12*m33 + m02*m13*m30 + m03*m10*m32 - m30*m12*m03 - m32*m13*m00 - m33*m10*m02;
  d22 = m00*m11*m33 + m01*m13*m30 + m03*m10*m31 - m30*m11*m03 - m31*m13*m00 - m33*m10*m01;
  d23 = m00*m11*m32 + m01*m12*m30 + m02*m10*m31 - m30*m11*m02 - m31*m12*m00 - m32*m10*m01;

  d30 = m01*m12*m23 + m02*m13*m21 + m03*m11*m22 - m21*m12*m03 - m22*m13*m01 - m23*m11*m02;
  d31 = m00*m12*m23 + m02*m13*m20 + m03*m10*m22 - m20*m12*m03 - m22*m13*m00 - m23*m10*m02;
  d32 = m00*m11*m23 + m01*m13*m20 + m03*m10*m21 - m20*m11*m03 - m21*m13*m00 - m23*m10*m01;
  d33 = m00*m11*m22 + m01*m12*m20 + m02*m10*m21 - m20*m11*m02 - m21*m12*m00 - m22*m10*m01;

  D = m00*d00 - m01*d01 + m02*d02 - m03*d03;

  
  if (D == 0.0)
  {
    /* MatStack_Error("Singular matrix in MInvers."); */
    return TCL_ERROR;
  }

  r[0][0] =  d00/D; r[0][1] = -d10/D;  r[0][2] =  d20/D; r[0][3] = -d30/D;
  r[1][0] = -d01/D; r[1][1] =  d11/D;  r[1][2] = -d21/D; r[1][3] =  d31/D;
  r[2][0] =  d02/D; r[2][1] = -d12/D;  r[2][2] =  d22/D; r[2][3] = -d32/D;
  r[3][0] = -d03/D; r[3][1] =  d13/D;  r[3][2] = -d23/D; r[3][3] =  d33/D;
  return TCL_OK;
}

/*
** description: Pushes an affine identity matrix onto the stack
*/
static int  affine3d_method_identity (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *C;
  C=Matrix_NewObj();
  
  Matrix_Alloc(C,MATFORM_affine);
  affine_IdentityMatrix(C->matrix);
  Matrix_Dump(C);
  Tcl_SetObjResult(interp,Matrix_To_TclObj(C));
  return TCL_OK;
}

/*
** description:
** Multiply 2 4x4 matrices. Used to combine 2 affine transformations.
** Note: Some affine transformations need to be performed in a particular
** order to make sense.
*/
static int  affine3d_method_multiply (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A,*B,*C;
  int i;
  int size_a;
  int size_b;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A B" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_affine);
  if(!A) return TCL_ERROR;

  B=Odie_GetMatrixFromTclObj(interp,objv[2],MATFORM_affine);
  if(!B) return TCL_ERROR;

  C=Matrix_NewObj();
  Matrix_Alloc(C,MATFORM_affine);
  affine_Multiply(A->matrix,B->matrix,C->matrix);
  Tcl_SetObjResult(interp,Matrix_To_TclObj(C));
  return TCL_OK;
}

/*
** description:
** Convert a rotation vector (X Y Z) into an affine transformation
*/
static int  affine3d_method_from_euler (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A,*C;
  int i;
  int size_a;
  int size_b;
  if(objc != 2) {
    Tcl_WrongNumArgs( interp, 1, objv, "EULER" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_euler);
  if(!A) return TCL_ERROR;

  C=Matrix_NewObj();
  Matrix_Alloc(C,MATFORM_affine);
  affine_Rotate(A->matrix,C->matrix);
  Tcl_SetObjResult(interp,Matrix_To_TclObj(C));
  return TCL_OK;
}

/*
** description:
** Convert a scale vector (X Y Z) into an affine transformation
*/
static int  affine3d_method_scale (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A,*C;
  int i;
  int size_a;
  int size_b;
  if(objc != 2) {
    Tcl_WrongNumArgs( interp, 1, objv, "VECTORXYZ" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_vectorxyz);
  if(!A) return TCL_ERROR;

  C=Matrix_NewObj();
  Matrix_Alloc(C,MATFORM_affine);
  affine_Scale(A->matrix,C->matrix);
  Tcl_SetObjResult(interp,Matrix_To_TclObj(C));
  return TCL_OK;
}

/*
** description:
** Convert a displacement vector (X Y Z) into an affine transformation
*/
static int  affine3d_method_translation (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A,*C;
  int i;
  int size_a;
  int size_b;
  if(objc != 2) {
    Tcl_WrongNumArgs( interp, 1, objv, "VECTORXYZ" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_vectorxyz);
  if(!A) return TCL_ERROR;

  C=Matrix_NewObj();
  Matrix_Alloc(C,MATFORM_affine);
  affine_Translate(A->matrix,C->matrix);
  Tcl_SetObjResult(interp,Matrix_To_TclObj(C));
  return TCL_OK;
}

DLLEXPORT int Affine3d_Init(Tcl_Interp *interp) {
  Tcl_Namespace *modPtr;

  modPtr=Tcl_FindNamespace(interp,"affine3d",NULL,TCL_NAMESPACE_ONLY);
  if(!modPtr) {
    modPtr = Tcl_CreateNamespace(interp, "affine3d", NULL, NULL);
  }
  
  Tcl_CreateObjCommand(interp,"::affine3d::from_euler",(Tcl_ObjCmdProc *)affine3d_method_from_euler,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::affine3d::identity",(Tcl_ObjCmdProc *)affine3d_method_identity,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::affine3d::multiply",(Tcl_ObjCmdProc *)affine3d_method_multiply,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::affine3d::scale",(Tcl_ObjCmdProc *)affine3d_method_scale,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::affine3d::translation",(Tcl_ObjCmdProc *)affine3d_method_translation,NULL,NULL);

  Tcl_CreateEnsemble(interp, modPtr->fullName, modPtr, TCL_ENSEMBLE_PREFIX);
  Tcl_Export(interp, modPtr, "[a-z]*", 1);
  
  return TCL_OK;
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<














































































































































































































































































































































































































































































































































































































































































































































Deleted cmodules/math/generic/cmatrix.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include "odieInt.h"



void vector_Scale(VECTOR A,SCALER S)
{
  A[X_IDX]*=S;
  A[Y_IDX]*=S;
  A[Z_IDX]*=S;
}

void odiemath_cartesian_to_spherical(VECTOR A,VECTOR R)
{
  double S;
  /* Work with a copy in case we are writing back to the same pointer */
  double radius,theta,phi;
  radius=odiemath_vectorxyz_length(A);
  S=sqrt(A[X_IDX]*A[X_IDX]+A[Y_IDX]*A[Y_IDX]);
  if (A[X_IDX] > 0.0) {
    theta =asin(A[Y_IDX]/S);
  } else {
    theta =M_PI - asin(A[Y_IDX]/S);
  }
  phi    =asin(A[Z_IDX]/R[RADIUS]);
  
  R[RADIUS]=radius;
  R[THETA]=theta;
  R[PHI]=phi;
}


void odiemath_spherical_to_cartesian(VECTOR A,VECTOR R)
{
  /*
  ** Make a copy of the input matrix in case we are outputing back
  ** to the same pointer
  */
  double radius,theta,phi;
  radius=A[RADIUS];
  theta=A[THETA];
  phi=A[PHI];
  R[X_IDX]=radius*cos(theta)*cos(phi);
  R[Y_IDX]=radius*sin(theta)*cos(phi);
  R[Z_IDX]=radius*sin(phi);
}

void odiemath_cylindrical_to_cartesian(VECTOR A,VECTOR R)
{
  /*
  ** Make a copy of the input matrix in case we are outputing back
  ** to the same pointer
  */
  double radius,theta,z;
  radius=A[RADIUS];
  theta=A[THETA];
  z=A[Z_IDX];
  R[X_IDX]=radius*cos(theta);
  R[Y_IDX]=radius*sin(theta);
  R[Z_IDX]=z;
}

void odiemath_cartesian_to_cylindrical(VECTOR A,VECTOR R)
{
  /*
  ** Make a copy of the input matrix in case we are outputing back
  ** to the same pointer
  */
  double x,y,z;
  x=A[X_IDX];
  y=A[Y_IDX];
  z=A[Z_IDX];
  R[RADIUS]=sqrt(x*x + y*y);
  R[THETA] =atan2(y,x);
  R[Z_IDX] =z;
}

void odiemath_polar_to_vec2(VECTORXY A,VECTORXY R) {
  /*
  ** Make a copy of the input matrix in case we are outputing back
  ** to the same pointer
  */
  double radius,theta;
  radius=A[RADIUS];
  theta=A[THETA];
  R[X_IDX]=radius*cos(theta);
  R[Y_IDX]=radius*sin(theta);
}

void odiemath_vec2_to_polar(VECTORXY A,VECTORXY R)
{
  double x,y;
  x=A[X_IDX];
  y=A[Y_IDX];
  R[RADIUS]=sqrt(x*x + y*y);
  R[THETA] =atan2(y,x);
}

void Matrix_Dump(MATOBJ *A) 
{
  int i,j,idx=0;
  //printf("\nRows: %d Cols %d",A->rows,A->cols);
  for (i=0;i<4;i++)
  {
    //printf("\nRow %d:",i);
    for (j=0;j<4;j++)
    {
      //printf(" %f ",*(A->matrix+idx));
      idx++;
    }
    //printf("\n");
  }
  //printf("\n");
}

/*
 * Tcl List Utilities
 */





<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




















































































































































































































































Deleted cmodules/math/generic/cmatrixforms.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
/*
** This file is automatically generated by the TclVexpr.tcl
** script located in the same directory
*/

#include "odieInt.h"

/*
 * Module-Wide Variables
 */


const MatrixForm MatrixForms[] = {
{ MATFORM_null, "null", 0 , 0, "A matrix of arbitrary size", NULL },
{ MATFORM_affine, "affine", 4, 4, "A 4x4 affine matrix", Matrix_To_affine },
{ MATFORM_cylindrical, "cylindrical", 3, 1, "A 3 dimensional vector: RADIUS THETA Z", Matrix_To_cylindrical },
{ MATFORM_euler, "euler", 3, 1, "A 3 dimensional rotation: X Y Z", NULL },
{ MATFORM_heading, "heading", 3, 1, "A 3 dimensional rotation: yaw pitch roll", NULL },
{ MATFORM_mat2, "mat2", 2, 2, "A 2x2 matrix", NULL },
{ MATFORM_mat3, "mat3", 3, 3, "A 3x3 matrix", NULL },
{ MATFORM_mat4, "mat4", 4, 4, "A 4x4 matrix", NULL },
{ MATFORM_polar, "polar", 2, 1, "A 2 dimensional vector: RADIUS THETA", Matrix_To_cylindrical },
{ MATFORM_scaler, "scaler", 1, 1, "A scaler (1x1)", NULL },
{ MATFORM_spherical, "spherical", 3, 1, "A 3 dimensional vector: RADIUS THETA PHI", Matrix_To_cylindrical },
{ MATFORM_vector_xy, "vector_xy", 2, 1, "A 2 dimensional vector: X Y", Matrix_To_cartesian },
{ MATFORM_vector_xyz, "vector_xyz", 3, 1, "A 3 dimensional vector: X Y Z", Matrix_To_cartesian },
{ MATFORM_vector_xyzw, "vector_xyzw", 4, 1, "A 4 dimensional vector: X Y Z W", Matrix_To_cartesian }
};

STUB_EXPORT int Odie_Get_AFFINE_FromObj(Tcl_Interp *interp,Tcl_Obj *objPtr,AFFINE *ptr) {
  MATOBJ *T=Odie_GetMatrixFromTclObj(interp,objPtr,MATFORM_affine);
  if(!T) return TCL_ERROR;
  ptr=T->matrix;
  return TCL_OK;
}

STUB_EXPORT int Odie_Set_AFFINE_FromObj(Tcl_Interp *interp,Tcl_Obj *objPtr,AFFINE ptr) {
  MATOBJ *T=Odie_GetMatrixFromTclObj(interp,objPtr,MATFORM_affine);
  if(!T) return TCL_ERROR;
  memcpy(ptr,T->matrix,sizeof(AFFINE));
  return TCL_OK;
}

STUB_EXPORT Tcl_Obj *Odie_New_AFFINE_Obj(AFFINE ptr) {
  MATOBJ *C;
  Tcl_Obj *result;

  C=Matrix_NewObj();
  Matrix_Alloc(C,MATFORM_affine);
  memcpy(C->matrix,ptr,sizeof(AFFINE));
  result=Matrix_To_TclObj(C);
  return result;
}


static int  matrix_method_to_affine (
  ClientData dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A;
  int i;
  int size_a;
  int size_b;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_affine);
  if(!A) return TCL_ERROR;
  Tcl_SetObjResult(interp,Matrix_To_TclObj(A));
  return TCL_OK;
}
  

static int  matrix_method_to_cylindrical (
  ClientData dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A;
  int i;
  int size_a;
  int size_b;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_cylindrical);
  if(!A) return TCL_ERROR;
  Tcl_SetObjResult(interp,Matrix_To_TclObj(A));
  return TCL_OK;
}
  

static int  matrix_method_to_euler (
  ClientData dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A;
  int i;
  int size_a;
  int size_b;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_euler);
  if(!A) return TCL_ERROR;
  Tcl_SetObjResult(interp,Matrix_To_TclObj(A));
  return TCL_OK;
}
  

static int  matrix_method_to_heading (
  ClientData dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A;
  int i;
  int size_a;
  int size_b;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_heading);
  if(!A) return TCL_ERROR;
  Tcl_SetObjResult(interp,Matrix_To_TclObj(A));
  return TCL_OK;
}
  

static int  matrix_method_to_mat2 (
  ClientData dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A;
  int i;
  int size_a;
  int size_b;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_mat2);
  if(!A) return TCL_ERROR;
  Tcl_SetObjResult(interp,Matrix_To_TclObj(A));
  return TCL_OK;
}
  

STUB_EXPORT int Odie_Get_AFFINE3X3_FromObj(Tcl_Interp *interp,Tcl_Obj *objPtr,AFFINE3X3 *ptr) {
  MATOBJ *T=Odie_GetMatrixFromTclObj(interp,objPtr,MATFORM_mat3);
  if(!T) return TCL_ERROR;
  ptr=T->matrix;
  return TCL_OK;
}

STUB_EXPORT int Odie_Set_AFFINE3X3_FromObj(Tcl_Interp *interp,Tcl_Obj *objPtr,AFFINE3X3 ptr) {
  MATOBJ *T=Odie_GetMatrixFromTclObj(interp,objPtr,MATFORM_mat3);
  if(!T) return TCL_ERROR;
  memcpy(ptr,T->matrix,sizeof(AFFINE3X3));
  return TCL_OK;
}

STUB_EXPORT Tcl_Obj *Odie_New_AFFINE3X3_Obj(AFFINE3X3 ptr) {
  MATOBJ *C;
  Tcl_Obj *result;

  C=Matrix_NewObj();
  Matrix_Alloc(C,MATFORM_mat3);
  memcpy(C->matrix,ptr,sizeof(AFFINE3X3));
  result=Matrix_To_TclObj(C);
  return result;
}


static int  matrix_method_to_mat3 (
  ClientData dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A;
  int i;
  int size_a;
  int size_b;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_mat3);
  if(!A) return TCL_ERROR;
  Tcl_SetObjResult(interp,Matrix_To_TclObj(A));
  return TCL_OK;
}
  

static int  matrix_method_to_mat4 (
  ClientData dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A;
  int i;
  int size_a;
  int size_b;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_mat4);
  if(!A) return TCL_ERROR;
  Tcl_SetObjResult(interp,Matrix_To_TclObj(A));
  return TCL_OK;
}
  

static int  matrix_method_to_null (
  ClientData dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A;
  int i;
  int size_a;
  int size_b;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_null);
  if(!A) return TCL_ERROR;
  Tcl_SetObjResult(interp,Matrix_To_TclObj(A));
  return TCL_OK;
}
  

static int  matrix_method_to_polar (
  ClientData dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A;
  int i;
  int size_a;
  int size_b;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_polar);
  if(!A) return TCL_ERROR;
  Tcl_SetObjResult(interp,Matrix_To_TclObj(A));
  return TCL_OK;
}
  

static int  matrix_method_to_scaler (
  ClientData dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A;
  int i;
  int size_a;
  int size_b;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_scaler);
  if(!A) return TCL_ERROR;
  Tcl_SetObjResult(interp,Matrix_To_TclObj(A));
  return TCL_OK;
}
  

static int  matrix_method_to_spherical (
  ClientData dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A;
  int i;
  int size_a;
  int size_b;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_spherical);
  if(!A) return TCL_ERROR;
  Tcl_SetObjResult(interp,Matrix_To_TclObj(A));
  return TCL_OK;
}
  

STUB_EXPORT int Odie_Get_VECTORXY_FromObj(Tcl_Interp *interp,Tcl_Obj *objPtr,VECTORXY *ptr) {
  MATOBJ *T=Odie_GetMatrixFromTclObj(interp,objPtr,MATFORM_vector_xy);
  if(!T) return TCL_ERROR;
  ptr=T->matrix;
  return TCL_OK;
}

STUB_EXPORT int Odie_Set_VECTORXY_FromObj(Tcl_Interp *interp,Tcl_Obj *objPtr,VECTORXY ptr) {
  MATOBJ *T=Odie_GetMatrixFromTclObj(interp,objPtr,MATFORM_vector_xy);
  if(!T) return TCL_ERROR;
  memcpy(ptr,T->matrix,sizeof(VECTORXY));
  return TCL_OK;
}

STUB_EXPORT Tcl_Obj *Odie_New_VECTORXY_Obj(VECTORXY ptr) {
  MATOBJ *C;
  Tcl_Obj *result;

  C=Matrix_NewObj();
  Matrix_Alloc(C,MATFORM_vector_xy);
  memcpy(C->matrix,ptr,sizeof(VECTORXY));
  result=Matrix_To_TclObj(C);
  return result;
}


static int  matrix_method_to_vector_xy (
  ClientData dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A;
  int i;
  int size_a;
  int size_b;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_vector_xy);
  if(!A) return TCL_ERROR;
  Tcl_SetObjResult(interp,Matrix_To_TclObj(A));
  return TCL_OK;
}
  

STUB_EXPORT int Odie_Get_VECTORXYZ_FromObj(Tcl_Interp *interp,Tcl_Obj *objPtr,VECTORXYZ *ptr) {
  MATOBJ *T=Odie_GetMatrixFromTclObj(interp,objPtr,MATFORM_vector_xyz);
  if(!T) return TCL_ERROR;
  ptr=T->matrix;
  return TCL_OK;
}

STUB_EXPORT int Odie_Set_VECTORXYZ_FromObj(Tcl_Interp *interp,Tcl_Obj *objPtr,VECTORXYZ ptr) {
  MATOBJ *T=Odie_GetMatrixFromTclObj(interp,objPtr,MATFORM_vector_xyz);
  if(!T) return TCL_ERROR;
  memcpy(ptr,T->matrix,sizeof(VECTORXYZ));
  return TCL_OK;
}

STUB_EXPORT Tcl_Obj *Odie_New_VECTORXYZ_Obj(VECTORXYZ ptr) {
  MATOBJ *C;
  Tcl_Obj *result;

  C=Matrix_NewObj();
  Matrix_Alloc(C,MATFORM_vector_xyz);
  memcpy(C->matrix,ptr,sizeof(VECTORXYZ));
  result=Matrix_To_TclObj(C);
  return result;
}


static int  matrix_method_to_vector_xyz (
  ClientData dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A;
  int i;
  int size_a;
  int size_b;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_vector_xyz);
  if(!A) return TCL_ERROR;
  Tcl_SetObjResult(interp,Matrix_To_TclObj(A));
  return TCL_OK;
}
  

static int  matrix_method_to_vector_xyzw (
  ClientData dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A;
  int i;
  int size_a;
  int size_b;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_vector_xyzw);
  if(!A) return TCL_ERROR;
  Tcl_SetObjResult(interp,Matrix_To_TclObj(A));
  return TCL_OK;
}
  
DLLEXPORT int Odie_MatrixForms_Init(Tcl_Interp *interp) {
  Tcl_Namespace *modPtr;


  modPtr=Tcl_FindNamespace(interp,"matrix",NULL,TCL_NAMESPACE_ONLY);
  if(!modPtr) {
    modPtr = Tcl_CreateNamespace(interp, "matrix", NULL, NULL);
  }
  
  Tcl_CreateObjCommand(interp,"::matrix::to_affine",(Tcl_ObjCmdProc *)matrix_method_to_affine,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::matrix::to_cylindrical",(Tcl_ObjCmdProc *)matrix_method_to_cylindrical,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::matrix::to_euler",(Tcl_ObjCmdProc *)matrix_method_to_euler,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::matrix::to_heading",(Tcl_ObjCmdProc *)matrix_method_to_heading,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::matrix::to_mat2",(Tcl_ObjCmdProc *)matrix_method_to_mat2,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::matrix::to_mat3",(Tcl_ObjCmdProc *)matrix_method_to_mat3,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::matrix::to_mat4",(Tcl_ObjCmdProc *)matrix_method_to_mat4,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::matrix::to_null",(Tcl_ObjCmdProc *)matrix_method_to_null,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::matrix::to_polar",(Tcl_ObjCmdProc *)matrix_method_to_polar,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::matrix::to_scaler",(Tcl_ObjCmdProc *)matrix_method_to_scaler,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::matrix::to_spherical",(Tcl_ObjCmdProc *)matrix_method_to_spherical,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::matrix::to_vector_xy",(Tcl_ObjCmdProc *)matrix_method_to_vector_xy,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::matrix::to_vector_xyz",(Tcl_ObjCmdProc *)matrix_method_to_vector_xyz,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::matrix::to_vector_xyzw",(Tcl_ObjCmdProc *)matrix_method_to_vector_xyzw,NULL,NULL);
  Tcl_Obj *varname=Tcl_NewStringObj("math_const",-1);
  Tcl_IncrRefCount(varname);
  Tcl_ObjSetVar2(interp,varname,Tcl_NewStringObj("m_2_x_pi",-1),Tcl_NewDoubleObj(M_2_X_PI),TCL_GLOBAL_ONLY);
  Tcl_ObjSetVar2(interp,varname,Tcl_NewStringObj("m_pi_180",-1),Tcl_NewDoubleObj(M_PI_180),TCL_GLOBAL_ONLY);
  Tcl_ObjSetVar2(interp,varname,Tcl_NewStringObj("m_pi_360",-1),Tcl_NewDoubleObj(M_PI_360),TCL_GLOBAL_ONLY);
  Tcl_ObjSetVar2(interp,varname,Tcl_NewStringObj("m_e",-1),Tcl_NewDoubleObj(M_E),TCL_GLOBAL_ONLY);
  Tcl_ObjSetVar2(interp,varname,Tcl_NewStringObj("m_log2e",-1),Tcl_NewDoubleObj(M_LOG2E),TCL_GLOBAL_ONLY);
  Tcl_ObjSetVar2(interp,varname,Tcl_NewStringObj("m_log10e",-1),Tcl_NewDoubleObj(M_LOG10E),TCL_GLOBAL_ONLY);
  Tcl_ObjSetVar2(interp,varname,Tcl_NewStringObj("m_ln2",-1),Tcl_NewDoubleObj(M_LN2),TCL_GLOBAL_ONLY);
  Tcl_ObjSetVar2(interp,varname,Tcl_NewStringObj("m_ln10",-1),Tcl_NewDoubleObj(M_LN10),TCL_GLOBAL_ONLY);
  Tcl_ObjSetVar2(interp,varname,Tcl_NewStringObj("m_pi",-1),Tcl_NewDoubleObj(M_PI),TCL_GLOBAL_ONLY);
  Tcl_ObjSetVar2(interp,varname,Tcl_NewStringObj("m_pi_2",-1),Tcl_NewDoubleObj(M_PI_2),TCL_GLOBAL_ONLY);
  Tcl_ObjSetVar2(interp,varname,Tcl_NewStringObj("m_pi_4",-1),Tcl_NewDoubleObj(M_PI_4),TCL_GLOBAL_ONLY);
  Tcl_ObjSetVar2(interp,varname,Tcl_NewStringObj("m_1_pi",-1),Tcl_NewDoubleObj(M_1_PI),TCL_GLOBAL_ONLY);
  Tcl_ObjSetVar2(interp,varname,Tcl_NewStringObj("m_2_pi",-1),Tcl_NewDoubleObj(M_2_PI),TCL_GLOBAL_ONLY);
  Tcl_ObjSetVar2(interp,varname,Tcl_NewStringObj("m_2_sqrtpi",-1),Tcl_NewDoubleObj(M_2_SQRTPI),TCL_GLOBAL_ONLY);
  Tcl_ObjSetVar2(interp,varname,Tcl_NewStringObj("m_sqrt2",-1),Tcl_NewDoubleObj(M_SQRT2),TCL_GLOBAL_ONLY);
  Tcl_ObjSetVar2(interp,varname,Tcl_NewStringObj("m_sqrt1_2",-1),Tcl_NewDoubleObj(M_SQRT1_2),TCL_GLOBAL_ONLY);
  Tcl_ObjSetVar2(interp,varname,Tcl_NewStringObj("m_sqrt3",-1),Tcl_NewDoubleObj(M_SQRT3),TCL_GLOBAL_ONLY);
  Tcl_ObjSetVar2(interp,varname,Tcl_NewStringObj("m_sqrt3_2",-1),Tcl_NewDoubleObj(M_SQRT3_2),TCL_GLOBAL_ONLY);
Tcl_DecrRefCount(varname);

  return TCL_OK;
}

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
























































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted cmodules/math/generic/cmatrixforms.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*
** This file is automatically generated by the cmatrixforms.tcl
** script located in the same directory
*/

/* Constants */

#ifndef M_2_X_PI
#define M_2_X_PI 6.283185307179586476925286766560 /* 2 * PI */
#endif
#ifndef M_PI_180
#define M_PI_180 0.01745329251994329576 /* pi / 180 */
#endif
#ifndef M_PI_360
#define M_PI_360 0.00872664625997164788 /* pi / 360 */
#endif
#ifndef M_E
#define M_E 2.71828182845904523536028747135266250 /* e */
#endif
#ifndef M_LOG2E
#define M_LOG2E 1.44269504088896340735992468100189214 /* log2(e) */
#endif
#ifndef M_LOG10E
#define M_LOG10E 0.434294481903251827651128918916605082 /* log10(e) */
#endif
#ifndef M_LN2
#define M_LN2 0.693147180559945309417232121458176568 /* loge(2) */
#endif
#ifndef M_LN10
#define M_LN10 2.30258509299404568401799145468436421 /* loge(10) */
#endif
#ifndef M_PI
#define M_PI 3.14159265358979323846264338327950288 /* pi */
#endif
#ifndef M_PI_2
#define M_PI_2 1.57079632679489661923132169163975144 /* pi/2 */
#endif
#ifndef M_PI_4
#define M_PI_4 0.785398163397448309615660845819875721 /* pi/4 */
#endif
#ifndef M_1_PI
#define M_1_PI 0.318309886183790671537767526745028724 /* 1/pi */
#endif
#ifndef M_2_PI
#define M_2_PI 0.636619772367581343075535053490057448 /* 2/pi */
#endif
#ifndef M_2_SQRTPI
#define M_2_SQRTPI 1.12837916709551257389615890312154517 /* 2/sqrt(pi) */
#endif
#ifndef M_SQRT2
#define M_SQRT2 1.41421356237309504880168872420969808 /* sqrt(2) */
#endif
#ifndef M_SQRT1_2
#define M_SQRT1_2 0.707106781186547524400844362104849039 /* 1/sqrt(2) */
#endif
#ifndef M_SQRT3
#define M_SQRT3 1.7320508075688772935 /*  */
#endif
#ifndef M_SQRT3_2
#define M_SQRT3_2 0.8660254037844387 /*  */
#endif

/* Vector array elements. */
#define U 0
#define V 1

#define iX 0
#define jY 1
#define kZ 2
#define W  3

#define VX(X) { *(X+0) }
#define VY(X) { *(X+1) }
#define VZ(X) { *(X+2) }

#define RADIUS 0
#define THETA  1
#define PHI    2

/*
 * Macros
 */

#define CosD(A) { cos(A * M_PI_180); }
#define SinD(A) { sin(A * M_PI_180); }

/* 
 * Structures and Datatypes
 */

typedef double AFFINE[4][4];
typedef double AFFINE3X3[3][3];
typedef double cartesian[4];
typedef double MATRIX3x3[3][3];
typedef double SCALER;
typedef double vec2[2];
typedef double vec3[3];
typedef double vec4[4];
typedef double VECTOR[3];
typedef double VECTORXY[2];
typedef double VectorXY[2];
typedef double VECTORXYZ[3];
typedef double VectorXYZ[3];

typedef struct VexprMatrix {
  int refCount;
  char rows;
  char cols;
  char form;
  char units;
  double *matrix;
} MATOBJ;

typedef struct MatrixForm {
  int id;
  const char *name;
  int rows;
  int cols;
  const char *description;
  const char *(*xConvertToForm)(MATOBJ*,int form);
} MatrixForm;

#define MATFORM_null 0
#define MATFORM_affine 1
#define MATFORM_cylindrical 2
#define MATFORM_euler 3
#define MATFORM_heading 4
#define MATFORM_mat2 5
#define MATFORM_mat3 6
#define MATFORM_matrix3x3 6
#define MATFORM_mat4 7
#define MATFORM_polar 8
#define MATFORM_scaler 9
#define MATFORM_spherical 10
#define MATFORM_vector_xy 11
#define MATFORM_vectorxy 11
#define MATFORM_vec2 11
#define MATFORM_vector_xyz 12
#define MATFORM_vector 12
#define MATFORM_vec3 12
#define MATFORM_vectorxyz 12
#define MATFORM_vector_xyzw 13
#define MATFORM_vec4 13
#define MATFORM_cartesian 13

/* Module wide constants */
extern const Tcl_ObjType matrix_tclobjtype;
extern const MatrixForm MatrixForms[];

extern const Tcl_ObjType *tclListType;
extern const Tcl_ObjType *tclDoubleType;
extern const Tcl_ObjType *NumArrayType;
extern const Tcl_ObjType *odieMatrixType;

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




















































































































































































































































































































Deleted cmodules/math/generic/cmatrixforms.tcl.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
###
# This file contains the definitions and documentation
# for the vexpr opcodes. To add a new opcode, run this
# script and recompile tclVexpr.c
#
# It need only be called if the developer wishes to add
# a new opcode
###

set ::constants {
 2_X_PI 6.283185307179586476925286766560 { 2 * PI }
 PI_180 0.01745329251994329576  { pi / 180 }
 PI_360 0.00872664625997164788  { pi / 360 }
 E         2.71828182845904523536028747135266250   { e              }
 LOG2E     1.44269504088896340735992468100189214   { log2(e)        }
 LOG10E    0.434294481903251827651128918916605082  { log10(e)       }
 LN2       0.693147180559945309417232121458176568  { loge(2)        }
 LN10      2.30258509299404568401799145468436421   { loge(10)       }
 PI        3.14159265358979323846264338327950288   { pi             }
 PI_2      1.57079632679489661923132169163975144   { pi/2           }
 PI_4      0.785398163397448309615660845819875721  { pi/4           }
 1_PI      0.318309886183790671537767526745028724  { 1/pi           }
 2_PI      0.636619772367581343075535053490057448  { 2/pi           }
 2_SQRTPI  1.12837916709551257389615890312154517   { 2/sqrt(pi)     }
 SQRT2     1.41421356237309504880168872420969808   { sqrt(2)        }
 SQRT1_2   0.707106781186547524400844362104849039  { 1/sqrt(2)      }
 SQRT3     1.7320508075688772935
 SQRT3_2   0.8660254037844387
}

set path [file dirname [file normalize [info script]]]
#package require dict

proc vexpr_argtype_enum name {
  return vexpr_arg_[string map {+ plus - minus * star . dot} $name]
}

proc vexpr_argtype_typedef {name info} {
  dict with info {}
  if { $rows eq "*"} {
    return "double *$name\;"
  }
  if { $cols eq 1 } {
    if { $rows eq 1 } {
      return "double $name\;"      
    } else {
      return "double $name\[$rows\]\;"
    }
  } else {
    return "double $name\[$cols\]\[$rows\]\;"
  }
}

proc vexpr_argtype {name info} {
  global argtype_cname argtype_aliases argtype_body argtype_info argtype_enum
  set argtype_cname($name) $name
  set argtype_enum($name) [vexpr_argtype_enum $name]
  set argtype_info($name) {
    aliases {}
    typedef {}
    cname   {}
    rows 1
    cols 1
    description {}
    function-convert NULL
    opcode {}
  }
  #dict set argtype_info($name) cname matrix_tclobjtype_$name
  foreach {field value} $info {
    dict set argtype_info($name) $field $value
    switch $field {
      aliases {
        foreach v $value {
          set argtype_cname($v) $name
          set argtype_enum($v) [vexpr_argtype_enum $v]
          lappend argtype_aliases($name) $v
        }
      }
    }
  }
  dict with argtype_info($name) {}
}

vexpr_argtype null {
  aliases {}
  rows *
  cols *
  description {A matrix of arbitrary size}
  function-convert NULL
}

vexpr_argtype scaler {
  aliases {SCALER}
  rows 1
  cols 1
  description {A scaler (1x1)}
}
vexpr_argtype mat2 {
  aliases {}
  rows 2
  cols 2
  description {A 2x2 matrix}
}
vexpr_argtype mat3 {
  aliases {MATRIX3x3}
  typedef AFFINE3X3
  rows 3
  cols 3
  description {A 3x3 matrix}
}
vexpr_argtype mat4 {
  aliases {}
  rows 4
  cols 4
  description {A 4x4 matrix}
}
vexpr_argtype affine {
  aliases {AFFINE}
  typedef AFFINE
  rows 4
  cols 4
  opcode to_affine
  description {A 4x4 affine matrix}
  function-convert Matrix_To_affine
}
vexpr_argtype cylindrical {
  aliases {}
  forms {}
  rows 3
  cols 1
  units radians
  opcode to_cylindrical
  description {A 3 dimensional vector: RADIUS THETA Z}
  function-convert Matrix_To_cylindrical
}
#vexpr_argtype cylindrical_degrees {
#  aliases {}
#  forms {}
#  rows 3
#  cols 1
#  units degrees
#  description {A 3 dimensional vector: RADIUS THETA Z, with theta in degrees}
#  function-convert Matrix_To_cylindrical
#}
#vexpr_argtype dual_quaternion {
#  aliases {dquat}
#  forms {}
#  opcode to_dual_quaternion
#  typedef {DualQuat dquat;}
#  description {A dual quaternion}
#}
vexpr_argtype euler {
  aliases {}
  forms {}
  rows 3
  cols 1
  units radians
  description {A 3 dimensional rotation: X Y Z}
}
vexpr_argtype heading {
  aliases {}
  forms {}
  rows 3
  cols 1
  units degrees
  description {A 3 dimensional rotation: yaw pitch roll}
}
vexpr_argtype polar {
  aliases {}
  rows 2
  cols 1
  opcode to_polar
  units radians
  description {A 2 dimensional vector: RADIUS THETA}
  function-convert Matrix_To_cylindrical
}
#vexpr_argtype polar_degrees {
#  aliases {}
#  rows 2
#  cols 1
#  units degrees
#  description {A 2 dimensional vector: RADIUS THETA, theta in degrees}
#  function-convert Matrix_To_cylindrical
#}
#vexpr_argtype quaternion {
#  aliases {QUATERNION}
#  typedef QUATERNION
#  forms {}
#  rows 4
#  cols 1
#  opcode to_quaternion
#  description {A quaternion: W X Y Z}
#  function-convert Matrix_To_quaternion
#}

vexpr_argtype spherical {
  aliases {}
  forms {}
  rows 3
  cols 1
  opcode to_spherical
  units radians
  description {A 3 dimensional vector: RADIUS THETA PHI}
  function-convert Matrix_To_cylindrical
}
#vexpr_argtype spherical_degrees {
#  aliases {}
#  forms {}
#  rows 3
#  cols 1
#  units degrees
#  description {A 3 dimensional vector: RADIUS THETA PHI, with THETA and PHI in degrees}
#  function-convert Matrix_To_cylindrical
#}
#vexpr_argtype unit_quaternion {
#  aliases {}
#  forms {}
#  rows 4
#  cols 1
#  opcode to_unit_quaternion
#  description {A Unit quaternion: W X Y Z}
#  function-convert Matrix_To_quaternion
#}
vexpr_argtype vector_xy {
  aliases {VECTORXY vec2 VectorXY}
  typedef VECTORXY
  rows 2
  cols 1
  units meters
  opcode to_vector_xy
  description {A 2 dimensional vector: X Y}
  function-convert Matrix_To_cartesian
}
vexpr_argtype vector_xyz {
  aliases {VECTOR vec3 VectorXYZ}
  typedef VECTORXYZ
  forms {}
  rows 3
  cols 1
  units meters
  opcode to_cartesian
  description {A 3 dimensional vector: X Y Z}
  function-convert Matrix_To_cartesian
}
vexpr_argtype vector_xyzw {
  aliases {vec4 cartesian}
  forms {}
  rows 4
  cols 1
  units meters
  description {A 4 dimensional vector: X Y Z W}
  function-convert Matrix_To_cartesian
}

###
# With documentation in hand, lets start writing files
###

###
# Generate the manpage
###
#set manout [open [file join $path .. doc vexpr.n] w]
#puts $manout "
#.\\\"
#.\\\" Copyright (c) 2014 Sean Woods
#.\\\"
#.\\\" See the file \"license.terms\" for information on usage and redistribution
#.\\\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#.\\\"
#.so man.macros
#.TH vexpr n 8.7 Tcl \"Tcl Built-In Commands\"
#.BS
#.\\\" Note:  do not modify the .SH NAME line immediately below!
#.SH NAME
#vexpr \\- Vector Expression Evaluator
#.SH SYNOPSIS
#\\fBvexpr \\fIarg arg opcode \\fR?\\fIarg opcode...?\\fR
#.BE
#.SH DESCRIPTION
#.PP
#Performs one of several vector operations, depending on the \\fIopcode\\fR.
#Opcodes and arguments are evaluated using reverse-polish notation.
#.
#Example:
#.CS
#\\fBvexpr {1 1 1} {2 2 2} +\\fR
#.CE
#.PP
#Will return \\fB\\{3.0 3.0 3.0}\\fR.
#.PP
#.RE
#The legal \\fIopcode\\fRs:
#"
#foreach opcode [lsort -dictionary [array names opcode_body]] {
#  
#  puts $manout .TP
#  dict with opcode_info($opcode) {}
#  puts $manout "\\fB${opcode}\\fR"
#
#  puts $manout ".RS 1"
#  if {[llength $arguments]} {
#    puts $manout "Usage: \\fI$arguments\\fR \\fB${opcode}\\fR"
#  } else {
#    puts $manout "Usage: \\fB${opcode}\\fR"
#  }
#  puts $manout .RE
#  if { $aliases ne {} } {
#    puts $manout ".RS 1"
#    puts $manout "Aliases: $aliases"
#    puts $manout .RE
#  }
#  puts $manout ".RS 1"
#  if { $result eq {} } {
#    puts $manout "Result: (None)"
#  } else {
#    puts $manout "Result: $result"
#  }
#  puts $manout .RE
#  puts $manout .PP
#  puts $manout ".RS 1"
#  puts $manout "$description"
#  puts $manout .RE
#}
#
#puts $manout {
#.SH "SEE ALSO"
#expr(n)
#.SH KEYWORDS
#vector
#}
#close $manout

###
# Generate the "cmatrix.h" file
###
set hout   [open [file join $path cmatrixforms.h] w]
fconfigure $hout -translation crlf
::cthulhu::add_cheader [file join $path cmatrixforms.h]

puts $hout {/*
** This file is automatically generated by the cmatrixforms.tcl
** script located in the same directory
*/

/* Constants */
}

foreach line [split $constants \n] {
  if {[string trim $line] eq {}} continue
  set const M_[lindex $line 0]
  set value [lindex $line 1]
  set comment [string trim [lindex $line 2]]
  puts $hout "#ifndef $const
#define $const $value /* $comment */
#endif"
}

puts $hout {
/* Vector array elements. */
#define U 0
#define V 1

#define iX 0
#define jY 1
#define kZ 2
#define W  3

#define VX(X) { *(X+0) }
#define VY(X) { *(X+1) }
#define VZ(X) { *(X+2) }

#define RADIUS 0
#define THETA  1
#define PHI    2

/*
 * Macros
 */

#define CosD(A) { cos(A * M_PI_180); }
#define SinD(A) { sin(A * M_PI_180); }

/* 
 * Structures and Datatypes
 */
}

foreach {name info} [lsort -stride 2 -dictionary [array get argtype_info]] {
  dict with info {}
  if {[dict get $info typedef] ne {}} {
    set alias [dict get $info typedef]
    set typedef_aliases($alias) [vexpr_argtype_typedef $alias $info]
  }
  foreach alias [dict get $info aliases] {
    set typedef_aliases($alias) [vexpr_argtype_typedef $alias $info]
  }
}
foreach {alias def} [lsort -stride 2 -dictionary [array get typedef_aliases]] {
  puts $hout "typedef $def"
}

set enum_types {}
puts $hout {
typedef struct VexprMatrix {
  int refCount;
  char rows;
  char cols;
  char form;
  char units;
  double *matrix;
} MATOBJ;

typedef struct MatrixForm {
  int id;
  const char *name;
  int rows;
  int cols;
  const char *description;
  const char *(*xConvertToForm)(MATOBJ*,int form);
} MatrixForm;
}

set idx 0
set enum_types {MATFORM_null 0}
foreach {name info} [lsort -stride 2 -dictionary [array get argtype_info]] {
  set ntype MATFORM_[string tolower $name]
  if { $ntype in $enum_types } continue
  lappend enum_types $ntype [incr idx]
  foreach alias [dict get $info aliases] {
    set ntype MATFORM_[string tolower $alias]
    if { $ntype in $enum_types } continue
    lappend enum_types $ntype $idx
  }
}

foreach {type idx} $enum_types {
  puts $hout "#define $type $idx"
}

#typedef struct GenMatrix {
#  int rows,cols;
#  union {
#    double  *pointer;
#    double  cells[16];
#    SCALER  scaler;
#    VECTOR  vector;
#    QUATERNION quaternion;
#    AFFINE  affine;
#  };
#} MATOBJ;

puts $hout {
/* Module wide constants */
extern const Tcl_ObjType matrix_tclobjtype;
extern const MatrixForm MatrixForms[];

extern const Tcl_ObjType *tclListType;
extern const Tcl_ObjType *tclDoubleType;
extern const Tcl_ObjType *NumArrayType;
extern const Tcl_ObjType *odieMatrixType;
}
close $hout




###
# Generate the main C source file
###
set fout   [open [file join $path cmatrixforms.c] w]
fconfigure $fout -translation crlf
::cthulhu::add_csource [file join $path cmatrixforms.c]
::cthulhu::add_dynamic [file join $path cmatrixforms.c] [info script]

set tcl_cmds {}

###
# Add in the start of the file
###
puts $fout {/*
** This file is automatically generated by the TclVexpr.tcl
** script located in the same directory
*/

#include "odieInt.h"

/*
 * Module-Wide Variables
 */

}
puts $fout "const MatrixForm MatrixForms\[\] = \{"
set lines {}
foreach {name info} [lsort -stride 2 -dictionary [array get argtype_info null]] {
  set rows {}
  dict with info {}
  set line "\{ MATFORM_[string tolower $name], \"$name\", "
  if {![string is integer $rows] && $rows < 1} {
    append line "0 , 0"
  } else {
    append line "$rows, $cols"
  }
  append line ", \"$description\", ${function-convert} \}"
  lappend lines $line
}
foreach {name info} [lsort -stride 2 -dictionary [array get argtype_info]] {
  if { $name eq "null" } continue
  set rows {}
  dict with info {}
  set line "\{ MATFORM_[string tolower $name], \"$name\", "
  if {![string is integer $rows] && $rows < 1} {
    append line "0 , 0"
  } else {
    append line "$rows, $cols"
  }
  append line ", \"$description\", ${function-convert} \}"
  lappend lines $line
}
puts $fout [join $lines ",\n"]
puts $fout "\}\;"

foreach {name info} [lsort -stride 2 -dictionary [array get argtype_info]] {
  set rows {}
  dict with info {}
  if { $typedef ne {} } {
  puts $fout [string map [list %form% $name %typedef% $typedef]  {
STUB_EXPORT int Odie_Get_%typedef%_FromObj(Tcl_Interp *interp,Tcl_Obj *objPtr,%typedef% *ptr) {
  MATOBJ *T=Odie_GetMatrixFromTclObj(interp,objPtr,MATFORM_%form%);
  if(!T) return TCL_ERROR;
  ptr=T->matrix;
  return TCL_OK;
}

STUB_EXPORT int Odie_Set_%typedef%_FromObj(Tcl_Interp *interp,Tcl_Obj *objPtr,%typedef% ptr) {
  MATOBJ *T=Odie_GetMatrixFromTclObj(interp,objPtr,MATFORM_%form%);
  if(!T) return TCL_ERROR;
  memcpy(ptr,T->matrix,sizeof(%typedef%));
  return TCL_OK;
}

STUB_EXPORT Tcl_Obj *Odie_New_%typedef%_Obj(%typedef% ptr) {
  MATOBJ *C;
  Tcl_Obj *result;

  C=Matrix_NewObj();
  Matrix_Alloc(C,MATFORM_%form%);
  memcpy(C->matrix,ptr,sizeof(%typedef%));
  result=Matrix_To_TclObj(C);
  return result;
}
}]
  }
  dict set tcl_cmds matrix to_${name} matrix_method_to_${name}
  puts $fout [string map [list %form% $name %typedef% $typedef]  {
static int  matrix_method_to_%form% (
  ClientData dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A;
  int i;
  int size_a;
  int size_b;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_%form%);
  if(!A) return TCL_ERROR;
  Tcl_SetObjResult(interp,Matrix_To_TclObj(A));
  return TCL_OK;
}
  }]

}

puts $fout "DLLEXPORT int Odie_MatrixForms_Init(Tcl_Interp *interp) \{
  Tcl_Namespace *modPtr;
"
set curnspace {}
foreach {nspace dat} [lsort -stride 2 $tcl_cmds] {
  puts $fout [string map [list %nspace% $nspace] {
  modPtr=Tcl_FindNamespace(interp,"%nspace%",NULL,TCL_NAMESPACE_ONLY);
  if(!modPtr) {
    modPtr = Tcl_CreateNamespace(interp, "%nspace%", NULL, NULL);
  }
  }]
  foreach {procname cfunct} [lsort -stride 2 $dat] {
    puts $fout [format {  Tcl_CreateObjCommand(interp,"::%s::%s",(Tcl_ObjCmdProc *)%s,NULL,NULL);} $nspace $procname $cfunct]    
  }
}
puts $fout {  Tcl_Obj *varname=Tcl_NewStringObj("math_const",-1);
  Tcl_IncrRefCount(varname);}
foreach line [split $constants \n] {
  if {[string trim $line] eq {}} continue
  set const M_[lindex $line 0]
  set value [lindex $line 1]
  set comment [string trim [lindex $line 2]]
  puts $fout [format {  Tcl_ObjSetVar2(interp,varname,Tcl_NewStringObj("%s",-1),Tcl_NewDoubleObj(%s),TCL_GLOBAL_ONLY);} [string tolower $const] $const]
}
puts $fout "Tcl_DecrRefCount(varname);"
puts $fout "
  return TCL_OK;
\}
"
close $fout
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted cmodules/math/generic/objtypes.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
#include "odieInt.h"

/*
 * Module-Wide Variables
 */
const Tcl_ObjType *tclListType;
const Tcl_ObjType *tclDoubleType;
const Tcl_ObjType *NumArrayType;
const Tcl_ObjType *odieMatrixType;

const Tcl_ObjType matrix_tclobjtype = {
  "odie_matrix", /* name */
  &MatrixObj_freeIntRepProc, /* freeIntRepProc */
  &MatrixObj_dupIntRepProc, /* dupIntRepProc */
  &MatrixObj_updateStringProc, /* updateStringProc */
  &MatrixObj_setFromAnyProc /* setFromAnyProc */
};

MATOBJ *Odie_GetMatrixFromTclObj(Tcl_Interp *interp,Tcl_Obj *tclObj,int form) {
  MATOBJ *result=NULL;  
  if(MatrixObj_setFromAnyProc(interp,tclObj)) {
    return NULL;
  }
  result=tclObj->internalRep.otherValuePtr;
  const char *(*xConvertToForm)(MATOBJ*,int)=MatrixForms[form].xConvertToForm;
  if(!xConvertToForm) {
    return result;
  }
  const char *error;
  error=xConvertToForm(result,form);
  if(error) {
    Tcl_SetObjResult(interp,Tcl_NewStringObj(error,-1));
    return NULL;
  }
  return result;
}

void Matrix_Copy(MATOBJ *dest,MATOBJ *src) {
  int size_t;
  dest->cols=src->cols;
  dest->rows=src->rows;
  dest->form=src->form;
  size_t=Matrix_Alloc(dest,src->form);
  memcpy(dest->matrix,src->matrix,size_t);
}

void Matrix_Transfer(MATOBJ *dest,MATOBJ *src) {
  dest->rows=src->rows;
  dest->cols=src->cols;
  dest->form=src->form;
  dest->matrix=src->matrix;
  src->rows=0;
  src->cols=0;
  src->form=0;
  src->matrix=NULL;
}

MATOBJ *Matrix_NewObj(void) {
  MATOBJ *new=(MATOBJ *)Odie_Alloc(sizeof(MATOBJ));
  memset(new,0,sizeof(MATOBJ));
  return new;
}

void Matrix_Free(MATOBJ *matrix) {
  if(matrix->matrix) {
    Odie_Free((char *)matrix->matrix);
  }
  matrix->rows=0;
  matrix->cols=0;
  matrix->form=0;
  matrix->matrix=NULL;
}

int Matrix_Alloc(MATOBJ *matrix,int form) {
  int rows,cols;
  if(form) {
    rows=MatrixForms[form].rows;
    cols=MatrixForms[form].cols;
    matrix->cols=cols;
    matrix->rows=rows;
  } else {
    rows=matrix->rows;
    cols=matrix->cols;
  }
  int size_t=rows*cols*sizeof(double);
  matrix->form=form;
  matrix->matrix=(double*)Odie_Alloc(size_t);
  memset(matrix->matrix,0,size_t);
  return size_t;
}

/* Accept any input */
const char *Matrix_ToAny(MATOBJ *matrix,int form) {
  matrix->form=form;
  return NULL;
}

const char *Matrix_To_affine(MATOBJ *matrix,int form) {
  if(matrix->form==MATFORM_affine) {
    return NULL;
  }
  if(matrix->form==MATFORM_euler) {
    MATOBJ TEMPMATRIX;
    Matrix_Alloc(&TEMPMATRIX,MATFORM_affine);
    affine_Rotate(matrix->matrix,TEMPMATRIX.matrix);
    Matrix_Free(matrix);
    Matrix_Transfer(matrix,&TEMPMATRIX);
    return NULL;
  }
  if(matrix->form==MATFORM_vector_xyz) {
    MATOBJ TEMPMATRIX;
    Matrix_Alloc(&TEMPMATRIX,MATFORM_affine);
    affine_Translate(matrix->matrix,TEMPMATRIX.matrix);
    Matrix_Free(matrix);
    Matrix_Transfer(matrix,&TEMPMATRIX);
    return NULL;
  }
  if(matrix->rows==4 && matrix->cols==4) {
    if(matrix->form==MATFORM_null) {
        matrix->form=MATFORM_affine;
    }
    return NULL;
  }
  return "Cannot convert to affine";
}

const char *Matrix_To_vector_xy(MATOBJ *matrix,int form) {
  if(matrix->form==form) {
    return TCL_OK;
  }
  switch(matrix->form) {
    case MATFORM_vector_xy:
      return TCL_OK;
    case MATFORM_vector_xyz: {
      return NULL;
    }
    case MATFORM_cylindrical: {
      MATOBJ TEMPMATRIX;
      Matrix_Alloc(&TEMPMATRIX,MATFORM_vector_xy);
      odiemath_vec2_to_polar(matrix->matrix,TEMPMATRIX.matrix);
      Matrix_Free(matrix);
      Matrix_Transfer(matrix,&TEMPMATRIX);
      return NULL;
    }
  }
  if(form==MATFORM_vector_xyz) {
    return "Cannot convert to vector_xy";
  }
  if(Matrix_To_cartesian(matrix,MATFORM_vector_xy)) {
    return "Cannot convert to vector_xy";
  }
  return NULL;
}

const char *Matrix_To_cartesian(MATOBJ *matrix,int form) {
  if(matrix->form==form) {
    return TCL_OK;
  }
  switch(matrix->form) {
    case MATFORM_vector_xy:
    case MATFORM_vector_xyz:
    case MATFORM_vector_xyzw:
      break;
    case MATFORM_spherical:
    {
      MATOBJ TEMPMATRIX;
      Matrix_Alloc(&TEMPMATRIX,MATFORM_cartesian);
      odiemath_spherical_to_cartesian(matrix->matrix,TEMPMATRIX.matrix);
      Matrix_Free(matrix);
      Matrix_Transfer(matrix,&TEMPMATRIX);
      break;
    }
    case MATFORM_polar:
    case MATFORM_cylindrical: {
      MATOBJ TEMPMATRIX;
      Matrix_Alloc(&TEMPMATRIX,MATFORM_cartesian);
      odiemath_cylindrical_to_cartesian(matrix->matrix,TEMPMATRIX.matrix);
      Matrix_Free(matrix);
      Matrix_Transfer(matrix,&TEMPMATRIX);
      break;
    }
    default: {
      if(matrix->rows==1) {
        int temp=matrix->cols;
        matrix->cols=matrix->rows;
        matrix->rows=temp;
      }
      if(matrix->cols != 1) {
        return "Cannot convert to cartesian";
      }
      if(matrix->rows<3) {
        /* Allocate storage for a 4d cartesian */
        MATOBJ TEMPMATRIX;
        int i;
        Matrix_Alloc(&TEMPMATRIX,MATFORM_cartesian);
        for(i=0;i<matrix->rows && i<4;i++) {
          *(TEMPMATRIX.matrix+i)=*(matrix->matrix+i);
        }
        Matrix_Free(matrix);
        Matrix_Transfer(matrix,&TEMPMATRIX);
      }
    }
  }
  switch(form) {
    case MATFORM_vector_xyz:
      matrix->form=form;
      matrix->rows=3;
      matrix->cols=1;
      return NULL;
    case MATFORM_vector_xy:
      matrix->form=form;
      matrix->rows=2;
      matrix->cols=1;
      return NULL;   
  }
  return NULL;
}

const char *Matrix_To_cylindrical(MATOBJ *matrix,int form) {
  if(matrix->form==form) {
    return NULL;
  }
  switch(matrix->form) {
    case MATFORM_polar:
    case MATFORM_cylindrical:
    case MATFORM_vector_xy:
    case MATFORM_vector_xyz:
    case MATFORM_vector_xyzw:
    break;
    default:
    if(Matrix_To_cartesian(matrix,MATFORM_cartesian)) {
      return "Cannot convert to polar";
    }
  }
  if(matrix->form==MATFORM_cartesian) {
    odiemath_cartesian_to_cylindrical(matrix->matrix,matrix->matrix);
  }
  switch(form) {
    case MATFORM_cylindrical:
      matrix->form=form;
      matrix->rows=3;
      matrix->cols=1;
      return NULL;
    case MATFORM_polar:
      matrix->form=form;
      matrix->rows=2;
      matrix->cols=1;
      return NULL;   
  }
  return NULL;
}

const char *Matrix_To_spherical(MATOBJ *matrix,int form) {
  if(matrix->form==form) {
    return NULL;
  }
  if(Matrix_To_cartesian(matrix,MATFORM_cartesian)) {
    return "Cannot convert to spherical";
  }
  if(matrix->form==MATFORM_cartesian) {
    odiemath_cartesian_to_spherical(matrix->matrix,matrix->matrix);
  }
  matrix->form=form;
  matrix->rows=3;
  matrix->cols=1;
  return NULL;
}

const char *Matrix_To_quaternion(MATOBJ *matrix,int form) {
  if(matrix->form==form) {
    return NULL;
  }
  if(Matrix_To_cartesian(matrix,MATFORM_cartesian)) {
    return "Cannot convert to spherical";
  }
  if(matrix->form==MATFORM_cartesian) {
    odiemath_cartesian_to_spherical(matrix->matrix,matrix->matrix);
  }
  matrix->form=form;
  matrix->rows=3;
  matrix->cols=1;
  return NULL;
}

int TclObj_To_Matrix(
  Tcl_Interp *interp,
  Tcl_Obj *listPtr,
  MATOBJ *matrix
) {
  Tcl_Obj **rowPtrs;
  Tcl_Obj **elemPtrs;
  int result;
  int rows,cols;
  int idx,i,j;
  int len;
  
  /* Step one, Measure the matrix */
  result = Tcl_ListObjGetElements(interp, listPtr, &rows, &rowPtrs);
  if (result != TCL_OK) {
    return result;
  }
  if(rows<1) {
    Tcl_AppendResult(interp, "Could not interpret matrix", 0);
    return TCL_ERROR;
  }
  result = Tcl_ListObjGetElements(interp, rowPtrs[0], &cols, &elemPtrs);
  if (result != TCL_OK) {
    return result;
  }
  /*
  ** For NULL form, we pass the rows and cols
  ** via the data structure
  */
  matrix->rows=rows;
  matrix->cols=cols;
  Matrix_Alloc(matrix,MATFORM_null);
  idx=-1;
  for(i=0;i<rows;i++) {
    result = Tcl_ListObjGetElements(interp, rowPtrs[i], &len, &elemPtrs);
    if (result != TCL_OK) {
      return result;
    }
    if(len != cols) {
      Tcl_SetObjResult(interp,Tcl_NewStringObj("Columns are not uniform",-1));
      return TCL_ERROR;
    }
    for(j=0;j<len;j++) {
      double temp;
      idx++;
      result =  Tcl_GetDoubleFromObj(interp, elemPtrs[j], &temp);
      if (result != TCL_OK) {
        return result;
      }
      *(matrix->matrix+idx)=(SCALER)temp;
    }
  }
  return TCL_OK;
}

Tcl_Obj *Matrix_To_TclObj(MATOBJ *matrix) {
  Tcl_Obj *dest=Tcl_NewObj();
  dest->typePtr=&matrix_tclobjtype;
  dest->internalRep.otherValuePtr=matrix;
  Tcl_InvalidateStringRep(dest);
  //Tcl_IncrRefCount(dest);
  return dest;
}

int MatrixObj_setFromAnyProc(Tcl_Interp *interp,Tcl_Obj *objPtr) {
  if(objPtr->typePtr) {
    if(objPtr->typePtr->setFromAnyProc==&MatrixObj_setFromAnyProc) {
      /*
      ** Object is already of the type requested
      */
      return TCL_OK;
    }
  }
  MATOBJ *matrix=Matrix_NewObj();
  if(TclObj_To_Matrix(interp,objPtr,matrix)) {
    Odie_Free((char *)matrix);
    return TCL_ERROR;
  }
  objPtr->internalRep.otherValuePtr=matrix;
  objPtr->typePtr=&matrix_tclobjtype;
  return TCL_OK;
}

void MatrixObj_updateStringProc(Tcl_Obj *objPtr) {
  char outbuffer[128];
  Tcl_DString result;
  MATOBJ *matrix=objPtr->internalRep.otherValuePtr;
  int rows,cols;
  register int j;
  /* Step 1, dimension matrix */
  rows = matrix->rows;
  cols = matrix->cols;
  Tcl_DStringInit(&result);
  if(cols==1) {
    /*
     * Output single-row matrices (i.e. vectors)
     * as a single tcl list (rather than nest them
     * as a list within a list)
     */
    for(j=0;j<rows;j++) {
      sprintf(outbuffer,"%g",(float)*(matrix->matrix+j));
      Tcl_DStringAppendElement(&result,outbuffer);
    }    
  } else if(rows==1) {
    /*
     * Output single-row matrices (i.e. vectors)
     * as a single tcl list (rather than nest them
     * as a list within a list)
     */
    for(j=0;j<cols;j++) {
      sprintf(outbuffer,"%g",(float)*(matrix->matrix+j));
      Tcl_DStringAppendElement(&result,outbuffer);
    }
  } else {
    register int i,idx=0;
    for(i=0;i<rows;i++) {
      Tcl_DStringStartSublist(&result);
      for(j=0;j<cols;j++) {
        idx=(i*cols)+j;
        sprintf(outbuffer,"%g",*(matrix->matrix+idx));
        Tcl_DStringAppendElement(&result,outbuffer); 
      }
      Tcl_DStringEndSublist(&result);
    }
  }
  objPtr->length=Tcl_DStringLength(&result);
  objPtr->bytes=Odie_Alloc(objPtr->length+1);
  memcpy(objPtr->bytes,Tcl_DStringValue(&result),objPtr->length);
  objPtr->bytes[objPtr->length]='\0';
  Tcl_DStringFree(&result);
}

void MatrixObj_dupIntRepProc(Tcl_Obj *srcPtr,Tcl_Obj *dupPtr) {
  MATOBJ *srcmatrix=srcPtr->internalRep.otherValuePtr;
  MATOBJ *dupmatrix=Matrix_NewObj();
  int matsize;
  
  dupPtr->typePtr=srcPtr->typePtr;
  dupPtr->internalRep.otherValuePtr=dupmatrix;
  
  Matrix_Copy(dupmatrix,srcmatrix);
  dupmatrix->rows=srcmatrix->rows;
  dupmatrix->cols=srcmatrix->cols;
  dupmatrix->form=srcmatrix->form;
  matsize=sizeof(double)*dupmatrix->rows*dupmatrix->cols;
  if(matsize<1) {
    matsize=1;
  }
  dupmatrix->matrix=(double *)Odie_Alloc(matsize);
  memcpy(dupmatrix->matrix,srcmatrix->matrix,matsize);
}

void MatrixObj_freeIntRepProc(Tcl_Obj *objPtr) {
  if(!objPtr->internalRep.otherValuePtr) return;
  MATOBJ *matrix=objPtr->internalRep.otherValuePtr;
  Matrix_Free(matrix);
  Odie_Free((char *)matrix);
  objPtr->typePtr=NULL;
  objPtr->internalRep.otherValuePtr=NULL;
}

DLLEXPORT int MatrixObjType_Init(Tcl_Interp *interp) {

  Tcl_RegisterObjType(&matrix_tclobjtype);
  tclListType=Tcl_GetObjType("list");
  tclDoubleType=Tcl_GetObjType("double");
  
  return TCL_OK;
}


<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<














































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted cmodules/math/generic/quaternion.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include "odieInt.h"

/* 
 * Structures and Datatypes
 */

typedef struct DualQuat {
	float quat[4];
	float trans[4];

	float scale[4][4];
	float scale_weight;
} DualQuat;

void mul_qt_qtqt(float q[4], const float q1[4], const float q2[4])
{
	float t0, t1, t2;

	t0 = q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2] - q1[3] * q2[3];
	t1 = q1[0] * q2[1] + q1[1] * q2[0] + q1[2] * q2[3] - q1[3] * q2[2];
	t2 = q1[0] * q2[2] + q1[2] * q2[0] + q1[3] * q2[1] - q1[1] * q2[3];
	q[3] = q1[0] * q2[3] + q1[3] * q2[0] + q1[1] * q2[2] - q1[2] * q2[1];
	q[0] = t0;
	q[1] = t1;
	q[2] = t2;
}

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






















































Deleted cmodules/math/generic/vector.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
#include "odieInt.h"

static int  vector_method_list(
  ClientData dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A;
  int idx,n;
  int size_a;
  if(objc != 2) {
    Tcl_WrongNumArgs( interp, 1, objv, "A" );
    return TCL_ERROR;
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_null);
  if(!A) return TCL_ERROR;
  size_a=A->rows*A->cols;
  Tcl_Obj **pList=NULL;
  
  pList=Odie_Alloc(sizeof(Tcl_Obj)*size_a);
  for(idx=0;idx<size_a;idx++) {
    pList[idx]=Tcl_NewDoubleObj(*(A->matrix+idx));
  }
  Tcl_SetObjResult(interp,Tcl_NewListObj(size_a,pList));
  return TCL_OK;
}


static int  vector_method_index(
  ClientData dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A;
  int i=-1,j=-1,idx,n=0;
  int size_a;
  Tcl_Obj **pList=NULL;

  if(objc != 3 && objc != 4) {
    Tcl_WrongNumArgs( interp, 1, objv, "A i ?j?" );
    return TCL_ERROR;
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_null);
  if(!A) return TCL_ERROR;

  if(Tcl_GetIntFromObj(interp,objv[2],&i)) return TCL_ERROR;
  size_a=A->rows*A->cols;

  if(i<0) {
    i=0;
  }
  if(i>=size_a) {
    i=size_a-1;
  }
  if(objc==3) {
    j==i;
  } else{
    if(Tcl_GetIntFromObj(interp,objv[3],&j)) return TCL_ERROR;
  }
  if ( j < 0 ) {
    j=size_a-1;
  } else if (j<=i) {
    Tcl_SetObjResult(interp,Tcl_NewDoubleObj(*(A->matrix+i)));
    return TCL_OK;
  }
  if(j>=size_a) {
    j=size_a-1; 
  }
  n=(j-i)+1;
  pList=Odie_Alloc(sizeof(Tcl_Obj)*n);
  for(idx=i;idx<=j;idx++) {
    pList[idx]=Tcl_NewDoubleObj(*(A->matrix+idx));
  }
  Tcl_SetObjResult(interp,Tcl_NewListObj(n,pList));
  return TCL_OK;
}

static int  vector_add(
  ClientData dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A,*B,*C;
  int i;
  int size_a;
  int size_b;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A B" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_null);
  if(!A) return TCL_ERROR;
  B=Odie_GetMatrixFromTclObj(interp,objv[2],MATFORM_null);
  if(!B) return TCL_ERROR;

  size_a=A->rows*A->cols;
  size_b=B->rows*B->cols;
  C=Matrix_NewObj();
  if(A->rows==B->rows && A->cols==B->cols) {
    C->form=A->form;
    C->rows=A->rows;
    C->cols=A->cols;
    if(A->form!=B->form) {
      C->form=MATFORM_null;
    }
  } else if(A->form==B->form && A->form != MATFORM_null) {
    C->form=A->form;
    C->rows=A->rows;
    C->cols=A->cols;
  } else if(size_a<size_b) {
    C->rows=size_b;
    C->cols=1;
    C->form=MATFORM_null;
  } else {
    C->rows=size_a;
    C->cols=1;
    C->form=MATFORM_null;
  }
  Matrix_Alloc(C,C->form);
  for(i=0;i<size_a && i<size_b;i++) {
    *(C->matrix+i) = *(A->matrix+i) + *(B->matrix+i);
  }
  Tcl_SetObjResult(interp,Matrix_To_TclObj(C));
  return TCL_OK;
}

static int  vector_subtract(
  ClientData dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A,*B,*C;
  int i;
  int size_a;
  int size_b;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A B" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_null);
  if(!A) return TCL_ERROR;

  B=Odie_GetMatrixFromTclObj(interp,objv[2],MATFORM_null);
  if(!B) return TCL_ERROR;

  size_a=A->rows*A->cols;
  size_b=B->rows*B->cols;
  C=Matrix_NewObj();
  if(A->rows==B->rows && A->cols==B->cols) {
    C->form=A->form;
    C->rows=A->rows;
    C->cols=A->cols;
    if(A->form!=B->form) {
      C->form=MATFORM_null;
    }
  } else if(A->form==B->form && A->form != MATFORM_null) {
    C->form=A->form;
    C->rows=A->rows;
    C->cols=A->cols;
  } else if(size_a<size_b) {
    C->rows=size_b;
    C->cols=1;
    C->form=MATFORM_null;
  } else {
    C->rows=size_a;
    C->cols=1;
    C->form=MATFORM_null;
  }
  Matrix_Alloc(C,C->form);
  for(i=0;i<size_a && i<size_b;i++) {
    *(C->matrix+i) = *(A->matrix+i) - *(B->matrix+i);
  }
  Tcl_SetObjResult(interp,Matrix_To_TclObj(C));
  return TCL_OK;
}

static int  vector_dot_product (
  ClientData dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A,*B;
  int i;
  int size_a;
  int size_b;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A B" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_null);
  if(!A) return TCL_ERROR;

  B=Odie_GetMatrixFromTclObj(interp,objv[2],MATFORM_null);
  if(!B) return TCL_ERROR;

  size_a=A->rows*A->cols;
  size_b=B->rows*B->cols;
  double result=0;
  for(i=0;i<size_a && i<size_b;i++) {
    result += *(A->matrix+i) * *(B->matrix+i);
  }
  Tcl_SetObjResult(interp,Tcl_NewDoubleObj(result));
  return TCL_OK;
}

static int  vector_to_matrix (
  ClientData dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A;
  int i;
  int size_a;
  int size_b;
  if(objc < 2) {
    Tcl_WrongNumArgs( interp, 1, objv, "A" );
    return TCL_ERROR;
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_null);
  if(!A) return TCL_ERROR;
  Tcl_SetObjResult(interp,Matrix_To_TclObj(A));
  return TCL_OK;
}

static int  vector_scale (
  ClientData dummy,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A,*C;
  double scaler;
  int i;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A B" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_null);
  if(!A) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&scaler)) {
    return TCL_ERROR;
  }
  
  C=Matrix_NewObj();
  Matrix_Copy(C,A);
  int   size_a=A->rows*A->cols;
  for(i=0;i<size_a;i++) {
    *(C->matrix+i) *= scaler;
  }
  
  Tcl_SetObjResult(interp,Matrix_To_TclObj(C));
  return TCL_OK;
}

static int  vector_method_length (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  int i,size;
  MATOBJ *A;
  double result,sum=0.0;
  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "A");
    return TCL_ERROR;
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_null);
  size=A->rows*A->cols;
  result=0.0;
  for(i=1;i<size;i++) {
    double a=*(A->matrix+1);
    sum+=a*a;
  }
  result=sqrt(sum);
  Tcl_SetObjResult(interp,Tcl_NewDoubleObj(result));
  return TCL_OK;
}

static int  vector_method_length_squared (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  int i,size;
  MATOBJ *A;
  double result,sum=0.0;
  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "A");
    return TCL_ERROR;
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_null);
  size=A->rows*A->cols;
  result=0.0;
  for(i=1;i<size;i++) {
    double a=*(A->matrix+1);
    sum+=a*a;
  }
  result=sum;
  Tcl_SetObjResult(interp,Tcl_NewDoubleObj(result));
  return TCL_OK;
}

static int  vectorN_method_length (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  int i;
  double result,sum=0.0;
  if( objc<1 ){
    Tcl_WrongNumArgs(interp, 1, objv, "x ?y? ?z? ?...?");
    return TCL_ERROR;
  }
  result=0.0;
  for(i=1;i<objc;i++) {
    double a;
    if(Tcl_GetDoubleFromObj(interp,objv[i],&a)) return TCL_ERROR;
    sum+=a*a;
  }
  result=sqrt(sum);
  Tcl_SetObjResult(interp,Tcl_NewDoubleObj(result));
  return TCL_OK;
}

static int  vectorN_method_distance (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  int i;
  double result;
  if( objc<1 ){
    Tcl_WrongNumArgs(interp, 1, objv, "i1 i2 ?j1 j2? ?k1 k2?");
    return TCL_ERROR;
  }
  result=0.0;
  for(i=1;i<objc;i+=2) {
    double a,b,dx;
    if(Tcl_GetDoubleFromObj(interp,objv[i],&a)) return TCL_ERROR;
    if(i+1>=objc) {
      Tcl_AppendResult(interp, "Odd number of arguments",(char*)0);
      return TCL_ERROR;
    }
    if(Tcl_GetDoubleFromObj(interp,objv[i+1],&b)) return TCL_ERROR;
    dx=b-a;
    result=result+dx*dx;
  }
  result=sqrt(result);
  Tcl_SetObjResult(interp,Tcl_NewDoubleObj(result));
  return TCL_OK;
}

DLLEXPORT int Odie_Vector_Init(Tcl_Interp *interp) {
  Tcl_Namespace *modPtr;

  modPtr=Tcl_FindNamespace(interp,"vector",NULL,TCL_NAMESPACE_ONLY);
  if(!modPtr) {
    modPtr = Tcl_CreateNamespace(interp, "vector", NULL, NULL);
  }
  
  Tcl_CreateObjCommand(interp,"::vector::add",(Tcl_ObjCmdProc *)vector_add,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vector::dot_product",(Tcl_ObjCmdProc *)vector_dot_product,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vector::length",(Tcl_ObjCmdProc *)vector_method_length,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vector::length_squared",(Tcl_ObjCmdProc *)vector_method_length_squared,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vector::scale",(Tcl_ObjCmdProc *)vector_scale,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vector::subtract",(Tcl_ObjCmdProc *)vector_subtract,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vector::to_matrix",(Tcl_ObjCmdProc *)vector_to_matrix,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vector::index",(Tcl_ObjCmdProc *)vector_method_index,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vector::to_list",(Tcl_ObjCmdProc *)vector_method_list,NULL,NULL);

  Tcl_CreateEnsemble(interp, modPtr->fullName, modPtr, TCL_ENSEMBLE_PREFIX);
  Tcl_Export(interp, modPtr, "[a-z]*", 1);

  modPtr=Tcl_FindNamespace(interp,"vectorN",NULL,TCL_NAMESPACE_ONLY);
  if(!modPtr) {
    modPtr = Tcl_CreateNamespace(interp, "vectorN", NULL, NULL);
  }
  
  Tcl_CreateObjCommand(interp,"::vectorN::distance",(Tcl_ObjCmdProc *)vectorN_method_distance,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vectorN::length",(Tcl_ObjCmdProc *)vectorN_method_length,NULL,NULL);

  Tcl_CreateEnsemble(interp, modPtr->fullName, modPtr, TCL_ENSEMBLE_PREFIX);
  Tcl_Export(interp, modPtr, "[a-z]*", 1);
  

  return TCL_OK;
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































































































































































































































































































































































































































































































































































































































































































































































































Deleted cmodules/math/generic/vector2d.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
#include "odieInt.h"

/*
** Routines in this file are designed to work with double numbers
** directly. Useful for screen operations where X Y lists are used
*/

static int  vector2d_method_add(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  VectorXY A, B, P;
  if( objc!=5 ){
    Tcl_WrongNumArgs(interp, 1, objv, "X0 Y0 X1 Y1");
    return TCL_ERROR;
  }
  if( Tcl_GetDoubleFromObj(interp, objv[1], &A[X_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[2], &A[Y_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[3], &B[X_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[4], &B[Y_IDX]) ) return TCL_ERROR;
  VectorXY_Add(P,A,B);
  Tcl_Obj *pResult=Tcl_NewObj();
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(P[X_IDX]));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(P[Y_IDX]));
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

static int  vector2d_method_subtract(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  VectorXY A, B, P;
  if( objc!=5 ){
    Tcl_WrongNumArgs(interp, 1, objv, "X0 Y0 X1 Y1");
    return TCL_ERROR;
  }
  printf("vector2d_method_subtract\n");
  if( Tcl_GetDoubleFromObj(interp, objv[1], &A[X_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[2], &A[Y_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[3], &B[X_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[4], &B[Y_IDX]) ) return TCL_ERROR;
  printf("VectorXY_Subtract\n");
  VectorXY_Subtract(P,A,B);
  printf("/VectorXY_Subtract\n");
  Tcl_Obj *pResult=Tcl_NewObj();
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(P[X_IDX]));
  Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(P[Y_IDX]));
  Tcl_SetObjResult(interp, pResult);
  printf("/vector2d_method_subtract\n");
  return TCL_OK;
}

static int  vector2d_method_angle(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  VectorXY A, B, P;
  if( objc!=7 ){
    Tcl_WrongNumArgs(interp, 1, objv, "X0 Y0 X1 Y1 X2 Y2");
    return TCL_ERROR;
  }
  if( Tcl_GetDoubleFromObj(interp, objv[1], &A[X_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[2], &A[Y_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[3], &B[X_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[4], &B[Y_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[5], &P[X_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[6], &P[Y_IDX]) ) return TCL_ERROR;
  Tcl_SetObjResult(interp, Tcl_NewDoubleObj(VectorXY_angleOf(A, B, P)));
  return TCL_OK;
}

static int  vector2d_method_distance (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  double result,ax,ay,bx,by,dx,dy;
  if( objc!=5 ){
    Tcl_WrongNumArgs(interp, 1, objv, "x0 y0 x1 y1");
    return TCL_ERROR;
  }
  result=0.0;
  if(Tcl_GetDoubleFromObj(interp,objv[1],&ax)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&ay)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[3],&bx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[4],&by)) return TCL_ERROR;
  dx=bx-ax;
  dy=by-ay;
  
  result=sqrt(dx*dx + dy*dy);
  Tcl_SetObjResult(interp,Tcl_NewDoubleObj(result));
  return TCL_OK;
}

static int  vector2d_method_dotproduct(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  VectorXY A, B, P;
  if( objc!=7 ){
    Tcl_WrongNumArgs(interp, 1, objv, "X0 Y0 X1 Y1 X2 Y2");
    return TCL_ERROR;
  }
  if( Tcl_GetDoubleFromObj(interp, objv[1], &A[X_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[2], &A[Y_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[3], &B[X_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[4], &B[Y_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[5], &P[X_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[6], &P[Y_IDX]) ) return TCL_ERROR;
  Tcl_SetObjResult(interp, Tcl_NewDoubleObj(VectorXY_dotProduct(A, B, P)));
  return TCL_OK;
}

static int  vector2d_method_crossproduct(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  VectorXY A, B, P;
  if( objc!=7 ){
    Tcl_WrongNumArgs(interp, 1, objv, "X0 Y0 X1 Y1 X2 Y2");
    return TCL_ERROR;
  }
  if( Tcl_GetDoubleFromObj(interp, objv[1], &A[X_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[2], &A[Y_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[3], &B[X_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[4], &B[Y_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[5], &P[X_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[6], &P[Y_IDX]) ) return TCL_ERROR;
  Tcl_SetObjResult(interp, Tcl_NewDoubleObj(VectorXY_crossProduct(A, B, P)));
  return TCL_OK;
}

/*
** tclcmd:  triag_test_rightof X0 Y0 X1 Y1 X2 Y2
**
** A TCL command for testing the rightOf() function.
*/
static int  vector2d_method_rightof(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  VectorXY A, B, P;
  if( objc!=7 ){
    Tcl_WrongNumArgs(interp, 1, objv, "X0 Y0 X1 Y1 X2 Y2");
    return TCL_ERROR;
  }
  if( Tcl_GetDoubleFromObj(interp, objv[1], &A[X_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[2], &A[Y_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[3], &B[X_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[4], &B[Y_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[5], &P[X_IDX]) ) return TCL_ERROR;
  if( Tcl_GetDoubleFromObj(interp, objv[6], &P[Y_IDX]) ) return TCL_ERROR;
  Tcl_SetObjResult(interp, Tcl_NewIntObj(VectorXY_rightOf(A, B, P)));
  return TCL_OK;
}

static int  vector2d_method_rotate_and_size (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  /*
  ** Apply Matrices
  */
  Tcl_Obj *pResult=Tcl_NewObj();
  int i;
  double matA[6] = {1.0,0.0,0.0,1.0,0.0,0.0};
  double nx,ny,scalex,scaley,angle;
  
  if( objc < 7 ){
    Tcl_WrongNumArgs(interp, 1, objv, "normalx normaly sizex sizey x1 y1 ?x2 y2?...");
    return TCL_ERROR;
  }
  
  if(Tcl_GetDoubleFromObj(interp,objv[1],&nx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&ny)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[3],&scalex)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[4],&scaley)) return TCL_ERROR;
  
  angle=atan2(ny,nx);
  matA[0]=cos(angle);
  matA[1]=sin(angle);
  matA[2]=-sin(angle);
  matA[3]=cos(angle);
  matA[4]=0.0;
  matA[5]=0.0;
  
  scalex*=0.5;
  scaley*=0.5;
  for(i=5;i<objc;i+=2) {
    double x,y,sx,sy,newx,newy;
    if(Tcl_GetDoubleFromObj(interp,objv[i],&x)) return TCL_ERROR;
    if(Tcl_GetDoubleFromObj(interp,objv[i+1],&y)) return TCL_ERROR;
    sx=x*scalex;
    sy=y*scaley;
    newx=matA[0]*sx+matA[1]*sy+matA[4];
    newy=matA[2]*sx+matA[3]*sy+matA[5];
  
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(newx));
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(newy));
  }
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

static int  vector2d_method_scale (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  Tcl_Obj *pResult=Tcl_NewObj();
  int i;
  double scalex,scaley;
  
  if( objc < 5 ){
    Tcl_WrongNumArgs(interp, 1, objv, "sizex sizey x1 y1 ?x2 y2?...");
    return TCL_ERROR;
  }
  
  if(Tcl_GetDoubleFromObj(interp,objv[1],&scalex)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&scaley)) return TCL_ERROR;
  
  scalex*=0.5;
  scaley*=0.5;
  for(i=3;i<objc;i+=2) {
    double x,y,sx,sy;
    if(Tcl_GetDoubleFromObj(interp,objv[i],&x)) return TCL_ERROR;
    if(Tcl_GetDoubleFromObj(interp,objv[i+1],&y)) return TCL_ERROR;
    sx=x*scalex;
    sy=y*scaley;
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(sx));
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(sy));
  }
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

static int  vector2d_method_translate_and_zoom (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
/*
** Apply Matrices
*/
  
  Tcl_Obj *pResult=Tcl_NewObj();
  int i;
  double zoom;
  double centerx,centery;
  
  if( objc < 6 ){
    Tcl_WrongNumArgs(interp, 1, objv, "zoom centerx centery x1 y1 ?x2 y2?...");
    return TCL_ERROR;
  }   
  if(Tcl_GetDoubleFromObj(interp,objv[1],&zoom)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&centerx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[3],&centery)) return TCL_ERROR;
  for(i=4;i<objc;i+=2) {
    double x,y,newx,newy;
    if(Tcl_GetDoubleFromObj(interp,objv[i],&x)) return TCL_ERROR;
    if(Tcl_GetDoubleFromObj(interp,objv[i+1],&y)) return TCL_ERROR;
    newx=(x/zoom)+centerx;
    newy=(y/zoom)+centery;
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(newx));
    Tcl_ListObjAppendElement(interp,pResult,Tcl_NewDoubleObj(newy));
  }
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

DLLEXPORT int Odie_Vector2d_Init(Tcl_Interp *interp) {
  Tcl_Namespace *modPtr;

  modPtr=Tcl_FindNamespace(interp,"vector2d",NULL,TCL_NAMESPACE_ONLY);
  if(!modPtr) {
    modPtr = Tcl_CreateNamespace(interp, "vector2d", NULL, NULL);
  }
  
  Tcl_CreateObjCommand(interp,"::vector2d::+",(Tcl_ObjCmdProc *)vector2d_method_add,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vector2d::-",(Tcl_ObjCmdProc *)vector2d_method_subtract,NULL,NULL);

  Tcl_CreateObjCommand(interp,"::vector2d::angle",(Tcl_ObjCmdProc *)vector2d_method_angle,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vector2d::crossproduct",(Tcl_ObjCmdProc *)vector2d_method_dotproduct,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vector2d::distance",(Tcl_ObjCmdProc *)vector2d_method_distance,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vector2d::dotproduct",(Tcl_ObjCmdProc *)vector2d_method_dotproduct,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vector2d::rightof",(Tcl_ObjCmdProc *)vector2d_method_rightof,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vector2d::rotate_and_size",(Tcl_ObjCmdProc *)vector2d_method_rotate_and_size,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vector2d::scale",(Tcl_ObjCmdProc *)vector2d_method_scale,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vector2d::translate_and_zoom",(Tcl_ObjCmdProc *)vector2d_method_translate_and_zoom,NULL,NULL);

  Tcl_CreateEnsemble(interp, modPtr->fullName, modPtr, TCL_ENSEMBLE_PREFIX);
  Tcl_Export(interp, modPtr, "[a-z]*", 1);
  
  return TCL_OK;
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































































































































































































































































































































































































































































































































































































































Deleted cmodules/math/generic/vector3d.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include "odieInt.h"

/*
** Routines in this file are designed to work with double numbers
** directly. Useful for screen operations where X Y Z lists are used
*/

static int  vector3d_method_distance (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  double result,ax,ay,az,bx,by,bz,dx,dy,dz;
  if( objc!=7 ){
    Tcl_WrongNumArgs(interp, 1, objv, "x0 y0 z0 x1 y1 z1");
    return TCL_ERROR;
  }
  result=0.0;
  if(Tcl_GetDoubleFromObj(interp,objv[1],&ax)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&ay)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[3],&az)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[4],&bx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[5],&by)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[6],&bz)) return TCL_ERROR;    
  dx=bx-ax;
  dy=by-ay;
  dz=bz-az;
  
  result=sqrt(dx*dx + dy*dy + dz*dz);
  Tcl_SetObjResult(interp,Tcl_NewDoubleObj(result));
  return TCL_OK;
}

DLLEXPORT int Odie_Vector3d_Init(Tcl_Interp *interp) {
  Tcl_Namespace *modPtr;

  modPtr=Tcl_FindNamespace(interp,"vector3d",NULL,TCL_NAMESPACE_ONLY);
  if(!modPtr) {
    modPtr = Tcl_CreateNamespace(interp, "vector3d", NULL, NULL);
  }
  
  Tcl_CreateObjCommand(interp,"::vector3d::distance",(Tcl_ObjCmdProc *)vector3d_method_distance,NULL,NULL);

  Tcl_CreateEnsemble(interp, modPtr->fullName, modPtr, TCL_ENSEMBLE_PREFIX);
  Tcl_Export(interp, modPtr, "[a-z]*", 1);
  
  return TCL_OK;
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


































































































Deleted cmodules/math/generic/vectorxy.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
#include "odieInt.h"

/*
** Routines in this file are designed to work with Tcl_Objs formatted
** as 2d vector structures.
*/

CTHULHU_INLINE void VectorXY_Add(VECTORXY C,VECTORXY A,VECTORXY B) {
  C[X_IDX]=B[X_IDX]+A[X_IDX];
  C[Y_IDX]=B[Y_IDX]+A[Y_IDX];
}

CTHULHU_INLINE void VectorXY_Subtract(VECTORXY C,VECTORXY A,VECTORXY B) {
  C[X_IDX]=B[X_IDX]-A[X_IDX];
  C[Y_IDX]=B[Y_IDX]-A[Y_IDX];
}

CTHULHU_INLINE void VectorXY_Normalize(VECTORXY A) {
  double length=sqrt(A[X_IDX]*A[X_IDX]+A[Y_IDX]*A[Y_IDX]);
  if(length < __FLT_EPSILON__ ) {
    return;
  }
  A[X_IDX]/=length;
  A[Y_IDX]/=length;
}

CTHULHU_INLINE void VectorXY_Round(VECTORXY A) {
  A[X_IDX]=round(A[X_IDX]);
  A[Y_IDX]=round(A[Y_IDX]);
}
CTHULHU_INLINE void VectorXY_Set(VECTORXY A,VECTORXY B) {
  A[X_IDX]=B[X_IDX];
  A[Y_IDX]=B[Y_IDX];
}

CTHULHU_INLINE double VectorXY_crossProduct(VectorXY A, VectorXY B, VectorXY P){
  double r = (A[Y_IDX]-B[Y_IDX])*(P[X_IDX]-B[X_IDX]) + (B[X_IDX]-A[X_IDX])*(P[Y_IDX]-B[Y_IDX]);
  if(fabs(r) < __FLT_EPSILON__ ) {
    return 0.0;
  }
  return r;
}

CTHULHU_INLINE double VectorXY_dotProduct(VectorXY A, VectorXY B,VectorXY C){
  double r=(A[X_IDX]-B[X_IDX])*(C[X_IDX]-B[X_IDX])+(A[Y_IDX]-B[Y_IDX])*(C[Y_IDX]-B[Y_IDX]);
  if(fabs(r) < __FLT_EPSILON__ ) {
    return 0.0;
  }
  return r;
}


/*
** Consider traveling from VectorXY A to B to P.  If you have to make
** a left-turn at B, then this routine returns -1.  If P is on the
** same line as A and B then return 0.  If you make a right turn
** at B in order to reach P then return +1.
*/
CTHULHU_INLINE int VectorXY_rightOf(VectorXY A, VectorXY B, VectorXY P){
  /* Algorithm:  Rotate AB 90 degrees counter-clockwise.  Take
  ** the dot product with BP.  The dot produce will be the product
  ** of two (non-negative) magnitudes and the cosine of the angle.  So if
  ** the dot product is positive, the bend is to the left, or to the right if
  ** the dot product is negative.
  */
  double r = (A[Y_IDX]-B[Y_IDX])*(P[X_IDX]-B[X_IDX]) + (B[X_IDX]-A[X_IDX])*(P[Y_IDX]-B[Y_IDX]);
  if(fabs(r) < __FLT_EPSILON__ ) {
    return 0;
  }
  if(r>0.0) {
    return -1;
  }
  return 1;
}

/*
** This is a variation on rightOf().  Return 0 only if BP is a continuation
** of the line AB.  If BP doubles back on AB then return -1.
*/
CTHULHU_INLINE int VectorXY_strictlyRightOf(VectorXY A, VectorXY B, VectorXY P){
  int c = VectorXY_rightOf(A,B,P);
  if( c==0 ){
    double r = (A[X_IDX]-B[X_IDX])*(P[X_IDX]-B[X_IDX]) + (A[Y_IDX]-B[Y_IDX])*(P[Y_IDX]-B[Y_IDX]);
    c = r<0.0 ? +1 : -1;
  }
  return c;
}

/*
** Return TRUE if segments AB and CD intersect
*/
CTHULHU_INLINE int VectorXY_intersect(VectorXY A, VectorXY B, VectorXY C, VectorXY D){
  return
    VectorXY_rightOf(A,B,C)*VectorXY_rightOf(A,B,D)<0 &&
    VectorXY_rightOf(C,D,A)*VectorXY_rightOf(C,D,B)<0;
}

/*
** Compute angle ABC measured counter-clockwise from AB.  Return the
** result.
**
** This does not need to be a true angular measure as long as it is
** monotonically increasing.
*/
CTHULHU_INLINE double VectorXY_angleOf(VectorXY A, VectorXY B, VectorXY C){
#ifdef NEVER
  double a1, a2, a3;
  //if( sameVectorXY(A,C) ){
  //  return M_PI;
  //}
  a1 = atan2(B[Y_IDX] - A[Y_IDX], B[X_IDX] - A[X_IDX]);
  a2 = atan2(C[Y_IDX] - B[Y_IDX], C[X_IDX] - B[X_IDX]);
  a3 = a2-a1;
  if( a3>0 ) a3 -= 2.0*M_PI;
  if( a3<=-(2*M_PI) ) a3 += 2.0*M_PI;
  return fabs(a3);
#else
  double a1, a2, a3;
  if( sameVectorXY(A,C) ){
    return M_PI;
  }
  a1 = atan2(B[Y_IDX] - A[Y_IDX], B[X_IDX] - A[X_IDX]);
  a2 = atan2(C[Y_IDX] - B[Y_IDX], C[X_IDX] - B[X_IDX]);
  a3 = a2-a1;
  if( a3>M_PI ) a3 -= 2.0*M_PI;
  if( a3<=-M_PI ) a3 += 2.0*M_PI;
  return a3;
#endif
}

/*
** Return the squared distance between two VectorXYs.
*/
CTHULHU_INLINE double VectorXY_distance_squared(VectorXY A, VectorXY B){
  double dx = B[X_IDX] - A[X_IDX];
  double dy = B[Y_IDX] - A[Y_IDX];
  return dx*dx + dy*dy;
}

/*
** Return the distance between two VectorXYs.
*/
CTHULHU_INLINE double VectorXY_distance(VectorXY A, VectorXY B){
  double dx = B[X_IDX] - A[X_IDX];
  double dy = B[Y_IDX] - A[Y_IDX];
  return sqrt(dx*dx + dy*dy);
}

static int  vectorxy_method_add(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A,*B,*C;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A B" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_vectorxy);
  if(!A) return TCL_ERROR;

  B=Odie_GetMatrixFromTclObj(interp,objv[2],MATFORM_vectorxy);
  if(!B) return TCL_ERROR;

  C=Matrix_NewObj();
  Matrix_Alloc(C,MATFORM_vectorxy);
  VectorXY_Add(C->matrix,A->matrix,B->matrix);
  Tcl_SetObjResult(interp,Matrix_To_TclObj(C));
  return TCL_OK;
}

static int  vectorxy_method_subtract(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  MATOBJ *A,*B,*C;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A B" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_vectorxy);
  if(!A) return TCL_ERROR;

  B=Odie_GetMatrixFromTclObj(interp,objv[2],MATFORM_vectorxy);
  if(!B) return TCL_ERROR;

  C=Matrix_NewObj();
  Matrix_Alloc(C,MATFORM_vectorxy);
  VectorXY_Subtract(C->matrix,A->matrix,B->matrix);
  Tcl_SetObjResult(interp,Matrix_To_TclObj(C));
  return TCL_OK;
}


static int  vectorxy_method_add_stream(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A,*B,*C;
  int i,n;
  Tcl_Obj *pObj,*pResult;

  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A BLIST" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_vectorxy);
  if(!A) return TCL_ERROR;
  if( Tcl_ListObjLength(interp, objv[2], &n) ) return TCL_ERROR;
  pResult=Tcl_NewListObj(0,NULL);
  for(i=0; i<n; i++){
    Tcl_ListObjIndex(0, objv[2], i, &pObj);
    B=Odie_GetMatrixFromTclObj(interp,pObj,MATFORM_vectorxy);
    if(!B) return TCL_ERROR;

    C=Matrix_NewObj();
    Matrix_Alloc(C,MATFORM_vectorxy);
    VectorXY_Add(C->matrix,A->matrix,B->matrix);
    Tcl_ListObjAppendElement(0, pResult, Matrix_To_TclObj(C));
  }
  Tcl_SetObjResult(interp,pResult);
  return TCL_OK;
}

static int  vectorxy_method_angle(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  VectorXY A, B, C;
  if( objc!=4 ){
    Tcl_WrongNumArgs(interp, 1, objv, "A B C");
    return TCL_ERROR;
  }
  if(Odie_Set_VECTORXY_FromObj(interp,objv[1],A)) return TCL_ERROR;
  if(Odie_Set_VECTORXY_FromObj(interp,objv[2],B)) return TCL_ERROR;
  if(Odie_Set_VECTORXY_FromObj(interp,objv[3],C)) return TCL_ERROR;
  Tcl_SetObjResult(interp, Tcl_NewDoubleObj(VectorXY_angleOf(A, B, C)));
  return TCL_OK;
}

static int  vectorxy_method_point_on_segment(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
#ifdef NEVER
  VectorXY A, B, C;
  if( objc!=4 ){
    Tcl_WrongNumArgs(interp, 1, objv, "A B P");
    return TCL_ERROR;
  }
  if(Odie_Set_VECTORXY_FromObj(interp,objv[1],A)) return TCL_ERROR;
  if(Odie_Set_VECTORXY_FromObj(interp,objv[2],B)) return TCL_ERROR;
  if(Odie_Set_VECTORXY_FromObj(interp,objv[3],C)) return TCL_ERROR;
  Tcl_SetObjResult(interp, Tcl_NewIntObj(VectorXY_Point_On_Segment(A, B, C)));
#endif
  return TCL_OK;
}

/*
** tclcmd: vectorxy crossproduct A B C
** Return the the cross product of AB*BC
*/
static int  vectorxy_method_crossproduct(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  VectorXY A, B, C;
  if( objc!=4 ){
    Tcl_WrongNumArgs(interp, 1, objv, "A B C");
    return TCL_ERROR;
  }
  if(Odie_Set_VECTORXY_FromObj(interp,objv[1],A)) return TCL_ERROR;
  if(Odie_Set_VECTORXY_FromObj(interp,objv[2],B)) return TCL_ERROR;
  if(Odie_Set_VECTORXY_FromObj(interp,objv[3],C)) return TCL_ERROR;
  Tcl_SetObjResult(interp, Tcl_NewDoubleObj(VectorXY_crossProduct(A, B, C)));
  return TCL_OK;
}

static int  vectorxy_method_distance (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  VECTORXY A,B;
  if( objc!=3 ){
    Tcl_WrongNumArgs(interp, 1, objv, "A B");
    return TCL_ERROR;
  }
  double result=0.0,dx,dy;
  if(Odie_Set_VECTORXY_FromObj(interp,objv[1],A)) return TCL_ERROR;
  if(Odie_Set_VECTORXY_FromObj(interp,objv[2],B)) return TCL_ERROR;
  dx=B[X_IDX]-A[X_IDX];
  dy=B[Y_IDX]-A[Y_IDX];
  
  result=sqrt(dx*dx + dy*dy);
  Tcl_SetObjResult(interp,Tcl_NewDoubleObj(result));
  return TCL_OK;
}

static int  vectorxy_method_length (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  VECTORXY A;
  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "A");
    return TCL_ERROR;
  }
  double result=0.0;
  if(Odie_Set_VECTORXY_FromObj(interp,objv[1],A)) return TCL_ERROR;  
  result=sqrt(A[X_IDX]*A[X_IDX] + A[Y_IDX]*A[Y_IDX]);
  Tcl_SetObjResult(interp,Tcl_NewDoubleObj(result));
  return TCL_OK;
}

static int  vectorxy_method_normalize (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  VECTORXY A;
  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "A");
    return TCL_ERROR;
  }
  double result=0.0,dx,dy;
  if(Odie_Set_VECTORXY_FromObj(interp,objv[1],A)) return TCL_ERROR;
  VectorXY_Normalize(A);
  Tcl_SetObjResult(interp,Matrix_To_TclObj(A));
  return TCL_OK;
}

static int  vectorxy_method_dotproduct(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  VectorXY A, B, C;
  if( objc!=4 ){
    Tcl_WrongNumArgs(interp, 1, objv, "A B C");
    return TCL_ERROR;
  }
  if(Odie_Set_VECTORXY_FromObj(interp,objv[1],A)) return TCL_ERROR;
  if(Odie_Set_VECTORXY_FromObj(interp,objv[2],B)) return TCL_ERROR;
  if(Odie_Set_VECTORXY_FromObj(interp,objv[3],C)) return TCL_ERROR;
  Tcl_SetObjResult(interp, Tcl_NewDoubleObj(VectorXY_dotProduct(A, B, C)));
  return TCL_OK;
}


/*
** tclcmd:  triag_test_rightof X0 Y0 X1 Y1 X2 Y2
**
** A TCL command for testing the rightOf() function.
*/
static int  vectorxy_method_rightof(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  VectorXY A, B, C;
  if( objc!=4 ){
    Tcl_WrongNumArgs(interp, 1, objv, "X0 Y0 X1 Y1 X2 Y2");
    return TCL_ERROR;
  }
  if(Odie_Set_VECTORXY_FromObj(interp,objv[1],A)) return TCL_ERROR;
  if(Odie_Set_VECTORXY_FromObj(interp,objv[2],B)) return TCL_ERROR;
  if(Odie_Set_VECTORXY_FromObj(interp,objv[3],C)) return TCL_ERROR;
  Tcl_SetObjResult(interp, Tcl_NewIntObj(VectorXY_rightOf(A, B, C)));
  return TCL_OK;
}

static int  vectorxy_method_rotate_and_size (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  /*
  ** Apply Matrices
  */
  Tcl_Obj *pResult=Tcl_NewObj();
  int i;
  double matA[6] = {1.0,0.0,0.0,1.0,0.0,0.0};
  double nx,ny,scalex,scaley,angle;
  
  if( objc < 7 ){
    Tcl_WrongNumArgs(interp, 1, objv, "normalx normaly sizex sizey V ?V?...");
    return TCL_ERROR;
  }
  
  if(Tcl_GetDoubleFromObj(interp,objv[1],&nx)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&ny)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[3],&scalex)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[4],&scaley)) return TCL_ERROR;
  
  angle=atan2(ny,nx);
  matA[0]=cos(angle);
  matA[1]=sin(angle);
  matA[2]=-sin(angle);
  matA[3]=cos(angle);
  matA[4]=0.0;
  matA[5]=0.0;
  
  scalex*=0.5;
  scaley*=0.5;
  for(i=5;i<objc;i++) {
    double sx,sy;
    VECTORXY A,B;
    if(Odie_Get_VECTORXY_FromObj(interp,objv[i],&A)) return TCL_ERROR;
    sx=A[X_IDX] * scalex;
    sy=A[Y_IDX] * scaley;
    B[X_IDX]=matA[0]*sx+matA[1]*sy+matA[4];
    B[Y_IDX]=matA[2]*sx+matA[3]*sy+matA[5];  
    Tcl_ListObjAppendElement(interp,pResult,Odie_New_VECTORXY_Obj(B));
  }
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

static int  vectorxy_method_scale (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  Tcl_Obj *pResult=Tcl_NewObj();
  int i;
  double scalex,scaley;
  
  if( objc < 5 ){
    Tcl_WrongNumArgs(interp, 1, objv, "sizex sizey V ?V...?");
    return TCL_ERROR;
  }
  
  if(Tcl_GetDoubleFromObj(interp,objv[1],&scalex)) return TCL_ERROR;
  if(Tcl_GetDoubleFromObj(interp,objv[2],&scaley)) return TCL_ERROR;
  
  scalex*=0.5;
  scaley*=0.5;
  for(i=3;i<objc;i+=2) {
    double x,y;
    VECTORXY A,B;
    if(Odie_Get_VECTORXY_FromObj(interp,objv[i],&A)) return TCL_ERROR;
    x=A[X_IDX];
    y=A[Y_IDX];  
    B[X_IDX]=x*scalex;
    B[Y_IDX]=y*scaley;
    Tcl_ListObjAppendElement(interp,pResult,Odie_New_VECTORXY_Obj(B));
  }
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

static int  vectorxy_method_translate_and_zoom (
  ClientData *simulator,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
/*
** Apply Matrices
*/
  
  Tcl_Obj *pResult=Tcl_NewObj();
  int i;
  double zoom;
  VECTORXY center;
  if( objc < 5 ){
    Tcl_WrongNumArgs(interp, 1, objv, "zoom CENTER V ?V...?");
    return TCL_ERROR;
  }   
  if(Tcl_GetDoubleFromObj(interp,objv[1],&zoom)) return TCL_ERROR;
  
  if(Odie_Set_VECTORXY_FromObj(interp,objv[2],center)) return TCL_ERROR;
  for(i=3;i<objc;i++) {
    VECTORXY A,B;
    if(Odie_Get_VECTORXY_FromObj(interp,objv[i],&A)) return TCL_ERROR;
    B[X_IDX]=(A[X_IDX]/zoom)+center[X_IDX];
    B[Y_IDX]=(A[Y_IDX]/zoom)+center[Y_IDX];
    Tcl_ListObjAppendElement(interp,pResult,Odie_New_VECTORXY_Obj(B));
  }
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

DLLEXPORT int Odie_VectorXY_Init(Tcl_Interp *interp) {
  Tcl_Namespace *modPtr;


  modPtr=Tcl_FindNamespace(interp,"vectorxy",NULL,TCL_NAMESPACE_ONLY);
  if(!modPtr) {
    modPtr = Tcl_CreateNamespace(interp, "vectorxy", NULL, NULL);
  }
  
  Tcl_CreateObjCommand(interp,"::vectorxy::add",(Tcl_ObjCmdProc *)vectorxy_method_add,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vectorxy::subtract",(Tcl_ObjCmdProc *)vectorxy_method_subtract,NULL,NULL);

  Tcl_CreateObjCommand(interp,"::vectorxy::add_stream",(Tcl_ObjCmdProc *)vectorxy_method_add_stream,NULL,NULL);

  Tcl_CreateObjCommand(interp,"::vectorxy::angle",(Tcl_ObjCmdProc *)vectorxy_method_angle,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vectorxy::crossproduct",(Tcl_ObjCmdProc *)vectorxy_method_crossproduct,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vectorxy::dotproduct",(Tcl_ObjCmdProc *)vectorxy_method_dotproduct,NULL,NULL);

  Tcl_CreateObjCommand(interp,"::vectorxy::distance",(Tcl_ObjCmdProc *)vectorxy_method_distance,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vectorxy::rightof",(Tcl_ObjCmdProc *)vectorxy_method_rightof,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vectorxy::rotate_and_size",(Tcl_ObjCmdProc *)vectorxy_method_rotate_and_size,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vectorxy::length",(Tcl_ObjCmdProc *)vectorxy_method_normalize,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vectorxy::normalize",(Tcl_ObjCmdProc *)vectorxy_method_normalize,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vectorxy::scale",(Tcl_ObjCmdProc *)vectorxy_method_scale,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vectorxy::translate_and_zoom",(Tcl_ObjCmdProc *)vectorxy_method_translate_and_zoom,NULL,NULL);

  Tcl_CreateEnsemble(interp, modPtr->fullName, modPtr, TCL_ENSEMBLE_PREFIX);
  Tcl_Export(interp, modPtr, "[a-z]*", 1);
  

  return TCL_OK;
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted cmodules/math/generic/vectorxyz.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
#include "odieInt.h"

CTHULHU_INLINE void vectorxyz_cross_product(VectorXYZ C,VectorXYZ A,VectorXYZ B) {
  C[X_IDX] = A[Y_IDX] * B[Z_IDX] - A[Z_IDX] * B[Y_IDX];
  C[Y_IDX] = A[Z_IDX] * B[X_IDX] - A[X_IDX] * B[Z_IDX];
  C[Z_IDX] = A[X_IDX] * B[Y_IDX] - A[Y_IDX] * B[X_IDX];
}


CTHULHU_INLINE void VectorXYZ_Add(VECTORXY C,VECTORXY A,VECTORXY B) {
  C[X_IDX]=B[X_IDX]+A[X_IDX];
  C[Y_IDX]=B[Y_IDX]+A[Y_IDX];
  C[Z_IDX]=B[Z_IDX]+A[Z_IDX];
}

CTHULHU_INLINE void VectorXYZ_Subtract(VECTORXY C,VECTORXY A,VECTORXY B) {
  C[X_IDX]=B[X_IDX]-A[X_IDX];
  C[Y_IDX]=B[Y_IDX]-A[Y_IDX];
  C[Z_IDX]=B[Z_IDX]-A[Z_IDX];
}


static int  vectorxyz_method_create (
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A;
  double result;
  if(objc != 2 && objc !=4) {
    Tcl_WrongNumArgs( interp, 1, objv, "LIST\nor\nx y z" );
    return TCL_ERROR;
  }
  if(objc==2) {
    A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_vectorxyz);
    if(!A) return TCL_ERROR;
  } else {
    double x,y,z;
    if(Tcl_GetDoubleFromObj(interp,objv[1],&x)) {
      return TCL_ERROR;
    }
    if(Tcl_GetDoubleFromObj(interp,objv[2],&y)) {
      return TCL_ERROR;
    }
    if(Tcl_GetDoubleFromObj(interp,objv[3],&z)) {
      return TCL_ERROR;
    }
    A=Matrix_NewObj();
    Matrix_Alloc(A,MATFORM_vectorxyz);
    *(A->matrix+X_IDX)=x;
    *(A->matrix+Y_IDX)=y;
    *(A->matrix+Z_IDX)=z;
  }
  Tcl_SetObjResult(interp,Matrix_To_TclObj(A));
  return TCL_OK;
}


static int  vectorxyz_method_add(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A,*B,*C;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A B" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_vectorxyz);
  if(!A) return TCL_ERROR;

  B=Odie_GetMatrixFromTclObj(interp,objv[2],MATFORM_vectorxyz);
  if(!B) return TCL_ERROR;

  C=Matrix_NewObj();
  Matrix_Alloc(C,MATFORM_vectorxyz);
  VectorXYZ_Add(C->matrix,A->matrix,B->matrix);
  Tcl_SetObjResult(interp,Matrix_To_TclObj(C));
  return TCL_OK;
}



static int  vectorxyz_method_subtract(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A,*B,*C;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A B" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_vectorxyz);
  if(!A) return TCL_ERROR;

  B=Odie_GetMatrixFromTclObj(interp,objv[2],MATFORM_vectorxyz);
  if(!B) return TCL_ERROR;

  C=Matrix_NewObj();
  Matrix_Alloc(C,MATFORM_vectorxyz);
  VectorXYZ_Subtract(C->matrix,A->matrix,B->matrix);
  Tcl_SetObjResult(interp,Matrix_To_TclObj(C));
  return TCL_OK;
}



static int  vectorxyz_method_cross_product (
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A,*B,*C;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A B" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_vectorxyz);
  if(!A) return TCL_ERROR;

  B=Odie_GetMatrixFromTclObj(interp,objv[2],MATFORM_vectorxyz);
  if(!B) return TCL_ERROR;

  C=Matrix_NewObj();
  Matrix_Alloc(C,MATFORM_vectorxyz);
  vectorxyz_cross_product(C->matrix,A->matrix,B->matrix);

  Tcl_SetObjResult(interp,Matrix_To_TclObj(C));
  return TCL_OK;
}

CTHULHU_INLINE double vectorxyz_dot_product(VectorXYZ A,VectorXYZ B) {
  return A[X_IDX] * B[X_IDX] + A[Y_IDX] * B[Y_IDX] + A[Z_IDX] * B[Z_IDX];
}

static int  vectorxyz_method_dot_product (
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A,*B;
  double result;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "A B" );
    return TCL_ERROR;
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_vectorxyz);
  if(!A) return TCL_ERROR;

  B=Odie_GetMatrixFromTclObj(interp,objv[2],MATFORM_vectorxyz);
  if(!B) return TCL_ERROR;

  result=vectorxyz_dot_product(A->matrix,B->matrix);

  Tcl_SetObjResult(interp,Tcl_NewDoubleObj(result));
  return TCL_OK;
}


/*
 * A - the vector to be tranformed
 * B - the affine tranformation matrix
 * R - a place to dump the result
 *
 * A and R MUST BE DIFFERENT
 */

CTHULHU_INLINE void vectorxyz_MatrixMultiply(VECTORXYZ R,VECTORXYZ A,AFFINE M)
{
  int i;
  
  for(i=0;i<3;i++)
  {
    R[i]=A[X_IDX]*M[0][i] + A[Y_IDX]*M[1][i] + A[Z_IDX]* M[2][i] + M[3][i];
  }
}

static int  vectorxyz_method_transform (
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *Affine,*A,*C;
  if(objc < 3) {
    Tcl_WrongNumArgs( interp, 1, objv, "affine vector ?vector...?" );
  }

  Affine=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_affine);
  if(!Affine) return TCL_ERROR;
  
  if(objc==3) {
    A=Odie_GetMatrixFromTclObj(interp,objv[2],MATFORM_vectorxyz);
    if(!A) return TCL_ERROR;
  
    C=Matrix_NewObj();
    Matrix_Alloc(C,MATFORM_vectorxyz);
    vectorxyz_MatrixMultiply(C->matrix,A->matrix,Affine->matrix);
    
    Tcl_SetObjResult(interp,Matrix_To_TclObj(C));
    return TCL_OK;
  }
  int i,n;
  n=objc-2;
  Tcl_Obj **pArray=Odie_Alloc(sizeof(Tcl_Obj)*n);
  for(i=0;i<n;i++) {
    A=Odie_GetMatrixFromTclObj(interp,objv[i+2],MATFORM_vectorxyz);
    if(!A) return TCL_ERROR;
  
    C=Matrix_NewObj();
    Matrix_Alloc(C,MATFORM_vectorxyz);
    vectorxyz_MatrixMultiply(C->matrix,A->matrix,Affine->matrix);
    pArray[i]=Matrix_To_TclObj(C);
  }
  Tcl_SetObjResult(interp,Tcl_NewListObj(n,pArray));
  Odie_Free(pArray);
  return TCL_OK;
}

CTHULHU_INLINE double odiemath_vectorxyz_length(VECTOR A)
{
  double length=(sqrt(A[0]*A[0]+A[1]*A[1]+A[2]*A[2]));
  if(length<__FLT_EPSILON__) {
    return 0.0;
  }
  return length;
}

static int  vectorxyz_method_length (
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A;
  double result;
  if(objc != 2) {
    Tcl_WrongNumArgs( interp, 1, objv, "A" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_vectorxyz);
  if(!A) return TCL_ERROR;

  result=odiemath_vectorxyz_length(A->matrix);

  Tcl_SetObjResult(interp,Tcl_NewDoubleObj(result));
  return TCL_OK;
}

CTHULHU_INLINE double odiemath_vectorxyz_lengthInvSqr(VECTOR A) {
  double r=A[0]+A[1]+A[2];
  if(fabs(r)<__FLT_EPSILON__) {
    return NAN;
  }
  return (1.0/(A[0]*A[0]+A[1]*A[1]+A[2]*A[2]));
}

static int  vectorxyz_method_length_inv_sqr (
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A;
  double result;
  if(objc != 2) {
    Tcl_WrongNumArgs( interp, 1, objv, "A" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_vectorxyz);
  if(!A) return TCL_ERROR;

  result=odiemath_vectorxyz_lengthInvSqr(A->matrix);

  Tcl_SetObjResult(interp,Tcl_NewDoubleObj(result));
  return TCL_OK;
}

CTHULHU_INLINE void odiemath_vectorxyz_normalize(VectorXYZ A)
{
  double d;
  double r=odiemath_vectorxyz_length(A);
  if(fabs(r) < __FLT_EPSILON__) {
    A[0]=0.0;
    A[1]=0.0;
    A[3]=0.0;
  } else {
    d=1.0 / r;
    A[0]*=d;
    A[1]*=d;
    A[2]*=d;
  }
}

static int  vectorxyz_method_normalize (
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  MATOBJ *A,*C;
  double result;
  if(objc != 2) {
    Tcl_WrongNumArgs( interp, 1, objv, "A" );
  }
  A=Odie_GetMatrixFromTclObj(interp,objv[1],MATFORM_vectorxyz);
  if(!A) return TCL_ERROR;

  C=Matrix_NewObj();
  Matrix_Copy(C,A);
  odiemath_vectorxyz_normalize(C->matrix);
  Tcl_SetObjResult(interp,Matrix_To_TclObj(C));
  return TCL_OK;
}

DLLEXPORT int Odie_VectorXYZ_Init(Tcl_Interp *interp) {
  Tcl_Namespace *modPtr;

  modPtr=Tcl_FindNamespace(interp,"vectorxyz",NULL,TCL_NAMESPACE_ONLY);
  if(!modPtr) {
    modPtr = Tcl_CreateNamespace(interp, "vectorxyz", NULL, NULL);
  }
  Tcl_CreateObjCommand(interp,"::vectorxyz::add",(Tcl_ObjCmdProc *)vectorxyz_method_add,NULL,NULL);  
  Tcl_CreateObjCommand(interp,"::vectorxyz::subtract",(Tcl_ObjCmdProc *)vectorxyz_method_subtract,NULL,NULL);  

  Tcl_CreateObjCommand(interp,"::vectorxyz::create",(Tcl_ObjCmdProc *)vectorxyz_method_create,NULL,NULL);  
  Tcl_CreateObjCommand(interp,"::vectorxyz::cross_product",(Tcl_ObjCmdProc *)vectorxyz_method_cross_product,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vectorxyz::dot_product",(Tcl_ObjCmdProc *)vectorxyz_method_dot_product,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vectorxyz::length",(Tcl_ObjCmdProc *)vectorxyz_method_length,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vectorxyz::length_inv_sqr",(Tcl_ObjCmdProc *)vectorxyz_method_length_inv_sqr,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vectorxyz::normalize",(Tcl_ObjCmdProc *)vectorxyz_method_normalize,NULL,NULL);
  Tcl_CreateObjCommand(interp,"::vectorxyz::transform",(Tcl_ObjCmdProc *)vectorxyz_method_transform,NULL,NULL);

  Tcl_CreateEnsemble(interp, modPtr->fullName, modPtr, TCL_ENSEMBLE_PREFIX);
  Tcl_Export(interp, modPtr, "[a-z]*", 1);
  return TCL_OK;
}

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






































































































































































































































































































































































































































































































































































































































































































Deleted cmodules/math/math.man.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
[comment {-*- tao -*-}]
[manpage_begin odielib::math n 2.0]
[keywords odielib]
[copyright {2000-2014 Sean Woods <yoda@etoyoc.com>}]
[moddesc {The Odielib Accellerated Math Module}]
[titledesc {The Odielib Accellerated Math Module}]
[category {Mathematics}]
[require odielib 2.0]
[description]

[para]

The [package math] package is included with [package odielib]. It contains
a series of C-accellerated routines for matrix math, tailored for graphics
and basic (i.e 3 dimensions and time) physics.

[section COMMANDS]
[list_begin definitions]
[call [cmd affine2d::combine] [arg "transform"] [arg "transform"] [opt [arg "transform..."]]]
Accepts N 3x3 affine matrices, and returns a 3x3 matrix which is the combination of them all.

[call [cmd affine2d::rotation_from_angle] [arg "theta"] [opt [arg "units"]]]
Computes a 2d affine rotation (a 3x3 matrix) from an angle [arg theta].
[para]
Valid units r - radians (2pi = one turn), g - gradian (400 = one turn), d - degree (360 = 1 turn)
 
[call [cmd affine2d::rotation_from_normal] [arg "normalx"] [arg "normaly"]]
Computes a 2d affine rotation (a 3x3 matrix) from a directional normal, given
my %of travel in X and Y.



[list_end]
[section "REFERENCES"]


[section AUTHORS]
Sean Woods

[vset CATEGORY tao]
[include scripts/feedback.inc]

[manpage_end]
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






















































































Deleted cmodules/odieutil/constant.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/*
** constant.c
** Series of routines to minimize memory usage through the use
** of shared strings
*/
#include "odieInt.h"

#include <strings.h>

Tcl_HashTable constant_strings;
Tcl_Obj *irmStatic[14];

typedef struct constObj {
  char    *string;
  Tcl_Obj *tclobj;
} constObj;

Tcl_Obj *Odie_shared_tclobj(int which) {
  if(which > ODIE_STATIC_MAX || which < 0) {
    Tcl_IncrRefCount(irmStatic[ODIE_STATIC_NULL]);
    return irmStatic[ODIE_STATIC_NULL];
  }
  Tcl_IncrRefCount(irmStatic[which]);
  return irmStatic[which];
}

Tcl_Obj *Odie_NewBooleanObj(int value) {
  if(value) {
    return Odie_shared_tclobj(ODIE_STATIC_ONE);
  }
  return Odie_shared_tclobj(ODIE_STATIC_ZERO);
}

Tcl_Obj *Odie_NewIntObj(int value) {
  if(value>=0 && value < 10) {
    int idx=ODIE_STATIC_ZERO+value;
    Tcl_IncrRefCount(irmStatic[idx]);
    return irmStatic[idx];
  }
  if(value==-1) {
    Tcl_IncrRefCount(irmStatic[ODIE_STATIC_NEG1]);
    return irmStatic[ODIE_STATIC_NEG1];
  }
  return Tcl_NewIntObj(value);
}


static constObj *Odie_constant(const char *zName,int create) {
  int len,isNew=0;
  Tcl_HashEntry *pEntry;
  constObj *p;
  if(zName==NULL) {
    return NULL;
  }
  if(create) {
    pEntry=Tcl_CreateHashEntry(&constant_strings,zName,&isNew);
  } else {
    pEntry=Tcl_FindHashEntry(&constant_strings,zName);
  }
  if(isNew) {
    len = strlen(zName);
    p =(constObj*)Odie_Alloc(sizeof(*p)+len+1);
    p->string=p+1;
    strncpy(p->string, zName, len+1);
    p->tclobj=Tcl_NewStringObj(zName,len);
    Tcl_IncrRefCount(p->tclobj);
    Tcl_SetHashValue(pEntry,(ClientData)p);
    return p;
  }
  if(pEntry) {
    p=(constObj*)Tcl_GetHashValue(pEntry);
    return p;
  }
  return NULL;
}

int Odie_SameString(char *aPtr,char *bPtr) {
  if(aPtr==bPtr) {
    return 1;
  }
  if(!bPtr || !aPtr) {
    return 0;
  }
  if(strcmp(aPtr,bPtr)==0) {
    return 1;
  }
  return 0;
}
char *Odie_constant_string(const char *zName) {
  constObj *p;
  p=Odie_constant(zName,1);
  return p->string;
}


Tcl_Obj *Odie_constant_tclobj(const char *zName) {
  constObj *p;
  p=Odie_constant(zName,1);
  Tcl_IncrRefCount(p->tclobj);
  return p->tclobj;
}

Tcl_Obj *Odie_NewStringObj(const char *str) {
  /*
  if(!str) {
    return Tcl_NewObj();
  }
  return Tcl_NewStringObj(str,-1);
  */
  return Odie_constant_tclobj(str);
}

static int constantMapCmd(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  char *newName;
  Tcl_Obj *result;

  if(objc != 2) {
      Tcl_WrongNumArgs(interp, 1, objv, "string");
  }
  newName=Tcl_GetString(objv[1]);
  result=Odie_constant_tclobj(newName);
  if (!result) return TCL_ERROR;
  Tcl_SetObjResult(interp,result);
  return TCL_OK;
}

DLLEXPORT int Odie_Constant_Init(Tcl_Interp *interp) {
  static int once = 1;

  if( once ){
    int i;
    once = 0;

    irmStatic[ODIE_STATIC_NULL]=Tcl_NewObj();
    Tcl_IncrRefCount(irmStatic[ODIE_STATIC_NULL]);
    
    irmStatic[ODIE_STATIC_ZERO] = Tcl_NewBooleanObj(0);
    Tcl_IncrRefCount(irmStatic[ODIE_STATIC_ZERO]);
    
    irmStatic[ODIE_STATIC_ONE] = Tcl_NewBooleanObj(1);
    Tcl_IncrRefCount(irmStatic[ODIE_STATIC_ONE]);
    for(i=2;i<10;i++) {
      int idx=ODIE_STATIC_ZERO+i;
      irmStatic[idx] = Tcl_NewIntObj(i);
      Tcl_IncrRefCount(irmStatic[idx]);
    }

    irmStatic[ODIE_STATIC_FZERO] = Tcl_NewDoubleObj(0.0);
    Tcl_IncrRefCount(irmStatic[ODIE_STATIC_FZERO]);

    irmStatic[ODIE_STATIC_NEG1] = Tcl_NewIntObj(-1);
    Tcl_IncrRefCount(irmStatic[ODIE_STATIC_NEG1]);

    Tcl_InitHashTable(&constant_strings,TCL_STRING_KEYS);
  }
  
  return TCL_OK;
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






































































































































































































































































































































Deleted cmodules/odieutil/constant.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
enum ODIE_NULL_REPRESENTATION {
  ODIE_NULL_NULL,
  ODIE_NULL_ZERO,
  ODIE_NULL_EMPTY
};

enum ODIE_STATIC_OBJS {
  ODIE_STATIC_ZERO,
  ODIE_STATIC_ONE,
  ODIE_STATIC_TWO,
  ODIE_STATIC_THREE,
  ODIE_STATIC_FOUR,
  ODIE_STATIC_FIVE,
  ODIE_STATIC_SIX,
  ODIE_STATIC_SEVEN,
  ODIE_STATIC_EIGHT,
  ODIE_STATIC_NINE,
  ODIE_STATIC_NULL,
  ODIE_STATIC_NEG1,
  ODIE_STATIC_FZERO,
  ODIE_STATIC_INF,
  ODIE_STATIC_MAX
};
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<














































Deleted cmodules/odieutil/cthulhu.ini.
1
2
3
4
set here [file dirname [file normalize [info script]]]

::cthulhu::add_directory $here {
}
<
<
<
<








Deleted cmodules/odieutil/listcmd.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#include "odieInt.h"

//const Tcl_ObjType *tclListType;
const Tcl_ObjType *tclIntType;
//const Tcl_ObjType *tclDoubleType;

/*
** Print a trace message
*/
void Odie_trace_printf(Tcl_Interp *interp,const char *zFormat, ...){
  int n;
  va_list ap;
  char zBuf[4000];

  va_start(ap, zFormat);
  strcpy(zBuf, "puts -nonewline {");
  n = strlen(zBuf);
  vsnprintf(&zBuf[n], sizeof(zBuf)-5-n, zFormat, ap);
  strcat(zBuf, "}\n");
  Tcl_Eval(interp, zBuf);
}

int Odie_GetIntFromObj(Tcl_Interp *interp,Tcl_Obj *tclObj,int *result) {
  if(tclObj->typePtr==tclDoubleType) {
    double s=tclObj->internalRep.doubleValue;
    *result=(int)round(s);
    return TCL_OK;
  }
  if(tclObj->typePtr==tclIntType) {
    *result=tclObj->internalRep.longValue;
    return TCL_OK;
  }
  if(!Tcl_GetIntFromObj(NULL,tclObj,result)) {
    return TCL_OK;
  }
  double s;
  if(Tcl_GetDoubleFromObj(interp,tclObj,&s)) return TCL_ERROR;
  *result=(int)round(s);
  return TCL_OK;
}

Tcl_Obj *Odie_Obj_To_Int(Tcl_Obj *tclObj) {
  if(tclObj->typePtr==tclDoubleType) {
    double s=tclObj->internalRep.doubleValue;
    return Tcl_NewIntObj((int)round(s));
  }
  if(tclIntType->setFromAnyProc(NULL,tclObj)==TCL_OK) {
    return tclObj;
  }
  double s;
  if (Tcl_GetDoubleFromObj(NULL,tclObj,&s)) {
    return tclObj;
  }
  return Tcl_NewIntObj((int)round(s));
}

static int getCmd(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  Tcl_Obj *result;
  if(objc != 2) {
      Tcl_WrongNumArgs(interp, 1, objv, "varname");
  }
  result=Tcl_ObjGetVar2(interp,objv[1],NULL,0);
  if(!result) {
    Tcl_ResetResult(interp);
    result=Odie_shared_tclobj(ODIE_STATIC_NULL);
  }
  Tcl_SetObjResult(interp,result);
  return TCL_OK;
}

static int listToInt(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  int i;
  Tcl_Obj *resultPtr;
  if(objc < 2) {
      Tcl_WrongNumArgs(interp, 1, objv, "element ?element");
  }
  resultPtr=Tcl_NewObj();
  for(i=1;i<objc;i++) {
    Tcl_ListObjAppendElement(interp,resultPtr,Odie_Obj_To_Int(objv[i]));
  }
  Tcl_SetObjResult(interp,resultPtr);
  return TCL_OK;
}

/*
** topic:
** command: ladd
** arglist: variable element ...
** title: Add all [emph element] arguments to list stored in [emph variable]
** description:
** If [emph varname] does not exist, an empty list is created. Only one
** instance of [emph element] is added per list. If an element is already
** in the list, it is not added.
*/

static int laddCmd(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  Tcl_Obj *varPtr,*listObj,*resultPtr;
  int length;
  Tcl_Obj **data;
  
  if(objc < 2) {
      Tcl_WrongNumArgs(interp, 1, objv, "varname element ...");
  }
  varPtr=Tcl_ObjGetVar2(interp,objv[1],NULL,0);
  if(!varPtr) {
    Tcl_ResetResult(interp);
    varPtr=Tcl_NewObj();
  }
  /*
  ** Make sure we have well formed list
  */
  if(Tcl_ListObjGetElements(interp,varPtr,&length,&data)!=TCL_OK) {
    return TCL_ERROR;
  }
  listObj=Tcl_NewListObj(length,data);
  
  if(objc>2) {
    if(Tcl_ListObjReplace(interp,listObj,length,0,(objc-2), (objv+2))) {
      return TCL_ERROR;
    }
  }
  resultPtr=Odie_ListObj_Sort(listObj);
  Tcl_ObjSetVar2(interp,objv[1],NULL,resultPtr,0);
  Tcl_SetObjResult(interp,resultPtr);
  return TCL_OK;
}

/*
** topic:
** command: ldelete
** arglist: variable element ...
** title: Remove all instances of [emph element] from a list stored in [emph variable]
** description:
** If [emph varname] does not exist, an empty list is created.
*/
static int  ldeleteCmd (
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
) {
  int listLength, idx;
  Tcl_Obj *resultPtr,*listPtr;
  Tcl_Obj **listObjPtrs;

  if (objc < 2) {
    Tcl_WrongNumArgs(interp, 1, objv, "variable element ...");
    return TCL_ERROR;
  }
  listPtr=Tcl_ObjGetVar2(interp,objv[1],NULL,0);
  if(!listPtr) {
    Tcl_ResetResult(interp);
    listPtr=Tcl_NewObj();
  } else {
    listPtr=Tcl_DuplicateObj(listPtr);
  }
  
  if(Tcl_ListObjGetElements(interp, listPtr, &listLength, &listObjPtrs)) {
    return TCL_ERROR;
  }

  resultPtr=Tcl_NewObj();
  for(idx=0;idx<listLength;idx++) {
    int matchIdx=Odie_Lsearch((objc-2),(Tcl_Obj **)(objv+2),listObjPtrs[idx]);
    if(matchIdx < 0) {
      Tcl_ListObjAppendElement(interp,resultPtr,listObjPtrs[idx]);
    }
  }
  Tcl_DecrRefCount(listPtr);
  Tcl_ObjSetVar2(interp,objv[1],NULL,resultPtr,0);
  Tcl_SetObjResult(interp,resultPtr);
  return TCL_OK;
}



DLLEXPORT int Odie_Listcmd_Init(Tcl_Interp *interp) {

  tclIntType=Tcl_GetObjType("int");
  tclListType=Tcl_GetObjType("list");
  tclDoubleType=Tcl_GetObjType("double");

  Tcl_CreateObjCommand(interp, "get", getCmd, 0, 0);
  Tcl_CreateObjCommand(interp, "ladd", laddCmd, 0, 0);
  Tcl_CreateObjCommand(interp, "ldelete", ldeleteCmd, 0, 0);
  Tcl_CreateObjCommand(interp, "list_to_int", listToInt, 0, 0);

  return TCL_OK;
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
























































































































































































































































































































































































































Deleted cmodules/odieutil/md5.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
/*
** SQLite uses this code for testing only.  It is not a part of
** the SQLite library.  This file implements two new TCL commands
** "md5" and "md5file" that compute md5 checksums on arbitrary text
** and on complete files.  These commands are used by the "testfixture"
** program to help verify the correct operation of the SQLite library.
**
** The original use of these TCL commands was to test the ROLLBACK
** feature of SQLite.  First compute the MD5-checksum of the database.
** Then make some changes but rollback the changes rather than commit
** them.  Compute a second MD5-checksum of the file and verify that the
** two checksums are the same.  Such is the original use of this code.
** New uses may have been added since this comment was written.
*/
/*
 * This code implements the MD5 message-digest algorithm.
 * The algorithm is due to Ron Rivest.  This code was
 * written by Colin Plumb in 1993, no copyright is claimed.
 * This code is in the public domain; do with it what you wish.
 *
 * Equivalent code is available from RSA Data Security, Inc.
 * This code has been tested against that, and is equivalent,
 * except that you don't need to include two pages of legalese
 * with every copy.
 *
 * To compute the message digest of a chunk of bytes, declare an
 * MD5Context structure, pass it to MD5Init, call MD5Update as
 * needed on buffers full of bytes, and then call MD5Final, which
 * will fill a supplied 16-byte array with the digest.
 */

#include <tcl.h>
#include "odieInt.h"
#include <string.h>

/*
 * If compiled on a machine that doesn't have a 32-bit integer,
 * you just set "uint32" to the appropriate datatype for an
 * unsigned 32-bit integer.  For example:
 *
 *       cc -Duint32='unsigned long' md5.c
 *
 */
#ifndef uint32
#  define uint32 unsigned int
#endif

struct Context {
  uint32 buf[4];
  uint32 bits[2];
  unsigned char in[64];
};
typedef char MD5Context[88];

/*
 * Note: this code is harmless on little-endian machines.
 */
static void byteReverse (unsigned char *buf, unsigned longs){
        uint32 t;
        do {
                t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
                            ((unsigned)buf[1]<<8 | buf[0]);
                *(uint32 *)buf = t;
                buf += 4;
        } while (--longs);
}
/* The four core functions - F1 is optimized somewhat */

#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))

/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f, w, x, y, z, data, s) ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )

/*
 * The core of the MD5 algorithm, this alters an existing MD5 hash to
 * reflect the addition of 16 longwords of new data.  MD5Update blocks
 * the data and converts bytes into longwords for this routine.
 */
static void MD5Transform(uint32 buf[4], const uint32 in[16]){
        register uint32 a, b, c, d;

        a = buf[0];
        b = buf[1];
        c = buf[2];
        d = buf[3];

        MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478,  7);
        MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
        MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
        MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
        MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf,  7);
        MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
        MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
        MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
        MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8,  7);
        MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
        MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
        MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
        MD5STEP(F1, a, b, c, d, in[12]+0x6b901122,  7);
        MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
        MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
        MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);

        MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562,  5);
        MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340,  9);
        MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
        MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
        MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d,  5);
        MD5STEP(F2, d, a, b, c, in[10]+0x02441453,  9);
        MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
        MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
        MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6,  5);
        MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6,  9);
        MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
        MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
        MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905,  5);
        MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8,  9);
        MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
        MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);

        MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942,  4);
        MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
        MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
        MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
        MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44,  4);
        MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
        MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
        MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
        MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6,  4);
        MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
        MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
        MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
        MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039,  4);
        MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
        MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
        MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);

        MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244,  6);
        MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
        MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
        MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
        MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3,  6);
        MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
        MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
        MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
        MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f,  6);
        MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
        MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
        MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
        MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82,  6);
        MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
        MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
        MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);

        buf[0] += a;
        buf[1] += b;
        buf[2] += c;
        buf[3] += d;
}

/*
 * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
 * initialization constants.
 */
static void MD5Init(MD5Context *pCtx){
        struct Context *ctx = (struct Context *)pCtx;
        ctx->buf[0] = 0x67452301;
        ctx->buf[1] = 0xefcdab89;
        ctx->buf[2] = 0x98badcfe;
        ctx->buf[3] = 0x10325476;
        ctx->bits[0] = 0;
        ctx->bits[1] = 0;
}

/*
 * Update context to reflect the concatenation of another buffer full
 * of bytes.
 */
static 
void MD5Update(MD5Context *pCtx, const unsigned char *buf, unsigned int len){
        struct Context *ctx = (struct Context *)pCtx;
        uint32 t;

        /* Update bitcount */

        t = ctx->bits[0];
        if ((ctx->bits[0] = t + ((uint32)len << 3)) < t)
                ctx->bits[1]++; /* Carry from low to high */
        ctx->bits[1] += len >> 29;

        t = (t >> 3) & 0x3f;    /* Bytes already in shsInfo->data */

        /* Handle any leading odd-sized chunks */

        if ( t ) {
                unsigned char *p = (unsigned char *)ctx->in + t;

                t = 64-t;
                if (len < t) {
                        memcpy(p, buf, len);
                        return;
                }
                memcpy(p, buf, t);
                byteReverse(ctx->in, 16);
                MD5Transform(ctx->buf, (uint32 *)ctx->in);
                buf += t;
                len -= t;
        }

        /* Process data in 64-byte chunks */

        while (len >= 64) {
                memcpy(ctx->in, buf, 64);
                byteReverse(ctx->in, 16);
                MD5Transform(ctx->buf, (uint32 *)ctx->in);
                buf += 64;
                len -= 64;
        }

        /* Handle any remaining bytes of data. */

        memcpy(ctx->in, buf, len);
}

/*
 * Final wrapup - pad to 64-byte boundary with the bit pattern 
 * 1 0* (64-bit count of bits processed, MSB-first)
 */
static void MD5Final(unsigned char digest[16], MD5Context *pCtx){
        struct Context *ctx = (struct Context *)pCtx;
        unsigned count;
        unsigned char *p;

        /* Compute number of bytes mod 64 */
        count = (ctx->bits[0] >> 3) & 0x3F;

        /* Set the first char of padding to 0x80.  This is safe since there is
           always at least one byte free */
        p = ctx->in + count;
        *p++ = 0x80;

        /* Bytes of padding needed to make 64 bytes */
        count = 64 - 1 - count;

        /* Pad out to 56 mod 64 */
        if (count < 8) {
                /* Two lots of padding:  Pad the first block to 64 bytes */
                memset(p, 0, count);
                byteReverse(ctx->in, 16);
                MD5Transform(ctx->buf, (uint32 *)ctx->in);

                /* Now fill the next block with 56 bytes */
                memset(ctx->in, 0, 56);
        } else {
                /* Pad block to 56 bytes */
                memset(p, 0, count-8);
        }
        byteReverse(ctx->in, 14);

        /* Append length in bits and transform */
        ((uint32 *)ctx->in)[ 14 ] = ctx->bits[0];
        ((uint32 *)ctx->in)[ 15 ] = ctx->bits[1];

        MD5Transform(ctx->buf, (uint32 *)ctx->in);
        byteReverse((unsigned char *)ctx->buf, 4);
        memcpy(digest, ctx->buf, 16);
        memset(ctx, 0, sizeof(ctx));    /* In case it's sensitive */
}

/*
** Convert a digest into base-16.  digest should be declared as
** "unsigned char digest[16]" in the calling function.  The MD5
** digest is stored in the first 16 bytes.  zBuf should
** be "char zBuf[33]".
*/
static void DigestToBase16(unsigned char *digest, char *zBuf){
  static char const zEncode[] = "0123456789abcdef";
  int i, j;

  for(j=i=0; i<16; i++){
    int a = digest[i];
    zBuf[j++] = zEncode[(a>>4)&0xf];
    zBuf[j++] = zEncode[a & 0xf];
  }
  zBuf[j] = 0;
}

/*
** A TCL command for md5.  The argument is the text to be hashed.  The
** Result is the hash in base64.  
*/
static int md5_cmd(void*cd, Tcl_Interp *interp, int argc, const char **argv){
  MD5Context ctx;
  unsigned char digest[16];

  if( argc!=2 ){
    Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], 
        " TEXT\"", 0);
    return TCL_ERROR;
  }
  MD5Init(&ctx);
  MD5Update(&ctx, (unsigned char*)argv[1], (unsigned)strlen(argv[1]));
  MD5Final(digest, &ctx);
  DigestToBase16(digest, Tcl_GetStringResult(interp));
  return TCL_OK;
}

/*
** A TCL command to take the md5 hash of a file.  The argument is the
** name of the file.
*/
static int md5file_cmd(void*cd, Tcl_Interp*interp, int argc, const char **argv){
  FILE *in;
  MD5Context ctx;
  unsigned char digest[16];
  char zBuf[10240];

  if( argc!=2 ){
    Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], 
        " FILENAME\"", 0);
    return TCL_ERROR;
  }
  in = fopen(argv[1],"rb");
  if( in==0 ){
    Tcl_AppendResult(interp,"unable to open file \"", argv[1], 
         "\" for reading", 0);
    return TCL_ERROR;
  }
  MD5Init(&ctx);
  for(;;){
    int n;
    n = fread(zBuf, 1, sizeof(zBuf), in);
    if( n<=0 ) break;
    MD5Update(&ctx, (unsigned char*)zBuf, (unsigned)n);
  }
  fclose(in);
  MD5Final(digest, &ctx);
  DigestToBase16(digest, Tcl_GetStringResult(interp));
  return TCL_OK;
}

/*
** Register the two TCL commands above with the TCL interpreter.
*/
DLLEXPORT int Md5_Init(Tcl_Interp *interp){
  //Tcl_CreateCommand(interp, "md5", (Tcl_CmdProc*)md5_cmd, 0, 0);
  Tcl_CreateCommand(interp, "irmmd5", (Tcl_CmdProc*)md5_cmd, 0, 0);
  Tcl_CreateCommand(interp, "md5file", (Tcl_CmdProc*)md5file_cmd, 0, 0);
  return TCL_OK;
}

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




































































































































































































































































































































































































































































































































































































































































































































Deleted cmodules/odieutil/memory.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include "odieInt.h"

/*
** Memory routines
*/
int nMalloc=0;

/*
** Provide wrappers around malloc and free
*/
char *Odie_Alloc(unsigned int size) {
  char *p;
  p=Tcl_Alloc(size);
  if(p) {
    nMalloc++;
    memset(p,0,size);
  } else {
    printf("out of memory\n");
    exit(1);
  }
  return p;
}

void Odie_Free(char *ptr) {
  if(ptr) {
    nMalloc--;
  }
  Tcl_Free(ptr);
}

char *Odie_Realloc(char *ptr, unsigned int size) {
  return Tcl_Realloc(ptr, size);
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


































































Deleted cmodules/odieutil/mkPassword.tcl.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
###
# This TCL script generates a unique password used by this executable
# to encrypt or sign code
#
# Adapted from Dennis LaBelle's Freewrap
##
set here [file dirname [file normalize [info script]]]
set outfile [file join $here password.c]
if {[file exists $outfile]} {
  return
}
set curpwd [lindex $argv 0]
set curpwd {}
if { $curpwd eq {} } {
  set charset {*+-.0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_}
  set maxpos [string length $charset]
  set keylen [expr 8 + int(8 * rand())]
  set curpwd {}
  for {set idx 0} {$idx < $keylen} {incr idx} {
    append curpwd [string index $charset [expr int($maxpos * rand())]]
  }
} else {
  set keylen [string length $curpwd]
}
set fout [open $outfile w]
puts $fout "/* Automatically generated by setinfo.tcl - [clock format [clock seconds]]"
puts $fout {}
puts $fout "   This file defines the function that returns the encryption password."
puts $fout "   Its contents have been randomly generated to produce a password"
puts $fout "   that is difficult to extract from the compiled binary file."
puts $fout "*/"
puts $fout {}
puts $fout {char *getPwdKey(char *keybuf)}
puts $fout "\{"
for {set idx 0} {$idx < $keylen} {incr idx} {
    set cval [string index $curpwd $idx]
    scan $cval %c ival
    puts $fout "  keybuf\[$idx\] = $ival;"
}
puts $fout "  keybuf\[$keylen\] = 0;"
puts $fout {}
puts $fout "  return keybuf;"
puts $fout "\}"
close $fout
set fout [open [file rootname $outfile].txt w]
puts $fout $curpwd
close $fout


<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


































































































Deleted cmodules/odieutil/rc4.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
/*
** Implementation of an RC4 codec for TCL.
*/
const char rc4_c_version[] = "$Header: /readi/code/tobe/rc4.c,v 1.6 2007/05/08 21:53:56 drh Exp $";
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <tcl.h>
#include "odieInt.h"

//#include <sqlite3.h>

/*
** An RC4 codec is an instance of the following structure.
*/
typedef struct Rc4Codec Rc4Codec;
struct Rc4Codec {
  unsigned char i, j;
  unsigned char s[256];
};
static Tcl_WideInt next_random_number = 1;

/*
** Initialize an RC4 codec with the given key sequence.
*/
static void rc4_init(Rc4Codec *p, int nByte, unsigned char *pKey){
  int k, l;
  unsigned char i, j, t, *s;
  i = j = p->i = p->j = 0;
  s = p->s;
  for(k=0; k<256; k++){
    s[k] = k;
  }
  l = 0;
  for(k=0; k<256; k++){
    t = s[k];
    j += t + pKey[l++];
    if( l>=nByte ) l = 0;
    s[k] = s[j];
    s[j] = t;
  }

  /* Discard the first 1024 bytes of key stream to thwart the
  ** Fluhrer-Mantin-Shamir attack.
  */
  for(k=0; k<1024; k++){
    t = s[++i];
    j += t;
    s[i] = s[j];
    s[j] = t;
  }
  p->j = j;
}

/*
** Encode/Decode nBytes bytes of traffic using the given codec.
*/
static void rc4_coder(Rc4Codec *p, int nByte, unsigned char *pData){
  register unsigned char ti, tj, i, j, *s;
  s = p->s;
  i = p->i;
  j = p->j;
  while( nByte-->0 ){
    ti = s[++i];
    j += ti;
    tj = s[i] = s[j];
    s[j] = ti;
    tj += ti;
    *(pData++) ^= s[tj];
  }
  p->i = i;
  p->j = j;
}

/*
** Usage:    NAME TEXT
**
** There is a separate TCL command created for each rc4 codec instance.
** This is the implementation of that command.  Apply the codec to the
** input and return the results.
*/
static int CodecObjCmd(
  void *pCodec,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  unsigned char *data;
  int nData;
  Tcl_Obj *pResult;
  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 2, objv, "TEXT");
    return TCL_ERROR;
  }
  data = Tcl_GetByteArrayFromObj(objv[1], &nData);
  pResult = Tcl_NewByteArrayObj(data, nData);
  data = Tcl_GetByteArrayFromObj(pResult, 0);
  rc4_coder((Rc4Codec*)pCodec, nData, data);
  Tcl_SetObjResult(interp, pResult);
  return TCL_OK;
}

/*
** Destructor for codec.
*/
static void CodecDestructor(void *pCodec){
  Odie_Free(pCodec);
}

/*
** Usage:   rc4  NAME  PASSWORD
**
** Create a new rc4 codec called NAME and initialized using PASSWORD.
*/
static int Rc4ObjCmd(
  void *NotUsed,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  Rc4Codec *pCodec;
  const char *zName;
  unsigned char *pKey;
  int nKey;

  if( objc!=3 ){
    Tcl_WrongNumArgs(interp, 2, objv, "NAME PASSWORD");
    return TCL_ERROR;
  }
  zName = Tcl_GetStringFromObj(objv[1], 0);
  pKey = Tcl_GetByteArrayFromObj(objv[2], &nKey);
  pCodec = (Rc4Codec*)Odie_Alloc( sizeof(*pCodec) );
  rc4_init(pCodec, nKey, pKey);
  Tcl_CreateObjCommand(interp, zName, CodecObjCmd, pCodec, CodecDestructor);
  return TCL_OK;
}

/*
** The characters used for HTTP base64 encoding.
*/
static const unsigned char zBase[] = 
  "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~";

/*
** Encode a string using a base-64 encoding.
** The encoding can be reversed using the <b>decode64</b> function.
**
** Space to hold the result comes from Odie_Alloc().
*/
static char *encode64(const char *zData, int nData, int *pnOut){
  char *z64;
  int i, n;

  if( nData<=0 ){
    nData = strlen(zData);
  }
  z64 = Odie_Alloc( (nData*4)/3 + 6 );
  for(i=n=0; i+2<nData; i+=3){
    z64[n++] = zBase[ (zData[i]>>2) & 0x3f ];
    z64[n++] = zBase[ ((zData[i]<<4) & 0x30) | ((zData[i+1]>>4) & 0x0f) ];
    z64[n++] = zBase[ ((zData[i+1]<<2) & 0x3c) | ((zData[i+2]>>6) & 0x03) ];
    z64[n++] = zBase[ zData[i+2] & 0x3f ];
  }
  if( i+1<nData ){
    z64[n++] = zBase[ (zData[i]>>2) & 0x3f ];
    z64[n++] = zBase[ ((zData[i]<<4) & 0x30) | ((zData[i+1]>>4) & 0x0f) ];
    z64[n++] = zBase[ ((zData[i+1]<<2) & 0x3c) ];
  }else if( i<nData ){
    z64[n++] = zBase[ (zData[i]>>2) & 0x3f ];
    z64[n++] = zBase[ ((zData[i]<<4) & 0x30) ];
  }
  z64[n] = 0;
  if( pnOut ) *pnOut = n;
  return z64;
}

/*
** This function treats its input as a base-64 string and returns the
** decoded value of that string.  Characters of input that are not
** valid base-64 characters (such as spaces and newlines) are ignored.
**
** Space to hold the decoded string is obtained from Odie_Alloc().
*/
char *decode64(const char *z64, int n64, int *pnOut){
  char *zData;
  int i, j;
  int a, b, c, d;
  static int isInit = 0;
  static int trans[128];

  if( !isInit ){
    for(i=0; i<128; i++){ trans[i] = 0; }
    for(i=0; zBase[i]; i++){ trans[zBase[i] & 0x7f] = i; }
    isInit = 1;
  }
  if( n64<0 ){
    n64 = strlen(z64);
  }
  while( n64>0 && z64[n64-1]=='=' ) n64--;
  zData = Odie_Alloc( (n64*3)/4 + 4 );
  for(i=j=0; i+3<n64; i+=4){
    a = trans[z64[i] & 0x7f];
    b = trans[z64[i+1] & 0x7f];
    c = trans[z64[i+2] & 0x7f];
    d = trans[z64[i+3] & 0x7f];
    zData[j++] = ((a<<2) & 0xfc) | ((b>>4) & 0x03);
    zData[j++] = ((b<<4) & 0xf0) | ((c>>2) & 0x0f);
    zData[j++] = ((c<<6) & 0xc0) | (d & 0x3f);
  }
  if( i+2<n64 ){
    a = trans[z64[i] & 0x7f];
    b = trans[z64[i+1] & 0x7f];
    c = trans[z64[i+2] & 0x7f];
    zData[j++] = ((a<<2) & 0xfc) | ((b>>4) & 0x03);
    zData[j++] = ((b<<4) & 0xf0) | ((c>>2) & 0x0f);
  }else if( i+1<n64 ){
    a = trans[z64[i] & 0x7f];
    b = trans[z64[i+1] & 0x7f];
    zData[j++] = ((a<<2) & 0xfc) | ((b>>4) & 0x03);
  }
  zData[j] = 0;
  if( pnOut ) *pnOut = j;
  return zData;
}

static unsigned char randomByte(void) {
  char nextbyte,i;
  /* RAND_MAX assumed to be 256 */
  char repeat=(next_random_number % 10)+2;
  for(i=0;i<repeat;i++) {
    next_random_number = next_random_number * 1103515245 + 12345;
  }
  return ((unsigned)(next_random_number/256) % 256);
}

static void rc4_randomness(int N, void *pBuf) {
  unsigned char *zBuf = pBuf;
  while( N-- ){
    *(zBuf++) = randomByte();
  }
}

static int Rc4SeedObjCmd(
  void *NotUsed,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 2, objv, "SEEDINT");
    return TCL_ERROR;
  }
  if(Tcl_GetWideIntFromObj(interp,objv[1],&next_random_number)) {
    return TCL_ERROR;
  }
  return TCL_OK;
}

/*
** Usage:   rc4encrypt  PASSWORD   TEXT
**
** Encrypt TEXT using PASSWORD and a random nonce.  Encode the result
** as a single token using base64.
*/
static int Rc4EncryptObjCmd(
  void *NotUsed,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  const char *zPasswd;
  int nPasswd;
  char *zIn;
  int nIn;
  char *zBuf;
  char *zOut;
  int nOut;
  char zKey[256];
  Rc4Codec codec;
  extern void sqliteRandomness(int,void*);

  if( objc!=3 ){
    Tcl_WrongNumArgs(interp, 2, objv, "PASSWORD TEXT");
    return TCL_ERROR;
  }
  zPasswd = Tcl_GetStringFromObj(objv[1], &nPasswd);

  rc4_randomness(4, zKey);
  if( nPasswd>252 ) nPasswd = 252;
  memcpy(&zKey[4], zPasswd, nPasswd);
  rc4_init(&codec, nPasswd+4, (unsigned char*)zKey);
  zIn = Tcl_GetStringFromObj(objv[2], &nIn);
  zBuf = Odie_Alloc( nIn + 5 );
  memcpy(zBuf, zKey, 4);
  memcpy(&zBuf[4], zIn, nIn);
  rc4_coder(&codec, nIn, (unsigned char*)&zBuf[4]);
  zOut = encode64(zBuf, nIn+4, &nOut);
  Tcl_SetObjResult(interp, Tcl_NewStringObj(zOut, nOut));
  Odie_Free((char *)zOut);
  Odie_Free((char *)zBuf);
  return TCL_OK;
}

/*
** Usage:   rc4decrypt  PASSWORD   CYPHERTEXT
**
** Decrypt CYPHERTEXT using PASSWORD and a nonce found at the beginning of
** the cyphertext.  The cyphertext is base64 encoded.
*/
static int Rc4DecryptObjCmd(
  void *NotUsed,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  const char *zPasswd;
  int nPasswd;
  char *zIn;
  int nIn;
  char *zOut;
  int nOut;
  char zKey[256];
  Rc4Codec codec;

  if( objc!=3 ){
    Tcl_WrongNumArgs(interp, 2, objv, "PASSWORD TEXT");
    return TCL_ERROR;
  }
  zPasswd = Tcl_GetStringFromObj(objv[1], &nPasswd);
  zIn = Tcl_GetStringFromObj(objv[2], &nIn);
  zOut = decode64(zIn, nIn, &nOut);
  if( nOut<4 ){
    return TCL_OK;
  }
  memcpy(zKey, zOut, 4);
  if( nPasswd>252 ) nPasswd = 252;
  memcpy(&zKey[4], zPasswd, nPasswd);
  rc4_init(&codec, nPasswd+4, (unsigned char*)zKey);
  rc4_coder(&codec, nOut-4, (unsigned char*)&zOut[4]);
  Tcl_SetObjResult(interp, Tcl_NewStringObj(&zOut[4], nOut-4));
  Odie_Free(zOut);
  return TCL_OK;
}


/*
** Usage:   source_encrypt TEXT
**
** Encrypt TEXT using compiled in PASSWORD and a random nonce.  Encode the result
** as a single token using base64.
*/
static int Rc4EncryptSourceObjCmd(
  void *NotUsed,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  char zPasswd[32];
  int nPasswd;
  char *zIn;
  int nIn;
  char *zBuf;
  char *zOut;
  int nOut;
  char zKey[256];
  Rc4Codec codec;
  extern void sqliteRandomness(int,void*);

  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 2, objv, "TEXT");
    return TCL_ERROR;
  }
  getPwdKey(zPasswd);
  nPasswd=strlen(zPasswd);
  
  rc4_randomness(4, zKey);
  if( nPasswd>252 ) nPasswd = 252;
  memcpy(&zKey[4], zPasswd, nPasswd);
  rc4_init(&codec, nPasswd+4, (unsigned char*)zKey);
  zIn = Tcl_GetStringFromObj(objv[1], &nIn);
  zBuf = Odie_Alloc( nIn + 5 );
  memcpy(zBuf, zKey, 4);
  memcpy(&zBuf[4], zIn, nIn);
  rc4_coder(&codec, nIn, (unsigned char*)&zBuf[4]);
  zOut = encode64(zBuf, nIn+4, &nOut);
  Tcl_SetObjResult(interp, Tcl_NewStringObj(zOut, nOut));
  Odie_Free((char *)zOut);
  Odie_Free((char *)zBuf);
  return TCL_OK;
}

/*
** Usage:   source_decrypt   CYPHERTEXT
**
** Decrypt CYPHERTEXT using compiled in PASSWORD and a nonce
** found at the beginning of
** the cyphertext.  The cyphertext is base64 encoded.
*/
static int Rc4DecryptSourceObjCmd(
  void *NotUsed,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  char zPasswd[32];
  int nPasswd;
  char *zIn;
  int nIn;
  char *zOut;
  int nOut;
  char zKey[256];
  Rc4Codec codec;

  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 2, objv, "TEXT");
    return TCL_ERROR;
  }
  getPwdKey(zPasswd);
  nPasswd=strlen(zPasswd);

  zIn = Tcl_GetStringFromObj(objv[1], &nIn);
  zOut = decode64(zIn, nIn, &nOut);
  if( nOut<4 ){
    return TCL_OK;
  }
  memcpy(zKey, zOut, 4);
  if( nPasswd>252 ) nPasswd = 252;
  memcpy(&zKey[4], zPasswd, nPasswd);
  rc4_init(&codec, nPasswd+4, (unsigned char*)zKey);
  rc4_coder(&codec, nOut-4, (unsigned char*)&zOut[4]);
  Tcl_SetObjResult(interp, Tcl_NewStringObj(&zOut[4], nOut-4));
  Odie_Free(zOut);
  return TCL_OK;
}

/*
** Usage:   eval_decrypt   CYPHERTEXT
**
** Decrypt CYPHERTEXT using compiled in PASSWORD and a nonce
** found at the beginning of
** the cyphertext.  The cyphertext is base64 encoded.
*/
static int Rc4DecryptEvalObjCmd(
  void *NotUsed,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  char zPasswd[32];
  int nPasswd;
  char *zIn;
  int nIn;
  char *zOut;
  int nOut;
  char zKey[256];
  Rc4Codec codec;
  Tcl_Obj *cleartext;
  int code=TCL_OK;

  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 2, objv, "TEXT");
    return TCL_ERROR;
  }
  getPwdKey(zPasswd);
  nPasswd=strlen(zPasswd);

  zIn = Tcl_GetStringFromObj(objv[1], &nIn);
  zOut = decode64(zIn, nIn, &nOut);
  if( nOut<4 ){
    return TCL_OK;
  }
  memcpy(zKey, zOut, 4);
  if( nPasswd>252 ) nPasswd = 252;
  memcpy(&zKey[4], zPasswd, nPasswd);
  rc4_init(&codec, nPasswd+4, (unsigned char*)zKey);
  rc4_coder(&codec, nOut-4, (unsigned char*)&zOut[4]);
  cleartext=Tcl_NewStringObj(&zOut[4], nOut-4);
  Tcl_IncrRefCount(cleartext);
  code=Tcl_EvalObjEx(interp,cleartext,NULL);
  Tcl_DecrRefCount(cleartext);
  Odie_Free(zOut);
  return code;
}

/*
** Initialize the rc4 codec subsystem.
*/
DLLEXPORT int Rc4_Init(Tcl_Interp *interp){
  Tcl_CreateObjCommand(interp, "rc4", Rc4ObjCmd, 0, 0);
  Tcl_CreateObjCommand(interp, "rc4seed", Rc4SeedObjCmd, 0, 0);
  Tcl_CreateObjCommand(interp, "rc4encrypt", Rc4EncryptObjCmd, 0, 0);
  Tcl_CreateObjCommand(interp, "rc4decrypt", Rc4DecryptObjCmd, 0, 0);
  Tcl_CreateObjCommand(interp, "source_encrypt", Rc4EncryptSourceObjCmd, 0, 0);
  //Tcl_CreateObjCommand(interp, "source_decrypt", Rc4DecryptSourceObjCmd, 0, 0);
  Tcl_CreateObjCommand(interp, "eval_decrypt", Rc4DecryptEvalObjCmd, 0, 0);
  return TCL_OK;
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Deleted cmodules/odieutil/tclextra.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
/*
** This module implements an assortment of small TCL extensions.
*/
const char tclextra_c_version[] = "$Header: /readi/code/tobe/tclextra.c,v 1.15 2009/02/06 19:58:25 sdw Exp $";
#include <tcl.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "odieInt.h"

/*
** Determine if we are running under windows or unix and include the
** necessary headers depending on which is being used.
*/
#if !defined(__WIN32__) && !defined(_WIN32) && !defined(WIN32)
# define UNIX
# include <sys/types.h>
# include <signal.h>
# include <sys/wait.h>
/* # include <uuid/uuid.h> */
#else
# ifndef WIN32
#   define WIN32 1
# endif
# include <windows.h>
#endif


/*
** This routine implements a TCL command that kills off a subprocess.
** The code is different for Unix and Windows.  The process is identified
** by the process ID that the Tcl "exec" command returns.
*/
static int KillSubprocessCmd(
  void *NotUsed,
  Tcl_Interp *interp,
  int argc,
  const char **argv
){
  int pid;
  int rc = TCL_OK;
  if( argc!=2 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
       " PROCESS-ID\"", 0);
    return TCL_ERROR;
  }
  if( Tcl_GetInt(interp, argv[1], &pid)!=TCL_OK ){
    return TCL_ERROR;
  }
#ifdef UNIX
  kill(pid,9);
#else
  {
    HANDLE h;
    h = OpenProcess(PROCESS_TERMINATE, TRUE, pid);
    if( h==FALSE ){
      Tcl_AppendResult(interp, "OpenProcess failed for pid ", argv[1], 0);
      rc = TCL_ERROR;
    }else{
      if( TerminateProcess(h,(UINT)0)==FALSE ){
        Tcl_AppendResult(interp, "unable to terminate process ", argv[1], 0);
        rc = TCL_ERROR;
      }
      CloseHandle(h);
    }
  }
#endif
  return rc;
}

/*
** This routine implements a TCL command that checks to see if a process
** started in the background by the Tcl "exec" command is still running.
** The code is different for Unix and Windows.  The process is identified
** by the process ID that the Tcl "exec" command returns.  1 is returned
** if the process exists and 0 if it does not.
*/
static int SubprocessExistsCmd(
  void *NotUsed,
  Tcl_Interp *interp,
  int argc,
  const char **argv
){
  int pid;
  int rc = TCL_OK;
  if( argc!=2 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
       " PROCESS-ID\"", 0);
    return TCL_ERROR;
  }
  if( Tcl_GetInt(interp, argv[1], &pid)!=TCL_OK ){
    return TCL_ERROR;
  }
#ifdef UNIX
  {
    int status;
    waitpid(pid, &status, WNOHANG);
    Tcl_AppendResult(interp, kill(pid,0)==0 ? "1" : "0", 0);
  }
#else
  {
    HANDLE h;
    h = OpenProcess(PROCESS_QUERY_INFORMATION, TRUE, pid);
    if( h==FALSE ){
      Tcl_AppendResult(interp, "0", 0);
    }else{
      DWORD exitCode;
      if( !GetExitCodeProcess(h, &exitCode) ){
        Tcl_AppendResult(interp, "0", 0);
      }else if( exitCode==STILL_ACTIVE ){
        Tcl_AppendResult(interp, "1", 0);
      }else{
        Tcl_AppendResult(interp, "0", 0);
      }
      CloseHandle(h);
    }
  }
#endif
  return rc;
}

/*
** Kill off all zombie child processes.  This is only helpful under Unix.
** On windows, this is a no-op
*/
static int HarvestZombieObjCmd(
  void *NotUsed,
  Tcl_Interp *interp,
  int objcc,
  Tcl_Obj *const* objv
){
#ifdef UNIX
  int status;
  while( waitpid(-1, &status, WNOHANG)>0 );
#endif
  return TCL_OK;
}

#ifdef NEVER
#ifdef UNIX
/*
** Generate a UUID
*/
static void uuid_generate(unsigned char v[16]){
  int fd = open("/dev/urandom", O_RDONLY);
  read(fd, v, 16);
  close(fd);
}
#endif


/*
** Compute a Universally Unique IDentifier or UUID.  (Also sometimes called
** a Globally Unique IDentifier or GUID.)  Return the UUID as a base-64
** encoded string.
*/
static int GetUuid64(
  void *NotUsed,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *const* objv
){
  unsigned char v[16];
  char zOut[23];
  int i, n;
  static const unsigned char zBase[] = 
    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz-";


#ifdef UNIX
  uuid_generate(v);
#endif
#ifdef WIN32
  UuidCreate((UUID*)v);
#endif
  for(i=n=0; i<15; i+=3){
    zOut[n++] = zBase[ (v[i]>>2) & 0x3f ];
    zOut[n++] = zBase[ ((v[i]<<4) & 0x30) | ((v[i+1]>>4) & 0x0f) ];
    zOut[n++] = zBase[ ((v[i+1]<<2) & 0x3c) | ((v[i+2]>>6) & 0x03) ];
    zOut[n++] = zBase[ v[i+2] & 0x3f ];
  }
  zOut[n++] = zBase[ (v[i]>>2) & 0x3f ];
  zOut[n++] = zBase[ ((v[i]<<4) & 0x30) ];
  zOut[n] = 0;
  Tcl_SetObjResult(interp, Tcl_NewStringObj(zOut, 22));
  return TCL_OK;
}

/*
** Compute a Universally Unique IDentifier or UUID.  (Also sometimes called
** a Globally Unique IDentifier or GUID.)  Return the UUID as 64-bit integer.
*/
static int GetUuidInt64(
  void *NotUsed,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *const* objv
){
  unsigned char v[16];
  Tcl_WideInt w1, w2;

#ifdef UNIX
  uuid_generate(v);
#endif
#ifdef WIN32
  UuidCreate((UUID*)v);
#endif
  memcpy(&w1, v, sizeof(w1));
  memcpy(&w2, &v[16-sizeof(w2)], sizeof(w2));
  w1 ^= w2;
  if( w1<0 ){
    w1 = -w1;
    if( w1<0 ) w1--;
  }
  Tcl_SetObjResult(interp, Tcl_NewWideIntObj(w1));
  return TCL_OK;
}

/*
** Compute a Universally Unique IDentifier or UUID.  (Also sometimes called
** a Globally Unique IDentifier or GUID.)  Return the UUID as a base-32
** encoded string.
*/
static int GetUuid32(
  void *NotUsed,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *const* objv
){
  int nOut;
  unsigned char v[16];
  unsigned char zOut[28];
  extern int encode32(const unsigned char*, unsigned char*, int);
  if( objc==1 ){
    nOut = 9;
  }else{
    if( Tcl_GetIntFromObj(interp, objv[1], &nOut) ) return TCL_ERROR;
    if( nOut<=0 || nOut>24 ){
      Tcl_AppendResult(interp, "number of digits should be between 1 and 24",
             0);
      return TCL_ERROR;
    }
  }

#ifdef UNIX
  uuid_generate(v);
#endif
#ifdef WIN32
  UuidCreate((UUID*)v);
#endif
  encode32(v, zOut, 15);
  Tcl_SetObjResult(interp, Tcl_NewStringObj((char*)zOut, nOut));
  return TCL_OK;
}
#endif
#ifdef  UNIX
/*
** This routine returns the size of the currently running program in kilobytes.
** It returns 0 on failure.
*/
static int GetMemory(void){
  char *zFmt = "/proc/%d/statm";
  char *zFile = ckalloc( strlen(zFmt) + 32 + 1 );
  FILE *pFile;
  int progSize = 0;
  sprintf(zFile, zFmt, getpid());
  pFile = fopen(zFile, "r");
  ckfree(zFile);
  if( pFile ){
    fscanf(pFile, "%d", &progSize);
    fclose(pFile);
  }
  return progSize*4;
}
#endif

/*
** tclcmd:  memory
** title:  Return memory statistics for the currently running process
**
** This proc returns the current resident set size of this process.
** Under Windows it throws an error.
*/
static int MemoryCmd(void *NotUsed, Tcl_Interp *interp, int argc, char **argv){
  char res[33];
  if( argc!=1 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 0);
    return TCL_ERROR;
  }
#ifdef  UNIX
  sprintf(res, "%d", GetMemory());
  Tcl_AppendResult(interp, res, 0);
  return TCL_OK;
#else
  Tcl_AppendResult(interp, "Sorry, memory not available under Windows", 0);
  return TCL_ERROR;
#endif
}

#ifdef WIN32
/*
** tclcmd:  registry_get  ROOTKEY  KEY  DATA
** title:   Retrieve a registry setting in windows.
**
** Tcl contains a build-in "registry get" command.  But it requires some
** special linking and it is omitted from our library.  So we supply the
** following "registry_get" command as an alternative.
** for use by the installer.
*/
static int registryGet(void *NotUsed, Tcl_Interp *interp, int argc, char **argv){
  HKEY hKey = 0;
  DWORD dw = 0;
  DWORD dwType;
  LONG rc;
  HKEY keyClass = 0;
  char zBuf[2000];
  if( argc!=3 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 
      " CLASS KEY\"", 0);
    return TCL_ERROR;
  }
  if( strcmp(argv[1],"HKEY_CLASSES_ROOT")==0 ){
    keyClass = HKEY_CLASSES_ROOT;
  }else if( strcmp(argv[1],"HKEY_CURRENT_USER")==0 ){
    keyClass = HKEY_CURRENT_USER;
  }else if( strcmp(argv[1],"HKEY_LOCAL_MACHINE")==0 ){
    keyClass = HKEY_LOCAL_MACHINE;
  }else if( strcmp(argv[1],"HKEY_USERS")==0 ){
    keyClass = HKEY_USERS;
  }else if( strcmp(argv[1],"HKEY_CURRENT_CONFIG")==0 ){
    keyClass = HKEY_CURRENT_CONFIG;
  }else if( strcmp(argv[1],"HKEY_DYN_DATA")==0 ){
    keyClass = HKEY_DYN_DATA;
  }else{
    Tcl_AppendResult(interp, "unknown registry key: ", argv[1], 0);
    return TCL_ERROR;
  }
#if 0
  rc = RegCreateKeyEx(keyClass, argv[2], 
         0, "", REG_OPTION_NON_VOLATILE,
         KEY_ALL_ACCESS, NULL, &hKey, &dw);
  if( rc!=ERROR_SUCCESS ){
    Tcl_AppendResult(interp, "RegCreateKeyEx() returns an error", 0);
    return TCL_ERROR;
  }
  rc = RegSetValueEx(hKey, NULL, 0, REG_SZ, argv[3], strlen(argv[3]));
  RegCloseKey(hKey);
#endif
  rc = RegOpenKeyEx(keyClass, argv[2], 0, KEY_ALL_ACCESS, &hKey);
  if( rc!=ERROR_SUCCESS ){
    Tcl_AppendResult(interp, "RegOpenKeyEx() returns an error", 0);
    return TCL_ERROR;
  }
  dw = sizeof(zBuf)-1;
  dwType = REG_SZ;
  rc = RegQueryValueEx(hKey, "", 0, &dwType, zBuf, &dw);
  if( rc!=ERROR_SUCCESS ){
    Tcl_AppendResult(interp, "RegQueryValueEx() returns an error", 0);
    return TCL_ERROR;
  }
  zBuf[dw] = 0;
  Tcl_AppendResult(interp, zBuf, 0);
  return TCL_OK;
}
#endif

/*
** Encode a string for HTTP.  This means converting lots of
** characters into the "%HH" where H is a hex digit.  It also
** means converting spaces to "+".
**
** This is the opposite of DeHttpizeString below.
*/
char *HttpizeString(const char *zIn, int encodeSlash){
  int c;
  int i = 0;
  int count = 0;
  char *zOut;
  int other;
# define IsSafeChar(X)  \
     (isalnum(X) || (X)=='.' || (X)=='$' || (X)=='-' || (X)=='_' || (X)==other)

  if( zIn==0 ){
    zOut = ckalloc( 4 );
    if( zOut==0 ) return 0;
    strcpy(zOut, "%00");
    return zOut;
  }
  other = encodeSlash ? 'a' : '/';
  while( (c = zIn[i])!=0 ){
    if( IsSafeChar(c) || c==' ' ){
      count++;
    }else{
      count += 3;
    }
    i++;
  }
  i = 0;
  zOut = ckalloc( count+1 );
  if( zOut==0 ) return 0;
  while( (c = *zIn)!=0 ){
    if( IsSafeChar(c) ){
      zOut[i++] = c;
    }else if( c==' ' ){
      zOut[i++] = '+';
    }else{
      zOut[i++] = '%';
      zOut[i++] = "0123456789ABCDEF"[(c>>4)&0xf];
      zOut[i++] = "0123456789ABCDEF"[c&0xf];
    }
    zIn++;
  }
  zOut[i] = 0;
  return zOut;
}

/*
** This routine returns TRUE if the given time is considered to be a null time.
*/
int TimeIsNull(double dtime){
  return dtime>=-0.5 && dtime<0.5;
}

/*
** This routine returns TRUE if the given call time is considered to
** be the ABORT time.
*/
int CalltimeIsAbort(double dtime){
  return dtime<-0.5;
}

/*
** For convenience sake, we define the arguments to a Tcl string command.
*/
#define TCLARGS void*ClientData,Tcl_Interp*interp,int argc,const char**argv
#define TCLOBJS void*ClientData,Tcl_Interp*interp,int objc,Tcl_Obj*CONST*objv

/*
** tclcmd: httpize TEXT
** title: Escape characters that have special meaning within URLs
**
** This command escapes special characters in an arbitrary piece
** of input text according to the rules of HTTP.  Spaces are converted
** to "+" and other special characters are converted into a three-letter
** code consisting of a "%" followed by two hex digits that define the
** ASCII value of the character.  The resulting string is safe to
** use as part of a URL.
*/
static int HttpizeCmd(TCLARGS){
  if( argc!=2 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
        " STRING\"", 0);
    return TCL_ERROR;
  }
  Tcl_SetResult(interp, HttpizeString(argv[1],1), TCL_DYNAMIC);
  return TCL_OK;
}

/*
** tclcmd: urlize TEXT
** title: Escape characters that have special meaning within URLs
**
** This command works just like the <b>httpize</b> command except that
** it does <em>not</em> encode the "/" symbol.
*/
static int UrlizeCmd(TCLARGS){
  if( argc!=2 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
        " STRING\"", 0);
    return TCL_ERROR;
  }
  Tcl_SetResult(interp, HttpizeString(argv[1],0), TCL_DYNAMIC);
  return TCL_OK;
}

/*
** Convert a single HEX digit to an integer
*/
static int AsciiToHex(int c){
  if( c>='a' && c<='f' ){
    c += 10 - 'a';
  }else if( c>='A' && c<='F' ){
    c += 10 - 'A';
  }else if( c>='0' && c<='9' ){
    c -= '0';
  }else{
    c = 0;
  }
  return c;
}

/*
** Remove the HTTP encodings from a string and construct a Tcl_Obj to
** hold the result.
*/
static Tcl_Obj *DeHttpizeString(const char *z, int n){
  int i, j;
  char *zNew = ckalloc( n+1 );
  Tcl_Obj *pObj;
  i = j = 0;

  while( i<n ){
    switch( z[i] ){
      case '%':
        if( i+2<n ){
          zNew[j] = AsciiToHex(z[i+1]) << 4;
          zNew[j] |= AsciiToHex(z[i+2]);
          i += 2;
        }
        break;
      case '+':
        zNew[j] = ' ';
        break;
      default:
        zNew[j] = z[i];
        break;
    }
    i++;
    j++;
  }
  pObj = Tcl_NewStringObj(zNew, j);
  ckfree(zNew);
  return pObj;
}

/*
** tclcmd: dehttpize TEXT
** title: Remove the special HTTP encodings on a URL or query string.
**
** This command removes the URL encodings from its input string and
** returns the result.  URL encodings are the special codes used to
** escape syntactic within a URL or query string.  For example, this
** command converts "+" into a space and "%2F" into
** a single "/" character.
*/
static int DehttpizeCmd(TCLOBJS){
  char *zOld;
  int nOld;

  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "STRING");
    return TCL_ERROR;
  }
  zOld = Tcl_GetStringFromObj(objv[1], &nOld);
  Tcl_SetObjResult(interp, DeHttpizeString(zOld, nOld));
  return TCL_OK;
}

/*
** This procedure runs first to initialize the C and C++ data structures.
*/
DLLEXPORT int TclExtra_Init(Tcl_Interp *interp){
#ifdef UNIX
  /* The default action for these signals is to stop the process, which
  ** causes the GUI to hang.  So ignore these signals.
  */
  signal(SIGTSTP, SIG_IGN);
  signal(SIGTTIN, SIG_IGN);
  signal(SIGTTOU, SIG_IGN);
#endif
#ifdef WIN32
  Tcl_CreateCommand(interp, "registry_get", (Tcl_CmdProc *)registryGet, 0, 0);
#endif

  Tcl_CreateCommand(interp, "kill_subprocess", 
      (Tcl_CmdProc *)KillSubprocessCmd, 0, 0);
  Tcl_CreateCommand(interp, "subprocess_exists", 
      (Tcl_CmdProc *)SubprocessExistsCmd, 0, 0);
  Tcl_CreateObjCommand(interp, "harvest_zombies",HarvestZombieObjCmd,0,0);
#ifdef NEVER
  Tcl_CreateObjCommand(interp, "create_uuid", GetUuid64, 0, 0);
  Tcl_CreateObjCommand(interp, "create_uuid32", GetUuid32, 0, 0);
  Tcl_CreateObjCommand(interp, "create_int_uuid", GetUuidInt64, 0, 0);
#endif
  Tcl_CreateCommand(interp, "memorySize", 
      (Tcl_CmdProc *)MemoryCmd, 0, 0);
  Tcl_CreateCommand(interp, "httpize", 
      (Tcl_CmdProc *)HttpizeCmd, 0, 0);
  Tcl_CreateCommand(interp, "urlize", 
      (Tcl_CmdProc *)UrlizeCmd, 0, 0);
  Tcl_CreateObjCommand(interp, "dehttpize", DehttpizeCmd, 0, 0);
  return TCL_OK;
}


<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<