BRL-CAD
clone.c
Go to the documentation of this file.
1 /* C L O N E . C
2  * BRL-CAD
3  *
4  * Copyright (c) 2008-2014 United States Government as represented by
5  * the U.S. Army Research Laboratory.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this file; see the file named COPYING for more
18  * information.
19  */
20 /** @file libged/clone.c
21  *
22  * The clone command.
23  *
24  * routines related to performing deep object copies
25  *
26  * TODO:
27  * use bu_vls strings
28  * use bu_list lists
29  *
30  * ISSUES/TODO (for DK, ^D means done)
31  * 1. No -c option. This allows the increment given in the '-i' to
32  * act on the second number
33  * D2. Remove 15 char name limit. I ran into this today.
34  * 3. No -p option. I couldn't get this to work. I re-centered the
35  * geometry, then it tried to work but I ran into the 15 char limit
36  * and had to kill the process (^C).
37  * 4. Names - This tool is built around a naming convention. Currently,
38  * the second number does not list properly (it just truncated the
39  * second number of the 'cut' prims so they ended up 'mess.s1.c' instead
40  * of 'mess.s1.c1'). And the '+' and '-' didn't work, I had to switch
41  * from 'mess.s1-1' to 'mess.s1.c1'. Also, prims need to increment
42  * by the 'i' number but combs, regions, and assemblies (.c#, .r#, or
43  * just name with a # at the end) should increment by 1. So you end
44  * up with widget_1, widget_2, widget_3 and not widget_1, widget_4,
45  * widget_7...
46  * 5. Tree structure - please retain tree structure to the extent that
47  * you can and try not to re-create prims or combs used more than once.
48  * No warning needed for redundant copies. Warnings can come later...
49  * D6. Display - do display clones but do not resize or re-center view.
50  */
51 
52 #include "common.h"
53 
54 #include <stdlib.h>
55 #include <ctype.h>
56 #include <math.h>
57 #include <string.h>
58 
59 #include "bu/getopt.h"
60 #include "vmath.h"
61 #include "db.h"
62 #include "raytrace.h"
63 
64 #include "./ged_private.h"
65 
66 
67 #define CLONE_VERSION "Clone ver 4.0\n2006-08-08\n"
68 #define CLONE_BUFSIZE 512
69 
70 /**
71  * state structure used to keep track of what actions the user
72  * requested and values necessary to perform the cloning operation.
73  */
75  struct ged *gedp;
76  struct directory *src; /* Source object */
77  int incr; /* Amount to increment between copies */
78  size_t n_copies; /* Number of copies to make */
79  hvect_t trans; /* Translation between copies */
80  hvect_t rot; /* Rotation between copies */
81  hvect_t rpnt; /* Point to rotate about (default 0 0 0) */
82  int miraxis; /* Axis to mirror copy */
83  fastf_t mirpos; /* Point on axis to mirror copy */
84  int autoview; /* Execute autoview after drawing all objects */
85  int updpos; /* Position of number to update (for -c) */
86  struct bu_vls olist; /* List of cloned object names */
87 };
88 
89 
90 struct name {
91  struct bu_vls src; /* source object name */
92  struct bu_vls *dest; /* dest object names */
93 };
94 
95 
96 /**
97  * structure used to store the names of objects that are to be
98  * cloned. space is preallocated via names with len and used keeping
99  * track of space available and used.
100  */
101 struct nametbl {
102  struct name *names;
106 };
107 
108 
109 static struct nametbl obj_list;
110 
111 /**
112  * a polynomial value for representing knots
113  */
114 struct knot {
115  vect_t pt;
116  fastf_t c[3][4];
117 };
118 
119 
120 /**
121  * a spline path with various segments, break points, and polynomial
122  * values.
123  */
124 struct spline {
125  int n_segs;
126  fastf_t *t; /* break points */
127  struct knot *k; /* polynomials */
128 };
129 
130 
131 struct link {
132  struct bu_vls name;
135 };
136 
137 
138 /**
139  * initialize the name list used for stashing destination names
140  */
141 static void
142 init_list(struct nametbl *l, int s)
143 {
144  int i, j;
145 
146  l->names = (struct name *)bu_calloc(10, sizeof(struct name), "alloc l->names");
147  for (i = 0; i < 10; i++) {
148  bu_vls_init(&l->names[i].src);
149  l->names[i].dest = (struct bu_vls *)bu_calloc(s, sizeof(struct bu_vls), "alloc l->names.dest");
150  for (j = 0; j < s; j++)
151  bu_vls_init(&l->names[i].dest[j]);
152  }
153  l->name_size = s;
154  l->names_len = 10;
155  l->names_used = 0;
156 }
157 
158 
159 /**
160  * add a new name to the name list
161  */
162 static int
163 add_to_list(struct nametbl *l, char *name)
164 {
165  int i, j;
166 
167  /*
168  * add more slots if adding 1 more new name will fill up all the
169  * available slots.
170  */
171  if (l->names_len == (l->names_used+1)) {
172  l->names_len += 10;
173  l->names = (struct name *)bu_realloc(l->names, sizeof(struct name)*(l->names_len+1), "realloc l->names");
174  for (i = l->names_used; i < l->names_len; i++) {
175  bu_vls_init(&l->names[i].src);
176  l->names[i].dest = (struct bu_vls *)bu_calloc(l->name_size, sizeof(struct bu_vls), "alloc l->names.dest");
177  for (j = 0; j < l->name_size; j++)
178  bu_vls_init(&l->names[i].dest[j]);
179  }
180  }
181  bu_vls_strcpy(&l->names[l->names_used++].src, name);
182  return l->names_used-1; /* return number of available slots */
183 }
184 
185 
186 /**
187  * returns the location of 'name' in the list if it exists, returns
188  * -1 otherwise.
189  */
190 static int
191 index_in_list(struct nametbl l, char *name)
192 {
193  int i;
194 
195  for (i = 0; i < l.names_used; i++)
196  if (BU_STR_EQUAL(bu_vls_addr(&l.names[i].src), name))
197  return i;
198  return -1;
199 }
200 
201 
202 /**
203  * returns truthfully if 'name' exists in the list
204  */
205 static int
206 is_in_list(struct nametbl l, char *name)
207 {
208  return index_in_list(l, name) != -1;
209 }
210 
211 
212 /**
213  * returns the next available/unused name, using a consistent naming
214  * convention specific to combinations/regions and solids.
215  * state->incr is used for each number level increase.
216  */
217 static struct bu_vls *
218 clone_get_name(struct directory *dp, struct ged_clone_state *state, size_t iter)
219 {
220  struct bu_vls *newname;
221  char prefix[CLONE_BUFSIZE] = {0}, suffix[CLONE_BUFSIZE] = {0}, buf[CLONE_BUFSIZE] = {0}, suffix2[CLONE_BUFSIZE] = {0};
222  int num = 0, i = 1, j = 0;
223 
224  newname = bu_vls_vlsinit();
225 
226  /* Ugh. This needs much repair/cleanup. */
227  if (state->updpos == 0) {
228  sscanf(dp->d_namep, "%[!-/, :-~]%d%[!-/, :-~]%512s", prefix, &num, suffix, suffix2); /* CLONE_BUFSIZE */
229  snprintf(suffix, CLONE_BUFSIZE, "%s%s", suffix, suffix2);
230  } else if (state->updpos == 1) {
231  int num2 = 0;
232  sscanf(dp->d_namep, "%[!-/, :-~]%d%[!-/, :-~]%d%[!-/, :-~]", prefix, &num2, suffix2, &num, suffix);
233  if (num > 0) {
234  snprintf(prefix, CLONE_BUFSIZE, "%s%d%s", prefix, num2, suffix2);
235  } else {
236  num = num2;
237  snprintf(prefix, CLONE_BUFSIZE, "%s%s", prefix, suffix2);
238  }
239  } else
240  bu_exit(EXIT_FAILURE, "multiple -c options not supported yet.");
241 
242  do {
243  /* choke the name back to the prefix */
244  bu_vls_trunc(newname, 0);
245  bu_vls_strcpy(newname, prefix);
246 
247  if ((dp->d_flags & RT_DIR_SOLID) || (dp->d_flags & RT_DIR_REGION)) {
248  /* primitives and regions */
249  if (suffix[0] != 0)
250  if ((i == 1) && is_in_list(obj_list, buf)) {
251  j = index_in_list(obj_list, buf);
252  snprintf(buf, CLONE_BUFSIZE, "%s%d", prefix, num); /* save the name for the next pass */
253  /* clear and set the name */
254  bu_vls_trunc(newname, 0);
255  bu_vls_printf(newname, "%s%s", bu_vls_addr(&(obj_list.names[j].dest[iter])), suffix);
256  } else
257  bu_vls_printf(newname, "%d%s", num+i*state->incr, suffix);
258  else
259  bu_vls_printf(newname, "%d", num + i*state->incr);
260  } else /* non-region combinations */
261  bu_vls_printf(newname, "%d", (num == 0) ? i + 1 : i + num);
262  i++;
263  } while (db_lookup(state->gedp->ged_wdbp->dbip, bu_vls_addr(newname), LOOKUP_QUIET) != NULL);
264  return newname;
265 }
266 
267 
268 /**
269  * make a copy of a v4 solid by adding it to our book-keeping list,
270  * adding it to the db directory, and writing it out to disk.
271  */
272 static void
273 copy_v4_solid(struct db_i *dbip, struct directory *proto, struct ged_clone_state *state, int idx)
274 {
275  struct directory *dp = (struct directory *)NULL;
276  union record *rp = (union record *)NULL;
277  size_t i, j;
278 
279  /* make n copies */
280  for (i = 0; i < state->n_copies; i++) {
281  struct bu_vls *name;
282 
283  if (i == 0)
284  name = clone_get_name(proto, state, i);
285  else {
286  dp = db_lookup(dbip, bu_vls_addr(&obj_list.names[idx].dest[i-1]), LOOKUP_QUIET);
287  if (!dp) {
288  continue;
289  }
290  name = clone_get_name(dp, state, i);
291  }
292 
293  /* XXX: this can probably be optimized. */
294  bu_vls_strcpy(&obj_list.names[idx].dest[i], bu_vls_addr(name));
295  bu_vls_free(name);
296 
297  /* add the object to the directory */
298  dp = db_diradd(dbip, bu_vls_addr(&obj_list.names[idx].dest[i]), RT_DIR_PHONY_ADDR, proto->d_len, proto->d_flags, &proto->d_minor_type);
299  if ((dp == RT_DIR_NULL) || db_alloc(dbip, dp, proto->d_len)) {
300  bu_vls_printf(state->gedp->ged_result_str, "Database alloc error, aborting\n");
301  return;
302  }
303 
304  /* get an in-memory reference to the object being copied */
305  if ((rp = db_getmrec(dbip, proto)) == (union record *)0) {
306  bu_vls_printf(state->gedp->ged_result_str, "Database read error, aborting\n");
307  return;
308  }
309 
310  if (rp->u_id == ID_SOLID) {
311  bu_strlcpy(rp->s.s_name, dp->d_namep, NAMESIZE);
312 
313  /* mirror */
314  if (state->miraxis != W) {
315  /* XXX er, this seems rather wrong .. but it's v4 so punt */
316  rp->s.s_values[state->miraxis] += 2 * (state->mirpos - rp->s.s_values[state->miraxis]);
317  for (j = 3+state->miraxis; j < 24; j++)
318  rp->s.s_values[j] = -rp->s.s_values[j];
319  }
320  /* translate */
321  if (!ZERO(state->trans[W]))
322  /* assumes primitive's first parameter is its position */
323  VADD2(rp->s.s_values, rp->s.s_values, state->trans);
324  /* rotate */
325  if (!ZERO(state->rot[W])) {
326  mat_t r;
327  vect_t vec, ovec;
328 
329  if (!ZERO(state->rpnt[W]))
330  VSUB2(rp->s.s_values, rp->s.s_values, state->rpnt);
331  MAT_IDN(r);
332  bn_mat_angles(r, state->rot[X], state->rot[Y], state->rot[Z]);
333  for (j = 0; j < 24; j+=3) {
334  VMOVE(vec, rp->s.s_values+j);
335  MAT4X3VEC(ovec, r, vec);
336  VMOVE(rp->s.s_values+j, ovec);
337  }
338  if (!ZERO(state->rpnt[W]))
339  VADD2(rp->s.s_values, rp->s.s_values, state->rpnt);
340  }
341  } else
342  bu_vls_printf(state->gedp->ged_result_str, "mods not available on %s\n", proto->d_namep);
343 
344  /* write the object to disk */
345  if (db_put(dbip, dp, rp, 0, dp->d_len)) {
346  bu_vls_printf(state->gedp->ged_result_str, "ERROR: clone internal error writing to the database\n");
347  return;
348  }
349  }
350  if (rp)
351  bu_free((char *)rp, "copy_solid record[]");
352 
353  return;
354 }
355 
356 
357 /**
358  * make a copy of a v5 solid by adding it to our book-keeping list,
359  * adding it to the db directory, and writing it out to disk.
360  */
361 static void
362 copy_v5_solid(struct db_i *dbip, struct directory *proto, struct ged_clone_state *state, int idx)
363 {
364  size_t i;
365  mat_t matrix;
366 
367  MAT_IDN(matrix);
368 
369  /* mirror */
370  if (state->miraxis != W) {
371  matrix[state->miraxis*5] = -1.0;
372  matrix[3 + state->miraxis*4] -= 2 * (matrix[3 + state->miraxis*4] - state->mirpos);
373  }
374 
375  /* translate */
376  if (!ZERO(state->trans[W]))
377  MAT_DELTAS_ADD_VEC(matrix, state->trans);
378 
379  /* rotation */
380  if (!ZERO(state->rot[W])) {
381  mat_t m2, t;
382 
383  bn_mat_angles(m2, state->rot[X], state->rot[Y], state->rot[Z]);
384  if (!ZERO(state->rpnt[W])) {
385  mat_t m3;
386 
387  bn_mat_xform_about_pt(m3, m2, state->rpnt);
388  bn_mat_mul(t, matrix, m3);
389  } else
390  bn_mat_mul(t, matrix, m2);
391 
392  MAT_COPY(matrix, t);
393  }
394 
395  /* make n copies */
396  for (i = 0; i < state->n_copies; i++) {
397  char *argv[4];
398  struct bu_vls *name;
399  int ret;
400  struct directory *dp = (struct directory *)NULL;
401  struct rt_db_internal intern;
402 
403  if (i == 0)
404  dp = proto;
405  else
406  dp = db_lookup(dbip, bu_vls_addr(&obj_list.names[idx].dest[i-1]), LOOKUP_QUIET);
407 
408  if (!dp) {
409  continue;
410  }
411 
412  name = clone_get_name(dp, state, i); /* get new name */
413  bu_vls_strcpy(&obj_list.names[idx].dest[i], bu_vls_addr(name));
414 
415  /* actually copy the primitive to the new name */
416  argv[0] = "clone_copy";
417  argv[1] = proto->d_namep;
418  argv[2] = bu_vls_addr(name);
419  argv[3] = (char *)0;
420  ret = ged_copy(state->gedp, 3, (const char **)argv);
421  if (ret != GED_OK)
422  bu_vls_printf(state->gedp->ged_result_str, "WARNING: failure cloning \"%s\" to \"%s\"\n",
423  proto->d_namep, bu_vls_addr(name));
424 
425  /* get the original objects matrix */
426  if (rt_db_get_internal(&intern, dp, dbip, matrix, &rt_uniresource) < 0) {
427  bu_vls_printf(state->gedp->ged_result_str, "ERROR: clone internal error copying %s\n", proto->d_namep);
428  bu_vls_free(name);
429  return;
430  }
431  RT_CK_DB_INTERNAL(&intern);
432  /* pull the new name */
433  dp = db_lookup(dbip, bu_vls_addr(name), LOOKUP_QUIET);
434  if (!dp) {
435  rt_db_free_internal(&intern);
436  bu_vls_free(name);
437  continue;
438  }
439 
440  /* write the new matrix to the new object */
441  if (rt_db_put_internal(dp, dbip, &intern, &rt_uniresource) < 0)
442  bu_vls_printf(state->gedp->ged_result_str, "ERROR: clone internal error copying %s\n", proto->d_namep);
443 
444  bu_vls_printf(&state->olist, "%s ", bu_vls_addr(name));
445  bu_vls_free(name);
446  } /* end make n copies */
447 
448  return;
449 }
450 
451 
452 /**
453  * make n copies of a database combination by adding it to our
454  * book-keeping list, adding it to the directory, then writing it out
455  * to the db.
456  */
457 static void
458 copy_solid(struct db_i *dbip, struct directory *proto, void *clientData)
459 {
460  struct ged_clone_state *state = (struct ged_clone_state *)clientData;
461  int idx;
462 
463  if (is_in_list(obj_list, proto->d_namep)) {
464  bu_vls_printf(state->gedp->ged_result_str, "Solid primitive %s already cloned?\n", proto->d_namep);
465  return;
466  }
467 
468  idx = add_to_list(&obj_list, proto->d_namep);
469 
470  /* sanity check that the item was really added */
471  if ((idx < 0) || !is_in_list(obj_list, proto->d_namep)) {
472  bu_vls_printf(state->gedp->ged_result_str, "ERROR: clone internal error copying %s\n", proto->d_namep);
473  return;
474  }
475 
476  if (db_version(dbip) < 5)
477  (void)copy_v4_solid(dbip, proto, (struct ged_clone_state *)state, idx);
478  else
479  (void)copy_v5_solid(dbip, proto, (struct ged_clone_state *)state, idx);
480  return;
481 }
482 
483 
484 /**
485  * make n copies of a v4 combination.
486  */
487 static struct directory *
488 copy_v4_comb(struct db_i *dbip, struct directory *proto, struct ged_clone_state *state, int idx)
489 {
490  struct directory *dp = (struct directory *)NULL;
491  union record *rp = (union record *)NULL;
492  size_t i, j;
493 
494  /* make n copies */
495  for (i = 0; i < (size_t)state->n_copies; i++) {
496 
497  /* get a v4 in-memory reference to the object being copied */
498  if ((rp = db_getmrec(dbip, proto)) == (union record *)0) {
499  bu_vls_printf(state->gedp->ged_result_str, "Database read error, aborting\n");
500  return NULL;
501  }
502 
503  if (proto->d_flags & RT_DIR_REGION) {
504  if (!is_in_list(obj_list, rp[1].M.m_instname)) {
505  bu_vls_printf(state->gedp->ged_result_str, "ERROR: clone internal error looking up %s\n", rp[1].M.m_instname);
506  return NULL;
507  }
508  bu_vls_strcpy(&obj_list.names[idx].dest[i], bu_vls_addr(&obj_list.names[index_in_list(obj_list, rp[1].M.m_instname)].dest[i]));
509  /* bleh, odd convention going on here.. prefix regions with an 'r' */
510  *bu_vls_addr(&obj_list.names[idx].dest[i]) = 'r';
511  } else {
512  struct bu_vls *name;
513  if (i == 0)
514  name = clone_get_name(proto, state, i);
515  else {
516  dp = db_lookup(dbip, bu_vls_addr(&obj_list.names[idx].dest[i-1]), LOOKUP_QUIET);
517  if (!dp) {
518  continue;
519  }
520  name = clone_get_name(dp, state, i);
521  }
522  bu_vls_strcpy(&obj_list.names[idx].dest[i], bu_vls_addr(name));
523  bu_vls_free(name);
524  }
525  bu_strlcpy(rp[0].c.c_name, bu_vls_addr(&obj_list.names[idx].dest[i]), NAMESIZE);
526 
527  /* add the object to the directory */
528  dp = db_diradd(dbip, rp->c.c_name, RT_DIR_PHONY_ADDR, proto->d_len, proto->d_flags, &proto->d_minor_type);
529  if ((dp == NULL) || db_alloc(dbip, dp, proto->d_len)) {
530  bu_vls_printf(state->gedp->ged_result_str, "Database alloc error, aborting\n");
531  return NULL;
532  }
533 
534  for (j = 1; j < proto->d_len; j++) {
535  struct bu_vls *vp;
536  if (!is_in_list(obj_list, rp[j].M.m_instname)) {
537  bu_vls_printf(state->gedp->ged_result_str, "ERROR: clone internal error looking up %s\n", rp[j].M.m_instname);
538  return NULL;
539  }
540  vp = &obj_list.names[index_in_list(obj_list, rp[j].M.m_instname)].dest[i];
541  snprintf(rp[j].M.m_instname, NAMESIZE, "%s", bu_vls_addr(vp));
542  }
543 
544  /* write the object to disk */
545  if (db_put(dbip, dp, rp, 0, dp->d_len)) {
546  bu_vls_printf(state->gedp->ged_result_str, "ERROR: clone internal error writing to the database\n");
547  return NULL;
548  }
549 
550  /* our responsibility to free the record */
551  bu_free((char *)rp, "deallocate copy_v4_comb() db_getmrec() record");
552  }
553 
554  return dp;
555 }
556 
557 
558 /*
559  * update the v5 combination tree with the new names.
560  * DESTRUCTIVE RECURSIVE
561  */
562 static int
563 copy_v5_comb_tree(struct ged_clone_state *state, union tree *tree, size_t idx)
564 {
565  char *buf;
566  switch (tree->tr_op) {
567  case OP_UNION:
568  case OP_INTERSECT:
569  case OP_SUBTRACT:
570  case OP_XOR:
571  /* copy right */
572  copy_v5_comb_tree(state, tree->tr_b.tb_right, idx);
573 
574  /* fall through */
575  case OP_NOT:
576  case OP_GUARD:
577  case OP_XNOP:
578  /* copy left */
579  copy_v5_comb_tree(state, tree->tr_b.tb_left, idx);
580  break;
581  case OP_DB_LEAF:
582  buf = tree->tr_l.tl_name;
583  tree->tr_l.tl_name = bu_strdup(bu_vls_addr(&obj_list.names[index_in_list(obj_list, buf)].dest[idx]));
584  bu_free(buf, "node name");
585  break;
586  default:
587  bu_vls_printf(state->gedp->ged_result_str, "clone v5 - OPCODE NOT IMPLEMENTED: %d\n", tree->tr_op);
588  return -1;
589  }
590  return 0;
591 }
592 
593 
594 /**
595  * make n copies of a v5 combination.
596  */
597 static struct directory *
598 copy_v5_comb(struct db_i *dbip, struct directory *proto, struct ged_clone_state *state, size_t idx)
599 {
600  struct directory *dp = (struct directory *)NULL;
601  struct bu_vls *name;
602  size_t i;
603 
604  /* sanity */
605  if (!proto) {
606  bu_vls_printf(state->gedp->ged_result_str, "ERROR: clone internal consistency error\n");
607  return (struct directory *)NULL;
608  }
609 
610  /* make n copies */
611  for (i = 0; i < state->n_copies; i++) {
612  if (i == 0)
613  name = clone_get_name(proto, state, i);
614  else {
615  dp = db_lookup(dbip, bu_vls_addr(&obj_list.names[idx].dest[i-1]), LOOKUP_QUIET);
616  if (!dp) {
617  continue;
618  }
619  name = clone_get_name(dp, state, i);
620  }
621  bu_vls_strcpy(&obj_list.names[idx].dest[i], bu_vls_addr(name));
622 
623  /* we have a before and an after, do the copy */
624  if (proto->d_namep && bu_vls_addr(name)) {
625  struct rt_db_internal dbintern;
626  struct rt_comb_internal *comb;
627 
628  dp = db_lookup(dbip, proto->d_namep, LOOKUP_QUIET);
629  if (!dp) {
630  bu_vls_free(name);
631  continue;
632  }
633  if (rt_db_get_internal(&dbintern, dp, dbip, bn_mat_identity, &rt_uniresource) < 0) {
634  bu_vls_printf(state->gedp->ged_result_str, "ERROR: clone internal error copying %s\n", proto->d_namep);
635  return NULL;
636  }
637 
638  if ((dp=db_diradd(dbip, bu_vls_addr(name), RT_DIR_PHONY_ADDR, 0, proto->d_flags, (void *)&proto->d_minor_type)) == RT_DIR_NULL) {
639  bu_vls_printf(state->gedp->ged_result_str, "An error has occurred while adding a new object to the database.");
640  return NULL;
641  }
642 
643  RT_CK_DB_INTERNAL(&dbintern);
644  comb = (struct rt_comb_internal *)dbintern.idb_ptr;
645  RT_CK_COMB(comb);
646  RT_CK_TREE(comb->tree);
647 
648  /* recursively update the tree */
649  copy_v5_comb_tree(state, comb->tree, i);
650 
651  if (rt_db_put_internal(dp, dbip, &dbintern, &rt_uniresource) < 0) {
652  bu_vls_printf(state->gedp->ged_result_str, "ERROR: clone internal error copying %s\n", proto->d_namep);
653  bu_vls_free(name);
654  return NULL;
655  }
656  bu_vls_printf(&state->olist, "%s ", bu_vls_addr(name));
657  bu_vls_free(name);
658  rt_db_free_internal(&dbintern);
659  }
660 
661  /* done with this name */
662  bu_vls_free(name);
663  }
664 
665  return dp;
666 }
667 
668 
669 /**
670  * make n copies of a database combination by adding it to our
671  * book-keeping list, adding it to the directory, then writing it out
672  * to the db.
673  */
674 static void
675 copy_comb(struct db_i *dbip, struct directory *proto, void *clientData)
676 {
677  struct ged_clone_state *state = (struct ged_clone_state *)clientData;
678  int idx;
679 
680  if (is_in_list(obj_list, proto->d_namep)) {
681  bu_vls_printf(state->gedp->ged_result_str, "Combination %s already cloned?\n", proto->d_namep);
682  return;
683  }
684 
685  idx = add_to_list(&obj_list, proto->d_namep);
686 
687  /* sanity check that the item was really added to our bookkeeping */
688  if ((idx < 0) || !is_in_list(obj_list, proto->d_namep)) {
689  bu_vls_printf(state->gedp->ged_result_str, "ERROR: clone internal error copying %s\n", proto->d_namep);
690  return;
691  }
692 
693  if (db_version(dbip) < 5)
694  (void)copy_v4_comb(dbip, proto, (struct ged_clone_state *)state, idx);
695  else
696  (void)copy_v5_comb(dbip, proto, (struct ged_clone_state *)state, idx);
697 
698  return;
699 }
700 
701 
702 /**
703  * recursively copy a tree of geometry
704  */
705 static struct directory *
706 copy_tree(struct directory *dp, struct resource *resp, struct ged_clone_state *state)
707 {
708  size_t i;
709  union record *rp = (union record *)NULL;
710  struct directory *mdp = (struct directory *)NULL;
711  struct directory *copy = (struct directory *)NULL;
712 
713  struct bu_vls *copyname = NULL;
714  struct bu_vls *nextname = NULL;
715 
716  /* get the name of what the object "should" get cloned to */
717  copyname = clone_get_name(dp, state, 0);
718 
719  /* copy the object */
720  if (dp->d_flags & RT_DIR_COMB) {
721 
722  if (db_version(state->gedp->ged_wdbp->dbip) < 5) {
723  /* A v4 method of peeking into a combination */
724 
725  int errors = 0;
726 
727  /* get an in-memory record of this object */
728  if ((rp = db_getmrec(state->gedp->ged_wdbp->dbip, dp)) == (union record *)0) {
729  bu_vls_printf(state->gedp->ged_result_str, "Database read error, aborting\n");
730  goto done_copy_tree;
731  }
732  /*
733  * if it is a combination/region, copy the objects that
734  * make up the object.
735  */
736  for (i = 1; i < dp->d_len; i++) {
737  if ((mdp = db_lookup(state->gedp->ged_wdbp->dbip, rp[i].M.m_instname, LOOKUP_NOISY)) == RT_DIR_NULL) {
738  errors++;
739  bu_vls_printf(state->gedp->ged_result_str, "WARNING: failed to locate \"%s\"\n", rp[i].M.m_instname);
740  continue;
741  }
742  copy = copy_tree(mdp, resp, state);
743  if (!copy) {
744  errors++;
745  bu_vls_printf(state->gedp->ged_result_str, "WARNING: unable to fully clone \"%s\"\n", rp[i].M.m_instname);
746  }
747  }
748 
749  if (errors) {
750  bu_vls_printf(state->gedp->ged_result_str, "WARNING: some elements of \"%s\" could not be cloned\n", dp->d_namep);
751  }
752 
753  /* copy this combination itself */
754  copy_comb(state->gedp->ged_wdbp->dbip, dp, (void *)state);
755  } else
756  /* A v5 method of peeking into a combination */
757  db_functree(state->gedp->ged_wdbp->dbip, dp, copy_comb, copy_solid, resp, (void *)state);
758  } else if (dp->d_flags & RT_DIR_SOLID)
759  /* leaf node -- make a copy the object */
760  copy_solid(state->gedp->ged_wdbp->dbip, dp, (void *)state);
761  else {
762  bu_vls_printf(state->gedp->ged_result_str, "%s is neither a combination or a primitive?\n", dp->d_namep);
763  goto done_copy_tree;
764  }
765 
766  nextname = clone_get_name(dp, state, 0);
767  if (bu_vls_strcmp(copyname, nextname) == 0)
768  bu_vls_printf(state->gedp->ged_result_str, "ERROR: unable to successfully clone \"%s\" to \"%s\"\n",
769  dp->d_namep, bu_vls_addr(copyname));
770  else
771  copy = db_lookup(state->gedp->ged_wdbp->dbip, bu_vls_addr(copyname), LOOKUP_QUIET);
772 
773 done_copy_tree:
774  if (rp)
775  bu_free((char *)rp, "copy_tree record[]");
776  if (copyname)
777  bu_free((char *)copyname, "free clone_get_name() copyname");
778  if (nextname)
779  bu_free((char *)nextname, "free clone_get_name() copyname");
780 
781  return copy;
782 }
783 
784 
785 /**
786  * copy an object, recursively copying all of the object's contents
787  * if it's a combination/region.
788  */
789 static struct directory *
790 deep_copy_object(struct resource *resp, struct ged_clone_state *state)
791 {
792  struct directory *copy = (struct directory *)NULL;
793  int i, j;
794 
795  init_list(&obj_list, state->n_copies);
796 
797  /* do the actual copying */
798  copy = copy_tree(state->src, resp, state);
799 
800  /* make sure it made what we hope/think it made */
801  if (!copy || !is_in_list(obj_list, state->src->d_namep))
802  return copy;
803 
804  /* release our name allocations */
805  for (i = 0; i < obj_list.names_len; i++) {
806  for (j = 0; j < obj_list.name_size; j++)
807  bu_vls_free(&obj_list.names[i].dest[j]);
808  bu_free((char **)obj_list.names[i].dest, "free dest");
809  }
810  bu_free((struct name *)obj_list.names, "free names");
811 
812  /* better safe than sorry */
813  obj_list.names = NULL;
814  obj_list.name_size = obj_list.names_used = obj_list.names_len = 0;
815 
816  return copy;
817 }
818 
819 
820 /**
821  * how to use clone. blissfully simple interface.
822  */
823 static void
824 print_usage(struct bu_vls *str)
825 {
826  bu_vls_printf(str, "Usage: clone [-abhimnprtv] <object>\n\n");
827  bu_vls_printf(str, "-a <n> <x> <y> <z>\t- Specifies a translation split between n copies.\n");
828  bu_vls_printf(str, "-b <n> <x> <y> <z>\t- Specifies a rotation around x, y, and z axes \n\t\t\t split between n copies.\n");
829  bu_vls_printf(str, "-c\t\t\t- Increment the second number in object names.\n");
830  bu_vls_printf(str, "-f\t\t\t- Don't draw the new object.\n");
831  bu_vls_printf(str, "-g\t\t\t- Don't resize the view after drawing new objects.\n");
832  bu_vls_printf(str, "-h\t\t\t- Prints this message.\n");
833  bu_vls_printf(str, "-i <n>\t\t\t- Specifies the increment between each copy.\n");
834  bu_vls_printf(str, "-m <axis> <pos>\t\t- Specifies the axis and point to mirror the group.\n");
835  bu_vls_printf(str, "-n <# copies>\t\t- Specifies the number of copies to make.\n");
836  bu_vls_printf(str, "-p <x> <y> <z>\t\t- Specifies point to rotate around for -r. \n\t\t\t Default is 0 0 0.\n");
837  bu_vls_printf(str, "-r <x> <y> <z>\t\t- Specifies the rotation around x, y, and z axes.\n");
838  bu_vls_printf(str, "-t <x> <y> <z>\t\t- Specifies translation between each copy.\n");
839  bu_vls_printf(str, "-v\t\t\t- Prints version info.\n");
840  return;
841 }
842 
843 
844 /**
845  * process the user-provided arguments. stash their operations into
846  * our state structure.
847  */
848 static int
849 get_args(struct ged *gedp, int argc, char **argv, struct ged_clone_state *state)
850 {
851  int k;
852 
853  bu_optind = 1;
854 
855  state->gedp = gedp;
856  state->incr = 100;
857  state->n_copies = 1;
858  state->autoview = 1;
859  state->rot[W] = 0;
860  state->rpnt[W] = 0;
861  state->trans[W] = 0;
862  state->miraxis = W;
863  state->updpos = 0;
864 
865  while ((k = bu_getopt(argc, argv, "a:b:cgi:m:n:p:r:t:vh?")) != -1) {
866  if (bu_optopt == '?') k='h';
867  switch (k) {
868  case 'a':
869  state->n_copies = atoi(bu_optarg);
870  state->trans[X] = atof(argv[bu_optind++]) / state->n_copies;
871  state->trans[Y] = atof(argv[bu_optind++]) / state->n_copies;
872  state->trans[Z] = atof(argv[bu_optind++]) / state->n_copies;
873  state->trans[W] = 1;
874  break;
875  case 'b':
876  state->n_copies = atoi(bu_optarg);
877  state->rot[X] = atof(argv[bu_optind++]) / state->n_copies;
878  state->rot[Y] = atof(argv[bu_optind++]) / state->n_copies;
879  state->rot[Z] = atof(argv[bu_optind++]) / state->n_copies;
880  state->rot[W] = 1;
881  break;
882  case 'c':
883  /* I'd like to have an optional argument to -c, but for now,
884  * just let multiple -c's add it up as a hack. I believe the
885  * variant of this that was lost used this as a binary
886  * operation, so it SHOULD be functionally equivalent for a user
887  * who's dealt with this before. */
888  state->updpos++;
889  break;
890  case 'g':
891  state->autoview = 0;
892  break;
893  case 'i':
894  state->incr = atoi(bu_optarg);
895  break;
896  case 'm':
897  state->miraxis = bu_optarg[0] - 'x';
898  state->mirpos = atof(argv[bu_optind++]);
899  break;
900  case 'n':
901  state->n_copies = atoi(bu_optarg);
902  break;
903  case 'p':
904  state->rpnt[X] = atof(bu_optarg);
905  state->rpnt[Y] = atof(argv[bu_optind++]);
906  state->rpnt[Z] = atof(argv[bu_optind++]);
907  state->rpnt[W] = 1;
908  break;
909  case 'r':
910  state->rot[X] = atof(bu_optarg);
911  state->rot[Y] = atof(argv[bu_optind++]);
912  state->rot[Z] = atof(argv[bu_optind++]);
913  state->rot[W] = 1;
914  break;
915  case 't':
916  state->trans[X] = atof(bu_optarg);
917  state->trans[Y] = atof(argv[bu_optind++]);
918  state->trans[Z] = atof(argv[bu_optind++]);
919  state->trans[W] = 1;
920  break;
921  case 'v':
923  return GED_ERROR;
924  break;
925  default:
926  print_usage(gedp->ged_result_str);
927  return GED_ERROR;
928  }
929  }
930 
931  /* make sure not too few/many args */
932  if ((argc - bu_optind) == 0) {
933  bu_vls_printf(gedp->ged_result_str, "Need to specify an <object> to be cloned.\n");
934  print_usage(gedp->ged_result_str);
935  return GED_ERROR;
936  } else if (bu_optind + 1 < argc) {
937  bu_vls_printf(gedp->ged_result_str, "clone: Can only clone exactly one <object> at a time right now.\n");
938  print_usage(gedp->ged_result_str);
939  return GED_ERROR;
940  }
941 
942  /* sanity */
943  if (!argv[bu_optind])
944  return GED_ERROR;
945 
946  GED_DB_LOOKUP(gedp, state->src, argv[bu_optind], LOOKUP_QUIET, GED_ERROR);
947 
948  VSCALE(state->trans, state->trans, gedp->ged_wdbp->dbip->dbi_local2base);
949  VSCALE(state->rpnt, state->rpnt, gedp->ged_wdbp->dbip->dbi_local2base);
950  state->mirpos *= gedp->ged_wdbp->dbip->dbi_local2base;
951 
952  return GED_OK;
953 }
954 
955 
956 int
957 ged_clone(struct ged *gedp, int argc, const char *argv[])
958 {
959  struct ged_clone_state state;
960  struct directory *copy;
961 
964  GED_CHECK_ARGC_GT_0(gedp, argc, GED_ERROR);
965 
966  /* initialize result */
967  bu_vls_trunc(gedp->ged_result_str, 0);
968 
969  /* must be wanting help */
970  if (argc == 1) {
971  print_usage(gedp->ged_result_str);
972  return GED_HELP;
973  }
974 
975  /* validate user options */
976  if (get_args(gedp, argc, (char **)argv, &state) == GED_ERROR)
977  return GED_ERROR;
978 
979  bu_vls_init(&state.olist);
980 
981  if ((copy = deep_copy_object(&rt_uniresource, &state)) != (struct directory *)NULL)
982  bu_vls_printf(gedp->ged_result_str, "%s", copy->d_namep);
983 
984  bu_vls_printf(gedp->ged_result_str, " {%s}", bu_vls_addr(&state.olist));
985  bu_vls_free(&state.olist);
986 
987  return GED_OK;
988 }
989 
990 
991 /*
992  * Local Variables:
993  * tab-width: 8
994  * mode: C
995  * indent-tabs-mode: t
996  * c-file-style: "stroustrup"
997  * End:
998  * ex: shiftwidth=4 tabstop=8
999  */
struct bu_vls olist
Definition: clone.c:86
void bu_vls_init(struct bu_vls *vp)
Definition: vls.c:56
#define GED_OK
Definition: ged.h:55
char * d_namep
pointer to name string
Definition: raytrace.h:859
Definition: raytrace.h:800
int rt_db_get_internal(struct rt_db_internal *ip, const struct directory *dp, const struct db_i *dbip, const mat_t mat, struct resource *resp)
Definition: dir.c:76
int names_len
Definition: clone.c:104
int rt_db_put_internal(struct directory *dp, struct db_i *dbip, struct rt_db_internal *ip, struct resource *resp)
Definition: dir.c:136
size_t d_len
of db granules used
Definition: raytrace.h:867
Definition: ged.h:338
const mat_t bn_mat_identity
Matrix and vector functionality.
Definition: mat.c:46
struct db_i * dbip
Definition: raytrace.h:1266
if lu s
Definition: nmg_mod.c:3860
Definition: clone.c:90
#define GED_DB_LOOKUP(_gedp, _dp, _name, _noisy, _flags)
Definition: ged.h:223
#define OP_XOR
Binary: L xor R, not both.
Definition: raytrace.h:1130
void bu_vls_trunc(struct bu_vls *vp, int len)
Definition: vls.c:198
#define GED_CHECK_ARGC_GT_0(_gedp, _argc, _flags)
Definition: ged.h:202
struct directory * db_lookup(const struct db_i *, const char *name, int noisy)
Definition: db_lookup.c:153
struct knot * k
Definition: clone.c:127
Definition: clone.c:114
int db_version(struct db_i *dbip)
Definition: db5_scan.c:414
#define RT_CK_COMB(_p)
Definition: raytrace.h:955
struct rt_wdb * ged_wdbp
Definition: ged.h:340
char * bu_optarg
Definition: globals.c:91
Header file for the BRL-CAD common definitions.
int name_size
Definition: clone.c:103
int bu_optind
Definition: globals.c:89
#define OP_XNOP
Unary: L, mark region.
Definition: raytrace.h:1136
#define RT_DIR_REGION
region
Definition: raytrace.h:885
int bu_getopt(int nargc, char *const nargv[], const char *ostr)
Definition: getopt.c:43
int updpos
Definition: clone.c:85
#define GED_ERROR
Definition: ged.h:61
union tree * tb_left
Definition: raytrace.h:1149
struct ged * gedp
Definition: clone.c:75
int n_segs
Definition: clone.c:125
void bu_vls_free(struct bu_vls *vp)
Definition: vls.c:248
unsigned char d_minor_type
object minor type
Definition: raytrace.h:871
hvect_t trans
Definition: clone.c:79
#define OP_SUBTRACT
Binary: L subtract R.
Definition: raytrace.h:1129
Definition: color.c:49
vect_t pt
Definition: clone.c:115
#define OP_INTERSECT
Binary: L intersect R.
Definition: raytrace.h:1128
struct resource rt_uniresource
default. Defined in librt/globals.c
Definition: globals.c:41
#define GED_CHECK_DATABASE_OPEN(_gedp, _flags)
Definition: ged.h:114
void bu_exit(int status, const char *fmt,...) _BU_ATTR_NORETURN _BU_ATTR_PRINTF23
Definition: bomb.c:195
#define OP_DB_LEAF
Leaf of combination, db fmt.
Definition: raytrace.h:1139
#define RT_CK_DB_INTERNAL(_p)
Definition: raytrace.h:207
int bu_optopt
Definition: globals.c:90
Definition: clone.c:101
hvect_t rpnt
Definition: clone.c:81
void * bu_calloc(size_t nelem, size_t elsize, const char *str)
Definition: malloc.c:321
struct name * names
Definition: clone.c:102
#define RT_DIR_SOLID
this name is a solid
Definition: raytrace.h:883
int db_alloc(struct db_i *, struct directory *dp, size_t count)
#define LOOKUP_QUIET
Definition: raytrace.h:893
#define bu_strlcpy(dst, src, size)
Definition: str.h:60
int get_args(int argc, char **argv, FILE **ifp, FILE **ofp, double *angle)
Definition: rot.c:67
#define RT_DIR_PHONY_ADDR
Special marker for d_addr field.
Definition: raytrace.h:879
char * tl_name
Name of this leaf (bu_strdup'ed)
Definition: raytrace.h:1174
struct tree::tree_node tr_b
void * bu_realloc(void *ptr, size_t siz, const char *str)
#define OP_GUARD
Unary: not L, or else!
Definition: raytrace.h:1135
void bn_mat_mul(mat_t o, const mat_t a, const mat_t b)
struct bu_vls * bu_vls_vlsinit(void)
Definition: vls.c:91
char * bu_vls_addr(const struct bu_vls *vp)
Definition: vls.c:111
struct bu_vls * ged_result_str
Definition: ged.h:357
int ged_clone(struct ged *gedp, int argc, const char *argv[])
Definition: clone.c:957
void bn_mat_angles(mat_t mat, double alpha, double beta, double ggamma)
size_t n_copies
Definition: clone.c:78
fastf_t c[3][4]
Definition: clone.c:116
Definition: clone.c:124
fastf_t mirpos
Definition: clone.c:83
int miraxis
Definition: clone.c:82
struct directory * db_diradd(struct db_i *, const char *name, off_t laddr, size_t len, int flags, void *ptr)
Definition: db_lookup.c:190
struct tree::tree_db_leaf tr_l
union tree * tb_right
Definition: raytrace.h:1150
#define ZERO(val)
Definition: units.c:38
#define RT_DIR_COMB
combination
Definition: raytrace.h:884
int names_used
Definition: clone.c:105
int db_put(struct db_i *, const struct directory *dp, union record *where, off_t offset, size_t len)
Definition: db_io.c:212
hvect_t rot
Definition: clone.c:80
union tree * tree
Leading to tree_db_leaf leaves.
Definition: raytrace.h:938
#define CLONE_VERSION
Definition: clone.c:67
void bu_vls_printf(struct bu_vls *vls, const char *fmt,...) _BU_ATTR_PRINTF23
Definition: vls.c:694
#define RT_DIR_NULL
Definition: raytrace.h:875
#define LOOKUP_NOISY
Definition: raytrace.h:892
int bu_vls_strcmp(struct bu_vls *s1, struct bu_vls *s2)
Definition: vls.c:482
#define GED_HELP
Definition: ged.h:62
void db_functree(struct db_i *dbip, struct directory *dp, void(*comb_func)(struct db_i *, struct directory *, void *), void(*leaf_func)(struct db_i *, struct directory *, void *), struct resource *resp, void *client_data)
Definition: db_walk.c:199
Definition: color.c:51
void bu_vls_strcpy(struct bu_vls *vp, const char *s)
Definition: vls.c:310
int autoview
Definition: clone.c:84
void bu_free(void *ptr, const char *str)
Definition: malloc.c:328
#define RT_CK_TREE(_p)
Definition: raytrace.h:1182
double dbi_local2base
local2mm
Definition: raytrace.h:807
struct bu_vls * dest
Definition: clone.c:92
#define CLONE_BUFSIZE
Definition: clone.c:68
void bn_mat_xform_about_pt(mat_t mat, const mat_t xform, const point_t pt)
Definition: mat.c:909
struct bu_vls src
Definition: clone.c:91
fastf_t * t
Definition: clone.c:126
union record * db_getmrec(const struct db_i *, const struct directory *dp)
Definition: db_io.c:97
#define GED_CHECK_READ_ONLY(_gedp, _flags)
Definition: ged.h:181
int d_flags
flags
Definition: raytrace.h:869
Definition: vls.h:56
double fastf_t
Definition: defines.h:300
#define OP_UNION
Binary: L union R.
Definition: raytrace.h:1127
#define OP_NOT
Unary: not L.
Definition: raytrace.h:1134
void rt_db_free_internal(struct rt_db_internal *ip)
Definition: dir.c:216
Definition: color.c:50
#define bu_strdup(s)
Definition: str.h:71
struct directory * src
Definition: clone.c:76
int ged_copy(struct ged *gedp, int argc, const char *argv[])
Definition: copy.c:37
#define M
Definition: msr.c:52
#define BU_STR_EQUAL(s1, s2)
Definition: str.h:126