Shows the selected cut as a fan of cards with waybill, notes, and couple/handbrake/locate actions. WaypointQueue cuts appear as dividers when that mod is present.
520 lines
19 KiB
C#
520 lines
19 KiB
C#
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
namespace S3.Modules.CarCards;
|
|
|
|
static class CardUi
|
|
{
|
|
public const float Round = 8f;
|
|
|
|
static Sprite? _white;
|
|
static Sprite? _round;
|
|
static Sprite? _pin;
|
|
static Sprite? _dots;
|
|
static Sprite? _x;
|
|
static Sprite? _couple;
|
|
static Sprite? _brake;
|
|
static Sprite? _locate;
|
|
static Sprite? _cut;
|
|
static Sprite? _drop;
|
|
static Sprite? _pickup;
|
|
static TMP_FontAsset? _tmp;
|
|
static Font? _uiFont;
|
|
|
|
public static Sprite White()
|
|
{
|
|
if (_white != null) return _white;
|
|
var tex = Texture2D.whiteTexture;
|
|
_white = Sprite.Create(tex, new Rect(0f, 0f, tex.width, tex.height), new Vector2(0.5f, 0.5f), 4f);
|
|
_white.name = "S3CardWhite";
|
|
return _white;
|
|
}
|
|
|
|
public static Sprite RoundSprite()
|
|
{
|
|
if (_round != null) return _round;
|
|
// 8 UI-unit corners. uGUI slice size is border * canvasRefPPU / spritePPU
|
|
// (canvas reference PPU is 100), so sprite PPU must be aa * 100 to keep
|
|
// the radius at 8 while the texture is supersampled.
|
|
const int radiusUi = 8;
|
|
const int aa = 8;
|
|
const int r = radiusUi * aa;
|
|
const int s = r * 2 + 32;
|
|
const float ppu = aa * 100f;
|
|
var tex = new Texture2D(s, s, TextureFormat.RGBA32, false)
|
|
{
|
|
wrapMode = TextureWrapMode.Clamp,
|
|
filterMode = FilterMode.Bilinear,
|
|
hideFlags = HideFlags.HideAndDontSave,
|
|
};
|
|
var px = new Color32[s * s];
|
|
for (int y = 0; y < s; y++)
|
|
{
|
|
for (int x = 0; x < s; x++)
|
|
{
|
|
float a = CoverRound(x + 0.5f, y + 0.5f, s, r);
|
|
byte b = (byte)Mathf.Clamp(Mathf.RoundToInt(a * 255f), 0, 255);
|
|
px[y * s + x] = new Color32(255, 255, 255, b);
|
|
}
|
|
}
|
|
tex.SetPixels32(px);
|
|
tex.Apply(false, true);
|
|
_round = Sprite.Create(
|
|
tex, new Rect(0f, 0f, s, s), new Vector2(0.5f, 0.5f),
|
|
ppu, 0, SpriteMeshType.FullRect, new Vector4(r, r, r, r));
|
|
_round.name = "S3Round8";
|
|
return _round;
|
|
}
|
|
|
|
static float CoverRound(float x, float y, int s, int r)
|
|
{
|
|
float dx = x < r ? r - x : (x > s - r ? x - (s - r) : 0f);
|
|
float dy = y < r ? r - y : (y > s - r ? y - (s - r) : 0f);
|
|
if (dx == 0f || dy == 0f) return 1f;
|
|
float d = Mathf.Sqrt(dx * dx + dy * dy);
|
|
return Mathf.Clamp01(r + 0.5f - d);
|
|
}
|
|
|
|
public static Sprite PinSprite()
|
|
{
|
|
if (_pin != null) return _pin;
|
|
_pin = MakeGlyph(32, (tex, s) =>
|
|
{
|
|
FillCircle(tex, s, 16, 12, 7, Color.white);
|
|
FillCircle(tex, s, 16, 12, 3, new Color(0, 0, 0, 0));
|
|
FillTri(tex, s, 16, 16, 10, 28, 22, 16, Color.white);
|
|
});
|
|
_pin.name = "S3Pin";
|
|
return _pin;
|
|
}
|
|
|
|
public static Sprite DotsSprite()
|
|
{
|
|
if (_dots != null) return _dots;
|
|
_dots = MakeGlyph(32, (tex, s) =>
|
|
{
|
|
FillCircle(tex, s, 16, 8, 3, Color.white);
|
|
FillCircle(tex, s, 16, 16, 3, Color.white);
|
|
FillCircle(tex, s, 16, 24, 3, Color.white);
|
|
});
|
|
_dots.name = "S3Dots";
|
|
return _dots;
|
|
}
|
|
|
|
public static Sprite XSprite()
|
|
{
|
|
if (_x != null) return _x;
|
|
_x = MakeGlyph(32, (tex, s) =>
|
|
{
|
|
StrokeLine(tex, s, 8, 8, 24, 24, 2.2f, Color.white);
|
|
StrokeLine(tex, s, 24, 8, 8, 24, 2.2f, Color.white);
|
|
});
|
|
_x.name = "S3X";
|
|
return _x;
|
|
}
|
|
|
|
public static Sprite CoupleSprite()
|
|
{
|
|
if (_couple != null) return _couple;
|
|
_couple = MakeGlyph(32, (tex, s) =>
|
|
{
|
|
StrokeLine(tex, s, 6, 10, 14, 10, 2.2f, Color.white);
|
|
StrokeLine(tex, s, 14, 10, 14, 22, 2.2f, Color.white);
|
|
StrokeLine(tex, s, 14, 22, 6, 22, 2.2f, Color.white);
|
|
StrokeLine(tex, s, 26, 10, 18, 10, 2.2f, Color.white);
|
|
StrokeLine(tex, s, 18, 10, 18, 22, 2.2f, Color.white);
|
|
StrokeLine(tex, s, 18, 22, 26, 22, 2.2f, Color.white);
|
|
});
|
|
_couple.name = "S3Couple";
|
|
return _couple;
|
|
}
|
|
|
|
public static Sprite BrakeSprite()
|
|
{
|
|
if (_brake != null) return _brake;
|
|
_brake = MakeGlyph(32, (tex, s) =>
|
|
{
|
|
FillCircle(tex, s, 16, 14, 9, Color.white);
|
|
FillCircle(tex, s, 16, 14, 5, new Color(0, 0, 0, 0));
|
|
StrokeLine(tex, s, 16, 14, 16, 28, 2.2f, Color.white);
|
|
});
|
|
_brake.name = "S3Brake";
|
|
return _brake;
|
|
}
|
|
|
|
public static Sprite LocateSprite()
|
|
{
|
|
if (_locate != null) return _locate;
|
|
_locate = MakeGlyph(32, (tex, s) =>
|
|
{
|
|
FillCircle(tex, s, 16, 16, 8, Color.white);
|
|
FillCircle(tex, s, 16, 16, 4, new Color(0, 0, 0, 0));
|
|
StrokeLine(tex, s, 16, 4, 16, 10, 2f, Color.white);
|
|
StrokeLine(tex, s, 16, 22, 16, 28, 2f, Color.white);
|
|
StrokeLine(tex, s, 4, 16, 10, 16, 2f, Color.white);
|
|
StrokeLine(tex, s, 22, 16, 28, 16, 2f, Color.white);
|
|
});
|
|
_locate.name = "S3Locate";
|
|
return _locate;
|
|
}
|
|
|
|
public static Sprite CutSprite()
|
|
{
|
|
if (_cut != null) return _cut;
|
|
_cut = MakeGlyph(64, (px, s) =>
|
|
{
|
|
StrokeCar(px, s, 4f, 18f, 22f, 28f, 2.6f);
|
|
StrokeCar(px, s, 38f, 18f, 22f, 28f, 2.6f);
|
|
});
|
|
_cut.name = "S3Cut";
|
|
return _cut;
|
|
}
|
|
|
|
public static Sprite DropSprite()
|
|
{
|
|
if (_drop != null) return _drop;
|
|
_drop = MakeGlyph(64, (px, s) =>
|
|
{
|
|
StrokeCar(px, s, 16f, 8f, 32f, 24f, 2.6f);
|
|
StrokeLine(px, s, 32f, 36f, 32f, 50f, 2.8f, Color.white);
|
|
FillTri(px, s, 32f, 58f, 21f, 46f, 43f, 46f, Color.white);
|
|
});
|
|
_drop.name = "S3Drop";
|
|
return _drop;
|
|
}
|
|
|
|
public static Sprite PickupSprite()
|
|
{
|
|
if (_pickup != null) return _pickup;
|
|
_pickup = MakeGlyph(64, (px, s) =>
|
|
{
|
|
StrokeCar(px, s, 16f, 32f, 32f, 24f, 2.6f);
|
|
StrokeLine(px, s, 32f, 28f, 32f, 14f, 2.8f, Color.white);
|
|
FillTri(px, s, 32f, 6f, 21f, 18f, 43f, 18f, Color.white);
|
|
});
|
|
_pickup.name = "S3Pickup";
|
|
return _pickup;
|
|
}
|
|
|
|
static void StrokeCar(Color[] px, int s, float x, float y, float w, float h, float thick)
|
|
{
|
|
StrokeLine(px, s, x, y, x + w, y, thick, Color.white);
|
|
StrokeLine(px, s, x + w, y, x + w, y + h, thick, Color.white);
|
|
StrokeLine(px, s, x + w, y + h, x, y + h, thick, Color.white);
|
|
StrokeLine(px, s, x, y + h, x, y, thick, Color.white);
|
|
float wy = y + 4f;
|
|
FillCircle(px, s, x + w * 0.28f, wy, 3.2f, Color.white);
|
|
FillCircle(px, s, x + w * 0.72f, wy, 3.2f, Color.white);
|
|
}
|
|
|
|
public static void TintButton(Button btn, bool on, Color onColor)
|
|
{
|
|
if (btn == null) return;
|
|
var img = btn.targetGraphic as Image;
|
|
if (img == null) return;
|
|
img.color = on ? onColor : new Color(0.22f, 0.22f, 0.24f, 1f);
|
|
}
|
|
|
|
static Sprite MakeGlyph(int s, System.Action<Color[], int> draw)
|
|
{
|
|
var tex = new Texture2D(s, s, TextureFormat.RGBA32, false)
|
|
{
|
|
filterMode = FilterMode.Bilinear,
|
|
hideFlags = HideFlags.HideAndDontSave,
|
|
};
|
|
var px = new Color[s * s];
|
|
draw(px, s);
|
|
tex.SetPixels(px);
|
|
tex.Apply(false, true);
|
|
return Sprite.Create(tex, new Rect(0f, 0f, s, s), new Vector2(0.5f, 0.5f), 100f);
|
|
}
|
|
|
|
static void FillCircle(Color[] px, int s, float cx, float cy, float r, Color c)
|
|
{
|
|
for (int y = 0; y < s; y++)
|
|
for (int x = 0; x < s; x++)
|
|
{
|
|
float d = Vector2.Distance(new Vector2(x + 0.5f, y + 0.5f), new Vector2(cx, cy));
|
|
float a = Mathf.Clamp01(r + 0.5f - d);
|
|
if (a <= 0f) continue;
|
|
int i = y * s + x;
|
|
if (c.a <= 0.01f) px[i] = Color.clear;
|
|
else
|
|
{
|
|
Color dcol = c;
|
|
dcol.a *= a;
|
|
px[i] = Blend(px[i], dcol);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void FillTri(Color[] px, int s, float x1, float y1, float x2, float y2, float x3, float y3, Color c)
|
|
{
|
|
for (int y = 0; y < s; y++)
|
|
for (int x = 0; x < s; x++)
|
|
{
|
|
float pxp = x + 0.5f, pyp = y + 0.5f;
|
|
if (!InTri(pxp, pyp, x1, y1, x2, y2, x3, y3)) continue;
|
|
px[y * s + x] = Blend(px[y * s + x], c);
|
|
}
|
|
}
|
|
|
|
static bool InTri(float px, float py, float x1, float y1, float x2, float y2, float x3, float y3)
|
|
{
|
|
float d1 = Sign(px, py, x1, y1, x2, y2);
|
|
float d2 = Sign(px, py, x2, y2, x3, y3);
|
|
float d3 = Sign(px, py, x3, y3, x1, y1);
|
|
bool hasNeg = d1 < 0 || d2 < 0 || d3 < 0;
|
|
bool hasPos = d1 > 0 || d2 > 0 || d3 > 0;
|
|
return !(hasNeg && hasPos);
|
|
}
|
|
|
|
static float Sign(float px, float py, float x1, float y1, float x2, float y2) =>
|
|
(px - x2) * (y1 - y2) - (x1 - x2) * (py - y2);
|
|
|
|
static void StrokeLine(Color[] px, int s, float x0, float y0, float x1, float y1, float thick, Color c)
|
|
{
|
|
for (int y = 0; y < s; y++)
|
|
for (int x = 0; x < s; x++)
|
|
{
|
|
float d = DistToSeg(x + 0.5f, y + 0.5f, x0, y0, x1, y1);
|
|
float a = Mathf.Clamp01(thick + 0.5f - d);
|
|
if (a <= 0f) continue;
|
|
Color dcol = c;
|
|
dcol.a *= a;
|
|
px[y * s + x] = Blend(px[y * s + x], dcol);
|
|
}
|
|
}
|
|
|
|
static float DistToSeg(float px, float py, float x0, float y0, float x1, float y1)
|
|
{
|
|
float dx = x1 - x0, dy = y1 - y0;
|
|
float l2 = dx * dx + dy * dy;
|
|
if (l2 < 0.0001f) return Vector2.Distance(new Vector2(px, py), new Vector2(x0, y0));
|
|
float t = Mathf.Clamp01(((px - x0) * dx + (py - y0) * dy) / l2);
|
|
return Vector2.Distance(new Vector2(px, py), new Vector2(x0 + t * dx, y0 + t * dy));
|
|
}
|
|
|
|
static Color Blend(Color under, Color over)
|
|
{
|
|
float a = over.a + under.a * (1f - over.a);
|
|
if (a < 0.0001f) return Color.clear;
|
|
return new Color(
|
|
(over.r * over.a + under.r * under.a * (1f - over.a)) / a,
|
|
(over.g * over.a + under.g * under.a * (1f - over.a)) / a,
|
|
(over.b * over.a + under.b * under.a * (1f - over.a)) / a,
|
|
a);
|
|
}
|
|
|
|
public static TMP_FontAsset? TmpFont()
|
|
{
|
|
if (_tmp != null) return _tmp;
|
|
try { _tmp = TMP_Settings.defaultFontAsset; } catch { }
|
|
if (_tmp == null)
|
|
{
|
|
var all = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
|
|
if (all != null && all.Length > 0) _tmp = all[0];
|
|
}
|
|
return _tmp;
|
|
}
|
|
|
|
public static Font UiFont()
|
|
{
|
|
if (_uiFont != null) return _uiFont;
|
|
_uiFont = Resources.GetBuiltinResource<Font>("Arial.ttf");
|
|
return _uiFont;
|
|
}
|
|
|
|
public static RectTransform Rt(GameObject go) => (RectTransform)go.transform;
|
|
|
|
public static Image MakeImage(RectTransform parent, string name, Color color, bool raycast, bool round = true)
|
|
{
|
|
var go = new GameObject(name, typeof(RectTransform), typeof(Image));
|
|
var rt = Rt(go);
|
|
rt.SetParent(parent, false);
|
|
rt.anchorMin = rt.anchorMax = new Vector2(0.5f, 0.5f);
|
|
rt.pivot = new Vector2(0.5f, 0.5f);
|
|
var img = go.GetComponent<Image>();
|
|
if (round)
|
|
{
|
|
img.sprite = RoundSprite();
|
|
img.type = Image.Type.Sliced;
|
|
img.pixelsPerUnitMultiplier = 1f;
|
|
}
|
|
else
|
|
{
|
|
img.sprite = White();
|
|
img.type = Image.Type.Simple;
|
|
}
|
|
img.color = color;
|
|
img.raycastTarget = raycast;
|
|
return img;
|
|
}
|
|
|
|
public static Image MakeIcon(RectTransform parent, string name, Sprite sprite, Color color, bool raycast)
|
|
{
|
|
var go = new GameObject(name, typeof(RectTransform), typeof(Image));
|
|
var rt = Rt(go);
|
|
rt.SetParent(parent, false);
|
|
var img = go.GetComponent<Image>();
|
|
img.sprite = sprite;
|
|
img.type = Image.Type.Simple;
|
|
img.preserveAspect = true;
|
|
img.color = color;
|
|
img.raycastTarget = raycast;
|
|
return img;
|
|
}
|
|
|
|
public static TextMeshProUGUI Tmp(
|
|
RectTransform parent, string name, float size, Color color,
|
|
FontStyles style = FontStyles.Normal, TextAlignmentOptions align = TextAlignmentOptions.TopLeft)
|
|
{
|
|
var go = new GameObject(name, typeof(RectTransform), typeof(TextMeshProUGUI));
|
|
var rt = Rt(go);
|
|
rt.SetParent(parent, false);
|
|
var tmp = go.GetComponent<TextMeshProUGUI>();
|
|
tmp.fontSize = size;
|
|
tmp.color = color;
|
|
tmp.fontStyle = style;
|
|
tmp.alignment = align;
|
|
tmp.raycastTarget = false;
|
|
tmp.textWrappingMode = TextWrappingModes.Normal;
|
|
tmp.overflowMode = TextOverflowModes.Truncate;
|
|
var font = TmpFont();
|
|
if (font != null) tmp.font = font;
|
|
return tmp;
|
|
}
|
|
|
|
public static Button Button(RectTransform parent, string name, string label, Vector2 size)
|
|
{
|
|
var img = MakeImage(parent, name, new Color(0.22f, 0.22f, 0.24f, 1f), raycast: true);
|
|
img.rectTransform.sizeDelta = size;
|
|
var btn = img.gameObject.AddComponent<Button>();
|
|
btn.targetGraphic = img;
|
|
var colors = btn.colors;
|
|
colors.highlightedColor = new Color(0.32f, 0.32f, 0.34f, 1f);
|
|
colors.pressedColor = new Color(0.16f, 0.16f, 0.18f, 1f);
|
|
btn.colors = colors;
|
|
if (!string.IsNullOrEmpty(label))
|
|
{
|
|
var text = Tmp(img.rectTransform, "Label", 12f, Color.white, FontStyles.Normal, TextAlignmentOptions.Center);
|
|
Stretch(text.rectTransform, 4f);
|
|
text.text = label;
|
|
text.raycastTarget = false;
|
|
}
|
|
return btn;
|
|
}
|
|
|
|
public static Button IconButton(RectTransform parent, string name, Sprite icon, Vector2 size)
|
|
{
|
|
var btn = Button(parent, name, "", size);
|
|
var img = MakeIcon(btn.GetComponent<RectTransform>(), "Icon", icon, Color.white, raycast: false);
|
|
img.rectTransform.anchorMin = img.rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
|
|
img.rectTransform.sizeDelta = new Vector2(size.x * 0.55f, size.y * 0.55f);
|
|
return btn;
|
|
}
|
|
|
|
public static Toggle Toggle(RectTransform parent, string name, string label)
|
|
{
|
|
var img = MakeImage(parent, name, new Color(0.18f, 0.18f, 0.2f, 1f), raycast: true);
|
|
var tog = img.gameObject.AddComponent<Toggle>();
|
|
tog.targetGraphic = img;
|
|
var check = MakeImage(img.rectTransform, "Check", new Color(0.45f, 0.72f, 0.42f, 1f), raycast: false);
|
|
check.rectTransform.anchorMin = new Vector2(0f, 0.5f);
|
|
check.rectTransform.anchorMax = new Vector2(0f, 0.5f);
|
|
check.rectTransform.pivot = new Vector2(0f, 0.5f);
|
|
check.rectTransform.anchoredPosition = new Vector2(6f, 0f);
|
|
check.rectTransform.sizeDelta = new Vector2(12f, 12f);
|
|
tog.graphic = check;
|
|
var text = Tmp(img.rectTransform, "Label", 12f, Color.white, FontStyles.Normal, TextAlignmentOptions.MidlineLeft);
|
|
text.rectTransform.anchorMin = new Vector2(0f, 0f);
|
|
text.rectTransform.anchorMax = new Vector2(1f, 1f);
|
|
text.rectTransform.offsetMin = new Vector2(22f, 0f);
|
|
text.rectTransform.offsetMax = Vector2.zero;
|
|
text.text = label;
|
|
return tog;
|
|
}
|
|
|
|
public static InputField NotesField(RectTransform parent, Vector2 size)
|
|
{
|
|
var img = MakeImage(parent, "Notes", new Color(0.97f, 0.94f, 0.86f, 1f), raycast: true);
|
|
img.rectTransform.sizeDelta = size;
|
|
var field = img.gameObject.AddComponent<InputField>();
|
|
var textGo = new GameObject("Text", typeof(RectTransform), typeof(Text));
|
|
var textRt = Rt(textGo);
|
|
textRt.SetParent(img.rectTransform, false);
|
|
Stretch(textRt, 4f);
|
|
var text = textGo.GetComponent<Text>();
|
|
text.font = UiFont();
|
|
text.fontSize = 11;
|
|
text.color = new Color(0.22f, 0.18f, 0.14f, 1f);
|
|
text.supportRichText = false;
|
|
text.alignment = TextAnchor.UpperLeft;
|
|
text.horizontalOverflow = HorizontalWrapMode.Wrap;
|
|
text.verticalOverflow = VerticalWrapMode.Truncate;
|
|
field.textComponent = text;
|
|
field.lineType = InputField.LineType.MultiLineNewline;
|
|
field.customCaretColor = true;
|
|
field.caretColor = new Color(0.2f, 0.15f, 0.1f, 1f);
|
|
field.placeholder = null;
|
|
return field;
|
|
}
|
|
|
|
public static Image AddShadow(RectTransform host)
|
|
{
|
|
var sh = MakeImage(host, "Shadow", new Color(0f, 0f, 0f, 0.42f), raycast: false);
|
|
Stretch(sh.rectTransform, 0f);
|
|
sh.rectTransform.anchoredPosition = new Vector2(3f, -3f);
|
|
sh.rectTransform.SetAsFirstSibling();
|
|
return sh;
|
|
}
|
|
|
|
public static Image AddResizeGrip(RectTransform panel, System.Action? onEnd, float minW, float minH)
|
|
{
|
|
var grip = MakeImage(panel, "Resize", new Color(1f, 1f, 1f, 0.28f), raycast: true);
|
|
grip.rectTransform.anchorMin = grip.rectTransform.anchorMax = new Vector2(1f, 0f);
|
|
grip.rectTransform.pivot = new Vector2(1f, 0f);
|
|
grip.rectTransform.anchoredPosition = new Vector2(-3f, 3f);
|
|
grip.rectTransform.sizeDelta = new Vector2(14f, 14f);
|
|
grip.gameObject.AddComponent<PanelResize>().Bind(panel, onEnd, minW, minH);
|
|
grip.transform.SetAsLastSibling();
|
|
return grip;
|
|
}
|
|
|
|
public static Scrollbar HScroll(RectTransform parent)
|
|
{
|
|
var bg = MakeImage(parent, "HScroll", new Color(0.12f, 0.12f, 0.14f, 0.7f), raycast: true);
|
|
var handle = MakeImage(bg.rectTransform, "Handle", new Color(0.55f, 0.55f, 0.58f, 0.95f), raycast: true);
|
|
Stretch(handle.rectTransform, 2f);
|
|
var sb = bg.gameObject.AddComponent<Scrollbar>();
|
|
sb.handleRect = handle.rectTransform;
|
|
sb.targetGraphic = handle;
|
|
sb.direction = Scrollbar.Direction.LeftToRight;
|
|
sb.transition = Selectable.Transition.ColorTint;
|
|
return sb;
|
|
}
|
|
|
|
public static void Stretch(RectTransform rt, float pad)
|
|
{
|
|
rt.anchorMin = Vector2.zero;
|
|
rt.anchorMax = Vector2.one;
|
|
rt.offsetMin = new Vector2(pad, pad);
|
|
rt.offsetMax = new Vector2(-pad, -pad);
|
|
rt.pivot = new Vector2(0.5f, 0.5f);
|
|
}
|
|
|
|
public static void BottomLeft(RectTransform rt, Vector2 pos, Vector2 size)
|
|
{
|
|
rt.anchorMin = rt.anchorMax = new Vector2(0f, 0f);
|
|
rt.pivot = new Vector2(0f, 0f);
|
|
rt.anchoredPosition = pos;
|
|
rt.sizeDelta = size;
|
|
}
|
|
|
|
public static Color BandInk(Color band)
|
|
{
|
|
float lum = band.r * 0.3f + band.g * 0.59f + band.b * 0.11f;
|
|
return lum > 0.55f ? new Color(0.16f, 0.14f, 0.12f, 1f) : Color.white;
|
|
}
|
|
}
|