Skip to content

Instantly share code, notes, and snippets.

@mcneds
Last active June 15, 2023 20:32
Show Gist options
  • Save mcneds/d6bd36ac808970184bb0c6295acc902c to your computer and use it in GitHub Desktop.
Save mcneds/d6bd36ac808970184bb0c6295acc902c to your computer and use it in GitHub Desktop.
import itertools
import matplotlib.pyplot as plt
import numpy as np
import sympy as sp
from matplotlib.patches import Polygon
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import tkinter as tk
from tkinter import ttk
min_side_length = 1
max_side_length = 10
# Generate all possible scalene triangles with integer side lengths in the range
scalene_triangles = [t for t in itertools.combinations_with_replacement(range(min_side_length, max_side_length + 1), 3) if sum(t) > 2 * max(t)]
# Generate all possible right triangles with integer side lengths in the range
right_triangles = [t for t in itertools.combinations_with_replacement(range(min_side_length, max_side_length + 1), 3) if max(t)**2 == sum(x**2 for x in t if x != max(t))]
# Initialize the mode, index, and filter
mode = 'scalene'
index = 0
filter = 'both'
# Initialize the side length and angle filters
side_filters = [None, None, None]
def calculate_angle(t, i):
a, b, c = t
if i == 0:
return sp.acos((b**2 + c**2 - a**2) / (2 * b * c))
elif i == 1:
return sp.acos((a**2 + c**2 - b**2) / (2 * a * c))
elif i == 2:
return sp.acos((a**2 + b**2 - c**2) / (2 * a * b))
def set_side_filter(i, value):
global side_filters
try:
side_filters[i] = int(value)
except ValueError:
side_filters[i] = None
draw_triangle()
def filter_triangles(triangles):
# Filter out triangles that don't satisfy the triangle inequality theorem
triangles = [t for t in triangles if t[0] + t[1] >= t[2] and t[0] + t[2] >= t[1] and t[1] + t[2] >= t[0]]
if filter == 'acute':
triangles = [t for t in triangles if max(t)**2 < sum(x**2 for x in t if x != max(t))]
elif filter == 'obtuse':
triangles = [t for t in triangles if max(t)**2 > sum(x**2 for x in t if x != max(t))]
for i in range(3):
if side_filters[i] is not None:
triangles = [t for t in triangles if t[i] == side_filters[i]]
return triangles
def draw_triangle():
global index
root.focus()
# Clear the previous plot
ax.clear()
# Select one of the triangles based on the mode, index, and filter
triangles = filter_triangles(scalene_triangles if mode == 'scalene' else right_triangles)
if not triangles:
canvas.draw()
index_label.config(text='No possible triangles')
return
a, b, c = triangles[index]
# Calculate the angles of the triangle
angle_A = sp.acos((b**2 + c**2 - a**2) / (2 * b * c))
angle_B = sp.acos((a**2 + c**2 - b**2) / (2 * a * c))
angle_C = sp.acos((a**2 + b**2 - c**2) / (2 * a * b))
# Calculate the coordinates of the vertices
vertex_A = (0, 0)
vertex_B = (a, 0)
vertex_C = (b * np.cos(float(angle_C)), b * np.sin(float(angle_C)))
# Draw the triangle with colored sides
triangle_AB = Polygon([vertex_A, vertex_B], closed=None, fill=None, edgecolor='r',linewidth=line_width.get())
triangle_BC = Polygon([vertex_B, vertex_C], closed=None, fill=None, edgecolor='g',linewidth=line_width.get())
triangle_CA = Polygon([vertex_C, vertex_A], closed=None, fill=None, edgecolor='b',linewidth=line_width.get())
ax.add_patch(triangle_AB)
ax.add_patch(triangle_BC)
ax.add_patch(triangle_CA)
# Display the measures of the sides and angles in their corresponding colors
label_a.config(text=f'a={a}\nA={sp.deg(angle_A).evalf():.2f}')
label_b.config(text=f'b={b}\nB={sp.deg(angle_B).evalf():.2f}')
label_c.config(text=f'c={c}\nC={sp.deg(angle_C).evalf():.2f}')
# Set the limits of the axes with a margin
ax.set_xlim(-1, max(a, b, c) + 3)
ax.set_ylim(-3, max(a, b, c) + 2)
ax.axis('equal') # Ensure the aspect ratio is correct
# Redraw the plot
canvas.draw()
# Update the index label
index_label.config(text=f'Triangle {index + 1} of {len(triangles)}')
def set_mode(new_mode):
global mode, index
mode = new_mode
index = 0 # Reset the index when the mode changes
draw_triangle()
def change_index(delta):
global index
triangles = filter_triangles(scalene_triangles if mode == 'scalene' else right_triangles)
index = (index + delta) % len(triangles) # Use modulo to wrap around the list
draw_triangle()
def set_filter(new_filter):
global filter, index
filter = new_filter
index = 0 # Reset the index when the filter changes
draw_triangle()
entry_sides = []
entry_angles = []
def generate_triangles():
global index, min_side_length, max_side_length
index = 0 # Reset the index
try:
min_side_length = int(entry_min_side_length.get())
max_side_length = int(entry_max_side_length.get())
except ValueError:
# Handle the case where the user enters an invalid value
min_side_length = 1
max_side_length = 10
for i in range(3):
set_side_filter(i, entry_sides[i].get())
draw_triangle()
# Create a Tkinter window
root = tk.Tk()
line_width = tk.DoubleVar(value=1)
# Create a matplotlib figure and a canvas to draw on
fig, ax = plt.subplots(figsize=(10, 8)) # Adjust the size as needed
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.get_tk_widget().grid(row=0, column=0) # Use grid instead of pack
# Create a parent frame to contain all the other frames
frame_parent = tk.Frame(root)
frame_parent.grid(row=1, column=0) # Use grid instead of pack
# Create frames for the filter buttons, navigation buttons, and mode buttons
frame_filter = tk.Frame(frame_parent)
frame_filter.pack(side=tk.LEFT, padx=20)
frame_nav = tk.Frame(frame_parent)
frame_nav.pack(side=tk.LEFT, padx=20)
frame_mode = tk.Frame(frame_parent)
frame_mode.pack(side=tk.LEFT, padx=20)
# Add buttons for changing the filter
button_acute = tk.Button(master=frame_filter, text="Acute filter", command=lambda: set_filter('acute'))
button_acute.pack(side=tk.TOP)
button_obtuse = tk.Button(master=frame_filter, text="Obtuse filter", command=lambda: set_filter('obtuse'))
button_obtuse.pack(side=tk.TOP)
button_both = tk.Button(master=frame_filter, text="Both filter", command=lambda: set_filter('both'))
button_both.pack(side=tk.TOP)
# Add a label to display the current index and buttons for navigating through the list of triangles
button_prev = tk.Button(master=frame_nav, text="Previous triangle", command=lambda: change_index(-1))
button_prev.pack(side=tk.LEFT)
index_label = tk.Label(master=frame_nav)
index_label.pack(side=tk.LEFT)
button_next = tk.Button(master=frame_nav, text="Next triangle", command=lambda: change_index(1))
button_next.pack(side=tk.LEFT)
# Add buttons for changing the mode
button_scalene = tk.Button(master=frame_mode, text="Scalene mode", command=lambda: set_mode('scalene'))
button_scalene.pack(side=tk.TOP)
button_right = tk.Button(master=frame_mode, text="Right mode", command=lambda: set_mode('right'))
button_right.pack(side=tk.TOP)
# Create a frame for the labels
frame_labels = tk.Frame(root)
frame_labels.grid(row=0, column=1) # Use grid instead of pack
# Add labels for the sides and angles
label_a = tk.Label(master=frame_labels, text="", bg="red", fg="white")
label_a.pack(side=tk.TOP)
label_b = tk.Label(master=frame_labels, text="", bg="green", fg="white")
label_b.pack(side=tk.TOP)
label_c = tk.Label(master=frame_labels, text="", bg="blue", fg="white")
label_c.pack(side=tk.TOP)
# Add a slider for the line width
slider_line_width = ttk.Scale(master=frame_labels, from_=1, to=10, variable=line_width, command=lambda _: draw_triangle())
slider_line_width.pack(side=tk.BOTTOM)
for i, color in enumerate(['red', 'green', 'blue']):
frame_filter = tk.Frame(frame_labels, bg=color)
frame_filter.pack(side=tk.TOP, fill=tk.X, padx=10, pady=10)
label_side = tk.Label(master=frame_filter, text="Side length:")
label_side.pack(side=tk.LEFT)
entry_side = tk.Entry(master=frame_filter)
entry_side.pack(side=tk.LEFT)
entry_sides.append(entry_side)
button_generate = tk.Button(master=frame_labels, text="Generate", command=generate_triangles)
button_generate.pack(side=tk.BOTTOM)
frame_side_length = tk.Frame(frame_labels)
frame_side_length.pack(side=tk.TOP, fill=tk.X, padx=10, pady=10)
label_min_side_length = tk.Label(master=frame_side_length, text="Min side length:")
label_min_side_length.pack(side=tk.LEFT)
entry_min_side_length = tk.Entry(master=frame_side_length)
entry_min_side_length.pack(side=tk.LEFT)
label_max_side_length = tk.Label(master=frame_side_length, text="Max side length:")
label_max_side_length.pack(side=tk.LEFT)
entry_max_side_length = tk.Entry(master=frame_side_length)
entry_max_side_length.pack(side=tk.LEFT)
# Draw the initial triangle
draw_triangle()
tk.mainloop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment