Initial {rotate,tile} operator implementation.

This commit is contained in:
Lucio Andrés Illanes Albornoz 2019-09-21 11:27:52 +02:00
parent 62e7edc852
commit eb3795a98e
11 changed files with 220 additions and 37 deletions

1
.TODO Normal file
View File

@ -0,0 +1 @@
text bug: a) select text tool b) paste stuff c) undo d) artifacts

70
.vscode/launch.json vendored Executable file
View File

@ -0,0 +1,70 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python: Current File (Integrated Terminal)",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal"
},
{
"name": "Python: Remote Attach",
"type": "python",
"request": "attach",
"port": 5678,
"host": "localhost",
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "."
}
]
},
{
"name": "Python: Module",
"type": "python",
"request": "launch",
"module": "enter-your-module-name-here",
"console": "integratedTerminal"
},
{
"name": "Python: Django",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/manage.py",
"console": "integratedTerminal",
"args": [
"runserver",
"--noreload",
"--nothreading"
],
"django": true
},
{
"name": "Python: Flask",
"type": "python",
"request": "launch",
"module": "flask",
"env": {
"FLASK_APP": "app.py"
},
"args": [
"run",
"--no-debugger",
"--no-reload"
],
"jinja": true
},
{
"name": "Python: Current File (External Terminal)",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "externalTerminal"
}
]
}

12
.vscode/tasks.json vendored Executable file
View File

@ -0,0 +1,12 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "echo",
"type": "shell",
"command": "echo Hello"
}
]
}

View File

@ -9,6 +9,7 @@ Low-priority list:
8) Incremental auto{load,save} & {backup,restore} (needs Settings window)
9) Composition, parametrisation & keying of tools from higher-order operators (brushes, functions, filters, outlines, patterns & shaders) and unit tools
10) Sprites & scripted (Python?) animation on the basis of asset traits and {composable,parametrised} patterns (metric flow, particle system, rigging, ...)
11) Integrate ENNTool code in the form of OpenGL-based animation window (see 9) and 10))
High-priority list:
1) unit tools: arrow, {cloud,speech bubble}, curve, measure, pick, polygon, triangle, unicode

38
liboperators/OperatorRotate.py Executable file
View File

@ -0,0 +1,38 @@
#!/usr/bin/env python3
#
# OperatorRotate.py
# Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
#
from Operator import Operator
import math
class OperatorRotate(Operator):
name = "Rotate"
#
# apply2(self, mapPoint, mousePoint, regionOld, region)
def apply2(self, mapPoint, mousePoint, regionOld, region):
if self.originPoint == None:
self.originPoint = list(mousePoint)
delta = [b - a for a, b in zip(self.originPoint, mousePoint)]
radius = math.sqrt(math.pow(delta[0], 2) + math.pow(delta[1], 2))
if radius >= 10:
regionSize = (len(region[0]), len(region))
theta = math.atan2(-delta[1], delta[0]); cos, sin = math.cos(theta), math.sin(theta);
for numCol in range(regionSize[0]):
for numRow in range(regionSize[1]):
numRow_, numCol_ = (numRow / regionSize[1]) * 2 - 1, (numCol / regionSize[0]) * 2 - 1
b, a = (numCol_ * sin) + (numRow_ * cos), (numCol_ * cos) - (numRow_ * sin)
numRow_, numCol_ = int((b + 1) / 2 * regionSize[1]), int((a + 1) / 2 * regionSize[0])
if (numRow_ < regionSize[1]) and (numCol_ < regionSize[0]):
region[numRow][numCol] = list(regionOld[numRow_][numCol_])
return region
else:
return region
# __init__(self, *args): initialisation method
def __init__(self, *args):
self.originPoint = None
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120

39
liboperators/OperatorTile.py Executable file
View File

@ -0,0 +1,39 @@
#!/usr/bin/env python3
#
# OperatorTile.py
# Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
#
from Operator import Operator
import copy
class OperatorTile(Operator):
name = "Tile"
#
# apply2(self, mapPoint, mousePoint, regionOld, region)
def apply2(self, mapPoint, mousePoint, regionOld, region):
if self.lastPoint == None:
self.lastPoint = list(mapPoint)
if self.tileObject == None:
self.tileObject = copy.deepcopy(region)
delta = [b - a for a, b in zip(self.lastPoint, mapPoint)]
if delta[1] > 0:
for numNewRow in range(delta[1]):
newRow = copy.deepcopy(self.tileObject[len(region) % len(self.tileObject)])
if len(newRow) < len(region[0]):
for numNewCol in range(len(newRow), len(region[0])):
newRow += [list(self.tileObject[len(region) % len(self.tileObject)][numNewCol % len(self.tileObject[len(region) % len(self.tileObject)])])]
region += [newRow]
if delta[0] > 0:
for numRow in range(len(region)):
for numNewCol in range(len(region[numRow]), len(region[numRow]) + delta[0]):
region[numRow] += [list(self.tileObject[numRow % len(self.tileObject)][numNewCol % len(self.tileObject[numRow % len(self.tileObject)])])]
self.lastPoint = list(mapPoint)
return region
# __init__(self, *args): initialisation method
def __init__(self, *args):
self.lastPoint, self.tileObject = None, None
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120

View File

@ -64,6 +64,9 @@ class RoarCanvasCommands(RoarCanvasCommandsFile, RoarCanvasCommandsEdit, RoarCan
self.parentFrame.SetTitle("roar")
if "toolName" in self.lastPanelState:
textItems.append("Current tool: {}".format(self.lastPanelState["toolName"]))
if ("operator" in self.lastPanelState) \
and (self.lastPanelState["operator"] != None):
textItems.append("Current operator: {}".format(self.lastPanelState["operator"]))
if "dirty" in self.lastPanelState \
and self.lastPanelState["dirty"]:
textItems.append("*")

View File

@ -7,6 +7,8 @@
from OperatorFlipHorizontal import OperatorFlipHorizontal
from OperatorFlipVertical import OperatorFlipVertical
from OperatorInvert import OperatorInvert
from OperatorRotate import OperatorRotate
from OperatorTile import OperatorTile
from GuiFrame import GuiCommandListDecorator
from ToolObject import ToolObject
import copy, wx
@ -16,41 +18,13 @@ class RoarCanvasCommandsOperators():
@GuiCommandListDecorator(0, "Flip", "&Flip", None, None, None)
@GuiCommandListDecorator(1, "Flip horizontally", "Flip &horizontally", None, None, None)
@GuiCommandListDecorator(2, "Invert", "&Invert", None, None, None)
@GuiCommandListDecorator(3, "Rotate", "&Rotate", None, None, None)
@GuiCommandListDecorator(4, "Tile", "&Tile", None, None, None)
def canvasOperator(self, f, idx):
def canvasOperator_(event):
applyOperator = [OperatorFlipVertical, OperatorFlipHorizontal, OperatorInvert][idx]()
if (self.currentTool.__class__ == ToolObject) \
and (self.currentTool.toolState >= self.currentTool.TS_SELECT):
region = self.currentTool.getRegion(self.parentCanvas.canvas)
else:
region = self.parentCanvas.canvas.map
region = applyOperator.apply(copy.deepcopy(region))
if (self.currentTool.__class__ == ToolObject) \
and (self.currentTool.toolState >= self.currentTool.TS_SELECT):
if self.parentCanvas.popupEventDc == None:
eventDc = self.parentCanvas.backend.getDeviceContext(self.parentCanvas.GetClientSize(), self.parentCanvas)
else:
eventDc = self.parentCanvas.popupEventDc
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
self.currentTool.setRegion(self.parentCanvas.canvas, None, region, [len(region[0]), len(region)], self.currentTool.external)
self.currentTool.onSelectEvent(self.parentCanvas.canvas, (0, 0), self.parentCanvas.dispatchPatchSingle, eventDc, True, wx.MOD_NONE, None, self.currentTool.targetRect)
eventDc.SetDeviceOrigin(*eventDcOrigin)
else:
if self.parentCanvas.popupEventDc == None:
eventDc = self.parentCanvas.backend.getDeviceContext(self.parentCanvas.GetClientSize(), self.parentCanvas)
else:
eventDc = self.parentCanvas.popupEventDc
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
self.parentCanvas.canvas.journal.begin()
dirty = False
for numRow in range(len(region)):
for numCol in range(len(region[numRow])):
if not dirty:
self.parentCanvas.dirty = True
self.parentCanvas.dispatchPatchSingle(eventDc, False, [numCol, numRow, *region[numRow][numCol]])
self.parentCanvas.canvas.journal.end()
self.parentCanvas.commands.update(dirty=self.parentCanvas.dirty, undoLevel=self.parentCanvas.canvas.journal.patchesUndoLevel)
eventDc.SetDeviceOrigin(*eventDcOrigin)
self.currentOperator = [OperatorFlipVertical, OperatorFlipHorizontal, OperatorInvert, OperatorRotate, OperatorTile][idx]()
self.operatorState = None
self.parentCanvas.applyOperator(self.currentTool, self.parentCanvas.brushPos, None, False, self.currentOperator, self.parentCanvas.GetViewStart())
setattr(canvasOperator_, "attrDict", f.attrList[idx])
return canvasOperator_
# }}}
@ -60,9 +34,10 @@ class RoarCanvasCommandsOperators():
def __init__(self):
self.menus = (
("&Operators",
self.canvasOperator(self.canvasOperator, 0), self.canvasOperator(self.canvasOperator, 1), self.canvasOperator(self.canvasOperator, 2),
self.canvasOperator(self.canvasOperator, 0), self.canvasOperator(self.canvasOperator, 1), self.canvasOperator(self.canvasOperator, 2), self.canvasOperator(self.canvasOperator, 3), self.canvasOperator(self.canvasOperator, 4),
),
)
self.toolBars = ()
self.currentOperator, self.operatorState = None, None
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=0

View File

@ -31,6 +31,7 @@ class RoarCanvasCommandsTools():
self.lastTool, self.currentTool = self.currentTool, [ToolCircle, None, ToolFill, ToolLine, ToolObject, ToolRect, ToolText][idx]
if self.currentTool != None:
self.currentTool = self.currentTool()
self.currentOperator, self.operatorState = None, None
self.parentFrame.menuItemsById[self.canvasTool.attrList[idx]["id"]].Check(True)
toolBar = self.parentFrame.toolBarItemsById[self.canvasTool.attrList[idx]["id"]].GetToolBar()
toolBar.ToggleTool(self.canvasTool.attrList[idx]["id"], True)

View File

@ -7,7 +7,7 @@
from GuiWindow import GuiWindow
from ToolObject import ToolObject
from ToolText import ToolText
import json, wx, sys
import copy, json, wx, sys
class RoarCanvasWindowDropTarget(wx.TextDropTarget):
# {{{ done(self)
@ -54,6 +54,47 @@ class RoarCanvasWindow(GuiWindow):
self.canvas.journal.pushCursor(patchDelta)
# }}}
# {{{ applyOperator(self, currentTool, mapPoint, mouseLeftDown, mousePoint, operator, viewRect)
def applyOperator(self, currentTool, mapPoint, mouseLeftDown, mousePoint, operator, viewRect):
self.canvas.dirtyCursor = False
if (currentTool.__class__ == ToolObject) \
and (currentTool.toolState >= currentTool.TS_SELECT):
region = currentTool.getRegion(self.canvas)
else:
region = self.canvas.map
if hasattr(operator, "apply2"):
if mouseLeftDown:
if self.commands.operatorState == None:
self.commands.operatorState = True
region = operator.apply2(mapPoint, mousePoint, region, copy.deepcopy(region))
self.commands.update(operator=self.commands.currentOperator.name)
elif self.commands.operatorState != None:
self.commands.currentOperator = None
self.commands.update(operator=None)
return
else:
region = operator.apply(copy.deepcopy(region))
self.commands.currentOperator = None
if (currentTool.__class__ == ToolObject) \
and (currentTool.toolState >= currentTool.TS_SELECT):
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self) if self.popupEventDc == None else self.popupEventDc
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
currentTool.setRegion(self.canvas, None, region, [len(region[0]), len(region)], currentTool.external)
currentTool.onSelectEvent(self.canvas, (0, 0), self.dispatchPatchSingle, eventDc, True, wx.MOD_NONE, None, currentTool.targetRect)
currentTool._drawSelectRect(currentTool.targetRect, self.dispatchPatchSingle, eventDc)
eventDc.SetDeviceOrigin(*eventDcOrigin)
else:
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self) if self.popupEventDc == None else self.popupEventDc
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
self.canvas.journal.begin()
for numRow in range(len(region)):
for numCol in range(len(region[numRow])):
self.dirty = True if not self.dirty else self.dirty
self.dispatchPatchSingle(eventDc, False, [numCol, numRow, *region[numRow][numCol]])
self.canvas.journal.end()
self.commands.update(dirty=self.dirty, undoLevel=self.canvas.journal.patchesUndoLevel)
eventDc.SetDeviceOrigin(*eventDcOrigin)
# }}}
# {{{ applyTool(self, eventDc, eventMouse, keyChar, keyCode, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, tool, viewRect)
def applyTool(self, eventDc, eventMouse, keyChar, keyCode, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, tool, viewRect):
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
@ -223,7 +264,9 @@ class RoarCanvasWindow(GuiWindow):
viewRect = self.GetViewStart(); eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, viewRect);
mouseDragging, mouseLeftDown, mouseRightDown = event.Dragging(), event.LeftIsDown(), event.RightIsDown()
mapPoint = self.backend.xlateEventPoint(event, eventDc, viewRect)
if mouseRightDown \
if self.commands.currentOperator != None:
self.applyOperator(self.commands.currentTool, mapPoint, mouseLeftDown, event.GetLogicalPosition(eventDc), self.commands.currentOperator, viewRect)
elif mouseRightDown \
and (self.commands.currentTool.__class__ == ToolObject) \
and (self.commands.currentTool.toolState >= self.commands.currentTool.TS_SELECT):
self.popupEventDc = eventDc; self.PopupMenu(self.operatorsMenu); self.popupEventDc = None;

View File

@ -162,7 +162,7 @@ class ToolObject(Tool):
elif self.objectSize != objectSize:
if self.objectSize == None:
self.objectSize = objectSize
self.targetRect[1] = [t + d for t, d in zip(self.targetRect[1], (a - b for a, b in zip(self.objectSize, objectSize)))]
self.targetRect[1] = [t + d for t, d in zip(self.targetRect[1], (b - a for a, b in zip(self.objectSize, objectSize)))]
if self.srcRect == None:
self.srcRect = self.targetRect
self.objectMap, self.objectSize = objectMap, objectSize