Skip to content

Instantly share code, notes, and snippets.

@NT7S
Last active March 11, 2016 04:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save NT7S/2c2f4faca3c650418a62 to your computer and use it in GitHub Desktop.
Save NT7S/2c2f4faca3c650418a62 to your computer and use it in GitHub Desktop.
Strange Attractors
# coding: utf-8
# attractors.py
# 10 March 2016
#
# Copyright 2016 Jason Milldrum
#
# Draw various attractors in a GTK window
# See:
# https://en.wikipedia.org/wiki/Lorenz_system
# https://en.wikipedia.org/wiki/R%C3%B6ssler_attractor
# https://en.wikipedia.org/wiki/Chua%27s_circuit
import math
import gi
import cairo
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class MyWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Strange Attractors")
self.move(100, 100)
self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
self.add(self.vbox)
self.stack = Gtk.Stack()
self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
self.stack.set_transition_duration(300)
self.lorenz_drawing = Gtk.DrawingArea()
self.lorenz_drawing.set_size_request(400, 400)
self.lorenz_drawing.connect("draw", self.draw_lorenz)
self.stack.add_titled(self.lorenz_drawing, "lorenz", "Lorenz Attractor")
#self.vbox.pack_start(self.drawing, True, True, 0)
#self.add(self.drawing)
self.rossler_drawing = Gtk.DrawingArea()
self.rossler_drawing.set_size_request(400, 400)
self.rossler_drawing.connect("draw", self.draw_rossler)
self.stack.add_titled(self.rossler_drawing, "rossler", "Rössler Attractor")
self.chua_drawing = Gtk.DrawingArea()
self.chua_drawing.set_size_request(400, 400)
self.chua_drawing.connect("draw", self.draw_chua)
self.stack.add_titled(self.chua_drawing, "chua", "Chua's Circuit")
self.stack_switcher = Gtk.StackSwitcher()
self.stack_switcher.set_stack(self.stack)
self.switcher_align = Gtk.Alignment(xalign=0.5, yalign=0.0, xscale=0.0, yscale=0.0)
self.switcher_align.add(self.stack_switcher)
self.vbox.pack_start(self.stack, True, True, 0)
self.vbox.pack_start(self.switcher_align, False, False, 0)
self.lorenz_coords = []
self.rossler_coords = []
self.chua_coords = []
def draw_lorenz(self, widget, cr):
center_x = self.get_size()[0] / 2
center_y = self.get_size()[1] / 2
scale_x = self.get_size()[0] / 70.0
scale_y = self.get_size()[1] / 70.0
offset_y = center_y * 0.9
cr.set_line_width(1)
cr.set_source_rgb(0.0, 0.0, 0.0)
cr.move_to(center_x, center_y - offset_y)
for i in self.lorenz_coords:
# Draw the X-Z view
cr.line_to((i[0] * scale_x) + center_x, (i[2] * scale_y) + center_y - offset_y)
cr.stroke()
# Draw axes legend
cr.set_source_rgb(0.6, 0.0, 0.0)
cr.select_font_face("Courier", cairo.FONT_SLANT_NORMAL,
cairo.FONT_WEIGHT_BOLD)
cr.set_font_size(24)
(x, y, width, height, dx, dy) = cr.text_extents("X")
cr.move_to(center_x - width / 2, self.get_size()[1] - 40)
cr.show_text("X")
(x, y, width, height, dx, dy) = cr.text_extents("Z")
cr.move_to(self.get_size()[0] - width - 10, center_y)
cr.show_text("Z")
def draw_rossler(self, widget, cr):
center_x = self.get_size()[0] / 2
center_y = self.get_size()[1] / 2
scale_x = self.get_size()[0] / 60.0
scale_y = self.get_size()[1] / 60.0
offset_y = center_y * 0.01
cr.set_line_width(1)
cr.set_source_rgb(0.0, 0.0, 0.0)
cr.move_to(center_x, center_y - offset_y)
for i in self.rossler_coords:
# Draw the X-Y view
cr.line_to((i[0] * scale_x) + center_x, (i[1] * scale_y) + center_y - offset_y)
cr.stroke()
# Draw axes legend
cr.set_source_rgb(0.6, 0.0, 0.0)
cr.select_font_face("Courier", cairo.FONT_SLANT_NORMAL,
cairo.FONT_WEIGHT_BOLD)
cr.set_font_size(24)
(x, y, width, height, dx, dy) = cr.text_extents("X")
cr.move_to(center_x - width / 2, self.get_size()[1] - 40)
cr.show_text("X")
(x, y, width, height, dx, dy) = cr.text_extents("Y")
cr.move_to(self.get_size()[0] - width - 10, center_y)
cr.show_text("Y")
def draw_chua(self, widget, cr):
center_x = self.get_size()[0] / 2
center_y = self.get_size()[1] / 2
scale_x = self.get_size()[0] / 10.0
scale_y = self.get_size()[1] / 10.0
offset_y = center_y * 0.01
cr.set_line_width(1)
cr.set_source_rgb(0.0, 0.0, 0.0)
cr.move_to(center_x, center_y - offset_y)
for i in self.chua_coords:
# Draw the X-Z view
cr.line_to((i[0] * scale_x) + center_x, (i[2] * scale_y) + center_y - offset_y)
cr.stroke()
# Draw axes legend
cr.set_source_rgb(0.6, 0.0, 0.0)
cr.select_font_face("Courier", cairo.FONT_SLANT_NORMAL,
cairo.FONT_WEIGHT_BOLD)
cr.set_font_size(24)
(x, y, width, height, dx, dy) = cr.text_extents("X")
cr.move_to(center_x - width / 2, self.get_size()[1] - 40)
cr.show_text("X")
(x, y, width, height, dx, dy) = cr.text_extents("Z")
cr.move_to(self.get_size()[0] - width - 10, center_y)
cr.show_text("Z")
def lorenz(self):
h = 0.01
sigma = 10.0
rho = 28.0
beta = 8.0 / 3.0
x0 = 0.1
y0 = 0.0
z0 = 0.0
for i in range(10000):
x1 = x0 + h * sigma * (y0 - x0)
y1 = y0 + h * (x0 * (rho - z0) - y0)
z1 = z0 + h * (x0 * y0 - beta * z0)
x0 = x1
y0 = y1
z0 = z1
self.lorenz_coords.append([x0, y0, z0])
def rossler(self):
h = 0.02
a = 0.1
b = 0.1
c = 14.0
x0 = 0.1
y0 = 0.0
z0 = 0.0
for i in range(15000):
x1 = x0 + h * (-y0 - z0)
y1 = y0 + h * (x0 + a * y0)
z1 = z0 + h * (b + z0 * (x0 - c))
x0 = x1
y0 = y1
z0 = z1
self.rossler_coords.append([x0, y0, z0])
def chua(self):
h = 0.005
alpha = 15.6
beta = 28.0
d = -5.0 / 7.0
e = -8.0 / 7.0
x0 = 0.7
y0 = 0.0
z0 = 0.0
f = lambda x: d * x + 0.5 * (e - d) * (abs(x + 1) - abs(x - 1))
for i in range(15000):
x1 = x0 + h * (alpha * (y0 - x0 - f(x0)))
y1 = y0 + h * (x0 - y0 + z0)
z1 = z0 + h * (-beta * y0)
x0 = x1
y0 = y1
z0 = z1
self.chua_coords.append([x0, y0, z0])
win = MyWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
win.lorenz()
win.rossler()
win.chua()
Gtk.main()
@NT7S
Copy link
Author

NT7S commented Mar 11, 2016

strange attractors_005
strange attractors_004
strange attractors_003

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment