#pragma rtGlobals=3		// Use modern global access method and strict wave access.
#pragma version = 1.8
#pragma IgorVersion = 6.2
#pragma ModuleName = PearlAnglescanPanel
#include "pearl-anglescan-process"
#include "pearl-pmsco-import"

// copyright (c) 2018-20 Paul Scherrer Institut
//
// Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//   http:///www.apache.org/licenses/LICENSE-2.0
//
// Please acknowledge the use of this code.

/// @file
/// @brief interactive processing of angle scanned XPD data.
/// @ingroup ArpesPackage
///
/// steps to process a hemispherical scan into a diffractogram:
/// 1. load the scan file in data reduction mode.
/// 2. open the angle scan panel by calling asp_show_panel().
/// 3. select the dataset.
/// 4. set the processing parameters.
/// 5. start the processing and display the result.
/// 6. refine parameters and repeat steps 4-5.
/// 7. export the result in the desired form.
///
/// @author matthias muntwiler, matthias.muntwiler@psi.ch
///
/// @copyright 2018-20 Paul Scherrer Institut @n
/// Licensed under the Apache License, Version 2.0 (the "License"); @n
///   you may not use this file except in compliance with the License. @n
///   You may obtain a copy of the License at
///   http://www.apache.org/licenses/LICENSE-2.0
///

/// @namespace PearlAnglescanPanel
/// @brief interactive processing of angle scanned XPD data.
///
/// PearlAnglescanPanel is declared in @ref pearl-anglescan-panel.ipf.

/// package name is used as data folder name
static strconstant package_name = "pearl_anglescan_panel"
/// data folder path
static strconstant package_path = "root:packages:pearl_anglescan_panel:"

/// initialize package data once when the procedure is first loaded
static function AfterCompiledHook()

	dfref savedf = GetDataFolderDFR()
	variable do_init = 1
	if (DataFolderExists(package_path))
		setdatafolder $(package_path)
		nvar /z init_done
		if (nvar_exists(init_done))
			if (init_done)
				do_init = 0
			endif
		endif
	endif

	if (do_init)
		init_package()
		load_prefs()
		setdatafolder $(package_path)
		variable /g init_done = 1
	endif

	setdatafolder savedf
	return 0
end

static function init_package()
	dfref savedf = getdatafolderdfr()
	setdatafolder root:
	newdatafolder /o/s packages
	newdatafolder /o/s $package_name

	// configuration (persistent)
	string /g graphname = "graph_anglescan_panel"
	string /g prefs_objects = "prefs_objects;theta_offset;tilt_offset;phi_offset;alpha_offset;crop_enable;crop_alpha;crop_theta;"
	prefs_objects += "norm_alpha_enable;norm_alpha_mode;norm_alpha_smoothing;norm_phi_enable;norm_phi_mode;norm_phi_thetarange;"
	prefs_objects += "norm_theta_enable;norm_theta_mode;norm_theta_domain;norm_theta_smoothing;norm_thetaphi_enable;norm_thetaphi_mode;norm_thetaphi_smoothing;"
	prefs_objects += "output_folding;output_horizon;graph_mode;graph_projection;graph_colortable;graph_contrast;"
	
	// recently used (persistent)
	variable /g theta_offset = 0
	variable /g tilt_offset = 0
	variable /g phi_offset = 0
	variable /g alpha_offset = 0
	variable /g crop_enable = 0
	variable /g crop_alpha = 25
	variable /g crop_theta = 88
	string /g crop_rows = ""
	variable /g norm_alpha_enable = 0
	variable /g norm_alpha_mode = 4
	variable /g norm_alpha_smoothing = 0.25
	variable /g norm_theta_enable = 0
	variable /g norm_theta_mode = 4
	variable /g norm_theta_domain = 0
	variable /g norm_theta_smoothing = 0.25
	variable /g norm_thetaphi_enable = 0
	variable /g norm_thetaphi_mode = 4
	variable /g norm_thetaphi_smoothing = 0.25
	variable /g norm_phi_enable = 0
	variable /g norm_phi_mode = 4
	variable /g norm_phi_thetarange = 20
	string /g output_name = "holo1"
	variable /g output_folding = 1
	variable /g output_horizon = 88
	variable /g graph_mode = 1
	variable /g graph_projection = kProjStereo
	string /g graph_colortable = "grays"
	variable /g graph_contrast = 2
	
	// recently used (volatile)
	string /g source_path = ""
	string /g export_folderpath = "root:"
	variable /g export_format = 1

	// administrative data (volatile)
	string /g panel_name = ""
	string /g preview_graphname = ""
	string /g dist_x_graphname = ""
	string /g dist_y_graphname = ""
	string /g output_graphname = ""
	
	// data (volatile)
	make /n=(10,10) /o raw_data, process_data
	make /o dist_x, dist_x_smoo
	make /o dist_y, dist_y_smoo
	
	setdatafolder savedf
end

/// save persistent package data to the preferences file.
///
/// this function is called when the user clicks the corresponding button.
///
static function save_prefs()
	dfref saveDF = GetDataFolderDFR()
	dfref df = $(package_path)
	svar /sdfr=df prefs_objects

	string fullPath = SpecialDirPath("Packages", 0, 0, 0)
	fullPath += package_name
	NewPath/O/C/Q tempPackagePrefsPath, fullPath
	fullPath += ":preferences.pxp"

	SetDataFolder root:packages
	SetDataFolder $package_name
	SaveData /O /Q /J=prefs_objects fullPath

	KillPath/Z tempPackagePrefsPath
	
 	SetDataFolder saveDF
end

/// load persistent package data from the preferences file.
///
/// the preferences file is an Igor packed experiment file in a special preferences folder.
///
/// this function is called automatically when the procedure is first compiled,
/// or whenever the user clicks the corresponding button.
///
static function load_prefs()
	dfref saveDF = GetDataFolderDFR()

	variable result = -1
	setdatafolder root:
	NewDataFolder /O/S packages
	NewDataFolder /O/S $package_name
	string fullPath = SpecialDirPath("Packages", 0, 0, 0)
	fullPath += package_name

	GetFileFolderInfo /Q /Z fullPath
	if (V_Flag == 0) // Disk directory exists?
		fullPath += ":preferences.pxp"
		GetFileFolderInfo /Q /Z fullPath
		if (V_Flag == 0) // Preference file exist?
			LoadData /O /R /Q fullPath
			result = 0
		endif
	endif
	
 	SetDataFolder saveDF
	return result
end

/// import raw data
///
/// the raw data are copied into the package data folder,
/// and the process waves are initialized.
///
/// @param	raw_data		2D intensity distribution.
///							the x dimension is the detection angle.
///							the y dimension is an arbitrary sequence of manipulator scan positions.
///							ManipulatorTheta, ManipulatorPilt and ManipulatorPhi waves
///							that indicate the manipulator angles for each y position
///							must be present in the :attr sub-folder next to the raw data wave.
///
function asp_import_raw(raw_data)
	wave raw_data

	dfref saveDF = GetDataFolderDFR()
	dfref datadf = GetWavesDataFolderDFR(raw_data)
	dfref attrdf = datadf:attr
	if (!DataFolderRefStatus(attrdf))
		setdatafolder datadf
		setdatafolder ::
		dfref parentdf = GetDataFolderDFR()
		dfref attrdf = parentdf:attr
	endif
	setdatafolder $(package_path)
	
	// todo : check dimensions and scales
	
	svar source_path
	source_path = GetWavesDataFolder(raw_data, 2)
	
	duplicate /o raw_data, raw, process_data
	
	wave /sdfr=attrdf /z ManipulatorTheta
	wave /sdfr=attrdf /z ManipulatorTilt
	wave /sdfr=attrdf /z ManipulatorPhi
	if (WaveExists(ManipulatorTheta) && WaveExists(ManipulatorTilt) && WaveExists(ManipulatorPhi))
		duplicate /o attrdf:ManipulatorTheta, raw_theta, process_theta
		duplicate /o attrdf:ManipulatorTilt, raw_tilt, process_tilt
		duplicate /o attrdf:ManipulatorPhi, raw_phi, process_phi
	else
		DoAlert 0, "Can't find manipulator angle waves.\rCheck that the attr folder exists, or provide values in the following table."
		make /n=(dimsize(raw_data, 1)) /o raw_theta, raw_tilt, raw_phi
		make /n=(dimsize(raw_data, 1)) /o process_theta, process_tilt, process_phi
		edit /k=1 raw_theta, raw_tilt, raw_phi
	endif
	
	make /o /n=(dimsize(raw_data, 0)) dist_x, dist_x_smoo
	make /o /n=(dimsize(raw_data, 1)) dist_y, dist_y_smoo
	
 	SetDataFolder saveDF
end

/// display a graph window of the processed data.
///
/// if the window exists, it is updated and brought to the front of the window stack.
///
function asp_display_previews()
	dfref df = $(package_path)
	wave /sdfr=df process_data
	svar /sdfr=df preview_graphname

	if (strlen(preview_graphname) && (wintype(preview_graphname) == 1))
		ad_update_profiles(process_data)
		DoWindow /F $preview_graphname
	else
		preview_graphname = ad_display_profiles(process_data)
	endif
	
	nvar /sdfr=df graph_contrast
	svar /sdfr=df graph_colortable
	set_contrast(graph_contrast, graph_contrast, graphname=preview_graphname, colortable=graph_colortable)
end

/// display a graph window of the distribution checks.
///
/// if the window exists, it is updated and brought to the front of the window stack.
///
/// @param	xdist			1: display the x-distribution; 0: don't.
/// @param	ydist			1: display the y-distribution; 0: don't.
///
function asp_display_dist_check(xdist, ydist)
	variable xdist, ydist
	
	dfref df = $(package_path)
	wave /sdfr=df dist_x
	wave /sdfr=df dist_y
	wave /sdfr=df dist_x_smoo
	wave /sdfr=df dist_y_smoo
	svar /sdfr=df dist_x_graphname
	svar /sdfr=df dist_y_graphname

	if (xdist)
		if (strlen(dist_x_graphname) && (wintype(dist_x_graphname) == 1))
			DoWindow /F $dist_x_graphname
		else
			display /k=1 /n=graph_asp_dist_x dist_x, dist_x_smoo
			dist_x_graphname = s_name
			ModifyGraph /w=$dist_x_graphname mode(dist_x)=2
			ModifyGraph /w=$dist_x_graphname lsize(dist_x)=2
			ModifyGraph /w=$dist_x_graphname rgb(dist_x)=(0,0,0)
		endif
	endif

	if (ydist)
		if (strlen(dist_y_graphname) && (wintype(dist_y_graphname) == 1))
			DoWindow /F $dist_y_graphname
		else
			display /k=1 /n=graph_asp_dist_y dist_y, dist_y_smoo
			dist_y_graphname = s_name
			ModifyGraph /w=$dist_y_graphname mode(dist_y)=2
			ModifyGraph /w=$dist_y_graphname lsize(dist_y)=2
			ModifyGraph /w=$dist_y_graphname rgb(dist_y)=(0,0,0)
		endif
	endif
end

/// initialize the process data with a copy of the raw data.
///
/// applies the angle offsets.
///
/// @param[in]	check			select which output to generate (currently not used).
///				@arg 0 (default)	process data, don't calculate check waves.
///				@arg 1			process data and calculate check waves.
///				@arg 2			calculate check waves only, do not calculate process data.
///
static function do_init_process(check)
	variable check
	
	dfref df = $(package_path)
	wave /sdfr=df raw
	wave /sdfr=df raw_theta
	wave /sdfr=df raw_tilt
	wave /sdfr=df raw_phi
	nvar /sdfr=df theta_offset
	nvar /sdfr=df tilt_offset
	nvar /sdfr=df phi_offset
	nvar /sdfr=df alpha_offset
	
	duplicate /o raw, df:process_data
	duplicate /o raw_theta, df:process_theta
	duplicate /o raw_tilt, df:process_tilt
	duplicate /o raw_phi, df:process_phi
	
	wave /sdfr=df process_data
	wave /sdfr=df process_theta
	wave /sdfr=df process_tilt
	wave /sdfr=df process_phi
	
	process_theta = raw_theta - theta_offset
	process_tilt = raw_tilt - tilt_offset
	process_phi = raw_phi - phi_offset
	setscale /p x dimoffset(raw, 0) - alpha_offset, dimdelta(raw, 0), waveunits(raw, 0), process_data
end

/// crop the process data.
///
/// @param[in]	check			select which output to generate (currently not used).
///				@arg 0 (default)	process data, don't calculate check waves.
///				@arg 1			process data and calculate check waves.
///				@arg 2			calculate check waves only, do not calculate process data.
///
/// @param[in]	force			override the global enable flag.
///				@arg 0 (default)	apply this filter only if its global enable flag is set.
///				@arg 1			apply this filter even if it is not enabled.
///
static function do_crop(check, [force])
	variable check
	variable force
	
	if (ParamIsDefault(force))
		force = 0
	endif
	
	dfref df = $(package_path)
	wave /sdfr=df process_data
	wave /sdfr=df process_theta
	wave /sdfr=df process_tilt
	wave /sdfr=df process_phi
	nvar /sdfr=df crop_enable
	nvar /sdfr=df crop_alpha
	nvar /sdfr=df crop_theta
	svar /sdfr=df crop_rows

	if (force || crop_enable)
	
		if (crop_alpha > abs(dimdelta(process_data, 0)))
			crop_strip(process_data, -crop_alpha, +crop_alpha)
		endif
		
		if ((crop_theta >= 10) && (crop_theta < 90))
			crop_strip_theta(process_data, -0.1, crop_theta + 0.1, process_theta, process_tilt, process_phi)
		endif
		
		if (strlen(crop_rows) > 0)
			delete_rows(crop_rows, process_data, process_theta, process_tilt, process_phi)
		endif
	endif
end

/// delete individual rows from the data strip
///
/// @param[in]		rows		comma-separated list of row indices or ranges.
///									ranges are specified by joining the start and end index with a hyphen, e.g. 24-46.
///									the list does not need to be ordered.
/// @param[in,out]	data		2D data
///									the original wave is modified.
/// @param[in,out]	theta		theta positions along Y dimension of data.
///									the original wave is modified.
/// @param[in,out]	tilt		tilt positions along Y dimension of data
///									the original wave is modified.
/// @param[in,out]	phi			phi positions along Y dimension of data
///									the original wave is modified.
///
static function delete_rows(rows, data, theta, tilt, phi)
	string rows
	wave data
	wave theta
	wave tilt
	wave phi
	
	make /n=(numpnts(theta)) /i /free idx
	idx = p
	variable nrows = ItemsInList(rows, ",")
	variable irow
	string srow
	variable q1, q2
	for (irow = 0; irow < nrows; irow += 1)
		srow = StringFromList(irow, rows, ",")
		q1 = str2num(StringFromList(0, srow, "-"))
		q2 = str2num(StringFromList(1, srow, "-"))
		if (numtype(q2))
			q2 = q1
		endif
		if (q1 <= q2)
			idx[q1,q2] = -1
		endif
	endfor
	extract /free idx, idx, idx >= 0
	variable nx = dimsize(data, 0)
	variable ny = numpnts(idx)
	theta = theta[idx]
	tilt = tilt[idx]
	phi = phi[idx]
	redimension /n=(ny) theta, tilt, phi
	duplicate /free data, data_copy
	redimension /n=(nx,ny) data
	data = data_copy[p][idx[q]]
end

/// alpha-normalize the process data.
///
/// @param[in]	check			select which output to generate.
///				@arg 0 (default)	process data, don't calculate check waves.
///				@arg 1			process data and calculate check waves.
///				@arg 2			calculate check waves only, do not calculate process data.
///
/// @param[in]	force			override the global enable flag.
///				@arg 0 (default)	apply this filter only if its global enable flag is set.
///				@arg 1			apply this filter even if it is not enabled.
///
static function do_norm_alpha(check, [force])
	variable check
	variable force
	
	if (ParamIsDefault(force))
		force = 0
	endif
	
	dfref saveDF = GetDataFolderDFR()
	dfref df = $(package_path)
	wave /sdfr=df process_data
	nvar /sdfr=df norm_alpha_enable
	nvar /sdfr=df norm_alpha_mode
	nvar /sdfr=df norm_alpha_smoothing

	if (force || norm_alpha_enable)
		dfref temp_df = newfreedatafolder()
		setdatafolder temp_df
		normalize_strip_x(process_data, smooth_method=norm_alpha_mode, smooth_factor=norm_alpha_smoothing, check=check)
		if (check)
			wave check_dist
			wave check_smoo
			duplicate /o check_dist, df:dist_x
			duplicate /o check_smoo, df:dist_x_smoo
		endif
	endif
	
 	SetDataFolder saveDF
end

/// phi-normalize the process data.
///
/// @param[in]	check			select which output to generate.
///				@arg 0 (default)	process data, don't calculate check waves.
///				@arg 1			process data and calculate check waves.
///				@arg 2			calculate check waves only, do not calculate process data.
///
/// @param[in]	force			override the global enable flag.
///				@arg 0 (default)	apply this filter only if its global enable flag is set.
///				@arg 1			apply this filter even if it is not enabled.
///
static function do_norm_phi(check, [force])
	variable check
	variable force
	
	if (ParamIsDefault(force))
		force = 0
	endif
	
	dfref saveDF = GetDataFolderDFR()
	dfref df = $(package_path)
	wave /sdfr=df process_data
	wave /sdfr=df process_theta
	wave /sdfr=df process_phi
	nvar /sdfr=df norm_phi_enable
	nvar /sdfr=df norm_phi_mode
	nvar /sdfr=df norm_phi_thetarange

	if (force || norm_phi_enable)
		dfref temp_df = newfreedatafolder()
		setdatafolder temp_df
		normalize_strip_phi(process_data, process_theta, process_phi, theta_range=norm_phi_thetarange, check=check)
		if (check)
			wave check_dist
			wave check_smoo
			duplicate /o check_dist, df:dist_y
			duplicate /o check_smoo, df:dist_y_smoo
		endif
	endif
	
 	SetDataFolder saveDF
end

/// theta-normalize the process data.
///
/// @param[in]	check			select which output to generate.
///				@arg 0 (default)	process data, don't calculate check waves.
///				@arg 1			process data and calculate check waves.
///				@arg 2			calculate check waves only, do not calculate process data.
///
/// @param[in]	force			override the global enable flag.
///				@arg 0 (default)	apply this filter only if its global enable flag is set.
///				@arg 1			apply this filter even if it is not enabled.
///
static function do_norm_theta(check, [force])
	variable check
	variable force
	
	if (ParamIsDefault(force))
		force = 0
	endif
	
	dfref saveDF = GetDataFolderDFR()
	dfref df = $(package_path)
	wave /sdfr=df process_data
	wave /sdfr=df process_theta
	nvar /sdfr=df norm_theta_enable
	nvar /sdfr=df norm_theta_mode
	nvar /sdfr=df norm_theta_domain
	nvar /sdfr=df norm_theta_smoothing

	if (force || norm_theta_enable)
		dfref temp_df = newfreedatafolder()
		setdatafolder temp_df
		if (norm_theta_domain==1)
			normalize_strip_theta_scans(process_data, process_theta, smooth_method=norm_theta_mode, smooth_factor=norm_theta_smoothing, check=check)
		else
			normalize_strip_theta(process_data, process_theta, smooth_method=norm_theta_mode, smooth_factor=norm_theta_smoothing, check=check)
		endif
		if (check)
			wave check_dist
			wave check_smoo
			duplicate /o check_dist, df:dist_y
			duplicate /o check_smoo, df:dist_y_smoo
		endif
	endif
	
 	SetDataFolder saveDF
end

/// theta,phi-normalize the process data.
///
/// @param[in]	check			select which output to generate.
///				@arg 0 (default)	process data, don't calculate check waves.
///				@arg 1			process data and calculate check waves.
///				@arg 2			calculate check waves only, do not calculate process data.
///
/// @param[in]	force			override the global enable flag.
///				@arg 0 (default)	apply this filter only if its global enable flag is set.
///				@arg 1			apply this filter even if it is not enabled.
///
static function do_norm_thetaphi(check, [force])
	variable check
	variable force
	
	if (ParamIsDefault(force))
		force = 0
	endif
	
	dfref saveDF = GetDataFolderDFR()
	dfref df = $(package_path)
	wave /sdfr=df process_data
	wave /sdfr=df process_theta
	wave /sdfr=df process_phi
	nvar /sdfr=df norm_thetaphi_enable
	nvar /sdfr=df norm_thetaphi_mode
	nvar /sdfr=df norm_thetaphi_smoothing

	if (force || norm_thetaphi_enable)
		dfref temp_df = newfreedatafolder()
		setdatafolder temp_df
		normalize_strip_thetaphi(process_data, process_theta, process_phi, smooth_method=norm_thetaphi_mode, smooth_factor=norm_thetaphi_smoothing, check=check)
		if (check)
			wave check_dist
			wave check_smoo
			duplicate /o check_dist, df:dist_y
			duplicate /o check_smoo, df:dist_y_smoo
		endif
	endif
	
 	SetDataFolder saveDF
end

/// calculate the output using all enabled processing filters.
///
/// the diffractogram is calculated, to display the graph, call @ref asp_display_output.
///
function asp_calculate_output()
	dfref saveDF = GetDataFolderDFR()
	setdatafolder $(package_path)
	
	svar output_name
	
	wave process_data
	wave process_theta
	wave process_tilt
	wave process_phi

	nvar folding=output_folding
	nvar horizon=output_horizon

	do_init_process(0)
	do_crop(0)
	do_norm_alpha(0)
	do_norm_phi(0)
	do_norm_theta(0)
	do_norm_thetaphi(0)
	
	pizza_service_2(process_data, output_name, process_theta, process_tilt, process_phi, folding=folding, nograph=1)
	
	setdatafolder $output_name
	wave values
	wave pol
	if (horizon > 0)
		values = pol <= horizon ? values : nan
	endif
	
 	SetDataFolder saveDF
end

/// display the output diffractogram
///
/// this function either displays the current working data from the angle scan panel data folder,
/// or hologram data from a specified other data folder.
/// in either case, the graph is displayed using the current projection, mode, color table and contrast settings of the angle scan panel.
/// 
/// @param	data_df			data folder where the diffractogram data is located.
///							default: the folder of the angle scan panel package.
///
/// @param	data_name		name of the diffractogram data.
///							this is normally the name of a sub-folder that contains the data.
///							default: the name specified in the output_name variable in the package data folder.
///
function /s asp_display_output([data_df, data_name])
	dfref data_df
	string data_name
	
	dfref pkg_df = $(package_path)
	svar /sdfr=pkg_df output_name
	svar /sdfr=pkg_df output_graphname
	nvar /sdfr=pkg_df graph_projection
	nvar /sdfr=pkg_df graph_mode
	svar /sdfr=pkg_df graph_colortable
	nvar /sdfr=pkg_df graph_contrast

	if (ParamIsDefault(data_df))
		dfref data_df = pkg_df
	endif
	if (ParamIsDefault(data_name))
		data_name = output_name
	endif

	dfref saveDF = GetDataFolderDFR()
	setdatafolder data_df

	if (graph_mode == 3)
		interpolate_hemi_scan(data_name, projection=graph_projection)
	endif
	
	string graphname = data_name
	graphname = display_hemi_scan(data_name, projection=graph_projection, graphtype=graph_mode, graphname=graphname)
	if (ParamIsDefault(data_df))
		output_graphname = graphname
	endif
 	SetDataFolder saveDF

	if (strlen(graphname) && (wintype(graphname) == 1))
		set_contrast(graph_contrast, graph_contrast, graphname=graphname, colortable=graph_colortable)
	endif

	return graphname
end

/// update graphs with new color table or contrast
///
/// applies to the preview graph and the diffractogram.
///
function asp_update_graph()
	dfref df = $(package_path)

	svar /sdfr=df preview_graphname
	svar /sdfr=df output_graphname
	svar /sdfr=df graph_colortable
	nvar /sdfr=df graph_contrast

	if (strlen(preview_graphname) && (wintype(preview_graphname) == 1))
		set_contrast(graph_contrast, graph_contrast, graphname=preview_graphname, colortable=graph_colortable)
	endif
	if (strlen(output_graphname) && (wintype(output_graphname) == 1))
		set_contrast(graph_contrast, graph_contrast, graphname=output_graphname, colortable=graph_colortable)
	endif
end

/// close all graphs created by the angle scan panel
///
function asp_close_graphs()
	dfref df = $(package_path)

	svar /sdfr=df preview_graphname
	svar /sdfr=df output_graphname
	svar /sdfr=df dist_x_graphname
	svar /sdfr=df dist_y_graphname

	if (strlen(preview_graphname) && (wintype(preview_graphname) == 1))
		killwindow $preview_graphname
	endif
	if (strlen(output_graphname) && (wintype(output_graphname) == 1))
		killwindow $output_graphname
	endif
	if (strlen(dist_x_graphname) && (wintype(dist_x_graphname) == 1))
		killwindow $dist_x_graphname
	endif
	if (strlen(dist_y_graphname) && (wintype(dist_y_graphname) == 1))
		killwindow $dist_y_graphname
	endif

	preview_graphname = ""
	output_graphname = ""
	dist_x_graphname = ""
	dist_y_graphname = ""
end

/// copy the output data to a new folder
///
/// @param	dest_name			name of destination folder.
///								can contain a relative (starting with a colon) or absolute (starting with "root:") path.
///								folders in the path must exist except for the last one.
///								the destination folder does not need to exist.
///								existing data in the destination folder is overwritten.
///
/// @param	do_graph			switch to duplicate the graph window as well (1).
///								default: 0 (do not create a graph)
///
/// @return						name of the graph window
///
function /s asp_duplicate_output(dest_name, [do_graph])
	string dest_name
	variable do_graph
	
	if (ParamIsDefault(do_graph))
		do_graph = 0
	endif

	dfref df = $(package_path)
	svar /sdfr=df source_path
	svar /sdfr=df output_name
	svar /sdfr=df output_graphname
	wave raw_data = $source_path
	
	dfref saveDF = GetDataFolderDFR()
	dfref raw_df = GetWavesDataFolderDFR(raw_data)
	setdatafolder raw_df
	newdatafolder /o /s $dest_name
	dfref dest_df = GetDataFolderDFR()
	setdatafolder df
	duplicate_hemi_scan(output_name, dest_df, "")
	
	string graphname = ""
	if (do_graph)
		graphname = asp_display_output(data_df=raw_df, data_name=dest_name)
	endif
	
 	SetDataFolder saveDF
 	return graphname
end

/// save the output diffractogram to an igor text file
///
function asp_save_output_itx()
	dfref df = $(package_path)
	svar /sdfr=df output_name
	
	dfref saveDF = GetDataFolderDFR()
	setdatafolder df
	save_hemi_scan(output_name, "", "")
	
 	SetDataFolder saveDF
end

/// save the output diffractogram to a PMSCO ETPI file
///
/// @param	ekin					kinetic energy in eV
///
function asp_save_output_etpi(ekin)
	variable ekin
	
	dfref df = $(package_path)
	svar /sdfr=df output_name
	wave /sdfr=df process_data
	
	dfref saveDF = GetDataFolderDFR()
	setdatafolder df
	string s_prefix = ""
	string s_int = "values"
	dfref data_df = find_hemi_data(output_name, s_prefix, s_int)
	string s_polar = s_prefix + "pol"
	string s_azim = s_prefix + "az"
	
	pmsco_save_scan("", "", num2str(ekin), s_polar, s_azim, "", s_int, "", sdfr=data_df)
	
 	SetDataFolder saveDF
end

static function check_norm_alpha()
	do_init_process(0)
	do_crop(0)
	do_norm_alpha(2, force=1)
	asp_display_dist_check(1, 0)
end

static function check_norm_phi()
	do_init_process(0)
	do_crop(0)
	do_norm_phi(2, force=1)
	asp_display_dist_check(0, 1)
end

static function check_norm_theta()
	do_init_process(0)
	do_crop(0)
	do_norm_theta(2, force=1)
	asp_display_dist_check(0, 1)
end

static function check_norm_thetaphi()
	do_init_process(0)
	do_crop(0)
	do_norm_thetaphi(2, force=1)
	asp_display_dist_check(0, 1)
end

static function preview_crop()
	do_init_process(0)
	do_crop(0, force=1)
	asp_display_previews()
end

static function preview_norm_alpha()
	do_init_process(0)
	do_crop(0)
	do_norm_alpha(1, force=1)
	asp_display_previews()
end

static function preview_norm_phi()
	do_init_process(0)
	do_crop(0)
	do_norm_phi(1, force=1)
	asp_display_previews()
end

static function preview_norm_theta()
	do_init_process(0)
	do_crop(0)
	do_norm_theta(1, force=1)
	asp_display_previews()
end

static function preview_norm_thetaphi()
	do_init_process(0)
	do_crop(0)
	do_norm_thetaphi(1, force=1)
	asp_display_previews()
end

/// create the angle scan processing panel
///
function asp_show_panel()
	dfref df = $(package_path)
	svar /sdfr=df panel_name

	if (strlen(panel_name) && (wintype(panel_name) == 7))
		DoWindow /F $panel_name
		return 0
	endif

	NewPanel /K=1 /N=anglescan_panel /W=(200,50,480,874) as "angle scan processing"
	panel_name = s_name
	
	GroupBox gb_source, title="data source"
	Button b_source_select, size={50,20},proc=PearlAnglescanPanel#bp_source_select,title="select..."
	Button b_source_select, help={"select the source wave, e.g. ReducedData1. it must be in the scan or region data folder. the attr folder with the manipulator waves must be in the same folder or one level up."}
	Button b_source_update, size={50,20},proc=PearlAnglescanPanel#bp_source_update,title="update"
	Button b_source_update, help={"reload the process data from the previous source (link displayed below)"}
	TitleBox tb_source_path, size={240,21}
	TitleBox tb_source_path,variable= root:packages:pearl_anglescan_panel:source_path

	GroupBox gb_offsets, title="offsets"
	SetVariable sv_theta_offset, size={88,16},bodyWidth=60,title="theta"
	SetVariable sv_theta_offset,value= root:packages:pearl_anglescan_panel:theta_offset
	SetVariable sv_theta_offset, help={"manipulator theta value that corresponds to normal emission."}
	SetVariable sv_tilt_offset, size={74,16},bodyWidth=60,title="tilt"
	SetVariable sv_tilt_offset,value= root:packages:pearl_anglescan_panel:tilt_offset
	SetVariable sv_tilt_offset, help={"manipulator tilt value that corresponds to normal emission."}
	SetVariable sv_phi_offset, size={78,16},bodyWidth=60,title="phi"
	SetVariable sv_phi_offset,value= root:packages:pearl_anglescan_panel:phi_offset
	SetVariable sv_phi_offset, help={"manipulator phi value that should map to the 3 o'clock angle."}
	SetVariable sv_alpha_offset, size={90,16},bodyWidth=60,title="alpha"
	SetVariable sv_alpha_offset,value= root:packages:pearl_anglescan_panel:alpha_offset
	SetVariable sv_alpha_offset, help={"alpha value that corresponds to normal emission (if the sample normal is properly aligned)."}
	Button b_save_prefs, size={80,20},proc=PearlAnglescanPanel#bp_save_prefs,title="save prefs"
	Button b_save_prefs, help={"save settings as preferences."}
	Button b_load_prefs, size={80,20},proc=PearlAnglescanPanel#bp_load_prefs,title="load prefs"
	Button b_load_prefs, help={"load settings from preferences."}

	GroupBox gb_crop, title="crop and delete"
	CheckBox cb_crop_enable, size={50,14}, title="enable"
	CheckBox cb_crop_enable, help={"crop at +/-alpha and +theta, delete arbitrary rows"}
	CheckBox cb_crop_enable, variable= root:packages:pearl_anglescan_panel:crop_enable
	SetVariable sv_crop_alpha, size={90,16},bodyWidth=60,title="alpha"
	SetVariable sv_crop_alpha, limits={0,30,1},value= root:packages:pearl_anglescan_panel:crop_alpha
	SetVariable sv_crop_alpha, help={"alpha (detection angle) cropping angle (positive boundary), relative to normal emission"}
	SetVariable sv_crop_theta, size={90,16},bodyWidth=60,title="theta"
	SetVariable sv_crop_theta, limits={10,90,1},value= root:packages:pearl_anglescan_panel:crop_theta
	SetVariable sv_crop_theta, help={"theta (polar angle) upper limit, relative to normal emission"}
	SetVariable sv_crop_rows, size={200,16},bodyWidth=160,title="rows"
	SetVariable sv_crop_rows, limits={10,90,1},value= root:packages:pearl_anglescan_panel:crop_rows
	SetVariable sv_crop_rows, help={"rows to delete from the raw data. comma-separated point indices, hyphen for range."}
	Button b_crop_preview, size={80,20},proc=PearlAnglescanPanel#bp_crop_preview,title="preview"
	Button b_crop_preview, help={"show a preview of the cropped dataset."}

	GroupBox gb_norm_alpha, title="normalize alpha"
	CheckBox cb_norm_alpha_enable, size={50,14}, title="enable"
	CheckBox cb_norm_alpha_enable,variable= root:packages:pearl_anglescan_panel:norm_alpha_enable
	CheckBox cb_norm_alpha_enable, help={"enable normalization of the alpha distribution"}
	PopupMenu pm_norm_alpha_mode, size={138,21}, bodyWidth=100, proc=PearlAnglescanPanel#pmp_norm_alpha_mode,title="method"
	PopupMenu pm_norm_alpha_mode, mode=5, popvalue="loess", value= #"\"none;binomial;boxcar;scienta;loess;\""
	PopupMenu pm_norm_alpha_mode, help={"alpha normalization method. recommended: loess"}
	SetVariable sv_norm_alpha_smoothing, size={112,16}, bodyWidth=60, title="smoothing"
	SetVariable sv_norm_alpha_smoothing, limits={0,1,0.05}, value= root:packages:pearl_anglescan_panel:norm_alpha_smoothing
	SetVariable sv_norm_alpha_smoothing, help={"smoothing parameter (depends on the normalization method)."}
	Button b_norm_alpha_check, size={80,20}, proc=PearlAnglescanPanel#bp_norm_alpha_check,title="check"
	Button b_norm_alpha_check, help={"show a graph of the normalization function"}
	Button b_norm_alpha_preview, size={80,20}, proc=PearlAnglescanPanel#bp_norm_alpha_preview,title="preview"
	Button b_norm_alpha_preview, help={"show a preview of the normalized dataset (without other normalizations)."}

	GroupBox gb_norm_phi, title="normalize phi"
	CheckBox cb_norm_phi_enable, size={50,14}, title="enable"
	CheckBox cb_norm_phi_enable,variable= root:packages:pearl_anglescan_panel:norm_phi_enable
	CheckBox cb_norm_phi_enable, help={"enable normalization of the phi distribution to reduce the effect of wobble"}
	SetVariable sv_norm_phi_range, size={118,16}, bodyWidth=60, title="theta range"
	SetVariable sv_norm_phi_range, limits={0,90,1}, value= root:packages:pearl_anglescan_panel:norm_phi_thetarange
	SetVariable sv_norm_phi_range, help={"theta range (from normal) to factor into the normalization function"}
	Button b_norm_phi_check, size={80,20}, proc=PearlAnglescanPanel#bp_norm_phi_check, title="check"
	Button b_norm_phi_check, help={"show a graph of the normalization function"}
	Button b_norm_phi_preview, size={80,20}, proc=PearlAnglescanPanel#bp_norm_phi_preview, title="preview"
	Button b_norm_phi_preview, help={"show a preview of the normalized dataset (without other normalizations)."}

	GroupBox gb_norm_theta, title="normalize theta"
	CheckBox cb_norm_theta_enable, size={50,14},title="enable"
	CheckBox cb_norm_theta_enable, variable= root:packages:pearl_anglescan_panel:norm_theta_enable
	CheckBox cb_norm_theta_enable, help={"enable normalization of the theta distribution (integrated over phi)"}
	PopupMenu pm_norm_theta_domain, size={138,21}, bodyWidth=100, proc=PearlAnglescanPanel#pmp_norm_theta_domain, title="domain"
	PopupMenu pm_norm_theta_domain, mode=5, popvalue="loess", value= #"\"global;scans;\""
	PopupMenu pm_norm_theta_domain, help={"smoothing domain: global or individual scans. use global unless there is a stronga or irregular phi variation."}
	PopupMenu pm_norm_theta_mode, size={138,21},bodyWidth=100,proc=PearlAnglescanPanel#pmp_norm_theta_mode,title="method"
	PopupMenu pm_norm_theta_mode,mode=5,popvalue="loess",value= #"\"none;binomial;boxcar;polynomial;loess;\""
	PopupMenu pm_norm_theta_mode, help={"theta normalization method. recommended: loess"}
	SetVariable sv_norm_theta_smoothing, size={112,16}, bodyWidth=60, title="smoothing"
	SetVariable sv_norm_theta_smoothing, limits={0,1,0.05}, value= root:packages:pearl_anglescan_panel:norm_theta_smoothing
	SetVariable sv_norm_theta_smoothing, help={"smoothing parameter (depends on the normalization method)."}
	Button b_norm_theta_check, size={80,20}, proc=PearlAnglescanPanel#bp_norm_theta_check, title="check"
	Button b_norm_theta_check, help={"show a graph of the normalization function"}
	Button b_norm_theta_preview, size={80,20}, proc=PearlAnglescanPanel#bp_norm_theta_preview, title="preview"
	Button b_norm_theta_preview, help={"show a preview of the normalized dataset (without other normalizations)."}

	GroupBox gb_norm_thetaphi, size={272,97},title="normalize (theta,phi)"
	CheckBox cb_norm_thetaphi_enable, size={50,14},title="enable"
	CheckBox cb_norm_thetaphi_enable, variable= root:packages:pearl_anglescan_panel:norm_thetaphi_enable
	CheckBox cb_norm_thetaphi_enable, help={"enable normalization of the (theta, phi) distribution."}
	PopupMenu pm_norm_thetaphi_mode, size={138,21}, bodyWidth=100, proc=PearlAnglescanPanel#pmp_norm_thetaphi_mode,title="method"
	PopupMenu pm_norm_thetaphi_mode, mode=5, popvalue="loess", value= #"\"none;none;none;none;loess;\""
	PopupMenu pm_norm_thetaphi_mode, help={"theta normalization method. recommended: loess"}
	SetVariable sv_norm_thetaphi_smoothing, size={112,16}, bodyWidth=60, title="smoothing"
	SetVariable sv_norm_thetaphi_smoothing, limits={0,1,0.05}, value= root:packages:pearl_anglescan_panel:norm_thetaphi_smoothing
	SetVariable sv_norm_thetaphi_smoothing, help={"smoothing parameter (depends on the normalization method)."}
	Button b_norm_thetaphi_check, size={80,20}, proc=PearlAnglescanPanel#bp_norm_thetaphi_check, title="check"
	Button b_norm_thetaphi_check, help={"show a graph of the normalization function"}
	Button b_norm_thetaphi_preview, size={80,20}, proc=PearlAnglescanPanel#bp_norm_thetaphi_preview, title="preview"
	Button b_norm_thetaphi_preview, help={"show a preview of the normalized dataset (without other normalizations)."}

	GroupBox gb_output, title="output"
	SetVariable sv_output_folding, size={95,16}, bodyWidth=60, title="folding"
	SetVariable sv_output_folding, limits={1,20,1}, value= root:packages:pearl_anglescan_panel:output_folding
	SetVariable sv_output_folding, help={"n-fold rotational average. 1=no averaging."}
	SetVariable sv_output_horizon, size={98,16}, bodyWidth=60, title="horizon"
	SetVariable sv_output_horizon, limits={1,90,1}, value= root:packages:pearl_anglescan_panel:output_horizon
	SetVariable sv_output_horizon, help={"highest theta to display"}
	PopupMenu pm_graph_projection, size={149,21}, bodyWidth=100, proc=PearlAnglescanPanel#pmp_graph_projection, title="projection"
	PopupMenu pm_graph_projection, mode=2, popvalue="stereographic", value= #"\"equidistant;stereographic;equal area;gnomonic;orthographic;\""
	PopupMenu pm_graph_projection, help={"projection (theta mapping) mode"}
	PopupMenu pm_graph_mode, size={129,21}, bodyWidth=100, proc=PearlAnglescanPanel#pmp_graph_mode,title="mode"
	PopupMenu pm_graph_mode, mode=2, popvalue="dots", value= #"\"none;dots;none;image;\""
	PopupMenu pm_graph_mode, help={"graph type: dots = coloured dots on circles; image = interpolated matrix"}
	Button b_output_calc, size={80,20}, proc=PearlAnglescanPanel#bp_output_calc, title="calc + display"
	Button b_output_calc, help={"execute data processing with the enabled filters and display the diffractogram."}
	Button b_output_duplicate, size={80,20}, proc=PearlAnglescanPanel#bp_output_duplicate, title="duplicate ..."
	Button b_output_duplicate, help={"copy the result to an arbitrary data folder."}
	Button b_output_itx, size={80,20}, proc=PearlAnglescanPanel#bp_output_itx, title="save ITX ..."
	Button b_output_itx, help={"save the result to an igor text file (itx)."}
	Button b_output_etpi, size={80,20}, proc=PearlAnglescanPanel#bp_output_etpi, title="save ETPI ..."
	Button b_output_etpi, help={"save the result to a pmsco angle scan file (etpi)."}

	GroupBox gb_graph, title="graph"
	PopupMenu pm_graph_colortable, size={152,21}, bodyWidth=100, proc=PearlAnglescanPanel#pmp_graph_colortable, title="color table"
	PopupMenu pm_graph_colortable, mode=0, value= #"\"*COLORTABLEPOPNONAMES*\""
	PopupMenu pm_graph_colortable, help={"color table to use in pseudocolor graphs."}
	SetVariable sv_graph_contrast, size={119,16}, bodyWidth=60, title="contrast (%)"
	SetVariable sv_graph_contrast, limits={0,25,1}, value= root:packages:pearl_anglescan_panel:graph_contrast
	SetVariable sv_graph_contrast, help={"contrast value (percentile)."}
	Button b_graph_update, size={80,20}, proc=PearlAnglescanPanel#bp_graph_update, title="update"
	Button b_graph_update, help={"update the existing graph."}
	Button b_graph_png, size={80,20}, proc=PearlAnglescanPanel#bp_graph_png, title="save PNG ..."
	Button b_graph_png, help={"save the graph in png format."}

	arrange_controls()
	update_menus()
end

static function arrange_controls()
	dfref df = $(package_path)
	svar /sdfr=df panel_name

	variable gb_space = 2
	variable gb_internal_top = 16
	variable gb_internal_bot = 4
	variable line_space = 22
	
	variable cb_adj = 2
	variable sv_adj = 2
	variable pm_adj = 0
	variable b_adj = 0
	variable tb_adj = 0
	
	variable gb_top = 4
	variable gb_ht = 0

	// ht = line + 30
	// gb = gb + ht + 2
	// cb = gb + 18
	// pm = gb + 38
	// line += 26
	// sv = line + 2

	GroupBox gb_source,pos={4,gb_top}
	gb_ht = gb_internal_top
	Button b_source_select,pos={17, gb_top + gb_ht + b_adj},size={50,20}
	Button b_source_update, pos={67, gb_top + gb_ht + b_adj},size={50,20}
	gb_ht += line_space
	TitleBox tb_source_path,pos={18, gb_top + gb_ht + tb_adj},size={240,21}
	gb_ht += line_space
	gb_ht += gb_internal_bot
	GroupBox gb_source, size={272,gb_ht}

	gb_top += gb_ht + gb_space
	GroupBox gb_offsets,pos={4,gb_top}
	gb_ht = gb_internal_top
	SetVariable sv_theta_offset,pos={46, gb_top + gb_ht + sv_adj},size={88,16}
	Button b_save_prefs,pos={186, gb_top + gb_ht + b_adj},size={80,20}
	gb_ht += line_space
	SetVariable sv_tilt_offset,pos={60, gb_top + gb_ht + sv_adj},size={74,16}
	Button b_load_prefs,pos={186, gb_top + gb_ht + b_adj},size={80,20}
	gb_ht += line_space
	SetVariable sv_phi_offset,pos={56, gb_top + gb_ht + sv_adj},size={78,16}
	gb_ht += line_space
	SetVariable sv_alpha_offset,pos={44, gb_top + gb_ht + sv_adj},size={90,16}
	gb_ht += line_space
	gb_ht += gb_internal_bot
	GroupBox gb_offsets, size={272,gb_ht}

	gb_top += gb_ht + gb_space
	GroupBox gb_crop,pos={4,gb_top}
	gb_ht = gb_internal_top
	CheckBox cb_crop_enable,pos={73, gb_top + gb_ht + cb_adj},size={50,14}
	gb_ht += line_space
	SetVariable sv_crop_alpha, pos={44, gb_top + gb_ht + sv_adj}, size={90,16}
	Button b_crop_preview,pos={186, gb_top + gb_ht + b_adj},size={80,20}
	gb_ht += line_space
	SetVariable sv_crop_theta, pos={44, gb_top + gb_ht + sv_adj}, size={90,16}
	gb_ht += line_space
	SetVariable sv_crop_rows, pos={44, gb_top + gb_ht + sv_adj}, size={190,16}
	gb_ht += line_space
	gb_ht += gb_internal_bot
	GroupBox gb_crop, size={272,gb_ht}

	gb_top += gb_ht + gb_space
	GroupBox gb_norm_alpha,pos={4,gb_top}
	gb_ht = gb_internal_top
	CheckBox cb_norm_alpha_enable,pos={73, gb_top + gb_ht + cb_adj},size={50,14}
	gb_ht += line_space
	PopupMenu pm_norm_alpha_mode,pos={36, gb_top + gb_ht + pm_adj},size={138,21}
	Button b_norm_alpha_check,pos={186, gb_top + gb_ht + b_adj},size={80,20}
	gb_ht += line_space
	SetVariable sv_norm_alpha_smoothing,pos={22, gb_top + gb_ht + sv_adj},size={112,16}
	Button b_norm_alpha_preview,pos={186, gb_top + gb_ht + b_adj},size={80,20}
	gb_ht += line_space
	gb_ht += gb_internal_bot
	GroupBox gb_norm_alpha, size={272,gb_ht}

	gb_top += gb_ht + gb_space
	GroupBox gb_norm_phi,pos={4,gb_top}
	gb_ht = gb_internal_top
	CheckBox cb_norm_phi_enable,pos={73, gb_top + gb_ht + cb_adj},size={50,14}
	Button b_norm_phi_check,pos={186, gb_top + gb_ht + b_adj},size={80,20}
	gb_ht += line_space
	SetVariable sv_norm_phi_range,pos={15, gb_top + gb_ht + sv_adj},size={118,16}
	Button b_norm_phi_preview,pos={186, gb_top + gb_ht + b_adj},size={80,20}
	gb_ht += line_space
	gb_ht += gb_internal_bot
	GroupBox gb_norm_phi, size={272,gb_ht}

	gb_top += gb_ht + gb_space
	GroupBox gb_norm_theta,pos={4,gb_top}
	gb_ht = gb_internal_top
	CheckBox cb_norm_theta_enable,pos={73, gb_top + gb_ht + cb_adj},size={50,14}
	gb_ht += line_space
	PopupMenu pm_norm_theta_domain, pos={35, gb_top + gb_ht + pm_adj}, size={138,21}
	Button b_norm_theta_check,pos={186, gb_top + gb_ht + b_adj},size={80,20}
	gb_ht += line_space
	PopupMenu pm_norm_theta_mode,pos={35, gb_top + gb_ht + pm_adj},size={138,21}
	Button b_norm_theta_preview,pos={186, gb_top + gb_ht + b_adj},size={80,20}
	gb_ht += line_space
	SetVariable sv_norm_theta_smoothing,pos={21, gb_top + gb_ht + sv_adj},size={112,16}
	gb_ht += line_space
	gb_ht += gb_internal_bot
	GroupBox gb_norm_theta, size={272,gb_ht}

	gb_top += gb_ht + gb_space
	GroupBox gb_norm_thetaphi,pos={4,gb_top}
	gb_ht = gb_internal_top
	CheckBox cb_norm_thetaphi_enable,pos={73, gb_top + gb_ht + cb_adj},size={50,14}
	gb_ht += line_space
	PopupMenu pm_norm_thetaphi_mode,pos={35, gb_top + gb_ht + pm_adj},size={138,21}
	Button b_norm_thetaphi_check,pos={186, gb_top + gb_ht + b_adj},size={80,20}
	gb_ht += line_space
	SetVariable sv_norm_thetaphi_smoothing,pos={21, gb_top + gb_ht + sv_adj},size={112,16}
	Button b_norm_thetaphi_preview,pos={186, gb_top + gb_ht + b_adj},size={80,20}
	gb_ht += line_space
	gb_ht += gb_internal_bot
	GroupBox gb_norm_thetaphi, size={272,gb_ht}

	gb_top += gb_ht + gb_space
	GroupBox gb_output,pos={4,gb_top}
	gb_ht = gb_internal_top
	SetVariable sv_output_folding,pos={38, gb_top + gb_ht + sv_adj},size={95,16}
	Button b_output_calc,pos={186, gb_top + gb_ht + b_adj},size={80,20}
	gb_ht += line_space
	SetVariable sv_output_horizon,pos={35, gb_top + gb_ht + sv_adj},size={98,16}
	Button b_output_duplicate,pos={186, gb_top + gb_ht + b_adj},size={80,20}
	gb_ht += line_space
	PopupMenu pm_graph_projection,pos={24, gb_top + gb_ht + pm_adj},size={149,21}
	Button b_output_itx,pos={186, gb_top + gb_ht + b_adj},size={80,20}
	gb_ht += line_space
	PopupMenu pm_graph_mode,pos={44, gb_top + gb_ht + pm_adj},size={129,21}
	Button b_output_etpi,pos={186, gb_top + gb_ht + b_adj},size={80,20}
	gb_ht += line_space
	gb_ht += gb_internal_bot
	GroupBox gb_output, size={272,gb_ht}

	gb_top += gb_ht + gb_space
	GroupBox gb_graph,pos={4,gb_top}
	gb_ht = gb_internal_top
	PopupMenu pm_graph_colortable,pos={21, gb_top + gb_ht + pm_adj},size={152,21}
	Button b_graph_update,pos={186, gb_top + gb_ht + b_adj},size={80,20}
	gb_ht += line_space
	SetVariable sv_graph_contrast,pos={14, gb_top + gb_ht + sv_adj},size={119,16}
	Button b_graph_png,pos={186, gb_top + gb_ht + b_adj},size={80,20}
	gb_ht += line_space
	gb_ht += gb_internal_bot
	GroupBox gb_graph, size={272,gb_ht}
	
	gb_top += gb_ht + gb_space
	//MoveWindow 200, 100, 479, 100 + gb_top
end

/// update the popup menus to reflect the values of the global variables
///
static function update_menus()
	dfref df = $(package_path)
	svar /sdfr=df panel_name
	if (wintype(panel_name) == 7)
		variable m
		nvar /sdfr=df norm_alpha_mode
		m = norm_alpha_mode + 1
		PopupMenu pm_norm_alpha_mode win=$panel_name, mode=m
		nvar /sdfr=df norm_theta_domain
		m = norm_theta_domain + 1
		PopupMenu pm_norm_theta_domain win=$panel_name, mode=m
		nvar /sdfr=df norm_theta_mode
		m = norm_theta_mode + 1
		PopupMenu pm_norm_theta_mode win=$panel_name, mode=m
		nvar /sdfr=df norm_thetaphi_mode
		m = norm_thetaphi_mode + 1
		PopupMenu pm_norm_thetaphi_mode win=$panel_name, mode=m
		nvar /sdfr=df graph_mode
		m = graph_mode + 1
		PopupMenu pm_graph_mode win=$panel_name, mode=m
		nvar /sdfr=df graph_projection
		m = graph_projection + 1
		PopupMenu pm_graph_projection win=$panel_name, mode=m
		svar /sdfr=df graph_colortable
		m = 1 + WhichListItem(graph_colortable, CTabList())
		PopupMenu pm_graph_colortable win=$panel_name, mode=m
	endif
end

static function bp_load_prefs(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			load_prefs()
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_save_prefs(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			save_prefs()
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_source_select(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			dfref dfBefore = GetDataFolderDFR()
			Execute /q/z "CreateBrowser prompt=\"Select 2D holo scan wave\", showWaves=1, showVars=0, showStrs=0"
			dfref dfAfter = GetDataFolderDFR()
			SetDataFolder dfBefore
	
			SVAR list = S_BrowserList
			NVAR flag = V_Flag
			if ((flag != 0) && (ItemsInList(list) >= 1))
				string wname = StringFromList(0, list)
				wave w = $wname
				asp_import_raw(w)
			endif

			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_source_update(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			dfref packdf = $package_path
			svar /sdfr=packdf source_path
			wave /z w = $source_path
			if (waveexists(w))
				asp_import_raw(w)
			else
				DoAlert 0, "can't find source data."
			endif
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_norm_alpha_check(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			check_norm_alpha()
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_norm_theta_check(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			check_norm_theta()
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_norm_phi_check(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			check_norm_phi()
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_norm_thetaphi_check(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			check_norm_thetaphi()
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_crop_preview(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			preview_crop()
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_norm_alpha_preview(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			preview_norm_alpha()
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_norm_phi_preview(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			preview_norm_phi()
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_norm_theta_preview(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			preview_norm_theta()
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_norm_thetaphi_preview(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			preview_norm_thetaphi()
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_output_calc(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			asp_calculate_output()
			asp_display_output()
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_output_duplicate(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			string dest_folder
			variable do_graph = 1
			prompt dest_folder, "destination folder name (relative to data source)"
			prompt do_graph, "duplicate graph (yes = 1, no = 0)"
			doprompt "duplicate", dest_folder, do_graph
			if (!v_flag)
				asp_duplicate_output(dest_folder, do_graph=do_graph)
			endif
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_output_etpi(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			dfref df = $(package_path)
			wave /sdfr=df process_data
			variable ekin
			ekin = NumberByKey("KineticEnergy", note(process_data), "=", "\r")
			prompt ekin, "kinetic energy"
			doprompt "save etpi", ekin
			if (!v_flag)
				asp_save_output_etpi(ekin)
			endif
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_output_itx(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			asp_save_output_itx()
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_graph_update(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			asp_update_graph()
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function bp_graph_png(ba) : ButtonControl
	STRUCT WMButtonAction &ba

	switch( ba.eventCode )
		case 2: // mouse up
			dfref df = $(package_path)
			svar /sdfr=df source_path
			svar /sdfr=df output_graphname
			if (WinType(output_graphname) == 1)
				SavePICT /WIN=$output_graphname /E=-5 /B=144 /TRAN=0
			endif
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function pmp_norm_alpha_mode(pa) : PopupMenuControl
	STRUCT WMPopupAction &pa

	switch( pa.eventCode )
		case 2: // mouse up
			dfref df = $(package_path)
			nvar /sdfr=df norm_alpha_mode
			norm_alpha_mode = pa.popNum - 1
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function pmp_norm_theta_domain(pa) : PopupMenuControl
	STRUCT WMPopupAction &pa

	switch( pa.eventCode )
		case 2: // mouse up
			dfref df = $(package_path)
			nvar /sdfr=df norm_theta_domain
			norm_theta_domain = pa.popNum - 1
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function pmp_norm_theta_mode(pa) : PopupMenuControl
	STRUCT WMPopupAction &pa

	switch( pa.eventCode )
		case 2: // mouse up
			dfref df = $(package_path)
			nvar /sdfr=df norm_theta_mode
			norm_theta_mode = pa.popNum - 1
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function pmp_norm_thetaphi_mode(pa) : PopupMenuControl
	STRUCT WMPopupAction &pa

	switch( pa.eventCode )
		case 2: // mouse up
			dfref df = $(package_path)
			nvar /sdfr=df norm_thetaphi_mode
			norm_thetaphi_mode = pa.popNum - 1
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function pmp_graph_mode(pa) : PopupMenuControl
	STRUCT WMPopupAction &pa

	switch( pa.eventCode )
		case 2: // mouse up
			dfref df = $(package_path)
			nvar /sdfr=df graph_mode
			graph_mode = pa.popNum - 1
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function pmp_graph_projection(pa) : PopupMenuControl
	STRUCT WMPopupAction &pa

	switch( pa.eventCode )
		case 2: // mouse up
			dfref df = $(package_path)
			nvar /sdfr=df graph_projection
			graph_projection = pa.popNum - 1
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End

static function pmp_graph_colortable(pa) : PopupMenuControl
	STRUCT WMPopupAction &pa

	switch( pa.eventCode )
		case 2: // mouse up
			dfref df = $(package_path)
			svar /sdfr=df graph_colortable
			graph_colortable = StringFromList(pa.popNum - 1, CTabList())
			asp_update_graph()
			break
		case -1: // control being killed
			break
	endswitch

	return 0
End
