Changeset - 95e1ae426eb3
[Not reviewed]
default
0 4 0
Jason Maltzen - 12 months ago 2023-12-07 05:55:08
jason@hiddenachievement.com
Add an info box when a detected clipped reaction could be extrapolated from a white shift.
4 files changed with 21 insertions and 3 deletions:
0 comments (0 inline, 0 general)
Models/ReactionTest.cs
Show inline comments
 
using System;
 
using System.ComponentModel;
 
using System.Diagnostics;
 
using System.Runtime.CompilerServices;
 
using System.Threading.Tasks;
 
using DesertPaintCodex.Services;
 
using JetBrains.Annotations;
 

	
 
namespace DesertPaintCodex.Models
 
{
 
    public class ReactionTest : INotifyPropertyChanged, IProgress<float>, IComparable<ReactionTest>
 
    {
 
        public enum TestState
 
        {
 
            Unset = -1,
 
            Untested,
 
            Scanning,
 
            LabNotFound,
 
            ClippedResult,
 
            GoodResult,
 
            Saved
 
        }
 

	
 
        public Reagent Reagent1 { get; }
 
        public Reagent Reagent2 { get; }
 

	
 
        private Reagent? _bufferReagentFirst;
 
        public Reagent? BufferReagentFirst
 
        {
 
            get => _bufferReagentFirst;
 
            set
 
            {
 
                if (_bufferReagentFirst == value) return;
 
                _bufferReagentFirst = value;
 
                
 
                UpdateRecipe();
 
                
 
                NotifyPropertyChanged(nameof(BufferReagentFirst));
 
                NotifyPropertyChanged(nameof(CanScan));
 
            }
 
        }
 
        
 
        private Reagent? _bufferReagentLast;
 
        public Reagent? BufferReagentLast
 
        {
 
            get => _bufferReagentLast;
 
            set
 
            {
 
                if (_bufferReagentLast == value) return;
 
                _bufferReagentLast = value;
 

	
 
                UpdateRecipe();
 

	
 
                NotifyPropertyChanged(nameof(BufferReagentLast));
 
                NotifyPropertyChanged(nameof(CanScan));
 
            }
 
        }
 

	
 
        private bool _useFirstBuffer;
 

	
 
        public bool UseFirstBuffer
 
        {
 
            get => _useFirstBuffer;
 
            set
 
            {
 
                _useFirstBuffer = value;
 
                UpdateRecipe();
 
                NotifyPropertyChanged(nameof(UseFirstBuffer));
 
                NotifyPropertyChanged(nameof(CanScan));
 
                NotifyPropertyChanged(nameof(ShowFirstBuffer));
 
                NotifyPropertyChanged(nameof(ShowLastBuffer));
 
            }
 
        }
 

	
 
        private ClipType _clipType;
 
        public ClipType Clipped {
 
            get => _clipType;
 
            set
 
            {
 
                _clipType = value;
 
                NotifyPropertyChanged(nameof(Clipped));
 
            }
 
        }
 

	
 
        private bool _whiteShift;
 
        public bool WhiteShift
 
        {
 
            get => _whiteShift;
 
            set
 
            {
 
                _whiteShift = value;
 
                NotifyPropertyChanged(nameof(WhiteShift));
 
            }
 
        }
 

	
 
        public bool IsAllCatalysts { get; }
 

	
 
        private Reaction? _reaction;
 
        public Reaction? Reaction { get => _reaction; set { _reaction = value; NotifyPropertyChanged(nameof(Reaction)); } }
 

	
 
        private Reaction? _badReaction;
 
        public Reaction? BadReaction { get => _badReaction; set { _badReaction = value; NotifyPropertyChanged(nameof(BadReaction)); } }
 

	
 
        private int _scanProgress;
 
        public int ScanProgress { get => _scanProgress; set { _scanProgress = value; NotifyPropertyChanged(nameof(ScanProgress)); } }
 
        
 
        
 
        private TestState _state;
 

	
 
        public TestState State
 
        {
 
            get => _state;
 
            set
 
            {
 
                _state = value;
 
                NotifyPropertyChanged(nameof(State));
 
                NotifyPropertyChanged(nameof(Requires3Way));
 
                NotifyPropertyChanged(nameof(CanScan));
 
                NotifyPropertyChanged(nameof(IsScanning));
 
                NotifyPropertyChanged(nameof(HasResults));
 
                NotifyPropertyChanged(nameof(HasReaction));
 
                NotifyPropertyChanged(nameof(CanClear));
 
                NotifyPropertyChanged(nameof(CanSave));
 
                NotifyPropertyChanged(nameof(NoLab));
 
                NotifyPropertyChanged(nameof(CanPickBuffer));
 
                NotifyPropertyChanged(nameof(ShowFirstBuffer));
 
                NotifyPropertyChanged(nameof(ShowLastBuffer));
 
            }
 
        }
 

	
 
        public bool Requires3Way =>
 
            (State == TestState.ClippedResult) || IsAllCatalysts;
 

	
 
        public bool CanScan => (State is TestState.Untested or TestState.LabNotFound) || ((State == TestState.ClippedResult) && BufferIsSelected);
 
        
 
        public bool IsScanning => State == TestState.Scanning;
 

	
 
        public bool HasResults => (ObservedColor != null) && (State is TestState.ClippedResult or TestState.GoodResult or TestState.LabNotFound);
 

	
 
        public bool HasReaction => State is TestState.ClippedResult or TestState.GoodResult or TestState.Saved;
 
        
 
        public bool CanClear => State is TestState.ClippedResult or TestState.GoodResult or TestState.Saved;
 
        
 
        public bool CanSave => State == TestState.GoodResult;
 

	
 
        public bool NoLab => State == TestState.LabNotFound;
 

	
 
        public bool CanPickBuffer => (State == TestState.ClippedResult) || IsAllCatalysts;
 

	
 
        public bool ShowFirstBuffer => Requires3Way && UseFirstBuffer;
 

	
 
        public bool ShowLastBuffer => Requires3Way && !UseFirstBuffer;
 

	
 

	
 
        private PaintColor? _hypotheticalColor;
 
        public PaintColor? HypotheticalColor { get => _hypotheticalColor; set { _hypotheticalColor = value; NotifyPropertyChanged(nameof(HypotheticalColor)); } }
 

	
 
        private PaintColor? _observedColor;
 
        public PaintColor? ObservedColor { get => _observedColor; set { _observedColor = value; NotifyPropertyChanged(nameof(ObservedColor)); } }
 

	
 
        private readonly PaintRecipe _recipe = new();
 

	
 
        public bool IsStub { get; }
 

	
 

	
 
        public ReactionTest(Reagent reagent1, Reagent reagent2, Reaction? reaction, ClipType clipType, bool isStub = false)
 
        public ReactionTest(Reagent reagent1, Reagent reagent2, Reaction? reaction, ClipType clipType, bool whiteShift, bool isStub = false)
 
        {
 
            Reagent1 = reagent1;
 
            Reagent2 = reagent2;
 
            UseFirstBuffer = true;
 
            IsAllCatalysts = reagent1.IsCatalyst && reagent2.IsCatalyst;
 
            Clipped = clipType;
 
            WhiteShift = whiteShift;
 
            Reaction = reaction;
 
            State = (reaction != null) ? TestState.Saved :
 
                (clipType == ClipType.None) ? TestState.Untested : TestState.ClippedResult;
 
            _recipe.AddReagent(reagent1.Name);
 
            _recipe.AddReagent(reagent2.Name);
 
            IsStub = isStub;
 
            UpdateHypotheticalColor();
 
        }
 

	
 
        
 
        #region Actions
 
        public async Task StartScan()
 
        {
 
            Clipped = ClipType.None;
 
            ScanProgress = 0;
 
            Reaction = null;
 
            BadReaction = null;
 
            State = TestState.Scanning;
 
            bool testing3Way = Requires3Way && BufferIsSelected;
 
            bool foundLab = await ReactionScannerService.Instance.CaptureReactionAsync(this);
 
            if (foundLab)
 
            {
 
                ObservedColor = ReactionScannerService.Instance.RecordedColor;
 
                if (_observedColor != null)
 
                {
 
                    Clipped = _observedColor.Red switch
 
                        {
 
                            0   => ClipType.RedLow,
 
                            255 => ClipType.RedHigh,
 
                            _   => ClipType.None
 
                        }
 
                        | _observedColor.Green switch
 
                        {
 
                            0   => ClipType.GreenLow,
 
                            255 => ClipType.GreenHigh,
 
                            _   => ClipType.None
 
                        }
 
                        | _observedColor.Blue switch
 
                        {
 
                            0   => ClipType.BlueLow,
 
                            255 => ClipType.BlueHigh,
 
                            _   => ClipType.None
 
                        };
 

	
 
                    if (Clipped == ClipType.None)
 
                    {
 
                        State = TestState.GoodResult;
 
                        Reaction = CalculateReaction();
 
                    }
 
                    else
 
                    {
 
                        Reaction? reaction = CalculateReaction();
 
                        bool extrapolated = false;
 

	
 
                        // SPECIAL CASE:
 
                        // Check to see if we've got a white-shift reaction that's partially clipped, where we can
 
                        // still extrapolate the reaction, based on the available information.
 
                        bool isWhiteShift = (reaction != null) && Clipped switch
 
                        {
 
                            ClipType.RedLow => (reaction.Blue == reaction.Green) && (reaction.Blue != 0),
 
                            ClipType.RedHigh => (reaction.Blue == reaction.Green) && (reaction.Blue != 0),
 
                            ClipType.BlueLow => (reaction.Red == reaction.Green) && (reaction.Red != 0),
 
                            ClipType.BlueHigh => (reaction.Red == reaction.Green) && (reaction.Red != 0),
 
                            ClipType.GreenLow => (reaction.Red == reaction.Blue) && (reaction.Red != 0),
 
                            ClipType.GreenHigh => (reaction.Red == reaction.Blue) && (reaction.Red != 0),
 
                            ClipType.RedLow|ClipType.BlueLow => (reaction.Green < 0),
 
                            ClipType.RedLow|ClipType.GreenLow => (reaction.Blue < 0),
 
                            ClipType.BlueLow|ClipType.GreenLow => (reaction.Red < 0),
 
                            ClipType.RedHigh | ClipType.BlueHigh => (reaction.Green > 0),
 
                            ClipType.RedHigh | ClipType.GreenHigh => (reaction.Blue > 0),
 
                            ClipType.BlueHigh | ClipType.GreenHigh => (reaction.Red > 0),
 
                            _ => false
 
                        };
 
                        if (!testing3Way && (reaction != null) && isWhiteShift)
 
                        {
 
                            PaintColor baseColor = _recipe.BaseColor;
 
                            if ((reaction.Red < 0) &&
 
                                (reaction.Green < 0) &&
 
                                (reaction.Blue < 0))
 
                            {
 
                                // White-shift down clip.
 
                                extrapolated                    = ExtrapolateWhiteFromOneChannel(_observedColor.Red,   0, reaction.Red);
 
                                if (!extrapolated) extrapolated = ExtrapolateWhiteFromOneChannel(_observedColor.Green, 0, reaction.Green);
 
                                if (!extrapolated) extrapolated = ExtrapolateWhiteFromOneChannel(_observedColor.Blue,  0, reaction.Blue);
 
                            }
 
                            else if ((reaction.Red > 0) &&
 
                                (reaction.Green > 0) &&
 
                                (reaction.Blue > 0))
 
                            {
 
                                // White-shift up clip.
 
                                extrapolated                    = ExtrapolateWhiteFromOneChannel(_observedColor.Red,   255, reaction.Red);
 
                                if (!extrapolated) extrapolated = ExtrapolateWhiteFromOneChannel(_observedColor.Green, 255, reaction.Green);
 
                                if (!extrapolated) extrapolated = ExtrapolateWhiteFromOneChannel(_observedColor.Blue,  255, reaction.Blue);
 
                            }
 
                            WhiteShift = extrapolated;
 
                        }
 

	
 
                        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;
 
            ObservedColor = null; // Clear it out
 

	
 
            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
Models/ReactionTestService.cs
Show inline comments
 
using System.Collections.Generic;
 
using System.Collections.ObjectModel;
 
using System.Diagnostics;
 
using System.Linq;
 
using DesertPaintCodex.Services;
 
using DynamicData;
 

	
 
namespace DesertPaintCodex.Models
 
{
 
    public static class ReactionTestService
 
    {
 
        private static readonly List<ReactionTest> _allTests = new();
 
        
 
        static ReactionTestService()
 
        {
 
            
 
        }
 

	
 
        public static void Initialize()
 
        {
 
            _allTests.Clear();
 
            PlayerProfile? profile = ProfileManager.CurrentProfile;
 
            
 
            Debug.Assert(profile != null);
 
            
 
            List<string> reagentNames = ReagentService.Names;
 

	
 
            foreach (Reagent reagent1 in reagentNames.Select(ReagentService.GetReagent))
 
            {
 
                foreach (Reagent reagent2 in reagentNames.Select(ReagentService.GetReagent))
 
                {
 
                    if (reagent1 == reagent2) continue;
 

	
 
                    Reaction? reaction = profile.FindReaction(reagent1, reagent2);
 
                    ClipType clipType = profile.PairClipStatus(reagent1, reagent2);
 
                    
 
                    ReactionTest test = new(reagent1, reagent2, reaction, clipType)
 
                    ReactionTest test = new(reagent1, reagent2, reaction, clipType, false)
 
                    {
 
                        Clipped  = clipType,
 
                        Reaction = reaction,
 
                       
 
                    };
 

	
 
                    _allTests.Add(test);
 
                }
 
            }
 
        }
 

	
 
        public static void PopulateRemainingTests(ObservableCollection<ReactionTest> collection)
 
        {
 
            collection.Clear();
 
            collection.AddRange(_allTests.Where(test => test.State != ReactionTest.TestState.Saved).OrderBy(test => test));
 
        }
 
        
 
        public static void PopulateCompletedTests(ObservableCollection<ReactionTest> collection)
 
        {
 
            collection.Clear();
 
            collection.AddRange(_allTests.Where(test => test.State == ReactionTest.TestState.Saved).OrderBy(test => test));
 
        }
 
        
 
    }
 
}
...
 
\ No newline at end of file
Util/Constants.cs
Show inline comments
 
using DesertPaintCodex.Models;
 

	
 
namespace DesertPaintCodex.Util
 
{
 
    public static class Constants
 
    {
 
        public static int DefaultPixelMultiplier = 1;
 
        public static int DefaultScreenWidth = 1920;
 
        public static int DefaultScreenHeight = 1080;
 
        public static int DefaultScreenX = 0;
 
        public static int DefaultScreenY = 0;
 

	
 
        private static ReactionTest? _stubReactionTest = null;
 

	
 
        public static ReactionTest StubReactionTest
 
        {
 
            get
 
            {
 
                if (_stubReactionTest == null)
 
                {
 
                    _stubReactionTest = new ReactionTest(
 
                        new Reagent("Toad Skin", "ToadSkin", 48, 96, 48, 0), 
 
                        new Reagent("Falcons Bait", "FalconBait", 128, 240, 224, 1), 
 
                        null, ClipType.None, true);
 
                        null, ClipType.None, false, true);
 
                }
 

	
 
                return _stubReactionTest;
 
            }
 
            
 
        }
 
    }
 
}
...
 
\ No newline at end of file
Views/ReactionTestView.axaml
Show inline comments
...
 
@@ -41,111 +41,116 @@
 
            <TextBlock Classes="BlockHeader">REAGENTS</TextBlock>
 
            <Border Classes="ThinFrame">
 
                <StackPanel Orientation="Vertical" Spacing="10">
 
                    <StackPanel Classes="ReagentRow" IsVisible="{Binding ReactionTest.ShowFirstBuffer}">
 
                        <StackPanel Orientation="Horizontal" Classes="ReagentLabel" Spacing="10">
 
                            <Button Height="24" Width="24" Margin="8 0 0 0" Command="{Binding UseLastBuffer}">
 
                                <TextBlock VerticalAlignment="Center">▼</TextBlock>
 
                            </Button>
 
                            <TextBlock Classes="ReagentName">Buffer:</TextBlock>
 
                        </StackPanel>
 
                        <TextBlock Classes="ReagentName" Text="{Binding ReactionTest.BufferReagentFirst.Name, FallbackValue=[Unknown]}" IsVisible="{Binding !ReactionTest.CanPickBuffer}"/>
 
                        <ComboBox Items="{Binding FirstBufferList}"
 
                                  IsVisible="{Binding ReactionTest.CanPickBuffer}"
 
                                  SelectedItem="{Binding ReactionTest.BufferReagentFirst}"
 
                                  PlaceholderText="Choose" Width="120" Height="30">
 
                            <ComboBox.ItemTemplate>
 
                                <DataTemplate>
 
                                    <TextBlock Text="{Binding Name}" />
 
                                </DataTemplate>
 
                            </ComboBox.ItemTemplate>
 
                        </ComboBox>
 
                        <Border Classes="ReagentSwatch"
 
                            Background="{Binding ReactionTest.BufferReagentFirst.Color, Converter={StaticResource paintToBrush}, FallbackValue=#00000000}" />
 
                    </StackPanel>
 
                    <StackPanel Classes="ReagentRow">
 
                        <TextBlock Classes="ReagentLabel">Reagent #1:</TextBlock>
 
                        <TextBlock Classes="ReagentName" Text="{Binding ReactionTest.Reagent1.Name}" />
 
                        <Border Classes="ReagentSwatch"
 
                            Background="{Binding ReactionTest.Reagent1.Color, Converter={StaticResource paintToBrush}, FallbackValue=#00000000}" />
 
                    </StackPanel>
 
                    <StackPanel Classes="ReagentRow">
 
                        <TextBlock Classes="ReagentLabel">Reagent #2:</TextBlock>
 
                        <TextBlock Classes="ReagentName" Text="{Binding ReactionTest.Reagent2.Name}" />
 
                        <Border Classes="ReagentSwatch"
 
                            Background="{Binding ReactionTest.Reagent2.Color, Converter={StaticResource paintToBrush}, FallbackValue=#00000000}" />
 
                    </StackPanel>
 
                    <StackPanel Classes="ReagentRow" IsVisible="{Binding ReactionTest.ShowLastBuffer}">
 
                        <StackPanel Orientation="Horizontal" Classes="ReagentLabel" Spacing="10">
 
                            <Button Height="24" Width="24" Margin="8 0 0 0" Command="{Binding UseFirstBuffer}">
 
                                <TextBlock VerticalAlignment="Center">▲</TextBlock>
 
                            </Button>
 
                            <TextBlock Classes="ReagentName">Buffer:</TextBlock>
 
                        </StackPanel>
 
                        <TextBlock Classes="ReagentName" Text="{Binding ReactionTest.BufferReagentLast.Name, FallbackValue=[Unknown]}" IsVisible="{Binding !ReactionTest.CanPickBuffer}"/>
 
                        <ComboBox Items="{Binding LastBufferList}"
 
                                  IsVisible="{Binding ReactionTest.CanPickBuffer}"
 
                                  SelectedItem="{Binding ReactionTest.BufferReagentLast}"
 
                                  PlaceholderText="Choose" Width="120" Height="30">
 
                            <ComboBox.ItemTemplate>
 
                                <DataTemplate>
 
                                    <TextBlock Text="{Binding Name}" />
 
                                </DataTemplate>
 
                            </ComboBox.ItemTemplate>
 
                        </ComboBox>
 
                        <Border Classes="ReagentSwatch"
 
                                Background="{Binding ReactionTest.BufferReagentLast.Color, Converter={StaticResource paintToBrush}, FallbackValue=#00000000}" />
 
                    </StackPanel>                    
 
                </StackPanel>
 
            </Border>
 
        </StackPanel>
 

	
 
        <StackPanel DockPanel.Dock="Top" Orientation="Vertical" Spacing="5">
 
            <TextBlock Classes="BlockHeader">HYPOTHETICAL COLOR</TextBlock>
 
            <views:PaintSwatchView ShowName="False" Color="{Binding ReactionTest.HypotheticalColor}"/>
 
        </StackPanel>
 

	
 
         <Grid ColumnDefinitions="*,15,*" IsVisible="{Binding ReactionTest.CanScan}">
 
            <Button Grid.Column="0" Padding="11 5 11 5" Command="{Binding Analyze}">Analyze Mixture</Button>
 
            <Button Grid.Column="2" Padding="11 5 11 5" Command="{Binding MarkInert}">Mark Inert</Button>
 
        </Grid>
 

	
 
        <StackPanel Orientation="Vertical" Spacing="10" IsVisible="{Binding ReactionTest.IsScanning}">
 
            <ProgressBar DockPanel.Dock="Top" Value="{Binding ReactionTest.ScanProgress}" Height="20" />
 
            <Button Command="{Binding CancelScan}">Cancel Scan</Button>
 
        </StackPanel>
 

	
 
        <StackPanel DockPanel.Dock="Top" Orientation="Vertical" Spacing="5"
 
                    IsVisible="{Binding ReactionTest.HasResults}">
 
            <TextBlock Classes="BlockHeader">TEST RESULT</TextBlock>
 
            <views:PaintSwatchView ShowName="False" IsVisible="{Binding !ReactionTest.NoLab}" Color="{Binding ReactionTest.ObservedColor}"/>
 
            <views:EmbeddedWarningBox IsVisible="{Binding ReactionTest.NoLab}"
 
                Title="🛇 PIGMENT LAB NOT FOUND"
 
                Message="You may want to make sure your view of the Pigment Lab is clear, and that your screen settings are correct.">
 
                <Button Command="{Binding OpenScreenSettings}">
 
                    <TextBlock FontWeight="Bold" FontSize="15">SCREEN SETTINGS</TextBlock>
 
                </Button>
 
            </views:EmbeddedWarningBox>
 
        </StackPanel>
 

	
 
        <StackPanel DockPanel.Dock="Top" Orientation="Vertical" Spacing="5"
 
                    IsVisible="{Binding ReactionTest.HasReaction}">
 
            <TextBlock Classes="BlockHeader">REACTION OBSERVED</TextBlock>
 
            <StackPanel Orientation="Horizontal" Spacing="21" Margin="0 5">
 
                <views:ReactionUnitView Reaction="{Binding ReactionTest.Reaction}"/>
 
            </StackPanel>
 

	
 
          <views:EmbeddedWarningBox Title="🛈 EXTRAPOLATED REACTION"
 
                                    IsVisible="{Binding ReactionTest.WhiteShift}"
 
                                    Message="One component of the reaction clipped, but a value could be extrapolated from the shift in the color components.">
 
          </views:EmbeddedWarningBox>
 

	
 
            <views:EmbeddedWarningBox Title="🛇 REACTION CLIPPED"
 
                                      IsVisible="{Binding !!ReactionTest.Clipped}"
 
                                      Message="Your reaction fell outside of measurable values. We will need to use a buffer pigment to test it.">
 
                <StackPanel Orientation="Vertical" Spacing="10">
 
                    <Button Command="{Binding ShowClipInfo}">LEARN MORE</Button>
 
                </StackPanel>
 
            </views:EmbeddedWarningBox>
 
        </StackPanel>
 
        
 
        <Button IsVisible="{Binding ReactionTest.CanClear}"
 
                Command="{Binding ClearReaction}">Clear Reaction</Button>
 
        <Button IsVisible="{Binding ReactionTest.CanSave}"
 
                Command="{Binding SaveReaction}">Save Reaction</Button>
 
    </StackPanel>
 
</UserControl>
...
 
\ No newline at end of file
0 comments (0 inline, 0 general)