Example #1
0
        public SimpleHSV ToHSV()
        {
            // Algorithm from https://www.rapidtables.com/convert/color/rgb-to-hsv.html
            double rr = this.R / 255.0;
            double gg = this.G / 255.0;
            double bb = this.B / 255.0;

            double c_max = Math.Max(Math.Max(rr, gg), bb);
            double c_min = Math.Min(Math.Min(rr, gg), bb);

            double delta = c_max - c_min;

            SimpleHSV converted = new SimpleHSV();

            if (delta != 0.0)
            {
                if (c_max == rr)
                {
                    converted.H = 60.0 * (((gg - bb) / delta) % 6);
                }
                else if (c_max == gg)
                {
                    converted.H = 60.0 * ((bb - rr) / delta + 2);
                }
                else if (c_max == bb)
                {
                    converted.H = 60.0 * ((rr - gg) / delta + 4);
                }
            }
            if (c_max != 0.0)
            {
                converted.S = delta / c_max;
            }
            converted.V = c_max;

            // Just in case, we are clamping everything
            converted.H = Math.Min(360.0, Math.Max(0.0, converted.H));
            converted.S = Math.Min(1.0, Math.Max(0.0, converted.S));
            converted.V = Math.Min(1.0, Math.Max(0.0, converted.V));

            return(converted);
        }
Example #2
0
        public override void Entry(IModHelper helper)
        {
            Instance = this;
            Config   = helper.ReadConfig <PondPainterConfig>();

            helper.Events.GameLoop.DayStarted += GameLoop_DayStarted;
            if (Config.Enable_Animations)
            {
                helper.Events.GameLoop.UpdateTicked += GameLoop_UpdateTicked;
            }

            foreach (IContentPack contentPack in this.Helper.ContentPacks.GetOwned())
            {
                this.Monitor.Log($"Reading content pack: {contentPack.Manifest.Name} {contentPack.Manifest.Version} from {contentPack.DirectoryPath}", LogLevel.Trace);
                // We are assuming that we will receive the packs in proper dependency order.
                // However, dependencies are traditionally "whoever updates last wins" but tag matching is usually the opposite.
                // So we will try to do the following. If we get pack A, B, C we save the data as C entries, B entries, A entries.
                int entryIndex = 0;

                if (contentPack.HasFile(ContentPackFile))
                {
                    PondPainterPackData packData = contentPack.ReadJsonFile <PondPainterPackData>(ContentPackFile);

                    if (packData.EmptyPondColor != null)
                    {
                        Data.EmptyPondColor = ColorLookup.FromName(packData.EmptyPondColor);
                    }
                    int index = 0;
                    foreach (PondPainterPackEntry entry in packData.Entries)
                    {
                        index++;
                        string LogName = String.Format("Entry {0}", index);
                        if (entry.LogName != null && !entry.LogName.Equals(""))
                        {
                            LogName = entry.LogName;
                        }
                        //this.Monitor.Log($"Found an entry called \"{LogName}\" and will now try to parse it.", LogLevel.Debug);
                        if (entry.Tags.Count == 0)
                        {
                            this.Monitor.Log($"Entry \"{LogName}\" has an empty Tags list and will be skipped.", LogLevel.Warn);
                            continue;
                        }
                        int cindex = 0;
                        PondPainterDataEntry DataEntry = new PondPainterDataEntry(contentPack.Manifest.UniqueID, LogName, entry.Tags);
                        foreach (PondPainterPackColor c in entry.Colors)
                        {
                            cindex++;
                            Color?theColor = null;
                            if (c.ColorName != null)
                            {
                                theColor = ColorLookup.FromName(c.ColorName);
                            }
                            if (theColor == null)
                            {
                                this.Monitor.Log($"Entry \"{LogName}\" color definition {cindex} does not have a ColorName defined and will be skipped.", LogLevel.Warn);
                                continue;
                            }
                            // We've passed the null check so it is time to get rid of the damned nullable type
                            Color theRealColor = (Color)theColor;
                            bool  HasAnimation = false;
                            // Some defaults; note the range is a double even though the content pack only takes ints
                            // This is because the internal ColorMine properties are all doubles
                            int    AnimationFrameDelay = 10;
                            double AnimationRange      = 20.0f;
                            // Of course I had to make this configurable too, which means I have to sanity-check it.
                            // For other options I could delay the sanity checks until I was certain we actually had a properly defined type, but this one
                            //  needs to be checked before I try to construct the animations. This means some meaningless error messages will be logged if
                            //  there is an animation type of "none" and something wrong with this value.
                            // The total amount of steps is really twice this variable +1 since we go from base + steps to base - steps
                            int AnimationSteps = 30;
                            if (c.AnimationTotalFrames != null)
                            {
                                AnimationSteps = Math.Abs((int)c.AnimationTotalFrames);
                                if (c.AnimationTotalFrames > -2 && c.AnimationTotalFrames < 2)
                                {
                                    this.Monitor.Log($"Entry \"{LogName}\" color definition {cindex} has animation total frames set to ({c.AnimationTotalFrames}). The minimum useful value is 2 and that will be used instead.", LogLevel.Warn);
                                    AnimationSteps = 2;
                                }
                                else if (c.AnimationTotalFrames < 0)
                                {
                                    this.Monitor.Log($"Entry \"{LogName}\" color definition {cindex} has animation total frames set to ({c.AnimationTotalFrames}). This value should be positive and will be changed to {AnimationSteps}.", LogLevel.Warn);
                                }
                            }
                            List <Color> AnimationColors = new List <Color>();
                            if (c.AnimationType != null && !c.AnimationType.Equals("none"))
                            {
                                // An AnimationType was given, and each type is processed a bit differently.
                                // The range will be interpreted differently for various types, but it should
                                //   never be 0 since that represents an animation that does nothing.
                                // Note that null right now is still ok; it is only an explicit 0 being excluded
                                if (c.AnimationRange == null || c.AnimationRange != 0)
                                {
                                    if (c.AnimationType.Equals("hue"))
                                    {
                                        if (c.AnimationRange != null)
                                        {
                                            // restrict range to (0, 180]
                                            if (c.AnimationRange < 0 || c.AnimationRange > 180)
                                            {
                                                AnimationRange = Math.Min(180.0f, Math.Abs((int)c.AnimationRange));
                                                this.Monitor.Log($"Entry \"{LogName}\" color definition {cindex} has an animation range of ({c.AnimationRange}). Hue animations must have a positive range <= 180 so this will be interpreted as {AnimationRange}.", LogLevel.Warn);
                                            }
                                            else
                                            {
                                                AnimationRange = (double)c.AnimationRange;
                                            }
                                        }
                                        else
                                        {
                                            AnimationRange = 20.0f;
                                            this.Monitor.Log($"Entry \"{LogName}\" color definition {cindex} had no animation range listed; the default of {AnimationRange} will be used.", LogLevel.Debug);
                                        }
                                        HasAnimation = true;
                                        // Calculating the animation frames, in HSV via ColorMine
                                        // We use a sine model with the animation "range" as amplitude and a period of the number of steps.
                                        SimpleRGB BaseRGB = new SimpleRGB(theRealColor.R, theRealColor.G, theRealColor.B);
                                        SimpleHSV BaseHSV = BaseRGB.ToHSV();
                                        this.Monitor.Log($"Entry \"{LogName}\" color definition {cindex} Tracing HUE animation with base of {BaseHSV.H} and a range of {AnimationRange} in {AnimationSteps} steps.", LogLevel.Trace);
                                        for (int i = 0; i <= AnimationSteps; i++)
                                        {
                                            // We can't do the simpler NewHSV = BaseHSV because that is not a true copy
                                            SimpleHSV NewHSV = BaseRGB.ToHSV();
                                            double    hue    = (360 + BaseHSV.H + AnimationRange * Math.Sin(2 * i * Math.PI / AnimationSteps)) % 360;
                                            NewHSV.H = hue;
                                            //this.Monitor.Log($"** Animation trace step {i}: hue {hue}. Base {BaseHSV.H}", LogLevel.Trace);
                                            SimpleRGB NewRGB = NewHSV.ToRGB();
                                            AnimationColors.Add(new Color((int)NewRGB.R, (int)NewRGB.G, (int)NewRGB.B));
                                        }
                                    }
                                    else if (c.AnimationType.Equals("value"))
                                    {
                                        if (c.AnimationRange != null)
                                        {
                                            // restrict range to (0, 100] (will be converted to (0, 1] later)
                                            if (c.AnimationRange < 0 || c.AnimationRange > 100)
                                            {
                                                AnimationRange = Math.Min(100, Math.Abs((int)c.AnimationRange));
                                                this.Monitor.Log($"Entry \"{LogName}\" color definition {cindex} has an animation range of {c.AnimationRange}. Value animations must have a positive range <= 100 so this will be interpreted as {AnimationRange}.", LogLevel.Warn);
                                            }
                                            else
                                            {
                                                AnimationRange = (double)c.AnimationRange;
                                            }
                                        }
                                        else
                                        {
                                            AnimationRange = 20.0f;
                                            this.Monitor.Log($"Entry \"{LogName}\" color definition {cindex} had no animation range listed; the default of {AnimationRange} will be used.", LogLevel.Info);
                                        }
                                        AnimationRange /= 100.0f;
                                        HasAnimation    = true;
                                        // Calculating the animation frames, in HSV via ColorMine
                                        // We use a sine model with the animation "range" as amplitude and a period of the number of steps.

                                        SimpleRGB BaseRGB = new SimpleRGB(theRealColor.R, theRealColor.G, theRealColor.B);
                                        SimpleHSV BaseHSV = BaseRGB.ToHSV();
                                        //this.Monitor.Log($"Entry \"{LogName}\" color definition {cindex} Tracing VALUE animation with base of {BaseHSV.V} and a range of {AnimationRange} in {AnimationSteps} steps.", LogLevel.Trace);
                                        for (int i = 0; i <= AnimationSteps; i++)
                                        {
                                            // We can't do the simpler NewHSV = BaseHSV because that is not a true copy
                                            SimpleHSV NewHSV = BaseRGB.ToHSV();
                                            double    val    = Math.Min(Math.Max(BaseHSV.V + AnimationRange * Math.Sin(2 * i * Math.PI / AnimationSteps), 0), 1);
                                            NewHSV.V = val;
                                            this.Monitor.Log($"** Animation trace step {i}: value {val}. Base {BaseHSV.V}", LogLevel.Trace);
                                            SimpleRGB NewRGB = NewHSV.ToRGB();
                                            AnimationColors.Add(new Color((int)NewRGB.R, (int)NewRGB.G, (int)NewRGB.B));
                                        }
                                    }
                                    else
                                    {
                                        this.Monitor.Log($"Entry \"{LogName}\" color definition {cindex} has an unknown animation type ({c.AnimationType}). A static color will be used instead.", LogLevel.Warn);
                                    }
                                }
                                else                                 // AnimationRange was 0
                                {
                                    this.Monitor.Log($"Entry \"{LogName}\" color definition {cindex} has an animation range of zero. A static color will be used instead.", LogLevel.Warn);
                                }
                            }
                            if (HasAnimation)
                            {
                                // To get here, we had a valid animation type with an appropriate range.
                                // One final sanity check is on the timing.
                                if (c.AnimationFrameDelay == null || c.AnimationFrameDelay == 0)
                                {
                                    this.Monitor.Log($"Entry \"{LogName}\" color definition {cindex} had no animation frame delay listed; the default of {AnimationFrameDelay} frames will be used.", LogLevel.Info);
                                }
                                else
                                {
                                    AnimationFrameDelay = Math.Abs((int)c.AnimationFrameDelay);
                                    if (c.AnimationFrameDelay < 0)
                                    {
                                        this.Monitor.Log($"Entry \"{LogName}\" color definition {cindex} had an animation frame delay of {c.AnimationFrameDelay}; timings must be positive so this will be interpreted as {AnimationFrameDelay}.", LogLevel.Warn);
                                    }
                                }
                                DataEntry.Colors.Add(c.MinPopulationForColor, new PondPainterDataColorDef(AnimationColors, AnimationFrameDelay));
                                this.Monitor.Log($"Entry \"{LogName}\" color definition {cindex} successfully added as a new animation.", LogLevel.Trace);
                            }
                            else
                            {
                                DataEntry.Colors.Add(c.MinPopulationForColor, new PondPainterDataColorDef((Color)theColor));
                                this.Monitor.Log($"Entry \"{LogName}\" color definition {cindex} successfully added as a new static color.", LogLevel.Trace);
                            }
                        }
                        Data.Entries.Insert(entryIndex++, DataEntry);
                        //this.Monitor.Log($"Entry \"{LogName}\" complete entry added to internal data.", LogLevel.Trace);
                    }
                }
                else
                {
                    this.Monitor.Log($"Unable to load content pack {contentPack.Manifest.Name} {contentPack.Manifest.Version} because no {ContentPackFile} file was found.", LogLevel.Warn);
                }
            }
            this.Monitor.Log($"Finished loading content packs. Data has {Data.Entries.Count} entries.", LogLevel.Trace);
        }