feat: Initial commit

This commit is contained in:
2025-02-14 16:06:00 +08:00
commit da75a84e02
1056 changed files with 163517 additions and 0 deletions

View 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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c99e48f1a8befc34eb40305cdd762f9f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1360d2ab5159d7845b4e8f1df463d514
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: d21cf2141c549ce4193d48fa497a21bd
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 725cac357fd552542822cbf163b7affa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 96211bdddfa409a40a2e375d18b4e19d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 07c8433af4f94ee4b9fa3286a384ffe2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a2c2d746f0122b64a84ac5b921e0fadc
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 44a27900867d2ef44bb67cb466c445d9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 505a49fe5efac9247939cef8282923c4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d10b1601f6b7e8946bdf6c54585167af
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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++);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 92a1fd122e87e6c49b58d9d4c81b131c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e597c4b1800db0e47bb84271bcd9de7b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9028f83972f4b8648819141884011f70
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ccf7f796ffe73004a95fef5655177c3b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: af28bd1e6223c4e498888034b7b756ff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cccfefde2dac1e742a8c44924b678e12
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 00fc5cd64f19b374aba03a10b1fc3d43
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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));
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c59bd9617c7906543b3b23f07518b4e1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 073fe1bdd0645d64eb82329cb69e89fb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dc48b51fb50522e4785fd56204934984
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 772f71bcdaaa2504d9e9a469c3100593
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: db3aee38f4dfa62429bbe7025fd6c422
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3c4b9fd0d7969a94bae8539e2db32009
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cd4f627f0b43de845b20ffb1d668f14c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3914c07b14665e94aa3900b890884804
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c9a3ab42c572cf44b84b595592563f91
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ee62d4ae7a6870e49a0dfc662c3c2d41
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2a8a590d8e5b3e1468fe2a0f828625ff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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() { }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 76bc2768990085a4ca7f75326d524099
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b32a77c17e1945a499006419dae05258
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1ca3ce085c716d6449523bae22295880
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2426bd244f51fed429d955beee52e91d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2e6468ba458d7f44c84b64a0a12dd06f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,26 @@
using Cryville.EEW.Core;
using Cryville.EEW.Report;
using UnityEngine;
namespace Cryville.EEW.Unity.UI {
class EventListView : MonoBehaviour {
[SerializeField]
EventView m_prefabEventView;
public void Set(ReportUnit unit) {
foreach (Transform child in transform) {
child.SetParent(null, false);
Destroy(child.gameObject);
}
foreach (var e in unit) {
Add(e);
}
}
void Add(ReportViewModel e) {
var child = Instantiate(m_prefabEventView);
child.Set(e);
child.transform.SetParent(transform, false);
child.transform.SetSiblingIndex(0);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 11a3f253a932b1e4ebe0c7bdad12b3bf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,25 @@
using Cryville.EEW.Report;
using System.Collections.Generic;
using UnityEngine;
namespace Cryville.EEW.Unity.UI {
class EventPropertyListView : MonoBehaviour {
[SerializeField]
EventPropertyView m_prefabEventPropertyView;
public void Set(IEnumerable<Localized<ReportViewModelProperty>> props) {
foreach (Transform child in transform) {
child.SetParent(null, false);
Destroy(child.gameObject);
}
foreach (var prop in props) {
Add(prop);
}
}
void Add(Localized<ReportViewModelProperty> prop) {
var child = Instantiate(m_prefabEventPropertyView);
child.Set(prop);
child.transform.SetParent(transform, false);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 22019c55ede3c4245b1934a9a2d4a06b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,17 @@
using Cryville.Common.Unity.UI;
using Cryville.EEW.Report;
using UnityEngine;
namespace Cryville.EEW.Unity.UI {
class EventPropertyView : MonoBehaviour {
[SerializeField] TMPLocalizedText m_text;
public void Set(Localized<ReportViewModelProperty> prop) {
var culture = prop.Culture;
var p = prop.Value;
var s = string.IsNullOrWhiteSpace(p.Key) ? p.Value : string.Format(culture, "{0} {1}", p.Key, p.Value);
if (!string.IsNullOrWhiteSpace(p.Condition)) s = string.Format(culture, "{0} {1}", s, p.Condition);
m_text.SetText(s, culture);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d1b90ac529732a34e97c569ec13f5756
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,63 @@
using Cryville.Common.Unity.UI;
using Cryville.EEW.Core;
using Cryville.EEW.Report;
using System;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
namespace Cryville.EEW.Unity.UI {
class EventReportView : EventBaseView {
[SerializeField] Button m_reportViewButton;
[SerializeField] TMPLocalizedText m_titleView;
[SerializeField] GameObject m_revisionViewContainer;
[SerializeField] TMPLocalizedText m_revisionView;
ReportViewModel _viewModel;
protected virtual void Start() {
m_reportViewButton.onClick.AddListener(OnViewClicked);
}
void OnViewClicked() {
Worker.Instance.SetSelected(_viewModel);
}
public virtual void SetViewModel(ReportViewModel viewModel, bool hideRevision = false) {
_viewModel = viewModel;
if (m_titleView != null) {
var title = viewModel.Title ?? "";
if (!string.IsNullOrWhiteSpace(viewModel.Source))
title += " | " + viewModel.Source;
SetText(m_titleView, title, viewModel.Culture);
}
if (!hideRevision && viewModel.RevisionKey is IReportRevisionKey rev) {
using var lres = CoreMessages.Generic(viewModel.Culture);
var res = lres.RootMessageStringSet;
m_revisionViewContainer.SetActive(true);
if (rev.IsCancellation)
SetText(m_revisionView, res.GetString("SerialCancel") ?? "-", viewModel.Culture);
else
SetText(m_revisionView, string.Format(viewModel.Culture, res.GetString(rev.IsFinalRevision ? "SerialFinal" : "Serial") ?? "#{0}", rev.Serial), viewModel.Culture);
}
else {
m_revisionViewContainer.SetActive(false);
SetText(m_revisionView, null, viewModel.Culture);
}
var keyProp = viewModel.Properties.FirstOrDefault();
var props = viewModel.Properties.Skip(1);
SetView(
keyProp?.Severity ?? -1,
new Localized<string>(viewModel.Location, viewModel.Culture),
new Localized<string>(viewModel.Predicate, viewModel.Culture),
new Localized<DateTime?>(viewModel.Time, viewModel.Culture), viewModel.TimeZone,
new Localized<ReportViewModelProperty>(keyProp, viewModel.Culture),
props.Select(i => new Localized<ReportViewModelProperty>(i, viewModel.Culture))
);
}
protected static bool ShouldHideRevision(IReportRevisionKey key) {
return key == null || (key.Serial == null && !key.IsCancellation && !key.IsFinalRevision);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e740921cfbd27344a960c4245351e181
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,25 @@
using Cryville.EEW.Core;
using UnityEngine;
namespace Cryville.EEW.Unity.UI {
class EventUnitListView : MonoBehaviour {
[SerializeField]
EventUnitView m_prefabEventUnitView;
public void Set(ReportGroup group) {
foreach(Transform child in transform) {
child.SetParent(null, false);
Destroy(child.gameObject);
}
foreach(var unit in group) {
Add(unit);
}
}
void Add(ReportUnit unit) {
var child = Instantiate(m_prefabEventUnitView);
child.Set(unit);
child.transform.SetParent(transform, false);
child.transform.SetSiblingIndex(0);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 47104cf79f65bc9479d1ee4c994c7596
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,27 @@
using Cryville.EEW.Core;
using UnityEngine;
using UnityEngine.UI;
namespace Cryville.EEW.Unity.UI {
class EventUnitView : EventReportView {
[SerializeField] EventListView m_listView;
[SerializeField] Button m_revisionViewContainerButton;
protected override void Start() {
base.Start();
m_revisionViewContainerButton.onClick.AddListener(OnRevisionViewClicked);
}
void OnRevisionViewClicked() {
m_listView.gameObject.SetActive(!m_listView.gameObject.activeSelf);
}
public void Set(ReportUnit unit) {
lock (unit) {
SetViewModel(unit.LatestReport, ShouldHideRevision(unit.LatestReport.RevisionKey) && unit.Count <= 1);
m_listView.Set(unit);
}
m_listView.gameObject.SetActive(false);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 734ae71f23b2c1f4d9b38aa06a0de86c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
using Cryville.EEW.Report;
namespace Cryville.EEW.Unity.UI {
class EventView : EventReportView {
protected override bool UseShortTimeFormat => true;
public void Set(ReportViewModel e) {
SetViewModel(e);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9e5a2623e62bafc4e9f568053223b4ef
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Globalization;
namespace Cryville.EEW.Unity.UI {
struct Localized<T> : IEquatable<Localized<T>> {
public T Value { get; set; }
public CultureInfo Culture { get; set; }
public Localized(T value, CultureInfo culture) {
Value = value;
Culture = culture;
}
public override readonly bool Equals(object obj) => obj is Localized<T> localized && Equals(localized);
public readonly bool Equals(Localized<T> other) =>
EqualityComparer<T>.Default.Equals(Value, other.Value) &&
EqualityComparer<CultureInfo>.Default.Equals(Culture, other.Culture);
public override readonly int GetHashCode() => HashCode.Combine(Value, Culture);
public static bool operator ==(Localized<T> left, Localized<T> right) => left.Equals(right);
public static bool operator !=(Localized<T> left, Localized<T> right) => !(left == right);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c53b913c973fee44484efbbfa998f942
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,154 @@
using Cryville.EEW.Core;
using Cryville.EEW.CWAOpenData;
using Cryville.EEW.CWAOpenData.Model;
using Cryville.EEW.CWAOpenData.TTS;
using Cryville.EEW.GlobalQuake;
using Cryville.EEW.JMAAtom;
using Cryville.EEW.JMAAtom.TTS;
using Cryville.EEW.NOAA;
using Cryville.EEW.NOAA.TTS;
using Cryville.EEW.Report;
using Cryville.EEW.Unity.Map;
using Cryville.EEW.Unity.UI;
using Cryville.EEW.UpdateChecker;
using Cryville.EEW.Wolfx;
using Cryville.EEW.Wolfx.TTS;
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
namespace Cryville.EEW.Unity {
sealed class Worker : MonoBehaviour {
public static Worker Instance { get; private set; }
[SerializeField]
CameraController m_cameraController;
[SerializeField]
MapElementManager m_mapElementManager;
[SerializeField]
EventGroupListView m_historyEventGroupList;
GroupingCoreWorker _worker;
CancellationTokenSource _cancellationTokenSource;
void Awake() {
if (Instance != null) {
Destroy(this);
throw new InvalidOperationException("Duplicate worker.");
}
Instance = this;
App.Init();
_worker = new(new TTSWorker());
_cancellationTokenSource = new();
}
void Start() {
LocalizedResources.Init(new LocalizedResourcesManager());
RegisterViewModelGenerators(_worker);
RegisterTTSMessageGenerators(_worker);
BuildWorkers();
_worker.RVMGeneratorContext = SharedSettings.Instance;
_worker.TTSMessageGeneratorContext = SharedSettings.Instance;
_ongoingReportManager.Changed += OnOngoingReported;
_worker.Reported += OnReported;
_worker.GroupUpdated += OnGroupUpdated;
_worker.GroupRemoved += OnGroupRemoved;
Task.Run(() => _worker.RunAsync(_cancellationTokenSource.Token));
Task.Run(() => _ongoingReportManager.RunAsync(_cancellationTokenSource.Token));
}
void OnDestroy() {
_cancellationTokenSource.Cancel();
_worker.Dispose();
_ongoingReportManager.Dispose();
}
static void RegisterViewModelGenerators(CoreWorker worker) {
worker.RegisterViewModelGenerator(new CENCEarthquakeRVMGenerator());
worker.RegisterViewModelGenerator(new CENCEEWRVMGenerator());
worker.RegisterViewModelGenerator(new CWAEarthquakeRVMGenerator());
worker.RegisterViewModelGenerator(new CWAEEWRVMGenerator());
worker.RegisterViewModelGenerator(new CWATsunamiRVMGenerator());
worker.RegisterViewModelGenerator(new FujianEEWRVMGenerator());
worker.RegisterViewModelGenerator(new GlobalQuakeRVMGenerator());
worker.RegisterViewModelGenerator(new JMAAtomRVMGenerator());
worker.RegisterViewModelGenerator(new JMAEEWRVMGenerator());
worker.RegisterViewModelGenerator(new NOAAAtomRVMGenerator());
worker.RegisterViewModelGenerator(new SichuanEEWRVMGenerator());
worker.RegisterViewModelGenerator(new VersionRVMGenerator());
}
static void RegisterTTSMessageGenerators(CoreWorker worker) {
worker.RegisterTTSMessageGenerator(new CENCEarthquakeTTSMessageGenerator());
worker.RegisterTTSMessageGenerator(new CENCEEWTTSMessageGenerator());
worker.RegisterTTSMessageGenerator(new CWAEarthquakeTTSMessageGenerator());
worker.RegisterTTSMessageGenerator(new CWAEEWTTSMessageGenerator());
worker.RegisterTTSMessageGenerator(new CWATsunamiTTSMessageGenerator());
worker.RegisterTTSMessageGenerator(new FujianEEWTTSMessageGenerator());
worker.RegisterTTSMessageGenerator(new JMAAtomTTSMessageGenerator());
worker.RegisterTTSMessageGenerator(new JMAEEWTTSMessageGenerator());
worker.RegisterTTSMessageGenerator(new NOAATTSMessageGenerator());
worker.RegisterTTSMessageGenerator(new SichuanEEWTTSMessageGenerator());
}
WolfxWorker _wolfxWorker;
JMAAtomWorker _jmaWorker;
CWAReportWorker<Tsunami> _cwa14Worker;
CWAReportWorker<Earthquake> _cwa15Worker;
CWAReportWorker<Earthquake> _cwa16Worker;
GlobalQuakeWorker _gqWorker;
NOAAAtomWorker _ntwcWorker;
NOAAAtomWorker _ptwcWorker;
UpdateCheckerWorker _updateChecker;
void BuildWorkers() {
#if UNITY_EDITOR
_worker.AddWorker(_wolfxWorker = new WolfxWorker(new Uri("ws://localhost:9995/wolfx")));
_worker.AddWorker(_jmaWorker = new JMAAtomWorker(new Uri("http://localhost:9095/eqvol.xml")));
_worker.AddWorker(_cwa14Worker = new CWAReportWorker<Tsunami>(new Uri("http://localhost:9095/E-A0014-001.json"), "1"));
_worker.AddWorker(_cwa15Worker = new CWAReportWorker<Earthquake>(new Uri("http://localhost:9095/E-A0015-001.json"), "1"));
_worker.AddWorker(_cwa16Worker = new CWAReportWorker<Earthquake>(new Uri("http://localhost:9095/E-A0016-001.json"), "1"));
_worker.AddWorker(_ntwcWorker = new NOAAAtomWorker(new("http://localhost:9095/PAAQAtom.xml")));
// _worker.AddWorker(_gqWorker = new GlobalQuakeWorker("localhost", 38000));
#else
// TODO
#endif
if (_updateChecker == null) _worker.AddWorker(_updateChecker = new(typeof(Worker).Assembly.GetName().Version?.ToString(3) ?? "", "unity"));
}
readonly OngoingReportManager _ongoingReportManager = new();
readonly ConcurrentQueue<Action> _uiActionQueue = new();
void OnReported(object sender, ReportViewModel e) {
Debug.Log(e);
_ongoingReportManager.Report(e);
_uiActionQueue.Enqueue(() => m_mapElementManager.SetSelected(e));
}
void OnOngoingReported(ReportViewModel item, CollectionChangeAction action) {
if (action == CollectionChangeAction.Add) {
_uiActionQueue.Enqueue(() => m_mapElementManager.AddOngoing(item));
}
else if (action == CollectionChangeAction.Remove) {
_uiActionQueue.Enqueue(() => m_mapElementManager.RemoveOngoing(item));
}
}
void OnGroupUpdated(object sender, ReportGroup e) {
_uiActionQueue.Enqueue(() => m_historyEventGroupList.UpdateGroup(e));
}
void OnGroupRemoved(object sender, ReportGroup e) {
_uiActionQueue.Enqueue(() => m_historyEventGroupList.RemoveGroup(e));
}
public void SetSelected(ReportViewModel e) {
m_mapElementManager.SetSelected(e);
m_cameraController.OnMapElementUpdated();
}
void Update() {
while (_uiActionQueue.TryDequeue(out var action)) {
action();
m_cameraController.OnMapElementUpdated();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bb52721346df21a4a89269391fd1f9c3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: