text: /*                         G - X X X . C
   * BRL-CAD
   *
   * Copyright (c) 1993-2014 United States Government as represented by
   * the U.S. Army Research Laboratory.
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public License
   * version 2.1 as published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this file; see the file named COPYING for more
   * information.
   *
   */
  /** @file conv/g-xxx.c
   *
   * Sample code for converting BRL-CAD models to some other format.
   * This code assumes that your receiving format can handle CSG
   * primitives and Boolean trees with transformation matrices
   *
   */
  
  #include "common.h"
  
  /* system headers */
  #include <stdlib.h>
  #include <math.h>
  #include <string.h>
  #include "bio.h"
  
  /* interface headers */
  #include "vmath.h"
  #include "bu/getopt.h"
  #include "nmg.h"
  #include "rt/geom.h"
  #include "raytrace.h"
  #include "wdb.h"
  
  
  /*
   * data container for your data.  you create one of these with
   * whatever fields you need.  it gets passed to all of the callback
   * functions for your application use as a client_data pointer.
   */
  struct user_data {
      long int data;
      struct bn_tol tol;
  };
  
  
  /* This routine just produces an ascii description of the Boolean tree.
   * In a real converter, this would output the tree in the desired format.
   */
  void
  describe_tree(union tree *tree,
  	      struct bu_vls *str)
  {
      struct bu_vls left = BU_VLS_INIT_ZERO;
      struct bu_vls right = BU_VLS_INIT_ZERO;
      const char op_xor='^';
      char op='\0';
  
      BU_CK_VLS(str);
  
      if (!tree) {
  	/* this tree has no members */
  	bu_vls_strcat(str, "-empty-");
  	return;
      }
  
      RT_CK_TREE(tree);
  
      /* Handle all the possible node types.
       * the first four are the most common types, and are typically
       * the only ones found in a BRL-CAD database.
       */
      switch (tree->tr_op) {
  	case OP_DB_LEAF:	/* leaf node, this is a member */
  	    /* Note: tree->tr_l.tl_mat is a pointer to a
  	     * transformation matrix to apply to this member
  	     */
  	    bu_vls_strcat(str,  tree->tr_l.tl_name);
  	    break;
  	case OP_UNION:		/* union operator node */
  	    op = DB_OP_UNION;
  	    goto binary;
  	case OP_INTERSECT:	/* intersection operator node */
  	    op = DB_OP_INTERSECT;
  	    goto binary;
  	case OP_SUBTRACT:	/* subtraction operator node */
  	    op = DB_OP_SUBTRACT;
  	    goto binary;
  	case OP_XOR:		/* exclusive "or" operator node */
  	    op = op_xor;
  	binary:				/* common for all binary nodes */
  	    describe_tree(tree->tr_b.tb_left, &left);
  	    describe_tree(tree->tr_b.tb_right, &right);
  	    bu_vls_putc(str, '(');
  	    bu_vls_vlscatzap(str, &left);
  	    bu_vls_printf(str, " %c ", op);
  	    bu_vls_vlscatzap(str, &right);
  	    bu_vls_putc(str, ')');
  	    break;
  	case OP_NOT:
  	    bu_vls_strcat(str, "(!");
  	    describe_tree(tree->tr_b.tb_left, str);
  	    bu_vls_putc(str, ')');
  	    break;
  	case OP_GUARD:
  	    bu_vls_strcat(str, "(G");
  	    describe_tree(tree->tr_b.tb_left, str);
  	    bu_vls_putc(str, ')');
  	    break;
  	case OP_XNOP:
  	    bu_vls_strcat(str, "(X");
  	    describe_tree(tree->tr_b.tb_left, str);
  	    bu_vls_putc(str, ')');
  	    break;
  	case OP_NOP:
  	    bu_vls_strcat(str, "NOP");
  	    break;
  	default:
  	    bu_exit(1, "ERROR: describe_tree() got unrecognized op (%d)\n", tree->tr_op);
      }
  }
  
  
  /**
   * @brief This routine is called when a region is first encountered in the
   * hierarchy when processing a tree
   *
   *      @param tsp tree state (for parsing the tree)
   *      @param pathp A listing of all the nodes traversed to get to this node in the database
   *      @param combp the combination record for this region
   */
  int
  region_start(struct db_tree_state *tsp,
  	     const struct db_full_path *pathp,
  	     const struct rt_comb_internal *combp,
  	     void *client_data)
  {
      char *name;
      struct directory *dp;
      struct bu_vls str = BU_VLS_INIT_ZERO;
      struct user_data *your_stuff = (struct user_data *)client_data;
  
      RT_CK_DBTS(tsp);
  
      name = db_path_to_string(pathp);
      bu_log("region_start %s\n", name);
      bu_free(name, "reg_start name");
  
      bu_log("data = %ld\n", your_stuff->data);
      rt_pr_tol(&your_stuff->tol);
  
      dp = DB_FULL_PATH_CUR_DIR(pathp);
  
      /* here is where the conversion should be done */
      if (combp->region_flag)
  	printf("Write this region (name=%s) as a part in your format:\n", dp->d_namep);
      else
  	printf("Write this combination (name=%s) as an assembly in your format:\n", dp->d_namep);
  
      describe_tree(combp->tree, &str);
  
      printf("\t%s\n\n", bu_vls_addr(&str));
  
      bu_vls_free(&str);
  
      return 0;
  }
  
  
  /**
   * @brief This is called when all sub-elements of a region have been processed by leaf_func.
   *
   * @param tsp     tree state
   * @param pathp   db path
   * @param curtree current tree
   *
   * @return TREE_NULL if data in curtree was "stolen", otherwise db_walk_tree will
   * clean up the data in the union tree * that is returned
   *
   * If it wants to retain the data in curtree it can by returning TREE_NULL.  Otherwise
   * db_walk_tree will clean up the data in the union tree * that is returned.
   *
   */
  union tree *
  region_end (struct db_tree_state *tsp,
  	    const struct db_full_path *pathp,
  	    union tree *curtree,
  	    void *UNUSED(client_data))
  {
      char *name;
  
      RT_CK_DBTS(tsp);
  
      name = db_path_to_string(pathp);
      bu_log("region_end   %s\n", name);
      bu_free(name, "region_end name");
  
      return curtree;
  }
  
  
  /* This routine is called by the tree walker (db_walk_tree)
   * for every primitive encountered in the trees specified on the command line */
  union tree *
  primitive_func(struct db_tree_state *tsp,
  	       const struct db_full_path *pathp,
  	       struct rt_db_internal *ip,
  	       void *UNUSED(client_data))
  {
      struct directory *dp;
      char *name;
      dp = DB_FULL_PATH_CUR_DIR(pathp);
  
      RT_CK_DBTS(tsp);
  
      name = db_path_to_string(pathp);
      bu_log("leaf_func    %s\n", name);
      bu_free(name, "region_end name");
  
      /* handle each type of primitive (see h/rtgeom.h) */
      if (ip->idb_major_type == DB5_MAJORTYPE_BRLCAD) {
  	switch (ip->idb_type) {
  	    /* most commonly used primitives */
  	    case ID_TOR:	/* torus */
  		{
  		    struct rt_tor_internal *tor = (struct rt_tor_internal *)ip->idb_ptr;
  
  		    printf("Write this torus (name=%s) in your format:\n", dp->d_namep);
  		    printf("\tV=(%g %g %g)\n", V3ARGS(tor->v));
  		    printf("\tnormal=(%g %g %g)\n", V3ARGS(tor->h));
  		    printf("\tradius1 = %g\n", tor->r_a);
  		    printf("\tradius2 = %g\n", tor->r_h);
  		    break;
  		}
  	    case ID_TGC: /* truncated general cone frustum */
  	    case ID_REC: /* right elliptical cylinder */
  		{
  		    /* This primitive includes circular cross-section
  		     * cones and cylinders
  		     */
  		    struct rt_tgc_internal *tgc = (struct rt_tgc_internal *)ip->idb_ptr;
  
  		    printf("Write this TGC (name=%s) in your format:\n", dp->d_namep);
  		    printf("\tV=(%g %g %g)\n", V3ARGS(tgc->v));
  		    printf("\tH=(%g %g %g)\n", V3ARGS(tgc->h));
  		    printf("\tA=(%g %g %g)\n", V3ARGS(tgc->a));
  		    printf("\tB=(%g %g %g)\n", V3ARGS(tgc->b));
  		    printf("\tC=(%g %g %g)\n", V3ARGS(tgc->c));
  		    printf("\tD=(%g %g %g)\n", V3ARGS(tgc->d));
  		    break;
  		}
  	    case ID_ELL:
  	    case ID_SPH:
  		{
  		    /* spheres and ellipsoids */
  		    struct rt_ell_internal *ell = (struct rt_ell_internal *)ip->idb_ptr;
  
  		    printf("Write this ellipsoid (name=%s) in your format:\n", dp->d_namep);
  		    printf("\tV=(%g %g %g)\n", V3ARGS(ell->v));
  		    printf("\tA=(%g %g %g)\n", V3ARGS(ell->a));
  		    printf("\tB=(%g %g %g)\n", V3ARGS(ell->b));
  		    printf("\tC=(%g %g %g)\n", V3ARGS(ell->c));
  		    break;
  		}
  	    case ID_ARB8:	/* convex primitive with from four to six faces */
  		{
  		    int i;
  
  		    /* this primitive may have degenerate faces
  		     * faces are: 0123, 7654, 0347, 1562, 0451, 3267
  		     * (points listed above in counter-clockwise order)
  		     */
  		    struct rt_arb_internal *arb = (struct rt_arb_internal *)ip->idb_ptr;
  
  		    printf("Write this ARB (name=%s) in your format:\n", dp->d_namep);
  		    for (i=0; i<8; i++)
  			printf("\tpoint #%d: (%g %g %g)\n", i, V3ARGS(arb->pt[i]));
  		    break;
  		}
  
  		/* other primitives, left as an exercise to the reader */
  
  	    case ID_BOT:	/* Bag O' Triangles */
  	    case ID_ARS:
  		    /* series of curves
  		     * each with the same number of points
  		     */
  	    case ID_HALF:
  		    /* half universe defined by a plane */
  	    case ID_POLY:
  		    /* polygons (up to 5 vertices per) */
  	    case ID_BSPLINE:
  		    /* NURB surfaces */
  	    case ID_NMG:
  		    /* N-manifold geometry */
  	    case ID_ARBN:
  	    case ID_DSP:
  		    /* Displacement map (terrain primitive) */
  		    /* the DSP primitive may reference an external file or binunif object */
  	    case ID_HF:
  		    /* height field (terrain primitive) */
  		    /* the HF primitive references an external file */
  	    case ID_EBM:
  		    /* extruded bit-map */
  		    /* the EBM primitive references an external file */
  	    case ID_VOL:
  		    /* the VOL primitive references an external file */
  	    case ID_PIPE:
  	    case ID_PARTICLE:
  	    case ID_RPC:
  	    case ID_RHC:
  	    case ID_EPA:
  	    case ID_EHY:
  	    case ID_ETO:
  	    case ID_GRIP:
  	    case ID_SKETCH:
  	    case ID_EXTRUDE:
  		    /* note that an extrusion references a sketch, make sure you convert
  		     * the sketch also
  		     */
  	    default:
  		bu_log("Primitive %s is an unsupported or unrecognized type (%d)\n", dp->d_namep, ip->idb_type);
  		break;
  	}
      } else {
  	switch (ip->idb_major_type) {
  	    case DB5_MAJORTYPE_BINARY_UNIF:
  		{
  		    /* not actually a primitive, just a block of storage for data
  		     * a uniform array of chars, ints, floats, doubles, ...
  		     */
  		    struct rt_binunif_internal *bin = (struct rt_binunif_internal *)ip->idb_ptr;
  
  		    if (bin)
  			printf("Found a binary object (%s)\n\n", dp->d_namep);
  		    break;
  		}
  	    default:
  		bu_log("Major type of %s is unrecognized type (%d)\n", dp->d_namep, ip->idb_major_type);
  		break;
  	}
      }
  
      return (union tree *) NULL;
  }
  
  static void
  print_usage(const char *progname)
  {
      const char *usage = "[-xX lvl] [-a abs_tol] [-r rel_tol] [-n norm_tol] "
  	"[-o out_file] brlcad_db.g object(s)\n";
      bu_exit(1, "Usage: %s %s", progname, usage);
  }
  
  int
  main(int argc, char *argv[])
  {
      struct user_data your_data = {0, BN_TOL_INIT_ZERO};
  
      int i;
      int c;
      char idbuf[132] = {0};
  
      struct rt_i *rtip;
      struct db_tree_state init_state;
  
      bu_setprogname(argv[0]);
      bu_setlinebuf(stderr);
  
      /* calculational tolerances
       * mostly used by NMG routines
       */
      your_data.tol.magic = BN_TOL_MAGIC;
      your_data.tol.dist = BN_TOL_DIST;
      your_data.tol.dist_sq = your_data.tol.dist * your_data.tol.dist;
      your_data.tol.perp = 1e-6;
      your_data.tol.para = 1 - your_data.tol.perp;
  
      /* Get command line arguments. */
      while ((c = bu_getopt(argc, argv, "t:a:n:o:r:x:X:")) != -1) {
  	switch (c) {
  	    case 't':		/* calculational tolerance */
  		your_data.tol.dist = atof(bu_optarg);
  		your_data.tol.dist_sq = your_data.tol.dist * your_data.tol.dist;
  	    case 'o':		/* Output file name */
  		/* grab output file name */
  		break;
  	    case 'x':		/* librt debug flag */
  		sscanf(bu_optarg, "%x", &RTG.debug);
  		bu_printb("librt RT_G_DEBUG", RT_G_DEBUG, DEBUG_FORMAT);
  		bu_log("\n");
  		break;
  	    case 'X':		/* NMG debug flag */
  		sscanf(bu_optarg, "%x", &RTG.NMG_debug);
  		bu_printb("librt RTG.NMG_debug", RTG.NMG_debug, NMG_DEBUG_FORMAT);
  		bu_log("\n");
  		break;
  	    default:
  		print_usage(argv[0]);
  		break;
  	}
      }
  
      if (bu_optind+1 >= argc) {
  	print_usage(argv[0]);
      }
  
      /* Open BRL-CAD database */
      /* Scan all the records in the database and build a directory */
      rtip=rt_dirbuild(argv[bu_optind], idbuf, sizeof(idbuf));
      if (rtip == RTI_NULL) {
  	bu_exit(1, "g-xxx: rt_dirbuild failure\n");
      }
  
      init_state = rt_initial_tree_state;
  
      bu_optind++;
  
      /* Walk the trees named on the command line
       * outputting combinations and primitives
       */
      for (i=bu_optind; i<argc; i++) {
  	db_walk_tree(rtip->rti_dbip, argc - i, (const char **)&argv[i], 1 /* bu_avail_cpus() */,
  		     &init_state, region_start, region_end, primitive_func, (void *) &your_data);
      }
  
      return 0;
  }
  
  
  /*
   * Local Variables:
   * mode: C
   * tab-width: 8
   * indent-tabs-mode: t
   * c-file-style: "stroustrup"
   * End:
   * ex: shiftwidth=4 tabstop=8
   */
  