home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C ++ / Applications / Venus 3.5 / ElevationMap.cc < prev    next >
Encoding:
Text File  |  1997-05-01  |  8.8 KB  |  217 lines  |  [TEXT/CWIE]

  1. // This may look like C code, but it is really -*- C++ -*-
  2. /*
  3.  ************************************************************************
  4.  *
  5.  *                           Constructing of an Elevation Map
  6.  *
  7.  * This code implements the ElevationMap class, the brunt of which is
  8.  * construction of the map: generating it at random (as fractal clouds), or
  9.  * loading from a givem file.
  10.  *
  11.  * The file to load an ElevationMap from must be a PGM file, with the pixel matrix
  12.  * in its data fork. PGM, a Portable Graymap format, is fairly popular on UNIX
  13.  * platform: a variety of tools/converters/picture editors can create/read/and
  14.  * save pictures in this format. On a Mac, a PGM file can be created by an NIH Image
  15.  * application: the famous GraphicConverter can make PGM out of almost any other
  16.  * picture format (PICT, GIF, TIFF, etc) into PGM. Since PGM is a portable format, one
  17.  * can use a PGM file created using any other tool on any other platform. Thus
  18.  * limiting the data file format to PGM is not that big a deal.
  19.  *
  20.  * The PGM file to load an ElevationMap from must have a Mac type 'PPGM',
  21.  * which is what GraphicConverter assigns when it saves a file in this format.
  22.  *
  23.  * The PGM is the best format to store an elevation map in: indeed, map's
  24.  * pixels are "elevation codes" related to elevations of the corresponding points
  25.  * on the map (see "ElevationMap.h" for more detail). The elevation codes and
  26.  * grayscale pixel values (luminance) share the same property: the bigger the value, the
  27.  * higher the corresponding point on a map is, or the brighter the corresponding pixel
  28.  * is. Therefore, it is very natural to use grayscale pixels to represent elevation maps.
  29.  *
  30.  * To associate colors with elevation codes, a user may supply a colormap, as
  31.  * a 'clut' resource. The map loader would try to load the first clut resource
  32.  * it can find in the input (PGM) file. One can always use ResEdit to add a resource
  33.  * fork to any file and add/create a 'clut' resource. This colormap should associate
  34.  * a color to each possible elevation code: thus, the colormap must have 256 entries
  35.  * (otherwise, it won't be loaded). The colormap should be arranged to permit color
  36.  * interpolation (see "ElevationMap.h")
  37.  *
  38.  * One could argue that PICT appears to be the best format to load the ElevationMap
  39.  * from. It is not the case, unfortunately. Although a PICT file (Picture) does have
  40.  * a pixmap and colormap inside it, they are not directly accessible. For example, the only
  41.  * portable and _recommended_ way to find out Picture colors is to query it via
  42.  * GetPictInfo(). However, the colormap it returns is *not* a colormap stored within
  43.  * the Picture object. The PictUtil package code scans the Picture and builds a new
  44.  * colormap, which may differ from the Picture's own. For example, suppose the Picture's
  45.  * own colormap contains an entry (value=127, color.rgb = 0xffff:0:0); further suppose
  46.  * that the pixel with value 127 never actually occurs in the Picture's pixmap. Then
  47.  * CLUT returned by GetPictInfo() would not have this color. Thus drawing the Picture
  48.  * almost certainly would involve color translation, or at least pixel value remapping.
  49.  * Although pixel value remapping is a transparent operation visually, this is a disaster
  50.  * for us: we need pixel values themselves (because they are elevation codes), not pixel
  51.  * colors.
  52.  *
  53.  * $Id$
  54.  *
  55.  ************************************************************************
  56.  */
  57.  
  58. #include "ElevationMap.h"
  59. #include "fractal_map.h"
  60. #include "filter.h"
  61. //#include <PictUtils.h>
  62.  
  63. class GaussClouds : public FractalMap
  64. {
  65.   public:
  66.     GaussClouds(const card order)
  67.       : FractalMap(order) {}
  68.                 // Well-known Gaussian random number generator
  69.                 // Note rand() returns a number [0,2^15-1],
  70.                 // that is, within [0,1] with a 15-bit implicit
  71.                 // fraction
  72.     inline int get_noise(const card scale) const {
  73.       long sum = 0;
  74.       for(register int i=0; i<12; i++)
  75.          sum += rand();            // keep the result within 
  76.       return (scale * (sum-(6<<15)))>>17; }    // [-scale/2,scale/2]
  77. };
  78.  
  79.  
  80.                         // Make a default elevation map
  81. ElevationMap::ElevationMap(const int order)
  82.   : IMAGE(FractalMap(order)),
  83.     clu_table(default_clut_id),
  84.     z_cutoff(0), elevation_factor(700)
  85. {
  86.   assert(elevation_factor>10);
  87.   filter(*this).nondet_average(30);
  88. }
  89.                             // Dump the contents of ElevationMap
  90. void ElevationMap::dump(const char title[]) const
  91. {
  92.   message("\nElevation map: %s",title);
  93.   info();
  94.   clu_table.info();
  95.   message("  color-to-elevation transform: cutoff %d, scale %d\n",z_cutoff,elevation_factor);        
  96. }
  97.  
  98. static inline card min(card a, card b) { return a < b ? a : b; }
  99.  
  100.                             // assign src to an existing dst, wrapping-around if necessary
  101.                             // The source and destination IMAGEs' dimensions
  102.                             // don't have to be the same: we cut those parts of the
  103.                             // picture that don't fit into the IMAGE; we fill in the
  104.                             // parts which are shorter. In filling in, we assume
  105.                             // the PICTure is periodic...
  106. static void assign_wrap(IMAGE& dest, IMAGE& src)
  107. {
  108.   if( dest.q_nrows() == src.q_nrows() && dest.q_ncols() == src.q_ncols() )
  109.   {
  110.     dest = src;
  111.     return;
  112.   }
  113.   
  114.   const int min_row = ::min(dest.q_nrows(),src.q_nrows());
  115.   register int starting_col = 0;
  116.   for(; starting_col + src.q_ncols() < dest.q_ncols(); starting_col += src.q_ncols())
  117.   {                                    // Tiling the src image (columnwise) to fill the entire dst
  118.     dest.rectangle(rowcol(0,starting_col),
  119.                    rowcol(min_row-1,starting_col+src.q_ncols()-1)) =
  120.       src.rectangle(rowcol(0,0),rowcol(min_row-1,src.q_ncols()-1));
  121.   }
  122.   dest.rectangle(rowcol(0,starting_col),
  123.                  rowcol(min_row-1,dest.q_ncols()-1)) =
  124.       src.rectangle(rowcol(0,0),rowcol(min_row-1,dest.q_ncols()-starting_col-1));
  125.     
  126.                                     // There is overlapping assignments going on in here...
  127.                                     // Still, it is done in the _right_ way, so the result
  128.                                     // is correct!
  129.   if( dest.q_nrows() > src.q_nrows() )
  130.     dest.rectangle(rowcol(src.q_nrows(),0),rowcol(dest.q_nrows()-1,dest.q_ncols()-1)) =
  131.       dest.rectangle(rowcol(0,0),rowcol(dest.q_nrows() - src.q_nrows()-1,dest.q_ncols()-1));
  132. }
  133.  
  134. class FileContainer
  135. {
  136.   short vRefNum;            // Volume Ref number, or a Working Dir Number
  137.   long dirID;                // Dir number
  138. public:
  139.   FileContainer(void)        // The current file container
  140.       { do_well( HGetVol(nil,&vRefNum,&dirID) ); }
  141.   FileContainer(const FSSpec& file_spec) // File container of a specific file
  142.       : vRefNum(file_spec.vRefNum), dirID(file_spec.parID) {}
  143.   void make_current(void) const
  144.     { do_well( HSetVol("\p",vRefNum,dirID) ); }
  145.   void dump(const char title []) const
  146.       { message("\nFile container '%s': volume id %d, dir id %d\n",title,vRefNum,dirID); }
  147. };
  148.  
  149.                             // Try to load a CLUtable from the first clut resource of
  150.                             // the given file, and assign it to the given CLUTable,
  151.                             // if compatible (that is, has the same number of colors)
  152.                             // Return true if the assignment succeeds, return false
  153.                             // if the file does not have a clut resource, or it is
  154.                             // incompatible with the given CLUTable
  155. static bool load_new_clut(CLUTable& clut, const FSSpec& file_spec)
  156. {
  157.   //const short prev_res_file = CurrResFile();
  158.   const short clut_file_refno = FSpOpenResFile(&file_spec,fsRdPerm);
  159.   
  160.   if( clut_file_refno == -1 )
  161.   {
  162.     const OSErr resource_fork_open_err = ResError();
  163.     if( resource_fork_open_err == resFNotFound )
  164.       return false;                    // file_spec does not have a resource fork        
  165.     do_well( resource_fork_open_err );    // crash on any affirmative error
  166.     return false;
  167.   }
  168.  
  169.   Handle clut_handle = Get1IndResource('clut', 1);
  170.   bool result = true;
  171.   if( clut_handle == nil )
  172.   {
  173.      const OSErr resource_peek_err = ResError();
  174.      if( resource_peek_err != resNotFound )
  175.        do_well( resource_peek_err );
  176.      result = false;                  // file_spec does not have clut resources
  177.   }
  178.   else
  179.   {
  180.     short res_id;
  181.     ResType res_type;
  182.     Str255 name;
  183.     GetResInfo(clut_handle, &res_id, &res_type, name);
  184.     CLUTable loaded_clut(res_id);
  185.     if( result = (clut.q_size() == loaded_clut.q_size()) )
  186.       clut.deep_copy_from(loaded_clut);
  187.   }
  188.   //UseResFile(prev_res_file);
  189.   CloseResFile(clut_file_refno); // this restores the previous res file and frees up 
  190.                                    // clut_handle if any
  191.   return result;
  192. }
  193.  
  194.                 // Load the PGM file (with a possible clut) into the elevation map
  195. void ElevationMap::load(const FSSpec& file_spec)
  196. {
  197.   message("Loading a new elavation map from %#s",file_spec.name);
  198.   FileContainer old_dir;
  199.   FileContainer(file_spec).make_current();
  200. //  IMAGE new_image(PtoCstr((Str63)file_spec.name));
  201.   IMAGE new_image(PtoCstr( FSSpec(file_spec).name ));
  202.   old_dir.make_current();
  203.   assign_wrap(*this,new_image);
  204.   
  205.   if( load_new_clut(clu_table,file_spec) )
  206.     message("a custom color map has been loaded\n");
  207.   
  208.   
  209. //  write_pgm("coerced.pgm");
  210. #if 0 
  211.   PICTure picture_ext(file_spec);
  212.   clut = picture_ext.q_clut();
  213.   picture_ext.q_clut().dump("Picture's");
  214.   PICTView picture = picture_ext;
  215.   CLUTable(picture.q_clut()).dump("Offscreen's Picture");
  216. #endif
  217. }