void ConditionalScatterPlotCanvas::ResizeSelectableShps(int virtual_scrn_w,
														int virtual_scrn_h)
{
	// NOTE: we do not support both fixed_aspect_ratio_mode
	//    and fit_to_window_mode being false currently.
	LOG_MSG("Entering ConditionalScatterPlotCanvas::ResizeSelectableShps");
	int vs_w=virtual_scrn_w, vs_h=virtual_scrn_h;
	if (vs_w <= 0 && vs_h <= 0) GetVirtualSize(&vs_w, &vs_h);
	
	double image_width, image_height;
	bool ftwm = GetFitToWindowMode();
	
	// last_scale_trans is only used in calls made to ApplyLastResizeToShp
	// which are made in ScaterNewPlotView
	GdaScaleTrans **st;
	st = new GdaScaleTrans*[vert_num_cats];
	for (int i=0; i<vert_num_cats; i++) {
		st[i] = new GdaScaleTrans[horiz_num_cats];
	}
	
	// Total width height:  vs_w   vs_h
	// Working area margins: virtual_screen_marg_top,
	//  virtual_screen_marg_bottom,
	//  virtual_screen_marg_left,
	//  virtual_screen_marg_right
	// We need to increase these as needed for each tile area
	
	double scn_w = vs_w;
	double scn_h = vs_h;
	
	double min_pad = 10;
	if (display_axes_scale_values) {
		min_pad += 20;
	}
	if (!display_axes_scale_values && display_slope_values) {
		min_pad += 4;
	}
	
	// pixels between columns/rows
	double fac = 0.01;
	//if (vert_num_cats >= 4 || horiz_num_cats >=4) fac = 0.05;
	double pad_w = scn_w * fac;
	double pad_h = scn_h * fac;
	if (pad_w < 1) pad_w = 0;
	if (pad_h < 1) pad_h = 0;
	double pad_bump = GenUtils::min<double>(pad_w, pad_h);
	double pad = min_pad + pad_bump;
	
	double marg_top = virtual_screen_marg_top;
	double marg_bottom = virtual_screen_marg_bottom;
	double marg_left = virtual_screen_marg_left;
	double marg_right = virtual_screen_marg_right;
	
	double d_rows = vert_num_cats;
	double d_cols = horiz_num_cats;
	
	double tot_width = scn_w - ((d_cols-1)*pad + marg_left + marg_right);
	double tot_height = scn_h - ((d_rows-1)*pad + marg_top + marg_bottom);
	double del_width = tot_width / d_cols;
	double del_height = tot_height / d_rows;
	
	bin_extents.resize(boost::extents[vert_num_cats][horiz_num_cats]);
	for (int row=0; row<vert_num_cats; row++) {
		double drow = row;
		for (int col=0; col<horiz_num_cats; col++) {
			double dcol = col;
			double ml = marg_left + col*(pad+del_width);
			double mr = marg_right + ((d_cols-1)-col)*(pad+del_width);
			double mt = marg_top + row*(pad+del_height);
			double mb = marg_bottom + ((d_rows-1)-row)*(pad+del_height);
			
			double s_x, s_y, t_x, t_y;
			GdaScaleTrans::calcAffineParams(shps_orig_xmin, shps_orig_ymin,
										   shps_orig_xmax, shps_orig_ymax,
										   mt, mb, ml, mr,
										   vs_w, vs_h, fixed_aspect_ratio_mode,
										   ftwm,
										   &s_x, &s_y, &t_x, &t_y,
										   ftwm ? 0 : current_shps_width,
										   ftwm ? 0 : current_shps_height,
										   &image_width, &image_height);
			st[(vert_num_cats-1)-row][col].scale_x = s_x;
			st[(vert_num_cats-1)-row][col].scale_y = s_y;
			st[(vert_num_cats-1)-row][col].trans_x = t_x;
			st[(vert_num_cats-1)-row][col].trans_y = t_y;
			st[(vert_num_cats-1)-row][col].max_scale =
			GenUtils::max<double>(s_x, s_y);
			
			wxRealPoint ll(shps_orig_xmin, shps_orig_ymin);
			wxRealPoint ur(shps_orig_xmax, shps_orig_ymax);
			bin_extents[(vert_num_cats-1)-row][col] = GdaRectangle(ll, ur);
			bin_extents[(vert_num_cats-1)-row][col].applyScaleTrans(
											st[(vert_num_cats-1)-row][col]);
		}
	}
	
	BOOST_FOREACH( GdaShape* shp , foreground_shps ) { delete shp; }
	foreground_shps.clear();
	for (int row=0; row<vert_num_cats; row++) {
		for (int col=0; col<horiz_num_cats; col++) {
			GdaPolyLine* p = new GdaPolyLine(reg_line[row][col]);
			p->applyScaleTrans(st[row][col]);
			foreground_shps.push_back(p);
			
			GdaAxis* x_ax = new GdaAxis("", axis_scale_x,
									  wxRealPoint(0,0), wxRealPoint(100, 0));
			if (!display_axes_scale_values) x_ax->hideScaleValues(true);
			x_ax->setPen(*GdaConst::scatterplot_scale_pen);
			x_ax->applyScaleTrans(st[row][col]);
			foreground_shps.push_back(x_ax);
			
			GdaAxis* y_ax = new GdaAxis("", axis_scale_y,
									  wxRealPoint(0,0), wxRealPoint(0, 100));
			if (!display_axes_scale_values) y_ax->hideScaleValues(true);
			y_ax->setPen(*GdaConst::scatterplot_scale_pen);
			y_ax->applyScaleTrans(st[row][col]);
			foreground_shps.push_back(y_ax);
			
			if (display_slope_values && regression[row][col].valid) {
				wxString s;
				s << GenUtils::DblToStr(regression[row][col].beta);
				if (regression[row][col].p_value_beta <= 0.01 &&
					stats_x[row][col].sample_size >= 3) {
					s << "**";
				} else if (regression[row][col].p_value_beta <= 0.05 &&
						   stats_x[row][col].sample_size >= 3) {
					s << "*";
				}
				GdaShapeText* t = new GdaShapeText(s, *GdaConst::small_font,
									   wxRealPoint(50, 100), 0,
									   GdaShapeText::h_center, GdaShapeText::v_center);
				t->setPen(*GdaConst::scatterplot_scale_pen);
				t->applyScaleTrans(st[row][col]);
				foreground_shps.push_back(t);
			}
		}
	}
	
	int row_c;
	int col_c;
	for (int i=0; i<num_obs; i++) {
		row_c = vert_cat_data.categories[var_info[VERT_VAR].time].id_to_cat[i];
		col_c = horiz_cat_data.categories[var_info[HOR_VAR].time].id_to_cat[i];
		selectable_shps[i]->applyScaleTrans(st[row_c][col_c]);
	}
	
	BOOST_FOREACH( GdaShape* shp, background_shps ) { delete shp; }
	background_shps.clear();
	
	double bg_xmin = marg_left;
	double bg_xmax = scn_w-marg_right;
	double bg_ymin = marg_bottom;
	double bg_ymax = scn_h-marg_top;
	
	std::vector<wxRealPoint> v_brk_ref(vert_num_cats-1);
	std::vector<wxRealPoint> h_brk_ref(horiz_num_cats-1);
	
	for (int row=0; row<vert_num_cats-1; row++) {
		double y = (bin_extents[row][0].lower_left.y +
					bin_extents[row+1][0].upper_right.y)/2.0;
		v_brk_ref[row].x = bg_xmin;
		v_brk_ref[row].y = scn_h-y;
	}
	
	for (int col=0; col<horiz_num_cats-1; col++) {
		double x = (bin_extents[0][col].upper_right.x +
					bin_extents[0][col+1].lower_left.x)/2.0;
		h_brk_ref[col].x = x;
		h_brk_ref[col].y = bg_ymin;
	}
	
	int label_offset = 12;
	if (display_axes_scale_values) label_offset = 2+25;
	GdaShape* s;
	int vt = var_info[VERT_VAR].time;
	for (int row=0; row<vert_num_cats-1; row++) {
		double b;
		if (cat_classif_def_vert.cat_classif_type != CatClassification::custom){
			if (!vert_cat_data.HasBreakVal(vt, row)) continue;
			b = vert_cat_data.GetBreakVal(vt, row);
		} else {
			b = cat_classif_def_vert.breaks[row];
		}
		wxString t(GenUtils::DblToStr(b));
		s = new GdaShapeText(t, *GdaConst::small_font, v_brk_ref[row], 90,
					   GdaShapeText::h_center, GdaShapeText::bottom, -label_offset, 0);
		background_shps.push_back(s);
	}
	
	wxString vert_label;
	if (GetCatType(VERT_VAR) != CatClassification::no_theme) {
		if (GetCatType(VERT_VAR) == CatClassification::custom) {
			vert_label << cat_classif_def_vert.title;
		} else {
			vert_label << CatClassification::CatClassifTypeToString(
														GetCatType(VERT_VAR));
		}
		vert_label << " vert cat var: ";
		vert_label << GetNameWithTime(VERT_VAR);
		vert_label << ",   ";
	}
	vert_label << "dep. var: " << GetNameWithTime(DEP_VAR);
	s = new GdaShapeText(vert_label, *GdaConst::small_font,
				   wxRealPoint(bg_xmin, bg_ymin+(bg_ymax-bg_ymin)/2.0), 90,
				   GdaShapeText::h_center, GdaShapeText::bottom, -(label_offset+18), 0);
	background_shps.push_back(s);
	
	int ht = var_info[HOR_VAR].time;
	for (int col=0; col<horiz_num_cats-1; col++) {
		double b;
		if (cat_classif_def_horiz.cat_classif_type!= CatClassification::custom){
			if (!horiz_cat_data.HasBreakVal(ht, col)) continue;
			b = horiz_cat_data.GetBreakVal(ht, col);
		} else {
			b = cat_classif_def_horiz.breaks[col];
		}
		wxString t(GenUtils::DblToStr(b));
		s = new GdaShapeText(t, *GdaConst::small_font, h_brk_ref[col], 0,
					   GdaShapeText::h_center, GdaShapeText::top, 0, label_offset);
		background_shps.push_back(s);
	}
	
	wxString horiz_label;
	if (GetCatType(HOR_VAR) != CatClassification::no_theme) {
		if (GetCatType(HOR_VAR) == CatClassification::custom) {
			horiz_label << cat_classif_def_horiz.title;
		} else {
			horiz_label << CatClassification::CatClassifTypeToString(
														GetCatType(HOR_VAR));
		}
		horiz_label << " horiz cat var: ";
		horiz_label << GetNameWithTime(HOR_VAR);
		horiz_label << ",   ";
	}
	horiz_label << "ind. var: " << GetNameWithTime(IND_VAR);
	s = new GdaShapeText(horiz_label, *GdaConst::small_font,
				   wxRealPoint(bg_xmin+(bg_xmax-bg_xmin)/2.0, bg_ymin), 0,
				   GdaShapeText::h_center, GdaShapeText::top, 0, (label_offset+18));
	background_shps.push_back(s);
	
	GdaScaleTrans::calcAffineParams(marg_left, marg_bottom,
								   scn_w-marg_right,
								   scn_h-marg_top,
								   marg_top, marg_bottom,
								   marg_left, marg_right,
								   vs_w, vs_h,
								   fixed_aspect_ratio_mode,
								   fit_to_window_mode,
								   &last_scale_trans.scale_x,
								   &last_scale_trans.scale_y,
								   &last_scale_trans.trans_x,
								   &last_scale_trans.trans_y,
								   0, 0, &image_width, &image_height);
	last_scale_trans.max_scale =
	GenUtils::max<double>(last_scale_trans.scale_x,
						  last_scale_trans.scale_y);
	BOOST_FOREACH( GdaShape* ms, background_shps ) {
		ms->applyScaleTrans(last_scale_trans);
	}
void ConditionalHistogramCanvas::ResizeSelectableShps(int virtual_scrn_w,
														int virtual_scrn_h)
{
	// NOTE: we do not support both fixed_aspect_ratio_mode
	//    and fit_to_window_mode being false currently.
	//LOG_MSG("Entering ConditionalHistogramCanvas::ResizeSelectableShps");
	int vs_w=virtual_scrn_w, vs_h=virtual_scrn_h;
	if (vs_w <= 0 && vs_h <= 0) GetVirtualSize(&vs_w, &vs_h);
	
	double image_width, image_height;
	bool ftwm = GetFitToWindowMode();
	
	// last_scale_trans is only used in calls made to ApplyLastResizeToShp
	// which are made in ScaterNewPlotView
	GdaScaleTrans **st;
	st = new GdaScaleTrans*[vert_num_cats];
	for (int i=0; i<vert_num_cats; i++) {
		st[i] = new GdaScaleTrans[horiz_num_cats];
	}
	
	// Total width height:  vs_w   vs_h
	// Working area margins: virtual_screen_marg_top,
	//  virtual_screen_marg_bottom,
	//  virtual_screen_marg_left,
	//  virtual_screen_marg_right
	// We need to increase these as needed for each tile area
	
	double scn_w = vs_w;
	double scn_h = vs_h;
	
	double min_pad = 10;
	if (show_axes) {
		min_pad += 38;
	}
	
	// pixels between columns/rows
	double fac = 0.01;
	//if (vert_num_cats >= 4 || horiz_num_cats >=4) fac = 0.05;
	double pad_w = scn_w * fac;
	double pad_h = scn_h * fac;
	if (pad_w < 1) pad_w = 0;
	if (pad_h < 1) pad_h = 0;
	double pad_bump = GenUtils::min<double>(pad_w, pad_h);
	double pad = min_pad + pad_bump;
	
	double marg_top = virtual_screen_marg_top;
	double marg_bottom = virtual_screen_marg_bottom;
	double marg_left = virtual_screen_marg_left;
	double marg_right = virtual_screen_marg_right;
	
	double d_rows = vert_num_cats;
	double d_cols = horiz_num_cats;
	
	double tot_width = scn_w - ((d_cols-1)*pad + marg_left + marg_right);
	double tot_height = scn_h - ((d_rows-1)*pad + marg_top + marg_bottom);
	double del_width = tot_width / d_cols;
	double del_height = tot_height / d_rows;
	
	bin_extents.resize(boost::extents[vert_num_cats][horiz_num_cats]);
	for (int row=0; row<vert_num_cats; row++) {
		double drow = row;
		for (int col=0; col<horiz_num_cats; col++) {
			double dcol = col;
			double ml = marg_left + col*(pad+del_width);
			double mr = marg_right + ((d_cols-1)-col)*(pad+del_width);
			double mt = marg_top + row*(pad+del_height);
			double mb = marg_bottom + ((d_rows-1)-row)*(pad+del_height);
			
			double s_x, s_y, t_x, t_y;
			GdaScaleTrans::calcAffineParams(shps_orig_xmin, shps_orig_ymin,
										   shps_orig_xmax, shps_orig_ymax,
										   mt, mb, ml, mr,
										   vs_w, vs_h, fixed_aspect_ratio_mode,
										   ftwm,
										   &s_x, &s_y, &t_x, &t_y,
										   ftwm ? 0 : current_shps_width,
										   ftwm ? 0 : current_shps_height,
										   &image_width, &image_height);
			st[(vert_num_cats-1)-row][col].scale_x = s_x;
			st[(vert_num_cats-1)-row][col].scale_y = s_y;
			st[(vert_num_cats-1)-row][col].trans_x = t_x;
			st[(vert_num_cats-1)-row][col].trans_y = t_y;
			st[(vert_num_cats-1)-row][col].max_scale =
			GenUtils::max<double>(s_x, s_y);
			
			wxRealPoint ll(shps_orig_xmin, shps_orig_ymin);
			wxRealPoint ur(shps_orig_xmax, shps_orig_ymax);
			bin_extents[(vert_num_cats-1)-row][col] = GdaRectangle(ll, ur);
			bin_extents[(vert_num_cats-1)-row][col].applyScaleTrans(
											st[(vert_num_cats-1)-row][col]);
		}
	}
	
	int i=0;
	for (int r=0; r<vert_num_cats; r++) {
		for (int c=0; c<horiz_num_cats; c++) {
			for (int ival=0; ival<cur_intervals; ival++) {
				selectable_shps[i]->applyScaleTrans(st[r][c]);
				i++;
			}
		}
	}
	
	BOOST_FOREACH( GdaShape* shp, background_shps ) { delete shp; }
	background_shps.clear();
	
	GdaShape* s;
	if (show_axes) {
		for (int r=0; r<vert_num_cats; r++) {
			for (int c=0; c<horiz_num_cats; c++) {
				s = new GdaAxis(*x_axis);
				s->applyScaleTrans(st[r][c]);
				background_shps.push_front(s);
				s = new GdaAxis(*y_axis);
				s->applyScaleTrans(st[r][c]);
				background_shps.push_front(s);
			}
		}
	}
	
	double bg_xmin = marg_left;
	double bg_xmax = scn_w-marg_right;
	double bg_ymin = marg_bottom;
	double bg_ymax = scn_h-marg_top;
	
	std::vector<wxRealPoint> v_brk_ref(vert_num_cats-1);
	std::vector<wxRealPoint> h_brk_ref(horiz_num_cats-1);
	
	for (int row=0; row<vert_num_cats-1; row++) {
		double y = (bin_extents[row][0].lower_left.y +
					bin_extents[row+1][0].upper_right.y)/2.0;
		v_brk_ref[row].x = bg_xmin;
		v_brk_ref[row].y = scn_h-y;
	}
	
	for (int col=0; col<horiz_num_cats-1; col++) {
		double x = (bin_extents[0][col].upper_right.x +
					bin_extents[0][col+1].lower_left.x)/2.0;
		h_brk_ref[col].x = x;
		h_brk_ref[col].y = bg_ymin;
	}
	
	int bg_shp_cnt = 0;
	int label_offset = 25;
	if (show_axes) label_offset += 25;
	int vt = var_info[VERT_VAR].time;
	for (int row=0; row<vert_num_cats-1; row++) {
		double b;
		if (cat_classif_def_vert.cat_classif_type != CatClassification::custom){
			if (!vert_cat_data.HasBreakVal(vt, row)) continue;
			b = vert_cat_data.GetBreakVal(vt, row);
		} else {
			b = cat_classif_def_vert.breaks[row];
		}
		wxString t(GenUtils::DblToStr(b));
		s = new GdaShapeText(t, *GdaConst::small_font, v_brk_ref[row], 90,
					   GdaShapeText::h_center, GdaShapeText::bottom, -label_offset, 0);
		background_shps.push_front(s);
		bg_shp_cnt++;
	}
	if (GetCatType(VERT_VAR) != CatClassification::no_theme) {
		wxString vert_label;
		if (GetCatType(VERT_VAR) == CatClassification::custom) {
			vert_label << cat_classif_def_vert.title;
		} else {
			vert_label << CatClassification::CatClassifTypeToString(
														GetCatType(VERT_VAR));
		}
		vert_label << " vert cat var: ";
		vert_label << GetNameWithTime(VERT_VAR);
		s = new GdaShapeText(vert_label, *GdaConst::small_font,
					   wxRealPoint(bg_xmin, bg_ymin+(bg_ymax-bg_ymin)/2.0), 90,
					   GdaShapeText::h_center, GdaShapeText::bottom, -(label_offset+15), 0);
		background_shps.push_front(s);
		bg_shp_cnt++;
	}
	
	int ht = var_info[HOR_VAR].time;
	for (int col=0; col<horiz_num_cats-1; col++) {
		double b;
		if (cat_classif_def_horiz.cat_classif_type!= CatClassification::custom){
			if (!horiz_cat_data.HasBreakVal(ht, col)) continue;
			b = horiz_cat_data.GetBreakVal(ht, col);
		} else {
			b = cat_classif_def_horiz.breaks[col];
		}
		wxString t(GenUtils::DblToStr(b));
		s = new GdaShapeText(t, *GdaConst::small_font, h_brk_ref[col], 0,
					   GdaShapeText::h_center, GdaShapeText::top, 0, label_offset);
		background_shps.push_front(s);
		bg_shp_cnt++;
	}
	if (GetCatType(HOR_VAR) != CatClassification::no_theme) {
		wxString horiz_label;
		if (GetCatType(HOR_VAR) == CatClassification::custom) {
			horiz_label << cat_classif_def_horiz.title;
		} else {
			horiz_label << CatClassification::CatClassifTypeToString(
														GetCatType(HOR_VAR));
		}
		horiz_label << " horiz cat var: ";
		horiz_label << GetNameWithTime(HOR_VAR);
		s = new GdaShapeText(horiz_label, *GdaConst::small_font,
					   wxRealPoint(bg_xmin+(bg_xmax-bg_xmin)/2.0, bg_ymin), 0,
					   GdaShapeText::h_center, GdaShapeText::top, 0, (label_offset+15));
		background_shps.push_front(s);
		bg_shp_cnt++;
	}
	
	GdaScaleTrans::calcAffineParams(marg_left, marg_bottom,
								   scn_w-marg_right,
								   scn_h-marg_top,
								   marg_top, marg_bottom,
								   marg_left, marg_right,
								   vs_w, vs_h,
								   fixed_aspect_ratio_mode,
								   fit_to_window_mode,
								   &last_scale_trans.scale_x,
								   &last_scale_trans.scale_y,
								   &last_scale_trans.trans_x,
								   &last_scale_trans.trans_y,
								   0, 0, &image_width, &image_height);
	last_scale_trans.max_scale =
	GenUtils::max<double>(last_scale_trans.scale_x,
						  last_scale_trans.scale_y);
	
	int bg_shp_i = 0;
	for (std::list<GdaShape*>::iterator it=background_shps.begin();
		 bg_shp_i < bg_shp_cnt && it != background_shps.end();
		 bg_shp_i++, it++) {
		(*it)->applyScaleTrans(last_scale_trans);
	}
	
	layer0_valid = false;
	Refresh();
	
	for (int i=0; i<vert_num_cats; i++) delete [] st[i];
	delete [] st;
	
	//LOG_MSG("Exiting ConditionalHistogramCanvas::ResizeSelectableShps");
}