Embedded the matplotlib in a GtkViewport.

Still trying to work out the kinks, as only one plot is showing,
and it's not updating with the sliders. I'll get there sooner
or later. Also updated the gitignore, and added images showing
the parametric equations used to calculate the graphs. Neat!
This commit is contained in:
A.M. Rowsell 2021-06-27 15:40:30 -04:00
commit 3e96363771
Signed by: amr
GPG key ID: 0B6E2D8375CF79A9
7 changed files with 217 additions and 29 deletions

3
.gitignore vendored
View file

@ -1,3 +1,6 @@
# don't track temporary files
*~
# Created by https://www.toptal.com/developers/gitignore/api/kdevelop4 # Created by https://www.toptal.com/developers/gitignore/api/kdevelop4
# Edit at https://www.toptal.com/developers/gitignore?templates=kdevelop4 # Edit at https://www.toptal.com/developers/gitignore?templates=kdevelop4

BIN
img/epitrochoid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
img/hypotrochoid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
img/small_epitrochoid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
img/small_hypotrochoid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

153
spirographs.glade Normal file
View file

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<object class="GtkAdjustment" id="bigRadiusAdjustment">
<property name="lower">0.10</property>
<property name="upper">100</property>
<property name="value">0.10</property>
<property name="step-increment">0.10</property>
<property name="page-increment">1</property>
<signal name="value-changed" handler="bigRadiusAdjustment_value_changed_cb" swapped="no"/>
</object>
<object class="GtkAdjustment" id="distanceAdjustment">
<property name="lower">0.10</property>
<property name="upper">100</property>
<property name="value">0.10</property>
<property name="step-increment">0.10</property>
<property name="page-increment">1</property>
<signal name="value-changed" handler="distanceAdjustment_value_changed_cb" swapped="no"/>
</object>
<object class="GtkAdjustment" id="smallRadiusAdjustment">
<property name="lower">0.10</property>
<property name="upper">100</property>
<property name="value">0.10</property>
<property name="step-increment">0.10</property>
<property name="page-increment">1</property>
<signal name="value-changed" handler="smallRadiusAdjustment_value_changed_cb" swapped="no"/>
</object>
<object class="GtkWindow" id="spWindow">
<property name="can-focus">False</property>
<signal name="destroy" handler="onDestroy" swapped="no"/>
<child>
<!-- n-columns=2 n-rows=5 -->
<object class="GtkGrid" id="topGrid">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="row-spacing">10</property>
<property name="column-homogeneous">True</property>
<child>
<object class="GtkScale" id="bigRadiusScale">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="adjustment">bigRadiusAdjustment</property>
<property name="show-fill-level">True</property>
<property name="fill-level">100</property>
<property name="round-digits">1</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkScale" id="smallRadiusScale">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="adjustment">smallRadiusAdjustment</property>
<property name="show-fill-level">True</property>
<property name="fill-level">100</property>
<property name="round-digits">1</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkScale" id="distanceScale">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="adjustment">distanceAdjustment</property>
<property name="show-fill-level">True</property>
<property name="fill-level">100</property>
<property name="round-digits">1</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="bigRadiusLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Big Radius (R):</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="smallRadiusLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Small Radius (r):</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="distanceLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Distance (d):</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkImage" id="epichondroidFormulaImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="pixbuf">img/small_epitrochoid.png</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">4</property>
</packing>
</child>
<child>
<object class="GtkImage" id="hypotrochoidFormulaImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="pixbuf">img/small_hypotrochoid.png</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">4</property>
</packing>
</child>
<child>
<object class="GtkViewport" id="plotViewport">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
<property name="width">2</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

View file

@ -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 # gtk imports
import gi import gi
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GObject, GLib from gi.repository import Gtk, Gdk, GObject, GLib
# math/plot imports # math/plot imports
from matplotlib.backends.backend_gtk3agg import (
FigureCanvasGTK3Agg as FigureCanvas)
from matplotlib.figure import Figure
import numpy as np import numpy as np
import matplotlib.pyplot as plt
import math import math
import sys import sys
class spirographs: 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): def __init__(self):
self.bigRadius = 12 self.bigRadius = 12
self.smallRadius = 5 self.smallRadius = 5
self.distance = 4 self.distance = 4
self.highestTheta = (np.lcm(smallRadius, bigRadius)/bigRadius) * 2 * math.pi self.highestTheta = (np.lcm(self.smallRadius, self.bigRadius)/self.bigRadius) * 2 * math.pi
self.stepSize = highestTheta / 4096 self.stepSize = self.highestTheta / 4096
self.recalcPoints() self.recalcPoints()
self.showPlot() 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): def recalcPoints(self):
self.epiX = np.array([calcEpiX(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([calcEpiY(i, bigRadius, smallRadius, distance) for i in np.arange(0, highestTheta, stepSize)]) self.epiY = np.array([self.calcEpiY(i) for i in np.arange(0, self.highestTheta, self.stepSize)])
self.hypoX = np.array([calcHypoX(i, bigRadius, smallRadius, distance) for i in np.arange(0, highestTheta, stepSize)]) self.hypoX = np.array([self.calcHypoX(i) for i in np.arange(0, self.highestTheta, self.stepSize)])
self.hypoY = np.array([calcHypoY(i, bigRadius, smallRadius, distance) for i in np.arange(0, highestTheta, stepSize)]) self.hypoY = np.array([self.calcHypoY(i) for i in np.arange(0, self.highestTheta, self.stepSize)])
def showPlot(self): def showPlot(self):
plt.subplot(1, 2, 1) viewport = builder.get_object('plotViewport')
plt.title(f"Epichondroid of {bigRadius}, {smallRadius}, {distance}") self.plotFigure = Figure(figsize=(5, 4), dpi=100)
plt.plot(epiX, epiY) self.subPlot1 = self.plotFigure.add_subplot()
plt.subplot(1, 2, 2) #self.subPlot1.title = f"Epichondroid of {self.bigRadius}, {self.smallRadius}, {self.distance}"
plt.title(f"Hypochondroid of {bigRadius}, {smallRadius}, {distance}") self.subPlot1.plot(self.epiX, self.epiY)
plt.plot(hypoX, hypoY) self.subPlot2 = self.plotFigure.add_subplot()
plt.show() #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 = Gtk.Builder()
builder.add_from_file("spirographs.glade") builder.add_from_file("spirographs.glade")
@ -57,4 +90,3 @@ window = builder.get_object("spWindow")
window.show_all() window.show_all()
Gtk.main() Gtk.main()