using System; using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; namespace Treemap { public class Utility { public static Rect[] GetTreemapRects(float[] values, Rect targetRect) { if (values.Length == 0) throw new ArgumentException ("You need to at least pass in one valid value", "values"); Rect[] result = new Rect[values.Length]; float totalInputArea = 0f; for (int i = 0; i < values.Length; i++) totalInputArea += values[i]; float totalOutputArea = targetRect.width * targetRect.height; bool vertical = targetRect.width > targetRect.height; var unfinishedRects = new List(); for (int index = 0; index < values.Length; index++) { bool lastItem = index == values.Length - 1; float currentInputValue = values[index]; if (currentInputValue <= 0f) throw new ArgumentException ("only positive float values are supported. found: " + currentInputValue); float currentOutputArea = currentInputValue * totalOutputArea / totalInputArea; unfinishedRects = AddRect(unfinishedRects, currentOutputArea, targetRect, vertical); float currentAspect = GetAverageAspect(unfinishedRects); float nextInputValue = lastItem ? 0f : values[index + 1]; float nextOutputArea = nextInputValue * totalOutputArea / totalInputArea; float nextAspect = GetNextAspect(unfinishedRects, nextOutputArea, targetRect, vertical); if (Mathf.Abs(1f - currentAspect) < Mathf.Abs(1f - nextAspect) || lastItem) { int resultIndex = index - unfinishedRects.Count + 1; for (int rectIndex = 0; rectIndex < unfinishedRects.Count; rectIndex++) { result[resultIndex++] = unfinishedRects[rectIndex]; } targetRect = GetNewTarget(unfinishedRects, targetRect, vertical); vertical = !vertical; unfinishedRects.Clear(); } } return result; } private static List AddRect(List existing, float area, Rect space, bool vertical) { List result = new List(); if (vertical) { if (existing.Count == 0) { result.Add(new Rect(space.xMin, space.yMin, area / space.height, space.height)); } else { float totalSize = GetArea(existing) + area; float width = totalSize / space.height; float yPosition = space.yMin; foreach (Rect old in existing) { float itemArea = GetArea(old); result.Add(new Rect(old.xMin, yPosition, width, itemArea / width)); yPosition += itemArea / width; } result.Add(new Rect(space.xMin, yPosition, width, area / width)); } } else { if (existing.Count == 0) { result.Add(new Rect(space.xMin, space.yMin, space.width, area / space.width)); } else { float totalSize = GetArea(existing) + area; float height = totalSize / space.width; float xPosition = space.xMin; foreach (Rect old in existing) { float itemArea = GetArea(old); result.Add(new Rect(xPosition, old.yMin, itemArea / height, height)); xPosition += itemArea / height; } result.Add(new Rect(xPosition, space.yMin, area / height, height)); } } return result; } private static Rect GetNewTarget(List unfinished, Rect oldTarget, bool vertical) { if (vertical) { return new Rect(oldTarget.xMin + unfinished[0].width, oldTarget.yMin, oldTarget.width - unfinished[0].width, oldTarget.height); } else { return new Rect(oldTarget.xMin, oldTarget.yMin + unfinished[0].height, oldTarget.width, oldTarget.height - unfinished[0].height); } } private static float GetNextAspect(List existing, float area, Rect space, bool vertical) { List newExisting = AddRect(existing, area, space, vertical); return newExisting[newExisting.Count - 1].height / newExisting[newExisting.Count - 1].width; } private static float GetAverageAspect(List rects) { float aspect = 0f; foreach (Rect r in rects) { aspect += r.height / r.width; } return aspect / rects.Count; } private static float GetArea(Rect rect) { return rect.width * rect.height; } private static float GetArea(List rects) { return rects.Sum(x => GetArea(x)); } public static Color GetColorForName(string name) { int r = 0, g = 0, b = 0; for (int i = 0; i < name.Length; i++) { if (i % 3 == 0) { r += (int)name[i]; } else if (i % 3 == 1) { g += (int)name[i]; } else { b += (int)name[i]; } } r %= 128; g %= 128; b %= 128; return new Color32((byte)(r + 96), (byte)(g + 96), (byte)(b + 96), 255); } public static bool IsInside(Rect lhs, Rect rhs) { return lhs.xMax > rhs.xMin && lhs.xMin < rhs.xMax && lhs.yMax > rhs.yMin && lhs.yMin < rhs.yMax; } } }