Skip to content

Instantly share code, notes, and snippets.

@kankikuchi
Created November 2, 2024 02:35
Show Gist options
  • Save kankikuchi/a2726fdf7bfb132bc413d426983fc115 to your computer and use it in GitHub Desktop.
Save kankikuchi/a2726fdf7bfb132bc413d426983fc115 to your computer and use it in GitHub Desktop.
エディタ上でグラフを表示するクラス【Unity】【エディタ拡張】
// http://kan-kikuchi.hatenablog.com/entry/EditorGraphViewer
// Created by kan.kikuchi
#if UNITY_EDITOR
using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
/// <summary>
/// エディタ上でグラフを表示するクラス
/// 参考 https://qiita.com/nitto/items/8ad2027e1af2e0d7316c
/// </summary>
public static class EditorGraphViewer {
/// <summary>
/// グラフ表示
/// </summary>
public static void ShowGraph(
List<Vector2>values,
float width = 300, float height = 200, float padding = 10,
float xMin = 0, float xMax = 100,float yMin = 0, float yMax = 100,
float xStep = 1, float yStep = 1
) {
var settings = new AxisSettings(xRange: (xMin, xMax), yRange: (yMin, yMax), xStep:xStep, yStep:yStep);
var rect = GUILayoutUtility.GetRect(width, height).Padding(padding);
DrawBackDrop(rect, Colors.backdropColor, Colors.gridLineColor);
DrawGrid(rect, settings, Colors.gridLineColor);
DrawData(rect, settings, values.ToArray(), Colors.dataColor);
}
public static Rect Padding(this Rect rect, float padding) {
rect.position += new Vector2(padding, padding);
rect.size -= new Vector2(padding, padding) * 2f;
return rect;
}
/// <summary>
/// 点列を描画する
/// </summary>
private static void DrawData(Rect rect, AxisSettings settings, Vector2[] values, Color color) {
using (new GUI.ClipScope(rect)) // ※スコープ内のGUIメソッドは指定Rectを基準として処理される
using (new Handles.DrawingScope(color)) {
for (int i = 0; i < values.Length - 1; i++) {
Vector2 p1 = Convertor.ValueToGraphPoint(values[i], rect.size, settings);
Vector2 p2 = Convertor.ValueToGraphPoint(values[i + 1], rect.size, settings);
Handles.DrawLine(p1, p2);
}
foreach (var value in values) {
Vector2 p = Convertor.ValueToGraphPoint(value, rect.size, settings);
Handles.DrawSolidDisc(p, Vector3.forward, 2f);
}
}
}
private static void DrawGrid(Rect rect, AxisSettings settings, Color gridColor) {
using (new Handles.DrawingScope(gridColor)) {
var graphOrigin = rect.position;
// X軸
var xStart = Mathf.Ceil(settings.xRange.min / settings.xStep) * settings.xStep;
var xTicks = EnumerableUtil.LinspaceWithStep(xStart, settings.xRange.max, settings.xStep);
foreach (var x in xTicks) {
var value1 = new Vector2(x, settings.yRange.min);
var value2 = new Vector2(x, settings.yRange.max);
// GUI座標に変換して描画
var p1 = Convertor.ValueToGraphPoint(value1, rect.size, settings) + graphOrigin;
var p2 = Convertor.ValueToGraphPoint(value2, rect.size, settings) + graphOrigin;
Handles.DrawLine(p1, p2);
}
// Y軸
var yStart = Mathf.Ceil(settings.yRange.min / settings.yStep) * settings.yStep;
var yTicks = EnumerableUtil.LinspaceWithStep(yStart, settings.yRange.max, settings.yStep);
foreach (var y in yTicks) {
var value1 = new Vector2(settings.xRange.min, y);
var value2 = new Vector2(settings.xRange.max, y);
// GUI座標に変換して描画
var p1 = Convertor.ValueToGraphPoint(value1, rect.size, settings) + graphOrigin;
var p2 = Convertor.ValueToGraphPoint(value2, rect.size, settings) + graphOrigin;
Handles.DrawLine(p1, p2);
}
}
}
/// <summary>
/// 背景と枠線を描画する
/// </summary>
private static void DrawBackDrop(Rect rect, Color backdropColor, Color outlineColor) {
Handles.DrawSolidRectangleWithOutline(rect, backdropColor, outlineColor);
}
private static class Colors {
public static readonly Color backdropColor = new Color(0.8f, 0.8f, 0.8f, 0.1f);
public static readonly Color gridLineColor = new Color(0.9f, 0.9f, 0.9f, 0.5f);
public static readonly Color dataColor = Color.green;
}
private struct AxisSettings {
// グラフの値域
public (float min, float max) xRange;
public (float min, float max) yRange;
// グリッド幅
public float xStep;
public float yStep;
public AxisSettings((float min, float max) xRange, (float min, float max) yRange, float xStep, float yStep) {
this.xRange = xRange;
this.yRange = yRange;
this.xStep = xStep;
this.yStep = yStep;
}
}
private static class Convertor {
/// <summary>
/// グラフ範囲の座標に変換する
/// </summary>
public static Vector2 ValueToGraphPoint(Vector2 value, Vector2 size, AxisSettings settings) {
var xRange = settings.xRange;
var yRange = settings.yRange;
float x = Mathf.Lerp(0, size.x, (value.x - xRange.min) / (xRange.max - xRange.min));
float y = Mathf.Lerp(size.y, 0, (value.y - yRange.min) / (yRange.max - yRange.min));
return new Vector2(x, y);
}
}
private static class EnumerableUtil {
/// <summary>
/// ステップサイズで線形に配置されたデータを作成する
/// </summary>
public static IEnumerable<float> LinspaceWithStep(float start, float end, float step) {
if (step <= 0) throw new InvalidOperationException("Step must be positive and non-zero.");
int count = (int)((end - start) / step) + 1;
return Enumerable
.Range(0, count)
.Select(i => start + i * step)
.TakeWhile(value => value <= end);
}
}
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment