feat: Initial commit
This commit is contained in:
63
.gitattributes
vendored
Normal file
63
.gitattributes
vendored
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
###############################################################################
|
||||||
|
# Set default behavior to automatically normalize line endings.
|
||||||
|
###############################################################################
|
||||||
|
* text=auto
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Set default behavior for command prompt diff.
|
||||||
|
#
|
||||||
|
# This is need for earlier builds of msysgit that does not have it on by
|
||||||
|
# default for csharp files.
|
||||||
|
# Note: This is only used by command line
|
||||||
|
###############################################################################
|
||||||
|
#*.cs diff=csharp
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Set the merge driver for project and solution files
|
||||||
|
#
|
||||||
|
# Merging from the command prompt will add diff markers to the files if there
|
||||||
|
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||||
|
# the diff markers are never inserted). Diff markers may cause the following
|
||||||
|
# file extensions to fail to load in VS. An alternative would be to treat
|
||||||
|
# these files as binary and thus will always conflict and require user
|
||||||
|
# intervention with every merge. To do so, just uncomment the entries below
|
||||||
|
###############################################################################
|
||||||
|
#*.sln merge=binary
|
||||||
|
#*.csproj merge=binary
|
||||||
|
#*.vbproj merge=binary
|
||||||
|
#*.vcxproj merge=binary
|
||||||
|
#*.vcproj merge=binary
|
||||||
|
#*.dbproj merge=binary
|
||||||
|
#*.fsproj merge=binary
|
||||||
|
#*.lsproj merge=binary
|
||||||
|
#*.wixproj merge=binary
|
||||||
|
#*.modelproj merge=binary
|
||||||
|
#*.sqlproj merge=binary
|
||||||
|
#*.wwaproj merge=binary
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# behavior for image files
|
||||||
|
#
|
||||||
|
# image files are treated as binary by default.
|
||||||
|
###############################################################################
|
||||||
|
#*.jpg binary
|
||||||
|
#*.png binary
|
||||||
|
#*.gif binary
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# diff behavior for common document formats
|
||||||
|
#
|
||||||
|
# Convert binary document formats to text before diffing them. This feature
|
||||||
|
# is only available from the command line. Turn it on by uncommenting the
|
||||||
|
# entries below.
|
||||||
|
###############################################################################
|
||||||
|
#*.doc diff=astextplain
|
||||||
|
#*.DOC diff=astextplain
|
||||||
|
#*.docx diff=astextplain
|
||||||
|
#*.DOCX diff=astextplain
|
||||||
|
#*.dot diff=astextplain
|
||||||
|
#*.DOT diff=astextplain
|
||||||
|
#*.pdf diff=astextplain
|
||||||
|
#*.PDF diff=astextplain
|
||||||
|
#*.rtf diff=astextplain
|
||||||
|
#*.RTF diff=astextplain
|
63
.gitignore
vendored
Normal file
63
.gitignore
vendored
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# This .gitignore file should be placed at the root of your Unity project directory
|
||||||
|
#
|
||||||
|
# Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore
|
||||||
|
#
|
||||||
|
/[Ll]ibrary/
|
||||||
|
/[Tt]emp/
|
||||||
|
/[Oo]bj/
|
||||||
|
/[Bb]uild/
|
||||||
|
/[Bb]uilds/
|
||||||
|
/[Ll]ogs/
|
||||||
|
/[Mm]emoryCaptures/
|
||||||
|
|
||||||
|
# Asset meta data should only be ignored when the corresponding asset is also ignored
|
||||||
|
!/[Aa]ssets/**/*.meta
|
||||||
|
|
||||||
|
# Uncomment this line if you wish to ignore the asset store tools plugin
|
||||||
|
# /[Aa]ssets/AssetStoreTools*
|
||||||
|
|
||||||
|
# Autogenerated Jetbrains Rider plugin
|
||||||
|
[Aa]ssets/Plugins/Editor/JetBrains*
|
||||||
|
|
||||||
|
# Visual Studio cache directory
|
||||||
|
.vs/
|
||||||
|
|
||||||
|
# Gradle cache directory
|
||||||
|
.gradle/
|
||||||
|
|
||||||
|
# Autogenerated VS/MD/Consulo solution and project files
|
||||||
|
ExportedObj/
|
||||||
|
.consulo/
|
||||||
|
*.csproj
|
||||||
|
*.unityproj
|
||||||
|
*.sln
|
||||||
|
*.suo
|
||||||
|
*.tmp
|
||||||
|
*.user
|
||||||
|
*.userprefs
|
||||||
|
*.pidb
|
||||||
|
*.booproj
|
||||||
|
*.svd
|
||||||
|
*.pdb
|
||||||
|
*.mdb
|
||||||
|
*.opendb
|
||||||
|
*.VC.db
|
||||||
|
|
||||||
|
# Unity3D generated meta files
|
||||||
|
*.pidb.meta
|
||||||
|
*.pdb.meta
|
||||||
|
*.mdb.meta
|
||||||
|
|
||||||
|
# Unity3D generated file on crash reports
|
||||||
|
sysinfo.txt
|
||||||
|
|
||||||
|
# Builds
|
||||||
|
*.apk
|
||||||
|
*.unitypackage
|
||||||
|
|
||||||
|
# Crashlytics generated file
|
||||||
|
crashlytics-build.properties
|
||||||
|
|
||||||
|
#
|
||||||
|
/Local
|
||||||
|
/UserSettings
|
6
.vsconfig
Normal file
6
.vsconfig
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0",
|
||||||
|
"components": [
|
||||||
|
"Microsoft.VisualStudio.Workload.ManagedGame"
|
||||||
|
]
|
||||||
|
}
|
8
Assets/Cryville.Common.meta
Normal file
8
Assets/Cryville.Common.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9a556074c98d68643a16692f7f3af693
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
16
Assets/Cryville.Common/Cryville.Common.asmdef
Normal file
16
Assets/Cryville.Common/Cryville.Common.asmdef
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "Cryville.Common",
|
||||||
|
"rootNamespace": "",
|
||||||
|
"references": [
|
||||||
|
"GUID:da293eebbcb9a4947a212534c52d1a32"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": false,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
7
Assets/Cryville.Common/Cryville.Common.asmdef.meta
Normal file
7
Assets/Cryville.Common/Cryville.Common.asmdef.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b92f9c7ac10b1c04e86fc48210f62ab1
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Assets/Cryville.Common/Font.meta
Normal file
8
Assets/Cryville.Common/Font.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6e89e0a49233f9e48a97ca7751345a54
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
113
Assets/Cryville.Common/Font/FontFile.cs
Normal file
113
Assets/Cryville.Common/Font/FontFile.cs
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
using Cryville.Common.IO;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Cryville.Common.Font {
|
||||||
|
public abstract class FontFile : IEnumerable<Typeface> {
|
||||||
|
public abstract int Count { get; }
|
||||||
|
public abstract Typeface this[int index] { get; }
|
||||||
|
protected FileInfo File { get; private set; }
|
||||||
|
protected BinaryReader Reader { get; private set; }
|
||||||
|
public FontFile(FileInfo file) {
|
||||||
|
File = file;
|
||||||
|
Reader = new BinaryReaderBE(new FileStream(file.FullName, FileMode.Open, FileAccess.Read));
|
||||||
|
}
|
||||||
|
public void Close() { Reader.Close(); }
|
||||||
|
|
||||||
|
public static FontFile Create(FileInfo file) {
|
||||||
|
switch (file.Extension) {
|
||||||
|
case ".ttf": case ".otf": return new FontFileTTF(file);
|
||||||
|
case ".ttc": case ".otc": return new FontFileTTC(file);
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Enumerator GetEnumerator() {
|
||||||
|
return new Enumerator(this);
|
||||||
|
}
|
||||||
|
IEnumerator<Typeface> IEnumerable<Typeface>.GetEnumerator() {
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() {
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Enumerator : IEnumerator<Typeface> {
|
||||||
|
readonly FontFile _self;
|
||||||
|
int _index;
|
||||||
|
internal Enumerator(FontFile self) {
|
||||||
|
_self = self;
|
||||||
|
_index = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Typeface Current {
|
||||||
|
get {
|
||||||
|
if (_index < 0)
|
||||||
|
throw new InvalidOperationException(_index == -1 ? "Enum not started" : "Enum ended");
|
||||||
|
return _self[_index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object IEnumerator.Current { get { return Current; } }
|
||||||
|
|
||||||
|
public void Dispose() {
|
||||||
|
_index = -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext() {
|
||||||
|
if (_index == -2) return false;
|
||||||
|
_index++;
|
||||||
|
if (_index >= _self.Count) {
|
||||||
|
_index = -2;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset() {
|
||||||
|
_index = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class FontFileTTF : FontFile {
|
||||||
|
public override int Count { get { return 1; } }
|
||||||
|
public override Typeface this[int index] {
|
||||||
|
get {
|
||||||
|
if (index != 0) throw new ArgumentOutOfRangeException("index");
|
||||||
|
try {
|
||||||
|
return new TypefaceTTF(Reader, File, index);
|
||||||
|
}
|
||||||
|
catch (Exception) {
|
||||||
|
throw new InvalidDataException("Invalid font");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public FontFileTTF(FileInfo file) : base(file) { }
|
||||||
|
}
|
||||||
|
public class FontFileTTC : FontFile {
|
||||||
|
readonly IReadOnlyList<uint> _offsets;
|
||||||
|
public override int Count { get { return _offsets.Count; } }
|
||||||
|
public override Typeface this[int index] {
|
||||||
|
get {
|
||||||
|
if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException("index");
|
||||||
|
Reader.BaseStream.Position = _offsets[index];
|
||||||
|
try {
|
||||||
|
return new TypefaceTTF(Reader, File, index);
|
||||||
|
}
|
||||||
|
catch (Exception) {
|
||||||
|
throw new InvalidDataException("Invalid font");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public FontFileTTC(FileInfo file) : base(file) {
|
||||||
|
try {
|
||||||
|
_offsets = new TTCHeader(Reader, 0).GetItems();
|
||||||
|
}
|
||||||
|
catch (Exception) {
|
||||||
|
throw new InvalidDataException("Invalid font");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.Common/Font/FontFile.cs.meta
Normal file
11
Assets/Cryville.Common/Font/FontFile.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c1804280aa04fb744a331a1d2dc0066b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
64
Assets/Cryville.Common/Font/FontManager.cs
Normal file
64
Assets/Cryville.Common/Font/FontManager.cs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Cryville.Common.Font {
|
||||||
|
public abstract class FontManager {
|
||||||
|
public IReadOnlyDictionary<string, Typeface> MapFullNameToTypeface { get; private set; }
|
||||||
|
public IReadOnlyDictionary<string, IReadOnlyCollection<Typeface>> MapNameToTypefaces { get; private set; }
|
||||||
|
public FontManager() {
|
||||||
|
var map1 = new Dictionary<string, Typeface>();
|
||||||
|
var map2 = new Dictionary<string, List<Typeface>>();
|
||||||
|
foreach (var f in EnumerateAllTypefaces()) {
|
||||||
|
if (!map1.ContainsKey(f.FullName)) {
|
||||||
|
map1.Add(f.FullName, f);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
List<Typeface> set2;
|
||||||
|
if (!map2.TryGetValue(f.FamilyName, out set2)) {
|
||||||
|
map2.Add(f.FamilyName, set2 = new List<Typeface>());
|
||||||
|
}
|
||||||
|
set2.Add(f);
|
||||||
|
}
|
||||||
|
MapFullNameToTypeface = map1;
|
||||||
|
MapNameToTypefaces = map2.ToDictionary(i => i.Key, i => (IReadOnlyCollection<Typeface>)i.Value);
|
||||||
|
}
|
||||||
|
protected abstract IEnumerable<Typeface> EnumerateAllTypefaces();
|
||||||
|
protected static IEnumerable<Typeface> ScanDirectoryForTypefaces(string dir) {
|
||||||
|
foreach (var f in new DirectoryInfo(dir).EnumerateFiles()) {
|
||||||
|
FontFile file;
|
||||||
|
try {
|
||||||
|
file = FontFile.Create(f);
|
||||||
|
}
|
||||||
|
catch (InvalidDataException) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (file == null) continue;
|
||||||
|
var enumerator = file.GetEnumerator();
|
||||||
|
while (enumerator.MoveNext()) {
|
||||||
|
Typeface ret;
|
||||||
|
try {
|
||||||
|
ret = enumerator.Current;
|
||||||
|
}
|
||||||
|
catch (InvalidDataException) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
yield return ret;
|
||||||
|
}
|
||||||
|
file.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class FontManagerAndroid : FontManager {
|
||||||
|
protected override IEnumerable<Typeface> EnumerateAllTypefaces() {
|
||||||
|
return ScanDirectoryForTypefaces("/system/fonts");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class FontManagerWindows : FontManager {
|
||||||
|
protected override IEnumerable<Typeface> EnumerateAllTypefaces() {
|
||||||
|
return ScanDirectoryForTypefaces("C:/Windows/Fonts");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.Common/Font/FontManager.cs.meta
Normal file
11
Assets/Cryville.Common/Font/FontManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9fd2cf4e5a96f5146a8b950c3647e4c9
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
343
Assets/Cryville.Common/Font/FontMatcher.cs
Normal file
343
Assets/Cryville.Common/Font/FontMatcher.cs
Normal file
@@ -0,0 +1,343 @@
|
|||||||
|
using Cryville.Culture;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Cryville.Common.Font {
|
||||||
|
public abstract class FontMatcher {
|
||||||
|
protected FontManager Manager { get; private set; }
|
||||||
|
public FontMatcher(FontManager manager) { Manager = manager; }
|
||||||
|
public abstract IEnumerable<Typeface> MatchLanguage(LanguageId lang, bool distinctFamily = false);
|
||||||
|
}
|
||||||
|
public class FallbackListFontMatcher : FontMatcher {
|
||||||
|
readonly LanguageMatching _matcher;
|
||||||
|
static readonly string UltimateFallbackScript = "zzzz";
|
||||||
|
public Dictionary<string, List<string>> MapScriptToTypefaces = new();
|
||||||
|
public static Dictionary<string, List<string>> GetDefaultWindowsFallbackMap() {
|
||||||
|
var map = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
FillKeysWithScripts(map, () => new List<string>());
|
||||||
|
// Reference: https://github.com/chromium/chromium/blob/main/third_party/blink/renderer/platform/fonts/win/font_fallback_win.cc
|
||||||
|
map[UltimateFallbackScript].Insert(0, "SimSun"); // Custom
|
||||||
|
map[UltimateFallbackScript].Insert(0, "SimHei"); // Custom
|
||||||
|
map[UltimateFallbackScript].Insert(0, "Microsoft YaHei"); // Custom
|
||||||
|
map[UltimateFallbackScript].Insert(0, "Arial");
|
||||||
|
map[UltimateFallbackScript].Insert(0, "Times New Roman");
|
||||||
|
map[UltimateFallbackScript].Insert(0, "Segoe UI"); // Custom
|
||||||
|
map["arab"].Insert(0, "Tahoma");
|
||||||
|
map["cyrl"].Insert(0, "Times New Roman");
|
||||||
|
map["grek"].Insert(0, "Times New Roman");
|
||||||
|
map["hebr"].Insert(0, "David");
|
||||||
|
map["jpan"].Insert(0, "MS PGothic");
|
||||||
|
map["latn"].Insert(0, "Times New Roman");
|
||||||
|
map["hans"].Insert(0, "SimSun");
|
||||||
|
map["hans"].Insert(0, "SimHei"); // Custom
|
||||||
|
map["thai"].Insert(0, "Tahoma");
|
||||||
|
map["hans"].Insert(0, "PMingLiU");
|
||||||
|
// Reference: https://learn.microsoft.com/en-us/globalization/input/font-support
|
||||||
|
var ver = Environment.OSVersion.Version;
|
||||||
|
if (ver >= new Version(5, 0)) { // Windows 2000
|
||||||
|
map["armn"].Insert(0, "Sylfaen");
|
||||||
|
map["deva"].Insert(0, "Mangal");
|
||||||
|
map["geor"].Insert(0, "Sylfaen");
|
||||||
|
map["taml"].Insert(0, "Latha");
|
||||||
|
}
|
||||||
|
if (ver >= new Version(5, 1)) { // Windows XP
|
||||||
|
map["gujr"].Insert(0, "Shruti");
|
||||||
|
map["guru"].Insert(0, "Raavi");
|
||||||
|
map["knda"].Insert(0, "Tunga");
|
||||||
|
map["syrc"].Insert(0, "Estrangelo Edessa");
|
||||||
|
map["telu"].Insert(0, "Gautami");
|
||||||
|
map["thaa"].Insert(0, "MV Boli");
|
||||||
|
// SP2
|
||||||
|
map["beng"].Insert(0, "Vrinda");
|
||||||
|
map["mlym"].Insert(0, "Kartika");
|
||||||
|
}
|
||||||
|
if (ver >= new Version(6, 0)) { // Windows Vista
|
||||||
|
map["cans"].Insert(0, "Euphemia");
|
||||||
|
map["cher"].Insert(0, "Plantagenet");
|
||||||
|
map["ethi"].Insert(0, "Nyala");
|
||||||
|
map["khmr"].Insert(0, "DaunPenh MoolBoran");
|
||||||
|
map["kore"].Insert(0, "Malgun Gothic"); // Reference: https://en.wikipedia.org/wiki/List_of_typefaces_included_with_Microsoft_Windows
|
||||||
|
map["laoo"].Insert(0, "DokChampa");
|
||||||
|
map["mong"].Insert(0, "Mongolian Baiti");
|
||||||
|
map["orya"].Insert(0, "Kalinga");
|
||||||
|
map["sinh"].Insert(0, "Iskoola Pota");
|
||||||
|
map["tibt"].Insert(0, "Microsoft Himalaya");
|
||||||
|
map["yiii"].Insert(0, "Microsoft Yi Baiti");
|
||||||
|
map["arab"].Insert(0, "Segoe UI");
|
||||||
|
map["cyrl"].Insert(0, "Segoe UI");
|
||||||
|
map["grek"].Insert(0, "Segoe UI");
|
||||||
|
map["latn"].Insert(0, "Segoe UI");
|
||||||
|
map["hans"].Add("SimSun-ExtB");
|
||||||
|
map["hant"].Add("MingLiU-ExtB");
|
||||||
|
map["hant"].Add("MingLiU_HKSCS-ExtB");
|
||||||
|
map["arab"].Add("Microsoft Uighur");
|
||||||
|
map["zmth"].Insert(0, "Cambria Math");
|
||||||
|
// Reference: https://en.wikipedia.org/wiki/List_of_CJK_fonts
|
||||||
|
map["jpan"].Insert(0, "Meiryo");
|
||||||
|
map["hans"].Insert(0, "Microsoft YaHei");
|
||||||
|
}
|
||||||
|
if (ver >= new Version(6, 1)) { // Windows 7
|
||||||
|
map["brai"].Insert(0, "Segoe UI Symbol");
|
||||||
|
map["dsrt"].Insert(0, "Segoe UI Symbol");
|
||||||
|
map["talu"].Insert(0, "Microsoft New Tai Lue");
|
||||||
|
map["ogam"].Insert(0, "Segoe UI Symbol");
|
||||||
|
map["osma"].Insert(0, "Ebrima");
|
||||||
|
map["phag"].Insert(0, "Microsoft PhagsPa");
|
||||||
|
map["runr"].Insert(0, "Segoe UI Symbol");
|
||||||
|
map["zsym"].Insert(0, "Segoe UI Symbol");
|
||||||
|
map["tale"].Insert(0, "Microsoft Tai Le");
|
||||||
|
map["tfng"].Insert(0, "Ebrima");
|
||||||
|
map["vaii"].Insert(0, "Ebrima");
|
||||||
|
}
|
||||||
|
if (ver >= new Version(6, 2)) { // Windows 8
|
||||||
|
map["glag"].Insert(0, "Segoe UI Symbol");
|
||||||
|
map["goth"].Insert(0, "Segoe UI Symbol");
|
||||||
|
map["hang"].Add("Malgun Gothic");
|
||||||
|
map["ital"].Insert(0, "Segoe UI Symbol");
|
||||||
|
map["lisu"].Insert(0, "Segoe UI");
|
||||||
|
map["mymr"].Insert(0, "Myanmar Text");
|
||||||
|
map["nkoo"].Insert(0, "Ebrima");
|
||||||
|
map["orkh"].Insert(0, "Segoe UI Symbol");
|
||||||
|
map["ethi"].Insert(0, "Ebrima");
|
||||||
|
map["cans"].Insert(0, "Gadugi");
|
||||||
|
map["hant"].Insert(0, "Microsoft JhengHei UI");
|
||||||
|
map["hans"].Insert(0, "Microsoft YaHei UI");
|
||||||
|
map["beng"].Insert(0, "Nirmala UI");
|
||||||
|
map["deva"].Insert(0, "Nirmala UI");
|
||||||
|
map["gujr"].Insert(0, "Nirmala UI");
|
||||||
|
map["guru"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
|
||||||
|
map["knda"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
|
||||||
|
map["mlym"].Insert(0, "Nirmala UI");
|
||||||
|
map["orya"].Insert(0, "Nirmala UI");
|
||||||
|
map["sinh"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
|
||||||
|
map["taml"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
|
||||||
|
map["telu"].Insert(0, "Nirmala UI");
|
||||||
|
map["armn"].Insert(0, "Segoe UI");
|
||||||
|
map["geor"].Insert(0, "Segoe UI");
|
||||||
|
map["hebr"].Insert(0, "Segoe UI");
|
||||||
|
}
|
||||||
|
if (ver >= new Version(6, 3)) { // Windows 8.1
|
||||||
|
map["bugi"].Insert(0, "Leelawadee UI");
|
||||||
|
map["copt"].Insert(0, "Segoe UI Symbol");
|
||||||
|
map["java"].Insert(0, "Javanese Text");
|
||||||
|
map["merc"].Insert(0, "Segoe UI Symbol");
|
||||||
|
map["olck"].Insert(0, "Nirmala UI");
|
||||||
|
map["sora"].Insert(0, "Nirmala UI");
|
||||||
|
map["khmr"].Insert(0, "Leelawadee UI");
|
||||||
|
map["laoo"].Insert(0, "Leelawadee UI");
|
||||||
|
map["thai"].Insert(0, "Leelawadee UI");
|
||||||
|
map["zsye"].Insert(0, "Segoe UI Emoji");
|
||||||
|
}
|
||||||
|
if (ver >= new Version(10, 0)) { // Windows 10
|
||||||
|
map["brah"].Insert(0, "Segoe UI Historic");
|
||||||
|
map["cari"].Insert(0, "Segoe UI Historic");
|
||||||
|
map["cprt"].Insert(0, "Segoe UI Historic");
|
||||||
|
map["egyp"].Insert(0, "Segoe UI Historic");
|
||||||
|
map["armi"].Insert(0, "Segoe UI Historic");
|
||||||
|
map["phli"].Insert(0, "Segoe UI Historic");
|
||||||
|
map["prti"].Insert(0, "Segoe UI Historic");
|
||||||
|
map["khar"].Insert(0, "Segoe UI Historic");
|
||||||
|
map["lyci"].Insert(0, "Segoe UI Historic");
|
||||||
|
map["lydi"].Insert(0, "Segoe UI Historic");
|
||||||
|
map["phnx"].Insert(0, "Segoe UI Historic");
|
||||||
|
map["xpeo"].Insert(0, "Segoe UI Historic");
|
||||||
|
map["sarb"].Insert(0, "Segoe UI Historic");
|
||||||
|
map["shaw"].Insert(0, "Segoe UI Historic");
|
||||||
|
map["xsux"].Insert(0, "Segoe UI Historic");
|
||||||
|
map["ugar"].Insert(0, "Segoe UI Historic");
|
||||||
|
// Segoe UI Symbol -> Segoe UI Historic
|
||||||
|
map["glag"].Insert(0, "Segoe UI Historic");
|
||||||
|
map["goth"].Insert(0, "Segoe UI Historic");
|
||||||
|
map["merc"].Insert(0, "Segoe UI Historic");
|
||||||
|
map["ogam"].Insert(0, "Segoe UI Historic");
|
||||||
|
map["ital"].Insert(0, "Segoe UI Historic");
|
||||||
|
map["orkh"].Insert(0, "Segoe UI Historic");
|
||||||
|
map["runr"].Insert(0, "Segoe UI Historic");
|
||||||
|
//
|
||||||
|
map["jpan"].Insert(0, "Yu Gothic UI");
|
||||||
|
map["zsym"].Add("Segoe MDL2 Assets");
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
public static Dictionary<string, List<string>> GetDefaultAndroidFallbackMap() {
|
||||||
|
var map = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
FillKeysWithScripts(map, () => new List<string>());
|
||||||
|
map[UltimateFallbackScript].Insert(0, "Noto Sans CJK TC"); // TODO Modify default fallback
|
||||||
|
map[UltimateFallbackScript].Insert(0, "Noto Sans CJK JP");
|
||||||
|
map[UltimateFallbackScript].Insert(0, "Noto Sans CJK SC");
|
||||||
|
map[UltimateFallbackScript].Insert(0, "Roboto");
|
||||||
|
map["zsye"].Insert(0, "Noto Color Emoji");
|
||||||
|
map["zsye"].Add("Noto Color Emoji Flags");
|
||||||
|
map["arab"].Insert(0, "Noto Naskh Arabic");
|
||||||
|
map["adlm"].Insert(0, "Noto Sans Adlam");
|
||||||
|
map["ahom"].Insert(0, "Noto Sans Ahom");
|
||||||
|
map["hluw"].Insert(0, "Noto Sans Anatolian Hieroglyphs");
|
||||||
|
map["armn"].Insert(0, "Noto Sans Armenian");
|
||||||
|
map["avst"].Insert(0, "Noto Sans Avestan");
|
||||||
|
map["bali"].Insert(0, "Noto Sans Balinese");
|
||||||
|
map["bamu"].Insert(0, "Noto Sans Bamum");
|
||||||
|
map["bass"].Insert(0, "Noto Sans Bassa Vah");
|
||||||
|
map["batk"].Insert(0, "Noto Sans Batak");
|
||||||
|
map["beng"].Insert(0, "Noto Sans Bengali");
|
||||||
|
map["bhks"].Insert(0, "Noto Sans Bhaiksuki");
|
||||||
|
map["brah"].Insert(0, "Noto Sans Brahmi");
|
||||||
|
map["bugi"].Insert(0, "Noto Sans Buginese");
|
||||||
|
map["buhd"].Insert(0, "Noto Sans Buhid");
|
||||||
|
map["jpan"].Insert(0, "Noto Sans CJK JP");
|
||||||
|
map["kore"].Insert(0, "Noto Sans CJK KR");
|
||||||
|
map["hans"].Insert(0, "Noto Sans CJK SC");
|
||||||
|
map["hant"].Insert(0, "Noto Sans CJK TC");
|
||||||
|
map["hant"].Add("Noto Sans CJK HK");
|
||||||
|
map["cans"].Insert(0, "Noto Sans Canadian Aboriginal");
|
||||||
|
map["cari"].Insert(0, "Noto Sans Carian");
|
||||||
|
map["cakm"].Insert(0, "Noto Sans Chakma");
|
||||||
|
map["cham"].Insert(0, "Noto Sans Cham");
|
||||||
|
map["cher"].Insert(0, "Noto Sans Cherokee");
|
||||||
|
map["copt"].Insert(0, "Noto Sans Coptic");
|
||||||
|
map["xsux"].Insert(0, "Noto Sans Cuneiform");
|
||||||
|
map["cprt"].Insert(0, "Noto Sans Cypriot");
|
||||||
|
map["dsrt"].Insert(0, "Noto Sans Deseret");
|
||||||
|
map["deva"].Insert(0, "Noto Sans Devanagari");
|
||||||
|
map["egyp"].Insert(0, "Noto Sans Egyptian Hieroglyphs");
|
||||||
|
map["elba"].Insert(0, "Noto Sans Elbasan");
|
||||||
|
map["ethi"].Insert(0, "Noto Sans Ethiopic");
|
||||||
|
map["geor"].Insert(0, "Noto Sans Georgian");
|
||||||
|
map["glag"].Insert(0, "Noto Sans Glagolitic");
|
||||||
|
map["goth"].Insert(0, "Noto Sans Gothic");
|
||||||
|
map["gran"].Insert(0, "Noto Sans Grantha");
|
||||||
|
map["gujr"].Insert(0, "Noto Sans Gujarati");
|
||||||
|
map["gong"].Insert(0, "Noto Sans Gunjala Gondi");
|
||||||
|
map["guru"].Insert(0, "Noto Sans Gurmukhi");
|
||||||
|
map["rohg"].Insert(0, "Noto Sans Hanifi Rohingya");
|
||||||
|
map["hano"].Insert(0, "Noto Sans Hanunoo");
|
||||||
|
map["hatr"].Insert(0, "Noto Sans Hatran");
|
||||||
|
map["hebr"].Insert(0, "Noto Sans Hebrew");
|
||||||
|
map["armi"].Insert(0, "Noto Sans Imperial Aramaic");
|
||||||
|
map["phli"].Insert(0, "Noto Sans Inscriptional Pahlavi");
|
||||||
|
map["prti"].Insert(0, "Noto Sans Inscriptional Parthian");
|
||||||
|
map["java"].Insert(0, "Noto Sans Javanese");
|
||||||
|
map["kthi"].Insert(0, "Noto Sans Kaithi");
|
||||||
|
map["knda"].Insert(0, "Noto Sans Kannada");
|
||||||
|
map["kali"].Insert(0, "Noto Sans KayahLi");
|
||||||
|
map["khar"].Insert(0, "Noto Sans Kharoshthi");
|
||||||
|
map["khmr"].Insert(0, "Noto Sans Khmer");
|
||||||
|
map["khoj"].Insert(0, "Noto Sans Khojki");
|
||||||
|
map["laoo"].Insert(0, "Noto Sans Lao");
|
||||||
|
map["lepc"].Insert(0, "Noto Sans Lepcha");
|
||||||
|
map["limb"].Insert(0, "Noto Sans Limbu");
|
||||||
|
map["lina"].Insert(0, "Noto Sans Linear A");
|
||||||
|
map["linb"].Insert(0, "Noto Sans Linear B");
|
||||||
|
map["lisu"].Insert(0, "Noto Sans Lisu");
|
||||||
|
map["lyci"].Insert(0, "Noto Sans Lycian");
|
||||||
|
map["lydi"].Insert(0, "Noto Sans Lydian");
|
||||||
|
map["mlym"].Insert(0, "Noto Sans Malayalam");
|
||||||
|
map["mand"].Insert(0, "Noto Sans Mandiac");
|
||||||
|
map["mani"].Insert(0, "Noto Sans Manichaean");
|
||||||
|
map["marc"].Insert(0, "Noto Sans Marchen");
|
||||||
|
map["gonm"].Insert(0, "Noto Sans Masaram Gondi");
|
||||||
|
map["medf"].Insert(0, "Noto Sans Medefaidrin");
|
||||||
|
map["mtei"].Insert(0, "Noto Sans Meetei Mayek");
|
||||||
|
map["merc"].Insert(0, "Noto Sans Meroitic");
|
||||||
|
map["mero"].Insert(0, "Noto Sans Meroitic");
|
||||||
|
map["plrd"].Insert(0, "Noto Sans Miao");
|
||||||
|
map["modi"].Insert(0, "Noto Sans Modi");
|
||||||
|
map["mong"].Insert(0, "Noto Sans Mongolian");
|
||||||
|
map["mroo"].Insert(0, "Noto Sans Mro");
|
||||||
|
map["mult"].Insert(0, "Noto Sans Multani");
|
||||||
|
map["mymr"].Insert(0, "Noto Sans Myanmar");
|
||||||
|
map["nkoo"].Insert(0, "Noto Sans Nko");
|
||||||
|
map["nbat"].Insert(0, "Noto Sans Nabataean");
|
||||||
|
map["talu"].Insert(0, "Noto Sans New Tai Lue");
|
||||||
|
map["newa"].Insert(0, "Noto Sans Newa");
|
||||||
|
map["ogam"].Insert(0, "Noto Sans Ogham");
|
||||||
|
map["olck"].Insert(0, "Noto Sans Ol Chiki");
|
||||||
|
map["ital"].Insert(0, "Noto Sans Old Italian");
|
||||||
|
map["narb"].Insert(0, "Noto Sans Old North Arabian");
|
||||||
|
map["perm"].Insert(0, "Noto Sans Old Permic");
|
||||||
|
map["xpeo"].Insert(0, "Noto Sans Old Persian");
|
||||||
|
map["sarb"].Insert(0, "Noto Sans Old South Arabian");
|
||||||
|
map["orkh"].Insert(0, "Noto Sans Old Turkic");
|
||||||
|
map["orya"].Insert(0, "Noto Sans Oriya");
|
||||||
|
map["osge"].Insert(0, "Noto Sans Osage");
|
||||||
|
map["osma"].Insert(0, "Noto Sans Osmanya");
|
||||||
|
map["hmng"].Insert(0, "Noto Sans Pahawh Hmong");
|
||||||
|
map["palm"].Insert(0, "Noto Sans Palmyrene");
|
||||||
|
map["pauc"].Insert(0, "Noto Sans Pau Cin Hau");
|
||||||
|
map["phag"].Insert(0, "Noto Sans Phags Pa");
|
||||||
|
map["phnx"].Insert(0, "Noto Sans Phoenician");
|
||||||
|
map["rjng"].Insert(0, "Noto Sans Rejang");
|
||||||
|
map["runr"].Insert(0, "Noto Sans Runic");
|
||||||
|
map["samr"].Insert(0, "Noto Sans Samaritan");
|
||||||
|
map["saur"].Insert(0, "Noto Sans Saurashtra");
|
||||||
|
map["shrd"].Insert(0, "Noto Sans Sharada");
|
||||||
|
map["shaw"].Insert(0, "Noto Sans Shavian");
|
||||||
|
map["sinh"].Insert(0, "Noto Sans Sinhala");
|
||||||
|
map["sora"].Insert(0, "Noto Sans Sora Sompeng");
|
||||||
|
map["soyo"].Insert(0, "Noto Sans Soyombo");
|
||||||
|
map["sund"].Insert(0, "Noto Sans Sundanese");
|
||||||
|
map["sylo"].Insert(0, "Noto Sans Syloti Nagri");
|
||||||
|
map["zsym"].Insert(0, "Noto Sans Symbols");
|
||||||
|
map["syrc"].Add("Noto Sans Syriac Eastern");
|
||||||
|
map["syrc"].Add("Noto Sans Syriac Western");
|
||||||
|
map["syrc"].Add("Noto Sans Syriac Estrangela");
|
||||||
|
map["tglg"].Insert(0, "Noto Sans Tagalog");
|
||||||
|
map["tagb"].Insert(0, "Noto Sans Tagbanwa");
|
||||||
|
map["tale"].Insert(0, "Noto Sans Tai Le");
|
||||||
|
map["lana"].Insert(0, "Noto Sans Tai Tham");
|
||||||
|
map["tavt"].Insert(0, "Noto Sans Tai Viet");
|
||||||
|
map["takr"].Insert(0, "Noto Sans Takri");
|
||||||
|
map["taml"].Insert(0, "Noto Sans Tamil");
|
||||||
|
map["telu"].Insert(0, "Noto Sans Telugu");
|
||||||
|
map["thaa"].Insert(0, "Noto Sans Thaana");
|
||||||
|
map["thai"].Insert(0, "Noto Sans Thai");
|
||||||
|
map["tfng"].Insert(0, "Noto Sans Tifinagh");
|
||||||
|
map["ugar"].Insert(0, "Noto Sans Ugaritic");
|
||||||
|
map["vaii"].Insert(0, "Noto Sans Vai");
|
||||||
|
map["wcho"].Insert(0, "Noto Sans Wancho");
|
||||||
|
map["wara"].Insert(0, "Noto Sans Warang Citi");
|
||||||
|
map["yiii"].Insert(0, "Noto Sans Yi");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
static void FillKeysWithScripts<T>(IDictionary<string, T> map, Func<T> value) {
|
||||||
|
foreach (var s in IdValidity.Enumerate("script")) map.Add(s, value());
|
||||||
|
}
|
||||||
|
|
||||||
|
public FallbackListFontMatcher(LanguageMatching matcher, FontManager manager) : base(manager) {
|
||||||
|
_matcher = matcher;
|
||||||
|
}
|
||||||
|
public override IEnumerable<Typeface> MatchLanguage(LanguageId lang, bool distinctFamily = false) {
|
||||||
|
var supported = MapScriptToTypefaces.Keys.Select(i => new LanguageId(i)).ToList();
|
||||||
|
bool flag = false;
|
||||||
|
while (_matcher.Match(lang, supported, out var match, out var distance)) {
|
||||||
|
if (distance > 40) break;
|
||||||
|
if (match.Script.Equals(UltimateFallbackScript, StringComparison.OrdinalIgnoreCase)) {
|
||||||
|
flag = true;
|
||||||
|
}
|
||||||
|
var candidates = MapScriptToTypefaces[match.Script];
|
||||||
|
foreach (var typeface in EnumerateTypefaces(candidates, distinctFamily)) {
|
||||||
|
yield return typeface;
|
||||||
|
}
|
||||||
|
supported.Remove(match);
|
||||||
|
}
|
||||||
|
if (flag) yield break;
|
||||||
|
foreach (var typeface in EnumerateTypefaces(MapScriptToTypefaces[UltimateFallbackScript], distinctFamily)) {
|
||||||
|
yield return typeface;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IEnumerable<Typeface> EnumerateTypefaces(List<string> candidates, bool distinctFamily) {
|
||||||
|
foreach (var candidate in candidates) {
|
||||||
|
if (Manager.MapFullNameToTypeface.TryGetValue(candidate, out var typeface1)) {
|
||||||
|
yield return typeface1;
|
||||||
|
}
|
||||||
|
if (distinctFamily) continue;
|
||||||
|
if (Manager.MapNameToTypefaces.TryGetValue(candidate, out var typefaces2)) {
|
||||||
|
foreach (var typeface in typefaces2) {
|
||||||
|
if (typeface1 == typeface) continue;
|
||||||
|
yield return typeface;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.Common/Font/FontMatcher.cs.meta
Normal file
11
Assets/Cryville.Common/Font/FontMatcher.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 157390bd8c4b14243b9109b88480a1c6
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
256
Assets/Cryville.Common/Font/FontTable.cs
Normal file
256
Assets/Cryville.Common/Font/FontTable.cs
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
#pragma warning disable IDE0049
|
||||||
|
namespace Cryville.Common.Font {
|
||||||
|
public abstract class FontTable<T> {
|
||||||
|
protected UInt32 Offset { get; private set; }
|
||||||
|
protected BinaryReader Reader { get; private set; }
|
||||||
|
|
||||||
|
protected FontTable(BinaryReader reader, UInt32 offset) {
|
||||||
|
Reader = reader;
|
||||||
|
Offset = offset;
|
||||||
|
reader.BaseStream.Position = offset;
|
||||||
|
}
|
||||||
|
public abstract IReadOnlyList<T> GetItems();
|
||||||
|
}
|
||||||
|
public abstract class FontTable<T, U> : FontTable<T> {
|
||||||
|
protected FontTable(BinaryReader reader, UInt32 offset) : base(reader, offset) { }
|
||||||
|
public abstract U GetSubTable(T item);
|
||||||
|
}
|
||||||
|
public sealed class TTCHeader : FontTable<UInt32, TableDirectory> {
|
||||||
|
readonly String ttcTag;
|
||||||
|
readonly UInt16 majorVersion;
|
||||||
|
readonly UInt16 minorVersion;
|
||||||
|
readonly UInt32 numFonts;
|
||||||
|
readonly List<UInt32> tableDirectoryOffsets = new List<UInt32>();
|
||||||
|
#pragma warning disable IDE0052 // Reserved
|
||||||
|
readonly String dsigTag;
|
||||||
|
readonly UInt32 dsigLength;
|
||||||
|
readonly UInt32 dsigOffset;
|
||||||
|
#pragma warning restore IDE0052 // Reserved
|
||||||
|
public TTCHeader(BinaryReader reader, UInt32 offset) : base(reader, offset) {
|
||||||
|
ttcTag = reader.ReadTag();
|
||||||
|
if (ttcTag != "ttcf") throw new NotSupportedException();
|
||||||
|
majorVersion = reader.ReadUInt16();
|
||||||
|
minorVersion = reader.ReadUInt16();
|
||||||
|
if (minorVersion != 0) throw new NotSupportedException();
|
||||||
|
numFonts = reader.ReadUInt32();
|
||||||
|
for (UInt32 i = 0; i < numFonts; i++) tableDirectoryOffsets.Add(reader.ReadUInt32());
|
||||||
|
if (majorVersion == 2) {
|
||||||
|
dsigTag = reader.ReadTag();
|
||||||
|
dsigLength = reader.ReadUInt32();
|
||||||
|
dsigOffset = reader.ReadUInt32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override IReadOnlyList<UInt32> GetItems() {
|
||||||
|
return tableDirectoryOffsets;
|
||||||
|
}
|
||||||
|
public override TableDirectory GetSubTable(UInt32 item) {
|
||||||
|
var i = item;
|
||||||
|
return new TableDirectory(Reader, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public sealed class TableDirectory : FontTable<TableRecord, object> {
|
||||||
|
readonly UInt32 sfntVersion;
|
||||||
|
readonly UInt16 numTables;
|
||||||
|
#pragma warning disable IDE0052 // Reserved
|
||||||
|
readonly UInt16 searchRange;
|
||||||
|
readonly UInt16 entrySelector;
|
||||||
|
readonly UInt16 rangeShift;
|
||||||
|
#pragma warning restore IDE0052 // Reserved
|
||||||
|
readonly List<TableRecord> tableRecords = new List<TableRecord>();
|
||||||
|
public TableDirectory(BinaryReader reader, UInt32 offset) : base(reader, offset) {
|
||||||
|
sfntVersion = reader.ReadUInt32();
|
||||||
|
if (sfntVersion != 0x00010000 && sfntVersion != 0x4F54544F &&
|
||||||
|
sfntVersion != 0x74727565 && sfntVersion != 0x74797031) throw new NotSupportedException();
|
||||||
|
numTables = reader.ReadUInt16();
|
||||||
|
searchRange = reader.ReadUInt16();
|
||||||
|
entrySelector = reader.ReadUInt16();
|
||||||
|
rangeShift = reader.ReadUInt16();
|
||||||
|
for (int i = 0; i < numTables; i++)
|
||||||
|
tableRecords.Add(new TableRecord {
|
||||||
|
tableTag = reader.ReadTag(),
|
||||||
|
checksum = reader.ReadUInt32(),
|
||||||
|
offset = reader.ReadUInt32(),
|
||||||
|
length = reader.ReadUInt32(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public override IReadOnlyList<TableRecord> GetItems() {
|
||||||
|
return tableRecords;
|
||||||
|
}
|
||||||
|
public override object GetSubTable(TableRecord item) {
|
||||||
|
switch (item.tableTag) {
|
||||||
|
case "name": return new NameTable(Reader, item.offset);
|
||||||
|
case "meta": return new MetaTable(Reader, item.offset);
|
||||||
|
default: throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public struct TableRecord {
|
||||||
|
public string tableTag;
|
||||||
|
public UInt32 checksum;
|
||||||
|
public UInt32 offset;
|
||||||
|
public UInt32 length;
|
||||||
|
}
|
||||||
|
public sealed class NameTable : FontTable<NameRecord> {
|
||||||
|
readonly UInt16 version;
|
||||||
|
readonly UInt16 count;
|
||||||
|
readonly UInt16 storageOffset;
|
||||||
|
readonly List<NameRecord> nameRecord = new List<NameRecord>();
|
||||||
|
readonly UInt16 langTagCount;
|
||||||
|
readonly List<LangTagRecord> langTagRecord = new List<LangTagRecord>();
|
||||||
|
public NameTable(BinaryReader reader, UInt32 offset) : base(reader, offset) {
|
||||||
|
version = reader.ReadUInt16();
|
||||||
|
count = reader.ReadUInt16();
|
||||||
|
storageOffset = reader.ReadUInt16();
|
||||||
|
for (UInt16 i = 0; i < count; i++)
|
||||||
|
nameRecord.Add(new NameRecord(
|
||||||
|
reader.ReadUInt16(),
|
||||||
|
reader.ReadUInt16(),
|
||||||
|
reader.ReadUInt16(),
|
||||||
|
(NameID)reader.ReadUInt16(),
|
||||||
|
reader.ReadUInt16(),
|
||||||
|
reader.ReadUInt16()
|
||||||
|
));
|
||||||
|
if (version == 1) {
|
||||||
|
langTagCount = reader.ReadUInt16();
|
||||||
|
for (UInt16 i = 0; i < langTagCount; i++)
|
||||||
|
langTagRecord.Add(new LangTagRecord(
|
||||||
|
reader.ReadUInt16(),
|
||||||
|
reader.ReadUInt16()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
foreach (var i in nameRecord)
|
||||||
|
i.Load(reader, offset + storageOffset);
|
||||||
|
if (version == 1) {
|
||||||
|
foreach (var i in langTagRecord)
|
||||||
|
i.Load(reader, offset + storageOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public sealed override IReadOnlyList<NameRecord> GetItems() {
|
||||||
|
return nameRecord;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class NameRecord {
|
||||||
|
public UInt16 PlatformID { get; private set; }
|
||||||
|
public UInt16 EncodingID { get; private set; }
|
||||||
|
public UInt16 LanguageID { get; private set; }
|
||||||
|
public NameID NameID { get; private set; }
|
||||||
|
public UInt16 Length { get; private set; }
|
||||||
|
public UInt16 StringOffset { get; private set; }
|
||||||
|
public String Value { get; private set; }
|
||||||
|
public NameRecord(UInt16 platformID, UInt16 encodingID, UInt16 languageID, NameID nameID, UInt16 length, UInt16 stringOffset) {
|
||||||
|
PlatformID = platformID;
|
||||||
|
EncodingID = encodingID;
|
||||||
|
LanguageID = languageID;
|
||||||
|
NameID = nameID;
|
||||||
|
Length = length;
|
||||||
|
StringOffset = stringOffset;
|
||||||
|
}
|
||||||
|
public void Load(BinaryReader reader, UInt32 origin) {
|
||||||
|
reader.BaseStream.Position = origin + StringOffset;
|
||||||
|
Encoding encoding;
|
||||||
|
try {
|
||||||
|
switch (PlatformID) {
|
||||||
|
case 0: encoding = Encoding.BigEndianUnicode; break;
|
||||||
|
case 1: encoding = Encoding.GetEncoding(10000 + EncodingID); break;
|
||||||
|
case 3: encoding = Encoding.BigEndianUnicode; break;
|
||||||
|
default: return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (NotSupportedException) { return; }
|
||||||
|
catch (ArgumentException) { return; }
|
||||||
|
Value = encoding.GetString(reader.ReadBytes(Length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public enum NameID : UInt16 {
|
||||||
|
CopyrightNotice = 0,
|
||||||
|
FontFamilyName = 1,
|
||||||
|
FontSubfamilyName = 2,
|
||||||
|
UniqueFontIdentifier = 3,
|
||||||
|
FullFontName = 4,
|
||||||
|
VersionString = 5,
|
||||||
|
PostScriptName = 6,
|
||||||
|
Trademark = 7,
|
||||||
|
ManufacturerName = 8,
|
||||||
|
Designer = 9,
|
||||||
|
Description = 10,
|
||||||
|
URLVendor = 11,
|
||||||
|
URLDesigner = 12,
|
||||||
|
LicenseDescription = 13,
|
||||||
|
LicenseInfoURL = 14,
|
||||||
|
|
||||||
|
TypographicFamilyName = 16,
|
||||||
|
TypographicSubfamilyName = 17,
|
||||||
|
CompatibleFull = 18,
|
||||||
|
SampleText = 19,
|
||||||
|
PostScriptCIDFindfontName = 20,
|
||||||
|
WWSFamilyName = 21,
|
||||||
|
WWSSubfamilyName = 22,
|
||||||
|
LightBackgroundPalette = 23,
|
||||||
|
DarkBackgroundPalette = 24,
|
||||||
|
VariationsPostScriptNamePrefix = 25,
|
||||||
|
}
|
||||||
|
public class LangTagRecord {
|
||||||
|
public UInt16 Length { get; private set; }
|
||||||
|
public UInt16 LangTagOffset { get; private set; }
|
||||||
|
public String Value { get; private set; }
|
||||||
|
public LangTagRecord(UInt16 length, UInt16 langTagOffset) {
|
||||||
|
Length = length;
|
||||||
|
LangTagOffset = langTagOffset;
|
||||||
|
}
|
||||||
|
public void Load(BinaryReader reader, UInt32 origin) {
|
||||||
|
reader.BaseStream.Position = origin + LangTagOffset;
|
||||||
|
Value = Encoding.BigEndianUnicode.GetString(reader.ReadBytes(Length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public sealed class MetaTable : FontTable<DataMap> {
|
||||||
|
readonly UInt32 version;
|
||||||
|
#pragma warning disable IDE0052 // Reserved
|
||||||
|
readonly UInt32 flags;
|
||||||
|
#pragma warning restore IDE0052 // Reserved
|
||||||
|
readonly UInt32 dataMapCount;
|
||||||
|
readonly List<DataMap> dataMaps = new List<DataMap>();
|
||||||
|
public MetaTable(BinaryReader reader, UInt32 offset) : base(reader, offset) {
|
||||||
|
version = reader.ReadUInt32();
|
||||||
|
if (version != 1) throw new NotSupportedException();
|
||||||
|
flags = reader.ReadUInt32();
|
||||||
|
reader.ReadUInt32();
|
||||||
|
dataMapCount = reader.ReadUInt32();
|
||||||
|
for (UInt32 i = 0; i < dataMapCount; i++)
|
||||||
|
dataMaps.Add(new DataMap (
|
||||||
|
reader.ReadTag(),
|
||||||
|
reader.ReadUInt32(),
|
||||||
|
reader.ReadUInt32()
|
||||||
|
));
|
||||||
|
foreach (var i in dataMaps)
|
||||||
|
i.Load(reader, offset);
|
||||||
|
}
|
||||||
|
public sealed override IReadOnlyList<DataMap> GetItems() {
|
||||||
|
return dataMaps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class DataMap {
|
||||||
|
public String Tag { get; private set; }
|
||||||
|
public UInt32 DataOffset { get; private set; }
|
||||||
|
public UInt32 DataLength { get; private set; }
|
||||||
|
public String Value { get; private set; }
|
||||||
|
public DataMap(String tag, UInt32 dataOffset, UInt32 dataLength) {
|
||||||
|
Tag = tag;
|
||||||
|
DataOffset = dataOffset;
|
||||||
|
DataLength = dataLength;
|
||||||
|
}
|
||||||
|
public void Load(BinaryReader reader, UInt32 origin) {
|
||||||
|
reader.BaseStream.Position = origin + DataOffset;
|
||||||
|
Value = Encoding.ASCII.GetString(reader.ReadBytes((int)DataLength));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static class BinaryReaderExtensions {
|
||||||
|
public static string ReadTag(this BinaryReader reader) {
|
||||||
|
return Encoding.ASCII.GetString(reader.ReadBytes(4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.Common/Font/FontTable.cs.meta
Normal file
11
Assets/Cryville.Common/Font/FontTable.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3805282a4858eec4d8ff91826ed7e896
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
31
Assets/Cryville.Common/Font/Typeface.cs
Normal file
31
Assets/Cryville.Common/Font/Typeface.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Cryville.Common.Font {
|
||||||
|
public abstract class Typeface {
|
||||||
|
public FileInfo File { get; private set; }
|
||||||
|
public int IndexInFile { get; private set; }
|
||||||
|
public string FamilyName { get; protected set; }
|
||||||
|
public string SubfamilyName { get; protected set; }
|
||||||
|
public string FullName { get; protected set; }
|
||||||
|
protected abstract void GetName(BinaryReader reader);
|
||||||
|
|
||||||
|
public Typeface(BinaryReader reader, FileInfo file, int index) {
|
||||||
|
File = file;
|
||||||
|
IndexInFile = index;
|
||||||
|
GetName(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class TypefaceTTF : Typeface {
|
||||||
|
public TypefaceTTF(BinaryReader reader, FileInfo file, int index)
|
||||||
|
: base(reader, file, index) { }
|
||||||
|
|
||||||
|
protected override void GetName(BinaryReader reader) {
|
||||||
|
var dir = new TableDirectory(reader, (uint)reader.BaseStream.Position);
|
||||||
|
var nameTable = (NameTable)dir.GetSubTable((from i in dir.GetItems() where i.tableTag == "name" select i).Single());
|
||||||
|
FamilyName = (from i in nameTable.GetItems() where i.NameID == NameID.FontFamilyName && i.Value != null select i.Value).First();
|
||||||
|
SubfamilyName = (from i in nameTable.GetItems() where i.NameID == NameID.FontSubfamilyName && i.Value != null select i.Value).First();
|
||||||
|
FullName = (from i in nameTable.GetItems() where i.NameID == NameID.FullFontName && i.Value != null select i.Value).First();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.Common/Font/Typeface.cs.meta
Normal file
11
Assets/Cryville.Common/Font/Typeface.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 943e0cf54256f2a479996d877b2b0e84
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Assets/Cryville.Common/IO.meta
Normal file
8
Assets/Cryville.Common/IO.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: dbb2a81779b10f34489a8c83287d48bc
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
75
Assets/Cryville.Common/IO/BinaryReaderBE.cs
Normal file
75
Assets/Cryville.Common/IO/BinaryReaderBE.cs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Cryville.Common.IO {
|
||||||
|
public class BinaryReaderBE : BinaryReader {
|
||||||
|
readonly byte[] m_buffer = new byte[8];
|
||||||
|
|
||||||
|
public BinaryReaderBE(Stream input) : base(input) { }
|
||||||
|
|
||||||
|
public BinaryReaderBE(Stream input, Encoding encoding) : base(input, encoding) { }
|
||||||
|
|
||||||
|
public BinaryReaderBE(Stream input, Encoding encoding, bool leaveOpen) : base(input, encoding, leaveOpen) { }
|
||||||
|
|
||||||
|
public override short ReadInt16() {
|
||||||
|
FillBuffer(2);
|
||||||
|
return (short)(m_buffer[1] | (m_buffer[0] << 8));
|
||||||
|
}
|
||||||
|
public override ushort ReadUInt16() {
|
||||||
|
FillBuffer(2);
|
||||||
|
return (ushort)(m_buffer[1] | (m_buffer[0] << 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int ReadInt32() {
|
||||||
|
FillBuffer(4);
|
||||||
|
return m_buffer[3] | (m_buffer[2] << 8) | (m_buffer[1] << 16) | (m_buffer[0] << 24);
|
||||||
|
}
|
||||||
|
public override uint ReadUInt32() {
|
||||||
|
FillBuffer(4);
|
||||||
|
return (uint)(m_buffer[3] | (m_buffer[2] << 8) | (m_buffer[1] << 16) | (m_buffer[0] << 24));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override long ReadInt64() {
|
||||||
|
FillBuffer(8);
|
||||||
|
uint num = (uint)(m_buffer[7] | (m_buffer[6] << 8) | (m_buffer[5] << 16) | (m_buffer[4] << 24));
|
||||||
|
uint num2 = (uint)(m_buffer[3] | (m_buffer[2] << 8) | (m_buffer[1] << 16) | (m_buffer[0] << 24));
|
||||||
|
return (long)(((ulong)num2 << 32) | num);
|
||||||
|
}
|
||||||
|
public override ulong ReadUInt64() {
|
||||||
|
FillBuffer(8);
|
||||||
|
uint num = (uint)(m_buffer[7] | (m_buffer[6] << 8) | (m_buffer[5] << 16) | (m_buffer[4] << 24));
|
||||||
|
uint num2 = (uint)(m_buffer[3] | (m_buffer[2] << 8) | (m_buffer[1] << 16) | (m_buffer[0] << 24));
|
||||||
|
return ((ulong)num2 << 32) | num;
|
||||||
|
}
|
||||||
|
protected new void FillBuffer(int numBytes) {
|
||||||
|
if (m_buffer != null && (numBytes < 0 || numBytes > m_buffer.Length)) {
|
||||||
|
throw new ArgumentOutOfRangeException("numBytes", "Requested numBytes is larger than the internal buffer size");
|
||||||
|
}
|
||||||
|
|
||||||
|
int num = 0, num2;
|
||||||
|
if (BaseStream == null) {
|
||||||
|
throw new IOException("File not open");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numBytes == 1) {
|
||||||
|
num2 = BaseStream.ReadByte();
|
||||||
|
if (num2 == -1) {
|
||||||
|
throw new EndOfStreamException("The end of the stream is reached before numBytes could be read");
|
||||||
|
}
|
||||||
|
m_buffer[0] = (byte)num2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
num2 = BaseStream.Read(m_buffer, num, numBytes - num);
|
||||||
|
if (num2 == 0) {
|
||||||
|
throw new EndOfStreamException("The end of the stream is reached before numBytes could be read");
|
||||||
|
}
|
||||||
|
|
||||||
|
num += num2;
|
||||||
|
}
|
||||||
|
while (num < numBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.Common/IO/BinaryReaderBE.cs.meta
Normal file
11
Assets/Cryville.Common/IO/BinaryReaderBE.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8eaf9874d914c1349aabbb884c54b2d7
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Assets/Cryville.Common/Unity.meta
Normal file
8
Assets/Cryville.Common/Unity.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bf3461b95c913eb44b2f2e653bf5eb79
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Assets/Cryville.Common/Unity/UI.meta
Normal file
8
Assets/Cryville.Common/Unity/UI.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ad96c636bfa80024f8427b7755b56639
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
113
Assets/Cryville.Common/Unity/UI/AspectRatioLayoutElement.cs
Normal file
113
Assets/Cryville.Common/Unity/UI/AspectRatioLayoutElement.cs
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.EventSystems;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
namespace Cryville.Common.Unity.UI {
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="ILayoutElement" /> that takes the length of one axis to compute the preferred length of the other axis with respect to a aspect ratio.
|
||||||
|
/// </summary>
|
||||||
|
[AddComponentMenu("Layout/Aspect Ratio Layout Element")]
|
||||||
|
[ExecuteAlways]
|
||||||
|
public class AspectRatioLayoutElement : UIBehaviour, ILayoutElement {
|
||||||
|
[SerializeField]
|
||||||
|
[Tooltip("The aspect ratio. Width divided by height.")]
|
||||||
|
private float m_aspectRatio = 1;
|
||||||
|
/// <summary>
|
||||||
|
/// The aspect ratio. Width divided by height.
|
||||||
|
/// </summary>
|
||||||
|
public float AspectRatio {
|
||||||
|
get { return m_aspectRatio; }
|
||||||
|
set { SetProperty(ref m_aspectRatio, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
[Tooltip("Whether to compute the length of the y axis.")]
|
||||||
|
private bool m_isVertical = false;
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to compute the length of the y axis.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsVertical {
|
||||||
|
get { return m_isVertical; }
|
||||||
|
set { SetProperty(ref m_isVertical, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetProperty<T>(ref T prop, T value) {
|
||||||
|
if (Equals(prop, value)) return;
|
||||||
|
prop = value;
|
||||||
|
SetDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public float minWidth {
|
||||||
|
get {
|
||||||
|
return m_isVertical ? -1 : (transform as RectTransform).rect.height * m_aspectRatio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <inheritdoc />
|
||||||
|
public float preferredWidth { get { return minWidth; } }
|
||||||
|
/// <inheritdoc />
|
||||||
|
public float flexibleWidth { get { return -1; } }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public float minHeight {
|
||||||
|
get {
|
||||||
|
return m_isVertical ? (transform as RectTransform).rect.width / m_aspectRatio : -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <inheritdoc />
|
||||||
|
public float preferredHeight { get { return minHeight; } }
|
||||||
|
/// <inheritdoc />
|
||||||
|
public float flexibleHeight { get { return -1; } }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int layoutPriority { get { return 1; } }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void CalculateLayoutInputHorizontal() { }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void CalculateLayoutInputVertical() { }
|
||||||
|
|
||||||
|
protected override void OnEnable() {
|
||||||
|
base.OnEnable();
|
||||||
|
SetDirty();
|
||||||
|
}
|
||||||
|
protected override void OnBeforeTransformParentChanged() {
|
||||||
|
base.OnBeforeTransformParentChanged();
|
||||||
|
SetDirty();
|
||||||
|
}
|
||||||
|
protected override void OnTransformParentChanged() {
|
||||||
|
base.OnTransformParentChanged();
|
||||||
|
SetDirty();
|
||||||
|
}
|
||||||
|
protected override void OnDidApplyAnimationProperties() {
|
||||||
|
base.OnDidApplyAnimationProperties();
|
||||||
|
SetDirty();
|
||||||
|
}
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
protected override void OnValidate() {
|
||||||
|
base.OnValidate();
|
||||||
|
SetDirty();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
protected override void OnDisable() {
|
||||||
|
SetDirty();
|
||||||
|
base.OnDisable();
|
||||||
|
}
|
||||||
|
protected override void OnRectTransformDimensionsChange() {
|
||||||
|
base.OnRectTransformDimensionsChange();
|
||||||
|
SetDirty();
|
||||||
|
}
|
||||||
|
bool _delayedSetDirty;
|
||||||
|
private void SetDirty() {
|
||||||
|
if (!IsActive()) return;
|
||||||
|
if (CanvasUpdateRegistry.IsRebuildingLayout()) _delayedSetDirty = true;
|
||||||
|
else LayoutRebuilder.MarkLayoutForRebuild(transform as RectTransform);
|
||||||
|
}
|
||||||
|
void Update() {
|
||||||
|
if (!_delayedSetDirty) return;
|
||||||
|
_delayedSetDirty = false;
|
||||||
|
LayoutRebuilder.MarkLayoutForRebuild(transform as RectTransform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 96276a3ffc8f32c4fbb7035a6fb28e74
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,24 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Cryville.Common.Unity.UI {
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="DockLayoutGroup" /> that sets the aspect ratio of the docking element.
|
||||||
|
/// </summary>
|
||||||
|
[AddComponentMenu("Layout/Dock Aspect Ratio Layout Group")]
|
||||||
|
public sealed class DockAspectRatioLayoutGroup : DockLayoutGroup {
|
||||||
|
[SerializeField]
|
||||||
|
[Tooltip("The aspect ratio of the docking element.")]
|
||||||
|
private float m_dockAspectRatio = 1;
|
||||||
|
/// <summary>
|
||||||
|
/// The aspect ratio of the docking element.
|
||||||
|
/// </summary>
|
||||||
|
public float DockAspectRatio {
|
||||||
|
get { return m_dockAspectRatio; }
|
||||||
|
set { base.SetProperty(ref m_dockAspectRatio, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override float GetDockElementSize(Vector2 groupSize) {
|
||||||
|
return groupSize.y * m_dockAspectRatio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9023f24d2fe04fe45bf66a9d029591c1
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
136
Assets/Cryville.Common/Unity/UI/DockLayoutGroup.cs
Normal file
136
Assets/Cryville.Common/Unity/UI/DockLayoutGroup.cs
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
namespace Cryville.Common.Unity.UI {
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="LayoutGroup" /> that docks its first child element to one side.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class DockLayoutGroup : LayoutGroup {
|
||||||
|
/// <summary>
|
||||||
|
/// The dock side.
|
||||||
|
/// </summary>
|
||||||
|
public enum Side {
|
||||||
|
/// <summary>
|
||||||
|
/// Top.
|
||||||
|
/// </summary>
|
||||||
|
Top = 0,
|
||||||
|
/// <summary>
|
||||||
|
/// Right.
|
||||||
|
/// </summary>
|
||||||
|
Right = 1,
|
||||||
|
/// <summary>
|
||||||
|
/// Bottom.
|
||||||
|
/// </summary>
|
||||||
|
Bottom = 2,
|
||||||
|
/// <summary>
|
||||||
|
/// Left.
|
||||||
|
/// </summary>
|
||||||
|
Left = 3,
|
||||||
|
}
|
||||||
|
[SerializeField]
|
||||||
|
[Tooltip("The docking side of the first child element.")]
|
||||||
|
private Side m_side;
|
||||||
|
/// <summary>
|
||||||
|
/// The docking side of the first child element.
|
||||||
|
/// </summary>
|
||||||
|
public Side DockSide {
|
||||||
|
get { return m_side; }
|
||||||
|
set { SetProperty(ref m_side, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
[Tooltip("The slide index. The children slide along the cross axis.")]
|
||||||
|
private float m_slideIndex;
|
||||||
|
/// <summary>
|
||||||
|
/// The slide index. The children slide along the axis.
|
||||||
|
/// </summary>
|
||||||
|
public float SlideIndex {
|
||||||
|
get { return m_slideIndex; }
|
||||||
|
set { SetProperty(ref m_slideIndex, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public sealed override void CalculateLayoutInputHorizontal() { base.CalculateLayoutInputHorizontal(); CalcAlongAxis(0); }
|
||||||
|
/// <inheritdoc />
|
||||||
|
public sealed override void CalculateLayoutInputVertical() { CalcAlongAxis(1); }
|
||||||
|
/// <inheritdoc />
|
||||||
|
public sealed override void SetLayoutHorizontal() { SetChildrenAlongAxis(0); }
|
||||||
|
/// <inheritdoc />
|
||||||
|
public sealed override void SetLayoutVertical() { SetChildrenAlongAxis(1); }
|
||||||
|
|
||||||
|
private void CalcAlongAxis(int axis) {
|
||||||
|
int isHorizontal = (int)m_side & 1;
|
||||||
|
if ((isHorizontal ^ axis) == 1) {
|
||||||
|
SetLayoutInputForAxis(0, 0, 1, axis); // TODO
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float padding = isHorizontal == 0 ? m_Padding.horizontal : m_Padding.vertical;
|
||||||
|
float totalMin = 0, totalPreferred = 0, totalFlexible = 0;
|
||||||
|
for (int i = 0; i < rectChildren.Count; i++) {
|
||||||
|
GetChildSizes(rectChildren[i], axis, out float min, out float preferred, out float flexible);
|
||||||
|
if (min > totalMin) totalMin = min;
|
||||||
|
if (preferred > totalPreferred) totalPreferred = preferred;
|
||||||
|
if (flexible > totalFlexible) totalFlexible = flexible;
|
||||||
|
}
|
||||||
|
SetLayoutInputForAxis(totalMin + padding, totalPreferred + padding, totalFlexible, axis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void GetChildSizes(RectTransform child, int axis, out float min, out float preferred, out float flexible) {
|
||||||
|
min = LayoutUtility.GetMinSize(child, axis);
|
||||||
|
preferred = LayoutUtility.GetPreferredSize(child, axis);
|
||||||
|
flexible = LayoutUtility.GetFlexibleSize(child, axis);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float GetSlidePosition(float groupHeight, float dockHeight) {
|
||||||
|
bool d = Mathf.FloorToInt(m_slideIndex - Mathf.Floor(m_slideIndex / 2) * 2) == 0;
|
||||||
|
int l = Mathf.FloorToInt(m_slideIndex / 2);
|
||||||
|
float p = m_slideIndex - Mathf.Floor(m_slideIndex);
|
||||||
|
if (d) return l * groupHeight + p * dockHeight;
|
||||||
|
else return l * groupHeight + dockHeight + p * (groupHeight - dockHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetChildrenAlongAxis(int axis) {
|
||||||
|
int isHorizontal = (int)m_side & 1;
|
||||||
|
bool isReversed = m_side == Side.Right || m_side == Side.Bottom;
|
||||||
|
var rect = rectTransform.rect;
|
||||||
|
if ((isHorizontal ^ axis) == 1) {
|
||||||
|
float p0 = isHorizontal == 1 ? m_Padding.left : m_Padding.top;
|
||||||
|
float p1 = isHorizontal == 1 ? m_Padding.right : m_Padding.bottom;
|
||||||
|
var gs = rect.size - new Vector2(m_Padding.horizontal, m_Padding.vertical);
|
||||||
|
if (isHorizontal == 0) gs = new Vector2(gs.y, gs.x);
|
||||||
|
if (rectChildren.Count == 1) {
|
||||||
|
SetChildAlongAxis(rectChildren[0], axis, p0, gs.x);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float s1 = GetDockElementSize(gs);
|
||||||
|
float s0 = GetSlidePosition(gs.x, s1);
|
||||||
|
float a1 = (isHorizontal == 0 ? rect.height : rect.width) - p0 - p1;
|
||||||
|
for (int i = 0; i < rectChildren.Count; i++) {
|
||||||
|
var c = rectChildren[i];
|
||||||
|
bool d = i % 2 == 0;
|
||||||
|
int l = i / 2;
|
||||||
|
if (isReversed)
|
||||||
|
SetChildAlongAxis(c, axis, (d ? a1 - s1 + p0 : p0) - a1 * l + s0, d ? s1 : a1 - s1);
|
||||||
|
else
|
||||||
|
SetChildAlongAxis(c, axis, (d ? p0 : s1 + p0) - s0 + a1 * l, d ? s1 : a1 - s1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float p0 = isHorizontal == 0 ? m_Padding.left : m_Padding.top;
|
||||||
|
float p1 = isHorizontal == 0 ? m_Padding.right : m_Padding.bottom;
|
||||||
|
var height = (isHorizontal == 1 ? rect.height : rect.width) - p0 - p1;
|
||||||
|
for (int i = 0; i < rectChildren.Count; i++) {
|
||||||
|
SetChildAlongAxis(rectChildren[i], axis, p0, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the length of the first child element along the axis.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="groupSize">The size of the layout group.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected abstract float GetDockElementSize(Vector2 groupSize);
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.Common/Unity/UI/DockLayoutGroup.cs.meta
Normal file
11
Assets/Cryville.Common/Unity/UI/DockLayoutGroup.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fb94861327965934cb429511060d45fb
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,24 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Cryville.Common.Unity.UI {
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="DockLayoutGroup" /> that sets the occupied ratio of the docking element.
|
||||||
|
/// </summary>
|
||||||
|
[AddComponentMenu("Layout/Dock Occupied Ratio Layout Group")]
|
||||||
|
public sealed class DockOccupiedRatioLayoutGroup : DockLayoutGroup {
|
||||||
|
[SerializeField]
|
||||||
|
[Tooltip("The occupied ratio of the docking element.")]
|
||||||
|
private float m_dockOccupiedRatio = 1;
|
||||||
|
/// <summary>
|
||||||
|
/// The occupied ratio of the docking element.
|
||||||
|
/// </summary>
|
||||||
|
public float DockOccupiedRatio {
|
||||||
|
get { return m_dockOccupiedRatio; }
|
||||||
|
set { base.SetProperty(ref m_dockOccupiedRatio, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override float GetDockElementSize(Vector2 groupSize) {
|
||||||
|
return groupSize.x * m_dockOccupiedRatio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0bbf3330df0a07e46bcdf0b3c4879ecf
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
97
Assets/Cryville.Common/Unity/UI/FlowLayoutGroup.cs
Normal file
97
Assets/Cryville.Common/Unity/UI/FlowLayoutGroup.cs
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
namespace Cryville.Common.Unity.UI {
|
||||||
|
[AddComponentMenu("Layout/Flow Layout Group")]
|
||||||
|
public class FlowLayoutGroup : LayoutGroup {
|
||||||
|
[SerializeField] Vector2 m_spacing;
|
||||||
|
public Vector2 Spacing {
|
||||||
|
get { return m_spacing; }
|
||||||
|
set { SetProperty(ref m_spacing, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[SerializeField][Range(0, 1)] float m_itemAlignmentRatio;
|
||||||
|
public float ItemAlignmentRatio {
|
||||||
|
get { return m_itemAlignmentRatio; }
|
||||||
|
set { SetProperty(ref m_itemAlignmentRatio, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[SerializeField][Range(0, 1)] float m_itemAlignmentStretchingRatio;
|
||||||
|
public float ItemAlignmentStretchingRatio {
|
||||||
|
get { return m_itemAlignmentStretchingRatio; }
|
||||||
|
set { SetProperty(ref m_itemAlignmentStretchingRatio, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public sealed override void CalculateLayoutInputHorizontal() { base.CalculateLayoutInputHorizontal(); CalcAlongAxis(0); }
|
||||||
|
/// <inheritdoc />
|
||||||
|
public sealed override void CalculateLayoutInputVertical() { CalcAlongAxis(1); }
|
||||||
|
/// <inheritdoc />
|
||||||
|
public sealed override void SetLayoutHorizontal() { SetChildrenAlongAxis(0); }
|
||||||
|
/// <inheritdoc />
|
||||||
|
public sealed override void SetLayoutVertical() { SetChildrenAlongAxis(1); }
|
||||||
|
|
||||||
|
void CalcAlongAxis(int axis) {
|
||||||
|
if (axis == 0) {
|
||||||
|
SetLayoutInputForAxis(0, 0, 1, axis); // TODO
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float width = rectTransform.rect.width - padding.horizontal;
|
||||||
|
float x = 0, y = padding.top;
|
||||||
|
float lineHeight = 0;
|
||||||
|
for (int i = 0; i < rectChildren.Count; i++) {
|
||||||
|
RectTransform child = rectChildren[i];
|
||||||
|
float minWidth = LayoutUtility.GetMinWidth(child);
|
||||||
|
float childWidth = Math.Max(minWidth, Math.Min(width, LayoutUtility.GetPreferredWidth(child)));
|
||||||
|
float childHeight = LayoutUtility.GetPreferredHeight(child);
|
||||||
|
|
||||||
|
if (childWidth > width - x) {
|
||||||
|
x = 0;
|
||||||
|
if (i > 0) y += lineHeight + m_spacing.y;
|
||||||
|
lineHeight = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
x += childWidth + m_spacing.x;
|
||||||
|
if (childHeight > lineHeight) lineHeight = childHeight;
|
||||||
|
}
|
||||||
|
SetLayoutInputForAxis(y + lineHeight, y + lineHeight, 0, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetChildrenAlongAxis(int axis) {
|
||||||
|
float width = rectTransform.rect.width - padding.horizontal;
|
||||||
|
float x = 0, y = padding.top;
|
||||||
|
float lineHeight = 0;
|
||||||
|
int firstItemIndexOfLine = 0;
|
||||||
|
for (int i = 0; i < rectChildren.Count; i++) {
|
||||||
|
RectTransform child = rectChildren[i];
|
||||||
|
float minWidth = LayoutUtility.GetMinWidth(child);
|
||||||
|
float childWidth = Math.Max(minWidth, Math.Min(width, LayoutUtility.GetPreferredWidth(child)));
|
||||||
|
float childHeight = LayoutUtility.GetPreferredHeight(child);
|
||||||
|
|
||||||
|
if (childWidth > width - x) {
|
||||||
|
AlignItemsInLine(firstItemIndexOfLine, i, y, lineHeight);
|
||||||
|
x = 0;
|
||||||
|
if (i > 0) y += lineHeight + m_spacing.y;
|
||||||
|
lineHeight = 0;
|
||||||
|
firstItemIndexOfLine = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetChildAlongAxis(child, 0, x + padding.left, childWidth);
|
||||||
|
SetChildAlongAxis(child, 1, y, childHeight);
|
||||||
|
|
||||||
|
x += childWidth + m_spacing.x;
|
||||||
|
if (childHeight > lineHeight) lineHeight = childHeight;
|
||||||
|
}
|
||||||
|
AlignItemsInLine(firstItemIndexOfLine, rectChildren.Count, y, lineHeight);
|
||||||
|
}
|
||||||
|
void AlignItemsInLine(int startIndex, int endIndex, float y, float lineHeight) {
|
||||||
|
for (int i = startIndex; i < endIndex; i++) {
|
||||||
|
RectTransform child = rectChildren[i];
|
||||||
|
float childHeight = LayoutUtility.GetPreferredHeight(child) * (1 - m_itemAlignmentStretchingRatio) + lineHeight * m_itemAlignmentStretchingRatio;
|
||||||
|
SetChildAlongAxis(child, 1, y + (lineHeight - childHeight) * m_itemAlignmentRatio, childHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.Common/Unity/UI/FlowLayoutGroup.cs.meta
Normal file
11
Assets/Cryville.Common/Unity/UI/FlowLayoutGroup.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1ce1debad67438245a6e00e503975beb
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
94
Assets/Cryville.Common/Unity/UI/SingleLayoutGroup.cs
Normal file
94
Assets/Cryville.Common/Unity/UI/SingleLayoutGroup.cs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
namespace Cryville.Common.Unity.UI {
|
||||||
|
[AddComponentMenu("Layout/Single Layout Group")]
|
||||||
|
[ExecuteAlways]
|
||||||
|
public class SingleLayoutGroup : LayoutGroup {
|
||||||
|
[SerializeField]
|
||||||
|
protected bool m_ChildForceExpandWidth = true;
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
protected bool m_ChildForceExpandHeight = true;
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
protected bool m_ChildControlWidth = true;
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
protected bool m_ChildControlHeight = true;
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
protected bool m_ChildScaleWidth;
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
protected bool m_ChildScaleHeight;
|
||||||
|
|
||||||
|
public override void CalculateLayoutInputHorizontal() {
|
||||||
|
base.CalculateLayoutInputHorizontal();
|
||||||
|
CalcAlongAxis(0);
|
||||||
|
}
|
||||||
|
public override void CalculateLayoutInputVertical() { CalcAlongAxis(1); }
|
||||||
|
public override void SetLayoutHorizontal() { SetChildrenAlongAxis(0); }
|
||||||
|
public override void SetLayoutVertical() { SetChildrenAlongAxis(1); }
|
||||||
|
|
||||||
|
protected void CalcAlongAxis(int axis) {
|
||||||
|
float combinedPadding = (axis == 0) ? padding.horizontal : padding.vertical;
|
||||||
|
bool controlSize = (axis == 0) ? m_ChildControlWidth : m_ChildControlHeight;
|
||||||
|
bool useScale = (axis == 0) ? m_ChildScaleWidth : m_ChildScaleHeight;
|
||||||
|
bool childForceExpandSize = (axis == 0) ? m_ChildForceExpandWidth : m_ChildForceExpandHeight;
|
||||||
|
float totalMin = combinedPadding;
|
||||||
|
float totalPreferred = combinedPadding;
|
||||||
|
float totalFlexible = 0f;
|
||||||
|
RectTransform child = rectChildren[0];
|
||||||
|
GetChildSizes(child, axis, controlSize, childForceExpandSize, out var min, out var preferred, out var flexible);
|
||||||
|
if (useScale) {
|
||||||
|
float scaleFactor = child.localScale[axis];
|
||||||
|
min *= scaleFactor;
|
||||||
|
preferred *= scaleFactor;
|
||||||
|
flexible *= scaleFactor;
|
||||||
|
}
|
||||||
|
totalMin = Mathf.Max(min + combinedPadding, totalMin);
|
||||||
|
totalPreferred = Mathf.Max(preferred + combinedPadding, totalPreferred);
|
||||||
|
totalFlexible = Mathf.Max(flexible, totalFlexible);
|
||||||
|
totalPreferred = Mathf.Max(totalMin, totalPreferred);
|
||||||
|
SetLayoutInputForAxis(totalMin, totalPreferred, totalFlexible, axis);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void SetChildrenAlongAxis(int axis) {
|
||||||
|
float size = rectTransform.rect.size[axis];
|
||||||
|
bool controlSize = (axis == 0) ? m_ChildControlWidth : m_ChildControlHeight;
|
||||||
|
bool useScale = (axis == 0) ? m_ChildScaleWidth : m_ChildScaleHeight;
|
||||||
|
bool childForceExpandSize = (axis == 0) ? m_ChildForceExpandWidth : m_ChildForceExpandHeight;
|
||||||
|
float alignmentOnAxis = GetAlignmentOnAxis(axis);
|
||||||
|
float innerSize = size - ((axis == 0) ? padding.horizontal : padding.vertical);
|
||||||
|
RectTransform child = rectChildren[0];
|
||||||
|
GetChildSizes(child, axis, controlSize, childForceExpandSize, out var min2, out var preferred2, out var flexible2);
|
||||||
|
float scaleFactor2 = useScale ? child.localScale[axis] : 1f;
|
||||||
|
float requiredSpace = Mathf.Clamp(innerSize, min2, (flexible2 > 0f) ? size : preferred2);
|
||||||
|
float startOffset = GetStartOffset(axis, requiredSpace * scaleFactor2);
|
||||||
|
if (controlSize) {
|
||||||
|
SetChildAlongAxisWithScale(child, axis, startOffset, requiredSpace, scaleFactor2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float offsetInCell2 = (requiredSpace - child.sizeDelta[axis]) * alignmentOnAxis;
|
||||||
|
SetChildAlongAxisWithScale(child, axis, startOffset + offsetInCell2, scaleFactor2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetChildSizes(RectTransform child, int axis, bool controlSize, bool childForceExpand, out float min, out float preferred, out float flexible) {
|
||||||
|
if (!controlSize) {
|
||||||
|
min = child.sizeDelta[axis];
|
||||||
|
preferred = min;
|
||||||
|
flexible = 0f;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
min = LayoutUtility.GetMinSize(child, axis);
|
||||||
|
preferred = LayoutUtility.GetPreferredSize(child, axis);
|
||||||
|
flexible = LayoutUtility.GetFlexibleSize(child, axis);
|
||||||
|
}
|
||||||
|
if (childForceExpand) {
|
||||||
|
flexible = Mathf.Max(flexible, 1f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.Common/Unity/UI/SingleLayoutGroup.cs.meta
Normal file
11
Assets/Cryville.Common/Unity/UI/SingleLayoutGroup.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 21255a16576b76f4280a765b43a4ae1c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
80
Assets/Cryville.Common/Unity/UI/TMPLocalizedText.cs
Normal file
80
Assets/Cryville.Common/Unity/UI/TMPLocalizedText.cs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
using Cryville.Common.Font;
|
||||||
|
using Cryville.Culture;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Reflection;
|
||||||
|
using TMPro;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.TextCore.LowLevel;
|
||||||
|
using UnityEngine.TextCore.Text;
|
||||||
|
|
||||||
|
namespace Cryville.Common.Unity.UI {
|
||||||
|
[RequireComponent(typeof(TMP_Text))]
|
||||||
|
public class TMPLocalizedText : MonoBehaviour {
|
||||||
|
public static Shader DefaultShader;
|
||||||
|
public static FontMatcher FontMatcher;
|
||||||
|
public static int MaxFallbackCount = 4;
|
||||||
|
|
||||||
|
static readonly Dictionary<CultureInfo, FontAsset> _cachedFonts = new();
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
Shader m_shader;
|
||||||
|
|
||||||
|
public TMP_Text Text { get; private set; }
|
||||||
|
void Awake() {
|
||||||
|
Text = GetComponent<TMP_Text>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetText(string text, CultureInfo culture = null) {
|
||||||
|
Text.text = text;
|
||||||
|
SetCulture(culture ?? CultureInfo.CurrentCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetCulture(CultureInfo culture) {
|
||||||
|
if (FontMatcher == null) return;
|
||||||
|
string cultureName = culture.Name;
|
||||||
|
if (string.IsNullOrEmpty(cultureName)) cultureName = CultureInfo.CurrentCulture.Name;
|
||||||
|
if (!_cachedFonts.TryGetValue(culture, out var font)) {
|
||||||
|
foreach (var typeface in FontMatcher.MatchLanguage(new LanguageId(cultureName), true)) {
|
||||||
|
try {
|
||||||
|
var ifont = CreateFontAsset(typeface.File.FullName, typeface.IndexInFile);
|
||||||
|
if (m_shader) ifont.material.shader = m_shader;
|
||||||
|
else if (DefaultShader) ifont.material.shader = DefaultShader;
|
||||||
|
if (font == null) {
|
||||||
|
font = ifont;
|
||||||
|
if (MaxFallbackCount <= 0) break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
font.fallbackFontAssetTable ??= new List<FontAsset>();
|
||||||
|
font.fallbackFontAssetTable.Add(ifont);
|
||||||
|
if (font.fallbackFontAssetTable.Count >= MaxFallbackCount) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception) { }
|
||||||
|
}
|
||||||
|
_cachedFonts.Add(culture, font);
|
||||||
|
}
|
||||||
|
Text.font = font;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MethodInfo _methodCreateFontAsset;
|
||||||
|
static readonly object[] _paramsCreateFontAsset = new object[] { null, null, 90, 9, GlyphRenderMode.SDFAA, 1024, 1024, Type.Missing, Type.Missing };
|
||||||
|
static FontAsset CreateFontAsset(string path, int index) {
|
||||||
|
if (_methodCreateFontAsset == null) {
|
||||||
|
_methodCreateFontAsset = typeof(FontAsset).GetMethod(
|
||||||
|
"CreateFontAsset", BindingFlags.Static | BindingFlags.NonPublic, null,
|
||||||
|
new Type[] {
|
||||||
|
typeof(string), typeof(int), typeof(int), typeof(int),
|
||||||
|
typeof(GlyphRenderMode), typeof(int), typeof(int),
|
||||||
|
typeof(AtlasPopulationMode), typeof(bool)
|
||||||
|
},
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_paramsCreateFontAsset[0] = path;
|
||||||
|
_paramsCreateFontAsset[1] = index;
|
||||||
|
return (FontAsset)_methodCreateFontAsset.Invoke(null, _paramsCreateFontAsset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.Common/Unity/UI/TMPLocalizedText.cs.meta
Normal file
11
Assets/Cryville.Common/Unity/UI/TMPLocalizedText.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c03870a7d4386e846be005a0ac36e987
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Assets/Cryville.EEW.Unity.meta
Normal file
8
Assets/Cryville.EEW.Unity.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c47cfa4f1761c7c438e882cd9bf3fa60
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
43
Assets/Cryville.EEW.Unity/App.cs
Normal file
43
Assets/Cryville.EEW.Unity/App.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
using Cryville.Common.Font;
|
||||||
|
using Cryville.Common.Unity.UI;
|
||||||
|
using Cryville.Crtr;
|
||||||
|
using Cryville.Culture;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using System.Xml;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity {
|
||||||
|
class App {
|
||||||
|
static bool _init;
|
||||||
|
public static void Init() {
|
||||||
|
if (_init) return;
|
||||||
|
_init = true;
|
||||||
|
|
||||||
|
foreach (var res in Resources.LoadAll<TextAsset>("cldr/common/validity")) {
|
||||||
|
IdValidity.Load(LoadXmlDocument(res));
|
||||||
|
}
|
||||||
|
var metadata = new SupplementalMetadata(LoadXmlDocument("cldr/common/supplemental/supplementalMetadata"));
|
||||||
|
var subtags = new LikelySubtags(LoadXmlDocument("cldr/common/supplemental/likelySubtags"), metadata);
|
||||||
|
var matcher = new LanguageMatching(LoadXmlDocument("cldr/common/supplemental/languageInfo"), subtags);
|
||||||
|
TMPLocalizedText.FontMatcher = new FallbackListFontMatcher(matcher, PlatformConfig.FontManager) {
|
||||||
|
MapScriptToTypefaces = PlatformConfig.ScriptFontMap
|
||||||
|
};
|
||||||
|
TMPLocalizedText.DefaultShader = Resources.Load<Shader>(PlatformConfig.TextShader);
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly Encoding _encoding = new UTF8Encoding(false, true);
|
||||||
|
static readonly XmlReaderSettings _xmlSettings = new() {
|
||||||
|
DtdProcessing = DtdProcessing.Ignore,
|
||||||
|
};
|
||||||
|
static XDocument LoadXmlDocument(string path) {
|
||||||
|
return LoadXmlDocument(Resources.Load<TextAsset>(path));
|
||||||
|
}
|
||||||
|
static XDocument LoadXmlDocument(TextAsset asset) {
|
||||||
|
using var stream = new MemoryStream(_encoding.GetBytes(asset.text));
|
||||||
|
using var reader = XmlReader.Create(stream, _xmlSettings);
|
||||||
|
return XDocument.Load(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.EEW.Unity/App.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/App.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c99e48f1a8befc34eb40305cdd762f9f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
7
Assets/Cryville.EEW.Unity/ColorUtils.cs
Normal file
7
Assets/Cryville.EEW.Unity/ColorUtils.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity {
|
||||||
|
static class ColorUtils {
|
||||||
|
public static Color ToUnityColor(this System.Drawing.Color color) => new(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f);
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.EEW.Unity/ColorUtils.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/ColorUtils.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1360d2ab5159d7845b4e8f1df463d514
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
18
Assets/Cryville.EEW.Unity/Cryville.EEW.Unity.asmdef
Normal file
18
Assets/Cryville.EEW.Unity/Cryville.EEW.Unity.asmdef
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "Cryville.EEW.Unity",
|
||||||
|
"rootNamespace": "",
|
||||||
|
"references": [
|
||||||
|
"GUID:b92f9c7ac10b1c04e86fc48210f62ab1",
|
||||||
|
"GUID:e5b7e7f40a80a814ba706299d68f9213",
|
||||||
|
"GUID:da293eebbcb9a4947a212534c52d1a32"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": false,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
7
Assets/Cryville.EEW.Unity/Cryville.EEW.Unity.asmdef.meta
Normal file
7
Assets/Cryville.EEW.Unity/Cryville.EEW.Unity.asmdef.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d21cf2141c549ce4193d48fa497a21bd
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
13
Assets/Cryville.EEW.Unity/LocalizedResourcesManager.cs
Normal file
13
Assets/Cryville.EEW.Unity/LocalizedResourcesManager.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using Cryville.EEW.Core;
|
||||||
|
using System.IO;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity {
|
||||||
|
sealed class LocalizedResourcesManager : JSONFileLocalizedResourceManager {
|
||||||
|
protected override Stream Open(string path) {
|
||||||
|
path = Path.Combine(Application.streamingAssetsPath, "Messages", path);
|
||||||
|
if (!File.Exists(path)) return null;
|
||||||
|
return new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.EEW.Unity/LocalizedResourcesManager.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/LocalizedResourcesManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 725cac357fd552542822cbf163b7affa
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Assets/Cryville.EEW.Unity/Map.meta
Normal file
8
Assets/Cryville.EEW.Unity/Map.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 96211bdddfa409a40a2e375d18b4e19d
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
107
Assets/Cryville.EEW.Unity/Map/CameraController.cs
Normal file
107
Assets/Cryville.EEW.Unity/Map/CameraController.cs
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity.Map {
|
||||||
|
[RequireComponent(typeof(Camera))]
|
||||||
|
sealed class CameraController : MonoBehaviour {
|
||||||
|
Camera _camera;
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
Transform m_layerTile;
|
||||||
|
[SerializeField]
|
||||||
|
MapElementManager m_layerElement;
|
||||||
|
[SerializeField]
|
||||||
|
MapElementManager m_layerElementSub;
|
||||||
|
[SerializeField]
|
||||||
|
GameObject m_prefabTile;
|
||||||
|
|
||||||
|
readonly MapTileCacheManager _tiles = new();
|
||||||
|
float _elementLayerZ;
|
||||||
|
|
||||||
|
void Start() {
|
||||||
|
_camera = GetComponent<Camera>();
|
||||||
|
_tiles.Parent = m_layerTile;
|
||||||
|
_tiles.PrefabTile = m_prefabTile;
|
||||||
|
_tiles.CacheDir = Application.temporaryCachePath;
|
||||||
|
_camera.orthographicSize = 0.5f / MathF.Max(1, (float)_camera.pixelWidth / _camera.pixelHeight);
|
||||||
|
_elementLayerZ = m_layerElement.transform.position.z;
|
||||||
|
UpdateTransform();
|
||||||
|
}
|
||||||
|
void OnDestroy() {
|
||||||
|
_tiles.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
float Scale {
|
||||||
|
get => _camera.orthographicSize;
|
||||||
|
set => _camera.orthographicSize = Mathf.Clamp(value, 1e-3f, 0.5f / MathF.Max(1, (float)_camera.pixelWidth / _camera.pixelHeight));
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly Rect _viewportRect = new(0, 0, 1, 1);
|
||||||
|
Vector3? ppos;
|
||||||
|
void Update() {
|
||||||
|
var cpos = Input.mousePosition;
|
||||||
|
bool isMouseInViewport = _viewportRect.Contains(_camera.ScreenToViewportPoint(Input.mousePosition));
|
||||||
|
if (Input.GetMouseButtonDown(0) && isMouseInViewport) {
|
||||||
|
ppos = cpos;
|
||||||
|
}
|
||||||
|
if (Input.GetMouseButton(0) && ppos is Vector3 pos0) {
|
||||||
|
var delta = _camera.ScreenToWorldPoint(pos0) - _camera.ScreenToWorldPoint(cpos);
|
||||||
|
transform.position += delta;
|
||||||
|
ppos = cpos;
|
||||||
|
UpdateTransform();
|
||||||
|
}
|
||||||
|
if (Input.GetMouseButtonUp(0)) {
|
||||||
|
ppos = null;
|
||||||
|
}
|
||||||
|
if (Input.mouseScrollDelta.y != 0 && isMouseInViewport) {
|
||||||
|
Scale *= Mathf.Pow(2, -Input.mouseScrollDelta.y / 8);
|
||||||
|
UpdateTransform();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnMapElementUpdated() {
|
||||||
|
var aabb = m_layerElement.AABB;
|
||||||
|
if (aabb is not RectangleF b) return;
|
||||||
|
if (b.Width * _camera.pixelHeight < _camera.pixelWidth * b.Height)
|
||||||
|
Scale = b.Height;
|
||||||
|
else
|
||||||
|
Scale = b.Width * _camera.pixelHeight / _camera.pixelWidth;
|
||||||
|
Scale *= 0.6f;
|
||||||
|
transform.localPosition = new PointF(b.X + b.Width / 2, b.Y + b.Height / 2).ToVector2();
|
||||||
|
UpdateTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateTransform() {
|
||||||
|
float h = _camera.orthographicSize * 2;
|
||||||
|
float w = h * _camera.pixelWidth / _camera.pixelHeight;
|
||||||
|
float vz = 1 / h * _camera.pixelHeight;
|
||||||
|
float nx = transform.position.x % 1;
|
||||||
|
if (nx < 0) nx += 1;
|
||||||
|
transform.localPosition = new(nx, Math.Clamp(transform.position.y, h / 2 - 1, -h / 2), -20);
|
||||||
|
|
||||||
|
var bounds = new Bounds((Vector2)transform.position, new Vector2(w, h));
|
||||||
|
int zoom = Math.Clamp((int)Math.Log(vz / 256, 2) + 1, 0, 10);
|
||||||
|
int zoomScale = 1 << zoom;
|
||||||
|
_tiles.MoveTo(
|
||||||
|
new(Mathf.FloorToInt(bounds.min.x * zoomScale), Mathf.FloorToInt(-bounds.max.y * zoomScale), zoom),
|
||||||
|
new(Mathf.CeilToInt(bounds.max.x * zoomScale), Mathf.CeilToInt(-bounds.min.y * zoomScale), zoom)
|
||||||
|
);
|
||||||
|
|
||||||
|
m_layerElement.Scale = h;
|
||||||
|
m_layerElementSub.Scale = h;
|
||||||
|
|
||||||
|
if (nx - w / 2 < 0) {
|
||||||
|
m_layerElementSub.gameObject.SetActive(true);
|
||||||
|
m_layerElementSub.transform.localPosition = new(-1, 0, _elementLayerZ);
|
||||||
|
}
|
||||||
|
else if (nx + w / 2 > 1) {
|
||||||
|
m_layerElementSub.gameObject.SetActive(true);
|
||||||
|
m_layerElementSub.transform.localPosition = new(1, 0, _elementLayerZ);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_layerElementSub.gameObject.SetActive(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.EEW.Unity/Map/CameraController.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/Map/CameraController.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 07c8433af4f94ee4b9fa3286a384ffe2
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Assets/Cryville.EEW.Unity/Map/Element.meta
Normal file
8
Assets/Cryville.EEW.Unity/Map/Element.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a2c2d746f0122b64a84ac5b921e0fadc
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
45
Assets/Cryville.EEW.Unity/Map/Element/GroupElement.cs
Normal file
45
Assets/Cryville.EEW.Unity/Map/Element/GroupElement.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using Cryville.EEW.Core.Map;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity.Map.Element {
|
||||||
|
class GroupElement : MapElement {
|
||||||
|
protected virtual Transform Container => transform;
|
||||||
|
|
||||||
|
readonly List<MapElement> _elements = new();
|
||||||
|
|
||||||
|
public override RectangleF? AABB {
|
||||||
|
get {
|
||||||
|
RectangleF? ret = null;
|
||||||
|
foreach (var element in _elements) {
|
||||||
|
if (element == null) continue;
|
||||||
|
if (element.AABB is not RectangleF aabb) continue;
|
||||||
|
if (ret == null) ret = aabb;
|
||||||
|
else ret = MapTileUtils.UnionTileAABBs(ret.Value, aabb);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void AddElement(MapElement element) {
|
||||||
|
if (element == null) return;
|
||||||
|
element.Scale = Scale;
|
||||||
|
if (element.InheritMaterial) element.Material = Material;
|
||||||
|
element.transform.SetParent(Container, false);
|
||||||
|
_elements.Add(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnSetScale() {
|
||||||
|
foreach (Transform element in Container) {
|
||||||
|
element.GetComponent<MapElement>().Scale = Scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected override void OnSetMaterial(Material material) {
|
||||||
|
foreach (Transform child in Container) {
|
||||||
|
var element = child.GetComponent<MapElement>();
|
||||||
|
if (element.InheritMaterial) element.Material = Material;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.EEW.Unity/Map/Element/GroupElement.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/Map/Element/GroupElement.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 44a27900867d2ef44bb67cb466c445d9
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
50
Assets/Cryville.EEW.Unity/Map/Element/HypocenterElement.cs
Normal file
50
Assets/Cryville.EEW.Unity/Map/Element/HypocenterElement.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
using Cryville.EEW.Core.Map;
|
||||||
|
using System.Drawing;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity.Map.Element {
|
||||||
|
[RequireComponent(typeof(SpriteRenderer))]
|
||||||
|
class HypocenterElement : MapElement {
|
||||||
|
[SerializeField] Sprite m_sprite;
|
||||||
|
[SerializeField] Sprite m_spriteLowQuality;
|
||||||
|
|
||||||
|
PointF _tilePos;
|
||||||
|
public override RectangleF? AABB => new(_tilePos, SizeF.Empty);
|
||||||
|
|
||||||
|
public void SetLocation(PointF location, ref int order) {
|
||||||
|
_tilePos = MapTileUtils.WorldToTilePos(location);
|
||||||
|
Vector3 pos = _tilePos.ToVector2();
|
||||||
|
pos.z = OrderToZ(ref order);
|
||||||
|
transform.localPosition = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool m_isLowQuality;
|
||||||
|
public bool IsLowQuality {
|
||||||
|
get => m_isLowQuality;
|
||||||
|
set {
|
||||||
|
if (m_isLowQuality == value) return;
|
||||||
|
m_isLowQuality = value;
|
||||||
|
_spriteRenderer.sprite = value ? m_spriteLowQuality : m_sprite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float m_size;
|
||||||
|
public float Size {
|
||||||
|
get => m_size;
|
||||||
|
set {
|
||||||
|
if (m_size == value) return;
|
||||||
|
m_size = value;
|
||||||
|
OnSetScale();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SpriteRenderer _spriteRenderer;
|
||||||
|
void Awake() {
|
||||||
|
_spriteRenderer = GetComponent<SpriteRenderer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnSetScale() {
|
||||||
|
transform.localScale = ComputedScale * m_size / 256 * Vector3.one;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 505a49fe5efac9247939cef8282923c4
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
90
Assets/Cryville.EEW.Unity/Map/Element/LabeledPointElement.cs
Normal file
90
Assets/Cryville.EEW.Unity/Map/Element/LabeledPointElement.cs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
using Cryville.EEW.Core.Map;
|
||||||
|
using System.Drawing;
|
||||||
|
using TMPro;
|
||||||
|
using UnityEngine;
|
||||||
|
using Color = UnityEngine.Color;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity.Map.Element {
|
||||||
|
[RequireComponent(typeof(SpriteRenderer))]
|
||||||
|
class LabeledPointElement : MapElement {
|
||||||
|
[SerializeField] Sprite m_sprite;
|
||||||
|
[SerializeField] Sprite m_spriteArea;
|
||||||
|
[SerializeField] TextMeshPro m_textMesh;
|
||||||
|
|
||||||
|
PointF _tilePos;
|
||||||
|
public override RectangleF? AABB => new(_tilePos, SizeF.Empty);
|
||||||
|
|
||||||
|
public void SetLocation(PointF location, ref int order) {
|
||||||
|
_tilePos = MapTileUtils.WorldToTilePos(location);
|
||||||
|
Vector3 pos = _tilePos.ToVector2();
|
||||||
|
pos.z = OrderToZ(ref order);
|
||||||
|
transform.localPosition = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
string m_text = "";
|
||||||
|
public string Text {
|
||||||
|
get => m_text;
|
||||||
|
set {
|
||||||
|
if (m_text == value) return;
|
||||||
|
m_text = value;
|
||||||
|
m_textMesh.text = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
Color m_color = Color.white;
|
||||||
|
public Color Color {
|
||||||
|
get => m_color;
|
||||||
|
set {
|
||||||
|
if (m_color == value) return;
|
||||||
|
m_color = value;
|
||||||
|
_spriteRenderer.color = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
Color m_textColor = Color.black;
|
||||||
|
public Color TextColor {
|
||||||
|
get => m_textColor;
|
||||||
|
set {
|
||||||
|
if (m_textColor == value) return;
|
||||||
|
m_textColor = value;
|
||||||
|
m_textMesh.color = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool m_isArea;
|
||||||
|
public bool IsArea {
|
||||||
|
get => m_isArea;
|
||||||
|
set {
|
||||||
|
if (m_isArea == value) return;
|
||||||
|
m_isArea = value;
|
||||||
|
_spriteRenderer.sprite = value ? m_spriteArea : m_sprite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float m_size;
|
||||||
|
public float Size {
|
||||||
|
get => m_size;
|
||||||
|
set {
|
||||||
|
if (m_size == value) return;
|
||||||
|
m_size = value;
|
||||||
|
OnSetScale();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SpriteRenderer _spriteRenderer;
|
||||||
|
void Awake() {
|
||||||
|
_spriteRenderer = GetComponent<SpriteRenderer>();
|
||||||
|
m_textMesh.text = m_text;
|
||||||
|
_spriteRenderer.color = m_color;
|
||||||
|
m_textMesh.color = m_textColor;
|
||||||
|
_spriteRenderer.sprite = m_isArea ? m_spriteArea : m_sprite;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnSetScale() {
|
||||||
|
transform.localScale = ComputedScale * m_size / 256 * Vector3.one;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d10b1601f6b7e8946bdf6c54585167af
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
39
Assets/Cryville.EEW.Unity/Map/Element/MapElement.cs
Normal file
39
Assets/Cryville.EEW.Unity/Map/Element/MapElement.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity.Map.Element {
|
||||||
|
abstract class MapElement : MonoBehaviour {
|
||||||
|
public abstract RectangleF? AABB { get; }
|
||||||
|
|
||||||
|
public float MaxScale { get; set; }
|
||||||
|
float m_scale;
|
||||||
|
public float Scale {
|
||||||
|
get => m_scale;
|
||||||
|
set {
|
||||||
|
m_scale = value;
|
||||||
|
OnSetScale();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public float ComputedScale => Math.Min(MaxScale, Scale);
|
||||||
|
protected virtual void OnSetScale() { }
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
bool m_inheritMaterial = true;
|
||||||
|
public bool InheritMaterial => m_inheritMaterial;
|
||||||
|
[SerializeField]
|
||||||
|
Material m_material;
|
||||||
|
public Material Material {
|
||||||
|
get => m_material;
|
||||||
|
set {
|
||||||
|
m_material = value;
|
||||||
|
OnSetMaterial(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected virtual void OnSetMaterial(Material material) { }
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected static float OrderToZ(ref int order) => MapElementManager.OrderToZ(order++);
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.EEW.Unity/Map/Element/MapElement.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/Map/Element/MapElement.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 92a1fd122e87e6c49b58d9d4c81b131c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
36
Assets/Cryville.EEW.Unity/Map/Element/MaskedGroupElement.cs
Normal file
36
Assets/Cryville.EEW.Unity/Map/Element/MaskedGroupElement.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity.Map.Element {
|
||||||
|
class MaskedGroupElement : GroupElement {
|
||||||
|
[SerializeField] Material m_maskMaterial;
|
||||||
|
[SerializeField] PolygonRenderer m_polygonRendererPrefab;
|
||||||
|
readonly List<PolygonRenderer> _polygonRenderers = new();
|
||||||
|
|
||||||
|
public void SetMasks(IEnumerable<IEnumerable<IEnumerable<PointF>>> polygons) {
|
||||||
|
foreach (var r in _polygonRenderers) {
|
||||||
|
Destroy(r.gameObject);
|
||||||
|
}
|
||||||
|
_polygonRenderers.Clear();
|
||||||
|
foreach (var polygon in polygons) {
|
||||||
|
CreatePolygon(polygon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreatePolygon(IEnumerable<IEnumerable<PointF>> polygon) {
|
||||||
|
var polygonRenderer = Instantiate(m_polygonRendererPrefab);
|
||||||
|
polygonRenderer.SetPolygon(polygon);
|
||||||
|
polygonRenderer.Material = m_maskMaterial;
|
||||||
|
polygonRenderer.transform.SetParent(transform, false);
|
||||||
|
_polygonRenderers.Add(polygonRenderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform _container;
|
||||||
|
protected override Transform Container => _container;
|
||||||
|
void Awake() {
|
||||||
|
_container = new GameObject("_container").transform;
|
||||||
|
_container.SetParent(transform, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e597c4b1800db0e47bb84271bcd9de7b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
70
Assets/Cryville.EEW.Unity/Map/Element/MultiLineElement.cs
Normal file
70
Assets/Cryville.EEW.Unity/Map/Element/MultiLineElement.cs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
using Cryville.EEW.Core.Map;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using UnityEngine;
|
||||||
|
using Color = UnityEngine.Color;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity.Map.Element {
|
||||||
|
class MultiLineElement : MapElement {
|
||||||
|
[SerializeField] LineRenderer m_lineRendererPrefab;
|
||||||
|
readonly List<LineRenderer> _lineRenderers = new();
|
||||||
|
|
||||||
|
RectangleF? m_AABB;
|
||||||
|
public override RectangleF? AABB => m_AABB;
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
Color m_color = Color.white;
|
||||||
|
public Color Color {
|
||||||
|
get => m_color;
|
||||||
|
set {
|
||||||
|
if (m_color == value) return;
|
||||||
|
m_color = value;
|
||||||
|
foreach (var r in _lineRenderers) {
|
||||||
|
r.Color = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[SerializeField]
|
||||||
|
float m_width = 1;
|
||||||
|
public float Width {
|
||||||
|
get => m_width;
|
||||||
|
set {
|
||||||
|
if (m_width == value) return;
|
||||||
|
m_width = value;
|
||||||
|
foreach (var r in _lineRenderers) {
|
||||||
|
r.Width = value * ComputedScale / 512;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnSetScale() {
|
||||||
|
foreach (var r in _lineRenderers) {
|
||||||
|
r.Width = m_width * ComputedScale / 512;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected override void OnSetMaterial(Material material) {
|
||||||
|
foreach (var r in _lineRenderers) {
|
||||||
|
r.Material = Material;
|
||||||
|
r.Color = m_color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetLines(IEnumerable<IEnumerable<PointF>> lines, ref int order) {
|
||||||
|
foreach (var r in _lineRenderers) {
|
||||||
|
Destroy(r.gameObject);
|
||||||
|
}
|
||||||
|
_lineRenderers.Clear();
|
||||||
|
m_AABB = null;
|
||||||
|
foreach (var line in lines) {
|
||||||
|
CreateLine(line, ref order);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateLine(IEnumerable<PointF> line, ref int order) {
|
||||||
|
_lineRenderers.Add(LineRenderer.Create(line, OrderToZ(ref order), m_lineRendererPrefab, Material, m_color, m_width, this, out var aabb));
|
||||||
|
if (aabb is RectangleF b) {
|
||||||
|
m_AABB = m_AABB is RectangleF a ? MapTileUtils.UnionTileAABBs(a, b) : b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9028f83972f4b8648819141884011f70
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
102
Assets/Cryville.EEW.Unity/Map/Element/MultiPolygonElement.cs
Normal file
102
Assets/Cryville.EEW.Unity/Map/Element/MultiPolygonElement.cs
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
using Cryville.EEW.Core.Map;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using UnityEngine;
|
||||||
|
using Color = UnityEngine.Color;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity.Map.Element {
|
||||||
|
class MultiPolygonElement : MapElement {
|
||||||
|
[SerializeField] PolygonRenderer m_polygonRendererPrefab;
|
||||||
|
readonly List<PolygonRenderer> _polygonRenderers = new();
|
||||||
|
[SerializeField] LineRenderer m_lineRendererPrefab;
|
||||||
|
readonly List<LineRenderer> _lineRenderers = new();
|
||||||
|
|
||||||
|
RectangleF? m_AABB;
|
||||||
|
public override RectangleF? AABB => m_AABB;
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
Color m_fillColor = Color.white;
|
||||||
|
public Color FillColor {
|
||||||
|
get => m_fillColor;
|
||||||
|
set {
|
||||||
|
if (m_fillColor == value) return;
|
||||||
|
m_fillColor = value;
|
||||||
|
foreach (var r in _polygonRenderers) {
|
||||||
|
r.Color = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[SerializeField]
|
||||||
|
Color m_borderColor = Color.white;
|
||||||
|
public Color BorderColor {
|
||||||
|
get => m_borderColor;
|
||||||
|
set {
|
||||||
|
if (m_borderColor == value) return;
|
||||||
|
m_borderColor = value;
|
||||||
|
foreach (var r in _lineRenderers) {
|
||||||
|
r.Color = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[SerializeField]
|
||||||
|
float m_borderWidth = 1;
|
||||||
|
public float BorderWidth {
|
||||||
|
get => m_borderWidth;
|
||||||
|
set {
|
||||||
|
if (m_borderWidth == value) return;
|
||||||
|
m_borderWidth = value;
|
||||||
|
foreach (var r in _lineRenderers) {
|
||||||
|
r.Width = value * ComputedScale / 512;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnSetScale() {
|
||||||
|
foreach (var r in _lineRenderers) {
|
||||||
|
r.Width = m_borderWidth * ComputedScale / 512;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected override void OnSetMaterial(Material material) {
|
||||||
|
foreach (var r in _lineRenderers) {
|
||||||
|
r.Material = Material;
|
||||||
|
r.Color = m_borderColor;
|
||||||
|
}
|
||||||
|
foreach (var r in _polygonRenderers) {
|
||||||
|
r.Material = Material;
|
||||||
|
r.Color = m_fillColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPolygons(IEnumerable<IEnumerable<IEnumerable<PointF>>> polygons, ref int order) {
|
||||||
|
foreach (var r in _polygonRenderers) {
|
||||||
|
Destroy(r.gameObject);
|
||||||
|
}
|
||||||
|
foreach (var r in _lineRenderers) {
|
||||||
|
Destroy(r.gameObject);
|
||||||
|
}
|
||||||
|
_polygonRenderers.Clear();
|
||||||
|
_lineRenderers.Clear();
|
||||||
|
m_AABB = null;
|
||||||
|
foreach (var polygon in polygons) {
|
||||||
|
CreatePolygon(polygon, ref order);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreatePolygon(IEnumerable<IEnumerable<PointF>> polygon, ref int order) {
|
||||||
|
var polygonRenderer = Instantiate(m_polygonRendererPrefab);
|
||||||
|
polygonRenderer.SetPolygon(polygon);
|
||||||
|
polygonRenderer.Material = Material;
|
||||||
|
polygonRenderer.Color = m_fillColor;
|
||||||
|
polygonRenderer.transform.SetParent(transform, false);
|
||||||
|
polygonRenderer.transform.localPosition = new(0, 0, OrderToZ(ref order));
|
||||||
|
_polygonRenderers.Add(polygonRenderer);
|
||||||
|
|
||||||
|
foreach (var loop in polygon) {
|
||||||
|
_lineRenderers.Add(LineRenderer.Create(loop, OrderToZ(ref order), m_lineRendererPrefab, Material, m_borderColor, m_borderWidth, this, out var aabb));
|
||||||
|
if (aabb is RectangleF b) {
|
||||||
|
m_AABB = m_AABB is RectangleF a ? MapTileUtils.UnionTileAABBs(a, b) : b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ccf7f796ffe73004a95fef5655177c3b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
25
Assets/Cryville.EEW.Unity/Map/Element/OngoingGroupElement.cs
Normal file
25
Assets/Cryville.EEW.Unity/Map/Element/OngoingGroupElement.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity.Map.Element {
|
||||||
|
class OngoingGroupElement : GroupElement {
|
||||||
|
float _blinkingTime;
|
||||||
|
float m_blinkingPeriod;
|
||||||
|
public float BlinkingPeriod {
|
||||||
|
get => m_blinkingPeriod;
|
||||||
|
set {
|
||||||
|
m_blinkingPeriod = value;
|
||||||
|
_blinkingTime = Math.Max(value - 0.5f, value / 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Transform _container;
|
||||||
|
protected override Transform Container => _container;
|
||||||
|
void Awake() {
|
||||||
|
_container = new GameObject("_container").transform;
|
||||||
|
_container.SetParent(transform, false);
|
||||||
|
}
|
||||||
|
void Update() {
|
||||||
|
_container.gameObject.SetActive((DateTime.UtcNow - DateTime.UnixEpoch).TotalSeconds % m_blinkingPeriod < _blinkingTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: af28bd1e6223c4e498888034b7b756ff
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
48
Assets/Cryville.EEW.Unity/Map/Element/PointElement.cs
Normal file
48
Assets/Cryville.EEW.Unity/Map/Element/PointElement.cs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
using Cryville.EEW.Core.Map;
|
||||||
|
using System.Drawing;
|
||||||
|
using UnityEngine;
|
||||||
|
using Color = UnityEngine.Color;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity.Map.Element {
|
||||||
|
class PointElement : MapElement {
|
||||||
|
PointF _tilePos;
|
||||||
|
public override RectangleF? AABB => new(_tilePos, SizeF.Empty);
|
||||||
|
|
||||||
|
public void SetLocation(PointF location, ref int order) {
|
||||||
|
_tilePos = MapTileUtils.WorldToTilePos(location);
|
||||||
|
Vector3 pos = _tilePos.ToVector2();
|
||||||
|
pos.z = OrderToZ(ref order);
|
||||||
|
transform.localPosition = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
Color m_color = Color.white;
|
||||||
|
public Color Color {
|
||||||
|
get => m_color;
|
||||||
|
set {
|
||||||
|
if (m_color == value) return;
|
||||||
|
m_color = value;
|
||||||
|
_spriteRenderer.color = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float m_size;
|
||||||
|
public float Size {
|
||||||
|
get => m_size;
|
||||||
|
set {
|
||||||
|
if (m_size == value) return;
|
||||||
|
m_size = value;
|
||||||
|
OnSetScale();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SpriteRenderer _spriteRenderer;
|
||||||
|
void Awake() {
|
||||||
|
_spriteRenderer = GetComponent<SpriteRenderer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnSetScale() {
|
||||||
|
transform.localScale = ComputedScale * m_size / 256 * Vector3.one;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.EEW.Unity/Map/Element/PointElement.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/Map/Element/PointElement.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: cccfefde2dac1e742a8c44924b678e12
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
146
Assets/Cryville.EEW.Unity/Map/Element/TsunamiHeightElement.cs
Normal file
146
Assets/Cryville.EEW.Unity/Map/Element/TsunamiHeightElement.cs
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
using Cryville.EEW.Core.Map;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using UnityEngine;
|
||||||
|
using Color = UnityEngine.Color;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity.Map.Element {
|
||||||
|
class TsunamiHeightElement : MapElement {
|
||||||
|
[SerializeField] SpriteRenderer _barRenderer;
|
||||||
|
[SerializeField] SpriteRenderer _risingMark;
|
||||||
|
[SerializeField] SpriteRenderer _outOfRangeMark;
|
||||||
|
[SerializeField] SpriteRenderer _missingMark;
|
||||||
|
|
||||||
|
float _baseBarSize;
|
||||||
|
|
||||||
|
PointF _tilePos;
|
||||||
|
public override RectangleF? AABB => new(_tilePos, SizeF.Empty);
|
||||||
|
|
||||||
|
public void SetLocation(PointF location, ref int order) {
|
||||||
|
_tilePos = MapTileUtils.WorldToTilePos(location);
|
||||||
|
Vector3 pos = _tilePos.ToVector2();
|
||||||
|
pos.z = OrderToZ(ref order);
|
||||||
|
transform.localPosition = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class GlobalHeightScaler {
|
||||||
|
static GlobalHeightScaler s_instance;
|
||||||
|
public static GlobalHeightScaler Instance => s_instance ??= new();
|
||||||
|
|
||||||
|
readonly List<float> _heights = new();
|
||||||
|
public void PushHeight(float height) {
|
||||||
|
var i = _heights.BinarySearch(height);
|
||||||
|
if (i < 0) i = ~i;
|
||||||
|
_heights.Insert(i, height);
|
||||||
|
UpdateMaxHeight();
|
||||||
|
}
|
||||||
|
public void PopHeight(float height) {
|
||||||
|
var i = _heights.BinarySearch(height);
|
||||||
|
if (i < 0) return;
|
||||||
|
_heights.RemoveAt(i);
|
||||||
|
UpdateMaxHeight();
|
||||||
|
}
|
||||||
|
void UpdateMaxHeight() {
|
||||||
|
float newMaxHeight = _heights.Count == 0 ? 0 : _heights[^1];
|
||||||
|
if (newMaxHeight == MaxHeight) return;
|
||||||
|
MaxHeight = newMaxHeight;
|
||||||
|
Update?.Invoke();
|
||||||
|
}
|
||||||
|
public event Action Update;
|
||||||
|
public float MaxHeight { get; private set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
float? m_height;
|
||||||
|
public float Height {
|
||||||
|
get => m_height.Value;
|
||||||
|
set {
|
||||||
|
if (m_height == value) return;
|
||||||
|
if (m_height != null) GlobalHeightScaler.Instance.PopHeight(m_height.Value);
|
||||||
|
else GlobalHeightScaler.Instance.Update += UpdateHeight;
|
||||||
|
m_height = value;
|
||||||
|
GlobalHeightScaler.Instance.PushHeight(value);
|
||||||
|
UpdateHeight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void UpdateHeight() {
|
||||||
|
float h = _baseBarSize * (Math.Max(0, Height) / Math.Max(3, GlobalHeightScaler.Instance.MaxHeight) * 12);
|
||||||
|
_barRenderer.size = new(_baseBarSize, h + _baseBarSize);
|
||||||
|
UpdateMarkHeight(_risingMark, h);
|
||||||
|
UpdateMarkHeight(_outOfRangeMark, h);
|
||||||
|
}
|
||||||
|
void UpdateMarkHeight(SpriteRenderer mark, float h) {
|
||||||
|
mark.transform.localPosition = new(0, _baseBarSize / 2 + mark.size.y / 2 + h);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
Color m_color = Color.white;
|
||||||
|
public Color Color {
|
||||||
|
get => m_color;
|
||||||
|
set {
|
||||||
|
if (m_color == value) return;
|
||||||
|
m_color = value;
|
||||||
|
_barRenderer.color = value;
|
||||||
|
_risingMark.color = value;
|
||||||
|
_outOfRangeMark.color = value;
|
||||||
|
_missingMark.color = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool m_isRising;
|
||||||
|
public bool IsRising {
|
||||||
|
get => m_isRising;
|
||||||
|
set {
|
||||||
|
if (m_isRising == value) return;
|
||||||
|
m_isRising = value;
|
||||||
|
_risingMark.gameObject.SetActive(value);
|
||||||
|
UpdateHeight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool m_isOutOfRange;
|
||||||
|
public bool IsOutOfRange {
|
||||||
|
get => m_isOutOfRange;
|
||||||
|
set {
|
||||||
|
if (m_isOutOfRange == value) return;
|
||||||
|
m_isOutOfRange = value;
|
||||||
|
_outOfRangeMark.gameObject.SetActive(value);
|
||||||
|
UpdateHeight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool m_isMissing;
|
||||||
|
public bool IsMissing {
|
||||||
|
get => m_isMissing;
|
||||||
|
set {
|
||||||
|
if (m_isMissing == value) return;
|
||||||
|
m_isMissing = value;
|
||||||
|
_missingMark.gameObject.SetActive(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float m_width;
|
||||||
|
public float Width {
|
||||||
|
get => m_width;
|
||||||
|
set {
|
||||||
|
if (m_width == value) return;
|
||||||
|
m_width = value;
|
||||||
|
OnSetScale();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Awake() {
|
||||||
|
_baseBarSize = _barRenderer.size.x;
|
||||||
|
}
|
||||||
|
void OnDestroy() {
|
||||||
|
if (m_height != null) {
|
||||||
|
GlobalHeightScaler.Instance.PopHeight(m_height.Value);
|
||||||
|
GlobalHeightScaler.Instance.Update -= UpdateHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnSetScale() {
|
||||||
|
transform.localScale = ComputedScale * m_width / 256 * Vector3.one;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 00fc5cd64f19b374aba03a10b1fc3d43
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
121
Assets/Cryville.EEW.Unity/Map/Element/WaveCircleElement.cs
Normal file
121
Assets/Cryville.EEW.Unity/Map/Element/WaveCircleElement.cs
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
using Cryville.EEW.Core;
|
||||||
|
using Cryville.EEW.Core.Map;
|
||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity.Map.Element {
|
||||||
|
class WaveCircleElement : MapElement {
|
||||||
|
PointF _hypocenterLocation;
|
||||||
|
PointF _hypocenterTilePos;
|
||||||
|
internal void SetHypocenterLocation(PointF value, ref int order) {
|
||||||
|
_hypocenterLocation = value;
|
||||||
|
_hypocenterTilePos = MapTileUtils.WorldToTilePos(value);
|
||||||
|
transform.localPosition = new(0, 0, OrderToZ(ref order));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override RectangleF? AABB {
|
||||||
|
get {
|
||||||
|
var time = (float)(DateTime.UtcNow - OriginTime).TotalSeconds;
|
||||||
|
float angle = WaveTimeCalculator.Instance.CalculatePWaveAngleOrPercentage(Depth, time);
|
||||||
|
if (float.IsNaN(angle)) angle = WaveTimeCalculator.Instance.CalculateSWaveAngleOrPercentage(Depth, time);
|
||||||
|
if (float.IsNaN(angle)) {
|
||||||
|
var rTime = (float?)(ReportTime - OriginTime)?.TotalSeconds;
|
||||||
|
if (rTime != null) {
|
||||||
|
angle = WaveTimeCalculator.Instance.CalculatePWaveAngleOrPercentage(Depth, rTime.Value);
|
||||||
|
if (float.IsNaN(angle)) angle = WaveTimeCalculator.Instance.CalculateSWaveAngleOrPercentage(Depth, rTime.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var inflation = angle > 0 ? angle / 180 : 0;
|
||||||
|
return RectangleF.Inflate(new(_hypocenterTilePos, SizeF.Empty), inflation, inflation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime OriginTime { get; set; }
|
||||||
|
public float Depth { get; set; }
|
||||||
|
public DateTime? ReportTime { get; set; }
|
||||||
|
|
||||||
|
[SerializeField] MultiLineRenderer m_lineRendererP;
|
||||||
|
[SerializeField] MultiLineRenderer m_lineRendererS;
|
||||||
|
[SerializeField] Material m_ongoingMaterial;
|
||||||
|
[SerializeField] Material m_historyMaterial;
|
||||||
|
|
||||||
|
protected override void OnSetScale() {
|
||||||
|
m_lineRendererP.Width = ComputedScale / 256;
|
||||||
|
m_lineRendererS.Width = ComputedScale / 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update() {
|
||||||
|
var time = (float)(DateTime.UtcNow - OriginTime).TotalSeconds;
|
||||||
|
var rTime = (float?)(ReportTime - OriginTime)?.TotalSeconds;
|
||||||
|
|
||||||
|
float pAngle = WaveTimeCalculator.Instance.CalculatePWaveAngleOrPercentage(Depth, time);
|
||||||
|
if (float.IsNaN(pAngle) && rTime != null) {
|
||||||
|
DrawCore(WaveTimeCalculator.Instance.CalculatePWaveAngleOrPercentage(Depth, rTime.Value), m_lineRendererP, true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DrawCore(pAngle, m_lineRendererP);
|
||||||
|
}
|
||||||
|
|
||||||
|
float sAngle = WaveTimeCalculator.Instance.CalculateSWaveAngleOrPercentage(Depth, time);
|
||||||
|
if (float.IsNaN(sAngle) && rTime != null) {
|
||||||
|
DrawCore(WaveTimeCalculator.Instance.CalculateSWaveAngleOrPercentage(Depth, rTime.Value), m_lineRendererS, true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DrawCore(sAngle, m_lineRendererS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly Vector2[] _vertexBuffer = new Vector2[361];
|
||||||
|
void DrawCore(float angle, MultiLineRenderer renderer, bool isHistory = false) {
|
||||||
|
renderer.Clear();
|
||||||
|
if (float.IsNaN(angle)) return;
|
||||||
|
if (angle < -1) return;
|
||||||
|
if (angle < 0) {
|
||||||
|
renderer.Width = ComputedScale / 64;
|
||||||
|
renderer.TilingScale = 1;
|
||||||
|
var point = _hypocenterTilePos.ToVector2();
|
||||||
|
var offset = ComputedScale / 20;
|
||||||
|
angle = (angle + 1) * 360;
|
||||||
|
int segCount = (int)Math.Ceiling(angle);
|
||||||
|
for (int d = 0; d < segCount - 1; d++) {
|
||||||
|
float rad = d * Mathf.PI / 180;
|
||||||
|
_vertexBuffer[d] = point + offset * new Vector2(Mathf.Sin(rad), Mathf.Cos(rad));
|
||||||
|
}
|
||||||
|
float rad2 = angle * Mathf.PI / 180;
|
||||||
|
if (segCount > 0) _vertexBuffer[segCount - 1] = point + offset * new Vector2(Mathf.Sin(rad2), Mathf.Cos(rad2));
|
||||||
|
renderer.AddSegment(_vertexBuffer, 0, segCount);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
renderer.Width = ComputedScale / 256;
|
||||||
|
renderer.TilingScale = 8;
|
||||||
|
float radlat = _hypocenterLocation.Y / 180f * MathF.PI, radlon = _hypocenterLocation.X / 180f * MathF.PI;
|
||||||
|
float rplat = radlat + angle / 180f * MathF.PI;
|
||||||
|
Vector3 rp = new(MathF.Cos(radlon) * MathF.Cos(rplat), MathF.Sin(rplat), MathF.Sin(radlon) * MathF.Cos(rplat));
|
||||||
|
Vector3 axis = new(MathF.Cos(radlon) * MathF.Cos(radlat), MathF.Sin(radlat), MathF.Sin(radlon) * MathF.Cos(radlat));
|
||||||
|
Vector2? lp2 = null;
|
||||||
|
int segmentIndex = 0;
|
||||||
|
for (int d = 0; d < 360; d++) {
|
||||||
|
Quaternion q = Quaternion.AngleAxis(d, axis);
|
||||||
|
Vector3 p = q * rp;
|
||||||
|
Vector2 p2 = ToTilePos(p).ToVector2();
|
||||||
|
if (lp2 != null) {
|
||||||
|
float dx = p2.x - lp2.Value.x;
|
||||||
|
if (MathF.Abs(dx) >= 0.5) {
|
||||||
|
_vertexBuffer[d] = p2.x < 0.5 ? p2 + new Vector2(1, 0) : p2 - new Vector2(1, 0);
|
||||||
|
renderer.AddSegment(_vertexBuffer, segmentIndex, d - segmentIndex + 1);
|
||||||
|
segmentIndex = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_vertexBuffer[d] = p2;
|
||||||
|
lp2 = p2;
|
||||||
|
}
|
||||||
|
Vector2 rp2 = ToTilePos(rp).ToVector2();
|
||||||
|
_vertexBuffer[360] = rp2;
|
||||||
|
renderer.AddSegment(_vertexBuffer, segmentIndex, 361 - segmentIndex);
|
||||||
|
}
|
||||||
|
renderer.SetMaterial(isHistory ? m_historyMaterial : m_ongoingMaterial);
|
||||||
|
}
|
||||||
|
static PointF ToTilePos(Vector3 p) => MapTileUtils.WorldToTilePos(new(MathF.Atan2(p.z, p.x) / MathF.PI * 180f, MathF.Asin(p.y) / MathF.PI * 180f));
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c59bd9617c7906543b3b23f07518b4e1
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
235
Assets/Cryville.EEW.Unity/Map/LineRenderer.cs
Normal file
235
Assets/Cryville.EEW.Unity/Map/LineRenderer.cs
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
using Cryville.EEW.Core.Map;
|
||||||
|
using Cryville.EEW.Unity.Map.Element;
|
||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using UnityEngine;
|
||||||
|
using Color = UnityEngine.Color;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity.Map {
|
||||||
|
[RequireComponent(typeof(MeshFilter))]
|
||||||
|
[RequireComponent(typeof(MeshRenderer))]
|
||||||
|
class LineRenderer : MonoBehaviour {
|
||||||
|
public Material Material {
|
||||||
|
get => _meshRenderer.material;
|
||||||
|
set {
|
||||||
|
_meshRenderer.material = value;
|
||||||
|
_meshRenderer.material.color = m_color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
Color m_color = Color.white;
|
||||||
|
public Color Color {
|
||||||
|
get => m_color;
|
||||||
|
set {
|
||||||
|
if (m_color == value) return;
|
||||||
|
m_color = value;
|
||||||
|
_meshRenderer.material.color = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
float m_width = 1;
|
||||||
|
public float Width {
|
||||||
|
get => m_width;
|
||||||
|
set {
|
||||||
|
if (m_width == value) return;
|
||||||
|
m_width = value;
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
[Range(Vector2.kEpsilon, 1 - Vector2.kEpsilon)]
|
||||||
|
float m_flatCornerThreshold = 1 - Vector2.kEpsilon;
|
||||||
|
public float FlatCornerThreshold {
|
||||||
|
get => m_flatCornerThreshold;
|
||||||
|
set {
|
||||||
|
if (m_flatCornerThreshold == value) return;
|
||||||
|
m_flatCornerThreshold = Mathf.Clamp(value, Vector2.kEpsilon, 1 - Vector2.kEpsilon);
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
[Range(Vector2.kEpsilon, 1 - Vector2.kEpsilon)]
|
||||||
|
float m_sharpCornerThreshold = 0.5f;
|
||||||
|
public float SharpCornerThreshold {
|
||||||
|
get => m_sharpCornerThreshold;
|
||||||
|
set {
|
||||||
|
if (m_sharpCornerThreshold == value) return;
|
||||||
|
m_sharpCornerThreshold = Mathf.Clamp(value, Vector2.kEpsilon, 1 - Vector2.kEpsilon);
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
float m_tilingScale = 1;
|
||||||
|
public float TilingScale {
|
||||||
|
get => m_tilingScale;
|
||||||
|
set {
|
||||||
|
if (m_tilingScale == value) return;
|
||||||
|
m_tilingScale = value;
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int _positionCount;
|
||||||
|
Vector2[] _positions;
|
||||||
|
public void SetPositions(Vector2[] positions) => SetPositions(positions, 0, positions.Length);
|
||||||
|
public void SetPositions(Vector2[] positions, int index, int length) {
|
||||||
|
_positionCount = length;
|
||||||
|
if (_positions is not null)
|
||||||
|
ArrayPool<Vector2>.Shared.Return(_positions);
|
||||||
|
_positions = ArrayPool<Vector2>.Shared.Rent(length);
|
||||||
|
Array.Copy(positions, index, _positions, 0, length);
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LineRenderer Create(
|
||||||
|
IEnumerable<PointF> line, float z,
|
||||||
|
LineRenderer prefab, Material material, Color color, float width, MapElement parent,
|
||||||
|
out RectangleF? aabb
|
||||||
|
) {
|
||||||
|
var tileLine = line.Select(p => MapTileUtils.WorldToTilePos(p)).ToArray();
|
||||||
|
if (tileLine.Length == 0) {
|
||||||
|
aabb = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var lineRenderer = Instantiate(prefab);
|
||||||
|
lineRenderer.SetPositions(line.Select(p => MapTileUtils.WorldToTilePos(p).ToVector2()).ToArray());
|
||||||
|
lineRenderer.Material = material;
|
||||||
|
lineRenderer.Color = color;
|
||||||
|
lineRenderer.Width = width * parent.ComputedScale / 512;
|
||||||
|
lineRenderer.transform.SetParent(parent.transform, false);
|
||||||
|
lineRenderer.transform.localPosition = new(0, 0, z);
|
||||||
|
aabb = tileLine.Select(p => new RectangleF(p, SizeF.Empty)).Aggregate((a, b) => MapTileUtils.UnionTileAABBs(a, b));
|
||||||
|
return lineRenderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _valid;
|
||||||
|
void Invalidate() {
|
||||||
|
_valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mesh _mesh;
|
||||||
|
MeshFilter _meshFilter;
|
||||||
|
MeshRenderer _meshRenderer;
|
||||||
|
void Awake() {
|
||||||
|
_meshFilter = GetComponent<MeshFilter>();
|
||||||
|
_meshRenderer = GetComponent<MeshRenderer>();
|
||||||
|
if (!_meshFilter.mesh) {
|
||||||
|
_meshFilter.mesh = new();
|
||||||
|
}
|
||||||
|
_mesh = _meshFilter.mesh;
|
||||||
|
_meshRenderer.material.color = m_color;
|
||||||
|
}
|
||||||
|
void OnDestroy() {
|
||||||
|
if (_positions is not null)
|
||||||
|
ArrayPool<Vector2>.Shared.Return(_positions);
|
||||||
|
Destroy(_mesh);
|
||||||
|
}
|
||||||
|
void LateUpdate() {
|
||||||
|
if (_valid) return;
|
||||||
|
_valid = true;
|
||||||
|
|
||||||
|
_mesh.Clear();
|
||||||
|
if (_positions == null) return;
|
||||||
|
if (_positionCount <= 1) return;
|
||||||
|
float hw = m_width / 2;
|
||||||
|
int maxVertexCount = 4 * (_positionCount - 1);
|
||||||
|
var vbuf = ArrayPool<Vector3>.Shared.Rent(maxVertexCount);
|
||||||
|
var ubuf = ArrayPool<Vector2>.Shared.Rent(maxVertexCount);
|
||||||
|
var ibuf = ArrayPool<int>.Shared.Rent(3 * (2 + 4 * (_positionCount - 2)));
|
||||||
|
|
||||||
|
int i, vi = 0, ii = 0, li = 0, ri = 1;
|
||||||
|
float uvScale = 1 / (m_tilingScale * m_width);
|
||||||
|
Vector2 p0 = _positions[0], p1 = default;
|
||||||
|
for (i = 1; i < _positionCount; i++) {
|
||||||
|
if ((p1 = _positions[i]) != p0) break;
|
||||||
|
}
|
||||||
|
if (i >= _positionCount) return;
|
||||||
|
Vector2 dp0 = NormalizeSmallVector(p1 - p0), np0 = GetNormal(dp0 * hw);
|
||||||
|
vbuf[vi] = p0 - np0; ubuf[vi++] = new(0, 0);
|
||||||
|
vbuf[vi] = p0 + np0; ubuf[vi++] = new(0, 1);
|
||||||
|
float dist = (p1 - p0).magnitude * uvScale;
|
||||||
|
p0 = p1;
|
||||||
|
for (i++; i < _positionCount; i++) {
|
||||||
|
p1 = _positions[i];
|
||||||
|
if (p1 == p0) continue;
|
||||||
|
Vector2 dp1 = NormalizeSmallVector(p1 - p0), np1 = GetNormal(dp1 * hw);
|
||||||
|
Vector2 dpm = NormalizeSmallVector(dp1 - dp0);
|
||||||
|
float cc = dp1 == dp0 ? 1 : Mathf.Abs(Vector2.Dot(dpm, NormalizeSmallVector(np0)));
|
||||||
|
if (cc > m_flatCornerThreshold) {
|
||||||
|
vbuf[vi] = p0 - np0; ubuf[vi++] = new(dist, 0);
|
||||||
|
vbuf[vi] = p0 + np0; ubuf[vi++] = new(dist, 0);
|
||||||
|
ibuf[ii++] = li; ibuf[ii++] = vi - 2; ibuf[ii++] = ri;
|
||||||
|
ibuf[ii++] = ri; ibuf[ii++] = vi - 2; ibuf[ii++] = vi - 1;
|
||||||
|
li = vi - 2; ri = vi - 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (cc < m_sharpCornerThreshold) {
|
||||||
|
cc = m_sharpCornerThreshold;
|
||||||
|
}
|
||||||
|
float chw = hw / cc;
|
||||||
|
float cl = Mathf.Sqrt(chw * chw - hw * hw);
|
||||||
|
float cluv = cl * uvScale;
|
||||||
|
Vector2 sp0 = p0 - dp0 * cl, sp1 = p0 + dp1 * cl;
|
||||||
|
bool isRight = Vector3.Cross(dp0, dp1).z < 0;
|
||||||
|
|
||||||
|
if (isRight) {
|
||||||
|
vbuf[vi] = sp0 - np0; ubuf[vi++] = new(dist - cluv, 0);
|
||||||
|
vbuf[vi] = p0 + chw * dpm; ubuf[vi++] = new(dist, 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
vbuf[vi] = p0 + chw * dpm; ubuf[vi++] = new(dist, 0);
|
||||||
|
vbuf[vi] = sp0 + np0; ubuf[vi++] = new(dist - cluv, 1);
|
||||||
|
}
|
||||||
|
ibuf[ii++] = li; ibuf[ii++] = vi - 2; ibuf[ii++] = ri;
|
||||||
|
ibuf[ii++] = ri; ibuf[ii++] = vi - 2; ibuf[ii++] = vi - 1;
|
||||||
|
|
||||||
|
vbuf[vi] = p0 - chw * dpm; ubuf[vi++] = new(dist, isRight ? 0 : 1);
|
||||||
|
ibuf[ii++] = vi - 3; ibuf[ii++] = vi - 1; ibuf[ii++] = vi - 2;
|
||||||
|
|
||||||
|
if (isRight) {
|
||||||
|
li = vi; ri = vi - 2;
|
||||||
|
vbuf[vi] = sp1 - np1; ubuf[vi++] = new(dist + cluv, 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
li = vi - 3; ri = vi;
|
||||||
|
vbuf[vi] = sp1 + np1; ubuf[vi++] = new(dist + cluv, 1);
|
||||||
|
}
|
||||||
|
ibuf[ii++] = vi - 2; ibuf[ii++] = li; ibuf[ii++] = ri;
|
||||||
|
}
|
||||||
|
|
||||||
|
dist += (p1 - p0).magnitude * uvScale;
|
||||||
|
p0 = p1; dp0 = dp1; np0 = np1;
|
||||||
|
}
|
||||||
|
|
||||||
|
vbuf[vi] = p0 - np0; ubuf[vi++] = new(dist, 0);
|
||||||
|
vbuf[vi] = p0 + np0; ubuf[vi++] = new(dist, 1);
|
||||||
|
ibuf[ii++] = li; ibuf[ii++] = vi - 2; ibuf[ii++] = ri;
|
||||||
|
ibuf[ii++] = ri; ibuf[ii++] = vi - 2; ibuf[ii++] = vi - 1;
|
||||||
|
|
||||||
|
_mesh.SetVertices(vbuf, 0, vi);
|
||||||
|
_mesh.SetUVs(0, ubuf, 0, vi);
|
||||||
|
_mesh.SetTriangles(ibuf, 0, ii, 0);
|
||||||
|
_mesh.RecalculateNormals();
|
||||||
|
_mesh.RecalculateBounds();
|
||||||
|
|
||||||
|
ArrayPool<int>.Shared.Return(ibuf);
|
||||||
|
ArrayPool<Vector2>.Shared.Return(ubuf);
|
||||||
|
ArrayPool<Vector3>.Shared.Return(vbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
static Vector2 NormalizeSmallVector(Vector2 np0) => np0 / np0.magnitude;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
static Vector2 GetNormal(Vector2 dp) => new(dp.y, -dp.x);
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.EEW.Unity/Map/LineRenderer.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/Map/LineRenderer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 073fe1bdd0645d64eb82329cb69e89fb
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
224
Assets/Cryville.EEW.Unity/Map/MapElementManager.cs
Normal file
224
Assets/Cryville.EEW.Unity/Map/MapElementManager.cs
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
using Cryville.EEW.Core;
|
||||||
|
using Cryville.EEW.Core.Map;
|
||||||
|
using Cryville.EEW.CWAOpenData.Map;
|
||||||
|
using Cryville.EEW.GlobalQuake.Map;
|
||||||
|
using Cryville.EEW.JMAAtom.Map;
|
||||||
|
using Cryville.EEW.Map;
|
||||||
|
using Cryville.EEW.Models.GeoJSON;
|
||||||
|
using Cryville.EEW.NOAA.Map;
|
||||||
|
using Cryville.EEW.Report;
|
||||||
|
using Cryville.EEW.Wolfx.Map;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityMapElement = Cryville.EEW.Unity.Map.Element.MapElement;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity.Map {
|
||||||
|
sealed class MapElementManager : MonoBehaviour {
|
||||||
|
ReportViewModel _selected;
|
||||||
|
readonly List<UnityMapElement> _displayingElements = new();
|
||||||
|
readonly List<ReportViewModel> _displayingReports = new();
|
||||||
|
readonly List<int> _displayingOrder = new();
|
||||||
|
|
||||||
|
[SerializeField] MapElementManager m_subManager;
|
||||||
|
|
||||||
|
public RectangleF? AABB {
|
||||||
|
get {
|
||||||
|
RectangleF? ret = null;
|
||||||
|
foreach (var element in _displayingElements) {
|
||||||
|
if (element == null) continue;
|
||||||
|
if (element.AABB is not RectangleF aabb) continue;
|
||||||
|
if (ret == null) ret = aabb; // TODO dynamic
|
||||||
|
else ret = MapTileUtils.UnionTileAABBs(ret.Value, aabb);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddOngoing(ReportViewModel e) => Add(e);
|
||||||
|
public void RemoveOngoing(ReportViewModel e) => Remove(e);
|
||||||
|
public void SetSelected(ReportViewModel e) {
|
||||||
|
if (_selected is not null)
|
||||||
|
Remove(_selected);
|
||||||
|
Add(e);
|
||||||
|
_selected = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Add(ReportViewModel e) {
|
||||||
|
var element = Build(e.Model, out _, out int order);
|
||||||
|
if (element == null) return;
|
||||||
|
var pos = element.transform.localPosition;
|
||||||
|
pos.z = OrderToZ(_displayingOrder.Sum());
|
||||||
|
element.transform.localPosition = pos;
|
||||||
|
_displayingElements.Add(element);
|
||||||
|
_displayingReports.Add(e);
|
||||||
|
_displayingOrder.Add(order);
|
||||||
|
element.transform.SetParent(transform, false);
|
||||||
|
if (m_subManager != null) m_subManager.Add(e);
|
||||||
|
}
|
||||||
|
void Remove(ReportViewModel e) {
|
||||||
|
int index = _displayingReports.IndexOf(e);
|
||||||
|
if (index == -1) return;
|
||||||
|
_displayingElements.RemoveAt(index);
|
||||||
|
_displayingReports.RemoveAt(index);
|
||||||
|
_displayingOrder.RemoveAt(index);
|
||||||
|
var element = transform.GetChild(index);
|
||||||
|
element.SetParent(null, false);
|
||||||
|
Destroy(element.gameObject);
|
||||||
|
|
||||||
|
int order = _displayingOrder.Take(index).Sum();
|
||||||
|
for (int i = index; i < transform.childCount; i++) {
|
||||||
|
var child = transform.GetChild(i);
|
||||||
|
var pos = child.localPosition;
|
||||||
|
pos.z = OrderToZ(order);
|
||||||
|
child.localPosition = pos;
|
||||||
|
order += _displayingOrder[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_subManager != null) m_subManager.Remove(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
float m_scale = 1;
|
||||||
|
public float Scale {
|
||||||
|
get => m_scale;
|
||||||
|
set {
|
||||||
|
m_scale = value;
|
||||||
|
foreach (Transform element in transform) {
|
||||||
|
element.GetComponent<UnityMapElement>().Scale = value;
|
||||||
|
}
|
||||||
|
if (m_subManager != null) m_subManager.Scale = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly ContextedGeneratorManager<IMapGeneratorContext, MapElement> _gen = new(new IContextedGenerator<IMapGeneratorContext, MapElement>[] {
|
||||||
|
new CENCEarthquakeMapGenerator(),
|
||||||
|
new CENCEEWMapGenerator(),
|
||||||
|
new CWAEarthquakeMapGenerator(),
|
||||||
|
new CWAEEWMapGenerator(),
|
||||||
|
new CWATsunamiMapGenerator(),
|
||||||
|
new FujianEEWMapGenerator(),
|
||||||
|
new GlobalQuakeMapViewGenerator(),
|
||||||
|
new JMAAtomMapGenerator(),
|
||||||
|
new JMAEEWMapGenerator(),
|
||||||
|
new NOAAMapGenerator(),
|
||||||
|
new SichuanEEWMapGenerator(),
|
||||||
|
});
|
||||||
|
public UnityMapElement Build(object e, out CultureInfo culture, out int order) {
|
||||||
|
culture = CultureInfo.InvariantCulture;
|
||||||
|
order = 0;
|
||||||
|
var ret = Convert(_gen.TryGenerate(e, SharedSettings.Instance, ref culture), ref order);
|
||||||
|
if (ret == null)
|
||||||
|
return null;
|
||||||
|
ret.Scale = Scale;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static float OrderToZ(int order) => order / -1000f;
|
||||||
|
|
||||||
|
[SerializeField] Element.GroupElement m_prefabGroupElement;
|
||||||
|
[SerializeField] Element.MaskedGroupElement m_prefabMaskedGroupElement;
|
||||||
|
[SerializeField] Element.OngoingGroupElement m_prefabOngoingGroupElement;
|
||||||
|
[SerializeField] Element.HypocenterElement m_prefabHypocenterElement;
|
||||||
|
[SerializeField] Element.LabeledPointElement m_prefabLabeledPointElement;
|
||||||
|
[SerializeField] Element.MultiLineElement m_prefabMultiLineElement;
|
||||||
|
[SerializeField] Element.MultiPolygonElement m_prefabMultiPolygonElement;
|
||||||
|
[SerializeField] Element.PointElement m_prefabPointElement;
|
||||||
|
[SerializeField] Element.TsunamiHeightElement m_prefabTsunamiHeightElement;
|
||||||
|
[SerializeField] Element.WaveCircleElement m_prefabWaveCircleElement;
|
||||||
|
public UnityMapElement Convert(MapElement mapElement, ref int order) => mapElement switch {
|
||||||
|
MaskedGroupElement maskedGroup => ConvertMaskedGroup(maskedGroup, ref order),
|
||||||
|
OngoingGroupElement ongoingGroup => ConvertOngoingGroup(ongoingGroup, ref order),
|
||||||
|
GroupElement group => ConvertGroup(group, m_prefabGroupElement, ref order),
|
||||||
|
HypocenterElement hypocenter => ConvertHypocenter(hypocenter, ref order),
|
||||||
|
LabeledPointElement labeledPoint => ConvertLabeledPoint(labeledPoint, ref order),
|
||||||
|
MultiLineElement multiLine => ConvertMultiLine(multiLine, ref order),
|
||||||
|
MultiPolygonElement multiPolygon => ConvertMultiPolygon(multiPolygon, ref order),
|
||||||
|
PointElement point => ConvertPoint(point, ref order),
|
||||||
|
TsunamiHeightElement tsunamiHeight => ConvertTsunamiHeight(tsunamiHeight, ref order),
|
||||||
|
WaveCircleElement waveCircle => ConvertWaveCircle(waveCircle, ref order),
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
T Convert<T>(MapElement element, T prefab) where T : UnityMapElement {
|
||||||
|
var ret = Instantiate(prefab);
|
||||||
|
ret.MaxScale = element.MaxScale;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
T ConvertGroup<T>(GroupElement group, T prefab, ref int order) where T : Element.GroupElement {
|
||||||
|
var ret = Convert(group, prefab);
|
||||||
|
foreach (var e in group.Elements) ret.AddElement(Convert(e, ref order));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Element.MaskedGroupElement ConvertMaskedGroup(MaskedGroupElement maskedGroup, ref int order) {
|
||||||
|
var ret = ConvertGroup(maskedGroup, m_prefabMaskedGroupElement, ref order);
|
||||||
|
ret.SetMasks(maskedGroup.Masks);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Element.OngoingGroupElement ConvertOngoingGroup(OngoingGroupElement ongoingGroup, ref int order) {
|
||||||
|
var ret = ConvertGroup(ongoingGroup, m_prefabOngoingGroupElement, ref order);
|
||||||
|
ret.BlinkingPeriod = ongoingGroup.BlinkingPeriod;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Element.HypocenterElement ConvertHypocenter(HypocenterElement hypocenter, ref int order) {
|
||||||
|
var ret = Convert(hypocenter, m_prefabHypocenterElement);
|
||||||
|
ret.SetLocation(hypocenter.Location, ref order);
|
||||||
|
ret.IsLowQuality = hypocenter.IsLowQuality;
|
||||||
|
ret.Size = hypocenter.Size;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Element.LabeledPointElement ConvertLabeledPoint(LabeledPointElement labeledPoint, ref int order) {
|
||||||
|
var ret = Convert(labeledPoint, m_prefabLabeledPointElement);
|
||||||
|
ret.SetLocation(labeledPoint.Location, ref order);
|
||||||
|
ret.Text = labeledPoint.Text;
|
||||||
|
ret.Color = labeledPoint.FillColor.ToUnityColor();
|
||||||
|
ret.TextColor = labeledPoint.TextColor.ToUnityColor();
|
||||||
|
ret.IsArea = labeledPoint.IsAreaLabel;
|
||||||
|
ret.Size = labeledPoint.Size;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Element.MultiLineElement ConvertMultiLine(MultiLineElement multiLine, ref int order) {
|
||||||
|
var ret = Convert(multiLine, m_prefabMultiLineElement);
|
||||||
|
ret.SetLines(multiLine.Lines, ref order);
|
||||||
|
ret.Color = multiLine.BorderColor.ToUnityColor();
|
||||||
|
ret.Width = multiLine.StrokeWidth;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Element.MultiPolygonElement ConvertMultiPolygon(MultiPolygonElement multiPolygon, ref int order) {
|
||||||
|
var ret = Convert(multiPolygon, m_prefabMultiPolygonElement);
|
||||||
|
ret.SetPolygons(multiPolygon.Polygons, ref order);
|
||||||
|
ret.FillColor = multiPolygon.FillColor.ToUnityColor();
|
||||||
|
ret.BorderColor = multiPolygon.BorderColor.ToUnityColor();
|
||||||
|
ret.BorderWidth = multiPolygon.StrokeWidth;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Element.PointElement ConvertPoint(PointElement point, ref int order) {
|
||||||
|
var ret = Convert(point, m_prefabPointElement);
|
||||||
|
ret.SetLocation(point.Location, ref order);
|
||||||
|
ret.Color = point.FillColor.ToUnityColor();
|
||||||
|
ret.Size = point.Size;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Element.TsunamiHeightElement ConvertTsunamiHeight(TsunamiHeightElement tsunamiHeight, ref int order) {
|
||||||
|
var ret = Convert(tsunamiHeight, m_prefabTsunamiHeightElement);
|
||||||
|
ret.SetLocation(tsunamiHeight.Location, ref order);
|
||||||
|
ret.Height = tsunamiHeight.Height;
|
||||||
|
ret.Color = tsunamiHeight.FillColor.ToUnityColor();
|
||||||
|
ret.IsRising = tsunamiHeight.IsRising;
|
||||||
|
ret.IsOutOfRange = tsunamiHeight.IsOutOfRange;
|
||||||
|
ret.IsMissing = tsunamiHeight.IsMissing;
|
||||||
|
ret.Width = tsunamiHeight.Width;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Element.WaveCircleElement ConvertWaveCircle(WaveCircleElement waveCircle, ref int order) {
|
||||||
|
var ret = Convert(waveCircle, m_prefabWaveCircleElement);
|
||||||
|
ret.SetHypocenterLocation(waveCircle.HypocenterLocation, ref order);
|
||||||
|
ret.OriginTime = waveCircle.OriginTime;
|
||||||
|
ret.Depth = waveCircle.Depth;
|
||||||
|
ret.ReportTime = waveCircle.ReportTime;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.EEW.Unity/Map/MapElementManager.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/Map/MapElementManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: dc48b51fb50522e4785fd56204934984
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
109
Assets/Cryville.EEW.Unity/Map/MapTile.cs
Normal file
109
Assets/Cryville.EEW.Unity/Map/MapTile.cs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
using Cryville.EEW.Core.Map;
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Networking;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity.Map {
|
||||||
|
[RequireComponent(typeof(SpriteRenderer))]
|
||||||
|
sealed class MapTile : MonoBehaviour {
|
||||||
|
static readonly SemaphoreSlim _semaphore = new(2);
|
||||||
|
|
||||||
|
static readonly HttpClient _httpClient = new() { Timeout = TimeSpan.FromSeconds(10) };
|
||||||
|
|
||||||
|
public MapTileIndex Index { get; set; }
|
||||||
|
public bool IsEmpty { get; private set; }
|
||||||
|
|
||||||
|
Action<MapTile> _callback;
|
||||||
|
|
||||||
|
SpriteRenderer _renderer;
|
||||||
|
|
||||||
|
UnityWebRequest _req;
|
||||||
|
DownloadHandlerTexture _texHandler;
|
||||||
|
Texture2D _tex;
|
||||||
|
Sprite _sprite;
|
||||||
|
|
||||||
|
void Awake() {
|
||||||
|
_renderer = GetComponent<SpriteRenderer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
FileInfo _localFile;
|
||||||
|
bool _downloadDone;
|
||||||
|
public void Load(MapTileIndex index, string cacheDir, Action<MapTile> onUpdated) {
|
||||||
|
Index = index;
|
||||||
|
_callback = onUpdated;
|
||||||
|
_localFile = new(Path.Combine(cacheDir, $"map/{Index.Z}/{Index.NX}/{Index.NY}"));
|
||||||
|
float z = 1 << index.Z;
|
||||||
|
transform.localPosition = new(index.X / z, -(index.Y + 1) / z, -index.Z / 100f);
|
||||||
|
transform.localScale = new Vector3(1 / z, 1 / z);
|
||||||
|
if (_localFile.Exists) {
|
||||||
|
_downloadDone = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Task.Run(() => RunAsync($"https://server.arcgisonline.com/ArcGIS/rest/services/Ocean/World_Ocean_Base/MapServer/tile/{Index.Z}/{Index.NY}/{Index.NX}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async Task RunAsync(string url) {
|
||||||
|
await _semaphore.WaitAsync().ConfigureAwait(true);
|
||||||
|
try {
|
||||||
|
Directory.CreateDirectory(_localFile.DirectoryName);
|
||||||
|
using var webStream = await _httpClient.GetStreamAsync(new Uri(url)).ConfigureAwait(true);
|
||||||
|
using var fileStream = new FileStream(_localFile.FullName, FileMode.Create, FileAccess.Write);
|
||||||
|
await webStream.CopyToAsync(fileStream).ConfigureAwait(true);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
_semaphore.Release();
|
||||||
|
}
|
||||||
|
_downloadDone = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("CodeQuality", "IDE0051", Justification = "Unity message")]
|
||||||
|
void Update() {
|
||||||
|
if (_downloadDone) {
|
||||||
|
try {
|
||||||
|
_texHandler = new DownloadHandlerTexture();
|
||||||
|
_req = new UnityWebRequest($"file:///{_localFile}") {
|
||||||
|
downloadHandler = _texHandler,
|
||||||
|
disposeDownloadHandlerOnDispose = true,
|
||||||
|
};
|
||||||
|
_req.SendWebRequest();
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
Debug.LogException(ex);
|
||||||
|
}
|
||||||
|
_downloadDone = false;
|
||||||
|
}
|
||||||
|
if (_req == null || !_req.isDone) return;
|
||||||
|
if (_texHandler.isDone) {
|
||||||
|
_tex = _texHandler.texture;
|
||||||
|
_tex.wrapMode = TextureWrapMode.Clamp;
|
||||||
|
_sprite = Sprite.Create(_tex, new Rect(0, 0, _tex.width, _tex.height), Vector2.zero, _tex.height);
|
||||||
|
_renderer.sprite = _sprite;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Debug.LogError(_req.error);
|
||||||
|
_localFile.Delete();
|
||||||
|
IsEmpty = true;
|
||||||
|
}
|
||||||
|
_req.Dispose();
|
||||||
|
_req = null;
|
||||||
|
_callback?.Invoke(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("CodeQuality", "IDE0051", Justification = "Unity message")]
|
||||||
|
void OnDestroy() {
|
||||||
|
if (_req != null) {
|
||||||
|
_req.Abort();
|
||||||
|
_req.Dispose();
|
||||||
|
}
|
||||||
|
if (_sprite) Destroy(_sprite);
|
||||||
|
if (_tex) Destroy(_tex);
|
||||||
|
IsEmpty = true;
|
||||||
|
_callback?.Invoke(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.EEW.Unity/Map/MapTile.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/Map/MapTile.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 772f71bcdaaa2504d9e9a469c3100593
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
166
Assets/Cryville.EEW.Unity/Map/MapTileCacheManager.cs
Normal file
166
Assets/Cryville.EEW.Unity/Map/MapTileCacheManager.cs
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
using Cryville.EEW.Core.Map;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity.Map {
|
||||||
|
sealed class TileZOrderComparer : IComparer<MapTileIndex>, IComparer<MapTile> {
|
||||||
|
public static readonly TileZOrderComparer Instance = new();
|
||||||
|
public int Compare(MapTileIndex a, MapTileIndex b) {
|
||||||
|
var c = a.Z.CompareTo(b.Z);
|
||||||
|
if (c != 0) return c;
|
||||||
|
c = a.Y.CompareTo(b.Y);
|
||||||
|
if (c != 0) return c;
|
||||||
|
return a.X.CompareTo(b.X);
|
||||||
|
}
|
||||||
|
public int Compare(MapTile a, MapTile b) {
|
||||||
|
if (a == null) return b == null ? 0 : -1;
|
||||||
|
if (b == null) return 1;
|
||||||
|
return Compare(a.Index, b.Index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class MapTileCacheManager : IDisposable {
|
||||||
|
public int ExtraCachedZoomLevel { get; set; } = 2;
|
||||||
|
|
||||||
|
GameObject m_prefabTile;
|
||||||
|
public GameObject PrefabTile {
|
||||||
|
get => m_prefabTile;
|
||||||
|
set {
|
||||||
|
m_prefabTile = value;
|
||||||
|
if (_dummyTask) GameObject.Destroy(_dummyTask.gameObject);
|
||||||
|
_dummyTask = GameObject.Instantiate(m_prefabTile, Parent, false).GetComponent<MapTile>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transform Parent { get; set; }
|
||||||
|
|
||||||
|
public string CacheDir { get; set; }
|
||||||
|
|
||||||
|
public event Action Updated;
|
||||||
|
void OnUpdated(MapTile tile) {
|
||||||
|
if (tile.IsEmpty) {
|
||||||
|
lock (ActiveTiles) {
|
||||||
|
if (_cache.Remove(tile.Index)) {
|
||||||
|
ActiveTiles.RemoveAt(ActiveTiles.BinarySearch(tile, TileZOrderComparer.Instance));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Updated?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() {
|
||||||
|
MonoBehaviour.Destroy(_dummyTask);
|
||||||
|
|
||||||
|
lock (ActiveTiles) {
|
||||||
|
foreach (var task in ActiveTiles)
|
||||||
|
GameObject.Destroy(task.gameObject);
|
||||||
|
ActiveTiles.Clear();
|
||||||
|
_cache.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly Dictionary<MapTileIndex, MapTile> _cache = new();
|
||||||
|
public List<MapTile> ActiveTiles { get; } = new();
|
||||||
|
|
||||||
|
MapTileIndex _a, _b;
|
||||||
|
|
||||||
|
public void MoveTo(MapTileIndex a, MapTileIndex b) {
|
||||||
|
if (a.Z != b.Z) throw new ArgumentException("Mismatched Z index.");
|
||||||
|
if (a.X >= b.X || a.Y >= b.Y) throw new ArgumentException("Incorrect relative X/Y index.");
|
||||||
|
|
||||||
|
lock (ActiveTiles) {
|
||||||
|
UnloadTiles(a, b);
|
||||||
|
_a = a; _b = b;
|
||||||
|
LoadTiles(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void LoadTiles(MapTileIndex a, MapTileIndex b) {
|
||||||
|
for (int z = Math.Max(0, a.Z - ExtraCachedZoomLevel); z <= Math.Max(0, a.Z); z++) {
|
||||||
|
var ia = a.ZoomToLevel(z, Math.Floor);
|
||||||
|
var ib = b.ZoomToLevel(z, Math.Ceiling);
|
||||||
|
for (int x = ia.X; x < ib.X; x++) {
|
||||||
|
for (int y = ia.Y; y < ib.Y; y++) {
|
||||||
|
var index = new MapTileIndex(x, y, z);
|
||||||
|
if (_cache.ContainsKey(index)) continue;
|
||||||
|
var task = GameObject.Instantiate(PrefabTile, Parent, false).GetComponent<MapTile>();
|
||||||
|
task.Load(index, CacheDir, OnUpdated);
|
||||||
|
_cache.Add(index, task);
|
||||||
|
var i = ~ActiveTiles.BinarySearch(task, TileZOrderComparer.Instance);
|
||||||
|
ActiveTiles.Insert(i, task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void UnloadTiles(MapTileIndex a, MapTileIndex b) {
|
||||||
|
if (a.Z != _a.Z) {
|
||||||
|
for (int z = _a.Z - ExtraCachedZoomLevel; z < a.Z - ExtraCachedZoomLevel; z++) UnloadTilesAtZoomLevel(z);
|
||||||
|
for (int z = a.Z + 1; z <= _a.Z; z++) UnloadTilesAtZoomLevel(z);
|
||||||
|
}
|
||||||
|
if (a.X > _a.X) {
|
||||||
|
for (int z = Math.Max(0, a.Z); z >= Math.Max(0, a.Z - ExtraCachedZoomLevel); --z) {
|
||||||
|
var ia0 = _a.ZoomToLevel(z, Math.Floor);
|
||||||
|
var ib0 = _b.ZoomToLevel(z, Math.Ceiling);
|
||||||
|
var ia1 = a.ZoomToLevel(z, Math.Floor);
|
||||||
|
if (ia0.X == ia1.X) break;
|
||||||
|
UnloadTilesInRegion(ia0.X, ia0.Y, ia1.X, ib0.Y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (b.X < _b.X) {
|
||||||
|
for (int z = Math.Max(0, a.Z); z >= Math.Max(0, a.Z - ExtraCachedZoomLevel); --z) {
|
||||||
|
var ia0 = _a.ZoomToLevel(z, Math.Floor);
|
||||||
|
var ib0 = _b.ZoomToLevel(z, Math.Ceiling);
|
||||||
|
var ib1 = b.ZoomToLevel(z, Math.Ceiling);
|
||||||
|
if (ib0.X == ib1.X) break;
|
||||||
|
UnloadTilesInRegion(ib1.X, ia0.Y, ib0.X, ib0.Y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (a.Y > _a.Y) {
|
||||||
|
for (int z = Math.Max(0, a.Z); z >= Math.Max(0, a.Z - ExtraCachedZoomLevel); --z) {
|
||||||
|
var ia0 = _a.ZoomToLevel(z, Math.Floor);
|
||||||
|
var ib0 = _b.ZoomToLevel(z, Math.Ceiling);
|
||||||
|
var ia1 = a.ZoomToLevel(z, Math.Floor);
|
||||||
|
if (ia0.Y == ia1.Y) break;
|
||||||
|
UnloadTilesInRegion(ia0.X, ia0.Y, ib0.X, ia1.Y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (b.Y < _b.Y) {
|
||||||
|
for (int z = Math.Max(0, a.Z); z >= Math.Max(0, a.Z - ExtraCachedZoomLevel); --z) {
|
||||||
|
var ia0 = _a.ZoomToLevel(z, Math.Floor);
|
||||||
|
var ib0 = _b.ZoomToLevel(z, Math.Ceiling);
|
||||||
|
var ib1 = b.ZoomToLevel(z, Math.Ceiling);
|
||||||
|
if (ib0.Y == ib1.Y) break;
|
||||||
|
UnloadTilesInRegion(ia0.X, ib1.Y, ib0.X, ib0.Y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void UnloadTilesInRegion(int x1, int y1, int x2, int y2, int z) {
|
||||||
|
for (int x = x1; x < x2; x++) {
|
||||||
|
for (int y = y1; y < y2; y++) {
|
||||||
|
var index = new MapTileIndex(x, y, z);
|
||||||
|
if (!_cache.TryGetValue(index, out var task)) continue;
|
||||||
|
GameObject.Destroy(task.gameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MapTile _dummyTask;
|
||||||
|
void UnloadTilesAtZoomLevel(int z) {
|
||||||
|
if (z < 0) return;
|
||||||
|
|
||||||
|
_dummyTask.Index = new(int.MinValue, int.MinValue, z);
|
||||||
|
var i0 = ActiveTiles.BinarySearch(_dummyTask, TileZOrderComparer.Instance);
|
||||||
|
if (i0 < 0) i0 = ~i0;
|
||||||
|
|
||||||
|
_dummyTask.Index = new(int.MinValue, int.MinValue, z + 1);
|
||||||
|
var i1 = ActiveTiles.BinarySearch(_dummyTask, TileZOrderComparer.Instance);
|
||||||
|
if (i1 < 0) i1 = ~i1;
|
||||||
|
|
||||||
|
for (var i = i1 - 1; i >= i0; --i) {
|
||||||
|
var index = ActiveTiles[i].Index;
|
||||||
|
if (!_cache.TryGetValue(index, out var task)) continue;
|
||||||
|
GameObject.Destroy(task.gameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.EEW.Unity/Map/MapTileCacheManager.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/Map/MapTileCacheManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: db3aee38f4dfa62429bbe7025fd6c422
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
70
Assets/Cryville.EEW.Unity/Map/MultiLineRenderer.cs
Normal file
70
Assets/Cryville.EEW.Unity/Map/MultiLineRenderer.cs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity.Map {
|
||||||
|
class MultiLineRenderer : MonoBehaviour {
|
||||||
|
[SerializeField]
|
||||||
|
LineRenderer m_lineRendererPrefab;
|
||||||
|
public LineRenderer LineRenderer {
|
||||||
|
get => m_lineRendererPrefab;
|
||||||
|
set {
|
||||||
|
if (m_lineRendererPrefab == value) return;
|
||||||
|
m_lineRendererPrefab = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
float m_width = 1;
|
||||||
|
public float Width {
|
||||||
|
get => m_width;
|
||||||
|
set {
|
||||||
|
if (m_width == value) return;
|
||||||
|
m_width = value;
|
||||||
|
for (int i = 0; i < _segmentIndex; i++) {
|
||||||
|
_segments[i].Width = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
float m_tilingScale = 1;
|
||||||
|
public float TilingScale {
|
||||||
|
get => m_tilingScale;
|
||||||
|
set {
|
||||||
|
if (m_tilingScale == value) return;
|
||||||
|
m_tilingScale = value;
|
||||||
|
for (int i = 0; i < _segmentIndex; i++) {
|
||||||
|
_segments[i].TilingScale = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int _segmentIndex;
|
||||||
|
readonly List<LineRenderer> _segments = new();
|
||||||
|
public void AddSegment(Vector2[] positions) => AddSegment(positions, 0, positions.Length);
|
||||||
|
public void AddSegment(Vector2[] positions, int index, int length) {
|
||||||
|
LineRenderer segment;
|
||||||
|
if (_segmentIndex >= _segments.Count) {
|
||||||
|
_segments.Add(segment = Instantiate(m_lineRendererPrefab, transform, false).GetComponent<LineRenderer>());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
segment = _segments[_segmentIndex++];
|
||||||
|
segment.gameObject.SetActive(true);
|
||||||
|
}
|
||||||
|
segment.SetPositions(positions, index, length);
|
||||||
|
segment.Width = m_width;
|
||||||
|
}
|
||||||
|
public void Clear() {
|
||||||
|
for (int i = 0; i < _segmentIndex; i++) {
|
||||||
|
_segments[i].gameObject.SetActive(false);
|
||||||
|
}
|
||||||
|
_segmentIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetMaterial(Material material) {
|
||||||
|
foreach (var r in _segments) {
|
||||||
|
r.Material = material;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.EEW.Unity/Map/MultiLineRenderer.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/Map/MultiLineRenderer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3c4b9fd0d7969a94bae8539e2db32009
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
93
Assets/Cryville.EEW.Unity/Map/PolygonRenderer.cs
Normal file
93
Assets/Cryville.EEW.Unity/Map/PolygonRenderer.cs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
using Cryville.EEW.Core.Map;
|
||||||
|
using Poly2Tri;
|
||||||
|
using System.Buffers;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEngine;
|
||||||
|
using Color = UnityEngine.Color;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity.Map {
|
||||||
|
[RequireComponent(typeof(MeshFilter))]
|
||||||
|
[RequireComponent(typeof(MeshRenderer))]
|
||||||
|
class PolygonRenderer : MonoBehaviour {
|
||||||
|
public Material Material {
|
||||||
|
get => _meshRenderer.material;
|
||||||
|
set {
|
||||||
|
_meshRenderer.material = value;
|
||||||
|
_meshRenderer.material.color = m_color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
Color m_color = Color.white;
|
||||||
|
public Color Color {
|
||||||
|
get => m_color;
|
||||||
|
set {
|
||||||
|
if (m_color == value) return;
|
||||||
|
m_color = value;
|
||||||
|
_meshRenderer.material.color = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Mesh _mesh;
|
||||||
|
MeshFilter _meshFilter;
|
||||||
|
MeshRenderer _meshRenderer;
|
||||||
|
void Awake() {
|
||||||
|
_meshFilter = GetComponent<MeshFilter>();
|
||||||
|
_meshRenderer = GetComponent<MeshRenderer>();
|
||||||
|
if (!_meshFilter.mesh) {
|
||||||
|
_meshFilter.mesh = new();
|
||||||
|
}
|
||||||
|
_mesh = _meshFilter.mesh;
|
||||||
|
_meshRenderer.material.color = m_color;
|
||||||
|
}
|
||||||
|
void OnDestroy() {
|
||||||
|
Destroy(_mesh);
|
||||||
|
}
|
||||||
|
public void SetPolygon(IEnumerable<IEnumerable<PointF>> polygon) {
|
||||||
|
_mesh.Clear();
|
||||||
|
|
||||||
|
Polygon convertedPolygon = null;
|
||||||
|
foreach (var loop in polygon) {
|
||||||
|
var convertedLoop = new Polygon(loop.Select(p => {
|
||||||
|
var v = MapTileUtils.WorldToTilePos(p).ToVector2();
|
||||||
|
return new PolygonPoint(v.x, v.y);
|
||||||
|
}));
|
||||||
|
if (convertedPolygon is null) {
|
||||||
|
convertedPolygon = convertedLoop;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
convertedPolygon.AddHole(convertedLoop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tcx = new DTSweepContext();
|
||||||
|
tcx.PrepareTriangulation(convertedPolygon);
|
||||||
|
DTSweep.Triangulate(tcx);
|
||||||
|
|
||||||
|
var codeToIndex = new Dictionary<uint, int>();
|
||||||
|
var vertices = ArrayPool<Vector3>.Shared.Rent(convertedPolygon.Points.Count);
|
||||||
|
var triangles = ArrayPool<int>.Shared.Rent(convertedPolygon.Triangles.Count * 3);
|
||||||
|
int vi = 0, ii = 0;
|
||||||
|
foreach (var tri in convertedPolygon.Triangles) {
|
||||||
|
for (int i = 2; i >= 0; --i) {
|
||||||
|
var p = tri.Points[i];
|
||||||
|
if (!codeToIndex.TryGetValue(p.VertexCode, out int index)) {
|
||||||
|
codeToIndex.Add(p.VertexCode, index = vi++);
|
||||||
|
vertices[index] = new(p.Xf, p.Yf);
|
||||||
|
}
|
||||||
|
triangles[ii++] = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_mesh.SetVertices(vertices, 0, vi);
|
||||||
|
_mesh.SetTriangles(triangles, 0, ii, 0);
|
||||||
|
_mesh.RecalculateNormals();
|
||||||
|
_mesh.RecalculateBounds();
|
||||||
|
|
||||||
|
ArrayPool<int>.Shared.Return(triangles);
|
||||||
|
ArrayPool<Vector3>.Shared.Return(vertices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.EEW.Unity/Map/PolygonRenderer.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/Map/PolygonRenderer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: cd4f627f0b43de845b20ffb1d668f14c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Assets/Cryville.EEW.Unity/Map/VectorExtensions.cs
Normal file
8
Assets/Cryville.EEW.Unity/Map/VectorExtensions.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using System.Drawing;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity.Map {
|
||||||
|
static class VectorExtensions {
|
||||||
|
public static Vector2 ToVector2(this PointF point) => new(point.X, -point.Y);
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.EEW.Unity/Map/VectorExtensions.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/Map/VectorExtensions.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3914c07b14665e94aa3900b890884804
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
27
Assets/Cryville.EEW.Unity/PlatformConfig.cs
Normal file
27
Assets/Cryville.EEW.Unity/PlatformConfig.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using Cryville.Common.Font;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Cryville.Crtr {
|
||||||
|
internal static class PlatformConfig {
|
||||||
|
#if UNITY_STANDALONE_WIN
|
||||||
|
public static readonly string Name = "windows";
|
||||||
|
#elif UNITY_ANDROID
|
||||||
|
public static readonly string Name = "android";
|
||||||
|
#else
|
||||||
|
#error Unknown platform.
|
||||||
|
#endif
|
||||||
|
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
|
||||||
|
public static readonly string FileProtocolPrefix = "file:///";
|
||||||
|
public static readonly FontManager FontManager = new FontManagerWindows();
|
||||||
|
public static Dictionary<string, List<string>> ScriptFontMap => FallbackListFontMatcher.GetDefaultWindowsFallbackMap();
|
||||||
|
public static readonly string TextShader = "TextMesh Pro/Shaders/TMP_SDF SSD";
|
||||||
|
#elif UNITY_ANDROID
|
||||||
|
public static readonly string FileProtocolPrefix = "file://";
|
||||||
|
public static readonly FontManager FontManager = new FontManagerAndroid();
|
||||||
|
public static Dictionary<string, List<string>> ScriptFontMap => FallbackListFontMatcher.GetDefaultAndroidFallbackMap();
|
||||||
|
public static readonly string TextShader = "TextMesh Pro/Shaders/TMP_SDF-Mobile SSD";
|
||||||
|
#else
|
||||||
|
#error Unknown platform.
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.EEW.Unity/PlatformConfig.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/PlatformConfig.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c9a3ab42c572cf44b84b595592563f91
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
24
Assets/Cryville.EEW.Unity/SharedSettings.cs
Normal file
24
Assets/Cryville.EEW.Unity/SharedSettings.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using Cryville.EEW.Colors;
|
||||||
|
using Cryville.EEW.Core.Colors;
|
||||||
|
using Cryville.EEW.FERegion;
|
||||||
|
using Cryville.EEW.Map;
|
||||||
|
using Cryville.EEW.Report;
|
||||||
|
using Cryville.EEW.TTS;
|
||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity {
|
||||||
|
sealed class SharedSettings : IRVMGeneratorContext, IMapGeneratorContext, ITTSMessageGeneratorContext {
|
||||||
|
static SharedSettings s_instance;
|
||||||
|
public static SharedSettings Instance => s_instance ??= new();
|
||||||
|
|
||||||
|
public ISeverityScheme SeverityScheme => DefaultSeverityScheme.Instance;
|
||||||
|
public ISeverityColorMapping SeverityColorMapping => DefaultSeverityColorMapping.Instance;
|
||||||
|
public bool UseContinuousColor => true;
|
||||||
|
public IColorScheme ColorScheme => new SeverityBasedColorScheme(SeverityScheme, DefaultSeverityColorMapping.Instance);
|
||||||
|
public ISubColorScheme BorderColorScheme => new WrappedColorScheme(new SeverityBasedColorScheme(SeverityScheme, DefaultSeverityColorMapping.SecondaryInstance));
|
||||||
|
public ISubColorScheme TextColorScheme => new DefaultTextColorScheme(Color.White, Color.Black);
|
||||||
|
public ILocationConverter LocationConverter => new FERegionLongConverter();
|
||||||
|
public TimeSpan NowcastWarningDelayTolerance => TimeSpan.MaxValue;
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.EEW.Unity/SharedSettings.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/SharedSettings.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ee62d4ae7a6870e49a0dfc662c3c2d41
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
21
Assets/Cryville.EEW.Unity/SoundPlayer.cs
Normal file
21
Assets/Cryville.EEW.Unity/SoundPlayer.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using Cryville.Audio;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity {
|
||||||
|
class SoundPlayer : Core.SoundPlayer {
|
||||||
|
public SoundPlayer() : base(GetEngineList(), AudioUsage.NotificationEvent) { }
|
||||||
|
static List<Type> GetEngineList() => new() {
|
||||||
|
typeof(Audio.Wasapi.MMDeviceEnumeratorWrapper),
|
||||||
|
typeof(Audio.WaveformAudio.WaveDeviceManager)
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override Stream Open(string path) {
|
||||||
|
path = Path.Combine(Application.streamingAssetsPath, "Sounds", path + ".ogg");
|
||||||
|
if (!File.Exists(path)) return null;
|
||||||
|
return new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.EEW.Unity/SoundPlayer.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/SoundPlayer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2a8a590d8e5b3e1468fe2a0f828625ff
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
25
Assets/Cryville.EEW.Unity/TTSWorker.cs
Normal file
25
Assets/Cryville.EEW.Unity/TTSWorker.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity {
|
||||||
|
class TTSWorker : Core.TTSWorker {
|
||||||
|
public TTSWorker() : base(CreateSoundPlayer()) { }
|
||||||
|
|
||||||
|
static SoundPlayer CreateSoundPlayer() {
|
||||||
|
try {
|
||||||
|
return new SoundPlayer();
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool IsSpeaking() => false;
|
||||||
|
|
||||||
|
protected override Task Speak(CultureInfo culture, string content, CancellationToken cancellationToken) => Task.CompletedTask;
|
||||||
|
|
||||||
|
protected override void StopCurrent() { }
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.EEW.Unity/TTSWorker.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/TTSWorker.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 76bc2768990085a4ca7f75326d524099
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Assets/Cryville.EEW.Unity/UI.meta
Normal file
8
Assets/Cryville.EEW.Unity/UI.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b32a77c17e1945a499006419dae05258
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
84
Assets/Cryville.EEW.Unity/UI/EventBaseView.cs
Normal file
84
Assets/Cryville.EEW.Unity/UI/EventBaseView.cs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
using Cryville.Common.Unity.UI;
|
||||||
|
using Cryville.EEW.Colors;
|
||||||
|
using Cryville.EEW.Report;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity.UI {
|
||||||
|
abstract class EventBaseView : MonoBehaviour {
|
||||||
|
[SerializeField] Image[] m_reportView;
|
||||||
|
|
||||||
|
[SerializeField] Image m_keyView;
|
||||||
|
[SerializeField] TMPLocalizedText m_keyTitleView;
|
||||||
|
[SerializeField] TMPLocalizedText m_keyValueView;
|
||||||
|
[SerializeField] TMPLocalizedText m_keyConditionView;
|
||||||
|
|
||||||
|
[SerializeField] TMPLocalizedText m_locationView;
|
||||||
|
[SerializeField] TMPLocalizedText m_predicateView;
|
||||||
|
[SerializeField] TMPLocalizedText m_timeView;
|
||||||
|
[SerializeField] EventPropertyListView m_propertyListView;
|
||||||
|
|
||||||
|
protected virtual bool UseShortTimeFormat => false;
|
||||||
|
void SetSeverity(float severity, out IColor color) {
|
||||||
|
color = SharedSettings.Instance.SeverityColorMapping.From(severity);
|
||||||
|
SetMainColor(color.ToSrgb().ToUnityColor());
|
||||||
|
}
|
||||||
|
protected void SetMainColor(Color color, Image image) {
|
||||||
|
image.color = color;
|
||||||
|
}
|
||||||
|
protected virtual void SetMainColor(Color color) {
|
||||||
|
foreach (var view in m_reportView)
|
||||||
|
SetMainColor(color, view);
|
||||||
|
m_keyView.color = color;
|
||||||
|
}
|
||||||
|
protected virtual void SetView(float mainSeverity, Localized<string> location, Localized<string> predicate, Localized<DateTime?> time, TimeZoneInfo timeZone, Localized<ReportViewModelProperty> keyProp, IEnumerable<Localized<ReportViewModelProperty>> props) {
|
||||||
|
SetSeverity(mainSeverity, out var mainColor);
|
||||||
|
SetText(m_locationView, location.Value, location.Culture);
|
||||||
|
SetText(m_predicateView, predicate.Value, predicate.Culture);
|
||||||
|
if (time.Value is DateTime ttime) {
|
||||||
|
// TODO
|
||||||
|
//if (SharedSettings.Instance.OverrideTimeZone is TimeZoneInfo tTimeZone)
|
||||||
|
// ttime = TimeZoneInfo.ConvertTime(ttime, timeZone, tTimeZone);
|
||||||
|
//else
|
||||||
|
// tTimeZone = timeZone;
|
||||||
|
TimeZoneInfo tTimeZone = timeZone;
|
||||||
|
if (UseShortTimeFormat) {
|
||||||
|
SetText(m_timeView, ttime.ToString("G", time.Culture), time.Culture);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// TODO SetText(m_timeView, SharedSettings.Instance.DoDisplayTimeZone ? string.Format(time.Culture, "{0:G} ({1})", ttime, tTimeZone.ToTimeZoneString()) : ttime.ToString(time.Culture), time.Culture);
|
||||||
|
SetText(m_timeView, string.Format(time.Culture, "{0:G} ({1})", ttime, tTimeZone.ToTimeZoneString()), time.Culture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
SetText(m_timeView, null, time.Culture);
|
||||||
|
}
|
||||||
|
if (keyProp.Value != null) {
|
||||||
|
m_keyView.gameObject.SetActive(true);
|
||||||
|
Color color = SharedSettings.Instance.TextColorScheme.From("Severity", mainSeverity, mainColor).ToSrgb().ToUnityColor();
|
||||||
|
m_keyTitleView.Text.color = color;
|
||||||
|
m_keyValueView.Text.color = color;
|
||||||
|
m_keyConditionView.Text.color = color;
|
||||||
|
SetText(m_keyTitleView, keyProp.Value.Key, keyProp.Culture);
|
||||||
|
SetText(m_keyValueView, keyProp.Value.Value, keyProp.Culture);
|
||||||
|
SetText(m_keyConditionView, keyProp.Value.Condition, keyProp.Culture);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_keyView.gameObject.SetActive(false);
|
||||||
|
}
|
||||||
|
m_propertyListView.Set(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void SetText(TMPLocalizedText view, string text, CultureInfo culture) {
|
||||||
|
if (string.IsNullOrWhiteSpace(text)) {
|
||||||
|
view.gameObject.SetActive(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
view.gameObject.SetActive(true);
|
||||||
|
view.SetText(text, culture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.EEW.Unity/UI/EventBaseView.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/UI/EventBaseView.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1ca3ce085c716d6449523bae22295880
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
35
Assets/Cryville.EEW.Unity/UI/EventGroupListView.cs
Normal file
35
Assets/Cryville.EEW.Unity/UI/EventGroupListView.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using Cryville.EEW.Core;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity.UI {
|
||||||
|
class EventGroupListView : MonoBehaviour {
|
||||||
|
[SerializeField]
|
||||||
|
EventGroupView m_prefabEventGroupView;
|
||||||
|
|
||||||
|
readonly List<ReportGroup> _groups = new();
|
||||||
|
public void UpdateGroup(ReportGroup e) {
|
||||||
|
Remove(e);
|
||||||
|
Add(e);
|
||||||
|
}
|
||||||
|
public void RemoveGroup(ReportGroup e) {
|
||||||
|
Remove(e);
|
||||||
|
}
|
||||||
|
void Add(ReportGroup group) {
|
||||||
|
_groups.Add(group);
|
||||||
|
var child = Instantiate(m_prefabEventGroupView);
|
||||||
|
child.Set(group);
|
||||||
|
child.transform.SetParent(transform, false);
|
||||||
|
child.transform.SetSiblingIndex(0);
|
||||||
|
}
|
||||||
|
void Remove(ReportGroup group) {
|
||||||
|
int index = _groups.LastIndexOf(group);
|
||||||
|
if (index == -1) return;
|
||||||
|
_groups.RemoveAt(index);
|
||||||
|
|
||||||
|
var child = transform.GetChild(_groups.Count - index);
|
||||||
|
child.SetParent(null, false);
|
||||||
|
Destroy(child.gameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.EEW.Unity/UI/EventGroupListView.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/UI/EventGroupListView.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2426bd244f51fed429d955beee52e91d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
77
Assets/Cryville.EEW.Unity/UI/EventGroupView.cs
Normal file
77
Assets/Cryville.EEW.Unity/UI/EventGroupView.cs
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
using Cryville.EEW.Core;
|
||||||
|
using Cryville.EEW.Report;
|
||||||
|
using System.Linq;
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
namespace Cryville.EEW.Unity.UI {
|
||||||
|
class EventGroupView : EventBaseView {
|
||||||
|
[SerializeField] EventUnitListView m_listView;
|
||||||
|
|
||||||
|
[SerializeField] Button m_groupHeader;
|
||||||
|
[SerializeField] GameObject m_listViewContainer;
|
||||||
|
[SerializeField] GameObject m_listViewRail;
|
||||||
|
[SerializeField] GameObject m_expander;
|
||||||
|
|
||||||
|
void Start() {
|
||||||
|
m_groupHeader.onClick.AddListener(OnGroupHeaderClicked);
|
||||||
|
}
|
||||||
|
void OnGroupHeaderClicked() {
|
||||||
|
SetExpanded(!m_listViewContainer.gameObject.activeSelf);
|
||||||
|
}
|
||||||
|
void SetExpanded(bool expanded) {
|
||||||
|
m_listViewContainer.gameObject.SetActive(expanded);
|
||||||
|
m_expander.SetActive(!expanded);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Set(ReportGroup group) {
|
||||||
|
int reportCount;
|
||||||
|
ReportViewModel[] latestValidReports;
|
||||||
|
DateTime latestActiveTime;
|
||||||
|
lock (group) {
|
||||||
|
m_listView.Set(group);
|
||||||
|
// TODO _expanderContainer.Activated = false;
|
||||||
|
|
||||||
|
reportCount = group.Count;
|
||||||
|
latestValidReports = group.Where(u => !u.IsCanceled).Select(u => u.LatestReport).Reverse().ToArray();
|
||||||
|
latestActiveTime = group.Max(u => u.LatestActiveTime);
|
||||||
|
}
|
||||||
|
var mainLocationReport = latestValidReports.OrderByDescending(e => e.LocationSpecificity).FirstOrDefault();
|
||||||
|
var mainTimeReport = mainLocationReport?.Time != null ? mainLocationReport : latestValidReports.LastOrDefault(e => e.Time != null);
|
||||||
|
var props = latestValidReports
|
||||||
|
.SelectMany(e => e.Properties.Select(p => new Localized<ReportViewModelProperty>(p, e.Culture)))
|
||||||
|
.GroupBy(p => p.Value.Type)
|
||||||
|
.Select(g => g.OrderBy(p => p.Value, DefaultRVMPropertyInnerComparer.Instance).First())
|
||||||
|
.ToArray();
|
||||||
|
var accuracyOrderThreshold = props.Length == 0 ? int.MaxValue : props.Min(p => p.Value.AccuracyOrder) switch {
|
||||||
|
< 40 => 40,
|
||||||
|
_ => int.MaxValue,
|
||||||
|
};
|
||||||
|
props = props
|
||||||
|
.OrderBy(p => p.Value, DefaultRVMPropertyOuterComparer.Instance)
|
||||||
|
.Where(p => p.Value.AccuracyOrder < accuracyOrderThreshold && DefaultRVMPropertyOuterComparer.Instance.ShouldDisplayInGroup(p.Value))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
SetView(
|
||||||
|
props.FirstOrDefault().Value?.Severity ?? -1,
|
||||||
|
new Localized<string>(mainLocationReport?.Location, mainLocationReport?.Culture),
|
||||||
|
new Localized<string>(mainLocationReport?.Predicate, mainLocationReport?.Culture),
|
||||||
|
new Localized<DateTime?>(mainTimeReport?.Time, mainTimeReport?.Culture), mainTimeReport?.TimeZone,
|
||||||
|
props.FirstOrDefault(),
|
||||||
|
props.Skip(1)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (mainLocationReport?.Location == null || reportCount <= 1) {
|
||||||
|
SetExpanded(true);
|
||||||
|
m_listViewRail.SetActive(false);
|
||||||
|
m_groupHeader.gameObject.SetActive(false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
SetExpanded(DateTime.UtcNow - latestActiveTime < TimeSpan.FromMinutes(5));
|
||||||
|
m_listViewRail.SetActive(true);
|
||||||
|
m_groupHeader.gameObject.SetActive(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Cryville.EEW.Unity/UI/EventGroupView.cs.meta
Normal file
11
Assets/Cryville.EEW.Unity/UI/EventGroupView.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2e6468ba458d7f44c84b64a0a12dd06f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user