//$ Copyright 2015-22, Code Respawn Technologies Pvt Ltd - All Rights Reserved $//
using System;
using UnityEngine;
namespace DungeonArchitect.Graphs
{
///
/// A camera that manages the graph editor's viewport
///
[Serializable]
public class GraphCamera
{
float maxAllowedZoom = 6.0f;
public float MaxAllowedZoom
{
get { return maxAllowedZoom; }
set { maxAllowedZoom = value; }
}
[SerializeField]
Vector2 position = Vector2.zero;
///
/// Position of the camera
///
public Vector2 Position
{
get
{
return position;
}
set
{
position = value;
}
}
public Vector2 ScreenOffset = Vector2.zero;
[SerializeField]
float zoomLevel = 1;
///
/// Zoom scale of the graph camera
///
public float ZoomLevel
{
get
{
return zoomLevel;
}
set
{
zoomLevel = value;
}
}
///
/// Pan the camera along the specified delta value
///
/// Delta value to move along the X value
/// Delta value to move along the Y value
public void Pan(int x, int y)
{
Pan(new Vector2(x, y));
}
///
/// Pan the camera along the specified delta value
///
/// The delta offset to move the camera to
public void Pan(Vector2 delta)
{
position += delta * zoomLevel;
}
///
/// Handles the user mouse and keyboard input
///
///
public void HandleInput(Event e)
{
// Handle zooming
if (e.type == EventType.ScrollWheel) {
// Grab the original position under the mouse so we can restore it after the zoom
var originalGraphPosition = ScreenToWorld(e.mousePosition);
float zoomMultiplier = 0.1f;
zoomMultiplier *= Mathf.Sign(e.delta.y);
zoomLevel = Mathf.Clamp(zoomLevel * (1 + zoomMultiplier), 1, maxAllowedZoom);
var newGraphPosition = ScreenToWorld (e.mousePosition);
position += originalGraphPosition - newGraphPosition;
}
// Handle pan
int dragButton1 = 1;
int dragButton2 = 2;
if (e.type == EventType.MouseDrag && (e.button == dragButton1 || e.button == dragButton2))
{
if (e.delta.magnitude < 150)
{
Pan(-e.delta);
}
}
}
///
/// Converts world coordinates (in the graph view) into Screen coordinates (relative to the editor window)
///
/// The world cooridnates of the graph view
/// The screen cooridnates relative to the editor window
public Vector2 WorldToScreen(Vector2 worldCoord)
{
return (worldCoord - position) / zoomLevel + ScreenOffset;
}
///
/// Converts the Screen coordinates (of the editor window) into the graph's world coordinate
///
///
/// The world coordinates in the graph view
public Vector2 ScreenToWorld(Vector2 screenCoord)
{
screenCoord -= ScreenOffset;
return screenCoord * zoomLevel + position;
}
///
/// Converts world coordinates (in the graph view) into Screen coordinates (relative to the editor window)
///
/// The world cooridnates of the graph view
/// The screen cooridnates relative to the editor window
public Rect WorldToScreen(Rect worldCoord)
{
var screen = worldCoord;
screen.position = WorldToScreen(worldCoord.position);
screen.size = worldCoord.size / zoomLevel;
return screen;
}
///
/// Converts the Screen coordinates (of the editor window) into the graph's world coordinate
///
///
/// The world coordinates in the graph view
public Rect ScreenToWorld(Rect screenCoord)
{
var world = screenCoord;
world.position = ScreenToWorld(screenCoord.position);
world.size = screenCoord.size * ZoomLevel;
return world;
}
///
/// Moves the camera so most of the nodes are visible
///
/// The graph to query
/// The bounds of the editor window
public void FocusOnBestFit(Graph graph, Rect editorBounds)
{
if (graph == null) return;
if (graph.Nodes.Count > 0)
{
var graphVisibleSize = editorBounds.size * zoomLevel;
Vector2 average = Vector2.zero;
foreach (var node in graph.Nodes)
{
if (node == null) continue;
average += node.Bounds.center;
}
average /= graph.Nodes.Count;
position = average - graphVisibleSize / 2.0f;
}
else
{
position = Vector3.zero;
zoomLevel = 1.0f;
}
}
///
/// Moves the camera to the specified node
///
/// The node to focus on
/// The bounds of the editor window
public void FocusOnNode(GraphNode node, Rect editorBounds)
{
var graphVisibleSize = editorBounds.size * zoomLevel;
var nodePosition = node.Bounds.center;
position = nodePosition - graphVisibleSize / 2.0f;
}
}
}