BRL-CAD
comb.c
Go to the documentation of this file.
1 /* C O M B . 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/comb.c
21  *
22  * The comb command.
23  *
24  */
25 
26 #include "common.h"
27 
28 #include <string.h>
29 
30 #include "bu/cmd.h"
31 #include "bu/getopt.h"
32 #include "bu/sort.h"
33 #include "wdb.h"
34 
35 #include "./ged_private.h"
36 
37 HIDDEN int
38 region_flag_set(struct ged *gedp, struct directory *dp) {
39  struct bu_attribute_value_set avs;
40  bu_avs_init_empty(&avs);
41  if (db5_get_attributes(gedp->ged_wdbp->dbip, &avs, dp)) {
42  bu_vls_printf(gedp->ged_result_str, "Cannot get attributes for object %s\n", dp->d_namep);
43  return GED_ERROR;
44  }
45  db5_standardize_avs(&avs);
46  dp->d_flags |= RT_DIR_REGION;
47  (void)bu_avs_add(&avs, "region", "R");
48  if (db5_update_attributes(dp, &avs, gedp->ged_wdbp->dbip)) {
50  "Error: failed to update attributes\n");
51  bu_avs_free(&avs);
52  return GED_ERROR;
53  }
54  return GED_OK;
55 }
56 
57 HIDDEN int
58 region_flag_clear(struct ged *gedp, struct directory *dp) {
59  struct bu_attribute_value_set avs;
60  bu_avs_init_empty(&avs);
61  if (db5_get_attributes(gedp->ged_wdbp->dbip, &avs, dp)) {
62  bu_vls_printf(gedp->ged_result_str, "Cannot get attributes for object %s\n", dp->d_namep);
63  return GED_ERROR;
64  }
65  db5_standardize_avs(&avs);
66  dp->d_flags = dp->d_flags & ~(RT_DIR_REGION);
67  (void)bu_avs_remove(&avs, "region");
68  if (db5_replace_attributes(dp, &avs, gedp->ged_wdbp->dbip)) {
70  "Error: failed to update attributes\n");
71  bu_avs_free(&avs);
72  return GED_ERROR;
73  }
74  return GED_OK;
75 }
76 
77 HIDDEN int
78 color_shader_clear(struct ged *gedp, struct directory *dp) {
79  struct bu_attribute_value_set avs;
80  bu_avs_init_empty(&avs);
81  if (db5_get_attributes(gedp->ged_wdbp->dbip, &avs, dp)) {
82  bu_vls_printf(gedp->ged_result_str, "Cannot get attributes for object %s\n", dp->d_namep);
83  return GED_ERROR;
84  }
85  db5_standardize_avs(&avs);
86  (void)bu_avs_remove(&avs, "rgb");
87  (void)bu_avs_remove(&avs, "color");
88  (void)bu_avs_remove(&avs, "shader");
89  (void)bu_avs_remove(&avs, "oshader");
90  if (db5_replace_attributes(dp, &avs, gedp->ged_wdbp->dbip)) {
92  "Error: failed to update attributes\n");
93  bu_avs_free(&avs);
94  return GED_ERROR;
95  }
96  return GED_OK;
97 }
98 
99 HIDDEN int
100 comb_tree_clear(struct ged *gedp, struct directory *dp)
101 {
102  struct rt_db_internal intern;
103  struct rt_comb_internal *comb;
104  /* Clear the tree from the original object */
105  GED_DB_GET_INTERNAL(gedp, &intern, dp, (matp_t)NULL, &rt_uniresource, GED_ERROR);
106  RT_CK_DB_INTERNAL(&intern);
107  comb = (struct rt_comb_internal *)(&intern)->idb_ptr;
108  RT_CK_COMB(comb);
110  comb->tree = TREE_NULL;
111  db5_sync_comb_to_attr(&((&intern)->idb_avs), comb);
112  db5_standardize_avs(&((&intern)->idb_avs));
113  if (wdb_put_internal(gedp->ged_wdbp, dp->d_namep, &intern, 1.0) < 0) {
114  bu_vls_printf(gedp->ged_result_str, "wdb_export(%s) failure", dp->d_namep);
115  rt_db_free_internal(&intern);
116  return GED_ERROR;
117  }
118  rt_db_free_internal(&intern);
119  return GED_OK;
120 }
121 
122 
123 HIDDEN int
124 comb_wrap(struct ged *gedp, struct directory *dp) {
125 
126  struct bu_vls orig_name, comb_child_name;
127  struct bu_external external;
128  struct directory *orig_dp = dp;
129  struct directory *new_dp;
130 
131  bu_vls_init(&orig_name);
132  bu_vls_init(&comb_child_name);
133 
134  bu_vls_sprintf(&orig_name, "%s", dp->d_namep);
135  bu_vls_sprintf(&comb_child_name, "%s.c", dp->d_namep);
136 
137  /* First, make sure the target comb name for wrapping doesn't already exist */
138  if ((new_dp=db_lookup(gedp->ged_wdbp->dbip, bu_vls_addr(&comb_child_name), LOOKUP_QUIET)) != RT_DIR_NULL) {
139  bu_vls_printf(gedp->ged_result_str, "ERROR: %s already exists in the database, cannot wrap %s", bu_vls_addr(&comb_child_name), dp->d_namep);
140  bu_vls_free(&comb_child_name);
141  bu_vls_free(&orig_name);
142  return GED_ERROR;
143  }
144 
145  /* Create a copy of the comb using a new name */
146  if (db_get_external(&external, dp, gedp->ged_wdbp->dbip)) {
147  bu_vls_printf(gedp->ged_result_str, "Wrapping %s: Database read error retrieving external, aborting\n", dp->d_namep);
148  bu_vls_free(&comb_child_name);
149  bu_vls_free(&orig_name);
150  return GED_ERROR;
151  }
152  if (wdb_export_external(gedp->ged_wdbp, &external, bu_vls_addr(&comb_child_name), dp->d_flags, dp->d_minor_type) < 0) {
153  bu_free_external(&external);
154  bu_vls_printf(gedp->ged_result_str, "Failed to write new object (%s) to database - aborting!!\n", bu_vls_addr(&comb_child_name));
155  bu_vls_free(&comb_child_name);
156  bu_vls_free(&orig_name);
157  return GED_ERROR;
158  }
159  bu_free_external(&external);
160 
161  /* Load new obj.c comb and clear its region flag, if any */
162  if ((new_dp=db_lookup(gedp->ged_wdbp->dbip, bu_vls_addr(&comb_child_name), LOOKUP_QUIET)) == RT_DIR_NULL) {
163  bu_vls_printf(gedp->ged_result_str, "Wrapping %s: creation of %s failed!", dp->d_namep, bu_vls_addr(&comb_child_name));
164  bu_vls_free(&comb_child_name);
165  bu_vls_free(&orig_name);
166  return GED_ERROR;
167  }
168  if (region_flag_clear(gedp, new_dp) == GED_ERROR) {
169  bu_vls_free(&comb_child_name);
170  bu_vls_free(&orig_name);
171  return GED_ERROR;
172  }
173  if (color_shader_clear(gedp, new_dp) == GED_ERROR) {
174  bu_vls_free(&comb_child_name);
175  bu_vls_free(&orig_name);
176  return GED_ERROR;
177  }
178 
179  /* Clear the tree from the original object */
180  if (comb_tree_clear(gedp, dp) == GED_ERROR) {
181  bu_vls_printf(gedp->ged_result_str, "ERROR: %s tree clearing failed", bu_vls_addr(&orig_name));
182  bu_vls_free(&comb_child_name);
183  bu_vls_free(&orig_name);
184  return GED_ERROR;
185  }
186  if ((orig_dp=db_lookup(gedp->ged_wdbp->dbip, bu_vls_addr(&orig_name), LOOKUP_QUIET)) == RT_DIR_NULL) {
187  bu_vls_printf(gedp->ged_result_str, "ERROR: %s tree clearing failed", bu_vls_addr(&orig_name));
188  bu_vls_free(&comb_child_name);
189  bu_vls_free(&orig_name);
190  return GED_ERROR;
191  }
192 
193  /* add "child" comb to the newly cleared parent */
194  if (_ged_combadd(gedp, new_dp, orig_dp->d_namep, 0, WMOP_UNION, 0, 0) == RT_DIR_NULL) {
195  bu_vls_printf(gedp->ged_result_str, "Error adding '%s' (with op '%c') to '%s'\n", bu_vls_addr(&comb_child_name), WMOP_UNION, dp->d_namep);
196  bu_vls_free(&comb_child_name);
197  bu_vls_free(&orig_name);
198  return GED_ERROR;
199  }
200  bu_vls_free(&comb_child_name);
201  bu_vls_free(&orig_name);
202 
203  return GED_OK;
204 }
205 
206 /* bu_sort functions for solids */
207 HIDDEN int
208 name_compare(const void *d1, const void *d2, void *UNUSED(arg))
209 {
210  struct directory *dp1 = *(struct directory **)d1;
211  struct directory *dp2 = *(struct directory **)d2;
212  return bu_strcmp((const char *)dp2->d_namep, (const char *)dp1->d_namep);
213 }
214 
215 /* Define search strings that describe plans for finding:
216  *
217  * 1. non-unioned objects in a comb's tree
218  * 2. solid objects in a comb's tree
219  * 3. comb objects in a comb's tree
220  * 4. All comb objects below any comb's tree except the current comb
221  *
222  * Run the first search, and quit if any non-union ops are present.
223  * If all clear, search for the solid and comb lists. Clear the old
224  * tree and union in all the solids - solids are sorted via bu_sort.
225  * If we have combs, run the not-in-this-comb-tree search and check
226  * which (if any) of the combs under the current comb are not used
227  * elsewhere. For those that are not, remove them.
228  */
229 HIDDEN int
230 comb_flatten(struct ged *gedp, struct directory *dp)
231 {
232  int result_cnt = 0;
233  int obj_cnt = 0;
234  struct directory **all_paths;
235  char *only_unions_in_tree_plan = "! -bool u";
236  char *solids_in_tree_plan = "! -type comb";
237  char *combs_in_tree_plan = "-type comb";
238  struct bu_ptbl solids = BU_PTBL_INIT_ZERO;
239  struct bu_ptbl combs = BU_PTBL_INIT_ZERO;
240  struct bu_ptbl combs_outside_of_tree = BU_PTBL_INIT_ZERO;
241  struct bu_vls plan_string;
242  struct directory **dp_curr;
243 
244  /* if there are non-union booleans in this comb's tree, error out */
245  result_cnt = db_search(NULL, DB_SEARCH_TREE, only_unions_in_tree_plan, 1, &dp, gedp->ged_wdbp->dbip);
246  if (result_cnt) {
247  bu_vls_printf(gedp->ged_result_str, "ERROR: %s tree contains non-union booleans", dp->d_namep);
248  return GED_ERROR;
249  }
250 
251  /* Find the solids and combs in the tree */
252  (void)db_search(&solids, DB_SEARCH_RETURN_UNIQ_DP, solids_in_tree_plan, 1, &dp, gedp->ged_wdbp->dbip);
253  (void)db_search(&combs, DB_SEARCH_RETURN_UNIQ_DP, combs_in_tree_plan, 1, &dp, gedp->ged_wdbp->dbip);
254 
255  /* If it's all solids already, nothing to do */
256  if (!BU_PTBL_LEN(&combs)) {
257  db_search_free(&solids);
258  db_search_free(&combs);
259  return GED_OK;
260  }
261 
262 
263  /* Find the combs NOT in the tree */
264  obj_cnt = db_ls(gedp->ged_wdbp->dbip, DB_LS_TOPS, NULL, &all_paths);
265  bu_vls_init(&plan_string);
266  bu_vls_sprintf(&plan_string, "-mindepth 1 ! -below -name %s -type comb", dp->d_namep);
267  (void)db_search(&combs_outside_of_tree, DB_SEARCH_RETURN_UNIQ_DP, bu_vls_addr(&plan_string), obj_cnt, all_paths, gedp->ged_wdbp->dbip);
268  bu_vls_free(&plan_string);
269 
270  /* Done searching - now we can free the path list and clear the original tree */
271  bu_free(all_paths, "free db_tops output");
272  if (comb_tree_clear(gedp, dp) == GED_ERROR) {
273  bu_vls_printf(gedp->ged_result_str, "ERROR: %s tree clearing failed", dp->d_namep);
274  db_search_free(&solids);
275  db_search_free(&combs);
276  db_search_free(&combs_outside_of_tree);
277  return GED_ERROR;
278  }
279 
280  /* Sort the solids and union them into a new tree for dp */
281  if (BU_PTBL_LEN(&solids)) {
282  bu_sort((void *)BU_PTBL_BASEADDR(&solids), BU_PTBL_LEN(&solids), sizeof(struct directory *), name_compare, NULL);
283  for (BU_PTBL_FOR(dp_curr, (struct directory **), &solids)) {
284  /* add "child" comb to the newly cleared parent */
285  if (_ged_combadd(gedp, (*dp_curr), dp->d_namep, 0, WMOP_UNION, 0, 0) == RT_DIR_NULL) {
286  bu_vls_printf(gedp->ged_result_str, "Error adding '%s' to '%s'\n", (*dp_curr)->d_namep, dp->d_namep);
287  db_search_free(&solids);
288  db_search_free(&combs);
289  db_search_free(&combs_outside_of_tree);
290  return GED_ERROR;
291  }
292  }
293  }
294  /* Done with solids */
295  db_search_free(&solids);
296 
297  /* Remove any combs that were in dp and not used elsewhere from the .g file */
298  for (BU_PTBL_FOR(dp_curr, (struct directory **), &combs)) {
299  if (bu_ptbl_locate(&combs_outside_of_tree, (const long *)(*dp_curr)) == -1 && ((*dp_curr) != dp)) {
300  /* This comb is only part of the flattened tree - remove */
301  bu_vls_printf(gedp->ged_result_str, "an error occurred while deleting %s", (*dp_curr)->d_namep);
302  if (db_delete(gedp->ged_wdbp->dbip, (*dp_curr)) != 0 || db_dirdelete(gedp->ged_wdbp->dbip, (*dp_curr)) == 0) {
303  bu_vls_trunc(gedp->ged_result_str, 0);
304  } else {
305  db_search_free(&combs);
306  db_search_free(&combs_outside_of_tree);
307  return GED_ERROR;
308  }
309  }
310  }
311 
312  db_search_free(&combs);
313  db_search_free(&combs_outside_of_tree);
314  return GED_OK;
315 }
316 
317 
318 /* "Lift a region flag to the specified comb, removing all region
319  * flags below in the tree if practical.
320  */
321 HIDDEN int
322 comb_lift_region(struct ged *gedp, struct directory *dp)
323 {
324  int j;
325  int obj_cnt;
326  struct directory **all_paths;
327  char *regions_in_tree_plan = "-type region";
328 
329  struct bu_ptbl combs_outside_of_tree = BU_PTBL_INIT_ZERO;
330  struct bu_ptbl regions = BU_PTBL_INIT_ZERO;
331  struct bu_ptbl regions_to_clear = BU_PTBL_INIT_ZERO;
332  struct bu_ptbl regions_to_wrap = BU_PTBL_INIT_ZERO;
333 
334  struct bu_vls plan_string;
335  struct directory **dp_curr;
336  int failure_case = 0;
337 
338  /* Find the regions - need full paths here, because we'll be checking parents */
339  (void)db_search(&regions, DB_SEARCH_TREE, regions_in_tree_plan, 1, &dp, gedp->ged_wdbp->dbip);
340 
341  /* If it's all non-region combs and solids already, nothing to do except possibly set the region flag*/
342  if (!BU_PTBL_LEN(&regions)) {
343  db_search_free(&regions);
344  if (!(dp->d_flags & RT_DIR_REGION))
345  return region_flag_set(gedp, dp);
346  return GED_OK;
347  }
348 
349  /* Find the combs NOT in the tree */
350  obj_cnt = db_ls(gedp->ged_wdbp->dbip, DB_LS_TOPS, NULL, &all_paths);
351  bu_vls_init(&plan_string);
352  bu_vls_sprintf(&plan_string, "-mindepth 1 ! -below -name %s -type comb", dp->d_namep);
353  (void)db_search(&combs_outside_of_tree, DB_SEARCH_RETURN_UNIQ_DP, bu_vls_addr(&plan_string), obj_cnt, all_paths, gedp->ged_wdbp->dbip);
354  bu_vls_free(&plan_string);
355 
356  /* release our db_ls path */
357  bu_free(all_paths, "free db_tops output");
358 
359  /* check for entry last node in combs_outside of tree
360  * - if NO, add to quash region flag bu_ptbl list (uniq insert). If yes, continue.
361  * get parent of entry last node
362  * check for parent node in combs_outside_of_tree
363  * if problem found, append specifics to ged_result_str, set fail flag
364  * if no problem found, add to bu_ptbl list of regions to wrap (uniq insert)
365  * no point in storing the specific parent, since we'll in-tree mvall in any case to update */
366  bu_ptbl_init(&regions_to_clear, 64, "regions to clear");
367  bu_ptbl_init(&regions_to_wrap, 64, "regions to wrap");
368  for (j = (int)BU_PTBL_LEN(&regions) - 1; j >= 0; j--) {
369  struct db_full_path *entry = (struct db_full_path *)BU_PTBL_GET(&regions, j);
370  struct directory *dp_curr_dir = DB_FULL_PATH_CUR_DIR(entry);
371  struct directory *dp_parent = DB_FULL_PATH_GET(entry, entry->fp_len-2);
372  if (dp_curr_dir != dp) {
373  if (bu_ptbl_locate(&combs_outside_of_tree, (const long *)(dp_curr_dir)) == -1) {
374  bu_ptbl_ins_unique(&regions_to_clear, (long *)(dp_curr_dir));
375  } else {
376  if (bu_ptbl_locate(&combs_outside_of_tree, (const long *)(dp_parent)) == -1 || (dp_parent == dp)) {
377  bu_ptbl_ins_unique(&regions_to_wrap, (long *)(dp_curr_dir));
378  } else {
379  if (!failure_case) {
380  bu_vls_sprintf(gedp->ged_result_str, "Comb region lift failed - the following combs in the tree contain regions and are also used outside the tree of %s:\n\n", dp->d_namep);
381  failure_case = 1;
382  }
383  bu_vls_printf(gedp->ged_result_str, "%s, containing region %s\n", dp_parent->d_namep, dp_curr_dir->d_namep);
384  }
385  }
386  }
387  }
388  db_search_free(&regions);
389  db_search_free(&combs_outside_of_tree);
390 
391  if (failure_case) {
392  bu_vls_printf(gedp->ged_result_str, "\nThe above combs must be reworked before region lifting the tree of %s can succeed.\n", dp->d_namep);
393  bu_ptbl_free(&regions_to_clear);
394  bu_ptbl_free(&regions_to_wrap);
395  return GED_ERROR;
396  }
397 
398  /* Easy case first - if we can just clear the region flag, do it. */
399  for (BU_PTBL_FOR(dp_curr, (struct directory **), &regions_to_clear)) {
400  if ((*dp_curr) != dp) {
401  if (region_flag_clear(gedp, (*dp_curr)) == GED_ERROR) {
402  bu_ptbl_free(&regions_to_clear);
403  bu_ptbl_free(&regions_to_wrap);
404  return GED_ERROR;
405  }
406  }
407  }
408  bu_ptbl_free(&regions_to_clear);
409 
410  /* Now the tricky case. We need to wrap regions, then insert their
411  * comb definitions into the tree of dp->d_namep and its children.
412  * Because we're doing that, the combs *in* the tree will be changing
413  * as we go.
414  */
415  {
416  struct bu_vls new_comb_name = BU_VLS_INIT_ZERO;
417  struct bu_ptbl stack = BU_PTBL_INIT_ZERO;
418  struct directory *new_comb;
419 
420  char *combs_in_tree_plan = "-type comb";
421  struct bu_ptbl combs_in_tree = BU_PTBL_INIT_ZERO;
422 
423  bu_ptbl_init(&stack, 64, "comb mvall working stack");
424 
425  (void)db_search(&combs_in_tree, DB_SEARCH_RETURN_UNIQ_DP, combs_in_tree_plan, 1, &dp, gedp->ged_wdbp->dbip);
426  bu_ptbl_ins(&combs_in_tree, (long *)dp);
427  for (BU_PTBL_FOR(dp_curr, (struct directory **), &regions_to_wrap)) {
428  if ((*dp_curr) != dp) {
429  struct directory **dp_comb_from_tree;
430  if (comb_wrap(gedp, (*dp_curr)) == GED_ERROR) {
431  bu_ptbl_free(&regions_to_wrap);
432  db_search_free(&combs_in_tree);
433  bu_ptbl_free(&stack);
434  return GED_ERROR;
435  } else {
436  bu_vls_sprintf(&new_comb_name, "%s.c", (*dp_curr)->d_namep);
437  new_comb = db_lookup(gedp->ged_wdbp->dbip, bu_vls_addr(&new_comb_name), LOOKUP_QUIET);
438  }
439  for (BU_PTBL_FOR(dp_comb_from_tree, (struct directory **), &combs_in_tree)) {
440  bu_ptbl_reset(&stack);
441  if (db_comb_mvall((*dp_comb_from_tree), gedp->ged_wdbp->dbip, (*dp_curr)->d_namep, bu_vls_addr(&new_comb_name), &stack) == 2) {
442  db_search_free(&combs_in_tree);
443  bu_ptbl_free(&regions_to_wrap);
444  bu_ptbl_free(&stack);
445  return GED_ERROR;
446  }
447  }
448  bu_ptbl_ins(&combs_in_tree, (long *)new_comb);
449  bu_ptbl_rm(&combs_in_tree, (long *)(*dp_curr));
450  }
451  }
452  bu_ptbl_free(&stack);
453  db_search_free(&combs_in_tree);
454  }
455  bu_ptbl_free(&regions_to_wrap);
456 
457  /* Finally, set the region flag on the toplevel comb */
458  if (region_flag_set(gedp, dp) == GED_ERROR)
459  return GED_ERROR;
460 
461  return GED_OK;
462 }
463 
464 
465 int
466 ged_comb(struct ged *gedp, int argc, const char *argv[])
467 {
468  struct directory *dp;
469  const char *cmd_name;
470  char *comb_name;
471  int i,c, sum;
472  db_op_t oper;
473  int set_region = 0;
474  int set_comb = 0;
475  int standard_comb_build = 1;
476  int wrap_comb = 0;
477  int flatten_comb = 0;
478  int lift_region_comb = 0;
479  int alter_existing = 1;
480  static const char *usage = "[-c|-r] [-w|-f|-l] [-S] comb_name [<operation object>]";
481 
484  GED_CHECK_ARGC_GT_0(gedp, argc, GED_ERROR);
485 
486  /* initialize result */
487  bu_vls_trunc(gedp->ged_result_str, 0);
488 
489  cmd_name = argv[0];
490 
491  /* must be wanting help */
492  if (argc == 1) {
493  bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
494  return GED_HELP;
495  }
496 
497  if (argc < 3) {
498  bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
499  return GED_ERROR;
500  }
501 
502  /* First, handle options, if any */
503 
504  bu_optind = 1;
505  /* Grab any arguments off of the argv list */
506  while ((c = bu_getopt(argc, (char **)argv, "crwflS")) != -1) {
507  switch (c) {
508  case 'c' :
509  set_comb = 1;
510  break;
511  case 'r' :
512  set_region = 1;
513  break;
514  case 'w' :
515  wrap_comb = 1;
516  standard_comb_build = 0;
517  break;
518  case 'f' :
519  flatten_comb = 1;
520  standard_comb_build = 0;
521  break;
522  case 'l' :
523  lift_region_comb = 1;
524  standard_comb_build = 0;
525  break;
526  case 'S' :
527  alter_existing = 0;
528  break;
529  default :
530  break;
531  }
532  }
533 
534  argc -= bu_optind - 1;
535  argv += bu_optind - 1;
536 
537  if (set_comb && set_region) {
538  bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], cmd_name);
539  return GED_ERROR;
540  }
541 
542  sum = wrap_comb + flatten_comb + lift_region_comb;
543  if (sum > 1) {
544  bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
545  return GED_ERROR;
546  }
547 
548  if ((wrap_comb || flatten_comb || lift_region_comb) && argc != 2) {
549  bu_vls_printf(gedp->ged_result_str, "Usage: %s %s", argv[0], usage);
550  return GED_ERROR;
551  }
552 
553 
554  /* Get target combination info */
555  comb_name = (char *)argv[1];
556  dp = db_lookup(gedp->ged_wdbp->dbip, comb_name, LOOKUP_QUIET);
557  if (dp != RT_DIR_NULL) {
558  if (!(dp->d_flags & RT_DIR_COMB)) {
559  bu_vls_printf(gedp->ged_result_str, "ERROR: %s is not a combination", comb_name);
560  return GED_ERROR;
561  }
562  if (!alter_existing) {
563  bu_vls_printf(gedp->ged_result_str, "ERROR: %s already exists.", comb_name);
564  return GED_ERROR;
565  }
566  }
567 
568  /* If we aren't performing one of the option operations,
569  * proceed with the standard comb build */
570  if (standard_comb_build) {
571 
572  /* Now, we're ready to process operation/object pairs, if any */
573  /* Check for odd number of arguments */
574  if (argc & 01) {
575  bu_vls_printf(gedp->ged_result_str, "error in number of args!");
576  return GED_ERROR;
577  }
578 
579  /* Get operation and solid name for each solid */
580  for (i = 2; i < argc; i += 2) {
581  /* they come in pairs */
582  if (i+1 >= argc) {
583  bu_vls_printf(gedp->ged_result_str, "Invalid syntax near '%s', ignored. Expecting object name after operator.\n", argv[i+1]);
584  return GED_ERROR;
585  }
586 
587  oper = db_str2op(argv[i]);
588  if (oper == DB_OP_NULL) {
589  bu_vls_printf(gedp->ged_result_str, "Unknown operator '%c' (0x%x) encountered, invalid syntax.\n", argv[i][0], argv[i][0]);
590  continue;
591  }
592 
593  /* object name comes after op */
594  if ((dp = db_lookup(gedp->ged_wdbp->dbip, argv[i+1], LOOKUP_NOISY)) == RT_DIR_NULL) {
595  bu_vls_printf(gedp->ged_result_str, "Object '%s does not exist.\n", argv[i+1]);
596  continue;
597  }
598 
599  /* add it to the comb immediately */
600  if (_ged_combadd(gedp, dp, comb_name, 0, oper, 0, 0) == RT_DIR_NULL) {
601  bu_vls_printf(gedp->ged_result_str, "Error adding '%s' (with op '%c') to '%s'\n", dp->d_namep, (char)oper, comb_name);
602  return GED_ERROR;
603  }
604  }
605  }
606 
607  /* Handle the -w option for "wrapping" the contents of the comb */
608  if (wrap_comb) {
609  if (!dp || dp == RT_DIR_NULL) {
610  bu_vls_printf(gedp->ged_result_str, "Combination '%s does not exist.\n", comb_name);
611  return GED_ERROR;
612  }
613  if (comb_wrap(gedp, dp) == GED_ERROR) {
614  return GED_ERROR;
615  } else {
616  if ((dp=db_lookup(gedp->ged_wdbp->dbip, comb_name, LOOKUP_QUIET)) == RT_DIR_NULL) {
617  bu_vls_printf(gedp->ged_result_str, "ERROR: wrap of %s failed", comb_name);
618  return GED_ERROR;
619  }
620  }
621  }
622 
623  if (flatten_comb) {
624  if (!dp || dp == RT_DIR_NULL) {
625  bu_vls_printf(gedp->ged_result_str, "Combination '%s does not exist.\n", comb_name);
626  return GED_ERROR;
627  }
628  if (comb_flatten(gedp, dp) == GED_ERROR) {
629  return GED_ERROR;
630  } else {
631  if ((dp=db_lookup(gedp->ged_wdbp->dbip, comb_name, LOOKUP_QUIET)) == RT_DIR_NULL) {
632  bu_vls_printf(gedp->ged_result_str, "ERROR: flattening of %s failed", comb_name);
633  return GED_ERROR;
634  }
635  }
636  }
637 
638  if (lift_region_comb) {
639  if (!dp || dp == RT_DIR_NULL) {
640  bu_vls_printf(gedp->ged_result_str, "Combination '%s does not exist.\n", comb_name);
641  return GED_ERROR;
642  }
643  if (comb_lift_region(gedp, dp) == GED_ERROR) {
644  return GED_ERROR;
645  } else {
646  if ((dp=db_lookup(gedp->ged_wdbp->dbip, comb_name, LOOKUP_QUIET)) == RT_DIR_NULL) {
647  bu_vls_printf(gedp->ged_result_str, "ERROR: region lift to %s failed", comb_name);
648  return GED_ERROR;
649  }
650  }
651  }
652 
653 
654  /* Make sure the region flag is set appropriately */
655  if (set_comb || set_region) {
656  if ((dp = db_lookup(gedp->ged_wdbp->dbip, comb_name, LOOKUP_NOISY)) != RT_DIR_NULL) {
657  if (set_region) {
658  if (region_flag_set(gedp, dp) == GED_ERROR)
659  return GED_ERROR;
660  }
661  if (set_comb) {
662  if (region_flag_clear(gedp, dp) == GED_ERROR)
663  return GED_ERROR;
664  }
665  }
666  }
667 
668  return GED_OK;
669 }
670 
671 
672 /*
673  * Add an instance of object 'objp' to combination 'name'.
674  * If the combination does not exist, it is created.
675  * region_flag is 1 (region), or 0 (group).
676  *
677  * Preserves the GIFT semantics.
678  */
679 struct directory *
680 _ged_combadd(struct ged *gedp,
681  struct directory *objp,
682  char *combname,
683  int region_flag, /* true if adding region */
684  db_op_t relation, /* = UNION, SUBTRACT, INTERSECT */
685  int ident, /* "Region ID" */
686  int air /* Air code */)
687 {
688  int ac = 1;
689  const char *av[2];
690 
691  av[0] = objp->d_namep;
692  av[1] = NULL;
693 
694  if (_ged_combadd2(gedp, combname, ac, av, region_flag, relation, ident, air) == GED_ERROR)
695  return RT_DIR_NULL;
696 
697  return db_lookup(gedp->ged_wdbp->dbip, combname, LOOKUP_QUIET);
698 }
699 
700 
701 /*
702  * Add an instance of object 'objp' to combination 'name'.
703  * If the combination does not exist, it is created.
704  * region_flag is 1 (region), or 0 (group).
705  *
706  * Preserves the GIFT semantics.
707  */
708 int
709 _ged_combadd2(struct ged *gedp,
710  char *combname,
711  int argc,
712  const char *argv[],
713  int region_flag, /* true if adding region */
714  db_op_t relation, /* = UNION, SUBTRACT, INTERSECT */
715  int ident, /* "Region ID" */
716  int air /* Air code */)
717 {
718  struct directory *dp;
719  struct directory *objp;
720  struct rt_db_internal intern;
721  struct rt_comb_internal *comb;
722  union tree *tp;
723  struct rt_tree_array *tree_list;
724  size_t node_count;
725  size_t actual_count;
726  size_t curr_count;
727  int i;
728 
729  if (argc < 1)
730  return GED_ERROR;
731 
732  /*
733  * Check to see if we have to create a new combination
734  */
735  if ((dp = db_lookup(gedp->ged_wdbp->dbip, combname, LOOKUP_QUIET)) == RT_DIR_NULL) {
736  int flags;
737 
738  if (region_flag)
739  flags = RT_DIR_REGION | RT_DIR_COMB;
740  else
741  flags = RT_DIR_COMB;
742 
743  RT_DB_INTERNAL_INIT(&intern);
744  intern.idb_major_type = DB5_MAJORTYPE_BRLCAD;
745  intern.idb_type = ID_COMBINATION;
746  intern.idb_meth = &OBJ[ID_COMBINATION];
747 
748  GED_DB_DIRADD(gedp, dp, combname, -1, 0, flags, (void *)&intern.idb_type, 0);
749 
750  BU_ALLOC(comb, struct rt_comb_internal);
751  RT_COMB_INTERNAL_INIT(comb);
752 
753  intern.idb_ptr = (void *)comb;
754 
755  if (region_flag) {
756  comb->region_flag = 1;
757  comb->region_id = ident;
758  comb->aircode = air;
759  comb->los = gedp->ged_wdbp->wdb_los_default;
760  comb->GIFTmater = gedp->ged_wdbp->wdb_mat_default;
761  bu_vls_printf(gedp->ged_result_str, "Creating region with attrs: region_id=%d, ", ident);
762  if (air)
763  bu_vls_printf(gedp->ged_result_str, "air=%d, ", air);
764  bu_vls_printf(gedp->ged_result_str, "los=%d, material_id=%d\n",
765  gedp->ged_wdbp->wdb_los_default,
766  gedp->ged_wdbp->wdb_mat_default);
767  } else {
768  comb->region_flag = 0;
769  }
770 
771  goto addmembers;
772  } else if (!(dp->d_flags & RT_DIR_COMB)) {
773  bu_vls_printf(gedp->ged_result_str, "%s exists, but is not a combination\n", dp->d_namep);
774  return GED_ERROR;
775  }
776 
777  /* combination exists, add a new member */
778  GED_DB_GET_INTERNAL(gedp, &intern, dp, (fastf_t *)NULL, &rt_uniresource, 0);
779 
780  comb = (struct rt_comb_internal *)intern.idb_ptr;
781  RT_CK_COMB(comb);
782 
783  if (region_flag && !comb->region_flag) {
784  bu_vls_printf(gedp->ged_result_str, "%s: not a region\n", dp->d_namep);
785  return GED_ERROR;
786  }
787 
788 addmembers:
789  if (comb->tree && db_ck_v4gift_tree(comb->tree) < 0) {
791  if (db_ck_v4gift_tree(comb->tree) < 0) {
792  bu_vls_printf(gedp->ged_result_str, "Cannot flatten tree for editing\n");
793  rt_db_free_internal(&intern);
794  return GED_ERROR;
795  }
796  }
797 
798  /* make space for an extra leaf */
799  curr_count = db_tree_nleaves(comb->tree);
800  node_count = curr_count + argc;
801  tree_list = (struct rt_tree_array *)bu_calloc(node_count, sizeof(struct rt_tree_array), "tree list");
802 
803  /* flatten tree */
804  if (comb->tree) {
805  actual_count = argc + (struct rt_tree_array *)db_flatten_tree(tree_list, comb->tree, OP_UNION, 1, &rt_uniresource) - tree_list;
806  BU_ASSERT_SIZE_T(actual_count, ==, node_count);
807  comb->tree = TREE_NULL;
808  }
809 
810  for (i = 0; i < argc; ++i) {
811  if ((objp = db_lookup(gedp->ged_wdbp->dbip, argv[i], LOOKUP_NOISY)) == RT_DIR_NULL) {
812  bu_vls_printf(gedp->ged_result_str, "skip member %s\n", argv[i]);
813  continue;
814  }
815 
816  /* insert new member at end */
817  switch (relation) {
818  case DB_OP_INTERSECT:
819  tree_list[curr_count].tl_op = OP_INTERSECT;
820  break;
821  case DB_OP_SUBTRACT:
822  tree_list[curr_count].tl_op = OP_SUBTRACT;
823  break;
824  default:
825  if (relation != DB_OP_UNION) {
826  bu_vls_printf(gedp->ged_result_str, "unrecognized relation (assume UNION)\n");
827  }
828  tree_list[curr_count].tl_op = OP_UNION;
829  break;
830  }
831 
832  /* make new leaf node, and insert at end of list */
834  tree_list[curr_count].tl_tree = tp;
835  tp->tr_l.tl_op = OP_DB_LEAF;
836  tp->tr_l.tl_name = bu_strdup(objp->d_namep);
837  tp->tr_l.tl_mat = (matp_t)NULL;
838 
839  ++curr_count;
840  }
841 
842  /* rebuild the tree */
843  comb->tree = (union tree *)db_mkgift_tree(tree_list, node_count, &rt_uniresource);
844 
845  /* and finally, write it out */
846  GED_DB_PUT_INTERNAL(gedp, dp, &intern, &rt_uniresource, 0);
847 
848  bu_free((char *)tree_list, "combadd: tree_list");
849 
850  return GED_OK;
851 }
852 
853 
854 /*
855  * Local Variables:
856  * mode: C
857  * tab-width: 8
858  * indent-tabs-mode: t
859  * c-file-style: "stroustrup"
860  * End:
861  * ex: shiftwidth=4 tabstop=8
862  */
void usage(struct ged *gedp)
Definition: coil.c:315
#define GED_DB_DIRADD(_gedp, _dp, _name, _laddr, _len, _dirflags, _ptr, _flags)
Definition: ged.h:213
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
int db_dirdelete(struct db_i *, struct directory *dp)
Definition: db_lookup.c:262
size_t db5_standardize_avs(struct bu_attribute_value_set *avs)
Definition: db5_attr.c:223
void bu_avs_init_empty(struct bu_attribute_value_set *avp)
Definition: avs.c:36
void db_search_free(struct bu_ptbl *search_results)
Definition: search.c:2089
char region_flag
!0 ==> this COMB is a REGION
Definition: raytrace.h:939
int wdb_mat_default
GIFT material code.
Definition: raytrace.h:1280
#define BU_PTBL_INIT_ZERO
Definition: ptbl.h:92
int db5_update_attributes(struct directory *dp, struct bu_attribute_value_set *avsp, struct db_i *dbip)
Definition: attributes.c:285
size_t fp_len
Definition: db_fullpath.h:44
size_t db_tree_nleaves(const union tree *tp)
Definition: db_comb.c:99
Definition: ged.h:338
#define BU_PTBL_FOR(ip, cast, ptbl)
Definition: ptbl.h:125
struct db_i * dbip
Definition: raytrace.h:1266
void bu_ptbl_init(struct bu_ptbl *b, size_t len, const char *str)
Definition: ptbl.c:32
int bu_avs_add(struct bu_attribute_value_set *avp, const char *attribute, const char *value)
Definition: avs.c:78
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
#define RT_CK_COMB(_p)
Definition: raytrace.h:955
int bu_ptbl_rm(struct bu_ptbl *b, const long *p)
union tree * tl_tree
Definition: raytrace.h:1247
void bu_free_external(struct bu_external *ep)
#define DB_SEARCH_RETURN_UNIQ_DP
Return the set of unique directory pointers instead of full paths.
Definition: search.h:113
struct rt_wdb * ged_wdbp
Definition: ged.h:340
Header file for the BRL-CAD common definitions.
int bu_ptbl_ins_unique(struct bu_ptbl *b, long *p)
int bu_optind
Definition: globals.c:89
#define DB_FULL_PATH_CUR_DIR(_pp)
Definition: db_fullpath.h:51
db_op_t db_str2op(const char *str)
Definition: op.c:31
HIDDEN int region_flag_clear(struct ged *gedp, struct directory *dp)
Definition: comb.c:58
#define RT_DIR_REGION
region
Definition: raytrace.h:885
union tree * db_mkgift_tree(struct rt_tree_array *trees, size_t subtreecount, struct resource *resp)
Definition: db_comb.c:1016
int bu_ptbl_ins(struct bu_ptbl *b, long *p)
int _ged_combadd2(struct ged *gedp, char *combname, int argc, const char *argv[], int region_flag, db_op_t relation, int ident, int air)
Definition: comb.c:709
#define WMOP_UNION
Definition: wdb.h:887
#define ID_COMBINATION
Combination Record.
Definition: raytrace.h:499
int bu_getopt(int nargc, char *const nargv[], const char *ostr)
Definition: getopt.c:43
void bu_ptbl_reset(struct bu_ptbl *b)
Definition: ptbl.c:49
#define GED_ERROR
Definition: ged.h:61
#define HIDDEN
Definition: common.h:86
int wdb_put_internal(struct rt_wdb *wdbp, const char *name, struct rt_db_internal *ip, double local2mm)
Definition: wdb.c:218
#define DB_SEARCH_TREE
Do a hierarchy-aware search. This is the default.
Definition: search.h:110
Definition: ptbl.h:62
#define GED_DB_PUT_INTERNAL(_gedp, _dp, _intern, _resource, _flags)
Definition: ged.h:243
struct rt_tree_array * db_flatten_tree(struct rt_tree_array *rt_tree_array, union tree *tp, int op, int avail, struct resource *resp)
Definition: db_comb.c:138
int idb_major_type
Definition: raytrace.h:192
#define DB_FULL_PATH_GET(_pp, _i)
Definition: db_fullpath.h:55
void bu_vls_free(struct bu_vls *vp)
Definition: vls.c:248
unsigned char d_minor_type
object minor type
Definition: raytrace.h:871
#define OP_SUBTRACT
Binary: L subtract R.
Definition: raytrace.h:1129
HIDDEN int name_compare(const void *d1, const void *d2, void *arg)
Definition: comb.c:208
void db_non_union_push(union tree *tp, struct resource *resp)
Definition: db_tree.c:1426
#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
db_op_t
Definition: op.h:33
#define OP_DB_LEAF
Leaf of combination, db fmt.
Definition: raytrace.h:1139
#define RT_CK_DB_INTERNAL(_p)
Definition: raytrace.h:207
int db_get_external(struct bu_external *ep, const struct directory *dp, const struct db_i *dbip)
#define BU_ALLOC(_ptr, _type)
Definition: malloc.h:223
void * bu_calloc(size_t nelem, size_t elsize, const char *str)
Definition: malloc.c:321
int bu_avs_remove(struct bu_attribute_value_set *avp, const char *attribute)
Definition: avs.c:195
int wdb_export_external(struct rt_wdb *wdbp, struct bu_external *ep, const char *name, int flags, unsigned char minor_type)
Definition: wdb.c:105
#define BU_PTBL_GET(ptbl, i)
Definition: ptbl.h:108
#define RT_DB_INTERNAL_INIT(_p)
Definition: raytrace.h:199
void bu_vls_sprintf(struct bu_vls *vls, const char *fmt,...) _BU_ATTR_PRINTF23
Definition: vls.c:707
#define LOOKUP_QUIET
Definition: raytrace.h:893
#define DB_LS_TOPS
Definition: raytrace.h:4657
const struct rt_functab * idb_meth
for ft_ifree(), etc.
Definition: raytrace.h:194
HIDDEN int color_shader_clear(struct ged *gedp, struct directory *dp)
Definition: comb.c:78
#define TREE_NULL
Definition: raytrace.h:1181
void db_free_tree(union tree *tp, struct resource *resp)
Definition: db_tree.c:1296
void bu_sort(void *array, size_t nummemb, size_t sizememb, int(*compare)(const void *, const void *, void *), void *context)
Definition: sort.c:110
matp_t tl_mat
xform matp, NULL ==> identity
Definition: raytrace.h:1173
int db5_replace_attributes(struct directory *dp, struct bu_attribute_value_set *avsp, struct db_i *dbip)
Definition: attributes.c:223
char * tl_name
Name of this leaf (bu_strdup'ed)
Definition: raytrace.h:1174
int wdb_los_default
Line-of-sight estimate.
Definition: raytrace.h:1281
#define UNUSED(parameter)
Definition: common.h:239
char * bu_vls_addr(const struct bu_vls *vp)
Definition: vls.c:111
struct bu_vls * ged_result_str
Definition: ged.h:357
size_t db_ls(const struct db_i *dbip, int flags, const char *pattern, struct directory ***dpv)
Definition: ls.c:58
int bu_ptbl_locate(const struct bu_ptbl *b, const long *p)
HIDDEN int comb_tree_clear(struct ged *gedp, struct directory *dp)
Definition: comb.c:100
#define BU_PTBL_LEN(ptbl)
Definition: ptbl.h:107
void bu_ptbl_free(struct bu_ptbl *b)
Definition: ptbl.c:226
int bu_strcmp(const char *string1, const char *string2)
Definition: str.c:171
void db5_sync_comb_to_attr(struct bu_attribute_value_set *avs, const struct rt_comb_internal *comb)
Definition: db5_attr.c:447
struct tree::tree_db_leaf tr_l
void * idb_ptr
Definition: raytrace.h:195
#define RT_DIR_COMB
combination
Definition: raytrace.h:884
const struct rt_functab OBJ[]
Definition: table.c:159
Definition: op.h:35
int db_comb_mvall(struct directory *dp, struct db_i *dbip, const char *old_name, const char *new_name, struct bu_ptbl *stack)
Definition: db_comb.c:1107
#define RT_GET_TREE(_tp, _res)
Definition: raytrace.h:1210
#define RT_COMB_INTERNAL_INIT(_p)
Definition: raytrace.h:960
union tree * tree
Leading to tree_db_leaf leaves.
Definition: raytrace.h:938
void bu_vls_printf(struct bu_vls *vls, const char *fmt,...) _BU_ATTR_PRINTF23
Definition: vls.c:694
HIDDEN int comb_flatten(struct ged *gedp, struct directory *dp)
Definition: comb.c:230
#define RT_DIR_NULL
Definition: raytrace.h:875
#define LOOKUP_NOISY
Definition: raytrace.h:892
#define BU_PTBL_BASEADDR(ptbl)
Definition: ptbl.h:104
Definition: op.h:34
#define GED_HELP
Definition: ged.h:62
#define GED_DB_GET_INTERNAL(_gedp, _intern, _dp, _mat, _resource, _flags)
Definition: ged.h:233
#define BU_ASSERT_SIZE_T(_lhs, _relation, _rhs)
Definition: defines.h:253
HIDDEN int comb_wrap(struct ged *gedp, struct directory *dp)
Definition: comb.c:124
int ged_comb(struct ged *gedp, int argc, const char *argv[])
Definition: comb.c:466
void bu_free(void *ptr, const char *str)
Definition: malloc.c:328
#define BU_VLS_INIT_ZERO
Definition: vls.h:84
struct directory * _ged_combadd(struct ged *gedp, struct directory *objp, char *combname, int region_flag, db_op_t relation, int ident, int air)
Definition: comb.c:680
int tl_op
leaf, OP_DB_LEAF
Definition: raytrace.h:1172
HIDDEN int comb_lift_region(struct ged *gedp, struct directory *dp)
Definition: comb.c:322
#define GED_CHECK_READ_ONLY(_gedp, _flags)
Definition: ged.h:181
int db5_get_attributes(const struct db_i *dbip, struct bu_attribute_value_set *avs, const struct directory *dp)
Definition: db5_io.c:1027
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
int db_ck_v4gift_tree(const union tree *tp)
Definition: db_comb.c:927
int db_delete(struct db_i *, struct directory *dp)
Definition: db_alloc.c:132
void bu_avs_free(struct bu_attribute_value_set *avp)
Definition: avs.c:235
void rt_db_free_internal(struct rt_db_internal *ip)
Definition: dir.c:216
HIDDEN int region_flag_set(struct ged *gedp, struct directory *dp)
Definition: comb.c:38
#define bu_strdup(s)
Definition: str.h:71
int db_search(struct bu_ptbl *results, int flags, const char *filter, int path_c, struct directory **path_v, struct db_i *dbip)
Search for objects in a geometry database using filters.
Definition: search.c:2232