2018-12-09 12:25:51 +08:00

518 lines
14 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using _Math = System.Math;
namespace THREE
{
/// <summary>
/// @author mrdoob / http://mrdoob.com/
/// @author tengge / https://github.com/tengge1
/// </summary>
public class Color
{
public double r = 1.0;
public double g = 1.0;
public double b = 1.0;
public Color(double r, double g, double b)
{
this.SetRGB(r, g, b);
}
public Color(int color)
{
this.Set(color);
}
public Color(string color)
{
this.Set(color);
}
public const bool isColor = true;
public Color Set(Color value)
{
this.Copy(value);
return this;
}
public Color Set(int value)
{
this.SetHex(value);
return this;
}
public Color Set(string value)
{
this.SetStyle(value);
return this;
}
public Color SetScalar(double scalar)
{
this.r = scalar;
this.g = scalar;
this.b = scalar;
return this;
}
public Color SetHex(int hex)
{
this.r = (hex >> 16 & 255) / 255;
this.g = (hex >> 8 & 255) / 255;
this.b = (hex & 255) / 255;
return this;
}
public Color SetRGB(double r, double g, double b)
{
this.r = r;
this.g = g;
this.b = b;
return this;
}
private double Hue2rgb(double p, double q, double t)
{
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * 6 * (2 / 3 - t);
return p;
}
public Color SetHSL(double h, double s, double l)
{
// h,s,l ranges are in 0.0 - 1.0
h = Math.EuclideanModulo(h, 1);
s = Math.Clamp(s, 0, 1);
l = Math.Clamp(l, 0, 1);
if (s == 0)
{
this.r = this.g = this.b = l;
}
else
{
var p = l <= 0.5 ? l * (1 + s) : l + s - (l * s);
var q = (2 * l) - p;
this.r = Hue2rgb(q, p, h + 1 / 3);
this.g = Hue2rgb(q, p, h);
this.b = Hue2rgb(q, p, h - 1 / 3);
}
return this;
}
private void HandleAlpha(string str, string style)
{
if (str == null) return;
if (double.Parse(str) < 1)
{
Console.WriteLine("THREE.Color: Alpha component of " + style + " will be ignored.");
}
}
public Color SetStyle(string style)
{
Match m;
if ((m = new Regex(@"^((?:rgb|hsl)a?)\(\s*([^\)]*)\)", RegexOptions.ECMAScript).Match(style)).Success)
{
// rgb / hsl
Match color;
var name = m.Groups[1].Value;
var components = m.Groups[2].Value;
switch (name)
{
case "rgb":
case "rgba":
if ((color = new Regex(@"^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$",
RegexOptions.ECMAScript).Match(components)).Success)
{
// rgb(255,0,0) rgba(255,0,0,0.5)
this.r = _Math.Min(255, Convert.ToInt32(color.Groups[1].Value, 10)) / 255;
this.g = _Math.Min(255, Convert.ToInt32(color.Groups[2].Value, 10)) / 255;
this.b = _Math.Min(255, Convert.ToInt32(color.Groups[3].Value, 10)) / 255;
this.HandleAlpha(color.Groups[5].Value, style);
return this;
}
if ((color = new Regex(@"^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$",
RegexOptions.ECMAScript).Match(components)).Success)
{
// rgb(100%,0%,0%) rgba(100%,0%,0%,0.5)
this.r = _Math.Min(100, Convert.ToInt32(color.Groups[1].Value, 10)) / 100;
this.g = _Math.Min(100, Convert.ToInt32(color.Groups[2].Value, 10)) / 100;
this.b = _Math.Min(100, Convert.ToInt32(color.Groups[3].Value, 10)) / 100;
this.HandleAlpha(color.Groups[5].Value, style);
return this;
}
break;
case "hsl":
case "hsla":
if ((color = new Regex(@"^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$",
RegexOptions.ECMAScript).Match(components)).Success)
{
// hsl(120,50%,50%) hsla(120,50%,50%,0.5)
var h = Convert.ToDouble(color.Groups[1].Value) / 360;
var s = Convert.ToInt32(color.Groups[2].Value, 10) / 100;
var l = Convert.ToInt32(color.Groups[3].Value, 10) / 100;
this.HandleAlpha(color.Groups[5].Value, style);
return this.SetHSL(h, s, l);
}
break;
}
}
else if ((m = new Regex(@"^\#([A-Fa-f0-9]+)$", RegexOptions.ECMAScript).Match(style)).Success)
{
// hex color
var hex = m.Groups[1].Value;
var size = hex.Length;
if (size == 3)
{
// #ff0
this.r = Convert.ToInt32((hex[0] + hex[0]).ToString(), 16) / 255;
this.g = Convert.ToInt32((hex[1] + hex[1]).ToString(), 16) / 255;
this.b = Convert.ToInt32((hex[2] + hex[2]).ToString(), 16) / 255;
return this;
}
else if (size == 6)
{
// #ff0000
this.r = Convert.ToInt32((hex[0] + hex[1]).ToString(), 16) / 255;
this.g = Convert.ToInt32((hex[2] + hex[3]).ToString(), 16) / 255;
this.b = Convert.ToInt32((hex[4] + hex[5]).ToString(), 16) / 255;
return this;
}
}
if (style != null && style.Length > 0)
{
// color keywords
var field = typeof(ColorKeywords).GetField(style);
if (field != null)
{
var hex = Convert.ToInt32(field.GetValue(null).ToString());
// red
this.SetHex(hex);
}
else
{
// unknown color
Console.WriteLine("THREE.Color: Unknown color " + style);
}
}
return this;
}
public Color Clone()
{
return new Color(this.r, this.g, this.b);
}
public Color Copy(Color color)
{
this.r = color.r;
this.g = color.g;
this.b = color.b;
return this;
}
public Color CopyGammaToLinear(Color color, double gammaFactor = 2.0)
{
this.r = _Math.Pow(color.r, gammaFactor);
this.g = _Math.Pow(color.g, gammaFactor);
this.b = _Math.Pow(color.b, gammaFactor);
return this;
}
public Color CopyLinearToGamma(Color color, double gammaFactor = 2.0)
{
var safeInverse = (gammaFactor > 0) ? (1.0 / gammaFactor) : 1.0;
this.r = _Math.Pow(color.r, safeInverse);
this.g = _Math.Pow(color.g, safeInverse);
this.b = _Math.Pow(color.b, safeInverse);
return this;
}
public Color ConvertGammaToLinear(double gammaFactor)
{
this.CopyGammaToLinear(this, gammaFactor);
return this;
}
public Color ConvertLinearToGamma(double gammaFactor)
{
this.CopyLinearToGamma(this, gammaFactor);
return this;
}
private double SRGBToLinear(double c)
{
return (c < 0.04045) ? c * 0.0773993808 : _Math.Pow(c * 0.9478672986 + 0.0521327014, 2.4);
}
public Color CopySRGBToLinear(Color color)
{
this.r = SRGBToLinear(color.r);
this.g = SRGBToLinear(color.g);
this.b = SRGBToLinear(color.b);
return this;
}
private double LinearToSRGB(double c)
{
return (c < 0.0031308) ? c * 12.92 : 1.055 * (_Math.Pow(c, 0.41666)) - 0.055;
}
public Color CopyLinearToSRGB(Color color)
{
this.r = LinearToSRGB(color.r);
this.g = LinearToSRGB(color.g);
this.b = LinearToSRGB(color.b);
return this;
}
public Color ConvertSRGBToLinear()
{
this.CopySRGBToLinear(this);
return this;
}
public Color ConvertLinearToSRGB()
{
this.CopyLinearToSRGB(this);
return this;
}
public double GetHex()
{
return Convert.ToInt32(this.r * 255) << 16 ^ Convert.ToInt32(this.g * 255) << 8 ^ Convert.ToInt32(this.b * 255) << 0;
}
public string GetHexString()
{
var hex = ("000000" + this.GetHex().ToString("{0:X}"));
return hex.Substring(hex.Length - 6, 6);
}
public double[] GetHSL(double[] target = null) // [h, s, l]
{
// h,s,l ranges are in 0.0 - 1.0
if (target == null)
{
Console.WriteLine("THREE.Color: .getHSL() target is now required");
target = new double[] { 0, 0, 0 };
}
double r = this.r, g = this.g, b = this.b;
var max = _Math.Max(_Math.Max(r, g), b);
var min = _Math.Min(_Math.Max(r, g), b);
double hue = 0, saturation;
var lightness = (min + max) / 2.0;
if (min == max)
{
hue = 0;
saturation = 0;
}
else
{
var delta = max - min;
saturation = lightness <= 0.5 ? delta / (max + min) : delta / (2 - max - min);
if (max == r)
{
hue = (g - b) / delta + (g < b ? 6 : 0);
}
else if (max == g)
{
hue = (b - r) / delta + 2;
}
else if (max == b)
{
hue = (r - g) / delta + 4;
}
hue /= 6;
}
target = new double[] { hue, saturation, lightness };
return target;
}
public string GetStyle()
{
return "rgb(" + _Math.Floor(this.r * 255) + "," + _Math.Floor(this.g * 255) + "," + _Math.Floor(this.b * 255) + ")";
}
public Color OffsetHSL(double h, double s, double l)
{
var hsl = new double[3];
this.GetHSL(hsl);
hsl[0] += h;
hsl[1] += s;
hsl[2] += l;
this.SetHSL(hsl[0], hsl[1], hsl[2]);
return this;
}
public Color Add(Color color)
{
this.r += color.r;
this.g += color.g;
this.b += color.b;
return this;
}
public Color AddColors(Color color1, Color color2)
{
this.r = color1.r + color2.r;
this.g = color1.g + color2.g;
this.b = color1.b + color2.b;
return this;
}
public Color AddScalar(double s)
{
this.r += s;
this.g += s;
this.b += s;
return this;
}
public Color Sub(Color color)
{
this.r = _Math.Max(0, this.r - color.r);
this.g = _Math.Max(0, this.g - color.g);
this.b = _Math.Max(0, this.b - color.b);
return this;
}
public Color Multiply(Color color)
{
this.r *= color.r;
this.g *= color.g;
this.b *= color.b;
return this;
}
public Color MultiplyScalar(double s)
{
this.r *= s;
this.g *= s;
this.b *= s;
return this;
}
public Color Lerp(Color color, double alpha)
{
this.r += (color.r - this.r) * alpha;
this.g += (color.g - this.g) * alpha;
this.b += (color.b - this.b) * alpha;
return this;
}
public Color LerpHSL(Color color, double alpha)
{
var hslA = new double[] { 0, 0, 0 };
var hslB = new double[] { 0, 0, 0 };
this.GetHSL(hslA);
color.GetHSL(hslB);
var h = Math.Lerp(hslA[0], hslB[0], alpha);
var s = Math.Lerp(hslA[1], hslB[1], alpha);
var l = Math.Lerp(hslA[2], hslB[2], alpha);
this.SetHSL(h, s, l);
return this;
}
public bool Equals(Color c)
{
return (c.r == this.r) && (c.g == this.g) && (c.b == this.b);
}
public Color FromArray(double[] array, int offset = 0)
{
this.r = array[offset];
this.g = array[offset + 1];
this.b = array[offset + 2];
return this;
}
public double[] ToArray(double[] array = null, int offset = 0)
{
if (array == null) array = new double[3];
array[offset] = this.r;
array[offset + 1] = this.g;
array[offset + 2] = this.b;
return array;
}
public double ToJSON()
{
return this.GetHex();
}
}
}