예제 #1
0
        public MapViewModel(int MapViewId = 0)
        {
            _mapViewId = MapViewId;
            MinValue   = 1E-9;
            MaxValue   = 1.0;
            AutoScale  = false;

            ScalingTypeOptionVm =
                new OptionViewModel <ScalingType>(StringLookup.GetLocalizedString("Label_ScalingType") + _mapViewId,
                                                  false);
            ScalingTypeOptionVm.PropertyChanged += (sender, args) => UpdateImages();

            ColormapTypeOptionVm =
                new OptionViewModel <ColormapType>(StringLookup.GetLocalizedString("Label_ColormapType"));
            ColormapTypeOptionVm.PropertyChanged += (sender, args) =>
            {
                _colormap = new Colormap(ColormapTypeOptionVm.SelectedValue);
                UpdateImages();
            };

            _colormap = new Colormap(ColormapTypeOptionVm.SelectedValue);

            PlotMap  = new RelayCommand <object>(PlotMap_Executed);
            ClearMap = new RelayCommand <object>(ClearMap_Executed);

            ExportDataToTextCommand = new RelayCommand(() => Maps_ExportDataToText_Executed(null, null));
            DuplicateWindowCommand  = new RelayCommand(() => Map_DuplicateWindow_Executed(null, null));
        }
        public SimulationOptionsViewModel(SimulationOptions options)
        {
            _simulationOptions = options; // use the property to invoke the appropriate change notification

#if WHITELIST
            _absorptionWeightingTypeVM   = new OptionViewModel <AbsorptionWeightingType>("Absorption Weighting Type:", false, _simulationOptions.AbsorptionWeightingType, WhiteList.AbsorptionWeightingTypes);
            _randomNumberGeneratorTypeVM = new OptionViewModel <RandomNumberGeneratorType>("Random Number Generator Type:", false, _simulationOptions.RandomNumberGeneratorType, WhiteList.RandomNumberGeneratorTypes);
            _phaseFunctionTypeVM         = new OptionViewModel <PhaseFunctionType>("Phase Function Type:", false, _simulationOptions.PhaseFunctionType, WhiteList.PhaseFunctionTypes);
#else
            _absorptionWeightingTypeVM = new OptionViewModel <AbsorptionWeightingType>("Absorption Weighting Type:",
                                                                                       false, _simulationOptions.AbsorptionWeightingType);
            _randomNumberGeneratorTypeVM = new OptionViewModel <RandomNumberGeneratorType>("Random Number Generator:",
                                                                                           false, _simulationOptions.RandomNumberGeneratorType);
            _phaseFunctionTypeVM = new OptionViewModel <PhaseFunctionType>("Phase Function Type:", false,
                                                                           _simulationOptions.PhaseFunctionType);
#endif
            SetStatisticsFolderCommand = new RelayCommand(() => MC_SetStatisticsFolder_Executed(null, null));

            _absorptionWeightingTypeVM.PropertyChanged += (sender, args) =>
                                                          _simulationOptions.AbsorptionWeightingType = _absorptionWeightingTypeVM.SelectedValue;
            _randomNumberGeneratorTypeVM.PropertyChanged += (sender, args) =>
                                                            _simulationOptions.RandomNumberGeneratorType = _randomNumberGeneratorTypeVM.SelectedValue;
            _phaseFunctionTypeVM.PropertyChanged += (sender, args) =>
                                                    _simulationOptions.PhaseFunctionType = _phaseFunctionTypeVM.SelectedValue;
        }
예제 #3
0
        public SimulationInputViewModel(SimulationInput input)
        {
            _simulationInput = input; // use the property to invoke the appropriate change notification

            _simulationOptionsVM = new SimulationOptionsViewModel(_simulationInput.Options);
            _outputName          = input.OutputName;

#if WHITELIST
            TissueTypeVM = new OptionViewModel <string>("Tissue Type:", true, _simulationInput.TissueInput.TissueType, WhiteList.TissueTypes);
#else
            TissueTypeVM = new OptionViewModel <string>("Tissue Type:", true, _simulationInput.TissueInput.TissueType,
                                                        new[]
            {
                "MultiLayer",
                "SingleEllipsoid",
                "SingleVoxel"
            });
#endif
            UpdateTissueTypeVM(_simulationInput.TissueInput.TissueType);
            _simulationOptionsVM.PropertyChanged += (sender, args) =>
            {
                if (_simulationOptionsVM.TrackStatistics && _simulationOptionsVM.OutputFolder != null)
                {
                    _simulationInput.OutputName = Path.Combine(_simulationOptionsVM.OutputFolder, _outputName);
                }
            };
            _tissueTypeVM.PropertyChanged += (sender, args) =>
            {
                switch (_tissueTypeVM.SelectedValue)
                {
                case "MultiLayer":
                    _simulationInput.TissueInput = new MultiLayerTissueInput();
                    break;

                case "SingleEllipsoid":
                    _simulationInput.TissueInput = new SingleEllipsoidTissueInput();
                    break;

                case "SingleVoxel":
                    _simulationInput.TissueInput = new SingleVoxelTissueInput();
                    break;

                default:
                    throw new InvalidEnumArgumentException(StringLookup.GetLocalizedString("Error_NoTissueTypeExists"));
                }
                UpdateTissueTypeVM(_simulationInput.TissueInput.TissueType);
            };

            UpdateTissueInputVM(_simulationInput.TissueInput);
        }
예제 #4
0
        public PlotViewModel(int plotViewId = 0)
        {
            _plotViewId = plotViewId;
            _minYValue  = 1E-9;
            _maxYValue  = 1.0;
            _minXValue  = 1E-9;
            _maxXValue  = 1.0;
            _autoScaleX = true;
            _autoScaleY = true;

            RealLabels      = new List <string>();
            ImagLabels      = new List <string>();
            PhaseLabels     = new List <string>();
            AmplitudeLabels = new List <string>();

            Labels               = new List <string>();
            PlotTitles           = new List <string>();
            DataSeriesCollection = new List <DataPointCollection>();
            PlotSeriesCollection = new PlotPointCollection();

            PlotModel = new PlotModel
            {
                Title = "",
                // todo: Once the popout bug is fixed and we update OxyPlot, remove the line below:
                LegendPlacement = LegendPlacement.Outside,
                DefaultColors   = new List <OxyColor>
                {
                    OxyColor.FromRgb(0x00, 0x80, 0x00),     // Green
                    OxyColor.FromRgb(0xD6, 0x89, 0x10),     // Dark Orange
                    OxyColor.FromRgb(0xDC, 0x14, 0x3C),     // Crimson Red
                    OxyColor.FromRgb(0x00, 0x00, 0xFF),     // Blue
                    OxyColor.FromRgb(0xC4, 0x15, 0xC4),     // Dark Magenta
                    OxyColor.FromRgb(0x00, 0xBF, 0xBF),     // Turquoise
                    OxyColor.FromRgb(0x4F, 0x4F, 0x4F),     // Dark Grey
                    OxyColor.FromRgb(0x33, 0x99, 0xFF),     // Light Blue
                    OxyColor.FromRgb(0x80, 0x00, 0x00),     // Maroon
                    OxyColor.FromRgb(0x00, 0x80, 0x80),     // Teal
                    OxyColor.FromRgb(0x00, 0x00, 0x80),     // Navy Blue
                    OxyColor.FromRgb(0x99, 0x99, 0x00),     // Olive Green
                }
            };
            // todo: Once the popout bug is fixed and we update OxyPlot, uncomment this code:
            //var legend = new Legend
            //{
            //    LegendPlacement = LegendPlacement.Outside,
            //    IsLegendVisible = true
            //};
            //PlotModel.Legends.Add(legend);
            PlotType               = ReflectancePlotType.ForwardSolver;
            _holdOn                = true;
            _hideKey               = false;
            _showInPlotView        = true;
            _showAxes              = false;
            _showComplexPlotToggle = false;

            XAxisSpacingOptionVm = new OptionViewModel <ScalingType>("XAxisSpacing_" + _plotViewId, false);
            XAxisSpacingOptionVm.PropertyChanged += (sender, args) => UpdatePlotSeries();

            YAxisSpacingOptionVm = new OptionViewModel <ScalingType>("YAxisSpacing_" + _plotViewId, false);
            YAxisSpacingOptionVm.PropertyChanged += (sender, args) => UpdatePlotSeries();

            PlotToggleTypeOptionVm = new OptionViewModel <PlotToggleType>("ToggleType_" + _plotViewId, false);
            PlotToggleTypeOptionVm.PropertyChanged += (sender, args) => UpdatePlotSeries();

            PlotNormalizationTypeOptionVm =
                new OptionViewModel <PlotNormalizationType>("NormalizationType_" + _plotViewId, false);
            PlotNormalizationTypeOptionVm.PropertyChanged += (sender, args) => UpdatePlotSeries();

            CustomPlotLabel = "";

            PlotValues              = new RelayCommand <Array>(Plot_Executed);
            SetAxesLabels           = new RelayCommand <object>(Plot_SetAxesLabels_Executed);
            ClearPlotCommand        = new RelayCommand(() => Plot_Cleared(null, null));
            ClearPlotSingleCommand  = new RelayCommand(() => Plot_ClearedSingle(null, null));
            ExportDataToTextCommand = new RelayCommand(() => Plot_ExportDataToText_Executed(null, null));
            DuplicateWindowCommand  = new RelayCommand(() => Plot_DuplicateWindow_Executed(null, null));
        }
예제 #5
0
        public FluenceSolverViewModel()
        {
            RhoRangeVM = new RangeViewModel(new DoubleRange(0.1, 19.9, 100), StringLookup.GetLocalizedString("Measurement_mm"), IndependentVariableAxis.Rho, "");
            ZRangeVM   = new RangeViewModel(new DoubleRange(0.1, 19.9, 100), StringLookup.GetLocalizedString("Measurement_mm"), IndependentVariableAxis.Z, "");
            SourceDetectorSeparation = 10.0;
            TimeModulationFrequency  = 0.1;
            _tissueInputVM           = new OpticalPropertyViewModel(new OpticalProperties(),
                                                                    IndependentVariableAxisUnits.InverseMM.GetInternationalizedString(),
                                                                    StringLookup.GetLocalizedString("Heading_OpticalProperties"));

            // right now, we're doing manual databinding to the selected item. need to enable databinding
            // confused, though - do we need to use strings? or, how to make generics work with dependency properties?
            ForwardSolverTypeOptionVM = new OptionViewModel <ForwardSolverType>(
                "Forward Model",
                false,
                new[]
            {
                ForwardSolverType.DistributedPointSourceSDA,
                ForwardSolverType.PointSourceSDA,
                ForwardSolverType.DistributedGaussianSourceSDA,
                ForwardSolverType.TwoLayerSDA
            });     // explicitly enabling these for the workshop

            FluenceSolutionDomainTypeOptionVM = new FluenceSolutionDomainOptionViewModel(StringLookup.GetLocalizedString("Heading_FluenceSolutionDomain"),
                                                                                         FluenceSolutionDomainType.FluenceOfRhoAndZ);
            FluenceSolutionDomainTypeOptionVM.IsFluenceOfRhoAndZAndTimeEnabled = false;
            FluenceSolutionDomainTypeOptionVM.IsFluenceOfRhoAndZAndFtEnabled   = true;
            AbsorbedEnergySolutionDomainTypeOptionVM =
                new FluenceSolutionDomainOptionViewModel(StringLookup.GetLocalizedString("Heading_AbsorbedEnergySolutionDomain"),
                                                         FluenceSolutionDomainType.FluenceOfRhoAndZ);
            AbsorbedEnergySolutionDomainTypeOptionVM.IsFluenceOfRhoAndZAndTimeEnabled = false;
            AbsorbedEnergySolutionDomainTypeOptionVM.IsFluenceOfRhoAndZAndFtEnabled   = true;
            PhotonHittingDensitySolutionDomainTypeOptionVM =
                new FluenceSolutionDomainOptionViewModel(StringLookup.GetLocalizedString("Heading_PHDSolutionDomain"),
                                                         FluenceSolutionDomainType.FluenceOfRhoAndZ);
            PhotonHittingDensitySolutionDomainTypeOptionVM.IsFluenceOfRhoAndZAndTimeEnabled = false;
            PhotonHittingDensitySolutionDomainTypeOptionVM.IsFluenceOfRhoAndZAndFtEnabled   = true;
            PropertyChangedEventHandler updateSolutionDomain = (sender, args) =>
            {
                if (args.PropertyName == "IndependentAxisType")
                {
                    RhoRangeVM = ((FluenceSolutionDomainOptionViewModel)sender).IndependentAxesVMs[0].AxisRangeVM;
                }
                // todo: must this fire on ANY property, or is there a specific one we can listen to, as above?
                OnPropertyChanged("IsTimeFrequencyDomain");
            };

            FluenceSolutionDomainTypeOptionVM.PropertyChanged              += updateSolutionDomain;
            AbsorbedEnergySolutionDomainTypeOptionVM.PropertyChanged       += updateSolutionDomain;
            PhotonHittingDensitySolutionDomainTypeOptionVM.PropertyChanged += updateSolutionDomain;

            MapTypeOptionVM = new OptionViewModel <MapType>(
                "Map Type",
                new[]
            {
                MapType.Fluence,
                MapType.AbsorbedEnergy,
                MapType.PhotonHittingDensity
            });

            MapTypeOptionVM.PropertyChanged += (sender, args) =>
            {
                if (args.PropertyName == "SelectedValues")
                {
                    OnPropertyChanged("IsFluence");
                    OnPropertyChanged("IsAbsorbedEnergy");
                    OnPropertyChanged("IsPhotonHittingDensity");
                    OnPropertyChanged("IsTimeFrequencyDomain");
                    UpdateAvailableOptions();
                }
            };

            ForwardSolverTypeOptionVM.PropertyChanged += (sender, args) =>
            {
                OnPropertyChanged("ForwardSolver");
                OnPropertyChanged("IsGaussianForwardModel");
                OnPropertyChanged("IsMultiRegion");
                OnPropertyChanged("IsSemiInfinite");
                TissueInputVM = GetTissueInputVM(IsMultiRegion ? "MultiLayer" : "SemiInfinite");
                UpdateAvailableOptions();
                OnPropertyChanged("IsTimeFrequencyDomain");
            };

            ExecuteFluenceSolverCommand = new RelayCommand(() => ExecuteFluenceSolver_Executed(null, null));
            CancelFluenceSolverCommand  = new RelayCommand(() => CancelFluenceSolver_Executed(null, null));
            _canRunSolver    = true;
            _canCancelSolver = false;
        }
예제 #6
0
        public ForwardSolverViewModel()
        {
            _showOpticalProperties = true;
            _useSpectralPanelData  = false;


            _allRangeVMs = new[] { new RangeViewModel {
                                       Title = StringLookup.GetLocalizedString("IndependentVariableAxis_Rho")
                                   } };

#if WHITELIST
            ForwardSolverTypeOptionVM = new OptionViewModel <ForwardSolverType>("Forward Model", false, WhiteList.ForwardSolverTypes);
#else
            ForwardSolverTypeOptionVM = new OptionViewModel <ForwardSolverType>("Forward Model", false);
#endif
            SolutionDomainTypeOptionVM = new SolutionDomainOptionViewModel("Solution Domain", SolutionDomainType.ROfRho);

            ForwardAnalysisTypeOptionVM = new OptionViewModel <ForwardAnalysisType>(StringLookup.GetLocalizedString("Heading_ModelAnalysisOutput"), true);

            ForwardSolverTypeOptionVM.PropertyChanged += (sender, args) =>
            {
                OnPropertyChanged("IsGaussianForwardModel");
                OnPropertyChanged("IsMultiRegion");
                OnPropertyChanged("IsSemiInfinite");
                TissueInputVM = GetTissueInputVM(IsMultiRegion ? "MultiLayer" : "SemiInfinite");
                UpdateAvailableOptions();
            };
            ForwardSolverTypeOptionVM.SelectedValue = ForwardSolverType.PointSourceSDA; // force the model choice here?

            Action <double> updateSolutionDomainWithWavelength = wv =>
            {
                var wvAxis =
                    SolutionDomainTypeOptionVM.ConstantAxesVMs.FirstOrDefault(
                        axis => axis.AxisType == IndependentVariableAxis.Wavelength);
                if (wvAxis != null)
                {
                    wvAxis.AxisValue = wv;
                }
            };

            SolutionDomainTypeOptionVM.PropertyChanged += (sender, args) =>
            {
                if (args.PropertyName == "UseSpectralInputs")
                {
                    UseSpectralPanelData = SolutionDomainTypeOptionVM.UseSpectralInputs;
                }
                if (args.PropertyName == "IndependentAxesVMs")
                {
                    var useSpectralPanelDataAndNotNull = SolutionDomainTypeOptionVM.UseSpectralInputs &&
                                                         WindowViewModel.Current != null &&
                                                         WindowViewModel.Current.SpectralMappingVM != null;

                    AllRangeVMs =
                        (from i in
                         Enumerable.Range(0,
                                          SolutionDomainTypeOptionVM.IndependentVariableAxisOptionVM.SelectedValues.Length)
                         orderby i descending
                         // descending so that wavelength takes highest priority, then time/time frequency, then space/spatial frequency
                         select
                         useSpectralPanelDataAndNotNull&&
                         SolutionDomainTypeOptionVM.IndependentVariableAxisOptionVM.SelectedValues[i] ==
                         IndependentVariableAxis.Wavelength
                                    ? WindowViewModel.Current.SpectralMappingVM.WavelengthRangeVM
                         // bind to same instance, not a copy
                                    : SolutionDomainTypeOptionVM.IndependentAxesVMs[i].AxisRangeVM).ToArray();

                    // if the independent axis is wavelength, then hide optical properties (because they come from spectral panel)
                    ShowOpticalProperties =
                        _allRangeVMs.All(value => value.AxisType != IndependentVariableAxis.Wavelength);

                    // update solution domain wavelength constant if applicable
                    if (useSpectralPanelDataAndNotNull &&
                        SolutionDomainTypeOptionVM.ConstantAxesVMs.Any(
                            axis => axis.AxisType == IndependentVariableAxis.Wavelength))
                    {
                        updateSolutionDomainWithWavelength(WindowViewModel.Current.SpectralMappingVM.Wavelength);
                    }
                }
            };

            ExecuteForwardSolverCommand = new RelayCommand(() => ExecuteForwardSolver_Executed(null, null));

            OpticalPropertyVM.PropertyChanged += (sender, args) =>
            {
                //need to get the value from the checkbox in case UseSpectralPanelData has not yet been updated
                if (SolutionDomainTypeOptionVM != null)
                {
                    UseSpectralPanelData = SolutionDomainTypeOptionVM.UseSpectralInputs;
                }
                if (UseSpectralPanelData && WindowViewModel.Current != null &&
                    WindowViewModel.Current.SpectralMappingVM != null)
                {
                    updateSolutionDomainWithWavelength(WindowViewModel.Current.SpectralMappingVM.Wavelength);
                }
            };

            if (WindowViewModel.Current.SpectralMappingVM != null)
            {
                WindowViewModel.Current.SpectralMappingVM.PropertyChanged += (sender, args) =>
                {
                    if (args.PropertyName == "Wavelength")
                    {
                        //need to get the value from the checkbox in case UseSpectralPanelData has not yet been updated
                        if (SolutionDomainTypeOptionVM != null)
                        {
                            UseSpectralPanelData = SolutionDomainTypeOptionVM.UseSpectralInputs;
                        }
                        if (UseSpectralPanelData && WindowViewModel.Current != null &&
                            WindowViewModel.Current.SpectralMappingVM != null)
                        {
                            updateSolutionDomainWithWavelength(WindowViewModel.Current.SpectralMappingVM.Wavelength);
                        }
                    }

                    if (args.PropertyName == "OpticalProperties")
                    {
                        //need to get the value from the checkbox in case UseSpectralPanelData has not yet been updated
                        if (SolutionDomainTypeOptionVM != null)
                        {
                            UseSpectralPanelData = SolutionDomainTypeOptionVM.UseSpectralInputs;
                        }
                        if (UseSpectralPanelData && WindowViewModel.Current != null &&
                            WindowViewModel.Current.SpectralMappingVM != null)
                        {
                            if (IsMultiRegion && MultiRegionTissueVM != null)
                            {
                                MultiRegionTissueVM.RegionsVM.ForEach(region =>
                                                                      ((dynamic)region).OpticalPropertyVM.SetOpticalProperties(
                                                                          WindowViewModel.Current.SpectralMappingVM.OpticalProperties));
                            }
                            else if (OpticalPropertyVM != null)
                            {
                                OpticalPropertyVM.SetOpticalProperties(
                                    WindowViewModel.Current.SpectralMappingVM.OpticalProperties);
                            }
                        }
                    }
                };
            }
        }
        public SpectralMappingViewModel()
        {
#if WHITELIST
            ScatteringTypeVM = new OptionViewModel <ScatteringType>(StringLookup.GetLocalizedString("Heading_ScattererType"), true, WhiteList.ScatteringTypes);
#else
            ScatteringTypeVM = new OptionViewModel <ScatteringType>(StringLookup.GetLocalizedString("Heading_ScattererType"), true);
#endif
            ScatteringTypeVM.PropertyChanged += (sender, args) =>
            {
                if (args.PropertyName == "SelectedValue" && SelectedTissue != null)
                {
                    SelectedTissue.Scatterer = SolverFactory.GetScattererType(ScatteringTypeVM.SelectedValue);
                    if (SelectedTissue.Scatterer is INotifyPropertyChanged bindableScatterer)
                    {
                        bindableScatterer.PropertyChanged += (s, a) => UpdateOpticalProperties();
                    }
                    ScatteringTypeName = SelectedTissue.Scatterer.GetType().FullName;
                    // Set the tissue type again for skin so it will set the PowerLaw A and B values correctly
                    if (SelectedTissue.ScattererType == ScatteringType.PowerLaw && SelectedTissue.TissueType != TissueType.IntralipidPhantom)
                    {
                        var myScatterer = (PowerLawScatterer)SelectedTissue.Scatterer;
                        myScatterer.SetTissueType(SelectedTissue.TissueType);
                    }
                }
                OnPropertyChanged("Scatterer");
                UpdateOpticalProperties();
            };

            WavelengthRangeVM = new RangeViewModel(new DoubleRange(650.0, 1000.0, 36), StringLookup.GetLocalizedString("Measurement_nm"),
                                                   IndependentVariableAxis.Wavelength, StringLookup.GetLocalizedString("Heading_WavelengthRange"));

            Tissues = new List <Tissue>
            {
                new Tissue(TissueType.Skin),
                new Tissue(TissueType.BrainWhiteMatter),
                new Tissue(TissueType.BrainGrayMatter),
                new Tissue(TissueType.BreastPreMenopause),
                new Tissue(TissueType.BreastPostMenopause),
                new Tissue(TissueType.Liver),
                new Tissue(TissueType.IntralipidPhantom),
                //new Tissue(TissueType.PolystyreneSpherePhantom),
                new Tissue(TissueType.Custom)
            };

            BloodConcentrationVM = new BloodConcentrationViewModel();

            #region DC notes 1

            // DC NOTES on how to propagate the correct hemoglobin instances into BloodConcentrationVM:
            // Upon setting SelectedTissue (below), we internally update the BloodConcentrationVM hemoglobin references
            // This is the simplest solution, but maybe violates SOC...(see SelectedTissue property for details)
            // A second alternative way would be to override AfterPropertyChanged (see AfterPropertyChanged method below)

            #endregion

            BloodConcentrationVM.PropertyChanged += (sender, args) => UpdateOpticalProperties();

            SelectedTissue = Tissues.First();
            ScatteringTypeVM.SelectedValue = SelectedTissue.ScattererType;
            // forces update to all bindings established in hanlder for ScatteringTypeVM.PropertyChanged above
            ScatteringTypeName = SelectedTissue.Scatterer.GetType().FullName;

            OpticalProperties = new OpticalProperties(0.01, 1, 0.8, 1.4);
            Wavelength        = 650;

            ResetConcentrations     = new RelayCommand <object>(ResetConcentrations_Executed);
            UpdateWavelength        = new RelayCommand <object>(UpdateWavelength_Executed);
            PlotMuaSpectrumCommand  = new RelayCommand(PlotMuaSpectrum_Executed);
            PlotMuspSpectrumCommand = new RelayCommand(PlotMuspSpectrum_Executed);
        }
        public InverseSolverViewModel()
        {
            _showOpticalProperties = true;
            _useSpectralPanelData  = false;

            _allRangeVMs = new[] { new RangeViewModel {
                                       Title = Strings.IndependentVariableAxis_Rho
                                   } };

            SolutionDomainTypeOptionVM = new SolutionDomainOptionViewModel("Solution Domain", SolutionDomainType.ROfRho);
            SolutionDomainTypeOptionVM.EnableMultiAxis = false;
            SolutionDomainTypeOptionVM.AllowMultiAxis  = false;

            Action <double> updateSolutionDomainWithWavelength = wv =>
            {
                var wvAxis =
                    SolutionDomainTypeOptionVM.ConstantAxesVMs.FirstOrDefault(
                        axis => axis.AxisType == IndependentVariableAxis.Wavelength);
                if (wvAxis != null)
                {
                    wvAxis.AxisValue = wv;
                }
            };

            SolutionDomainTypeOptionVM.PropertyChanged += (sender, args) =>
            {
                if (args.PropertyName == "UseSpectralInputs")
                {
                    UseSpectralPanelData = SolutionDomainTypeOptionVM.UseSpectralInputs;
                }
                if (args.PropertyName == "IndependentAxesVMs")
                {
                    var useSpectralPanelDataAndNotNull = UseSpectralPanelData && WindowViewModel.Current != null &&
                                                         WindowViewModel.Current.SpectralMappingVM != null;

                    AllRangeVMs =
                        (from i in
                         Enumerable.Range(0,
                                          SolutionDomainTypeOptionVM.IndependentVariableAxisOptionVM.SelectedValues.Length)
                         orderby i descending
                         // descending so that wavelength takes highest priority, then time/time frequency, then space/spatial frequency
                         select
                         useSpectralPanelDataAndNotNull&&
                         SolutionDomainTypeOptionVM.IndependentVariableAxisOptionVM.SelectedValues[i] ==
                         IndependentVariableAxis.Wavelength
                                    ? WindowViewModel.Current.SpectralMappingVM.WavelengthRangeVM
                         // bind to same instance, not a copy
                                    : SolutionDomainTypeOptionVM.IndependentAxesVMs[i].AxisRangeVM).ToArray();

                    // if the independent axis is wavelength, then hide optical properties (because they come from spectral panel)
                    ShowOpticalProperties =
                        !_allRangeVMs.Any(value => value.AxisType == IndependentVariableAxis.Wavelength);

                    // update solution domain wavelength constant if applicable
                    if (useSpectralPanelDataAndNotNull &&
                        SolutionDomainTypeOptionVM.ConstantAxesVMs.Any(
                            axis => axis.AxisType == IndependentVariableAxis.Wavelength))
                    {
                        updateSolutionDomainWithWavelength(WindowViewModel.Current.SpectralMappingVM.Wavelength);
                    }
                }
            };

#if WHITELIST
            MeasuredForwardSolverTypeOptionVM = new OptionViewModel <ForwardSolverType>(
                "Forward Model Engine", false, WhiteList.InverseForwardSolverTypes);
#else
            MeasuredForwardSolverTypeOptionVM = new OptionViewModel <ForwardSolverType>(
                "Forward Model Engine", false); //These titles are not diplayed so we can hard-code strings
#endif

#if WHITELIST
            InverseForwardSolverTypeOptionVM = new OptionViewModel <ForwardSolverType>("Inverse Model Engine", false, WhiteList.InverseForwardSolverTypes);
#else
            InverseForwardSolverTypeOptionVM = new OptionViewModel <ForwardSolverType>("Inverse Model Engine", false); //These titles are not diplayed so we can hard-code strings
#endif
            InverseForwardSolverTypeOptionVM.PropertyChanged += (sender, args) =>
                                                                OnPropertyChanged("InverseForwardSolver");

            OptimizerTypeOptionVM = new OptionViewModel <OptimizerType>(StringLookup.GetLocalizedString("Heading_OptimizerType"), true);
            OptimizerTypeOptionVM.PropertyChanged += (sender, args) =>
                                                     OnPropertyChanged("Optimizer");

            InverseFitTypeOptionVM = new OptionViewModel <InverseFitType>(StringLookup.GetLocalizedString("Heading_OptimizationParameters"), true);

            MeasuredOpticalPropertyVM = new OpticalPropertyViewModel {
                Title = ""
            };
            InitialGuessOpticalPropertyVM = new OpticalPropertyViewModel {
                Title = ""
            };
            ResultOpticalPropertyVM = new OpticalPropertyViewModel {
                Title = ""
            };

            SimulateMeasuredDataCommand  = new RelayCommand(() => SimulateMeasuredDataCommand_Executed(null, null));
            CalculateInitialGuessCommand = new RelayCommand(() => CalculateInitialGuessCommand_Executed(null, null));
            SolveInverseCommand          = new RelayCommand(() => SolveInverseCommand_Executed(null, null));

            if (WindowViewModel.Current.SpectralMappingVM != null)
            {
                WindowViewModel.Current.SpectralMappingVM.PropertyChanged += (sender, args) =>
                {
                    if (args.PropertyName == "Wavelength")
                    {
                        //need to get the value from the checkbox in case UseSpectralPanelData has not yet been updated
                        if (SolutionDomainTypeOptionVM != null)
                        {
                            UseSpectralPanelData = SolutionDomainTypeOptionVM.UseSpectralInputs;
                        }
                        if (UseSpectralPanelData && WindowViewModel.Current != null &&
                            WindowViewModel.Current.SpectralMappingVM != null)
                        {
                            updateSolutionDomainWithWavelength(WindowViewModel.Current.SpectralMappingVM.Wavelength);
                        }
                    }

                    if (args.PropertyName == "OpticalProperties")
                    {
                        //need to get the value from the checkbox in case UseSpectralPanelData has not yet been updated
                        if (SolutionDomainTypeOptionVM != null)
                        {
                            UseSpectralPanelData = SolutionDomainTypeOptionVM.UseSpectralInputs;
                        }
                        if (UseSpectralPanelData && WindowViewModel.Current != null &&
                            WindowViewModel.Current.SpectralMappingVM != null &&
                            MeasuredOpticalPropertyVM != null)
                        {
                            MeasuredOpticalPropertyVM.SetOpticalProperties(
                                WindowViewModel.Current.SpectralMappingVM.OpticalProperties);
                        }
                    }
                };
            }
        }