These automatically are updated when the slider moves, and vice versa. Still cannot seem to fix the adjustment increment. It makes no sense.
130 lines
5.5 KiB
Python
130 lines
5.5 KiB
Python
# -*- 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 math
|
|
import sys
|
|
|
|
class spirographs:
|
|
def __init__(self):
|
|
self.bigRadius = 12
|
|
self.smallRadius = 5
|
|
self.distance = 4
|
|
self.highestTheta = (np.lcm(self.smallRadius, self.bigRadius)/self.bigRadius) * 2 * math.pi
|
|
self.stepSize = self.highestTheta / 4096
|
|
# update initial slider positions
|
|
self.bigRadiusAdjustment = builder.get_object('bigRadiusAdjustment')
|
|
self.smallRadiusAdjustment = builder.get_object('smallRadiusAdjustment')
|
|
self.distanceAdjustment = builder.get_object('distanceAdjustment')
|
|
self.bigRadiusEntry = builder.get_object('bigRadiusEntry')
|
|
self.smallRadiusEntry = builder.get_object('smallRadiusEntry')
|
|
self.distanceEntry = builder.get_object('distanceEntry')
|
|
# set the initial values to a neat shape
|
|
self.bigRadiusAdjustment.set_value(self.bigRadius)
|
|
self.smallRadiusAdjustment.set_value(self.smallRadius)
|
|
self.distanceAdjustment.set_value(self.distance)
|
|
self.bigRadiusEntry.set_text(str(self.bigRadius))
|
|
self.smallRadiusEntry.set_text(str(self.smallRadius))
|
|
self.distanceEntry.set_text(str(self.distance))
|
|
# try and set the step size?!
|
|
self.bigRadiusAdjustment.set_step_increment(0.5)
|
|
self.smallRadiusAdjustment.set_step_increment(0.5)
|
|
self.distanceAdjustment.set_step_increment(0.5)
|
|
# create initial plot
|
|
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.bigRadiusEntry.set_text(str(self.bigRadius))
|
|
self.recalcPoints()
|
|
self.updatePlot()
|
|
|
|
def bigRadiusEntry_changed_cb(self, widget):
|
|
self.bigRadius = float(widget.get_text())
|
|
self.bigRadiusAdjustment.set_value(self.bigRadius)
|
|
|
|
def smallRadiusAdjustment_value_changed_cb(self, widget):
|
|
self.smallRadius = widget.get_value()
|
|
self.smallRadiusEntry.set_text(str(self.smallRadius))
|
|
self.recalcPoints()
|
|
self.updatePlot()
|
|
|
|
def smallRadiusEntry_changed_cb(self, widget):
|
|
self.smallRadius = float(widget.get_text())
|
|
self.smallRadiusAdjustment.set_value(self.smallRadius)
|
|
|
|
def distanceAdjustment_value_changed_cb(self, widget):
|
|
self.distance = widget.get_value()
|
|
self.distanceEntry.set_text(str(self.distance))
|
|
self.recalcPoints()
|
|
self.updatePlot()
|
|
|
|
def distanceEntry_changed_cb(self, widget):
|
|
self.distance = float(widget.get_text())
|
|
self.distanceAdjustment.set_value(self.distance)
|
|
|
|
def recalcPoints(self):
|
|
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):
|
|
viewport = builder.get_object('plotViewport')
|
|
self.plotFigure = Figure(figsize=(5, 8), dpi=100)
|
|
self.epiPlot, self.hypoPlot = self.plotFigure.subplots(1, 2)
|
|
self.epiPlot.set_title(f"Epitrochoid of {self.bigRadius}, {self.smallRadius}, {self.distance}")
|
|
self.epiPlot.plot(self.epiX, self.epiY)
|
|
self.hypoPlot.set_title(f"Hypotrochoid of {self.bigRadius}, {self.smallRadius}, {self.distance}")
|
|
self.hypoPlot.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.epiPlot.clear()
|
|
self.hypoPlot.clear()
|
|
self.epiPlot.set_title(f"Epitrochoid of {self.bigRadius}, {self.smallRadius}, {self.distance}")
|
|
self.hypoPlot.set_title(f"Hypotrochoid of {self.bigRadius}, {self.smallRadius}, {self.distance}")
|
|
self.epiPlot.plot(self.epiX, self.epiY)
|
|
self.hypoPlot.plot(self.hypoX, self.hypoY)
|
|
self.canvas.draw_idle()
|
|
|
|
builder = Gtk.Builder()
|
|
builder.add_from_file("spirographs.glade")
|
|
sp = spirographs()
|
|
builder.connect_signals(sp)
|
|
window = builder.get_object("spWindow")
|
|
window.show_all()
|
|
|
|
Gtk.main()
|