--[[ WARNING! This script requires AE_Utilities and AE_Curvature! http://www.revival.ru/test/ae_curvature.lua ]] -- ************************************************** -- Provide Moho with the name of this script object -- ************************************************** ScriptName = "AE_TransformPoints" -- ************************************************** -- General information about this script -- ************************************************** AE_TransformPoints = {} AE_TransformPoints.BASE_STR = 2375 function AE_TransformPoints:Name() return "Transform Points" end function AE_TransformPoints:Version() return "10.08" end function AE_TransformPoints:IsBeginnerScript() return true end function AE_TransformPoints:Description() return string.format( MOHO.Localize("/Scripts/Tool/TransformPoints/Description=Move/Scale/Rotate selected points (press <%s> to weld, hold <shift> to constrain, <alt> to disable auto-welding, <alt> to scale to center, <shift> while scaling to squash, <ctrl/cmd> to select points)"), MOHO.BindingKeyName() ) end function AE_TransformPoints:BeginnerDescription() return MOHO.Localize("/Scripts/Tool/TransformPoints/BeginnerDescription=Click in the center of your object to select all points, allowing you to move the entire object. If you only select and drag one point or multiple points, you can distort the object. Move points around in time to animate them.") end function AE_TransformPoints:BeginnerDisabledDescription() return MOHO.Localize("/Scripts/Tool/TransformPoints/BeginnerDisabledDescription=You need to create a vector drawing on a Vector Layer first with the 'Add Point', 'Freehand' or 'Draw Shape' tool before being able to use this tool. To add a new vector layer, go to the Layers window and select New Layer > Vector.") end function AE_TransformPoints:Creator() return "Smith Micro Software, Inc., improved by Alexandra Evseeva, modified by Stan" end function AE_TransformPoints:UILabel() return(MOHO.Localize("/Scripts/Tool/TransformPoints/TransformPoints=Transform Points")) end function AE_TransformPoints:LoadPrefs(prefs) self.autoWeld = prefs:GetBool("AE_TransformPoints.autoWeld", false) self.autoFill = prefs:GetBool("AE_TransformPoints.autoFill", true) self.autoStroke = prefs:GetBool("AE_TransformPoints.autoStroke", true) self.showHandles = prefs:GetBool("AE_TransformPoints.showHandles", false) self.fixedHandles = prefs:GetBool("AE_TransformPoints.fixedHandles", false) end function AE_TransformPoints:SavePrefs(prefs) prefs:SetBool("AE_TransformPoints.autoWeld", self.autoWeld) prefs:SetBool("AE_TransformPoints.autoFill", self.autoFill) prefs:SetBool("AE_TransformPoints.autoStroke", self.autoStroke) prefs:SetBool("AE_TransformPoints.showHandles", self.showHandles) prefs:SetBool("AE_TransformPoints.fixedHandles", self.fixedHandles) end function AE_TransformPoints:ResetPrefs() AE_TransformPoints.autoWeld = false AE_TransformPoints.autoWeldRadius = 12 AE_TransformPoints.autoFill = true AE_TransformPoints.autoStroke = true AE_TransformPoints.showHandles = false AE_TransformPoints.fixedHandles = true end function AE_TransformPoints:NonDragMouseMove() return true -- Call MouseMoved() even if the mouse button is not down end -- ************************************************** -- Recurring values -- ************************************************** AE_TransformPoints.dragging = false AE_TransformPoints.keyMovement = false AE_TransformPoints.mode = 0 -- 0:translate, 1:rotate, 2:top left scale, 3:top right scale, 4: bottom left scale, 5: bottom right scale, 6:left scale, 7:right scale, 8:top scale, 9:bottom scale, 10:bezier handle, 100:select points AE_TransformPoints.numSel = 0 AE_TransformPoints.selID = -1 AE_TransformPoints.fillingShape = false AE_TransformPoints.endWeldVec = LM.Vector2:new_local() AE_TransformPoints.endWeldVec:Set(-10000000, -10000000) AE_TransformPoints.endWeldToPoint = true AE_TransformPoints.startAngle = 0 AE_TransformPoints.lastVec = LM.Vector2:new_local() AE_TransformPoints.selectMode = 100 -- ************************************************** -- The guts of this script -- ************************************************** function AE_TransformPoints:IsEnabled(moho) if (moho:CountPoints() > 0) then return true end return false end function AE_TransformPoints:IsRelevant(moho) local mesh = moho:DrawingMesh() if (mesh == nil) then return false end return true end function AE_TransformPoints:SelectedMeshBounds(moho, layer, mesh, frame, view) local bbox = LM.BBox:new_local() local min = LM.Vector2:new_local() local max = LM.Vector2:new_local() mesh:SelectedBounds(min, max) bbox.fMin:Set(min.x, min.y, 0) bbox.fMax:Set(max.x, max.y, 0) -- make sure the bounding box is not too thin in one direction local xLen = bbox.fMax.x - bbox.fMin.x local yLen = bbox.fMax.y - bbox.fMin.y if (xLen < yLen / 10.0) then local center = (bbox.fMin.x + bbox.fMax.x) / 2.0 bbox.fMin.x = center - yLen / 10.0 bbox.fMax.x = center + yLen / 10.0 elseif (yLen < xLen / 10.0) then local center = (bbox.fMin.y + bbox.fMax.y) / 2.0 bbox.fMin.y = center - xLen / 10.0 bbox.fMax.y = center + xLen / 10.0 end -- make sure the bounding box is not too small local minLength = 150 local m = LM.Matrix:new_local() local v = LM.Vector2:new_local() local pt1 = LM.Point:new_local() local pt2 = LM.Point:new_local() layer:GetFullTransform(frame, m, moho.document) v:Set(bbox.fMin.x, bbox.fMin.y) m:Transform(v) view:Graphics():WorldToScreen(v, pt1) v:Set(bbox.fMax.x, bbox.fMax.y) m:Transform(v) view:Graphics():WorldToScreen(v, pt2) pt1 = pt2 - pt1 local length = math.sqrt(pt1.x * pt1.x + pt1.y * pt1.y) if (length < minLength) then center = (bbox.fMin + bbox.fMax) / 2.0 v = bbox.fMax - center bbox.fMax = center + v * (minLength / length) / 2.0 v = bbox.fMin - center bbox.fMin = center + v * (minLength / length) / 2.0 end return bbox end function AE_TransformPoints:TestMousePoint(moho, mouseEvent) -- Returns what mode the tool would be in if the user clicked at the current mouse location if (self.keyMovement) then return 0 end local mesh = moho:DrawingMesh() if (mesh == nil) then return 0 end self.numSel = moho:CountSelectedPoints() if (self.numSel < 2) then return 0 end local markerR = 4 local bbox = self:SelectedMeshBounds(moho, moho.drawingLayer, mesh, moho.drawingFrame, mouseEvent.view) local v = LM.Vector2:new_local() local pt = LM.Point:new_local() local m = LM.Matrix:new_local() moho.drawingLayer:GetFullTransform(moho.frame, m, moho.document) -- test for uniform scaling v:Set(bbox.fMin.x, bbox.fMin.y) m:Transform(v) mouseEvent.view:Graphics():WorldToScreen(v, pt) if (math.abs(pt.x - mouseEvent.pt.x) < markerR and math.abs(pt.y - mouseEvent.pt.y) < markerR) then -- bottom left return 4 end v:Set(bbox.fMin.x, bbox.fMax.y) m:Transform(v) mouseEvent.view:Graphics():WorldToScreen(v, pt) if (math.abs(pt.x - mouseEvent.pt.x) < markerR and math.abs(pt.y - mouseEvent.pt.y) < markerR) then -- top left return 2 end v:Set(bbox.fMax.x, bbox.fMax.y) m:Transform(v) mouseEvent.view:Graphics():WorldToScreen(v, pt) if (math.abs(pt.x - mouseEvent.pt.x) < markerR and math.abs(pt.y - mouseEvent.pt.y) < markerR) then -- top right return 3 end v:Set(bbox.fMax.x, bbox.fMin.y) m:Transform(v) mouseEvent.view:Graphics():WorldToScreen(v, pt) if (math.abs(pt.x - mouseEvent.pt.x) < markerR and math.abs(pt.y - mouseEvent.pt.y) < markerR) then -- bottom right return 5 end -- test for X scaling v:Set(bbox.fMin.x, (bbox.fMin.y + bbox.fMax.y) * 0.5) m:Transform(v) mouseEvent.view:Graphics():WorldToScreen(v, pt) if (math.abs(pt.x - mouseEvent.pt.x) < markerR and math.abs(pt.y - mouseEvent.pt.y) < markerR) then -- left return 6 end v:Set(bbox.fMax.x, (bbox.fMin.y + bbox.fMax.y) * 0.5) m:Transform(v) mouseEvent.view:Graphics():WorldToScreen(v, pt) if (math.abs(pt.x - mouseEvent.pt.x) < markerR and math.abs(pt.y - mouseEvent.pt.y) < markerR) then -- right return 7 end -- test for Y scaling v:Set((bbox.fMin.x + bbox.fMax.x) * 0.5, bbox.fMin.y) m:Transform(v) mouseEvent.view:Graphics():WorldToScreen(v, pt) if (math.abs(pt.x - mouseEvent.pt.x) < markerR and math.abs(pt.y - mouseEvent.pt.y) < markerR) then -- bottom return 9 end v:Set((bbox.fMin.x + bbox.fMax.x) * 0.5, bbox.fMax.y) m:Transform(v) mouseEvent.view:Graphics():WorldToScreen(v, pt) if (math.abs(pt.x - mouseEvent.pt.x) < markerR and math.abs(pt.y - mouseEvent.pt.y) < markerR) then -- top return 8 end -- test for translation outside the bounding box local rotWidth = bbox.fMax.x - bbox.fMin.x if (bbox.fMax.y - bbox.fMin.y > rotWidth) then rotWidth = bbox.fMax.y - bbox.fMin.y end rotWidth = rotWidth * 0.1 if (mouseEvent.drawingVec.x < bbox.fMin.x - rotWidth or mouseEvent.drawingVec.x > bbox.fMax.x + rotWidth or mouseEvent.drawingVec.y < bbox.fMin.y - rotWidth or mouseEvent.drawingVec.y > bbox.fMax.y + rotWidth) then return 0 end -- test for rotation if (mouseEvent.drawingVec.x < bbox.fMin.x or mouseEvent.drawingVec.x > bbox.fMax.x or mouseEvent.drawingVec.y < bbox.fMin.y or mouseEvent.drawingVec.y > bbox.fMax.y) then return 1 end return 0 -- translation inside the bounding box end function AE_TransformPoints:OnMouseDown(moho, mouseEvent) local mesh = moho:DrawingMesh() if (mesh == nil) then return end self:PopulateSelList(moho, mesh) if (mouseEvent.altKey) and (mouseEvent.shiftKey) then if not self.customCenter then self.customCenter = mesh:SelectedCenter() end self.customCenter:Set(mouseEvent.drawingStartVec) if self.centerMode then self.centerVec:Set(self.customCenter) end return end self.dragging = true self.endWeldVec:Set(-10000000, -10000000) self.mode = self:TestMousePoint(moho, mouseEvent) if (mouseEvent.ctrlKey) then self.mode = self.selectMode mouseEvent.ctrlKey = false LM_SelectPoints:OnMouseDown(moho, mouseEvent) return end if not self.centerMode then self.centerVec:Set(mesh:SelectedCenter()) else self.centerVec:Set(self.customCenter) end self.selectedMin = LM.Vector2:new_local() self.selectedMax = LM.Vector2:new_local() mesh:SelectedBounds(self.selectedMin, self.selectedMax) moho.document:SetDirty() self.numSel = moho:CountSelectedPoints() if (self.selID == -1) then -- if this isn't true, then a point has already been selected (maybe by the add point tool) if (self.mode == 0 and self.numSel < 2 and self.showHandles) then AE_Curvature:TestForClosestHandle(moho, mouseEvent) if (AE_Curvature.selID >= 0 and AE_Curvature.handleSide ~= 0) then -- a bezier handle was selected self.mode = 10 local point = mesh:Point(AE_Curvature.selID) if (point:IsEndpoint()) then -- If working with a curve endpoint, and its handles have not been extended, don't do it with this tool. -- It's preferable to just move the endpoint. -- If the user wants to move the handle, they must use the curvature tool. local curve = nil local ptPos = -1 curve, ptPos = point:Curve(0, ptPos) local weight = curve:GetWeight(ptPos, moho.drawingFrame, AE_Curvature.handleSide == -1) local offset = curve:GetOffset(ptPos, moho.drawingFrame, AE_Curvature.handleSide == -1) local wDiff = weight - 1.0 local oDiff = offset - 0.0 if (math.abs(wDiff) < 0.0001 and math.abs(oDiff) < 0.0001 and curve:CountPoints() > 2) then self.mode = 0 end end end self.selID = -1 self.handleSide = 0 end moho.document:PrepUndo(moho.drawingLayer) end if (self.mode == 0) then -- translate if (self.selID == -1) then local shiftKey = mouseEvent.shiftKey local ctrlKey = mouseEvent.ctrlKey local altKey = mouseEvent.altKey mouseEvent.shiftKey = false mouseEvent.ctrlKey = false mouseEvent.altKey = false mouseEvent.shiftKey = altKey mouseEvent.ctrlKey = altKey mouseEvent.altKey = altKey self.numSel = moho:CountSelectedPoints(true) if (self.numSel < 2) then -- move just a single point -- find the closest point here self.selID = mesh:ClosestPoint(mouseEvent.drawingStartVec) if (self.selID >= 0) then self.numSel = 1 mesh:SelectNone() mesh:Point(self.selID).fSelected = true moho:UpdateSelectedChannels() end else self.selID = mesh:ClosestPoint(mouseEvent.drawingStartVec) end end elseif (self.mode == 1) then -- rotate local shiftKey = mouseEvent.shiftKey local ctrlKey = mouseEvent.ctrlKey local altKey = mouseEvent.altKey mouseEvent.shiftKey = false mouseEvent.ctrlKey = false mouseEvent.altKey = false mouseEvent.shiftKey = altKey mouseEvent.ctrlKey = altKey mouseEvent.altKey = altKey self.numSel = moho:CountSelectedPoints(true) self.startAngle = 0 self.lastVec:Set(mouseEvent.drawingVec) elseif (self.mode == 10) then -- bezier handle AE_Curvature:OnMouseDown(moho, mouseEvent) return else -- scale self.lastScaleX = 1.0 self.lastScaleY = 1.0 if (self.numSel < 2) then local shiftKey = mouseEvent.shiftKey local ctrlKey = mouseEvent.ctrlKey local altKey = mouseEvent.altKey mouseEvent.shiftKey = false mouseEvent.ctrlKey = false mouseEvent.altKey = false mouseEvent.shiftKey = altKey mouseEvent.ctrlKey = altKey mouseEvent.altKey = altKey self.numSel = moho:CountSelectedPoints(true) end if (self.numSel < 2) then mouseEvent.view:DrawMe() return end end mesh:PrepMovePoints() if (self:UseFixedHandles(moho)) then mesh:PrepFixedHandles() self:FixHandles(moho, mesh) end self:PopulateSelList(moho, mesh) mouseEvent.view:DrawMe() end function AE_TransformPoints:PopulateSelList(moho, mesh) self.fPoses = {} self.selList = {} self.inOffsets = {} self.outOffsets = {} for i = 0, mesh:CountPoints() - 1 do local pt = mesh:Point(i) if pt.fSelected then table.insert(self.selList, pt) table.insert(self.fPoses, LM.Vector2:new_local()) local ins = {} local outs = {} for c = 0, pt:CountCurves() -1 do local curve, where = pt:Curve(c) ins[c] = curve:GetOffset(where, moho.drawingFrame, true) outs[c] = curve:GetOffset(where, moho.drawingFrame, false) end table.insert(self.inOffsets, ins) table.insert(self.outOffsets, outs) end end end function AE_TransformPoints:OnMouseMoved(moho, mouseEvent) local mesh = moho:DrawingMesh() if (mesh == nil) then return end if (mouseEvent.altKey) and (mouseEvent.shiftKey) then return end if (not self.dragging) then local mode = self:TestMousePoint(moho, mouseEvent) if (mode == 0) then mouseEvent.view:SetCursor(MOHO.moveCursor) elseif (mode == 1) then mouseEvent.view:SetCursor(MOHO.rotateCursor) else mouseEvent.view:SetCursor(MOHO.scaleCursor) end mouseEvent.view:DrawMe() return end if (self.mode == self.selectMode) then mouseEvent.ctrlKey = false LM_SelectPoints:OnMouseMoved(moho, mouseEvent) elseif (self.mode == 0) then -- translate self:OnMouseMoved_T(moho, mouseEvent) elseif (self.mode == 1) then -- rotate self:OnMouseMoved_R(moho, mouseEvent) elseif (self.mode == 10) then -- bezier handle AE_Curvature:OnMouseMoved(moho, mouseEvent) return else -- scale self:OnMouseMoved_S(moho, mouseEvent) end --[[ for i, pt in pairs(self.selList) do self.fPoses[i] = pt.fPos end --]] end function AE_TransformPoints:OnMouseMoved_T(moho, mouseEvent) local mesh = moho:DrawingMesh() if (mesh == nil) then return end if (mouseEvent.ctrlKey) then -- hold down the control key to just select a single point and not move it return end self.endWeldVec:Set(-10000000, -10000000) local curVec = mouseEvent.drawingVec - mouseEvent.drawingStartVec if (mouseEvent.shiftKey) then if (math.abs(mouseEvent.drawingVec.x - mouseEvent.drawingStartVec.x) > math.abs(mouseEvent.drawingVec.y - mouseEvent.drawingStartVec.y)) then curVec.y = 0 else curVec.x = 0 end end if (self.numSel < 2) then -- just working with 1 point if (self.selID == -1) then return end local pt = mesh:Point(self.selID) pt.fPos:Set(pt.fTempPos + curVec) if (moho.gridOn) then moho:SnapToGrid(pt.fPos) end if (not(mouseEvent.altKey) and self.autoWeld and pt:IsEndpoint()) then -- Display the upcoming weld point for the user's benefit local m = LM.Matrix:new_local() local testVec1 = LM.Vector2:new_local() local p1 = LM.Point:new_local() local testVec2 = LM.Vector2:new_local() local p2 = LM.Point:new_local() local dist = 0 local curveID = -1 local segID = -1 local pickWidth = 3 self.endWeldVec:Set(-10000000, -10000000) testVec1:Set(pt.fPos) moho.drawingLayer:GetFullTransform(moho.frame, m, moho.document) m:Transform(testVec1) moho.view:Graphics():WorldToScreen(testVec1, p1) -- first try to find another point to weld this one to local closestID = mesh:ClosestPoint(pt.fPos, self.selID) if (closestID >= 0) then testVec2:Set(mesh:Point(closestID).fPos) m:Transform(testVec2) moho.view:Graphics():WorldToScreen(testVec2, p2) dist = (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) if (dist < self.autoWeldRadius * self.autoWeldRadius and (not mesh:ArePointsAdjacent(self.selID, closestID))) then self.endWeldToPoint = true self.endWeldVec:Set(mesh:Point(closestID).fPos) else while (pickWidth < self.autoWeldRadius) do curveID, segID = pt:GetEndpointEdge(curveID, segID) curveID, segID = moho.view:PickEdge(p1, curveID, segID, pickWidth) if (curveID >= 0) then -- add a point in the middle of some curve if ((not mesh:Curve(curveID):IsPointOnSegment(self.selID, segID)) and (not mesh:Curve(curveID):IsPointOnSegment(self.selID, segID - 1)) and (not mesh:Curve(curveID):IsPointOnSegment(self.selID, segID + 1))) then -- don't weld the point back on itself self.endWeldToPoint = false self.endWeldVec:Set(mesh:Curve(curveID):ClosestPointOnSegment(segID, pt.fPos, true, true)) break end end pickWidth = pickWidth + 3 end -- while end end end else -- move multiple points if (moho.gridOn) then if (self.selID > -1) then local pt = mesh:Point(self.selID) local tempVec = pt.fTempPos + curVec moho:SnapToGrid(tempVec) curVec:Set(tempVec - pt.fTempPos) else moho:SnapToGrid(curVec) end end mesh:TranslatePoints(curVec) end --TODO:apply all final calculations here, to use onion skin (uncomment next line and call a universal method to fix delta) --moho:AddPointKeyframe(moho.drawingFrame) self:AddPointKeyframe(moho, moho.drawingFrame) if (self:UseFixedHandles(moho)) then mesh:PreserveHandlePositions() end mouseEvent.view:DrawMe() end function AE_TransformPoints:OnMouseMoved_S(moho, mouseEvent) if (self.numSel < 2) then return end local mesh = moho:DrawingMesh() if (mesh == nil) then return end local center = LM.Vector2:new_local() center:Set(self.centerVec) if (mouseEvent.altKey) then center:Set(self.centerVec) else if (self.mode == 6) then -- LEFT center:Set(self.selectedMax.x, (self.selectedMin.y + self.selectedMax.y) / 2.0) elseif (self.mode == 7) then -- RIGHT center:Set(self.selectedMin.x, (self.selectedMin.y + self.selectedMax.y) / 2.0) elseif (self.mode == 8) then -- TOP center:Set((self.selectedMin.x + self.selectedMax.x) / 2.0, self.selectedMin.y) elseif (self.mode == 9) then -- BOTTOM center:Set((self.selectedMin.x + self.selectedMax.x) / 2.0, self.selectedMax.y) elseif (self.mode == 4) then -- BL center:Set(self.selectedMax.x, self.selectedMax.y) elseif (self.mode == 2) then -- TL center:Set(self.selectedMax.x, self.selectedMin.y) elseif (self.mode == 3) then -- TR center:Set(self.selectedMin.x, self.selectedMin.y) elseif (self.mode == 5) then -- BR center:Set(self.selectedMin.x, self.selectedMax.y) end end local scaling = LM.Vector2:new_local() scaling:Set(1, 1) -- scaling connected to actual drag amount local v1 = mouseEvent.drawingStartVec - center local v2 = mouseEvent.drawingVec - center scaling.x = v2.x / v1.x scaling.y = v2.y / v1.y if (self.mode == 6 or self.mode == 7) then -- horizontal scaling scaling.y = 1 if (mouseEvent.shiftKey) then scaling.y = 1 / scaling.x end elseif (self.mode == 8 or self.mode == 9) then -- vertical scaling scaling.x = 1 if (mouseEvent.shiftKey) then scaling.x = 1 / scaling.y end else if (not mouseEvent.shiftKey) then scaling.x = (scaling.x + scaling.y) / 2 scaling.y = scaling.x end end local flip = false if (scaling.x * self.lastScaleX < -0.0001) then if (scaling.y * self.lastScaleY > 0.0001) then flip = true end elseif (scaling.y * self.lastScaleY < -0.0001) then if (scaling.x * self.lastScaleX > 0.0001) then flip = true end end mesh:ScalePoints(scaling.x, scaling.y, center, flip) --[[--]] --offset correction local k = 1 if scaling.x * scaling.y < 0 then k = -1 end self:FixOffsets(moho, mesh, k) if (flip) then self.lastScaleX = scaling.x self.lastScaleY = scaling.y end --TODO:apply all final calculations here, to use onion skin (uncomment next line and call a universal method to fix delta) --moho:AddPointKeyframe(moho.drawingFrame) self:AddPointKeyframe(moho, moho.drawingFrame) if (self:UseFixedHandles(moho)) then mesh:PreserveHandlePositions() end mouseEvent.view:DrawMe() end function AE_TransformPoints:OnMouseMoved_R(moho, mouseEvent) if (self.numSel < 2) then return end local mesh = moho:DrawingMesh() if (mesh == nil) then return end local angle = self.startAngle local v1 = self.lastVec - self.centerVec local v2 = mouseEvent.drawingVec - self.centerVec v2:Rotate(-math.atan2(v1.y, v1.x)) angle = angle + math.atan2(v2.y, v2.x) self.startAngle = angle if (mouseEvent.shiftKey) then angle = angle / (math.pi / 4) angle = (math.pi / 4) * LM.Round(angle) end mesh:RotatePoints(angle, self.centerVec) --TODO:apply all final calculations here, to use onion skin (uncomment next line and call a universal method to fix delta) --moho:AddPointKeyframe(moho.drawingFrame) self:AddPointKeyframe(moho, moho.drawingFrame) self.lastVec:Set(mouseEvent.drawingVec) if (self:UseFixedHandles(moho)) then mesh:PreserveHandlePositions() end mouseEvent.view:DrawMe() end function AE_TransformPoints:OnMouseUp(moho, mouseEvent) if (mouseEvent.altKey) and (mouseEvent.shiftKey) then return end if (self.mode == self.selectMode) then mouseEvent.ctrlKey = false LM_SelectPoints:OnMouseUp(moho, mouseEvent) self.dragging = false self.selID = -1 mouseEvent.view:DrawMe() return end local mesh = moho:DrawingMesh() if (mesh == nil) then return end -- if we're just moving a single point, then try to auto-weld it if (self.mode == 0 and self.selID >= 0 and self.numSel == 1 and self.autoWeld and not(mouseEvent.altKey) and moho.drawingLayer:CurrentAction() == "" and not(moho:DisableDrawingTools())) then if (mesh:Point(self.selID):IsEndpoint()) then if (not AE_TransformPoints:WeldPoints(moho, mouseEvent.view, self.dragging) and self.selID >= 0) then -- Failed to weld this endpoint to another point. -- Instead, try welding it to the middle of a nearby curve. local m = LM.Matrix:new_local() local v = LM.Vector2:new_local() local pt = LM.Point:new_local() local curveID = -1 local segID = -1 local pickWidth = 3 moho.drawingLayer:GetFullTransform(moho.frame, m, moho.document) v:Set(mesh:Point(self.selID).fPos) m:Transform(v) mouseEvent.view:Graphics():WorldToScreen(v, pt) while (pickWidth < self.autoWeldRadius) do curveID, segID = mesh:Point(self.selID):GetEndpointEdge(curveID, segID) curveID, segID = moho.view:PickEdge(pt, curveID, segID, pickWidth) if (curveID >= 0) then -- add a point in the middle of some curve if ((not mesh:Curve(curveID):IsPointOnSegment(self.selID, segID)) and (not mesh:Curve(curveID):IsPointOnSegment(self.selID, segID - 1)) and (not mesh:Curve(curveID):IsPointOnSegment(self.selID, segID + 1))) then -- don't weld the point back on itself local curve = mesh:Curve(curveID) mesh:AddPoint(curve:ClosestPointOnSegment(segID, mesh:Point(self.selID).fPos, true, true), curveID, segID, moho.drawingLayerFrame) if (mesh:WeldPoints(self.selID, mesh:CountPoints() - 1, moho.drawingLayerFrame)) then moho:Click() if (self.autoFill or self.autoStroke) then self:CreateShape(moho, mesh, mesh:CountPoints() - 1, self.autoFill) end end break end end pickWidth = pickWidth + 3 end end end end self.dragging = false if (self.mode == 10) then -- bezier handle AE_Curvature:OnMouseUp(moho, mouseEvent) else --TODO: move this into separated method, something like self:StoreFPoses() or --self:SaveAddPointKeyframe(frame, layer, allSelectedKeys) --[[ for i, pt in pairs(self.selList) do self.fPoses[i]:Set(pt.fPos) --print(i, ": ", pt.fPos.x, ", ", pt.fPos.y) end moho:AddPointKeyframe(moho.drawingFrame, nil, MOHO.MohoGlobals.EditMultipleKeys) for i, pt in pairs(self.selList) do --print(i, ": ", pt.fPos.x, ", ", pt.fPos.y) --print("saved: ", i, ": ", self.fPoses[i].x, ", ", self.fPoses[i].y) local delta = self.fPoses[i] - pt.fPos pt.fPos = self.fPoses[i] + delta end moho:AddPointKeyframe(moho.drawingFrame, nil, MOHO.MohoGlobals.EditMultipleKeys) --]] self:AddPointKeyframe(moho, moho.drawingFrame, nil, MOHO.MohoGlobals.EditMultipleKeys) if (self:UseFixedHandles(moho)) then self:RepairFixedHandles(moho, mesh) end --TODO: end of area to replace --next line is commented out for smartbone-safe version too, but I do not remember the reason --moho:NewKeyframe(CHANNEL_POINT) moho:UpdateUI() end self.selID = -1 if (mouseEvent.pt.x == mouseEvent.startPt.x and mouseEvent.pt.y == mouseEvent.startPt.y) then --#34389 - allow shift key to pass to select points so extended selections are possible --mouseEvent.shiftKey = false mouseEvent.ctrlKey = false mouseEvent.altKey = false LM_SelectPoints:SingleClickSelect(moho, mouseEvent, false, true) end end function AE_TransformPoints:AddPointKeyframe(moho, frame, layer, allSelectedKeys) for i, pt in pairs(self.selList) do self.fPoses[i]:Set(pt.fPos) --print(i, ": ", pt.fPos.x, ", ", pt.fPos.y) end moho:AddPointKeyframe(frame, layer, allSelectedKeys) for i, pt in pairs(self.selList) do --print(i, ": ", pt.fPos.x, ", ", pt.fPos.y) --print("saved: ", i, ": ", self.fPoses[i].x, ", ", self.fPoses[i].y) local delta = self.fPoses[i] - pt.fPos pt.fPos = self.fPoses[i] + delta end moho:AddPointKeyframe(frame, layer, allSelectedKeys) end function AE_TransformPoints:FixHandles(moho, mesh) self.bezierList = {} for c=0, mesh:CountCurves()-1 do local nextCurveArray = {} for p=0, mesh:Curve(c):CountPoints()-1 do local preVect = mesh:Curve(c):GetControlHandle(p, moho.drawingLayerFrame, true) local postVect = mesh:Curve(c):GetControlHandle(p, moho.drawingLayerFrame, false) local pointArray = {pre = preVect, post = postVect} nextCurveArray[p] = pointArray end self.bezierList[c] = nextCurveArray end end function AE_TransformPoints:RepairFixedHandles(moho, mesh) if not self.bezierList then return end local precision = 0.0000001 for k,v in pairs(self.bezierList) do local nextCurve = mesh:Curve(k) if not nextCurve then return end for n,p in pairs(v) do local point = nextCurve:Point(n) if not point then return end local preVect = nextCurve:GetControlHandle(n, moho.drawingLayerFrame, true) local postVect = nextCurve:GetControlHandle(n, moho.drawingLayerFrame, false) if ((p.pre - preVect):Mag()>precision) or ((p.post - postVect):Mag()>precision) then --if handles changed lets try to fix it ---- get angle and scale to apply ---- or sum action influences and subtract it from current keys -- (see curvature for details) end end end end function AE_TransformPoints:OnKeyDown(moho, keyEvent) local mesh = moho:DrawingMesh() if (mesh == nil) then return end if keyEvent.key == "0" and keyEvent.ctrlKey then self.customCenter:Set(mesh:SelectedCenter()) keyEvent.view:DrawMe() return end if (keyEvent.keyCode == LM.GUI.KEY_BIND) then local doit = false if (self.dragging) then if (self.numSel == 1) then doit = true end else if (moho:CountSelectedPoints() == 1) then doit = true end end if (doit) then -- try welding the selected point to the nearest other point if (not AE_TransformPoints:WeldPoints(moho, keyEvent.view, self.dragging)) then -- Failed to weld this endpoint to another point. -- Instead, try welding it to the middle of a nearby curve. local selID = -1 for i = 0, mesh:CountPoints() - 1 do if (mesh:Point(i).fSelected) then selID = i break end end if (selID >= 0) then local m = LM.Matrix:new_local() local v = LM.Vector2:new_local() local pt = LM.Point:new_local() local curveID = -1 local segID = -1 local pickWidth = 3 moho.drawingLayer:GetFullTransform(moho.frame, m, moho.document) v:Set(mesh:Point(selID).fPos) m:Transform(v) keyEvent.view:Graphics():WorldToScreen(v, pt) while (pickWidth < self.autoWeldRadius) do curveID, segID = mesh:Point(selID):GetEndpointEdge(curveID, segID) curveID, segID = moho.view:PickEdge(pt, curveID, segID, pickWidth) if (curveID >= 0) then -- add a point in the middle of some curve if ((not mesh:Curve(curveID):IsPointOnSegment(selID, segID))) then -- don't weld the point back on itself local curve = mesh:Curve(curveID) mesh:AddPoint(curve:ClosestPointOnSegment(segID, mesh:Point(selID).fPos, true, true), curveID, segID, moho.drawingLayerFrame) if (mesh:WeldPoints(selID, mesh:CountPoints() - 1, moho.drawingLayerFrame)) then moho:Click() if (self.autoFill or self.autoStroke) then self:CreateShape(moho, mesh, mesh:CountPoints() - 1, self.autoFill) end end break end end pickWidth = pickWidth + 3 end end else self.selID = -1 self.numSel = -1 --mesh:SelectNone() end keyEvent.view:DrawMe() end elseif ((keyEvent.keyCode == LM.GUI.KEY_DELETE) or (keyEvent.keyCode == LM.GUI.KEY_BACKSPACE)) then if (not self.dragging) then moho.document:PrepUndo(moho.drawingLayer) moho.document:SetDirty() MOHO.DeleteSelectedPoints(mesh) keyEvent.view:DrawMe() end elseif (keyEvent.ctrlKey) then local inc = 1 if (keyEvent.shiftKey) then inc = 10 end local m = LM.Matrix:new_local() moho.drawingLayer:GetFullTransform(moho.frame, m, moho.document) local fakeME = {} fakeME.view = keyEvent.view fakeME.pt = LM.Point:new_local() fakeME.pt:Set(keyEvent.view:Graphics():Width() / 2, keyEvent.view:Graphics():Height() / 2) fakeME.startPt = LM.Point:new_local() fakeME.startPt:Set(fakeME.pt) fakeME.vec = keyEvent.view:Point2Vec(fakeME.pt, m) fakeME.drawingVec = keyEvent.view:Point2Vec(fakeME.pt, m) fakeME.drawingStartVec = keyEvent.view:Point2Vec(fakeME.pt, m) fakeME.shiftKey = false fakeME.ctrlKey = false fakeME.altKey = keyEvent.altKey fakeME.penPressure = 0 fakeME.doubleClick = false fakeME.eraser = false self.keyMovement = true if (keyEvent.keyCode == LM.GUI.KEY_UP) then moho.document:PrepUndo(MOHO.Localize("/Scripts/Tool/TransformPoints/TransformPointsUndoAction=Transform Point"), moho.drawingLayer) self.selID = self:SelIDForNudge(moho, mesh) if (self.selID == -2) then self.selID = -1 return end self:OnMouseDown(moho, fakeME) fakeME.pt.y = fakeME.pt.y - inc fakeME.vec = keyEvent.view:Point2Vec(fakeME.pt, m) fakeME.drawingVec = keyEvent.view:Point2Vec(fakeME.pt, m) self:OnMouseMoved(moho, fakeME) self:OnMouseUp(moho, fakeME) elseif (keyEvent.keyCode == LM.GUI.KEY_DOWN) then moho.document:PrepUndo(MOHO.Localize("/Scripts/Tool/TransformPoints/TransformPointsUndoAction=Transform Point"), moho.drawingLayer) self.selID = self:SelIDForNudge(moho, mesh) if (self.selID == -2) then self.selID = -1 return end self:OnMouseDown(moho, fakeME) fakeME.pt.y = fakeME.pt.y + inc fakeME.vec = keyEvent.view:Point2Vec(fakeME.pt, m) fakeME.drawingVec = keyEvent.view:Point2Vec(fakeME.pt, m) self:OnMouseMoved(moho, fakeME) self:OnMouseUp(moho, fakeME) elseif (keyEvent.keyCode == LM.GUI.KEY_LEFT) then moho.document:PrepUndo(MOHO.Localize("/Scripts/Tool/TransformPoints/TransformPointsUndoAction=Transform Point"), moho.drawingLayer) self.selID = self:SelIDForNudge(moho, mesh) if (self.selID == -2) then self.selID = -1 return end self:OnMouseDown(moho, fakeME) fakeME.pt.x = fakeME.pt.x - inc fakeME.vec = keyEvent.view:Point2Vec(fakeME.pt, m) fakeME.drawingVec = keyEvent.view:Point2Vec(fakeME.pt, m) self:OnMouseMoved(moho, fakeME) self:OnMouseUp(moho, fakeME) elseif (keyEvent.keyCode == LM.GUI.KEY_RIGHT) then moho.document:PrepUndo(MOHO.Localize("/Scripts/Tool/TransformPoints/TransformPointsUndoAction=Transform Point"), moho.drawingLayer) self.selID = self:SelIDForNudge(moho, mesh) if (self.selID == -2) then self.selID = -1 return end self:OnMouseDown(moho, fakeME) fakeME.pt.x = fakeME.pt.x + inc fakeME.vec = keyEvent.view:Point2Vec(fakeME.pt, m) fakeME.drawingVec = keyEvent.view:Point2Vec(fakeME.pt, m) self:OnMouseMoved(moho, fakeME) self:OnMouseUp(moho, fakeME) end self.keyMovement = false end end function AE_TransformPoints:OnInputDeviceEvent(moho, deviceEvent) local mesh = moho:DrawingMesh() if (mesh == nil) then return false end if (moho:CountSelectedPoints() < 1) then return false end if (deviceEvent.inputData:GetString("DeviceType") == "Wacom Multitouch") then local mtState = deviceEvent.inputData:GetInt("MultitouchState") if (mtState == 1) then -- first finger down self.mtFingersTouching = 0 -- we'll get the correct number on the next event self.mtTranslate = LM.Vector2:new_local() self.mtStartVec = LM.Vector2:new_local() self.mtAccumTranslate = LM.Vector2:new_local() self.mtScale = 1.0 self.mtStartScale = 1.0 self.mtAccumScale = 1.0 self.mtAngle = 0.0 self.mtStartAngle = 0.0 self.mtAccumAngle = 0.0 moho.document:SetDirty() moho.document:PrepUndo(moho.drawingLayer) self.numSel = moho:CountSelectedPoints() mesh:PrepMovePoints() if (self:UseFixedHandles(moho)) then mesh:PrepFixedHandles() end self.centerVec = mesh:SelectedCenter() if self.centerMode then self.centerVec:Set(self.customCenter) end self.mtTranslate:Set(0.0, 0.0) self.mtScale = 1.0 self.mtAngle = 0.0 elseif (mtState == 3) then -- last finger up self.mtFingersTouching = 0 moho:AddPointKeyframe(moho.drawingFrame, nil, MOHO.MohoGlobals.EditMultipleKeys) moho:NewKeyframe(CHANNEL_POINT) moho:UpdateUI() elseif (mtState == 2) then -- dragging local fingersTouching = deviceEvent.inputData:GetInt("FingersTouching") if (fingersTouching ~= self.mtFingersTouching) then self.mtAccumTranslate = self.mtAccumTranslate + self.mtTranslate self.mtAccumScale = self.mtAccumScale * self.mtScale self.mtAccumAngle = self.mtAccumAngle + self.mtAngle self.mtFingersTouching = fingersTouching self.mtStartVec = deviceEvent.inputData:GetVector2("MultitouchCenterVector") self.mtStartAngle = deviceEvent.inputData:GetFloat("MultitouchAngle") self.mtStartScale = deviceEvent.inputData:GetFloat("MultitouchScale") end if (deviceEvent.inputData:GetBool("DoubleTouch")) then if (fingersTouching == 2) then self:HandleMessage(moho, deviceEvent.view, self.RESET) if (frame ~= moho.frame) then moho:SetCurFrame(moho.frame) -- force a refresh when editing a key at a different frame end moho.document:DepthSort() end return true end self.mtTranslate = deviceEvent.inputData:GetVector2("MultitouchCenterVector") - self.mtStartVec if (deviceEvent.shiftKey) then if (math.abs(self.mtTranslate.x) > math.abs(self.mtTranslate.y)) then self.mtTranslate.y = 0 else self.mtTranslate.x = 0 end end self.mtScale = 1.0 self.mtAngle = 0.0 if (self.mtFingersTouching > 1) then self.mtAngle = deviceEvent.inputData:GetFloat("MultitouchAngle") - self.mtStartAngle self.mtScale = deviceEvent.inputData:GetFloat("MultitouchScale") / self.mtStartScale end mesh:TransformPoints(self.mtAccumTranslate + self.mtTranslate, self.mtAccumScale * self.mtScale, self.mtAccumScale * self.mtScale, self.mtAccumAngle + self.mtAngle, self.centerVec) if (self:UseFixedHandles(moho)) then mesh:PreserveHandlePositions() end moho:AddPointKeyframe(moho.drawingFrame) end return true end return false end function AE_TransformPoints:DrawMe(moho, view) if (moho:IsPlaying()) then return end if (self.mode == self.selectMode and self.dragging) then LM_SelectPoints:DrawMe(moho, view) elseif (self.fillingShape) then local g = view:Graphics() local matrix = LM.Matrix:new_local() moho.drawingLayer:GetFullTransform(moho.frame, matrix, moho.document) g:Push() g:ApplyMatrix(matrix) view:DrawPreviewShape() g:Pop() elseif (self.dragging and self.numSel == 1) then if (moho.drawingLayer:CurrentAction() ~= "" or moho:DisableDrawingTools()) then return -- welding in the middle of an action can lead to unexpected results end local g = view:Graphics() local matrix = LM.Matrix:new_local() moho.drawingLayer:GetFullTransform(moho.frame, matrix, moho.document) g:Push() g:ApplyMatrix(matrix) if (self.endWeldToPoint) then g:SetColor(0, 255, 0, 128) else g:SetColor(255, 0, 0, 128) end g:SetSmoothing(true) g:SetBezierTolerance(2) g:FillCircle(self.endWeldVec, moho:PixelToDoc(self.autoWeldRadius) / g:CurrentScale(false)) g:SetSmoothing(false) g:Pop() elseif (not self.dragging) then local mesh = moho:DrawingMesh() if (mesh == nil) then return end if (self.centerVec == nil) then self.centerVec = LM.Vector2:new_local() self.centerVec:Set(mesh:SelectedCenter()) if not self.customCenter then self.customCenter = LM.Vector2:new_local() self.customCenter:Set(mesh:SelectedCenter()) end if self.centerMode then self.centerVec:Set(self.customCenter) end end self.numSel = moho:CountSelectedPoints() if (self.numSel >= 2) then local g = view:Graphics() local min = LM.Vector2:new_local() local max = LM.Vector2:new_local() local markerR = 4 local matrix = LM.Matrix:new_local() local centerVec = LM.Vector2:new_local() local v = LM.Vector2:new_local() local vc1 = LM.ColorVector:new_local() local vc2 = LM.ColorVector:new_local() vc1:Set(MOHO.MohoGlobals.SelCol) vc2:Set(MOHO.MohoGlobals.BackCol) --vc1 = (vc1 * 3 + vc2 * 4) / 7 vc1 = (vc1 + vc2) / 2 local col = vc1:AsColorStruct() local bbox = self:SelectedMeshBounds(moho, moho.drawingLayer, mesh, moho.drawingFrame, view) min:Set(bbox.fMin.x, bbox.fMin.y) max:Set(bbox.fMax.x, bbox.fMax.y) moho.drawingLayer:GetFullTransform(moho.frame, matrix, moho.document) g:Push() g:ApplyMatrix(matrix) g:SetColor(col) g:SetPenWidth(1) g:SetSmoothing(true) g:DrawLine(min.x, min.y, max.x, min.y) g:DrawLine(min.x, max.y, max.x, max.y) g:DrawLine(min.x, min.y, min.x, max.y) g:DrawLine(max.x, min.y, max.x, max.y) g:SetPenWidth(1) local rotWidth = max.x - min.x if (max.y - min.y > rotWidth) then rotWidth = max.y - min.y end rotWidth = rotWidth * 0.1 g:DrawLine(min.x - rotWidth, min.y - rotWidth, min.x - rotWidth, max.y + rotWidth) g:DrawLine(min.x - rotWidth, max.y + rotWidth, max.x + rotWidth, max.y + rotWidth) g:DrawLine(max.x + rotWidth, max.y + rotWidth, max.x + rotWidth, min.y - rotWidth) g:DrawLine(max.x + rotWidth, min.y - rotWidth, min.x - rotWidth, min.y - rotWidth) g:SetSmoothing(false) v:Set(min.x, min.y) g:FrameCirclePixelRadius(v, markerR) v:Set(min.x, max.y) g:FrameCirclePixelRadius(v, markerR) v:Set(max.x, min.y) g:FrameCirclePixelRadius(v, markerR) v:Set(max.x, max.y) g:FrameCirclePixelRadius(v, markerR) v:Set((min.x + max.x) / 2, min.y) g:FrameCirclePixelRadius(v, markerR) v:Set((min.x + max.x) / 2, max.y) g:FrameCirclePixelRadius(v, markerR) v:Set(min.x, (min.y + max.y) / 2) g:FrameCirclePixelRadius(v, markerR) v:Set(max.x, (min.y + max.y) / 2) g:FrameCirclePixelRadius(v, markerR) if (self.dragging) then centerVec:Set(self.centerVec) else centerVec:Set(self.centerVec) --centerVec = mesh:SelectedCenter() end g:SetColor(col) g:SetSmoothing(true) g:DrawLine(centerVec.x - 0.05, centerVec.y, centerVec.x + 0.05, centerVec.y) g:DrawLine(centerVec.x, centerVec.y - 0.05, centerVec.x, centerVec.y + 0.05) g:Pop() end end -- Draw bezier handles if (self.showHandles) then local mesh = moho:DrawingMesh() if (mesh == nil) then return end local g = view:Graphics() local layerMatrix = LM.Matrix:new_local() moho.drawingLayer:GetFullTransform(moho.frame, layerMatrix, moho.document) g:Push() g:ApplyMatrix(layerMatrix) g:SetSmoothing(true) local meshLayer = moho:LayerAsVector(moho.drawingLayer) local selectedOnly = false --[[if (self.isDragging) then if (self.numSel > 1) then selectedOnly = true end elseif (moho:CountSelectedPoints() > 1) then selectedOnly = true end]] if (moho:CountSelectedPoints() < 2) then meshLayer:DrawHandles(moho.document, g, selectedOnly) end g:Pop() end end function AE_TransformPoints:WeldPoints(moho, view, dragging) local mesh = moho:DrawingMesh() if (mesh == nil) then return false end if (moho.drawingLayer:CurrentAction() ~= "" or moho:DisableDrawingTools()) then return false -- welding in the middle of an action can lead to unexpected results end moho.document:SetDirty() -- try welding to the nearest point if (not dragging) then self.selID = -1 self.numSel = 0 for i = 0, mesh:CountPoints() - 1 do local pt = mesh:Point(i) if (pt.fSelected) then self.numSel = self.numSel + 1 self.selID = i if (self.numSel > 1) then break end end end if (self.numSel > 1) then self.selID = -1 end if (self.selID < 0) then return false end moho.document:PrepUndo(moho.drawingLayer) end if (self.selID < 0) then return false end local testVec1 = LM.Vector2:new_local() local testVec2 = LM.Vector2:new_local() testVec1:Set(mesh:Point(self.selID).fPos) local closestID = mesh:ClosestPoint(testVec1, self.selID) if (closestID < 0) then self.selID = -1 return false end testVec2:Set(mesh:Point(closestID).fPos) if (mesh:Point(self.selID).fHidden or mesh:Point(closestID).fHidden) then return false end -- convert the two chosen points to pixel coordinates -- this is to make sure that they are actually close enough in screen space local p1 = LM.Point:new_local() local p2 = LM.Point:new_local() local m = LM.Matrix:new_local() moho.drawingLayer:GetFullTransform(moho.frame, m, moho.document) m:Transform(testVec1) m:Transform(testVec2) view:Graphics():WorldToScreen(testVec1, p1) view:Graphics():WorldToScreen(testVec2, p2) p1.x = p1.x - p2.x p1.y = p1.y - p2.y closest = (p1.x * p1.x) + (p1.y * p1.y) if (closest < self.autoWeldRadius * self.autoWeldRadius) then -- found one close enough to weld if (mesh:ArePointsAdjacent(self.selID, closestID)) then -- this avoids welding adjacent points, particularly the end point of a curve to the previous point return true end if (mesh:WeldPoints(self.selID, closestID, moho.drawingLayerFrame)) then moho:Click() if (self.selID < closestID) then closestID = closestID - 1 end self.selID = -1 -- flag to end this interaction if (self.autoFill or self.autoStroke) then self:CreateShape(moho, mesh, closestID, self.autoFill) end end else return false end self.selID = -1 return true end function AE_TransformPoints:CreateShape(moho, mesh, ptID, filled) mesh:Point(ptID).fSelected = true mesh:SelectConnected() self.fillingShape = true moho.view:DrawMe() local shapeID = moho:CreateShape(filled) self.fillingShape = false if (shapeID >= 0) then local shape = mesh:Shape(shapeID) shape.fSelected = true if (shape.fFillAllowed) then shape.fHasFill = self.autoFill end shape.fHasOutline = self.autoStroke moho:NewKeyframe(CHANNEL_FILL) moho:NewKeyframe(CHANNEL_LINE) moho:UpdateSelectedChannels() else mesh:SelectNone() mesh:Point(ptID).fSelected = true end return shapeID end function AE_TransformPoints:SelIDForNudge(moho, mesh) local id = moho:CountSelectedPoints() if (id < 1) then return -2 elseif (id > 1) then return -1 else for i = 0, mesh:CountPoints() - 1 do if (mesh:Point(i).fSelected) then return i end end end end function AE_TransformPoints:UseFixedHandles(moho) if (not MOHO.IsMohoPro()) then return false end if (self.showHandles and self.fixedHandles) then-- and moho.drawingFrame == 0) then return true else return false end end -- ************************************************** -- Tool options - create and respond to tool's UI -- ************************************************** AE_TransformPoints.DUMMY = MOHO.MSG_BASE AE_TransformPoints.CHANGE = MOHO.MSG_BASE + 1 AE_TransformPoints.MODIFY_S = MOHO.MSG_BASE + 2 AE_TransformPoints.MODIFY_R = MOHO.MSG_BASE + 3 AE_TransformPoints.RESET = MOHO.MSG_BASE + 4 AE_TransformPoints.AUTOWELD = MOHO.MSG_BASE + 5 AE_TransformPoints.AUTOFILL = MOHO.MSG_BASE + 6 AE_TransformPoints.AUTOSTROKE = MOHO.MSG_BASE + 7 AE_TransformPoints.SHOWHANDLES = MOHO.MSG_BASE + 8 AE_TransformPoints.FIXEDHANDLES = MOHO.MSG_BASE + 9 AE_TransformPoints.FLIP_H = MOHO.MSG_BASE + 10 AE_TransformPoints.FLIP_V = MOHO.MSG_BASE + 11 AE_TransformPoints.SELECTITEM = MOHO.MSG_BASE + 12 AE_TransformPoints.FLIPDIRECTION = MOHO.MSG_BASE + 13 AE_TransformPoints.CENTERPOINTS = MOHO.MSG_BASE + 14 AE_TransformPoints.CENTERMODE = MOHO.MSG_BASE + 15 AE_TransformPoints.autoWeld = false AE_TransformPoints.autoWeldRadius = 12 AE_TransformPoints.autoFill = true AE_TransformPoints.autoStroke = true AE_TransformPoints.showHandles = false AE_TransformPoints.fixedHandles = true AE_TransformPoints.centerMode = true AE_TransformPoints.centerVec = LM.Vector2:new_local() AE_TransformPoints.customCenter = LM.Vector2:new_local() function AE_TransformPoints:DoLayout(moho, layout) self.menu = LM.GUI.Menu(MOHO.Localize("/Scripts/Tool/TransformPoints/SelectGroup=Select Group")) self.popup = LM.GUI.PopupMenu(120, false) self.popup:SetMenu(self.menu) layout:AddChild(self.popup) self.resetBut = LM.GUI.Button(MOHO.Localize("/Scripts/Tool/TransformPoints/Reset=Reset"), self.RESET) layout:AddChild(self.resetBut) layout:AddChild(LM.GUI.StaticText(MOHO.Localize("/Scripts/Tool/TransformPoints/Position=Position"))) self.textX = LM.GUI.TextControl(0, "00.000", self.CHANGE, LM.GUI.FIELD_FLOAT, MOHO.Localize("/Scripts/Tool/TransformPoints/X=X:")) self.textX:SetWheelInc(0.1) layout:AddChild(self.textX) self.textY = LM.GUI.TextControl(0, "00.000", self.CHANGE, LM.GUI.FIELD_FLOAT, MOHO.Localize("/Scripts/Tool/TransformPoints/Y=Y:")) self.textY:SetWheelInc(0.1) layout:AddChild(self.textY) layout:AddChild(LM.GUI.StaticText(MOHO.Localize("/Scripts/Tool/TransformLayer/Scale=Scale"))) self.scaleX = LM.GUI.TextControl(0, "00.000", self.MODIFY_S, LM.GUI.FIELD_FLOAT, MOHO.Localize("/Scripts/Tool/TransformPoints/X=X:")) self.scaleX:SetWheelInc(0) layout:AddChild(self.scaleX) self.scaleY = LM.GUI.TextControl(0, "00.000", self.MODIFY_S, LM.GUI.FIELD_FLOAT, MOHO.Localize("/Scripts/Tool/TransformPoints/Y=Y:")) self.scaleY:SetWheelInc(0) layout:AddChild(self.scaleY) layout:AddChild(LM.GUI.StaticText(MOHO.Localize("/Scripts/Tool/TransformLayer/Angle=Angle:"))) self.angle = LM.GUI.TextControl(0, "000.000", self.MODIFY_R, LM.GUI.FIELD_FLOAT) self.angle:SetWheelInc(0) layout:AddChild(self.angle) self.autoWeldCheck = LM.GUI.CheckBox(MOHO.Localize("/Scripts/Tool/TransformPoints/AutoWeld=Auto-weld"), self.AUTOWELD) layout:AddChild(self.autoWeldCheck) self.autoFillCheck = LM.GUI.CheckBox(MOHO.Localize("/Scripts/Tool/TransformPoints/AutoFill=Auto-fill"), self.AUTOFILL) layout:AddChild(self.autoFillCheck) self.autoStrokeCheck = LM.GUI.CheckBox(MOHO.Localize("/Scripts/Tool/TransformPoints/AutoStroke=Auto-stroke"), self.AUTOSTROKE) layout:AddChild(self.autoStrokeCheck) if (MOHO.IsMohoPro()) then self.showHandleCheck = LM.GUI.ImageButton("ScriptResources/show_handles", MOHO.Localize("/Scripts/Tool/TransformPoints/ShowBezierHandles=Show Bezier Handles"), true, self.SHOWHANDLES, true) layout:AddChild(self.showHandleCheck) self.fixedHandleCheck = LM.GUI.ImageButton("ScriptResources/fixed_handles", MOHO.Localize("/Scripts/Tool/TransformPoints/FixedBezierHandles=Fixed Bezier Handles"), true, self.FIXEDHANDLES, true) layout:AddChild(self.fixedHandleCheck) end --self.centerPoints = LM.GUI.Button(MOHO.Localize("/Scripts/Tool/TransformPoints/Center=Center"), self.CENTERPOINTS) --layout:AddChild(self.centerPoints) self.centerPointsCheck = LM.GUI.ImageButton("channel_tracking_point_sel", "Use custom center (alt-click this button to center, alt-shift click screen to set custom center", true, self.CENTERMODE, true) layout:AddChild(self.centerPointsCheck) self.centerPointsCheck:SetAlternateMessage(self.CENTERPOINTS) self.flipH = LM.GUI.ImageButton("ScriptResources/flip_points_h", MOHO.Localize("/Scripts/Tool/SelectPoints/FlipH=Flip Horizontally"), false, self.FLIP_H, true) layout:AddChild(self.flipH) self.flipV = LM.GUI.ImageButton("ScriptResources/flip_points_v", MOHO.Localize("/Scripts/Tool/SelectPoints/FlipV=Flip Vertically"), false, self.FLIP_V, true) layout:AddChild(self.flipV) self.flipDirectionCB = LM.GUI.CheckBox("Flip direction", self.FLIPDIRECTION) layout:AddChild(self.flipDirectionCB) self.curNumber = LM.GUI.DynamicText("000") layout:AddChild(self.curNumber) end -- self.centerVec:Set(mesh:SelectedCenter()) function AE_TransformPoints:UpdateWidgets(moho) if (moho:CurrentTool() ~= "AE_TransformPoints") then return -- this could get called when doing a double-tap on a multitouch Wacom device with a different active tool end local mesh = moho:DrawingMesh() if (mesh == nil) then return end MOHO.BuildGroupMenu(self.menu, mesh, self.SELECTITEM, self.DUMMY) local center = mesh:SelectedCenter() self.centerPointsCheck:SetValue(self.centerMode) self.textX:SetValue(center.x) self.textY:SetValue(center.y) self.scaleX:SetValue(1) self.scaleY:SetValue(1) self.angle:SetValue(0) self.autoWeldCheck:SetValue(self.autoWeld) self.autoFillCheck:SetValue(self.autoFill) self.autoStrokeCheck:SetValue(self.autoStroke) if (MOHO.IsMohoPro()) then self.showHandleCheck:SetValue(self.showHandles) self.fixedHandleCheck:SetValue(self.fixedHandles) self.fixedHandleCheck:Enable(self.showHandles) end self.resetBut:Enable(moho.drawingFrame > 0) if (moho:CountSelectedPoints() > 1) then self.flipH:Enable(true) self.flipV:Enable(true) else self.flipH:Enable(false) self.flipV:Enable(false) end self.flipDirectionCB:SetValue(AE_Curvature.flipDirection) local selectedID = -1 for i=0, mesh:CountPoints()-1 do if mesh:Point(i).fSelected then selectedID = i break end end if selectedID > -1 then self.curNumber:SetValue(string.format("%s",selectedID)) end end function AE_TransformPoints:HandleMessage(moho, view, msg) local mesh = moho:DrawingMesh() if (mesh == nil) then return end if (msg == self.CHANGE) then moho.document:PrepUndo(moho.drawingLayer) moho.document:SetDirty() local offset = mesh:SelectedCenter() offset.x = self.textX:FloatValue() - offset.x offset.y = self.textY:FloatValue() - offset.y mesh:PrepMovePoints() if (self:UseFixedHandles(moho)) then mesh:PrepFixedHandles() end mesh:TranslatePoints(offset) moho:AddPointKeyframe(moho.drawingFrame, nil, MOHO.MohoGlobals.EditMultipleKeys) if (self:UseFixedHandles(moho)) then mesh:PreserveHandlePositions() end moho:NewKeyframe(CHANNEL_POINT) moho:UpdateUI() elseif (msg == self.CENTERPOINTS) then self.centerVec:Set(mesh:SelectedCenter()) elseif (msg == self.CENTERMODE) then self.centerMode = self.centerPointsCheck:Value() if self.centerMode then self.centerVec:Set(self.customCenter) else self.centerVec:Set(mesh:SelectedCenter()) end elseif (msg == self.RESET) then if (moho.drawingFrame > 0) then moho.document:PrepUndo(moho.drawingLayer) moho.document:SetDirty() for i = 0, mesh:CountPoints() - 1 do local pt = mesh:Point(i) if (pt.fSelected) then pt:SetPos(pt.fAnimPos:GetValue(0), moho.drawingLayerFrame) end end end self:UpdateWidgets(moho) moho:NewKeyframe(CHANNEL_POINT) moho:UpdateUI() elseif (msg == self.MODIFY_S) then moho.document:PrepUndo(moho.drawingLayer) moho.document:SetDirty() self:PopulateSelList(moho, mesh) local centerVec = mesh:SelectedCenter() local scaleX = self.scaleX:FloatValue() local scaleY = self.scaleY:FloatValue() self.scaleX:SetValue(1) self.scaleY:SetValue(1) mesh:PrepMovePoints() if (self:UseFixedHandles(moho)) then mesh:PrepFixedHandles() end local flip = false if (scaleX < 0.001) then if (scaleY > 0.001) then flip = true end elseif (scaleY < 0.001) then if (scaleX > 0.001) then flip = true end end mesh:ScalePoints(scaleX, scaleY, centerVec, flip) local k = 1 if flip then k = -1 end self:FixOffsets(moho, mesh, k) moho:AddPointKeyframe(moho.drawingFrame, nil, MOHO.MohoGlobals.EditMultipleKeys) if (self:UseFixedHandles(moho)) then mesh:PreserveHandlePositions() end moho:NewKeyframe(CHANNEL_POINT) moho:UpdateUI() elseif (msg == self.MODIFY_R) then moho.document:PrepUndo(moho.drawingLayer) moho.document:SetDirty() local centerVec = mesh:SelectedCenter() local angle = math.rad(self.angle:FloatValue()) self.angle:SetValue(0) mesh:PrepMovePoints() if (self:UseFixedHandles(moho)) then mesh:PrepFixedHandles() end mesh:RotatePoints(angle, centerVec) moho:AddPointKeyframe(moho.drawingFrame, nil, MOHO.MohoGlobals.EditMultipleKeys) if (self:UseFixedHandles(moho)) then mesh:PreserveHandlePositions() end moho:NewKeyframe(CHANNEL_POINT) moho:UpdateUI() elseif (msg == self.AUTOWELD) then self.autoWeld = self.autoWeldCheck:Value() elseif (msg == self.AUTOFILL) then self.autoFill = self.autoFillCheck:Value() elseif (msg == self.AUTOSTROKE) then self.autoStroke = self.autoStrokeCheck:Value() elseif (msg == self.SHOWHANDLES) then if (MOHO.IsMohoPro()) then self.showHandles = self.showHandleCheck:Value() moho:UpdateUI() end elseif (msg == self.FIXEDHANDLES) then if (MOHO.IsMohoPro()) then self.fixedHandles = self.fixedHandleCheck:Value() end elseif (msg == self.FLIP_H) then moho.document:PrepUndo(moho.drawingLayer) moho.document:SetDirty() self:PopulateSelList(moho, mesh) local center = mesh:SelectedCenter() for i = 0, mesh:CountPoints() - 1 do local pt = mesh:Point(i) if (pt.fSelected) then local f = center.x - pt.fPos.x pt.fPos.x = center.x + f --pt:FlipControlHandles(moho.drawingFrame) self:FlipControlHandles(moho, moho.layer, mesh, pt, moho.drawingFrame) end end --moho:AddPointKeyframe(moho.drawingFrame) self:AddPointKeyframe(moho, moho.drawingFrame) moho:NewKeyframe(CHANNEL_POINT) moho:UpdateUI() elseif (msg == self.FLIP_V) then moho.document:PrepUndo(moho.drawingLayer) moho.document:SetDirty() self:PopulateSelList(moho, mesh) local center = mesh:SelectedCenter() for i = 0, mesh:CountPoints() - 1 do local pt = mesh:Point(i) if (pt.fSelected) then local f = center.y - pt.fPos.y pt.fPos.y = center.y + f --pt:FlipControlHandles(moho.drawingFrame) self:FlipControlHandles(moho, moho.layer, mesh, pt, moho.drawingFrame) end end --moho:AddPointKeyframe(moho.drawingFrame) self:AddPointKeyframe(moho, moho.drawingFrame) moho:NewKeyframe(CHANNEL_POINT) moho:UpdateUI() elseif (msg == self.SELECTITEM) then mesh:SelectNone() local i = msg - self.SELECTITEM local name = mesh:Group(i):Name() mesh:SelectGroup(name) moho:UpdateUI() elseif (msg == self.FLIPDIRECTION) then AE_Curvature.flipDirection = self.flipDirectionCB:Value() end end function AE_TransformPoints:FlipControlHandles(moho, layer, mesh, point, frame) local skel = layer:ControllingSkeleton() for c = 0, point:CountCurves() - 1 do local curve, where = point:Curve(c) local inChannel = AE_Utilities:GetOffsetChannel(moho, layer, curve, where, true) local outChannel = AE_Utilities:GetOffsetChannel(moho, layer, curve, where, false) local inCurVal = inChannel:GetValue(frame) local outCurVal = outChannel:GetValue(frame) local inDelta = 0 local outDelta = 0 if skel then inDelta = AE_Utilities:SumActionInfluences(moho, frame, inChannel, layer) outDelta = AE_Utilities:SumActionInfluences(moho, frame, outChannel, layer) end local desiredInVal = -(inCurVal + inDelta) local desiredOutVal = -(outCurVal + outDelta) local targetInVal = desiredInVal - inDelta local targetOutVal = desiredOutVal - outDelta inChannel:SetValue(frame, targetInVal) outChannel:SetValue(frame, targetOutVal) end end function AE_TransformPoints:FixOffsets(moho, mesh, k) for i, pt in pairs(self.selList) do for c = 0, pt:CountCurves() - 1 do local curve, where = pt:Curve(c) local inChannel = AE_Utilities:GetOffsetChannel(moho, moho.layer, curve, where, true) local outChannel = AE_Utilities:GetOffsetChannel(moho, moho.layer, curve, where, false) inDelta = AE_Utilities:SumActionInfluences(moho, moho.drawingFrame, inChannel, moho.layer) outDelta = AE_Utilities:SumActionInfluences(moho, moho.drawingFrame, outChannel, moho.layer) if math.abs(inDelta) > 0.01 then local wanted = (self.inOffsets[i][c] + inDelta) * k local newOffset = wanted - inDelta curve:SetOffset(where, newOffset, moho.drawingFrame, true) end if math.abs(outDelta) > 0.01 then local wanted = (self.outOffsets[i][c] + outDelta) * k local newOffset = wanted - outDelta curve:SetOffset(where, newOffset, moho.drawingFrame, false) end end end end
AE Transform points and curvature
Listed
Author: A.Evseeva
View Script
Script type: Tool
Uploaded: Feb 12 2021, 21:53
Last modified: Feb 22 2021, 05:25
Tweak for built-in transform points and curvature to fix errors on smartbones driven points and bezier handles
This script, and all other scripts on this site are distributed as free software under the GNU General Public License 3.0 or later.
Downloads count: 22

AE Transform points and curvature
Listed
Author: A.Evseeva
View Script
Script type: Tool
Uploaded: Feb 12 2021, 21:53
Last modified: Feb 22 2021, 05:25
Tweak for built-in transform points and curvature to fix errors on smartbones driven points and bezier handles
This script, and all other scripts on this site are distributed as free software under the GNU General Public License 3.0 or later.
Downloads count: 22