Add a somewhat flaky Generate plugin for L-systems that currently only runs the Koch Snowflake system.

This plugin will probably not live for very long as it seems better to make Generate plugins directly for different L-systems.
This commit is contained in:
David Vierra 2015-05-13 01:11:51 -10:00
parent 16460f69d7
commit 4e9a020f0f
2 changed files with 227 additions and 0 deletions

120
src/plugins/koch.py Normal file
View File

@ -0,0 +1,120 @@
# coding=utf-8
"""
koch
"""
from __future__ import absolute_import, division, print_function
import logging
from math import pi, cos, sin
from mcedit2.synth.l_system import Geometric, Line
from mceditlib.geometry import Vector
log = logging.getLogger(__name__)
class Side(Line):
"""
A side of a Koch snowflake. A side can be replaced by four shorter sides in a _/\_ configuration.
Inherits from Line, so renders as a line.
Properties:
blocktype
p1: Vector
p2: Vector
"""
def replace(self):
p1 = self.p1
p2 = self.p2
d = p2 - p1
mid1 = p1 + d * 0.3333
mid2 = p2 - d * 0.3333
spike = mid1 + rotatePoint(d * -0.3333)
# First segment
yield Side(p1=p1, p2=mid1, blocktype=self.blocktype)
# Second segment
yield Side(p1=mid1, p2=spike, blocktype=self.blocktype)
# Third segment
yield Side(p1=spike, p2=mid2, blocktype=self.blocktype)
# Fourth segment
yield Side(p1=mid2, p2=p2, blocktype=self.blocktype)
# One-third of a circle
THETA = 2 * pi / 3
COS_THETA = cos(THETA)
SIN_THETA = sin(THETA)
# Rotate the point around 0, 0 by one-third of a circle.
# Also works for vectors! We use it to rotate the vector
# added to get the new point when replacing a side with
# four smaller sides.
def rotatePoint((x, y, z)):
u"""
Rotation matrix:
| cos θ - sin θ |
| sin θ cos θ |
"""
x2 = COS_THETA * x - SIN_THETA * z
z2 = SIN_THETA * x + COS_THETA * z
return Vector(x2, y, z2)
class Snowflake(Geometric):
"""
Koch snowflake.
The initial symbol is replaced by six sides.
Each side is replaced by four shorter sides, each 1/3 the length of the original side in a _/\_ configuration.
The side replacement is recursively defined and may be repeated any number of times.
This could probably be optimized a bit by introducing a LineStrip symbol and inheriting SideStrip from it.
Properties:
blocktype
+ properties inherited from Geometric
"""
def replace(self):
# Find the first corner's position relative to the hexagon's center
center = Vector(self.center.x, self.miny, self.center.z)
firstPoint = Vector(self.maxx, self.miny, self.center.z) - center
points = []
point = firstPoint
for i in range(3):
points.append(point)
point = rotatePoint(point)
# Translate the corners back to the hexagon's position
points = [p + center for p in points]
# Compute the endpoints of the line segments
startPoints = points
endPoints = points[1:] + points[:1]
for p1, p2 in zip(startPoints, endPoints):
yield Side(p1=p1, p2=p2, blocktype=self.blocktype)
if __name__ == '__main__':
point = 4.5, 0, 5.0
print("THETA", THETA)
print("COS_THETA", COS_THETA)
print("SIN_THETA", SIN_THETA)
for i in range(3):
print(point)
point = rotatePoint(point)

View File

@ -0,0 +1,107 @@
"""
l_system_plugin
"""
from __future__ import absolute_import, division, print_function
import logging
from PySide import QtGui
from mcedit2.editortools.generate import GeneratePlugin
from mcedit2.plugins import registerGeneratePlugin
import koch
from mcedit2.synth.l_system import renderBlocks, renderSceneNodes, applyReplacementsIterated
from mcedit2.util.showprogress import showProgress
from mcedit2.widgets.blockpicker import BlockTypeButton
from mcedit2.widgets.layout import Column
from mcedit2.widgets.spinslider import SpinSlider
from mceditlib.schematic import createSchematic
log = logging.getLogger(__name__)
class LSystemPlugin(GeneratePlugin):
def __init__(self, editorSession):
log.warn("type(LSystemPlugin): %s, type(self): %s (IS? %s)",
LSystemPlugin, type(self),
LSystemPlugin is type(self))
super(LSystemPlugin, self).__init__(editorSession)
self.optionsWidget = None
self.displayName = self.tr("L-System Test")
def getOptionsWidget(self):
if self.optionsWidget:
return self.optionsWidget
widget = QtGui.QWidget()
self.systemsBox = QtGui.QComboBox()
self.systemsBox.addItem("Koch Snowflake")
self.blocktypeButton = BlockTypeButton()
self.blocktypeButton.editorSession = self.editorSession
self.blocktypeButton.block = "minecraft:stone"
self.blocktypeButton.blocksChanged.connect(self.updatePreview)
self.iterationsSlider = SpinSlider()
self.iterationsSlider.setMinimum(1)
self.iterationsSlider.setMaximum(100)
self.iterationsSlider.setValue(3)
self.iterationsSlider.valueChanged.connect(self.updatePreview)
widget.setLayout(Column(self.systemsBox,
self.iterationsSlider,
self.blocktypeButton, # xxx from systemsBox
None))
self.optionsWidget = widget
return widget
def getPreviewNode(self, bounds):
system = koch.Snowflake(bounds, blocktype=self.blocktypeButton.block)
symbol_list = [system]
max_iterations = self.iterationsSlider.value()
def process(_symbol_list):
for iteration, _symbol_list in applyReplacementsIterated(_symbol_list, max_iterations):
yield iteration, max_iterations
yield _symbol_list
symbol_list = showProgress("Generating...", process(symbol_list), cancel=True)
if symbol_list is False:
return
sceneNodes = renderSceneNodes(symbol_list)
return sceneNodes
def generate(self, bounds, blocktypes):
# self.systemsBox.value()
schematic = createSchematic(bounds.size, blocktypes)
dim = schematic.getDimension()
system = koch.Snowflake(dim.bounds, blocktype=self.blocktypeButton.block)
symbol_list = [system]
max_iterations = self.iterationsSlider.value()
def process(_symbol_list):
for iteration, _symbol_list in applyReplacementsIterated(_symbol_list, max_iterations):
yield iteration, max_iterations
yield _symbol_list
symbol_list = showProgress("Generating...", process(symbol_list), cancel=True)
if symbol_list is False:
return
import pprint
pprint.pprint(symbol_list)
rendering = renderBlocks(symbol_list)
print("Rendering %d blocks" % len(rendering))
for x, y, z, blockType in rendering:
dim.setBlock(x, y, z, blockType)
return schematic
registerGeneratePlugin(LSystemPlugin)