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:
parent
16460f69d7
commit
4e9a020f0f
120
src/plugins/koch.py
Normal file
120
src/plugins/koch.py
Normal 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)
|
107
src/plugins/l_system_plugin.py
Normal file
107
src/plugins/l_system_plugin.py
Normal 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)
|
Reference in New Issue
Block a user