Changeset - 365cbd130bf2
[Not reviewed]
Tess Snider (Malkyne) - 3 years ago 2021-07-26 09:20:37
this@malkyne.org
Now correctly clearing buffer reagent selections when doing a test Clear.
Also, marking inert now immediately saves, because a verification step
is not necessary or useful, in this case.
3 files changed with 10 insertions and 5 deletions:
0 comments (0 inline, 0 general)
Models/ReactionTest.cs
Show inline comments
...
 
@@ -234,113 +234,118 @@ namespace DesertPaintCodex.Models
 
                            {
 
                                // White-shift up clip.
 
                                extrapolated                    = ExtrapolateWhiteFromOneChannel(_observedColor.Red,   255, reaction.Red);
 
                                if (!extrapolated) extrapolated = ExtrapolateWhiteFromOneChannel(_observedColor.Green, 255, reaction.Green);
 
                                if (!extrapolated)                ExtrapolateWhiteFromOneChannel(_observedColor.Blue,  255, reaction.Blue);
 
                            }
 
                        }
 

	
 
                        if (!extrapolated)
 
                        {
 
                            State       = TestState.ClippedResult;
 
                            BadReaction = CalculateReaction();
 
                        }
 
                    }
 
                    
 
                    PlayerProfile? profile = ProfileManager.CurrentProfile;
 
                    profile?.SetPairClipStatus(Reagent1, Reagent2, Clipped);
 
                }
 
            }
 
            else
 
            {
 
                Debug.WriteLine("ERROR: Lab UI not found.");
 
                State = TestState.LabNotFound;
 
            }
 
        }
 

	
 
        private bool ExtrapolateWhiteFromOneChannel(int result, int clipBound, int reaction)
 
        {
 
            if (result == clipBound) return false;
 
            
 
            Clipped = ClipType.None;
 
            State = TestState.GoodResult;
 
            Reaction = new Reaction(reaction, reaction, reaction);
 

	
 
            return true;
 
        }
 

	
 

	
 
        public void CancelScan()
 
        {
 
            ReactionScannerService.Instance.CancelScan();
 
            State = TestState.Untested;
 
        }
 

	
 
        public void MarkInert()
 
        {
 
            Reaction = new Reaction(0, 0, 0);
 
            State = TestState.GoodResult;
 
            SaveReaction();
 
        }
 

	
 
        public void ClearReaction()
 
        {
 
            PlayerProfile? profile = ProfileManager.CurrentProfile;
 
            if (profile == null) return;
 
            profile.Reactions.Remove(Reagent1, Reagent2);
 
            profile.SetPairClipStatus(Reagent1, Reagent2, ClipType.None);
 
            if (State == TestState.Saved)
 
            {
 
                profile.Save();
 
            }
 
            
 
            BufferReagentFirst = null;
 
            BufferReagentLast = null;
 
            
 
            Reaction = null;
 
            BadReaction = null;
 
            Clipped = ClipType.None;
 
            State = TestState.Untested;
 
            
 
            UpdateRecipe();
 
        }
 

	
 
        public void SaveReaction()
 
        {
 
            PlayerProfile? profile = ProfileManager.CurrentProfile;
 
            if (profile == null) return;
 
            profile.Reactions.Set(Reagent1, Reagent2, Reaction);
 
            profile.Save();
 
            State = TestState.Saved;
 
        }
 

	
 
        #endregion
 
        
 
        #region Internals
 

	
 
        private Reaction? CalculateReaction()
 
        {
 
            if (ProfileManager.CurrentProfile == null) return null;
 
            if (HypotheticalColor == null) return null;
 
            if (ObservedColor == null) return null;
 

	
 
            if (_useFirstBuffer)
 
            {
 
                if (BufferReagentFirst != null)
 
                {
 
                    return ReactionScannerService.Calculate3WayReaction(ProfileManager.CurrentProfile, HypotheticalColor,
 
                        ObservedColor, BufferReagentFirst, Reagent1, Reagent2, true);
 
                }
 
            }
 
            else
 
            {
 
                if (BufferReagentLast != null)
 
                {
 
                    return ReactionScannerService.Calculate3WayReaction(ProfileManager.CurrentProfile, HypotheticalColor,
 
                        ObservedColor, Reagent1, Reagent2, BufferReagentLast, false);
 
                }
 
            }
 
            
 
            return ReactionScannerService.CalculateReaction(HypotheticalColor, ObservedColor);
 
        }
 

	
 

	
 
        private void UpdateHypotheticalColor()
 
        {
 
            HypotheticalColor = null;
 
            HypotheticalColor = (IsAllCatalysts && !BufferIsSelected) ? null : _recipe.BaseColor;
 
        }
ViewModels/ExperimentLogViewModel.cs
Show inline comments
...
 
@@ -16,96 +16,97 @@ namespace DesertPaintCodex.ViewModels
 
        public ObservableCollection<ReactionTest> CompletedTests { get; } = new();
 

	
 
        private int _selectedList = 0;
 
        public int SelectedList { get => _selectedList; private set => this.RaiseAndSetIfChanged(ref _selectedList, value); }
 

	
 
        private int _selectedRemainingTest = 0;
 
        public int SelectedRemainingTest { get => _selectedRemainingTest; private set => this.RaiseAndSetIfChanged(ref _selectedRemainingTest, value); }
 
        
 
        private int _selectedCompletedTest = 0;
 
        public int SelectedCompletedTest { get => _selectedCompletedTest; private set => this.RaiseAndSetIfChanged(ref _selectedCompletedTest, value); }
 

	
 
        private ReactionTestViewModel _testView;
 
        public ReactionTestViewModel TestView { get => _testView; private set => this.RaiseAndSetIfChanged(ref _testView, value); }
 
        
 
        private readonly List<ObservableCollection<ReactionTest>> _testLists = new();
 

	
 
        public ExperimentLogViewModel()
 
        {
 
            PlayerProfile? profile = ProfileManager.CurrentProfile;
 
            Debug.Assert(profile != null);
 
            
 
            _testLists.Add(RemainingTests);
 
            _testLists.Add(CompletedTests);
 
            
 
            ReactionTestService.PopulateRemainingTests(RemainingTests);
 
            ReactionTestService.PopulateCompletedTests(CompletedTests);
 
            
 
            // If we have no remaining tests, switch to the completed list.
 
            _testView = new ReactionTestViewModel();
 
            if (RemainingTests.Count > 0)
 
            {
 
                SelectedList = 0;
 
                _testView.ReactionTest = RemainingTests[0];
 
            }
 
            else
 
            {
 
                SelectedList = 1;
 
                _testView.ReactionTest = CompletedTests[0];
 
            }
 

	
 
            this.WhenAnyPropertyChanged(nameof(SelectedList), nameof(SelectedRemainingTest), nameof(SelectedCompletedTest))
 
                .Subscribe(_ => _testView.ReactionTest = GetSelectedReactionTest());
 

	
 
            _testView.ReactionTest = GetSelectedReactionTest();
 

	
 
            TestView.SaveReaction.Subscribe(_ => OnSaveReaction());
 
            TestView.ClearReaction.Subscribe(_ => OnClearReaction());
 
            TestView.FinalizeTestResults.Subscribe(_ => OnReactionResults());
 
            TestView.MarkInert.Subscribe(_ => OnSaveReaction());
 
        }
 

	
 
        private ReactionTest GetSelectedReactionTest()
 
        {
 
            int itemIndex = (SelectedList == 0) ? SelectedRemainingTest : SelectedCompletedTest;
 
            if (itemIndex < 0) return Constants.StubReactionTest;
 
            var list = _testLists[SelectedList];
 
            return (itemIndex >= list.Count) ? Constants.StubReactionTest : list[itemIndex];
 
        }
 
        
 
        #region Command Handlers
 

	
 
        private void OnSaveReaction()
 
        {
 
            ReactionTest test = TestView.ReactionTest;
 
            int oldPos = RemainingTests.IndexOf(test);
 
            
 
            // Move test to Completed Tests.
 
            InsertTestIntoList(test, CompletedTests);
 
            RemainingTests.RemoveAt(oldPos);
 
            
 
            // Select next Remaining Test.
 
            if (RemainingTests.Count == 0) return;
 
            SelectedRemainingTest = (oldPos == RemainingTests.Count) ? RemainingTests.Count - 1 : oldPos;
 

	
 
            // If we have just inserted our first completed test, select it.
 
            if (CompletedTests.Count == 1)
 
            {
 
                 SelectedCompletedTest = 0;
 
            }
 
        }
 

	
 
        private void OnClearReaction()
 
        {
 
            int oldPos = 0;
 
            ReactionTest test = TestView.ReactionTest;
 
            if (RemainingTests.Contains(test))
 
            {
 
                SelectedRemainingTest = ResortTestInList(test, RemainingTests);
 
                return;
 
            }
 

	
 
            oldPos = CompletedTests.IndexOf(test);
 
            
 
            // Move test to Remaining Tests.
 
            InsertTestIntoList(test, RemainingTests);
 
            CompletedTests.RemoveAt(oldPos);
 
            
ViewModels/ReactionTestViewModel.cs
Show inline comments
 
using System;
 
using System.Collections.Generic;
 
using System.Collections.ObjectModel;
 
using System.Diagnostics;
 
using System.Linq;
 
using System.Reactive;
 
using System.Reactive.Linq;
 
using System.Threading.Tasks;
 
using DesertPaintCodex.Models;
 
using DesertPaintCodex.Services;
 
using DesertPaintCodex.Util;
 
using ReactiveUI;
 

	
 
namespace DesertPaintCodex.ViewModels
 
{
 
    public class ReactionTestViewModel : ViewModelBase
 
    {
 
        private ReactionTest _reactionTest = Constants.StubReactionTest;
 
        public ReactionTest ReactionTest
 
        {
 
            get => _reactionTest;
 
            set => this.RaiseAndSetIfChanged(ref _reactionTest, value);
 
        }
 

	
 
        private readonly List<Reagent> _allPigmentList = new();
 
        private readonly List<Reagent> _allReagentList = new();
 
        public ObservableCollection<Reagent> FirstBufferList { get; } = new();
 
        public ObservableCollection<Reagent> LastBufferList { get; } = new();
 

	
 
        public ReactionTestViewModel()
 
        {
 
            List<string> reagentNames = ReagentService.Names;
 
            
 
            foreach (var reagent in reagentNames.Select(ReagentService.GetReagent))
 
            {
 
                if (!reagent.IsCatalyst)
 
                { 
 
                    _allPigmentList.Add(reagent);
 
                }
 
                _allReagentList.Add(reagent);
 
            }
 

	
 
            this.WhenAnyValue(x => x.ReactionTest)
 
                .Subscribe(_ => UpdateDerivedState());
 
            
 
            ShowScreenSettingsDialog = new Interaction<ScreenSettingsViewModel, Unit>();
 
            SaveReaction = ReactiveCommand.Create(() => ReactionTest.SaveReaction());
 
            ClearReaction = ReactiveCommand.Create(() => ReactionTest.ClearReaction());
 
            MarkInert = ReactiveCommand.Create(() => ReactionTest.MarkInert());
 
            FinalizeTestResults = ReactiveCommand.Create(Test);
 
        }
 

	
 
        private void UpdateDerivedState()
 
        {
 
            // There are more "reactive" ways to pull this off, but I don't have time to figure out all those recipes
 
            // right now.
 
            SetupTest();
 
        }
 
            
 
        
 
        private void SetupTest()
 
        {
 
            if (ReactionTest.Requires3Way)
 
            {
 
                FilterBufferPigments();
 
            }
 
        }
 

	
 
        private void FilterBufferPigments()
 
        {
 
            // Avalonia doesn't really have proper filtering for listy controls yet.
 
            PlayerProfile? profile = ProfileManager.CurrentProfile;
 
            if (profile == null) return;
 
            ReactionSet reactions = profile.Reactions;
 
            List<Reagent> bufferList = ReactionTest.IsAllCatalysts ? _allPigmentList : _allReagentList;
 
            foreach (Reagent buffer in bufferList)
 
            {
 
                if (buffer == ReactionTest.Reagent1) continue;
 
                if (buffer == ReactionTest.Reagent2) continue;
 

	
 

	
 
                if ((reactions.Find(buffer, ReactionTest.Reagent1) != null)
 
                    && (reactions.Find(buffer, ReactionTest.Reagent2) != null))
 
                {
 
                    FirstBufferList.Add(buffer);
 
                }
 

	
 
                if ((reactions.Find(ReactionTest.Reagent1, buffer) != null)
 
                    && (reactions.Find(ReactionTest.Reagent2, buffer) != null))
 
                {
 
                    LastBufferList.Add(buffer);
 
                }
 
            }
 

	
 
            // BufferPigmentList.AddRange(_allPigmentList.Where(pigment =>
 
            //         pigment != ReactionTest.Reagent1 && pigment != ReactionTest.Reagent2));
 
        }
 

	
 
        public async void Analyze()
 
        {
 
            Debug.WriteLine("Analyze");
 
            try
 
            {
 
                await ReactionTest.StartScan();
 
                await FinalizeTestResults.Execute();
 
            }
 
            catch (OperationCanceledException)
 
            {
 
                Debug.WriteLine("Scan canceled.");
 
            }
 
        }
 

	
 
        public void MarkInert()
 
        {
 
            ReactionTest.MarkInert();
 
        }
 

	
 
        public void CancelScan()
 
        {
 
            ReactionTest.CancelScan();
 
        }
 

	
 
        public async Task OpenScreenSettings()
 
        {
 
            await ShowScreenSettingsDialog.Handle(new ScreenSettingsViewModel());
 
        }
 

	
 
        public async Task ShowClipInfo()
 
        {
 
            await ShowInfoBox("Clipped Reactions", "The Pigment Lab is only capable of displaying color channel values in the 0-255 range. However, sometimes, your reactions will push one or more channels outside of that range. When that happens, the value will be clamped either to 0 or 255. In this case, we say that the reaction was \"Clipped.\"\n\nThis is nothing to panic about, though. We solve this issue by using a third (buffer) reagent to offset the extreme value, so that it falls within measurable range, similar to how we test Catalyst+Catalyst reactions. Desert Paint Codex will automatically do the math for this, but it does move clipped reactions towards the end of your test list, because you'll want any reactions between the buffer reagent and your two test reagents already known, prior to running your buffered test.");
 
        }
 
        
 
        public void UseFirstBuffer()
 
        {
 
            ReactionTest.UseFirstBuffer = true;
 
        }
 

	
 
        public void UseLastBuffer()
 
        {
 
            ReactionTest.UseFirstBuffer = false;
 
        }
 

	
 
        private void Test()
 
        {
 
            Debug.WriteLine("Test complete");
 
        }
 
        
 
        public Interaction<ScreenSettingsViewModel, Unit> ShowScreenSettingsDialog { get; }
 

	
 
        public ReactiveCommand<Unit, Unit> ClearReaction { get; }
 
        public ReactiveCommand<Unit, Unit> SaveReaction { get; }
 
        
 
        public ReactiveCommand<Unit, Unit> MarkInert { get; }
 
        
 
        public ReactiveCommand<Unit, Unit> FinalizeTestResults { get; }
 
    }
 
}
...
 
\ No newline at end of file
0 comments (0 inline, 0 general)