KochSnowflake
Repository source: KochSnowflake
Description¶
This demo uses recursion to represent a Koch snowflake fractal. For more information about this fractal, there are many resources on the web: http://en.wikipedia.org/wiki/Koch_snowflake, http://mathworld.wolfram.com/KochSnowflake.html.
Other languages
See (Cxx)
Question
If you have a question about this example, please use the VTK Discourse Forum
Code¶
KochSnowflake.py
#!/usr/bin/env python
from math import pi, cos, sin, sqrt
# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import (
vtkIntArray,
vtkLookupTable,
vtkPoints
)
from vtkmodules.vtkCommonDataModel import (
vtkCellArray,
vtkPolyData,
vtkPolyLine,
vtkTriangle
)
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
LEVEL = 6
def as_polyline(points, level):
'''
Koch Snowflake as a vtkPolyLine
'''
# Use the points from the previous iteration to create the points of the next
# level. There is an assumption on my part that the curve is traversed in a
# counterclockwise fashion. If the initial triangle above is written to
# describe clockwise motion, the points will face inward instead of outward.
for i in range(level):
temp = vtkPoints()
# The first point of the previous vtkPoints is the first point of the next vtkPoints.
temp.InsertNextPoint(*points.GetPoint(0))
# Iterate over 'edges' in the vtkPoints
for i in range(1, points.GetNumberOfPoints()):
x0, y0, z0 = points.GetPoint(i - 1)
x1, y1, z1 = points.GetPoint(i)
t = sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2)
nx = (x1 - x0) / t # x-component of edge unit tangent
ny = (y1 - y0) / t # y-component of edge unit tangent
# the points describing the Koch snowflake edge
temp.InsertNextPoint(x0 + nx * t / 3, y0 + ny * t / 3, 0.)
temp.InsertNextPoint(x0 + nx * t / 2 + ny * t * sqrt(3) / 6, y0 + ny * t / 2 - nx * t * sqrt(3) / 6, 0.)
temp.InsertNextPoint(x0 + nx * 2 * t / 3, y0 + ny * 2 * t / 3, 0.)
temp.InsertNextPoint(x0 + nx * t, y0 + ny * t, 0.)
points = temp
# draw the outline
lines = vtkCellArray()
pl = vtkPolyLine()
pl.GetPointIds().SetNumberOfIds(points.GetNumberOfPoints())
for i in range(points.GetNumberOfPoints()):
pl.GetPointIds().SetId(i, i)
lines.InsertNextCell(pl)
# complete the polydata
polydata = vtkPolyData()
polydata.SetLines(lines)
polydata.SetPoints(points)
return polydata
def as_triangles(indices, cellarray, level, data):
'''
Koch Snowflake as a collection of vtkTriangles
'''
if len(indices) >= 3:
stride = len(indices) // 4
indices.append(indices[-1] + 1)
triangle = vtkTriangle()
triangle.GetPointIds().SetId(0, indices[stride])
triangle.GetPointIds().SetId(1, indices[2 * stride])
triangle.GetPointIds().SetId(2, indices[3 * stride])
cellarray.InsertNextCell(triangle)
data.InsertNextValue(level)
as_triangles(indices[0: stride], cellarray, level + 1, data)
as_triangles(indices[stride: 2 * stride], cellarray, level + 1, data)
as_triangles(indices[2 * stride: 3 * stride], cellarray, level + 1, data)
as_triangles(indices[3 * stride: -1], cellarray, level + 1, data)
def main():
colors = vtkNamedColors()
# Initially, set up the points to be an equilateral triangle. Note that the
# first point is the same as the last point to make this a closed curve when
# I create the vtkPolyLine.
points = vtkPoints()
for i in range(4):
points.InsertNextPoint(cos(2.0 * pi * i / 3), sin(2 * pi * i / 3.0), 0.0)
outline_pd = as_polyline(points, LEVEL)
# You have already gone through the trouble of putting the points in the
# right places - so 'all' you need to do now is to create polygons from the
# points that are in the vtkPoints.
# The points that are passed in, have an overlap of the beginning and the
# end. For this next trick, I will need a list of the indices in the
# vtkPoints. They're consecutive, so thats pretty straightforward.
indices = [i for i in range(outline_pd.GetPoints().GetNumberOfPoints() + 1)]
triangles = vtkCellArray()
# Set this up for each of the initial sides, then call the recursive function.
stride = (len(indices) - 1) // 3
# The cell data will allow us to color the triangles based on the level of
# the iteration of the Koch snowflake.
data = vtkIntArray()
data.SetNumberOfComponents(0)
data.SetName('Iteration Level')
# This is the starting triangle.
t = vtkTriangle()
t.GetPointIds().SetId(0, 0)
t.GetPointIds().SetId(1, stride)
t.GetPointIds().SetId(2, 2 * stride)
triangles.InsertNextCell(t)
data.InsertNextValue(0)
as_triangles(indices[0: stride + 1], triangles, 1, data)
as_triangles(indices[stride: 2 * stride + 1], triangles, 1, data)
as_triangles(indices[2 * stride: -1], triangles, 1, data)
triangle_pd = vtkPolyData()
triangle_pd.SetPoints(outline_pd.GetPoints())
triangle_pd.SetPolys(triangles)
triangle_pd.GetCellData().SetScalars(data)
# ---------------- #
# rendering stuff #
# ---------------- #
outline_mapper = vtkPolyDataMapper()
outline_mapper.SetInputData(outline_pd)
lut = vtkLookupTable()
lut.SetNumberOfTableValues(256)
lut.SetHueRange(0.6, 0.6)
lut.SetSaturationRange(0.0, 1.0)
lut.Build()
triangle_mapper = vtkPolyDataMapper()
triangle_mapper.SetInputData(triangle_pd)
triangle_mapper.SetScalarRange(0.0, LEVEL)
triangle_mapper.SetLookupTable(lut)
outline_actor = vtkActor()
outline_actor.SetMapper(outline_mapper)
triangle_actor = vtkActor()
triangle_actor.SetMapper(triangle_mapper)
outline_ren = vtkRenderer()
outline_ren.AddActor(outline_actor)
outline_ren.SetViewport(0.0, 0.0, 0.5, 1.0)
triangle_ren = vtkRenderer()
triangle_ren.AddActor(triangle_actor)
triangle_ren.SetViewport(0.5, 0.0, 1.0, 1.0)
triangle_ren.SetActiveCamera(outline_ren.GetActiveCamera())
renw = vtkRenderWindow()
renw.AddRenderer(outline_ren)
renw.AddRenderer(triangle_ren)
renw.SetSize(800, 400)
renw.SetWindowName('KochSnowflake')
outline_ren.SetBackground(colors.GetColor3d('CornFlowerBLue'))
triangle_ren.SetBackground(colors.GetColor3d('MistyRose'))
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(renw)
outline_ren.ResetCamera()
renw.Render()
iren.Start()
if __name__ == '__main__':
main()