file: hierarchy.c
/* hierarchy.c - function to generate dimensional stacking display for XmdvTool */ /* Copyright 1994, Matthew O. Ward * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without fee * is granted provided that the above copyright notice appears in all copies. * It is provided "as is" without express or implied warranty. */ /* This function displays the data using dimensional stacking. Each dimension is discretized into a user-specified number of uniformly sized buckets. This is called the cardinality of the dimension. Two dimensions are selected as the "slowest" or "outermost" dimensions, and a virtual display is created based on the cardinality of the 2 dimensions. At each virtual pixel, we create an image based on 2 more dimensions. This recursive embedding continues until all dimensions are accounted for. This function uses a fixed size rectangle to display each data point. This forces scrollbars to pop up in the display area if the resulting size is too large. Data points may map to the same location in the descrete space. Data is displayed using this projection algorithm, and grid marks of varying intensity show the transitions between the buckets. Darker grids indicate transitions in slower dimensions. */ #include "XmdvTool.h" #include "brush.h" #include "brush_oper.h" #include "multi_select.h" #include "util.h" #define SMALLEST 4 extern Widget appShell; extern GC gc; extern unsigned long color_cells[NUM_COLOR_CELLS]; extern XColor outline_col; extern int dims, data_size; extern double dim_min[MAXDIM], dim_max[MAXDIM]; extern int cardinality[MAXDIM]; /* the brushes */ extern Brush brushes[MAXBRUSH]; /* is this a color display? */ extern int COLOR; extern int X_SIZE, Y_SIZE; /* local functions */ int get_contrib(int, double); int get_pixel_size(int*); int within(int, int, int); void rec_fill(Widget, int, int, int, int, int [], int); void ds_outline(Widget, int []); char message[30]; Arg wargs[10]; int blocksize, x_size, y_size; void do_hier(Widget w) { int i, j, endx, endy, tmax; int contrib[MAXDIM], size[MAXDIM]; double data[MAXDIM]; unsigned long pixel; /* the brush color pixel value */ Widget tw; /* compute the size of the blocks needed for the slowest dimension */ blocksize = get_pixel_size(size); x_size = size[0]*cardinality[0]; y_size = size[1]*cardinality[1]; tmax = MAX(x_size, y_size); if(tmax > X_SIZE) { Y_SIZE = X_SIZE = tmax; tw = WcFullNameToWidget(appShell,"*canvas"); /* set canvas size to accomodate this */ XtSetArg(wargs[0],XtNwidth, X_SIZE); XtSetArg(wargs[1],XtNheight, Y_SIZE); XtSetValues(tw,wargs,2); } /* display the brush coverage if desired */ if(COLOR == 1) ds_outline(w, size); /* draw up to 3 levels of grid marks, based on the number of dimensions */ if(dims >= 6) { XSetForeground(XtDisplay(w),gc, color_cells[CCELL_GRID3]); for(i = 0;i < x_size; i = i + size[4]) XDrawLine(XtDisplay(w), XtWindow(w), gc, i, 0, i, y_size); for(i = 0;i < y_size; i = i + size[5]) XDrawLine(XtDisplay(w), XtWindow(w), gc, 0, i, x_size, i); } if(dims >= 4) { XSetForeground(XtDisplay(w),gc, color_cells[CCELL_GRID2]); for(i = 0;i < x_size; i = i + size[2]) XDrawLine(XtDisplay(w), XtWindow(w), gc, i, 0, i, y_size); for(i = 0;i < y_size; i = i + size[3]) XDrawLine(XtDisplay(w), XtWindow(w), gc, 0, i, x_size, i); } XSetForeground(XtDisplay(w),gc, color_cells[CCELL_GRID1]); for(i = 0;i < x_size; i = i + size[0]) XDrawLine(XtDisplay(w), XtWindow(w), gc, i, 0, i, y_size); for(i = 0;i < y_size; i = i + size[1]) XDrawLine(XtDisplay(w), XtWindow(w), gc, 0, i, x_size, i); /* now do the same thing with the data points being touched by the brush */ if(COLOR == 1) { /* first draw all points in the data color */ for(i=0; i<data_size; i++) { get_data(data, i); pixel = ColorFromOperations(data); /* if this point is masked, skip it */ if(pixel == COLOR_NONE) continue; /* first time through only paint regular data */ if(pixel != color_cells[CCELL_DATA]) continue; XSetForeground(XtDisplay(w),gc, pixel); for(j=0; j<dims; j++) contrib[j] = get_contrib(j, data[j]); endx = 0; endy = 0; for(j=0; j<dims; j++) { endx += contrib[j] * size[j]; j++; if(j < dims) endy += contrib[j] * size[j]; } XFillRectangle(XtDisplay(w), XtWindow(w), gc, endx, y_size - endy - blocksize, blocksize, blocksize); } /* now paint all points covered by an operation */ for(i=0; i<data_size; i++) { get_data(data, i); pixel = ColorFromOperations(data); /* if this point is masked, skip it */ if(pixel == COLOR_NONE) continue; /* second time through don't paint regular data */ if(pixel == color_cells[CCELL_DATA]) continue; XSetForeground(XtDisplay(w),gc, pixel); for(j=0; j<dims; j++) contrib[j] = get_contrib(j, data[j]); endx = 0; endy = 0; for(j=0; j<dims; j++) { endx += contrib[j] * size[j]; j++; if(j < dims) endy += contrib[j] * size[j]; } XFillRectangle(XtDisplay(w), XtWindow(w), gc, endx, y_size - endy - blocksize, blocksize, blocksize); } } else { XSetForeground(XtDisplay(w),gc, color_cells[CCELL_TEXT]); /* for each data point, compute where it should go and draw a rect */ for(i=0; i<data_size; i++) { get_data(data, i); /* each dimension will contribute to the position calculation */ for(j=0; j<dims; j++) contrib[j] = get_contrib(j, data[j]); endx = 0; endy = 0; for(j=0; j<dims; j++) { endx += contrib[j] * size[j]; j++; if(j < dims) endy += contrib[j] * size[j]; } XFillRectangle(XtDisplay(w), XtWindow(w), gc, endx, y_size - endy - blocksize, blocksize, blocksize); } } } /* at each level, the number of pixels between successive values for the dimension involved varies. It depends on the minimum block size and the cardinalities of the dimensions embedded within it. This in turn depends on the orientation and speed of each dimension. */ int get_pixel_size(int size[]) { int odd=1, even=1, smallest, i, j; /* compute product of cardinalities in each orientation */ for(i = 0;i < dims;i=i+2) odd = odd*cardinality[i]; for(i = 1;i < dims;i=i+2) even = even*cardinality[i]; /* find which orientation will cause smallest block (highest product) */ smallest = (odd > even)? odd:even; smallest = X_SIZE/smallest; /* if too small, set to SMALLEST, which will force scroll bars */ if(smallest < SMALLEST) smallest = SMALLEST; /* now compute size of a virtual pixel for each dimension */ for(i = 0;i < dims;i++) { size[i] = smallest; for(j = i+2; j < dims; j=j+2) size[i] = size[i] * cardinality[j]; } return(smallest); } /* compute the descretized value from an original data value for a particular dimension. Display will be messed up if it is out of bounds. You then should fix the range for that dimension in your data file. */ int get_contrib(int ind, double data) { int res; res = ((data - dim_min[ind]) / (dim_max[ind] - dim_min[ind])) * (double)(cardinality[ind]); if(res >= cardinality[ind] || res < 0) printf("Data point out of bounds: dimension %d: [%f, %f]: %f\n", ind, dim_min[ind], dim_max[ind], data); return(res); } /* display brush coverage by descretizing brush in each dimension and check where in range of that dimension it overlaps. Roundoff error in the descretization process can reduce the visual effect of brush coverage. */ void ds_outline(Widget w, int size[]) { int i, j; int brush; /* the current brush number */ for(brush=0; brush<MAXBRUSH; brush++) { /* if this brush is not being displayed, skip it */ if(brushes[brush].display == FALSE) continue; /* set the appropriate brush color */ XSetForeground(XtDisplay(w), gc, color_cells[BRUSH_COLOR(brush)]); /* for the outermost dimensions... */ for(i=0; i<cardinality[0]; i++) for(j=0; j<cardinality[1]; j++) { /* if the brush intersects those dimensions recursively */ /* call to fill */ if((within(i, 0, brush) == 1) && (within(j, 1, brush) == 1)) rec_fill(w, i * size[0], j * size[1], 2, 3, size, brush); } } } /* recursive check for brush coverage, 2 dimensions at a time */ void rec_fill(Widget w, int basex, int basey, int nextx, int nexty, int size[], int brush) { int i, j; /* if we had an even number of dimensions and are at the bottom of the recursion, draw a rectangle in that data spot, as it is covered. */ if(nextx == dims) { XFillRectangle(XtDisplay(w), XtWindow(w), gc, basex, y_size - basey - blocksize, blocksize, blocksize); return; } /* if we had an odd number of dimensions and are at the bottom of the recursion, check each descrete value for brush overlap and draw rectangles over covered spots. */ if(nexty == dims) { for(i=0; i<cardinality[nextx]; i++) if(within(i, nextx, brush) == 1) XFillRectangle(XtDisplay(w), XtWindow(w), gc, basex + i * size[nextx], y_size - basey - blocksize, blocksize, blocksize); return; } /* continue recursion for each bucket of the current 2 dimensions */ for(i=0; i<cardinality[nextx]; i++) for(j=0; j<cardinality[nexty]; j++) if((within(i, nextx, brush) == 1) && (within(j, nexty, brush) == 1)) rec_fill(w, basex + i * size[nextx], basey + j * size[nexty], nextx+2, nexty+2, size, brush); return; } /* determine if this value (val) for this dimension (dim) is covered by the brush. */ int within(int val, int dim, int brush) { double b_size, b_pos, fval, start, end; fval = val; b_pos = (double)(cardinality[dim]) * (brushes[brush].pos[dim]-dim_min[dim]) / (dim_max[dim] - dim_min[dim]); b_size = (double)(cardinality[dim]) * brushes[brush].size[dim] / (dim_max[dim] - dim_min[dim]); b_size = b_size / 2.0; start = b_pos - b_size; end = b_pos + b_size; if(end > fval && end <= (fval+1.0)) return(1); if(start >= fval && start < (fval+1.0)) return(1); if(start < fval && end > (fval+1.0)) return(1); return(0); } /* * RedrawSingleHier() - Redraw a single point in the dimensional * stacking display. Useful for things like * multi-select. */ void RedrawSingleHier(Widget w, int point) { int j, endx, endy; int contrib[MAXDIM], size[MAXDIM]; double data[MAXDIM]; Widget tw; /* compute the size of the blocks needed for the slowest dimension */ blocksize = get_pixel_size(size); tw = WcFullNameToWidget(appShell,"*canvas"); /* set canvas size to accomodate this */ XtSetArg(wargs[0],XtNwidth,(size[0] * cardinality[0])); XtSetArg(wargs[1],XtNheight,(size[1] * cardinality[1])); XtSetValues(tw,wargs,2); x_size = size[0]*cardinality[0]; y_size = size[1]*cardinality[1]; /* display the brush coverage if desired */ if(COLOR == 1) { XSetForeground(XtDisplay(w),gc, outline_col.pixel); ds_outline(w, size); } XSetForeground(XtDisplay(w),gc, color_cells[CCELL_TEXT]); /* reset color if brushing is on */ if(COLOR == 1) XSetForeground(XtDisplay(w),gc, color_cells[CCELL_DATA]); /* set the color */ if(COLOR == 1) { if(MultiSelectCovered(point)) XSetForeground(XtDisplay(w),gc, color_cells[CCELL_PAINT]); else if(covered(data) == 1) XSetForeground(XtDisplay(w),gc, color_cells[CCELL_HIGHLIGHT]); else XSetForeground(XtDisplay(w),gc, color_cells[CCELL_DATA]); } else XSetForeground(XtDisplay(w),gc, color_cells[CCELL_TEXT]); /* compute where it should go and draw a rectangle */ get_data(data, point); /* each dimension will contribute to the position calculation */ for(j = 0;j < dims;j++) contrib[j] = get_contrib(j, data[j]); endx = 0; endy = 0; for(j = 0;j < dims;j++) { endx += contrib[j] * size[j]; j++; if(j < dims) endy += contrib[j] * size[j]; } XFillRectangle(XtDisplay(w), XtWindow(w), gc, endx, y_size - endy - blocksize, blocksize, blocksize); } /* * HierDrawAverage() - Draw an average value on the dimensional stacking * display. * * PARAMETERS * data The average point to draw * * RETURNS * void */ void HierDrawAverage(double data[MAXDIM]) { Widget canvas_w; /* the canvas */ int j, endx, endy; int contrib[MAXDIM], size[MAXDIM]; /* compute the size of the blocks needed for the slowest dimension */ blocksize = get_pixel_size(size); /* get the canvas widget */ if(!(canvas_w = WcFullNameToWidget(appShell,"*canvas"))) { fprintf(stderr, "Error getting *canvas in HierDrawAverage()\n"); return; } x_size = size[0]*cardinality[0]; y_size = size[1]*cardinality[1]; /* each dimension will contribute to the position calculation */ for(j = 0;j < dims;j++) contrib[j] = get_contrib(j, data[j]); endx = 0; endy = 0; for(j = 0;j < dims;j++) { endx += contrib[j] * size[j]; j++; if(j < dims) endy += contrib[j] * size[j]; } XFillRectangle(XtDisplay(canvas_w), XtWindow(canvas_w), gc, endx, y_size - endy - blocksize, blocksize, blocksize); }
Back to Source File Index
C++ to HTML Conversion by ctoohtml