local n=game:GetService("\084\101\120\116\083\101\114\118\105\099\101")local W={}local Y={Name="\068\097\114\107";Background=Color3.fromRGB(35,35,40),LightBackground=Color3.fromRGB(45,45,50);Text=Color3.fromRGB(220,220,230)}local v=true local function g(n)local W=0 for Y=1,#n,1 do W=W+n:byte(Y)end local Y=Random.new(W)local g=Y:NextInteger(0,50)/50 return Color3.fromHSV(g,v and.63 or 1,v and.84 or.8)end function W.new(W)if not W then error("\077\117\115\116 \103\105\118\101 \103\114\097\112\104 \097 \102\114\097\109\101")end local v={Frame=W,Resolution=75}local N=false local m=v.Frame.ZIndex local j=Instance.new("\070\114\097\109\101")j.Name="\066\097\099\107\103\114\111\117\110\100"j.BackgroundColor3=Y.Background j.Size=UDim2.new(1,0,1,0)j.ZIndex=m+1 j.Parent=v.Frame local d=Instance.new("\070\114\097\109\101")d.Name="\077\097\114\107\101\114\066\097\099\107\103\114\111\117\110\100"d.Size=UDim2.new(.1,0,1,0)d.BackgroundColor3=Y.LightBackground d.BorderSizePixel=0 d.ZIndex=m+2 d.Parent=v.Frame local o=Instance.new("\070\114\097\109\101")o.Name="\077\097\114\107\101\114\115"o.Size=UDim2.new(.1,0,.85,0)o.Position=UDim2.new(0,0,.15,0)o.BackgroundTransparency=1 o.BorderSizePixel=0 o.ZIndex=m+2 o.Parent=v.Frame local c=Instance.new("\070\114\097\109\101")c.Name="\071\114\097\112\104\105\110\103\070\114\097\109\101"c.Size=UDim2.new(.9,0,.85,0)c.Position=UDim2.new(.1,0,.15,0)c.BackgroundTransparency=1 c.ZIndex=m+2 c.Parent=v.Frame local Z=Instance.new("\070\114\097\109\101")Z.Name="\075\101\121\078\097\109\101\115"Z.Size=UDim2.new(1,0,.1,0)Z.Position=UDim2.new(0,0,0,0)Z.BackgroundColor3=Y.LightBackground Z.BorderSizePixel=0 Z.ZIndex=m+2 Z.Parent=v.Frame;(v.Frame:GetPropertyChangedSignal("\065\098\115\111\108\117\116\101\083\105\122\101")):Connect(function()local n=v.Frame.AbsoluteSize wait(.04)if n==v.Frame.AbsoluteSize then v.Render()end end)function v.Theme(n)wait()Y={Name=n.Name or"\068\097\114\107",Background=n.Background or Color3.fromRGB(46,46,46);LightBackground=n.LightBackground or Color3.fromRGB(70,70,70),Text=n.Text or Color3.fromRGB(220,220,230)}j.BackgroundColor3=Y.Background d.BackgroundColor3=Y.LightBackground Z.BackgroundColor3=Y.LightBackground v.Render()end function v.Render()if not v.Frame or not v.Data or not v.Resolution then return end while N do wait(.1)end N=true o:ClearAllChildren()c:ClearAllChildren()Z:ClearAllChildren()m=v.Frame.ZIndex j.ZIndex=m+1 d.ZIndex=m+2 o.ZIndex=m+2 c.ZIndex=m+2 Z.ZIndex=m+2 local W=Instance.new("\085\073\076\105\115\116\076\097\121\111\117\116")W.FillDirection=Enum.FillDirection.Horizontal W.HorizontalAlignment=Enum.HorizontalAlignment.Left W.VerticalAlignment=Enum.VerticalAlignment.Center W.Padding=UDim.new(.01,0)W.Parent=Z local y,T=-math.huge,math.huge local k for n,W in pairs(v.Data)do local Y=#W for n=1,Y,math.ceil(Y/v.Resolution)do local g={}for n=n,n+math.ceil(Y/v.Resolution),1 do g[#g+1]=W[n]end table.sort(g)local N=g[math.round(#g*.55)]if not N then continue end T=math.min(T,N)y=math.max(y,N)end end if v.BaselineZero then T=0 y=y*1.75 end k=y-T for n=0,1,.2 do local W=Instance.new("\084\101\120\116\076\097\098\101\108")W.Name=n W.Size=UDim2.new(1,0,.08,0)W.AnchorPoint=Vector2.new(0,.5)W.Position=UDim2.new(0,0,.9-n*.9,0)W.Text=string.format("\037\046\050\102  ",T+k*n)W.TextXAlignment=Enum.TextXAlignment.Right W.TextColor3=Y.Text W.Font=Enum.Font.SourceSans W.BackgroundTransparency=1 W.TextSize=v.Frame.AbsoluteSize.X*.03 W.ZIndex=m+3 W.Parent=o end local t={}for W,N in pairs(v.Data)do t[W]=g(W)local j=v.Frame.AbsoluteSize.Y*.08 local d=n:GetTextSize(W,j,Enum.Font.SourceSansSemibold,Z.AbsoluteSize)local o=Instance.new("\084\101\120\116\076\097\098\101\108")o.Text=W o.TextColor3=t[W]o.Font=Enum.Font.SourceSansSemibold o.BackgroundTransparency=1 o.TextSize=j o.Size=UDim2.new(0,d.X+j,1,0)o.ZIndex=m+3 o.Parent=Z local y=#N local a for n=1,y,math.ceil(y/v.Resolution)do local g={}for n=n,n+math.ceil(y/v.Resolution),1 do g[#g+1]=N[n]end table.sort(g)local j=g[math.round(#g*.55)]if not j then continue end local d=Instance.new("\073\109\097\103\101\076\097\098\101\108")d.Name=W..n d.Position=UDim2.new(.05+(n/y)*.9,0,k==0 and.9 or.9-((j-T)/k)*.9,0)d.AnchorPoint=Vector2.new(.5,.5)d.SizeConstraint=Enum.SizeConstraint.RelativeXX d.Size=UDim2.new(math.clamp(.5/v.Resolution,.003,.016),0,math.clamp(.5/v.Resolution,.003,.016),0)d.ImageColor3=t[W]d.BorderSizePixel=0 d.BackgroundTransparency=1 d.Image="\114\098\120\097\115\115\101\116\105\100\058\047\047\050\048\048\049\056\050\056\052\055"d.ZIndex=m+5 local o=Instance.new("\084\101\120\116\076\097\098\101\108")o.Visible=false o.Text=string.format("\037\046\053\102",j)o.BackgroundColor3=Y.LightBackground o.TextColor3=Y.Text o.Position=UDim2.new(1,0,.4,0)o.Font=Enum.Font.Code o.TextSize=v.Frame.AbsoluteSize.X*.025 o.Size=UDim2.new(0,(o.TextSize*.6)*#o.Text,0,o.TextSize*1.1)o.Parent=d o.ZIndex=m+10 d.MouseEnter:Connect(function()o.Visible=true end)d.MouseLeave:Connect(function()o.Visible=false end)if a then local Y=Instance.new("\070\114\097\109\101")Y.Name=W..(n..("-"..n-1))Y.BackgroundColor3=t[W]Y.BorderSizePixel=0 Y.SizeConstraint=Enum.SizeConstraint.RelativeXX Y.AnchorPoint=Vector2.new(.5,.5)Y.ZIndex=m+4 local g=c.AbsoluteSize local N,j=d.Position.X.Scale*g.X,d.Position.Y.Scale*g.Y local o,Z=a.Position.X.Scale*g.X,a.Position.Y.Scale*g.Y local y=(Vector2.new(N,j)-Vector2.new(o,Z)).Magnitude Y.Size=UDim2.new(0,y,math.clamp(.2/v.Resolution,.002,.0035),0)Y.Position=UDim2.new(0,(N+o)/2,0,(j+Z)/2)Y.Rotation=math.atan2(Z-j,o-N)*(180/math.pi)Y.Parent=c end a=d d.Parent=c end end N=false end return setmetatable({},{__index=function(n,W)return v[W]end,__newindex=function(n,W,Y)if W=="\068\097\116\097"and type(Y)=="\116\097\098\108\101"then v.Data=Y v.Render()elseif W=="\082\101\115\111\108\117\116\105\111\110"and type(Y)=="\110\117\109\098\101\114"then v.Resolution=math.clamp(Y,3,500)v.Render()elseif W=="\066\097\115\101\108\105\110\101\090\101\114\111"and type(Y)=="\098\111\111\108\101\097\110"then v.BaselineZero=Y v.Render()end end})end return W
--[=[

Docs: https://devforum.roblox.com/t/graph-module-easily-draw-graphs-of-your-data/828982/1

API:

function Graph.new(Frame)
	returns a GraphHandler
	
GraphHandler.Resolution = The number of points it renders
GraphHandler.BaselineZero = Whether the bottom of the graph should start at zero (or at the minimum value)
GraphHandler.Data = The dictionary of data sets
	(Data must be a dictionary of arrays with no holes)
	
function GraphHandler.Theme(ThemeDictionary)
	Updates the Colors of the graph

--]=]

local TextService = game:GetService("TextService")

local Graph = {}

local Theme = {
	Name = "Dark";
	
	Background = Color3.fromRGB(35,35,40);
	LightBackground = Color3.fromRGB(45,45,50);
	Text = Color3.fromRGB(220,220,230)
}
local isDark = true

local function getKeyColor(name)
	-- Shoutout to Vocksel for the core of this function
	
	local seed = 0
	for i=1, #name do
		seed = seed + (name:byte(i))
	end
	local rng = Random.new(seed)
	local hue = rng:NextInteger(0,50)/50

	return Color3.fromHSV(hue, isDark and 0.63 or 1, isDark and 0.84 or 0.8)
end


function Graph.new(Frame)
	if not Frame then error("Must give graph a frame") end
	local GraphHandler = {Frame = Frame; Resolution = 75;}
	
	-- Private variables
	local Busy = false
	
	-- Create the GUIs
	local BaseZIndex = GraphHandler.Frame.ZIndex

	local Background = Instance.new("Frame")
	Background.Name = "Background"
	Background.BackgroundColor3 = Theme.Background
	Background.Size = UDim2.new(1,0,1,0)
	Background.ZIndex = BaseZIndex+1
	Background.Parent = GraphHandler.Frame

	local MarkerBG = Instance.new("Frame")
	MarkerBG.Name = "MarkerBackground"
	MarkerBG.Size = UDim2.new(0.1,0,1,0)
	MarkerBG.BackgroundColor3 = Theme.LightBackground
	MarkerBG.BorderSizePixel = 0
	MarkerBG.ZIndex = BaseZIndex+2
	MarkerBG.Parent = GraphHandler.Frame

	local YMarkers = Instance.new("Frame")
	YMarkers.Name = "Markers"
	YMarkers.Size = UDim2.new(0.1,0,0.85,0)
	YMarkers.Position = UDim2.new(0,0,0.15,0)
	YMarkers.BackgroundTransparency = 1
	YMarkers.BorderSizePixel = 0
	YMarkers.ZIndex = BaseZIndex+2
	YMarkers.Parent = GraphHandler.Frame

	local GraphingFrame = Instance.new("Frame")
	GraphingFrame.Name = "GraphingFrame"
	GraphingFrame.Size = UDim2.new(0.9,0,0.85,0)
	GraphingFrame.Position = UDim2.new(0.1,0,0.15,0)
	GraphingFrame.BackgroundTransparency = 1
	GraphingFrame.ZIndex = BaseZIndex+2
	GraphingFrame.Parent = GraphHandler.Frame
	
	local KeyNames = Instance.new("Frame")
	KeyNames.Name = "KeyNames"
	KeyNames.Size = UDim2.new(1,0,0.1,0)
	KeyNames.Position = UDim2.new(0,0,0,0)
	KeyNames.BackgroundColor3 = Theme.LightBackground
	KeyNames.BorderSizePixel = 0
	KeyNames.ZIndex = BaseZIndex+2
	KeyNames.Parent = GraphHandler.Frame
	
	-- Rerender if the frame changes size since our lines will be all wonky
	GraphHandler.Frame:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
		local Size = GraphHandler.Frame.AbsoluteSize
		wait(0.04)
		if Size == GraphHandler.Frame.AbsoluteSize then
			GraphHandler.Render()
		end
	end)
	
	function GraphHandler.Theme(newTheme)
		wait()
		-- Make sure we have latest theme data
		Theme = {
			Name = newTheme.Name or "Dark";

			Background = newTheme.Background or Color3.fromRGB(46,46,46);
			LightBackground = newTheme.LightBackground or Color3.fromRGB(70,70,70);
			Text = newTheme.Text or Color3.fromRGB(220,220,230);
		}
		
		-- Update GUIs
		Background.BackgroundColor3 = Theme.Background
		MarkerBG.BackgroundColor3 = Theme.LightBackground
		KeyNames.BackgroundColor3 = Theme.LightBackground
		
		-- Redraw graph with new colors
		GraphHandler.Render()
	end
	
	function GraphHandler.Render()
		-- Validate we have stuff to render
		if not GraphHandler.Frame or not GraphHandler.Data or not GraphHandler.Resolution then
			return
		end
		
		while Busy do wait(0.1) end
		Busy = true
		
		-- Clear old graph values
		YMarkers:ClearAllChildren()
		GraphingFrame:ClearAllChildren()
		KeyNames:ClearAllChildren()
		
		BaseZIndex = GraphHandler.Frame.ZIndex
		
		Background.ZIndex = BaseZIndex+1
		MarkerBG.ZIndex = BaseZIndex+2
		YMarkers.ZIndex = BaseZIndex+2
		GraphingFrame.ZIndex = BaseZIndex+2
		KeyNames.ZIndex = BaseZIndex+2
		
		local KeyLayout = Instance.new("UIListLayout")
		KeyLayout.FillDirection = Enum.FillDirection.Horizontal
		KeyLayout.HorizontalAlignment = Enum.HorizontalAlignment.Left
		KeyLayout.VerticalAlignment = Enum.VerticalAlignment.Center
		KeyLayout.Padding = UDim.new(0.01,0)
		KeyLayout.Parent = KeyNames
		
		local Max, Min = -math.huge,math.huge
		local Range
		
		-- Calculate our range of values

		for Key, Set in pairs(GraphHandler.Data) do
			local SetAmount = #Set

			for i=1,SetAmount, math.ceil(SetAmount/GraphHandler.Resolution) do
				local SortedChunk = {}
				for x=i,i+math.ceil(SetAmount/GraphHandler.Resolution) do
					SortedChunk[#SortedChunk+1] = Set[x]
				end
				table.sort(SortedChunk)

				local Value = SortedChunk[math.round(#SortedChunk*0.55)]
				if not Value then continue end

				-- Record for our range calc
				Min = math.min(Min, Value)
				Max = math.max(Max, Value)
			end
		end
		
		if GraphHandler.BaselineZero then
			Min = 0
			Max = Max * 1.75
		end

		Range = Max-Min
		
		-- Mark our Y axis values along the derived range
		
		for y=0,1,0.2 do
			local Marker = Instance.new("TextLabel")
			Marker.Name = y
			Marker.Size = UDim2.new(1,0,0.08,0)
			Marker.AnchorPoint = Vector2.new(0,0.5)
			Marker.Position = UDim2.new(0,0,0.9 - (y*0.9),0)
			Marker.Text = string.format("%.2f  ",(Min + (Range*y)))
			Marker.TextXAlignment = Enum.TextXAlignment.Right

			Marker.TextColor3 = Theme.Text
			Marker.Font = Enum.Font.SourceSans
			Marker.BackgroundTransparency = 1
			Marker.TextSize = (GraphHandler.Frame.AbsoluteSize.X*0.03)
			Marker.ZIndex = BaseZIndex+3
			Marker.Parent = YMarkers
		end
		
		-- Draw the graph at this range
		local KeyColors = {}
		for Key, Set in pairs(GraphHandler.Data) do
			-- Designate a color for this dataset
			KeyColors[Key] = getKeyColor(Key)
			
			local TextSize = GraphHandler.Frame.AbsoluteSize.Y*0.08
			local Size = TextService:GetTextSize(Key, TextSize, Enum.Font.SourceSansSemibold, KeyNames.AbsoluteSize)
			local KeyMarker = Instance.new("TextLabel")
			KeyMarker.Text = Key
			KeyMarker.TextColor3 = KeyColors[Key]
			KeyMarker.Font = Enum.Font.SourceSansSemibold
			KeyMarker.BackgroundTransparency = 1
			KeyMarker.TextSize = TextSize
			KeyMarker.Size = UDim2.new(0,Size.X+TextSize,1,0)
			KeyMarker.ZIndex = BaseZIndex+3
			KeyMarker.Parent = KeyNames
			
			-- Graph the set

			local SetAmount = #Set
			local LastPoint

			--print("  "..Key, Set)

			for i=1,SetAmount, math.ceil(SetAmount/GraphHandler.Resolution) do

				local SortedChunk = {}
				for x=i,i+math.ceil(SetAmount/GraphHandler.Resolution) do
					SortedChunk[#SortedChunk+1] = Set[x]
				end
				table.sort(SortedChunk)

				local Value = SortedChunk[math.round(#SortedChunk*0.55)]
				if not Value then continue end

				-- Create the point
				local Point = Instance.new("ImageLabel")
				Point.Name = Key..i
				Point.Position = UDim2.new(0.05+((i/SetAmount)*0.9),0, Range == 0 and 0.9 or 0.9 - (((Value-Min)/Range)*0.9),0)
				Point.AnchorPoint = Vector2.new(0.5,0.5)
				Point.SizeConstraint = Enum.SizeConstraint.RelativeXX
				Point.Size = UDim2.new(math.clamp(0.5/GraphHandler.Resolution, 0.003,0.016),0,math.clamp(0.5/GraphHandler.Resolution, 0.003,0.016),0)

				Point.ImageColor3 = KeyColors[Key]
				Point.BorderSizePixel = 0
				Point.BackgroundTransparency = 1
				Point.Image = "rbxassetid://200182847"
				Point.ZIndex = BaseZIndex+5

				local Label = Instance.new("TextLabel")
				Label.Visible = false
				Label.Text = string.format("%.5f",Value)
				Label.BackgroundColor3 = Theme.LightBackground
				Label.TextColor3 = Theme.Text
				Label.Position = UDim2.new(1,0,0.4,0)
				Label.Font = Enum.Font.Code
				Label.TextSize = (GraphHandler.Frame.AbsoluteSize.X*0.025)
				Label.Size = UDim2.new(0,Label.TextSize * 0.6 * #Label.Text,0,Label.TextSize * 1.1)
				Label.Parent = Point
				Label.ZIndex = BaseZIndex+10

				Point.MouseEnter:Connect(function()
					Label.Visible = true
				end)
				Point.MouseLeave:Connect(function()
					Label.Visible = false
				end)

				-- Create the line
				if LastPoint then
					local Connector = Instance.new("Frame")
					Connector.Name = Key..i.."-"..i-1
					Connector.BackgroundColor3 = KeyColors[Key]
					Connector.BorderSizePixel = 0
					Connector.SizeConstraint = Enum.SizeConstraint.RelativeXX
					Connector.AnchorPoint = Vector2.new(0.5, 0.5)
					Connector.ZIndex = BaseZIndex+4

					local Size = GraphingFrame.AbsoluteSize
					local startX, startY = Point.Position.X.Scale*Size.X, Point.Position.Y.Scale*Size.Y
					local endX, endY = LastPoint.Position.X.Scale*Size.X, LastPoint.Position.Y.Scale*Size.Y

					local Distance = (Vector2.new(startX, startY) - Vector2.new(endX, endY)).Magnitude

					Connector.Size = UDim2.new(0, Distance, math.clamp(0.2/GraphHandler.Resolution, 0.002,0.0035), 0)
					Connector.Position = UDim2.new(0, (startX + endX) / 2, 0, (startY + endY) / 2)
					Connector.Rotation = math.atan2(endY - startY, endX - startX) * (180 / math.pi)

					Connector.Parent = GraphingFrame
				end

				LastPoint = Point
				Point.Parent = GraphingFrame

			end

		end
		
		Busy = false
	end
	
	return setmetatable({}, {
		__index = function(t,Key)
			return GraphHandler[Key]
		end;
		__newindex = function(t, Key, Value)
			if Key == "Data" and type(Value) == "table" then
				GraphHandler.Data = Value
				GraphHandler.Render()
			elseif Key == "Resolution" and type(Value) == "number" then
				GraphHandler.Resolution = math.clamp(Value, 3, 500)
				GraphHandler.Render()
			elseif Key == "BaselineZero" and type(Value) == "boolean" then
				GraphHandler.BaselineZero = Value
				GraphHandler.Render()
			end
		end;
	})
	
end


return Graph
