initial commit
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
from krita import Krita
|
||||
|
||||
from .grid_generator import GridGenerator
|
||||
|
||||
# Create the plugin instance and add it to Krita
|
||||
instance = Krita.instance()
|
||||
extension = GridGenerator(instance)
|
||||
instance.addExtension(extension)
|
||||
@@ -0,0 +1,194 @@
|
||||
import math
|
||||
|
||||
from krita import Krita, ManagedColor
|
||||
from PyQt5.QtCore import QPointF
|
||||
from PyQt5.QtWidgets import (
|
||||
QCheckBox,
|
||||
QComboBox,
|
||||
QDialog,
|
||||
QDoubleSpinBox,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QPushButton,
|
||||
QVBoxLayout,
|
||||
)
|
||||
|
||||
|
||||
class GridDialog(QDialog):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle("Grid Generator")
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
layout = QVBoxLayout(self)
|
||||
|
||||
# Thickness
|
||||
thickness_layout = QHBoxLayout()
|
||||
thickness_layout.addWidget(QLabel("Line Thickness (px):"))
|
||||
self.thickness_spin = QDoubleSpinBox()
|
||||
self.thickness_spin.setRange(0.1, 1000.0)
|
||||
self.thickness_spin.setValue(2.0)
|
||||
thickness_layout.addWidget(self.thickness_spin)
|
||||
layout.addLayout(thickness_layout)
|
||||
|
||||
# Width
|
||||
width_layout = QHBoxLayout()
|
||||
width_layout.addWidget(QLabel("Grid Width (px):"))
|
||||
self.width_spin = QDoubleSpinBox()
|
||||
self.width_spin.setRange(1.0, 10000.0)
|
||||
self.width_spin.setValue(100.0)
|
||||
self.width_spin.valueChanged.connect(self.on_width_changed)
|
||||
width_layout.addWidget(self.width_spin)
|
||||
layout.addLayout(width_layout)
|
||||
|
||||
# Height
|
||||
height_layout = QHBoxLayout()
|
||||
height_layout.addWidget(QLabel("Grid Height (px):"))
|
||||
self.height_spin = QDoubleSpinBox()
|
||||
self.height_spin.setRange(1.0, 10000.0)
|
||||
self.height_spin.setValue(100.0)
|
||||
self.height_spin.valueChanged.connect(self.on_height_changed)
|
||||
height_layout.addWidget(self.height_spin)
|
||||
layout.addLayout(height_layout)
|
||||
|
||||
# Lock Width/Height
|
||||
self.lock_check = QCheckBox("Lock Aspect Ratio")
|
||||
self.lock_check.setChecked(True)
|
||||
layout.addWidget(self.lock_check)
|
||||
|
||||
# Grid Type
|
||||
type_layout = QHBoxLayout()
|
||||
type_layout.addWidget(QLabel("Grid Type:"))
|
||||
self.type_combo = QComboBox()
|
||||
self.type_combo.addItems(["Square", "Hexagon"])
|
||||
type_layout.addWidget(self.type_combo)
|
||||
layout.addLayout(type_layout)
|
||||
|
||||
# Layer Option
|
||||
layer_layout = QHBoxLayout()
|
||||
layer_layout.addWidget(QLabel("Generate on:"))
|
||||
self.layer_combo = QComboBox()
|
||||
self.layer_combo.addItems(["New Layer", "Current Layer"])
|
||||
layer_layout.addWidget(self.layer_combo)
|
||||
layout.addLayout(layer_layout)
|
||||
|
||||
# Buttons
|
||||
button_layout = QHBoxLayout()
|
||||
self.ok_button = QPushButton("Generate")
|
||||
self.ok_button.clicked.connect(self.generate)
|
||||
self.cancel_button = QPushButton("Cancel")
|
||||
self.cancel_button.clicked.connect(self.reject)
|
||||
button_layout.addWidget(self.ok_button)
|
||||
button_layout.addWidget(self.cancel_button)
|
||||
layout.addLayout(button_layout)
|
||||
|
||||
def on_width_changed(self, value):
|
||||
if self.lock_check.isChecked():
|
||||
self.height_spin.blockSignals(True)
|
||||
self.height_spin.setValue(value)
|
||||
self.height_spin.blockSignals(False)
|
||||
|
||||
def on_height_changed(self, value):
|
||||
if self.lock_check.isChecked():
|
||||
self.width_spin.blockSignals(True)
|
||||
self.width_spin.setValue(value)
|
||||
self.width_spin.blockSignals(False)
|
||||
|
||||
def generate(self):
|
||||
app = Krita.instance()
|
||||
doc = app.activeDocument()
|
||||
if not doc:
|
||||
return
|
||||
|
||||
window = app.activeWindow()
|
||||
if not window:
|
||||
return
|
||||
|
||||
thickness = self.thickness_spin.value()
|
||||
width = self.width_spin.value()
|
||||
height = self.height_spin.value()
|
||||
grid_type = self.type_combo.currentText()
|
||||
layer_option = self.layer_combo.currentText()
|
||||
|
||||
# Determine target layer
|
||||
target_node = None
|
||||
if layer_option == "New Layer":
|
||||
target_node = doc.createNode("grid", "vectorlayer")
|
||||
doc.rootNode().addChildNode(target_node, None)
|
||||
else:
|
||||
target_node = doc.activeNode()
|
||||
# If current layer is not a vector layer, we should probably create one above it
|
||||
# because SVG drawing only works on vector layers in Krita's API.
|
||||
if target_node and target_node.type() != "vectorlayer":
|
||||
parent = target_node.parentNode()
|
||||
new_vector = doc.createNode("grid", "vectorlayer")
|
||||
parent.addChildNode(new_vector, target_node)
|
||||
target_node = new_vector
|
||||
|
||||
if not target_node:
|
||||
target_node = doc.createNode("grid", "vectorlayer")
|
||||
doc.rootNode().addChildNode(target_node, None)
|
||||
|
||||
self.draw_grid(target_node, doc, thickness, width, height, grid_type)
|
||||
doc.refreshProjection()
|
||||
self.accept()
|
||||
|
||||
def draw_grid(self, node, doc, thickness, width, height, grid_type):
|
||||
if grid_type == "Square":
|
||||
self.draw_square_grid(node, doc, thickness, width, height)
|
||||
else:
|
||||
self.draw_hexagon_grid(node, doc, thickness, width, height)
|
||||
|
||||
def draw_square_grid(self, node, doc, thickness, width, height):
|
||||
svg = f'<svg><g fill="none" stroke="black" stroke-width="{thickness}">'
|
||||
|
||||
# Vertical lines
|
||||
x = 0
|
||||
while x <= doc.width():
|
||||
svg += f'<line x1="{x}" y1="0" x2="{x}" y2="{doc.height()}" />'
|
||||
x += width
|
||||
|
||||
# Horizontal lines
|
||||
y = 0
|
||||
while y <= doc.height():
|
||||
svg += f'<line x1="0" y1="{y}" x2="{doc.width()}" y2="{y}" />'
|
||||
y += height
|
||||
|
||||
svg += "</g></svg>"
|
||||
node.addShapesFromSvg(svg)
|
||||
|
||||
def draw_hexagon_grid(self, node, doc, thickness, width, height):
|
||||
# Pointy topped hex grid
|
||||
# Horizontal spacing = width
|
||||
# Vertical spacing = height * 0.75
|
||||
|
||||
svg = f'<svg><g fill="none" stroke="black" stroke-width="{thickness}">'
|
||||
|
||||
v_spacing = height * 0.75
|
||||
h_spacing = width
|
||||
|
||||
rows = int(doc.height() / v_spacing) + 2
|
||||
cols = int(doc.width() / h_spacing) + 2
|
||||
|
||||
for r in range(rows):
|
||||
offset_x = (width / 2) if (r % 2 == 1) else 0
|
||||
for c in range(cols):
|
||||
cx = c * h_spacing + offset_x
|
||||
cy = r * v_spacing
|
||||
|
||||
# Vertices for pointy-topped hex
|
||||
v = [
|
||||
(cx, cy - height / 2),
|
||||
(cx + width / 2, cy - height / 4),
|
||||
(cx + width / 2, cy + height / 4),
|
||||
(cx, cy + height / 2),
|
||||
(cx - width / 2, cy + height / 4),
|
||||
(cx - width / 2, cy - height / 4),
|
||||
]
|
||||
|
||||
points = " ".join([f"{px},{py}" for px, py in v])
|
||||
svg += f'<polygon points="{points}" />'
|
||||
|
||||
svg += "</g></svg>"
|
||||
node.addShapesFromSvg(svg)
|
||||
@@ -0,0 +1,21 @@
|
||||
from krita import Extension
|
||||
|
||||
from .grid_dialog import GridDialog
|
||||
|
||||
|
||||
class GridGenerator(Extension):
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
|
||||
def setup(self):
|
||||
pass
|
||||
|
||||
def createActions(self, window):
|
||||
action = window.createAction(
|
||||
"grid_generator", "Grid Generator...", "tools/scripts"
|
||||
)
|
||||
action.triggered.connect(self.show_dialog)
|
||||
|
||||
def show_dialog(self):
|
||||
dialog = GridDialog()
|
||||
dialog.exec_()
|
||||
Reference in New Issue
Block a user