-- ************************************************** -- Provide Moho with the name of this script object -- ************************************************** ScriptName = "AM_Paint_Bucket" -- ************************************************** -- General information about this script -- ************************************************** AM_Paint_Bucket = {} AM_Paint_Bucket.BASE_STR = 2740 function AM_Paint_Bucket:Name() return "Paint Bucket +" end function AM_Paint_Bucket:Version() return "1" end function AM_Paint_Bucket:IsBeginnerScript() return true end function AM_Paint_Bucket:Description() return "Click to fill a closed region with color (hold <alt> to pick fill color, <shift> to pick stroke color, <cmd/ctrl> to copy shape properties, <cmd/ctrl> + <alt> to pick fill color and fill a closed region with it)" end function AM_Paint_Bucket:BeginnerDescription() return "Click inside a closed shape to fill it with the current combination of colors and styles. Only completely closed shapes can be filled. The Fill and Stroke color can be changed in the Style window (Window > Style)." end function AM_Paint_Bucket:BeginnerDisabledDescription() return "You need to create a vector drawing first using the 'Add Point', 'Freehand' or 'Draw Shape' tool before you can use this tool. You need to be on Frame 0 in the Timeline to use this tool." end function AM_Paint_Bucket:Creator() return "Aleksei Maletin" end function AM_Paint_Bucket:UILabel() return("Paint Bucket +") end function AM_Paint_Bucket:LoadPrefs(prefs) self.creationMode = prefs:GetInt("AM_Paint_Bucket.creationMode", 2) end function AM_Paint_Bucket:SavePrefs(prefs) prefs:SetInt("AM_Paint_Bucket.creationMode", self.creationMode) end function AM_Paint_Bucket:ResetPrefs() self.creationMode = 2 end -- ************************************************** -- Recurring values -- ************************************************** AM_Paint_Bucket.prepUndo = false AM_Paint_Bucket.extendEdges = false AM_Paint_Bucket.dragMode = 0 -- ************************************************** -- The guts of this script -- ************************************************** function AM_Paint_Bucket:IsEnabled(moho) if (moho:DisableDrawingTools()) then return false end if (moho.drawingLayer:CurrentAction() ~= "") then return false end if (moho:CountEdges() > 0) then return true end return false end function AM_Paint_Bucket:IsRelevant(moho) if (moho:DisableDrawingTools()) then return false end local mesh = moho:DrawingMesh() if (mesh == nil) then return false end return true end function AM_Paint_Bucket:OnMouseDown(moho, mouseEvent) self.color = mouseEvent.view:SampleColor(mouseEvent.pt) self.mouseVec = mouseEvent.vec if (mouseEvent.ctrlKey) and not (mouseEvent.altKey) and not (mouseEvent.shiftKey) then -- control self.dragMode = 0 -- pick style self:OnMouseMoved(moho, mouseEvent) elseif (mouseEvent.altKey) and not (mouseEvent.ctrlKey) and not (mouseEvent.shiftKey) then -- alt self.dragMode = 1 -- pick fill color self:OnMouseMoved(moho, mouseEvent) elseif (mouseEvent.shiftKey) and not (mouseEvent.ctrlKey) and not (mouseEvent.altKey) then -- shift self.dragMode = 2 -- pick stroke color self:OnMouseMoved(moho, mouseEvent) elseif (mouseEvent.ctrlKey) and (mouseEvent.altKey) and not (mouseEvent.shiftKey) then -- control + alt self.dragMode = 3 -- pick fill color and create shape self:OnMouseMoved(moho, mouseEvent) elseif (mouseEvent.shiftKey) and (mouseEvent.ctrlKey) and not (mouseEvent.altKey) then -- control + shift self.dragMode = 4 -- pick stroke color and creaete shape self:OnMouseMoved(moho, mouseEvent) elseif (mouseEvent.shiftKey) and (mouseEvent.altKey) and not (mouseEvent.ctrlKey) then -- alt + shift self.dragMode = 6 -- do nothing else self.dragMode = 5 -- just create shape with current settings end end function AM_Paint_Bucket:OnMouseMoved(moho, mouseEvent) self.color = mouseEvent.view:SampleColor(mouseEvent.pt) if self.dragMode == 0 then local shape = mouseEvent.view:PickGlobalShape(mouseEvent.pt) if (shape ~= nil) then moho:PickStyleProperties(shape) else moho:PickStyleProperties(nil) end elseif (self.dragMode>=1 and self.dragMode<=4) then mouseEvent.view:DrawMe() else end end function AM_Paint_Bucket:OnMouseUp(moho, mouseEvent) self.mouseVec = mouseEvent.vec self.color = mouseEvent.view:SampleColor(mouseEvent.pt) if self.dragMode == 0 then -- pick style elseif self.dragMode == 1 or self.dragMode == 3 then -- pick fill color self.color = mouseEvent.view:SampleColor(mouseEvent.pt) local style = moho:CurrentEditStyle() style.fFillCol:SetValue(moho.drawingLayerFrame, self.color) elseif self.dragMode == 2 or self.dragMode == 4 then -- pick stroke color self.color = mouseEvent.view:SampleColor(mouseEvent.pt) local style = moho:CurrentEditStyle() style.fLineCol:SetValue(moho.drawingLayerFrame, self.color) end if self.dragMode == 3 or self.dragMode == 5 then moho.document:PrepUndo(moho.drawingLayer) moho.document:SetDirty() if (mouseEvent.altKey) then local style = moho:CurrentEditStyle() local col = mouseEvent.view:SampleColor(mouseEvent.pt) style.fFillCol:SetValue(moho.drawingLayerFrame, col) end local extension = 0 if (self.extendEdges) then extension = 20 end local mesh = moho:DrawingMesh() mesh:SelectNone() local curveID = -1 local segID = -1 curveID, segID = self:SelectEdge(mesh, mouseEvent) if (curveID >= 0) then local curve = mesh:Curve(curveID) if (not curve.fClosed) then mesh:SelectConnected() end else mouseEvent.view:FloodSelect(mouseEvent.pt, 1, true, extension) self:Weld(moho, mesh) mouseEvent.view:FloodSelect(mouseEvent.pt, 1, true, extension) end if (moho:CountSelectedEdges(true) > 0) then local mesh = moho:DrawingMesh() local sourcePoints = {} local shapeID = -1 if (shapeID < 0) then local fillable = false -- first try to create a fill no matter what to see if it's valid local existingShapeID = mouseEvent.view:PickShape(mouseEvent.pt) -- see if there is already a shape at the clicked location local fillCol = nil local lineCol = nil if (existingShapeID >= 0) then -- try to create a fill normally if (self.creationMode == 0) then -- fill only shapeID = moho:CreateShape(true, false, 0, true, false, false, true) elseif (self.creationMode == 1) then -- stroke only shapeID = moho:CreateShape(true, false, 0, true, true, true, false) else -- both fill and stroke shapeID = moho:CreateShape(true, false, 0) end else -- try to create a fill behind the neighboring shapes shapeID = moho:CreateShape(true, true, 0) end if (shapeID >= 0) then fillable = true end if (existingShapeID >= 0 and existingShape == shapeID) then -- modifying an existing shape, don't change fill/stroke status else if (shapeID >= 0 and self.creationMode == 0) then local shape = mesh:Shape(shapeID) shape.fHasOutline = false end if (shapeID >= 0 and self.creationMode == 1) then local shape = mesh:Shape(shapeID) shape.fHasFill = false end end if (shapeID < 0 and curveID >= 0) then shapeID = moho:CreateShape(false, false, 0) -- try to create an outline if (shapeID >= 0) then local shape = mesh:Shape(shapeID) if (self.creationMode == 0) then shape.fHasOutline = false elseif (self.creationMode == 1) then shape.fHasFill = false end end end if (shapeID < 0) then -- create a shape by tracing the flood fill local existingShapeID = mouseEvent.view:PickShape(mouseEvent.pt) mouseEvent.view:FloodSelect(mouseEvent.pt, 1, true, extension) mesh:SelectConnected() self:Weld(moho, mesh) mouseEvent.view:FloodSelect(mouseEvent.pt, 1, true, extension) local frame = moho.drawingLayerFrame for a=0, mesh:CountCurves()-1 do local curve = mesh:Curve(a) for i=0, curve:CountPoints()-1 do local point = curve:Point(i) local curvature = curve:Curvature(i):GetValue(frame) local handle1 = curve:GetControlHandle(i, frame, true) local handle2 = curve:GetControlHandle(i, frame, false) table.insert(sourcePoints, {point = point, curvature = curvature, handle1 = handle1, handle2 = handle2, ptID = i, curve = curve}) end end self:Split(moho, mesh) mouseEvent.view:FloodSelect(mouseEvent.pt, 1, true, extension) if (self.creationMode == 0) then -- fill only shapeID = moho:CreateShape(true, false, 0, true, false, false, true) elseif (self.creationMode == 1) then -- stroke only shapeID = moho:CreateShape(true, false, 0, true, true, true, false) else -- both fill and stroke shapeID = moho:CreateShape(true, false, 0) end if (shapeID >= 0) then local shape = mesh:Shape(shapeID) if (self.creationMode == 0) then shape.fHasOutline = false; elseif (self.creationMode == 1) then shape.fHasFill = false; end if (existingShapeID >= 0) then mesh:PlaceShapeAbove(shapeID, existingShapeID) else mesh:LowerShape(shapeID, true) end end mesh:SelectNone() for i, point in ipairs(sourcePoints) do point['point'].fSelected = true end mesh:SelectInverse() MOHO.DeleteSelectedPoints(mesh) for i, point in ipairs(sourcePoints) do point['curve']:SetCurvature(point['ptID'], point['curvature'], frame) point['curve']:SetControlHandle(point['ptID'], point['handle1'], frame, true, false) point['curve']:SetControlHandle(point['ptID'], point['handle2'], frame, false, false) end end if (shapeID >= 0) then moho:NewKeyframe(CHANNEL_FILL) moho:NewKeyframe(CHANNEL_LINE) moho:UpdateSelectedChannels() moho:UpdateUI() end else for i = 0, mesh:CountShapes() - 1 do mesh:Shape(i).fSelected = false end mesh:Shape(shapeID).fSelected = true mesh:Shape(shapeID):CopyStyleProperties(moho:NewShapeProperties()) moho:UpdateSelectedChannels() moho:UpdateUI() end end end self.dragMode = 0 end function AM_Paint_Bucket:Weld(moho, mesh) for i = 0, mesh:CountCurves() - 1 do local curve = mesh:Curve(i) if (curve:IsSelected()) then local awr = LM_TransformPoints.autoWeldRadius LM_TransformPoints.autoWeldRadius = 3 MOHO.WeldFullCurve(moho, moho.drawingLayer, mesh, i, moho.view) LM_TransformPoints.autoWeldRadius = awr end end end function AM_Paint_Bucket:Split(moho, mesh) local points = {} for i=0, mesh:CountPoints()-1 do local point = mesh:Point(i) if point.fSelected and point:CountCurves()>1 then table.insert(points, point) end for a=0, moho:CountSelectedCurves()-1 do local curve = mesh:Curve(a) end point.fSelected = false end for i, point in ipairs(points) do point.fSelected = true end MOHO:SplitSelectedSegments(mesh, 1, moho.drawingLayerFrame) mesh:SelectNone() end function AM_Paint_Bucket:OnKeyDown(moho, keyEvent) LM_SelectPoints:OnKeyDown(moho, keyEvent) end function AM_Paint_Bucket:DrawMe(moho, view, mouseEvent) if (self.dragMode>=1 and self.dragMode<=4) then local zoom = moho.view:Graphics():ViewZoom() local g = view:Graphics() g:Push() g:SetSmoothing(true) local matrix = LM.Matrix:new_local() moho.drawingLayer:GetFullTransform(moho.frame, matrix, moho.document) matrix:Transform(self.mouseVec) g:SetColor(128, 128, 128) g:SetPenWidth(10) g:FrameCirclePixelRadius(self.mouseVec, 74) g:SetColor(self.color.r, self.color.g, self.color.b, 255) g:SetPenWidth(20) g:FrameCirclePixelRadius(self.mouseVec, 64) g:Pop() end end function AM_Paint_Bucket:SelectEdge(mesh, mouseEvent) local curveID = -1 local segID = -1 curveID, segID = mouseEvent.view:PickEdge(mouseEvent.pt, curveID, segID) if (curveID >= 0 and segID >= 0) then -- an edge was clicked on self.selMode = SELMODE_EDGE local curve = mesh:Curve(curveID) -- new method - select the points in the curve that was clicked on if (mouseEvent.shiftKey) then local isCurveSelected = true for i = 0, curve:CountPoints() - 1 do if (not curve:Point(i).fSelected) then isCurveSelected = false break end end for i = 0, curve:CountPoints() - 1 do local point = curve:Point(i) if ((not isCurveSelected) or (point:CountCurves() < 2)) then point.fSelected = not isCurveSelected end end else for i = 0, curve:CountPoints() - 1 do local point = curve:Point(i) point.fSelected = true end end end return curveID, segID end function AM_Paint_Bucket:AutoWeld(moho, selCurveID) -- returns true if a weld occurred, otherwise false local mesh = moho:DrawingMesh() if (mesh == nil) then return false end if (not self.prepUndo) then self.prepUndo = true moho.document:PrepUndo(moho.drawingLayer) moho.document:SetDirty() end local didWeld = false local m = LM.Matrix:new_local() local testVec1 = LM.Vector2:new_local() local testVec2 = LM.Vector2:new_local() local p1 = LM.Point:new_local() local p2 = LM.Point:new_local() local dist = 0 moho.drawingLayer:GetFullTransform(moho.frame, m, moho.document) -- First see if we can close an open curve. That's the cleanest auto-weld possible. local curve = mesh:Curve(selCurveID) if (not curve.fClosed and curve:CountPoints() > 2) then local pt1 = curve:Point(0) local pt2 = curve:Point(curve:CountPoints() - 1) testVec1:Set(pt1.fPos) testVec2:Set(pt2.fPos) m:Transform(testVec1) m:Transform(testVec2) moho.view:Graphics():WorldToScreen(testVec1, p1) moho.view:Graphics():WorldToScreen(testVec2, p2) p1.x = p1.x - p2.x p1.y = p1.y - p2.y dist = (p1.x * p1.x) + (p1.y * p1.y) if (dist < LM_TransformPoints.autoWeldRadius * LM_TransformPoints.autoWeldRadius) then -- found one close enough to weld if ((mesh:PointID(pt1) ~= mesh:PointID(pt2)) and mesh:WeldPoints(mesh:PointID(pt1), mesh:PointID(pt2), 0)) then return true end end end -- Next, try welding curve endpoints local doAgain = true while doAgain do doAgain = false for i = 0, mesh:CountPoints() - 1 do local pt1 = mesh:Point(i) if (pt1.fSelected and pt1:IsEndpoint() and (not pt1.fHidden)) then for j = 0, mesh:CountPoints() - 1 do if (j ~= i) then local pt2 = mesh:Point(j) if (pt2:IsEndpoint() and (not pt2.fHidden)) then testVec1:Set(pt1.fPos) testVec2:Set(pt2.fPos) m:Transform(testVec1) m:Transform(testVec2) moho.view:Graphics():WorldToScreen(testVec1, p1) moho.view:Graphics():WorldToScreen(testVec2, p2) p1.x = p1.x - p2.x p1.y = p1.y - p2.y dist = (p1.x * p1.x) + (p1.y * p1.y) if (dist < LM_TransformPoints.autoWeldRadius * LM_TransformPoints.autoWeldRadius) then -- found one close enough to weld if ((mesh:PointID(pt1) ~= mesh:PointID(pt2)) and mesh:WeldPoints(mesh:PointID(pt1), mesh:PointID(pt2), 0)) then didWeld = true doAgain = true break end end end end if (doAgain) then break end end end if (doAgain) then break end end end -- Finally, try welding dead-end points to the middle of a nearby curve. local curveID = -1 local segID = -1 local doAgain = true while doAgain do doAgain = false for i = 0, mesh:CountPoints() - 1 do local pt1 = mesh:Point(i) if (pt1.fSelected and pt1:IsEndpoint()) then local id2 = mesh:ClosestPoint(pt1.fPos, i) if (id2 >= 0) then local pt2 = mesh:Point(id2) testVec1:Set(pt1.fPos) testVec2:Set(pt2.fPos) m:Transform(testVec1) m:Transform(testVec2) moho.view:Graphics():WorldToScreen(testVec1, p1) moho.view:Graphics():WorldToScreen(testVec2, p2) p2.x = p1.x - p2.x p2.y = p1.y - p2.y dist = (p2.x * p2.x) + (p2.y * p2.y) local pickWidth = 3 while (pickWidth < LM_TransformPoints.autoWeldRadius) do curveID, segID = pt1: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(i, segID)) and (not mesh:Curve(curveID):IsPointOnSegment(i, segID - 1)) and (not mesh:Curve(curveID):IsPointOnSegment(i, segID - 2)) and (not mesh:Curve(curveID):IsPointOnSegment(i, segID + 1)) and (not mesh:Curve(curveID):IsPointOnSegment(i, segID + 2)) and (i ~= mesh:CountPoints() - 1)) then -- don't weld the point back on itself local curve = mesh:Curve(curveID) mesh:AddPoint(curve:ClosestPointOnSegment(segID, pt1.fPos, true, true), curveID, segID, 0) mesh:WeldPoints(i, mesh:CountPoints() - 1, 0) didWeld = true doAgain = true break end end pickWidth = pickWidth + 3 end end end if (doAgain) then break end end end return didWeld end -- ************************************************** -- Tool options - create and respond to tool's UI -- ************************************************** AM_Paint_Bucket.FILL = MOHO.MSG_BASE AM_Paint_Bucket.OUTLINE = MOHO.MSG_BASE + 1 AM_Paint_Bucket.FILLOUTLINE = MOHO.MSG_BASE + 2 AM_Paint_Bucket.CHANGE_SIMPLIFY = MOHO.MSG_BASE + 3 AM_Paint_Bucket.CLOSEGAPS = MOHO.MSG_BASE + 4 AM_Paint_Bucket.creationMode = 2 -- 0:fill, 1:outline, 2:both AM_Paint_Bucket.DELETE = MOHO.MSG_BASE + 5 function AM_Paint_Bucket:DoLayout(moho, layout) self.fillRadio = LM.GUI.RadioButton("Fill", self.FILL) layout:AddChild(self.fillRadio) self.outlineRadio = LM.GUI.RadioButton("Stroke", self.OUTLINE) layout:AddChild(self.outlineRadio) self.fillOutlineRadio = LM.GUI.RadioButton("Both", self.FILLOUTLINE) layout:AddChild(self.fillOutlineRadio) layout:AddChild(LM.GUI.Button('Delete unused points', self.DELETE)) end function AM_Paint_Bucket:UpdateWidgets(moho) self.fillRadio:SetValue(self.creationMode == 0) self.outlineRadio:SetValue(self.creationMode == 1) self.fillOutlineRadio:SetValue(self.creationMode == 2) end function AM_Paint_Bucket:HandleMessage(moho, view, msg) if (msg == self.FILL) then self.creationMode = 0 elseif (msg == self.OUTLINE) then self.creationMode = 1 elseif (msg == self.FILLOUTLINE) then self.creationMode = 2 elseif msg == self.DELETE then self:DeleteUnusedPoints(moho) end end function AM_Paint_Bucket:DeleteUnusedPoints(moho) local mesh = moho:Mesh() mesh:SelectNone() for i=0, mesh:CountPoints()-1 do local point = mesh:Point(i) for a=0, mesh:CountShapes()-1 do local shape = mesh:Shape(a) if shape:ContainsPoint(i) then point.fSelected = true end end end mesh:SelectInverse() moho.document:PrepUndo(moho.drawingLayer) moho.document:SetDirty() MOHO.DeleteSelectedPoints(mesh) moho:UpdateUI() end
Paint Bucket +
Listed
Author: Danfield
View Script
Script type: Tool
Uploaded: Mar 16 2024, 19:30
Last modified: Mar 17 2024, 07:53
Script Version: 1
Modified Paint Bucket + Eyedropper
Click to fill a closed region with color. Hold <alt> to pick fill color, <shift> to pick stroke color, <cmd/ctrl> to copy shape properties, <cmd/ctrl> + <alt> to pick fill color and fill a closed region with it.
Press "Delete unused points" to remove points that are not used in shapes.
You can support the author at the link
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: 591
Paint Bucket +
Listed
Author: Danfield
View Script
Script type: Tool
Uploaded: Mar 16 2024, 19:30
Last modified: Mar 17 2024, 07:53
Script Version: 1
Modified Paint Bucket + Eyedropper
Click to fill a closed region with color. Hold <alt> to pick fill color, <shift> to pick stroke color, <cmd/ctrl> to copy shape properties, <cmd/ctrl> + <alt> to pick fill color and fill a closed region with it.
Press "Delete unused points" to remove points that are not used in shapes.
You can support the author at the link
Press "Delete unused points" to remove points that are not used in shapes.
You can support the author at the link
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: 591