
-----------------------------------------------------------
-- ANIMATION PANEL
-----------------------------------------------------------
local defaultOrientation = function() return quat(0,1,0,1) end
local testC,testCR
local function undoFunc    (panel,first)
		print("from after")

		local layerChange=0
		if panel.lastState.anims then 
	  	 layerChange = #panel.layers-#panel.lastState.anims[panel.lastState.anim].layers
	  end
	  if panel.lastState.action=="Stroke" then 
	  	if panel.lastFill then 
	  		panel.lastFill.count = 0
	  	end
	  	panel.proccedStrokes = false
	  end
	  panel.anims[panel.anim] = panel.lastState.animData and deepcopy(panel.lastState.animData) or panel.anims[panel.anim]

	  panel.selection = panel.lastState.selection

		panel.y 			= panel.y+panel.row*layerChange
		panel.height 		= panel.height-panel.row*layerChange
		panel.savedHeight 	= panel.savedHeight-panel.row*layerChange

		panel.region  	= panel.lastState.region and deepcopy(panel.lastState.region)
		panel.anims 		= panel.lastState.anims or panel.anims
		panel.anim 			= panel.lastState.anim
		panel.layer			= panel.lastState.layer
		panel.animation = panel.anims[panel.anim]
		panel.layers 		= panel.animation.layers
		panel.frames 		= panel.layers[panel.layer].frames
		panel.frame 		= panel.lastState.frame
		panel.currentFrame 	= panel.lastState.cframe
		
		panel.fillsUsed = {}
		panel.cachedStrokes = {}
		panel.cachedSFills = {}

		panel.frames[panel.frame] 		 = panel.lastState.frameData and deepcopy(panel.lastState.frameData) or panel.frames[panel.frame]
		if panel.layers[panel.lastState.layer].format == "normal" then 
			panel.frames[panel.frame].canvas = panel.lastState.canvas and gfx.fromTextureData(panel.lastState.canvas,UnmultiplyGammaPixel) or panel.frames[panel.frame].canvas
		end
		panel.f = panel.frames[panel.frame]
		panel.l = panel.layers[panel.layer]
		panel.l.clips =  deepcopy(panel.lastState.clips)
		if panel.lastState.strokes then panel.f.strokes=deepcopy(panel.lastState.strokes) end
		panel.frames[panel.frame].l = panel.lastState.frameLength 


		if panel.l.remodel and panel.lastState.meshData then 
			for v,vert in ipairs(panel.lastState.meshData.vertices)do
				setVec(panel.l.data.vertices[v],vert)
			end
			 panel.l.data.edges=deepcopy(panel.lastState.meshData.edges)
			 panel.l.data.polygons=deepcopy(panel.lastState.meshData.polygons)
		end

		--applyTransform(panel.f)

		applyTransform(
			panel.transformer,
			panel.lastState.transformer.position,
			panel.lastState.transformer.orientation,
			vec3(1)
		)

		panel.undid = 1
		panel.changed=true
		panel.l.updots = true
		editor.refreshStrokes()
		panel.sudoForceRestroke = true
		gfx.setBlendMode('alpha')
		print("to before")
		
	end


local function pushUndoState  (action)
	local panel = animationPanel
	local copy = true 
	
	if multicheckOR(action,{"Shorten","Lengthen","Duplicate","Stroke","VecFill"})		then 
		copy = false
	end

	local strokes

	panel.timelineMouseing=true

	local undo = {}
		
	if action == "Region Edit" then 
		undo.animData = deepcopy(panel.anims[panel.anim])
	end
	undo.action  = action
	undo.region  = panel.region and deepcopy(panel.region)
	-- undo.anims 	= copy and shallowcopy(panel.anims) or panel.anims
	undo.anim 		= panel.anim
	undo.frame 	 	= panel.frame
	undo.cframe 	= panel.currentFrame
	undo.layer 	 	= panel.layer
	undo.clips   	= deepcopy(panel.l.clips)
	undo.meshData = panel.l.remodel and {
		vertices 	= copyVerts(panel.l.data.vertices),
		edges  		= deepcopy(panel.l.data.edges),
		polygons 	= deepcopy(panel.l.data.polygons),
	} or nil
	undo.frameData 	= copy and deepcopy(panel.frames[panel.frame]) 
	undo.transformer = deepcopy(panel.transformer)
	undo.strokes = copyStrokes(panel.frames[panel.frame].strokes)
	undo.frameLength = panel.f.l
	undo.selection = deepcopy(panel.selection or {})
	----undo.--string 	= panel.frames[panel.frame].canvas:newImageData():getString()

	if copy then 
		local canvas = panel.frames[panel.frame].canvas
		if canvas~=panel.blankCanvas and gfx.isCanvas(canvas) then 
			gfx.push("all")
			gfx.setCanvas()
				undo.canvas  = panel.frames[panel.frame].canvas:newImageData()
				-- lthread.newThread(mapPixelString):start(undo.canvas)
				-- waitFor(function()
				-- 	if lthread.getChannel("map"):getCount()>0 then 
				-- 		undo.canvas = lthread.getChannel("map"):pop()
				-- 		return true
				-- 	end
				-- end)
			gfx.pop()
		else

		end
	end

	editor.pushUndo(panel,undo)
end

if animationPanel then 
	animationPanel.undoFunc = undoFunc
	animationPanel.pushUndoState = pushUndoState
end

function copyStrokes(source)
	local strokes = {}
	if type(source)=="table" and #source>0 then 
		for s,stroke in ipairs(source) do 
			if not stroke.debug then 
				table.insert(strokes,{
					color = stroke.color and lume.clone(stroke.color) or nil,
					size  = stroke.size,
					group = stroke.group,
					ends 	= stroke.ends,
					line 	  = lume.clone(stroke.line)
				})
			end
		end
	end
	return strokes
end

function editor.defineAnimationPanel(new)

	local panel = 
	{ 
		name 			= "",
		open 			= false,
		docked 			= true,
		hide 			= true,
		y 				= 0,
		visible = true,

		canSave = true,

		playing 		= false,
		drawing 		= false,
		
		releaseCount = 1,
		releaseLayer = 1,

		scrollSpeed 	= 0,
		scrollPos 		= 0,
		timelineMax 	= 0,
		maxPixels  	= 2^ 22,

		tick 			= 0,
		time 		= 0,
		object  		= {},
		allFrames 		= {},
		undos 			= {},
		tweenDrag = {side="",frame=0,layer=0},
		frame 			= 1,
		layer 			= config.layer or 1,
		anim 			= "Idle",
		id 				= "",
		anims 			= nil,
		solo 			= 0,
		minWidth = 450,
		layerWidth = 17,
		drawingNormal = vec3(0),
		drawingPosition=vec3(0),
		baseLayerWidth 	= 70,
		frameWidth 		= 15,
		draggingFrame 	= false,
		draggedFrame 	= 0,
		canvas  		= nil,
		brushPrev 		= nil,
		fillDivs = config.fillDivs or 4,
		threadFill = true,
		forceRefill = config.forceRefill,
		antiAlias = config.antiAlias,
		antiAliasing = config.antiAliasing or 2,
		exportLinesOnly = config.exportLinesOnly,
		frameShift = config.frameShift,
		debugView = config.animDebugView or false,
		layersToDelete={},

		cachedStrokes = {},
		cachedFills = {},
		brushCanvas 	= newCanvas(500,500),
		brushCanvas2 	= newCanvas(500,500),
		blankCanvas 	= newCanvas(10,10),
		fillBehind 		= false,
		autoCenter 		= true,
		defaultSize 	= 10,
		padding 		= 2,
		brushEase 		= 2,
		brushCoords 	= {x=0,y=0,px=0,py=0},
		frameBounds 	= false,
		fullWidth 		= false,
	 	maxUndos 		= config.maxUndos or 100,  
	 	undid = 0,
	 	drawn 			= 0,
	 	filter 			= "linear",
	 	onion 			= { 
	 		amount=1,
	 		enabled = false,
	 		before 	= {
	 			amount=0.5,
	 			enabled = true,
	 			tint 	= {r=1,g=0.8,b=0},
	 			alphas 	= {0.5,0,0}
	 		},
	 		after 	= {
	 			amount=0.5,
	 			enabled = true,
	 			tint 	= {r=0.6,g=0,b=1},
	 			alphas 	= {0.5,0,0}
	 		},
	 	},
	 	diff 			= {x=0,y=0,w=0,h=0},

	 	tipDist =0,
	 	tipDist2 =0,
	 	selection = {},
	 	markSize 	= 6,
	 	height 		= 150,
	 	savedHeight = 150,
	 	stage 		= false,
	 	savedWidth 	= 300,
	 	width 		= 300,

	 	selectMode={true,false,false},
	 	selectModeDraws = {
	 		function()
	 			gfx.circle("fill",8,8,2)
	 		end,
	 		function()
	 			gfx.line(8,8,24,24)
	 		end,
	 		function()
	 			gfx.rectangle("line",6,6,20,20)
	 		end,
	 	},
		
		imageFormats = {"none","png","tga"},
	 	exportFormats={mp4=true,gif=false,sequence="none"},

	 	defaultFormat = "vector",
	 	formats = {"r8","normal","vector"},

	 	loopModes = {"repeat","pingpong","random"},

	 	meshModeNames     = {"transform","pivot","proportional","falloff"},

	 	transformMode 		= "global",
	 	transformModes 		= {"global","local","normal"},

	 	pivotMode 				= "bound",
	 	pivotModes 				= {"bound","cursor","origins"},

	 	proportionalMode 	= "disabled",
	 	proportionalModes = {"disabled","distance","connected"},

		falloffMode 			= "smooth",
	 	falloffModes 			= {"smooth","sphere","sharp","linear","constant"},

	 	renderModes = {"debug","color","shaded"},

	 	meshViewToggles = {"seeThru","orbit","unlit"},

	 	tool  		= "brush",
	 	
	 	row 			= sidebar.row,
	 	update 		= function(x) editor.animationPanelUpdate(x) end,
	}
	print("HOII")
	
	panel.register =  function()
		panel.animation = panel.anims[panel.anim]
		panel.layers 	= panel.animation.layers
		panel.l 		  = panel.layers[mc(1,panel.layer,#panel.layers)]
		panel.frames  = panel.l.frames
		panel.f  		  = panel.frames[panel.frame]
	end
	if 	config.timelineBottom then
		panel.bottom 		= config.timelineBottom
	else
		panel.bottom  		= true
	end
	panel.undoFunc = undoFunc
	panel.pushUndoState = pushUndoState

	if new then 
		return panel
	else 
		animationPanel=panel
	end
end

local sin = math.sin
local cos = math.cos

function editor.refreshStrokes()
	animationPanel.forceRestroke=true
end

function editor.animationPanelUpdate(panel)
	-- animationPanel.stageHeight=1080
	if  resizingWindow then 
		panel.hide = true 
		return 
	else 
		panel.hide = false 
	end
	panel.bonusHeight = 20
	panel.color= {r=0,g=0,b=0,a=0.5}
	panel.minWidth = 300
	panel.hide=not panel.open
	panel.onion.after.tint = {r=0.5,g=0,b=0.2}
	if level.isAnim and  panel.animation and LOVE then 
		lwindow.setTitle( config.currentProject.." - "..panel.animation.name )
	end
	panel.cat = mf(utils.map(currentRooms[1].camera.zoom,8,5,1,3.5))
	-- trace("panel.cat",panel.cat)
	if panel.cat~=panel.pcat then 
		editor.refreshStrokes()
	end
	panel.pcat=panel.cat



	-- local v1,v2 = vec2(3,1),vec2(1,-1)
	-- print("testDot",vec2.dot(v2,v1:normalize()))
	panel.vector = drawingPanel.vector
	panel.tool   = drawingPanel.tool
	panel.maxUndos = round(panel.maxUndos,0)

	variablePanel.hide 	= false

	logShift 	= easeTo(logShift,(panel.open and panel.bottom) and (sidebar.visible and panel.height+4 or -5) or -5, 2)
	

	if not panel.open or panel.hide then  
		editor.clearBones(panel)
		return  
	end 

	currentTool = panel.l.remodel and "Select" or "Brush"

	variablePanel.hide 	= true
	pathsPanel.open 	= false
	scriptsPanel.hide 	= true

	panel.active 		= true --(keyboard.active or mouse.active or panel.playing)

	gfx.setShader()

	editor.traceAnimationHelp 		(panel)
	
	editor.timelineTitleBottomToggle(panel)
	scene.updateEntityRenderInfo 	(panel.instance)


	panel.changedFrame = panel.frame~=panel.prevFrame or panel.layer~=panel.prevLayer
	panel.changed =  panel.changedFrame or panel.pasted 
	panel.pasted = false



	if panel.changed then 
		panel.proccedStrokes=nil
		-- editor.prepareFill(panel)
	end

	panel.fillTick = ltimer.getTime() 
	if panel.fillThread then 
	  local _,time = coroutine.resume(panel.fillThread)
		-- print("FILL TIME ====================== ",time)
	end


	if panel.changedFrame then 
		panel.selection={}
	end

	if panel.changedFrame then 
		editor.releaseSelection(panel)
	end

	if ( not panel.f.strokeMesh) then
		editor.refreshStrokes()
	end

	
	if panel.three and panel.l.type~="model" then 
		panel.l.remodel = false
		-- camera.reset(cam)
	end

	panel.filter = panel.antiAlias and "linear" or "nearest"


	panel.three = panel.l.type=="model"

	panel.prevFrame = panel.frame
	panel.prevLayer = panel.layer


	if panel.animation.audio and (not panel.animation.source or not panel.waveForm)  then 
		local waveForm = {}
		panel.animation.source = afx.newSource( LINK_DIR.."audio/"..panel.animation.audio, "stream" )
		panel.animation.soundData = sfx.newSoundData(LINK_DIR.."audio/"..panel.animation.audio)
		local count = panel.animation.soundData:getSampleCount()*2
		local rate  = panel.animation.soundData:getSampleRate()
		local secs = count/rate
		local samp
		local divisions = 64
		for i=0,secs,1/divisions do
			samp = mc(0.01,panel.animation.soundData:getSample(i*rate),0.3)
			-- table.insert(waveForm, {i,0,0,1} )
			table.insert(waveForm, {i/2,-samp*20,0,1				, 1,1, 1,1,1,1} )
			table.insert(waveForm, {i/2,samp*20,0,1	, 1,1, 1,1,1,1} )
			-- table.insert(waveForm, {i,0,0,1} )
		end
		panel.waveForm = gfx.newMesh(zformat,waveForm,"strip")
	end

	if panel.exporting or panel.saving then 
		unlockKeys()
		if keypressed("escape") then 
			if panel.zipThread then 
				panel.zipThread:kill()
			end
			panel.canSave = true
			panel.saving = false 
			panel.exporting = false 
			editor.tempTip 		((panel.saving and "Save" or "Export").." Cancelled")
			panel.playing=false
			keyTime = 5
			panel.changed=true
			editor.refreshStrokes()
			for i,anim in pairs(panel.anims) do 
				for l,layer in ipairs(anim.layers) do 
					for f,frame in ipairs(layer.frames) do 
						if frame.lastSave == panel.attemptedSaveTime then 
							frame.lastSave=frame.preSaveTime
						end
					end
				end
			end
			waiting = {}
			unlockKeys()
			unlockMouse()
		end



		lockMouse()
		lockKeys()
	end

	if panel.saving then
		editor.tempTip("Saving: "..tostring(mu((panel.saveFrame/#panel.splitFrames)*100 ) ) .."%")  
	end

	if not panel.canSave and panel.zipThread then  
		gfx.print("Compressing Save...")
	end
	
	local doneZipping = lthread.getChannel( "zipped" ):pop()
	if doneZipping then 
		print("doneZipping")
		panel.canSave = true
		local size = 256
		local scale, aspect = size/renderWidth , (renderHeight/renderWidth)
		local icon = newCanvas(size,size)
		gfx.push("all")
		gfx.setCanvas(icon)
		gfx.setColor(1,1,1,1)
		gfx.draw(panel.viewport.canvas,0,size*0.5-scale*renderHeight*0.5,0,scale,scale)
		gfx.pop()
		icon:newImageData():encode("png","icon.png")
		local set = "\""..ENGINE_DIR.."source/libs/fileicon\" set \""..(config.projects[CURRENT_PROJECT].dir.."/"..panel.zipName):gsub("//","/").."\" \""..SAVE_DIR.."icon.png\""
		print(set)
		os.execute(set)
		tempTip("Save Complete")
	end

	panel.register()
		
	if level.isAnim then 
		editor.panMult = vec2(editor.prepareToolMouse(panel,true))
	end
	drawingPanel.vector 	= panel.l.format =="vector"

	editor.updatePanelHeight 	(panel,sidebar.titleH)

	if not panel.playing then 

		editor.timelineKeys 			(panel)
		panel.register()

		editor.releaseFrames   	(panel)
		editor.restoreFrames    (panel)

		editor.updateTimeline 		(panel)

		editor.meshTools 				(panel)
		editor.poseTools 				(panel)

		if not (panel.l.remodel or panel.l.repose) then 
			editor.drawingTools 		(panel)
			editor.updateFrameTransform(panel) 
		elseif animationPanel.transformer then  
			scene.deleteEntity(panel.transformer)
		end		
		
		editor.drawCurrentFrame 	(panel)
		if cmdDown and keyRepeat("z") and not panel.canvasClicked then 
			editor.undo(panel)
		end

	else 

		if panel.exporting then 
			panel.exportCanvas:renderTo(function()
				gfx.setColor(1,1,1,1)
				if LOVR and not _flipView then 
					gfx.setMeshCullMode("none")
					gfx.draw(view.canvas,0,view.canvas:getHeight(),0,panel.exportScale,panel.exportScale*-1)
				else
					gfx.draw(view.canvas,0,0,0,panel.exportScale,panel.exportScale)
				end
			end)
			panel.sequenceFormat = panel.exportFormats.sequence=="png" and "png" or "tga"

			local data = panel.exportCanvas:newImageData()
			local prep = LOVR and SAVE_DIR.."/projects/" or "projects/"
			local file = string.format("%s%s/export/sequence/frame_%04d.%s",
				prep,config.currentProject,panel.currentFrame,panel.sequenceFormat)
			data:encode(panel.sequenceFormat,file)
		else
			if keypressed("escape") then 
				editor.playAnimation(nil,panel.currentFrame)
			end
			editor.releaseFrames   	(panel)
			editor.restoreFrames    (panel)
			editor.updateTimeline 		(panel)
		end

		editor.updateFrame 				(panel,1,(level.isAnim and not panel.exporting) and main_dt or nil)
		editor.drawCurrentFrame 	(panel) 

		if panel.exporting then 
			panel.drawBack=false
			editor.tempTip("Exporting: "..tostring(mu((panel.currentFrame/panel.animation.length)*100 ) ) .."%") 
			if panel.currentFrame == panel.animation.length then 
				panel.drawBack=true
			end
		end
		
	end

	if panel.movingSelection then 
		trace("moving selection",	panel.movingSelection)
	end
	
	if not (panel.exporting ) then 
		pushPost()
		gfx.setBlendMode("alpha")
		editor.scrollTimeline 			(panel)
		editor.timelineSliders 		(panel)
		editor.timelineOnionIndicator(panel)
		editor.updatePanelHeight 		(panel,sidebar.titleH)
		editor.timelineAudio 				(panel)
		editor.drawButton 					(panel.titleButton)
		editor.animationKeys				(panel)
		popPost()
	end

	editor.resetTimlineHeight 	(panel)
	editor.centerFrames 			  (panel)

	-- if #panel.layersToDelete>0 then 
	-- 	print("#panel.layersToDelete",#panel.layersToDelete)
	-- 	for i,v in ipairs(panel.layersToDelete) do 
	-- 		editor.deleteLayer(panel,v)
	-- 		print("i",i)
	-- 		print("delete",panel.layersToDelete[i])
	-- 	end
	-- end
	panel.hideSpeed = 2
end

function editor.poseTools(panel)
	scene.inverseKinematics(panel.l,panel.f.pose,panel)
end

function editor.openAnimationPanel(obj,fromStage,selectedAnim,overridePanel)
	local panel = animationPanel or overridePanel
	local time = os.time()
	keyTime = 60

	panel.new = (obj=="nil") 

	panel.instance  	= obj
	panel.key 			= obj.key
	panel.object 		= objects[obj.key]
	panel.newImage 	 	= false
	panel.blankAnim 	= false
	panel.open 			= true
	panel.draggingFrame = false
	panel.draggedFrame 	= 0
	panel.draggedLayer 	= 0
	panel.playing 		= false
	panel.canvas  		= nil
	panel.brushPrev 	= nil
	panel.anim 			= selectedAnim or panel.object.default

	panel.visible = true
	panel.selection = {}
	panel.frame 		= 1
	panel.currentFrame  = config.currentFrame or 1
	panel.stage 		= fromStage
	panel.frames 		= {}
	panel.anims 		= {}
	panel.undos			= {}
	panel.undoPos 		= 0
	panel.scrollPos 	= 0
	panel.y 			= panel.bottom and screenHeight or 0
	panel.pnframe  		= 1
	panel.pframe 		= 1
	headerPanel.open 	= false
	panel.savedWidth 	= sidebar.width
	sidebar.width 		= panel.width


	if level.isAnim then 
		panel.stageWidth=level.stageWidth
		panel.stageHeight=level.stageHeight
		panel.exportScale=level.exportScale
	end

	

	print("opening: "..obj.key)
	obj.adata 		= objects[obj.key].animations[obj.anim]

	panel.transformer = scene.newEntity({x=panel.instance.x,y=panel.instance.y,z=panel.instance.z,simple=true})

	editor.activateSidebar()

	gfx.reset()
	gfx.setBlendMode('replace','premultiplied')

	local oldCanvas
	local rawData

	for key,anim in pairs(panel.object.animations) do 
		panel.anims[key] = {
			layers	=	{},
			name	=	anim.name,
			key		=	key,
			fps		=	anim.fps,
			audio = anim.audio,
			length	=	anim.length
		}

		if anim.audio then 
			panel.anims[key].source = afx.newSource( LINK_DIR.."audio/"..anim.audio, "stream" )
		end

		for l,layer in ipairs(anim.layers) do

			panel.anims[key].layers[l] = {
				name 		= layer.name,
				type    = layer.type,
				frames 	= {},
				obj 		= layer.obj,
				iqm 		= layer.iqm,
				clips   = layer.clips or {},
				visible = layer.visible, 
				alpha   = layer.alpha or 1,
				data    = layer.data and panel.object.models[layer.data.id],
				loop 		= layer.loop, 
				format  = layer.format or panel.defaultFormat,
				palette = layer.palette or {},
				center  = layer.center,
				skeleton= layer.skeleton,
				onePixel= layer.onePixel,
				wobble  = layer.wobble or 0,
				resolution = layer.format=="vector" and 1 or (layer.resolution or 1),
				smooth  = layer.smooth,
				catmull = layer.catmull or 3,
				lineWidth = layer.lineWidth or 1,
				lineWidthOverride = layer.lineWidthOverride,
				id 			= layer.id or uuid(),
				vector 	= layer.vector,
				line 		= layer.line,
				fill 		= layer.fill,
				length 	= layer.length,
			}
			for f,frame in ipairs(layer.frames) do 
				
				--FRAMEDEF
				panel.anims[key].layers[l].frames[f] = {
					blank 	= frame.blank,
					canvas 	= layer.format=="normal" and newCanvas(frame.w,  frame.h,layer.format) or panel.blankCanvas ,
					anim 	= frame.anim,
					w 		= frame.w,
					strokes = frame.strokes or {},
					isKey  = frame.isKey,
					fills 	= frame.fills,
					h 		= frame.h,
					pose   = frame.pose,
					id     = frame.id or uuid(),
					tween = frame.tween or {enabled=false,easeIn=0,easeOut=0},
					position= vec3(frame.position.x or frame.position[1],frame.position.y or frame.position[2],frame.position.z or frame.position[3]) ,
					orientation= quat(frame.orientation.x or frame.orientation[1],frame.orientation.y or frame.orientation[2],frame.orientation.z or frame.orientation[3],frame.orientation.w or frame.orientation[4]) ,
					scale 	= vec3(frame.scale.x or frame.scale[1],frame.scale.y or frame.scale[2],frame.scale.z or frame.scale[3]) 	,
					ox 		= frame.ox or 0,
					oy 		= frame.oy or 0,
					l 		= frame.l  or 1,
					lastDrawn = time,
					lastSave  = time,
				}

				applyTransform(panel.anims[key].layers[l].frames[f])
				
				oldCanvas = gfx.getCanvas()

				gfx.setFilter(panel.anims[key].layers[l].frames[f].canvas,panel.filter)
				
				gfx.setCanvas(panel.anims[key].layers[l].frames[f].canvas)

				if LOVE and layer.format=="normal"  then 
					print("\n\n frms")
					print(inspect(getImagePath(obj.key)))
					print("\n\n")
					local suc,rawData 					= pcall(limage.newImageData, getImagePath(obj.key)[frame.id])
					if suc then 
						rawData:mapPixel 	(PremultiplyGammaPixel) 
						-- gfx.setBlendMode("alpha","premultiplied")
						local img = gfx.newImage(rawData)
						gfx.draw 					(img)
						rawData:release()
						img:release()
					end
				end
				gfx.setCanvas(oldCanvas)
			end
		end
	end


	panel.loaded 		= true
	panel.animation = panel.anims[panel.anim]
	panel.layers 		= panel.animation.layers
	panel.layer 		= mc(1,panel.layer,#panel.layers)
	panel.l 				= panel.layers[mc(1,panel.layer,#panel.layers)]
	panel.frames 		= panel.animation.layers[1].frames
	panel.frame 		= editor.getKeyFrame(panel,panel.layer,panel.currentFrame)
	panel.f 				= panel.frames[panel.frame]
	panel.canvas 		= newCanvas(panel.f.w,  panel.f.h,panel.l.format)

	panel.instance.models = {}

	panel.pushUndoState()

	lume.clear(selectedEntities)

	obj.models[#panel.layers+1]=nil
	obj.models[#panel.layers+2]=nil

	panel.open=true

	editor.checkTimelineDocked(panel)
	editor.clearBones(panel)
end

function editor.saveAnimation(panel)
	editor.saveGraphics(panel)
end

function editor.centerFrames 			(panel)
	local d,add,new,mat = vec3()
	if panel.drawing or panel.playing then return end
	for l,layer in ipairs(panel.layers) do
		if layer.center then 
			for f,frame in ipairs(layer.frames) do
				local clip = false 
				if (frame.pw and frame.w>frame.pw) or (frame.ph and frame.h>frame.ph) then 
					editor.clipFrame(frame)
				end
				frame.pw,frame.ph = frame.w,frame.h
				if math.abs(frame.ox)>1 or math.abs(frame.oy)>0 then 
					d.x,d.y,d.z  			= -frame.ox,-frame.oy,0
					frame.ox,frame.oy = 0,0
					mat  							= applyTransform(nil,vec3(0),frame.orientation,frame.scale)
					add 							= mat*d
					new  							= frame.position+add
					frame.position  	= new
					applyTransform 		(frame,new)
					panel.changed  		= true
					editor.updateFrameTransformer(panel)
				end
			end
		end
	end
end


function editor.releaseFrames(panel,n,r)
	if n and n>20 then return end

	local r = editor.getKeyFrame(panel,panel.releaseLayer)
	local n = 1

	panel.releaseCount = panel.releaseCount+1

	if panel.releaseCount>#panel.layers[panel.releaseLayer].frames then 
		panel.releaseCount = 1
		panel.releaseLayer = panel.releaseLayer<#panel.layers and panel.releaseLayer+1 or 1
		r = editor.getKeyFrame(panel,panel.releaseLayer)
	end

	editor.releaseStrokes(panel,n,r)

end

function editor.releaseStrokes(panel,n,r)

	local l, layer = panel.releaseLayer, panel.layers[panel.releaseLayer]
	local f, frame = panel.releaseCount, layer.frames[panel.releaseCount]
	local release  = 
		r~=wrap2(1,f,#layer.frames) and 
		r~=wrap2(1,f-1,#layer.frames) and 
		r~=wrap2(1,f+1,#layer.frames) and 
		r~=wrap2(1,f-2,#layer.frames) and 
		r~=wrap2(1,f+2,#layer.frames)

	if release then 
		if  not frame.released then 
			frame.released=true
			if frame.strokeMesh then 
				frame.strokeMesh:release()
				frame.strokeMesh = nil
			end
			if layer.format=="normal" and frame.canvas and gfx.isCanvas(frame.canvas) then 
				-- print(l,f,r)
				local data = frame.canvas:newImageData() 
				mapPixel(data,UnmultiplyGammaPixel)
				-- print("release",frame.canvas)
				gfx.setTexture(frame.mesh)
				-- print(frame.canvas:release())
				-- print("released",frame.canvas)
				frame.canvas=nil 
				-- collectgarbage("collect")
				-- collectgarbage("collect")
				frame.canvas = data
				-- print("data ?",frame.canvas)
			end
		else
			editor.releaseFrames(panel,n+1)
		end
	end
end

function editor.restoreFrames(panel)
	for l,layer in ipairs(panel.layers) do 
		local r = editor.getKeyFrame(panel,l)
		for f=r-1,r+1,1 do
			local frame = getLooped(layer.frames,f)
			if frame.released then
				frame.released = false 
				if layer.format=="normal" and not gfx.isCanvas(frame.canvas) then 
					frame.canvas =  gfx.isCanvas(frame.canvas) and frame.canvas or gfx.fromTextureData(frame.canvas)
				end
				if layer.format=="vector" then 
					editor.refreshStrokes()
				end
			end
		end
	end
end

function editor.updateFrame( panel , mult , dt )
	local mult = mult or 1
	if dt then 
		panel.time = panel.time+main_dt
		panel.ticked = panel.currentFrame~=mu(panel.time*panel.animation.fps)
		panel.currentFrame = mu(panel.time*panel.animation.fps)

		panel.ticked2 = panel.currentFrame2~=mu(panel.time*panel.animation.fps/2)
		panel.currentFrame2 = mu(panel.time*panel.animation.fps/2)
	else
		panel.tick = panel.tick+(1/60)*mult
		panel.time = panel.time+(1/60)
		if panel.tick > (1/panel.animation.fps) then 
			panel.tick 			= panel.tick-(1/panel.animation.fps) 
			panel.currentFrame 	=  panel.currentFrame+1
		elseif panel.tick < 0 then 
			panel.tick 			= 1/panel.animation.fps
			panel.currentFrame 	=  panel.currentFrame-1
		end
	end
	editor.loopAnimation(panel)
end

function editor.loopAnimation(panel)
	if panel.currentFrame > panel.animation.length then 
		panel.currentFrame 	=  1
		panel.time = 0
		panel.scrollPos=0
		if panel.exporting then 
			editor.playAnimation()
			panel.exporting = false
		else
			if panel.animation.source then
				panel.animation.source:seek(panel.time,"seconds")
			end
		end
	end
	if panel.currentFrame < 1 then 
		panel.currentFrame 	=   panel.animation.length
	end
end

function editor.saveGraphics(panel)
	if not panel.canSave then return end
	panel.canSave = false
	-- editor.clipFrames 	(panel)
	editor.stackSheet 	(panel)
	panel.newImage  	= 	false
end

function editor.nilFrameCanvases( panel )
	for i,anim in pairs(panel.anims) do 
		for l,layer in ipairs(anim.layers) do 
			for f,frame in ipairs(layer.frames) do 
				frame.canvas=nil
			end
		end
	end
end

function editor.checkTimelineDocked(panel)
	panel.docked 	= not panel.bottom
	panel.y 		= panel.bottom and  screenHeight or sidebar.height
	drawingPanel.y 	= panel.bottom and  screenHeight or sidebar.height
	for l,layer in ipairs (panel.layers) do 
		if layer.element then layer.element.y = panel.y end
	end
	drawingPanel.bottom=panel.bottom
end

function editor.closeAnimationPanel(panel)
	panel.open 					= false 
	drawingPanel.open 	= false
	startButton 				= true
	panel.undos 				= nil

	for a,anim in pairs(panel.anims) do 
		for l,layer in ipairs(anim.layers) do 
			layer.poseMesh=nil
			for f,frame in ipairs(layer.frames) do 
				frame.canvas=nil
				frame 		=nil
			end
		end
	end
	
	panel.anims 	= nil
	panel.raw 		= nil
	panel.brushCanvas=nil
	panel.canvas 	= nil		
	--panel.instance.models = {}
	--panel.instance.img  = getImage(panel.instance.key)
	panel.instance = nil 

	collectgarbage()

	editState 		= "Normal"
	currentTool 	= "Select"

	editor.deactivateSidebar()
	sidebar.width   = panel.savedWidth
	scene.deleteEntity(panel.transformer)
	lume.clear(selectedEntities)
	panel.transformer=nil

	lmouse.setVisible(true)  
end

function editor.clipFrames(panel)
	for a,anim in pairs(panel.anims) do 
		for l,layer in ipairs(anim.layers) do 
			for i,f in ipairs(layer.frames) do 
				editor.clipFrame( f )
			end
		end
	end
end

function editor.clipFrame( f )
	if not f.blank then 
		local canvas, od, fdat = clipCanvas(f.canvas,true,nil,animationPanel.l.format=="r8")

		f.canvas 	= canvas
		f.w 			= fdat.w
		f.h 			= fdat.h
		f.ox 			= f.ox - od.xoff * animationPanel.l.resolution
		f.oy 			= f.oy - od.yoff * animationPanel.l.resolution


		log(od.xoff,od.yoff)
		
		animationPanel.changed = true

		animationPanel.l.canvas  =  newCanvas(animationPanel.f.w,animationPanel.f.h,animationPanel.l.format)
	end
end


function editor.changeLayer(l)
	local panel 				= animationPanel 
	local changed 			= (mc(1,l,#panel.layers)~=panel.layer)
	panel.layer 				= mc(1,l,#panel.layers)		
	panel.currentFrame 	= mc(1,panel.currentFrame,panel.layers[panel.layer].length)
	panel.frames 				= panel.layers[panel.layer].frames
	panel.frame 				= editor.getKeyFrame(panel,panel.layer,panel.currentFrame)
	if panel.solo >0 and panel.layer==l then 
		panel.solo = l
	end
	return changed
end


function editor.newLayer(panel,insert,format,type)
	local format = format or panel.l.format
	local insert = insert or #panel.layers
	panel.pushUndoState()
	table.insert(panel.layers, insert , {
		name 			= "Layer "..tostring(#panel.layers+1),
		visible 	= true,
		type     	= (format=="camera" or format=="model") and format or "drawing",
		resolution = panel.l.resolution,
		format 		= format,
		id 				= uuid(),
		frames 		= {},
		clips     = {},
		resolution = panel.l.resolution,
		alpha     =  1,
		catmull 	=  0,
		lineWidth =  1,
		line 			= {color = {r=0,g=0,b=0,h=0,s=0,v=0,a=1},width=1},
		fill 			= {color = {r=0,g=0,b=0,h=0,s=0,v=0,a=1}},
	})

	panel.layer  				= insert
	panel.l 						= panel.layers[insert]
	panel.frames 				= panel.layers[insert].frames
	panel.frame  				= 1	
	panel.canvas  			= (format=="normal" or format=="r8") and newCanvas(100,100,format) or nil
	
	if panel.canvas then 
		gfx.setFilter(panel.canvas,panel.filter) 
	end

	if format=="model" then 
		panel.l.format = false
		editor.createNewModelLayer()
	end

	panel.layers[insert].frames[1] = editor.defaultFrame(panel,{l=panel.currentFrame,canvas = canvas}) 

	panel.y         		= panel.y-panel.row
	keyTime = 16
end

function editor.queueDeleteLayer(panel,layer)
	table.insert(panel.layersToDelete,layer)
end

function editor.deleteLayer(panel,layer)
	local l = layer or panel.layer
	panel.pushUndoState()
	table.remove(panel.layers,l)
	panel.y=panel.y+panel.row+7
	panel.ty=panel.ty+panel.row+7
	keyTime = 10
	-- panel.height=panel.height-panel.row
	-- panel.savedHeight=panel.savedHeight-panel.row
	panel.layer=mc(1,panel.layer,#panel.layers)
	panel.releaseLayer=mc(1,panel.releaseLayer,#panel.layers)
	panel.hideSpeed = 1
	panel.register()
end



function editor.playAnimation(fps,start)
	local panel = animationPanel
	editor.refreshStrokes()
	panel.preFPS 				= panel.playing and panel.preFPS or panel.animation.fps
	panel.animation.fps = panel.playing and panel.preFPS or fps or panel.animation.fps
	panel.fpsSlider.pct = (panel.animation.fps-1)/59
	panel.playing 			= not panel.playing
	panel.playStart 		= panel.playing and  panel.currentFrame or panel.playStart

	panel.currentFrame 	= panel.playing and  (start or panel.currentFrame) or (start or panel.playStart)
	panel.tick 					= 0
	panel.time 					= (panel.currentFrame-1)/panel.animation.fps
	panel.playTrackSpeed = 128

	if panel.animation.source and not panel.exporting then 
		if not panel.playing then 
			afx.stop( )
		else
			panel.animation.source:seek(math.max(panel.time,0),"seconds")
			panel.animation.source:play()
		end
	end
end

function editor.drawFrameMesh(panel)
	panel.transformCursor = nil
	if not(scene.drawingShadow) and  panel and panel.selection and panel.selection.mesh  then 
		
		local ires = 1/panel.l.resolution

		local off 
		if panel.selection.position then 
			panel.selection.diff   = {
				x=panel.selection.position.x-panel.f.w/2,
				y=panel.selection.position.y-panel.f.h/2
			}
			off = {
				x=(-panel.selection.diff.x-panel.f.ox*ires)/ires,
				y=(-panel.selection.diff.y-panel.f.oy*ires)/ires
			}
		else
			off = {
				x=-panel.f.ox,
				y=-panel.f.oy
			}
		end

		-- panel.selection.offset = offset

		local offset = 
			applyTransform(nil,vec3(	
				-(panel.selection.bbox and -panel.selection.bbox[1]-panel.selection.bbox.w/2 or -panel.f.w/2),
				-(panel.selection.bbox and -panel.selection.bbox[2]-panel.selection.bbox.h/2 or -panel.f.h/2),
			0))
			*applyTransform(nil,nil,nil,panel.selection.scale and vec3( (1/panel.selection.scale.x)*ires,(1/panel.selection.scale.y)*ires,1) or vec3(ires))
			*mat4.from_quaternion(quat.rotate(panel.selection.position and -(panel.selection.angle or 0)+2*pi or 2*pi,axes.z))
			*applyTransform(nil,vec3(	off.x,off.y,0))

		panel.selection.transform = offset*applyTransform(panel.f)

		drawModel({
			stripe= -1,
			indexed = panel.selection.format=="r8",
			palette = panel.selection.palette,
			color = {1,1,1,1},
			wireframe=true,
			noCastShadow = true,
			mesh=panel.selection.mesh,
			parent=panel.instance,
			layer = panel.layer,
			transform = -offset*-panel.f.transform
		})

		if panel.selection.bbox and panel.instance then 

			local x,y,w,h =  panel.selection.bbox[1], panel.selection.bbox[2],panel.selection.bbox.w,panel.selection.bbox.h
			local x2,y2,w2,h2 = panel.selection.quad:getViewport()
			local w3,h3 = panel.selection.quad:getTextureDimensions()
			local verts ={
				{x,y,0,1, 		x2/w3,			y2/h3}, 
				{x+w,y,0,1, 	(x2+w2)/w3,	y2/h3}, 
				{x+w,y+h,0,1, (x2+w2)/w3,	(y2+h2)/h3}, 
				{x,y+h,0,1, 	x2/w3,			(y2+h2)/h3}}

			if panel.selection.format ~= "vector" then 
				local previewMesh = gfx.newMesh(zformat,verts,"fan")

				gfx.setTexture(previewMesh(panel.selection.canvas))
				drawModel({
					noCastShadow = true,
					indexed = panel.selection.format=="r8",
					palette = panel.selection.palette,
					mesh=previewMesh,
					parent=panel.instance,
					layer = -0.1,
					transform = panel.selection.transform
				})
			end

			local size,ang,dist,c,c2 = math.max(0.1,(1-panel.l.resolution))*5
			local roff = ires/(vec2.len(panel.selection.scale)/2)
			local cp = vec2( x+w/2, 		y+h/2 )
			local points = {
				vec2( x, 				y ),
				vec2( x+w/2, 		y ), -- side
				vec2( x+w, 	y),
				vec2( x+w, 	y+h/2), -- side
				vec2( x+w, 	y+h),
				vec2( x+w/2, 	y+h), -- side
				vec2( x, 		y+h),
				vec2( x, 		y+h/2), -- side

				vec2( x-roff, 		y-roff ),
				vec2( x+w+roff, 	y-roff),
				vec2( x+w+roff, 	y+h+roff),
				vec2( x-roff, 		y+h+roff),
			}

			local points2 = {
				vec2( -(w/2)*panel.selection.scale.x, 		-(h/2)*panel.selection.scale.y ),
				vec2( x, 		y ), -- side
				vec2( (w/2)*panel.selection.scale.x, 		-(h/2)*panel.selection.scale.y ),
				vec2( x+w, 	y), -- side
				vec2( (w/2)*panel.selection.scale.x, 		(h/2)*panel.selection.scale.y ),
				vec2( x+w, 	y+h), -- side
				vec2( -(w/2)*panel.selection.scale.x, 		(h/2)*panel.selection.scale.y ),
				vec2( x, 		y+h), -- side

				vec2( x-roff, 		y-roff ),
				vec2( x+w+roff, 	y-roff),
				vec2( x+w+roff, 	y+h+roff),
				vec2( x-roff, 		y+h+roff),
			}

			local p1,p2,p3 = 
				vec2(panel.brushCoords),
				vec2(panel.brushStart),
				vec2(panel.selection.offset.x,panel.selection.offset.y)

			local rot = vec2.angle_to(p3,p1)+pi*2-pi

			local square = 7
			local point2 
			local pts = {}
			local pointMat = -offset*-panel.f.transform*-panel.instance.transform
			local prepResize

			local shape = {}

			panel.selection.preTransform=false

			for i=1,12,1 do

				c = points[i]
				c2 = points2[i]

				local point = camera.project(pointMat*vec3(c.x,c.y,0))
				-- trace(point.x,point.y)
				point = {x=point.x,y=point.y,alpha=0.7}

					
				if i<9 then 
					if (i+1)%2==0 then 
						table.insert(shape,point.x)
						table.insert(shape,point.y)
					end
					if  math.dist(mouse.x,mouse.y,point.x,point.y)<12 then 
						point.alpha=1
						local angle = vec2.angle_between(vec2(-1,0),vec2.rotate(c-cp,panel.selection.angle))
						local angle2 = vec2.angle_to(cp,c)+panel.selection.angle
						while angle2>pi  do angle2=angle2-2*pi end
						while angle2<-pi do angle2=angle2+2*pi end
						trace(angle,angle2)
						local neg = angle2<0 
						angle = angle<0 and -angle or angle
						local inc=pi/8

						if angle>(pi-inc) or angle<(inc)   then 
							panel.transformCursor= cursors.scale_w 
						elseif angle>pi/2-inc and angle<pi/2+inc then 
							panel.transformCursor= cursors.scale_h 
						elseif angle>inc and angle<pi/2-inc then 
							panel.transformCursor= neg and cursors.scale_d1 or cursors.scale_d2 
						elseif angle<pi-inc and angle>pi/2+inc then 
							panel.transformCursor= neg and cursors.scale_d2 or cursors.scale_d1 
						end
						lmouse.setCursor( panel.transformCursor)
						prepResize = true
						panel.selection.preTransform=true
						mouse.set(function()  
							panel.pushUndoState() 
							editor.cutStrokeSelection(panel)
					 		panel.scalingSelection = i
					 		panel.selection.startScale = panel.selection.scale:clone()
					 		editor.prepareToolMouse(panel)
					 		panel.selection.scalePoint = i
						 end)
					end
				else 
					if not panel.scalingSelection and not prepResize and math.dist(mouse.x,mouse.y,point.x,point.y)<24 then 
						point.alpha=1
						panel.transformCursor= cursors.rotate 
						lmouse.setCursor( panel.transformCursor)
						panel.selection.preTransform=true
						mouse.set(function()    
							panel.pushUndoState() 
							editor.cutStrokeSelection(panel)
					 		panel.rotatingSelection = true  
					 		panel.selection.startAngle = panel.selection.angle
					 		panel.selection.startRot   = rot
					 		editor.prepareToolMouse(panel)
							panel.brushStart 				= {x=panel.brushCoords.x,y=panel.brushCoords.y}
							panel.selection.scalePoint = i
						 end)
					end
				end

				if i>8 then 
					if  not panel.scalingSelection then 
						point.alpha = point.alpha==1 and 1 or 0.2--mc(0.2,1-math.dist(mouse.x,mouse.y,point.x,point.y)/128,0.6)
						pushDraw(function() 

							-- gfx.circle("fill",point.x,point.y,3+5*point.alpha,8) 
							gfx.push()
							gfx.translate(point.x,point.y)
							gfx.setLineWidth(2)
							gfx.rotate(panel.selection.angle+((i-8)/4)*pi*2+pi/2)
							gfx.setColor(1,1,1,point.alpha)
							gfx.circle("line",0,0,3)
							gfx.setLineWidth(4)
							gfx.setColor(0,0,0,point.alpha*2)
							gfx.arc("line","open",0,0,3+5*point.alpha,-pi/6,pi/2+pi/6,8)
							gfx.pop()
							-- gfx.circle("line",point.x,point.y,3,8) 
						end)
					end
				else 
					-- if  not panel.rotatingSelection then 
						pushDraw(function() 
							gfx.push()
							gfx.translate(point.x,point.y)
							gfx.rotate(panel.selection.angle)
							-- gfx.setColor(1,1,1,point.alpha)
							-- gfx.rectangle("fill",-square/2,-square/2,square,square)
							gfx.setColor(0,0,0,point.alpha*1.2)
							gfx.setLineWidth(2)
							gfx.rectangle("fill",-square/2,-square/2,square,square)
							gfx.pop()
						end)
						if pts[i-1] then 
							pushDraw(function() 
								gfx.line(point.x,point.y,pts[i-1].x,pts[i-1].y)	
							end)
						end
						if i==8 then 
							pushDraw(function() 
								gfx.line(point.x,point.y,pts[1].x,pts[1].y)	
							end)
						end
						pts[i]=vec2(point)
					-- end
				end
			end

			trace("pretrans",panel.selection.preTransform)
			
			panel.hoveringSelection = point_in_poly(shape,mouse)

			if panel.rotatingSelection then 
				lmouse.setCursor( panel.transformCursor)
				panel.selection.angle =  panel.selection.startAngle+(rot-panel.selection.startRot)
				
				--[[(math.angle(panel.selection.bbox[1]+panel.selection.bbox.w/2,panel.selection.bbox[2]+panel.selection.bbox.h/2,panel.brushStart.x,panel.brushStart.y)
					-math.angle(panel.selection.bbox[1]+panel.selection.bbox.w/2,panel.selection.bbox[2]+panel.selection.bbox.h/2,panel.brushCoords.x,panel.brushCoords.y)
				)]]

				game.angleLine = {p1.x,p1.y,p3.x,p3.y,p2.x,p2.y}

				editor.refreshStrokes()
				mouse.pushRelease( function() 
					panel.rotatingSelection = false
				end)
			elseif panel.scalingSelection then 

					for i=1,3 do 
				lmouse.setCursor( panel.transformCursor)
				if shiftDown then 
					panel.selection.scale.x,panel.selection.scale.y =  setAll(math.dist(panel.selection.position.x,panel.selection.position.y,panel.brushCoords.x,panel.brushCoords.y  )/(panel.selection.bbox.w/2))
				else
					local old = vec2(panel.selection.position.x-panel.brushCoords.x,panel.selection.position.y-panel.brushCoords.y)

					local old2 = panel.brushPrev and vec2(panel.selection.position.x-panel.brushPrev.x,panel.selection.position.y-panel.brushPrev.y) or old
					-- local ang = math.angle(0,0,points2[panel.selection.scalePoint].x,points2[panel.selection.scalePoint].y)
					local mid = vec2.rotate(old, -panel.selection.angle)
					local mid2 = vec2.rotate(old2, -panel.selection.angle)
					local pscale = vec2(panel.selection.scale)
					if panel.scalingSelection%2~=0 then 
						panel.selection.scale.x = math.abs(mid.x/panel.selection.bbox.w*2)
						panel.selection.scale.y = math.abs(mid.y/panel.selection.bbox.h*2)
					else
						if panel.scalingSelection%4==0 then 
							local sign = panel.scalingSelection==4 and -1 or 1
							print("sign",sign)
							panel.selection.scale.x = altDown 
								and panel.selection.scale.x+sign*(mid-mid2).x/(panel.selection.bbox.w) 
								or  sign*(mid.x/panel.selection.bbox.w*2)
								mid.y,mid2.y=0,0
						elseif (panel.scalingSelection+2)%4==0 then 
							local sign = panel.scalingSelection==6 and -1 or 1
							print("sign",sign)
							panel.selection.scale.y = altDown 
								and panel.selection.scale.y+sign*(mid-mid2).y/(panel.selection.bbox.h) 
								or  sign*(mid.y/panel.selection.bbox.h*2)
								mid.x,mid2.x=0,0
						end
					end
					if altDown then 
					
						panel.selection.position.x,panel.selection.position.y=
							(vec2(panel.selection.position) - vec2.rotate(mid-mid2,panel.selection.angle)/2):unpack()
					end
				end
				panel.brushPrev = deepcopy(panel.brushCoords)
				editor.refreshStrokes()
				mouse.pushRelease( function() 
					panel.scalingSelection = false
				end)
			end
			
			else 
				
			end

		end
	end
end

function editor.updateLayerFrame(panel)

	panel.updateMesh = true 
	panel.changed = true
	panel.l.canvas  =  newCanvas(panel.f.w,panel.f.h,panel.l.format)
	if panel.l.canvas then 
		gfx.setFilter(panel.l.canvas,panel.filter)
		panel.l.canvas:renderTo(function()
			gfx.clear(0,0,0,0)
			gfx.setColor(1,1,1,1)
			gfx.setBlendMode("alpha","premultiplied")
			gfx.draw(panel.f.canvas)
		end)
	end
	editor.clipFrame(panel.f)
end

function editor.drawCurrentFrame(panel)



	panel.drawing = drawingPanel.tool ~= "erase" and panel.canvasClicked
	if panel.l.format ~="vector" then 

		if pcall(gfx.setFilter,panel.f.canvas,panel.filter) then 

			if panel.l.format=="r8" or panel.l.format=="normal" then 
				trace("frame size: "..mf(panel.f.w).."x"..mf(panel.f.h))
			end
			--trace("strokes: "..#panel.f.strokes)

			-- print("l.canvas",panel.updateMesh or (not panel.l.canvas) or panel.changed)
			
			if  panel.updateMesh or (not panel.l.canvas) or panel.changed then  
				if panel.l.canvas then 
					-- panel.l.canvas:release() 
				end
				panel.l.canvas  =  newCanvas(panel.f.w,panel.f.h,panel.l.format)
				gfx.push("all")
				gfx.setCanvas(panel.l.canvas)
					gfx.clear(0,0,0,0) 
				gfx.pop()
			end 

			if panel.l.canvas then 
				gfx.push("all")
				gfx.setCanvas(panel.l.canvas) 
				gfx.setFilter(panel.l.canvas,panel.filter)
				gfx.clear(0,0,0,0,1,1)
				gfx.setColor(1,1,1,1)
				gfx.setBlendMode("alpha","premultiplied")
				gfx.setShader(shaders.unmul)
				gl.BlendFuncSeparate(gle.SRC_ALPHA,gle.ONE_MINUS_SRC_ALPHA,gle.ONE,gle.ONE_MINUS_SRC_ALPHA)
				if LOVR then 
					gfx.draw(panel.f.canvas,0,panel.f.h,0,1,-1)
				else
					gfx.draw(panel.f.canvas)
				end
				if panel.selection and panel.selection.canvas then 
					-- editor.drawSelection(panel)
				end
				gfx.pop()

				-- local data = panel.l.canvas:newImageData()
				-- mapPixel(data,UnmultiplyGammaPixel)
				-- data:encode("png","tmp3.png")
			end


			if panel.canvasClicked and not panel.vector and panel.brushCanvas ~= nil then 
				editor.drawPreviewToFrame(panel)
			end

			if drawingPanel.tool=="brush" and not altDown then 
				gfx.push("all")
				if pcall(gfx.setCanvas,panel.l.canvas) then 
					--editor.brushPreviewCanvas(panel)
				end
				gfx.pop()
			end
		end
	end


	if panel.active then 
		if  editingLevel and animationPanel.open then		

			if drawingPanel.backing then
				--gfx.draw( 
				--	getImage("checkers"),
				--	gfx.newQuad(0, 0, screenWidth, screenHeight, 496,496)
				--)
			end

			local lengthMark = 1

			for l,layer in ipairs( panel.layers ) do
				layerLength(layer)
				lengthMark = layer.length>lengthMark and layer.length or lengthMark
				if (layer.visible and panel.solo == 0) or panel.solo==l  then 
					local frameMark    = editor.getKeyFrame(panel,l,layer.loop and (panel.currentFrame-1)%(layer.length)+1 or panel.currentFrame)
					layer.frame = frameMark
				end
			end    

			panel.animation.length 			= lengthMark

		end
	end

end


function editor.clearBones(panel)
	if panel.l and panel.l.bones then 
		for b,bone in pairs(panel.l.bones)do
			scene.deleteEntity(bone)
		end
		panel.l.bones=nil
		panel.l.chains = nil
		panel.l.poseMesh = nil
	end
end



function editor.updateFrameTransform(panel) 

	if not panel.transformer  then
		panel.transformer = scene.newEntity({x=panel.instance.x,y=panel.instance.y,z=panel.instance.z,simple=true})
		panel.changed = true
	end

	if panel.clearingFrame then 
		panel.transformer.position=vec3(0)
		panel.transformer.orientation=defaultOrientation
		panel.transformer.scale = vec3(1)
		panel.transformer.transform = mat4()
		panel.changed=true
	end

	panel.transformer.visible = false

	
	if panel.changed or panel.undid>0  then
		editor.updateFrameTransformer(panel)
	end

	if panel.transformer then 

		if editor.transforming  then 
			applyTransform(panel.f
				,panel.instance.orientation * (panel.transformer[multiTool.dragging and "nextPosition" or "position"]-panel.instance.position) * panel.instance.scale
				,panel.transformer[multiTool.rotating and "nextOrientation" or "orientation"]*panel.instance.orientation:inverse()
				,panel.transformer[multiTool.scaling and "nextScale" or "scale"]*vec3( 1/panel.instance.scale.x,1/panel.instance.scale.y,1/panel.instance.scale.z)
			)
		else
			if  panel.undid>0 then 
				panel.undid=0
				--temp
				--panel.f.position = panel.instance.orientation * ((panel.transformer.position-panel.instance.position)  )
				--panel.f.orientation = panel.transformer.orientation*panel.instance.orientation:inverse()
			 	--panel.f.scale = panel.transformer.scale*panel.instance.scale
				--applyTransform(panel.f)
			end 
		end

		if panel.clearingFrame then 
			panel.f.position = vec3(0)
			panel.f.orientation = defaultOrientation()
			panel.f.scale = vec3(1)
			panel.clearingFrame=false
		end

	end
end

function editor.applyFrameTransform()
	animationPanel.f.position = animationPanel.instance.orientation * (animationPanel.transformer.position-animationPanel.instance.position) * animationPanel.instance.scale
	animationPanel.f.orientation = animationPanel.transformer.orientation*animationPanel.instance.orientation:inverse()
	animationPanel.f.scale = animationPanel.transformer.scale*vec3( 1/animationPanel.instance.scale.x,1/animationPanel.instance.scale.y,1/animationPanel.instance.scale.z)
	applyTransform(animationPanel.f)
end

function editor.updateFrameTransformer(panel)
	if not panel.instance then return end
	if not panel.transformer then 
		panel.transformer=scene.newEntity({x=panel.instance.x,y=panel.instance.y,z=panel.instance.z,simple=true})
		panel.transformer.visible = true
	end
	if not panel.f.transform then  return end
	if not panel.instance then 
		if panel.transformer then 
			scene.setDelete(panel.transformer) 
		end 
		return 
	end
	local pos = -panel.f.transform * -panel.instance.transform * vec3(0)
	if panel.transformer then 
		panel.transformer.position=-pos
		panel.transformer.orientation = (panel.f.orientation*panel.instance.orientation)
		panel.transformer.scale  = panel.f.scale*panel.instance.scale
	end
	applyTransform(panel.transformer)
end


function editor.addBlankFrame(panel,l)
	panel.pushUndoState() 
	local newFrame = editor.defaultFrame(panel,panel.f)
	local diff = panel.currentFrame-editor.getAbsoluteFrame(panel)
	print("newwwww\n",diff)
	if diff>0 then 
		if diff>panel.frames[panel.frame].l then 
			newFrame.l=1
		else
			newFrame.l = panel.frames[panel.frame].l-diff
		end
		panel.frames[panel.frame].l=diff
		print("newFrame.l",newFrame.l)
	else 
		newFrame.l = l or newFrame.l
	end
	table.insert(panel.frames, panel.frame+1,newFrame)
	panel.frame=panel.frame+1
	applyTransform(panel.frames[panel.frame])
	editor.setAbsoluteFrame(panel)
	editor.tempTip("New Frame")
end

function editor.clampKeys(panel)
	panel.frame 		= (panel.frame>#panel.frames) and 1 or (panel.frame<1 and #panel.frames or panel.frame)
end 



function editor.getKeyFrame(panel,layer,currentFrame)
	local layer 		= layer or panel.layer 
	local currentFrame 	= currentFrame or panel.currentFrame
	layerLength(panel.layers[layer])
	currentFrame 		= panel.layers[layer].loop and (panel.currentFrame-1)%(panel.layers[layer].length)+1 or currentFrame
 	local frameCount 	= 1
 	local frameMark 	= 1
 	for f,frame in ipairs(panel.layers[layer].frames) do 
 		if not panel.layers[panel.layer].loop  and panel.layers[panel.layer].length and currentFrame>panel.layers[panel.layer].length then 
 			frameMark = #panel.layers[layer].frames
		elseif (frameCount+frame.l>=currentFrame and frameCount<=currentFrame) or frameCount==currentFrame   then
			frameMark = f
		end
		frameCount=frameCount+frame.l
	end
	return frameMark
end

function editor.setKeyFrame(panel)
	editor.clampKeys(panel)
	panel.frame = editor.getKeyFrame(panel)
end

function editor.getAbsoluteFrame(panel,layer,keyFrame)
	local layer 	= layer or panel.layer 
	local keyFrame 	= keyFrame or panel.frame
	local absFrame 	= 1
	for f,frame in ipairs(panel.layers[layer].frames) do 
		if f<keyFrame then 
			absFrame = absFrame+frame.l 
		else
			return absFrame
		end
	end
end

function editor.setAbsoluteFrame(panel)
	editor.clampKeys(panel)
	panel.currentFrame = editor.getAbsoluteFrame(panel)
	editor.updateFrameTransformer(panel)
	panel.changed = true
	if panel.currentFrame == 1 or panel.currentFrame == panel.l.length then 
		panel.skipTrack=true
	end
end

function editor.saveAnimData()
end




function editor.drawPreviewToFrame(panel)
	if drawingPanel.tool == "select" then 
	else
		local preBrush =  panel.brushCanvas
		if drawingPanel.brush.texture>0  then
			-- preBrush = copyCanvas(panel.brushCanvas)
			-- editor.drawBrushTexture(panel,preBrush)
		end
		gfx.push("all")
		if pcall(gfx.setCanvas,panel.l.canvas) and pcall(function()preBrush:getWidth()end) then 
			gfx.setBlendMode('alpha')
			gfx.setColor(drawingPanel.brush.color.r,drawingPanel.brush.color.g,drawingPanel.brush.color.b,1)
			-- shaders.aa:send("image_size",{preBrush:getWidth(),preBrush:getHeight()})
			-- gfx.setFilter(preBrush,panel.filter)
			-- gfx.setShader(panel.l.format=="r8" and shaders.r8 or shaders.aa)
			gfx.setShader(shaders.unmul)
			gl.BlendFuncSeparate(gle.SRC_ALPHA,gle.ONE_MINUS_SRC_ALPHA,gle.ONE,gle.ONE_MINUS_SRC_ALPHA)
			gfx.draw(preBrush)
		end
		gfx.pop()
	end
end


function editor.exportAnim(panel)
	-- LOCK EVERYTHING TILL PLAYBACK DONE
	panel.exporting = true
	panel.currentFrame 	=  1
	panel.time = 0
	panel.playing = false 
	panel.expFPS = panel.animation.fps

	editor.playAnimation(60)
	-- SET TARGET CANVAS TO STAGE SIZE
	view=currentRooms[1]
	view.canvas = newCanvas( panel.stageWidth ,panel.stageHeight ,{msaa=panel.antiAlias and panel.antiAliasing or nil})
	-- screenWidth, screenHeight = panel.stageWidth ,panel.stageHeight
	view.bounds.w,view.bounds.h  = panel.stageWidth, panel.stageHeight
	panel.stageAspect = panel.stageWidth/panel.stageHeight
	panel.stageScale = view.bounds.w/animationPanel.stageWidth

	-- SEND FRAME DATA TO ENCODE THREAD
	panel.exportCanvas = newCanvas(
		roundToNearest(panel.stageWidth*panel.exportScale,2),
		roundToNearest(panel.stageHeight*panel.exportScale,2)
	)


	-- WAIT FOR THREAD TO FINISH
	if ZIP_MODE then 
		mkdir(LINK_DIR.."/export")
		lfs.remove(LINK_DIR.."/export/sequence")
	else
		os.execute(os.rm().." \""..GAME_DIR.."/export/sequence/\"")
	end

	setVSync( false )

	mkdir((ZIP_MODE and LINK_DIR or GAME_DIR).."/export/sequence")
	panel.precam = {cam.x,cam.y}

	waitFor(function()
		if not panel.exporting then 
			view.canvas  	 			= newCanvas( renderWidth, 		renderHeight ,{msaa=panel.antiAlias and panel.antiAliasing or nil})
			local fps = panel.expFPS or panel.animation.fps
			local out  = string.format("%s/%s - %s",
				config.projects[CURRENT_PROJECT].dir,CURRENT_PROJECT,  panel.animation.name
			):gsub("//","/")
			local outt = panel.exportFormats.mp4 and out  or string.format("%s/.%s - %s",
				config.projects[CURRENT_PROJECT].dir,CURRENT_PROJECT,  panel.animation.name
			):gsub("//","/")
			local ffmpeg = "cd "..q(PROJ_DIR.."export").." && "
					.. "ffmpeg "
					-- .. "\""..ENGINE_DIR.."source/libs/ffmpeg\" "
			if panel.exportFormats.gif or panel.exportFormats.mp4 then 
				local strink = 
					ffmpeg
					..string.format ("-y -r %i -f image2 -s %ix%i " ,fps ,roundToNearest(panel.stageWidth,2) ,roundToNearest(panel.stageHeight,2))
					.. "-i sequence/frame_%04d."..panel.sequenceFormat.." " 
					.. (panel.animation.audio and "-i \""..PROJ_DIR.."audio/"..panel.animation.audio.."\" -acodec aac" or "")
					.. " -t "..tostring(round(panel.animation.length/fps,2)-0.01).." "
					.. " -vcodec libx264 -crf 1"
					.. " -pix_fmt yuv420p "
					.. "\""..outt..".mp4\""
					print(strink)
					os.execute(strink)
			end

			if panel.exportFormats.gif then 
				os.execute(ffmpeg .."-y -i \""..outt..".mp4\" -filter_complex \"[0:v] palettegen\" palette.png")
				os.execute(ffmpeg .."-y -i \""..outt..".mp4\" -i palette.png -filter_complex \"[0:v][1:v] paletteuse\" \""..out..".gif\"")
				if not panel.exportFormats.mp4 then
					print(os.execute(os.rm()..q(outt..".mp4")))
				end
			end

			if panel.exportFormats.sequence~="none" then 
				local from= PROJ_DIR:gsub("//","/").."export/sequence"
				print(from,out)
				print(os.execute(os.rm()..q(out)))
				print(os.rename(from,out))
			end

			cam.z=-145
			cam.x,cam.y = unpack(panel.precam)
			setVSync( true )
			unlockMouse()
			return true
		end
		return false
	end)
end


function editor.pasteSelectionContents(panel,noUndo)
	if not panel.selection.position then return end

	if panel.selection.strokes then 
		for s,stroke in ipairs(panel.f.strokes) do 
			if panel.selection.strokes[s] and stroke and stroke.line then 
				stroke.line = adjLine(stroke.line,panel.f,panel.selection)
			end
		end
	end
	
	if not (panel.selection.canvas) then return end
	if not noUndo then panel.pushUndoState()  end
	editor.expandCanvas 	(panel,true)
	panel.selection.position = {
		x=panel.selection.position.x+panel.selection.expanded.x,
		y=panel.selection.position.y+panel.selection.expanded.y
	}
	panel.f.canvas:renderTo(function()
		editor.drawSelection(panel) 
	end)
	panel.pasted = true
end

function editor.drawSelection(panel)
	if panel.selection.position then 
		gfx.push("all")
		gfx.setColor(1,1,1,1)
		if panel.l.format == "r8" then 
			gfx.setBlendMode("replace")
			shaders.r8:send("index",-1)
			gfx.setShader(shaders.r8)
		end
		gfx.draw(
			panel.selection.canvas,
			panel.selection.quad,
			 panel.selection.position.x,
			 panel.selection.position.y,
			 panel.selection.angle,
			 panel.selection.scale.x,
			 panel.selection.scale.y,
			 panel.selection.bbox.w/2,
			 panel.selection.bbox.h/2 )
		gfx.pop()
	end

end

function editor.pasteFrame(panel,f)
	if not panel.copiedFrame then return end
	panel.pushUndoState()  
	table.insert(panel.animation.layers[panel.layer].frames,f or panel.frame,lume.merge(editor.defaultFrame(panel,deepcopy(panel.copiedFrame)),{blank=false,canvas=copyCanvas(panel.copiedFrame.canvas)}))
end

function editor.renameAnimation(obj,anim)
	namer:typeString("Rename Animation",animationPanel.anims[anim].name,{x=mouse.x,y=mouse.y},function(x) 

		for i,v in ipairs (contextMenu.items) do 
			if v[2] then 
				if v[2].name==animationPanel.anims[anim].name  then
					contextMenu.items[i][2].name = x
				end
			end
		end
		animationPanel.anims[anim].name = x 

		if objects[obj].animations[anim] then 
			objects[obj].animations[anim].name =x 
		end
	end)
end

function  editor.deleteAnimation(obj,anim)
	objects[obj].anims[anim] = nil
	editor.saveGraphics(animationPanel)
end

function editor.switchAnimation(anim)  
	local panel= animationPanel
	panel.pushUndoState()  
	editor.clearBones(panel)
	panel.changed = true
	panel.anim 			= anim 
	panel.frame 		= 1
	panel.currentFrame 	= 1
	
	panel.animation 	= panel.anims[panel.anim]

	panel.layers 			= panel.animation.layers
	panel.layer 			= mc(1,panel.layer,#panel.layers)
	panel.l 				= panel.layers[panel.layer]
	panel.frames 		= panel.animation.layers[1].frames
	panel.canvas 		= newCanvas(panel.frames[1].w,  panel.frames[1].h,panel.l.format) 
	gfx.setFilter(panel.canvas,panel.filter)
end

function editor.newAnimation(entity,duplicate)
	namer:typeString("new animation","Cut "..(lume.count(animationPanel.anims)+1),entity and entity.render or {x=mouse.x,y=mouse.y},function(name)
		if not animationPanel.open and entity then 
			editor.openAnimationPanel(entity, true)
		end
		animationPanel.pushUndoState()
		newAnimation(name,duplicate)
	end)
end

function newAnimation(name,duplicate,background)
	local panel = animationPanel
	local animID = uuid()
	local newAnim

	if duplicate then 
		newAnim 	= deepcopy(panel.animation)
		newAnim.key = animID
		newAnim.name= name
	else
		newAnim = {
			key 	= animID, 
			name 	= name, 
			fps 	= 24,
			length 	= 1,
			layers 	= {{
				id 		= uuid(),
				clips = {},
				lineWidth = 1,
				visible = true,
				resolution = 1-editor.drawingResolution,
				format="normal",
				loop 	= false,
				length  = 1,
				name 	= "Layer 1",
				line 	= {color = {r=0,g=0,b=0,h=0,s=0,v=0,a=1},width=1},
				fill 	= {color = {r=0,g=0,b=0,h=0,s=0,v=0,a=1}},
				frames 	= {editor.defaultFrame(panel)}
			}},
		}
	end

	panel.anims[animID] = newAnim

	if not background then 
		panel.anim 	 				= animID
		panel.layer  				= 1
		panel.frame  				= 1
		panel.currentFrame 	= 1
		panel.animation 		= panel.anims[animID]
		panel.frames 				= panel.anims[animID].layers[1].frames
		panel.canvas  			= panel.frames[1].canvas
		gfx.setFilter( panel.canvas,panel.filter) 
	end


	return panel.anims[animID]
end


function editor.createAnimation(key)
	-- position camera
	game.enterRoom()
	local save = false
	if #room.objects==0 then 
		mouseLocalDrop 	= {0,0,0}
		local ent 			= editor.drawNewImage  ("camera x")
		level.animEnt 	= ent.uid
		objects[ent.key].animations.default.name = "Cut 1"
		save = true
	end

	waitFor(function()
		local ent = scene.getEntityByID(level.animEnt)
		if objects[ent.key].animations then 
			objects[ent.key].splitFrames = true
			level.stageWidth=level.stageWidth or 1280
			level.stageHeight=level.stageHeight or 720
			level.exportScale=level.exportScale or 1
			editor.openAnimationPanel( ent )
				
			animationPanel.fullWidth=true
			
			if save then 
				editor.saveAnimation(animationPanel)
				editor.save()
			end

			camera.reset(cam,true)
			cam.soom = 1
			editor.debug.trace_mouse_intersect = false
			return true
		end
	end)
end


function editor.contextAnims(entity)
	local anims = objects[entity.key].animations
	local subitems = {}
	local reset = function() entity.models={} log("reset")end
	for a,anim in pairs(anims) do
		table.insert(subitems,{ 
			{	
				a 		= a,
				size 	= 1,
				name 	= "", 
				rollOn = reset, rollOff=reset,
				align 	= "center", 
				func  	= function() 
					entity.default=(entity.default~=a) and a or nil
					entity.models={}
					return "" 
				end,
				update 	= function(x)
					local default = ((entity.default or objects[entity.key].default)==x.a)
					x.col  = (default and entity.default) and {r=0.4,g=0.4,b=0.4,a=0.4} or {}
					x.name = default and "»" or "•"
				end
				,hover= function() editor.animHovered = {key=a,object=entity.uid} end
			},
			{	
				name=anim.name, 
				rollOn = reset, rollOff=reset,
				size=8,	
				func = function() editor.openAnimationPanel( entity , true, a)  end , 
				hover= function() editor.animHovered = {key=a,object=entity.uid} end
			}
		})
	end
	table.insert(subitems, {
		name="New "..(level.isAnim and "Cut" or "Animation"), col={r=0.3,g=0.8,b=0.4,a=0.2} ,func = function()
			editor.newAnimation(entity)
		end
	})
	return subitems
end


function editor.defaultFrame(panel,frame,layer)
	local w,h = panel.defaultSize,panel.defaultSize
	local layer = layer or panel.l
	local frame = {
		blank 			= true,
		canvas  		= (layer.format=="normal" and newCanvas(w,  h,layer.format) or panel.blankCanvas ),
		w 					=  w,
		h 					=  h,
		id          =  uuid(),
		anim 				= frame and frame.anim,
		tween  			= frame and lume.clone(frame.tween or {enabled=false,easeIn=0,easeOut=0}),
		position		= (frame and (frame.position)~=nil) and frame.position:clone() or vec3(0),
		orientation = (frame and (frame.orientation)~=nil) and frame.orientation:clone() or quat(0,1,0,1),
		scale 			= (frame and (frame.scale)~=nil) and frame.scale:clone() or vec3(1),
		ox 					= frame and frame.ox or  0,
		oy 					= frame and frame.oy or  0,
		l 					= frame and frame.l or panel.f.l,
		fills 			= {}
	}
	
	applyTransform(frame)
	return frame
end

function editor.copyFrame(panel,frame,layer)
	local layer = layer or panel.l
	local copy = editor.defaultFrame(panel,frame,layer)
	copy.strokes= copyStrokes(frame.strokes or {})
	copy.canvas = layer.format=="normal" and copyCanvas(frame.canvas) or panel.blankCanvas 
	copy.blank=frame.blank
	copy.id = uuid()
	editor.refreshStrokes()
	return copy
end

