commit 35bc9e25f2b89da70b49e4911e2b9d34a8bdb520 Author: paoda Date: Fri Nov 29 15:21:16 2019 -0400 Initial Commit diff --git a/intergral.py b/intergral.py new file mode 100644 index 0000000..e93e930 --- /dev/null +++ b/intergral.py @@ -0,0 +1,299 @@ +# Assignment Name: Integral Approximator +# Author: Rekai Nyangadzayi Musuka +# Description: Approximates Any Given Function +# Inputs: None +# Outputs: A Graph in Turtle with the Approximated and Theorized Area, highlighted by drawing trapezoids or squares. +# Version: 2019.0107.0 + +#LINK TO GRAPH (DESMOS): https://www.desmos.com/calculator/igcdhrepy3 + +import math +import turtle + +CURSOR_SIZE = 0.1 # Makes the "Turtle" Invisible +DRAW_SPEED = 0 + +#Cosmetic +FONT_FAMILY = "Consolas" +NUM_FONT_SIZE = "8" +LABEL_FONT_SIZE = "10" +TEXT_SPACING = 15 +FIGURES = 5 + +# Changes these to mess with the scale of the graph +SCALE_X = 80 # Scale of the X Axis OG: 80 +SCALE_Y = 80 # Scale of the Y Axis OG: 80 +UNIT_LENGTH = 400 # Determines the maximum domain and Range of the cartesian plane (in this case (-400, 400) in both X and Y) + + +# Best not to mess with these :) +MIN_VALUE = -2 # The Domain of X is -2 <= x <= 2 +MAX_VALUE = 2 +TICK_STEP = 1 # Draw Ticks according to an interval of this variable +PLANE_INCREMENT = 1 + +# Change DRAW_STEP for a more or less accurately drawn function +DRAW_STEP = 0.01 # What we Increment by when drawing the function + +# Change to shorten or lengthen the height of the tick +TICK_HEIGHT = 10 # The Height of the Tick line + +# Changes How Detailed the Area Approximation (Trapezoid or Rectangle) is (higher is better) +ITERATIONS = 100 + +# Determines whether the program uses the rectangle rule +# or trapezoid rule (trapezoid is more exact I think?) + +# TODO? Implement Simpson's Rule? +APPROX_TYPE = "trapezoid" # or square + +# Initializing Turtle Variables +cursor = turtle.Turtle() +win = turtle.Screen() +cursor.shape() +cursor.hideturtle() +cursor.speed(DRAW_SPEED) + +# Draws a Cursor Tick +def draw_tick(num, cursor, height, x_axis = False): + + # Function draws the numerical value of the x or y value at any given tick + def draw_num(angle, distance, num): + cursor.penup() + cursor.left(angle) + cursor.forward(distance) + cursor.write(num, align="center", font=(FONT_FAMILY, NUM_FONT_SIZE, "normal")) + cursor.backward(distance) + cursor.left(-angle) + cursor.pendown() + + if x_axis: + draw_num(-90, (TICK_HEIGHT * 2), num) + else: + draw_num(90, (TICK_HEIGHT * 2), num) + + for angle in [90, -180]: + cursor.left(angle) + cursor.forward(height / 2) + cursor.backward(height / 2) + cursor.left(90) + + +# Draws the Cartesian Plane +def create_plane(): + cursor.penup() + + cursor.goto(-UNIT_LENGTH, 0) # Goes to Fartheset left coordinate + cursor.pendown() + + # Function will draw text nearby a given x value (used to draw axis labels). + def draw_axis_label(angle, distance, text): + cursor.penup() + cursor.left(angle) + cursor.forward(distance) + cursor.write(text, align="center", font=(FONT_FAMILY, LABEL_FONT_SIZE, "normal")) + cursor.backward(distance) + cursor.left(-angle) + cursor.pendown() + + # This draws the x line of the cartesian plane + while (cursor.pos()[0] <= UNIT_LENGTH): + if (cursor.pos()[0] == -UNIT_LENGTH): draw_axis_label(-90, TICK_HEIGHT * 4, "X Axis") # If x is the furthermost left value, draw the x axis label below + if (cursor.pos()[0] % (TICK_STEP * SCALE_X) == 0): draw_tick(cursor.pos()[0], cursor, TICK_HEIGHT, True) # Draw ticks whenever the condition is met + cursor.forward(PLANE_INCREMENT) + + cursor.penup() + cursor.goto(0, -UNIT_LENGTH) # Goes to the farthest bottom coordinate + cursor.pendown() + cursor.left(90) + + # This draws the y line of the cartesian plane + while (cursor.pos()[1] <= UNIT_LENGTH): + if (cursor.pos()[1] == -UNIT_LENGTH): draw_axis_label(90, TICK_HEIGHT * 4, "Y Axis") # If y is the furthermost bottom value, draw the y axis label there + if (cursor.pos()[1] % (TICK_STEP * SCALE_Y) == 0): draw_tick(cursor.pos()[1], cursor, TICK_HEIGHT, True) # Draw ticks whenever the if condition is met + cursor.forward(PLANE_INCREMENT) + + cursor.penup() + +# Used for Calculus Portion of Program +def draw_rectangle(length, width): + arr = [width, length, width, length] + i = 0 + + while (i < len(arr)): + cursor.forward(arr[i]) + cursor.left(90) + i += 1 + + cursor.penup() + cursor.forward(width) + cursor.pendown() + + +# Used for Calculus Portion of Program +# Should Rewrite this sometime +def draw_trapezoid(a, b, h): + # Drawing Trapezoid given Area + width_of_triangle = b - a + + c = math.sqrt(math.pow(h, 2) + math.pow(width_of_triangle, 2)) + + angle = math.atan2(h, width_of_triangle) * (180 / math.pi) + + cursor.forward(h) + cursor.left(90) + cursor.forward(b) + + cursor.left(180 - angle) + cursor.forward(c) + cursor.left(angle) + cursor.forward(a) + cursor.left(90) + + cursor.penup() + cursor.forward(h) + cursor.pendown() + + +# The function that draws the upper half of the Heart +def f(x): + return math.sqrt(1 - ((math.fabs(x) - 1) ** 2)) + +# The function that draws the Lower Half the the Heart +def g(x): + return -3 * math.sqrt(1 - math.sqrt(math.fabs(x) / 2 )) + +# Calls create_plane() which draws and labels the cartesian plane. +create_plane() + + +# This will set x to -2 and this while loop will then draw f(x) at -2 to 2 +# Iterates between MIN_VALUE and MAX_VALUE and draws the calculated y value given the x value +x = MIN_VALUE +while (x <= MAX_VALUE): + cursor.goto(SCALE_X * x, SCALE_Y * f(x)) # f(x) is the top half of the heart + cursor.pendown() + x += DRAW_STEP + +cursor.penup() + +# # This will set x to -2 and loop through g(x) until it reaches 2 +# # Iterates between MIN_VALUE and MAX_VALUE and draws the calculated y value given the x value +x = MIN_VALUE +while (x <= MAX_VALUE): + cursor.goto(SCALE_X * x, SCALE_Y * g(x)) # g(x) is the bottom half of the heart + cursor.pendown() + x += DRAW_STEP + + +# This code draws the functions used onto the screen. +cursor.penup() +cursor.goto(UNIT_LENGTH / 2, UNIT_LENGTH / 2) # We go to the middle of quadrant I +cursor.right(180) +cursor.write("Functions:", align="center", font=(FONT_FAMILY, NUM_FONT_SIZE, "normal")) +cursor.forward(TEXT_SPACING) +cursor.write("Top: y = math.sqrt(1 - ((math.abs(x) - 1) ** 2))", align="center", font=(FONT_FAMILY, NUM_FONT_SIZE, "normal")) +cursor.forward(TEXT_SPACING) +cursor.write("Bottom: y= math.sqrt(1 - math.sqrt(math.abs(x) / 2 ))", align="center", font=(FONT_FAMILY, NUM_FONT_SIZE, "normal")) + +# Intergral Stuff Goes Here + +cursor.left(90) # Settings Cursor Back to it's default position (heading right) + + +# Allows us to import a reasonably acccurate estimate as to what the intergral of a given function will be +# By accurate I mean more accurate than our estimate. +import scipy.integrate as integrate + +# Calculates Percent Error (the one we learned in chemistry) +def percent_error(exper, accept): + return (math.fabs(exper - accept) / accept) * 100 + +# Approximates the intergral of a function using the square rule. +def approx_integral_square(func, a, b, n): + # n is how many rectangles you want. + # area is length * width + area = 0.0 + width = (b - a) / n + i = 1 + + while (i <= n): + height = func(a + (i - 1) * width) + + draw_rectangle(height * SCALE_Y, width * SCALE_Y) + + area += width * height + i += 1 + + return area + +# Approximates the integral of a function using the trapezoid rule. +def approx_integral_trapezoid(func, a, b, n): + # n is how many trapezoids you want + width = (b - a) / n + res = func(a) + func(b) + i = 1 + + prev_height = func(a) + + while (i < n): + height = func(a + (i * width)) + draw_trapezoid(prev_height * SCALE_Y, height * SCALE_Y, width * SCALE_Y) + res += 2 * height + + prev_height = height + i += 1 + + return (width / 2) * res + + +# Exact Results +upper_area = integrate.quad(lambda x: f(x), MIN_VALUE, MAX_VALUE) +lower_area = integrate.quad(lambda x: g(x), MIN_VALUE, MAX_VALUE) + +# Don't really care for the margin of error at the moment +# Absolute Value of the area because we don't care about signed area at the moment.math. +upper_area = math.fabs(upper_area[0]) +lower_area = math.fabs(lower_area[0]) + +upper_area_approx = 0 +lower_area_approx = 0 + +if (APPROX_TYPE == "square"): + cursor.goto(MIN_VALUE * SCALE_X, 0) + upper_area_approx = math.fabs(approx_integral_square(lambda x: f(x), MIN_VALUE, MAX_VALUE, ITERATIONS)) + + cursor.goto(MIN_VALUE * SCALE_X, 0) + lower_area_approx = math.fabs(approx_integral_square(lambda x: g(x), MIN_VALUE, MAX_VALUE, ITERATIONS)) +elif (APPROX_TYPE == "trapezoid"): + cursor.goto(MIN_VALUE * SCALE_X, 0) + upper_area_approx = math.fabs(approx_integral_trapezoid(lambda x: f(x), MIN_VALUE, MAX_VALUE, ITERATIONS)) + + cursor.goto(MIN_VALUE * SCALE_X, 0) + lower_area_approx = math.fabs(approx_integral_trapezoid(lambda x: g(x), MIN_VALUE, MAX_VALUE, ITERATIONS)) + + +# Probably Want to Display the Results or smth here. + +cursor.penup() +cursor.goto(-(UNIT_LENGTH / 2), UNIT_LENGTH / 2) # We go to the middle of quadrant II +cursor.right(90) +if (APPROX_TYPE == "square"): + cursor.write("Using the Square Rule:", align="center", font=(FONT_FAMILY, NUM_FONT_SIZE, "normal")) +else: + cursor.write("Using the Trapezoid Rule:", align="center", font=(FONT_FAMILY, NUM_FONT_SIZE, "normal")) + +cursor.forward(TEXT_SPACING) +cursor.forward(TEXT_SPACING) + +cursor.write(("Approximated Top Half Area: " + str(round(upper_area_approx, FIGURES)) + " | Theorized: " + str(round(upper_area, FIGURES))), align="center", font=(FONT_FAMILY, NUM_FONT_SIZE, "normal")) +cursor.forward(TEXT_SPACING) +cursor.write(("Percent Error: " + str(round(percent_error(upper_area_approx, upper_area), FIGURES)) + "%"), align="center", font=(FONT_FAMILY, NUM_FONT_SIZE, "normal")) + +cursor.forward(TEXT_SPACING) +cursor.forward(TEXT_SPACING) +cursor.write(("Approximated Bottom Half Area: " + str(round(lower_area_approx, FIGURES)) + " | Theorized: " + str(round(lower_area, FIGURES))), align="center", font=(FONT_FAMILY, NUM_FONT_SIZE, "normal")) +cursor.forward(TEXT_SPACING) +cursor.write(("Percent Error: " + str(round(percent_error(lower_area_approx, lower_area), FIGURES)) + "%"), align="center", font=(FONT_FAMILY, NUM_FONT_SIZE, "normal")) + +win.exitonclick() \ No newline at end of file diff --git a/testing.py b/testing.py new file mode 100644 index 0000000..dfe6d09 --- /dev/null +++ b/testing.py @@ -0,0 +1,147 @@ +import math +import turtle +import scipy.integrate as integrate + +MIN_VALUE = -2 +MAX_VALUE = 2 +ITERATIONS = 100 + +TURTLE_SCALE = 100 + +#### TURTLE STUFF +cursor = turtle.Turtle() +win = turtle.Screen() +cursor.shape() +cursor.hideturtle() +cursor.speed(0) +cursor.goto(-2, 0) + +def draw_rectangle(length, width): + arr = [width, length, width, length] + i = 0 + + while (i < len(arr)): + cursor.forward(arr[i]) + cursor.left(90) + i += 1 + + cursor.penup() + cursor.forward(width) + cursor.pendown() + +def draw_trapezoid(a, b, h): + # Drawing Trapezoid given Area + width_of_triangle = b - a + + c = math.sqrt(math.pow(h, 2) + math.pow(width_of_triangle, 2)) + + angle = math.atan2(h, width_of_triangle) * (180 / math.pi) + + cursor.forward(h) + cursor.left(90) + cursor.forward(b) + + cursor.left(180 - angle) + cursor.forward(c) + cursor.left(angle) + cursor.forward(a) + cursor.left(90) + + cursor.penup() + cursor.forward(h) + cursor.pendown() +### + +# The function that draws the upper half of the Heart +def f(x): + return math.sqrt(1 - ((math.fabs(x) - 1) ** 2)) + +# The function that draws the Lower Half the the Heart +def g(x): + return -3 * math.sqrt(1 - math.sqrt(math.fabs(x) / 2 )) + +def percent_error(exper, accept): + return (math.fabs(exper - accept) / accept) * 100 + +def approx_integral_square(func, a, b, n): + # n is how many rectangles you want. + # area is length * width + area = 0.0 + width = (b - a) / n + i = 1 + + while (i <= n): + height = func(a + (i - 1) * width) + + # draw_rectangle(height * TURTLE_SCALE, width * TURTLE_SCALE) + + area += width * height + i += 1 + + return area + +def approx_integral_trapezoid(func, a, b, n): + # n is how many trapezoids you want + width = (b - a) / n + res = func(a) + func(b) + i = 1 + + prev_height = func(a) + + while (i < n): + height = func(a + (i * width)) + draw_trapezoid(prev_height * TURTLE_SCALE, height * TURTLE_SCALE, width * TURTLE_SCALE) + res += 2 * height + + prev_height = height + i += 1 + + return (width / 2) * res + + + + +# Compare Results +upper_area = integrate.quad(lambda x: f(x), MIN_VALUE, MAX_VALUE) +lower_area = integrate.quad(lambda x: g(x), MIN_VALUE, MAX_VALUE) + +# Don't really care for the margin of error at the moment +# Absolute Value of the area because we don't care about signed area at the moment.math. +upper_area = math.fabs(upper_area[0]) +lower_area = math.fabs(lower_area[0]) + + +print("Using the Square Method:") + +upper_area_approx = math.fabs(approx_integral_square(lambda x: f(x), MIN_VALUE, MAX_VALUE, ITERATIONS)) +# cursor.goto(-2, 0) +lower_area_approx = math.fabs(approx_integral_square(lambda x: g(x), MIN_VALUE, MAX_VALUE, ITERATIONS)) + +print("\nApproximated Top Half Area:", upper_area_approx, "Original:", upper_area) +print("Percent Error: ", percent_error(upper_area_approx, upper_area), "%", sep="") + +print("\nApproximated Bottom Half Area:", lower_area_approx, "Original:", lower_area) +print("Percent Error: ", percent_error(lower_area_approx, lower_area), "%\n", sep="") + +print("------") +print("\nUsing the Trapezoid Method:") + +upper_area_approx = math.fabs(approx_integral_trapezoid(lambda x: f(x), MIN_VALUE, MAX_VALUE, ITERATIONS)) +cursor.goto(-2, 0) +lower_area_approx = math.fabs(approx_integral_trapezoid(lambda x: g(x), MIN_VALUE, MAX_VALUE, ITERATIONS)) + + +print("\nApproximated Top Half Area:", upper_area_approx, "Original:", upper_area) +print("Percent Error: ", percent_error(upper_area_approx, upper_area), "%", sep="") + +print("\nApproximated Bottom Half Area:", lower_area_approx, "Original:", lower_area) +print("Percent Error: ", percent_error(lower_area_approx, lower_area), "%", sep="") + + +# Contratulations! + + + +# Figuring out How to draw a Trapezoid in Turtle. + +win.exitonclick() \ No newline at end of file