diff --git a/.gitignore b/.gitignore index b13ff3b..9e3c124 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# don't track temporary files +*~ + # Created by https://www.toptal.com/developers/gitignore/api/kdevelop4 # Edit at https://www.toptal.com/developers/gitignore?templates=kdevelop4 diff --git a/img/epitrochoid.png b/img/epitrochoid.png new file mode 100644 index 0000000..fc17c8c Binary files /dev/null and b/img/epitrochoid.png differ diff --git a/img/hypotrochoid.png b/img/hypotrochoid.png new file mode 100644 index 0000000..fc1ed1e Binary files /dev/null and b/img/hypotrochoid.png differ diff --git a/img/small_epitrochoid.png b/img/small_epitrochoid.png new file mode 100644 index 0000000..9602f61 Binary files /dev/null and b/img/small_epitrochoid.png differ diff --git a/img/small_hypotrochoid.png b/img/small_hypotrochoid.png new file mode 100644 index 0000000..0a2efa9 Binary files /dev/null and b/img/small_hypotrochoid.png differ diff --git a/spirographs.glade b/spirographs.glade new file mode 100644 index 0000000..d155178 --- /dev/null +++ b/spirographs.glade @@ -0,0 +1,153 @@ + + + + + + 0.10 + 100 + 0.10 + 0.10 + 1 + + + + 0.10 + 100 + 0.10 + 0.10 + 1 + + + + 0.10 + 100 + 0.10 + 0.10 + 1 + + + + False + + + + + True + False + 10 + True + + + True + True + bigRadiusAdjustment + True + 100 + 1 + + + 1 + 1 + + + + + True + True + smallRadiusAdjustment + True + 100 + 1 + + + 1 + 2 + + + + + True + True + distanceAdjustment + True + 100 + 1 + + + 1 + 3 + + + + + True + False + Big Radius (R): + + + 0 + 1 + + + + + True + False + Small Radius (r): + + + 0 + 2 + + + + + True + False + Distance (d): + + + 0 + 3 + + + + + True + False + img/small_epitrochoid.png + + + 0 + 4 + + + + + True + False + img/small_hypotrochoid.png + + + 1 + 4 + + + + + True + False + + + + + + 0 + 0 + 2 + + + + + + diff --git a/spirographs.py b/spirographs.py index 18d6c75..e5796d2 100644 --- a/spirographs.py +++ b/spirographs.py @@ -1,53 +1,86 @@ +# -*- coding: utf-8 -*- + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + # gtk imports import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, Gdk, GObject, GLib # math/plot imports +from matplotlib.backends.backend_gtk3agg import ( + FigureCanvasGTK3Agg as FigureCanvas) +from matplotlib.figure import Figure import numpy as np -import matplotlib.pyplot as plt import math import sys class spirographs: - - - def calcEpiX(theta, bigRadius, smallRadius, distance): - return ((bigRadius + smallRadius) * math.cos(theta)) - (distance * math.cos(((bigRadius + smallRadius)/(smallRadius))*theta)) - - def calcEpiY(theta, bigRadius, smallRadius, distance): - return ((bigRadius + smallRadius) * math.sin(theta)) - (distance * math.sin(((bigRadius + smallRadius)/(smallRadius))*theta)) - - def calcHypoX(theta, bigRadius, smallRadius, distance): - return ((bigRadius - smallRadius) * math.cos(theta)) + (distance * math.cos(((bigRadius - smallRadius)/(smallRadius))*theta)) - - def calcHypoY(theta, bigRadius, smallRadius, distance): - return ((bigRadius - smallRadius) * math.sin(theta)) - (distance * math.sin(((bigRadius - smallRadius)/(smallRadius))*theta)) - def __init__(self): self.bigRadius = 12 self.smallRadius = 5 self.distance = 4 - self.highestTheta = (np.lcm(smallRadius, bigRadius)/bigRadius) * 2 * math.pi - self.stepSize = highestTheta / 4096 + self.highestTheta = (np.lcm(self.smallRadius, self.bigRadius)/self.bigRadius) * 2 * math.pi + self.stepSize = self.highestTheta / 4096 self.recalcPoints() self.showPlot() + def calcEpiX(self, theta): + return ((self.bigRadius + self.smallRadius) * math.cos(theta)) - (self.distance * math.cos(((self.bigRadius + self.smallRadius)/(self.smallRadius))*theta)) + + def calcEpiY(self, theta): + return ((self.bigRadius + self.smallRadius) * math.sin(theta)) - (self.distance * math.sin(((self.bigRadius + self.smallRadius)/(self.smallRadius))*theta)) + + def calcHypoX(self, theta): + return ((self.bigRadius - self.smallRadius) * math.cos(theta)) + (self.distance * math.cos(((self.bigRadius - self.smallRadius)/(self.smallRadius))*theta)) + + def calcHypoY(self, theta): + return ((self.bigRadius - self.smallRadius) * math.sin(theta)) - (self.distance * math.sin(((self.bigRadius - self.smallRadius)/(self.smallRadius))*theta)) + + def onDestroy(self, widget): + Gtk.main_quit() + return + + def bigRadiusAdjustment_value_changed_cb(self, widget): + self.bigRadius = widget.get_value() + self.recalcPoints() + self.updatePlot() + + def smallRadiusAdjustment_value_changed_cb(self, widget): + self.smallRadius = widget.get_value() + self.recalcPoints() + self.updatePlot() + + def distanceAdjustment_value_changed_cb(self, widget): + self.distance = widget.get_value() + self.recalcPoints() + self.updatePlot() + def recalcPoints(self): - self.epiX = np.array([calcEpiX(i, bigRadius, smallRadius, distance) for i in np.arange(0, highestTheta, stepSize)]) - self.epiY = np.array([calcEpiY(i, bigRadius, smallRadius, distance) for i in np.arange(0, highestTheta, stepSize)]) - self.hypoX = np.array([calcHypoX(i, bigRadius, smallRadius, distance) for i in np.arange(0, highestTheta, stepSize)]) - self.hypoY = np.array([calcHypoY(i, bigRadius, smallRadius, distance) for i in np.arange(0, highestTheta, stepSize)]) + self.epiX = np.array([self.calcEpiX(i) for i in np.arange(0, self.highestTheta, self.stepSize)]) + self.epiY = np.array([self.calcEpiY(i) for i in np.arange(0, self.highestTheta, self.stepSize)]) + self.hypoX = np.array([self.calcHypoX(i) for i in np.arange(0, self.highestTheta, self.stepSize)]) + self.hypoY = np.array([self.calcHypoY(i) for i in np.arange(0, self.highestTheta, self.stepSize)]) def showPlot(self): - plt.subplot(1, 2, 1) - plt.title(f"Epichondroid of {bigRadius}, {smallRadius}, {distance}") - plt.plot(epiX, epiY) - plt.subplot(1, 2, 2) - plt.title(f"Hypochondroid of {bigRadius}, {smallRadius}, {distance}") - plt.plot(hypoX, hypoY) - plt.show() + viewport = builder.get_object('plotViewport') + self.plotFigure = Figure(figsize=(5, 4), dpi=100) + self.subPlot1 = self.plotFigure.add_subplot() + #self.subPlot1.title = f"Epichondroid of {self.bigRadius}, {self.smallRadius}, {self.distance}" + self.subPlot1.plot(self.epiX, self.epiY) + self.subPlot2 = self.plotFigure.add_subplot() + #self.subPlot2.title = f"Hypochondroid of {self.bigRadius}, {self.smallRadius}, {self.distance}" + self.subPlot2.plot(self.hypoX, self.hypoY) + self.canvas = FigureCanvas(self.plotFigure) + self.canvas.set_size_request(800, 600) + viewport.add(self.canvas) + + def updatePlot(self): + self.subPlot1.plot(self.epiX, self.epiY) + self.subPlot2.plot(self.hypoX, self.hypoY) builder = Gtk.Builder() builder.add_from_file("spirographs.glade") @@ -57,4 +90,3 @@ window = builder.get_object("spWindow") window.show_all() Gtk.main() -