Because the code for this and the next chapter build on each other, I’m approaching the way I talk about these two sets of exercises a bit differently than previous posts. First, let’s take a look at the instructions.

Instructions

[1] Add a distanceFromPoint method that works similar to distanceFromOrigin except that it takes a Point as a parameter and computes the distance between that point and self.


[2] Add a method reflect_x to Point which returns a new Point, one which is the reflection of the point about the x-axis. For example, Point(3, 5).reflect_x() is (3, -5)


[3] Add a method slope_from_origin which returns the slope of the line joining the origin to the point. For example,

What cases will cause your method to fail? Return None when it happens.


[4] The equation of a straight line is “y = ax + b”, (or perhaps “y = mx + c”). The coefficients a and b completely describe the line. Write a method in the Point class so that if a point instance is given another point, it will compute the equation of the straight line joining the two points. It must return the two coefficients as a tuple of two values. For example,

This tells us that the equation of the line joining the two points is “y = 2x + 3”. When will your method fail?


[5] Add a method called move that will take two parameters, call them dx and dy. The method will cause the point to move in the x and y direction the number of units given. (Hint: you will change the values of the state of the point)


[6] Given three points that fall on the circumference of a circle, find the center and radius of the circle.

Starting Point

As you can probably tell from the first question, we had been building on the Point class throughout the entire chapter. Here is was the code I was working with by the time I got to the exercises:

Class Point:
    """ Point class for representing and manipulating x,y coordinates. """

    def __init__(self, initX, initY):
        """ Create a new point at the given coordinates. """
        self.x = initX
        self.y = initY

    def getX(self):
        return self.x

    def getY(self):
        return self.y

    def distanceFromOrigin(self):
        return ((self.x ** 2) + (self.y ** 2)) ** 0.5

    def __str__(self):
        return "x=" + str(self.x) + ", y=" + str(self.y)

A quick note for anyone who isn’t familiar with Object Oriented Programming: in this and the next post, you are going to see a lot of references to the keyword self. self, similar to this in PHP and JavaScript, is simply a reference to the object that is currently in use. An object is kind of like a variable in that it can be manipulated. However, unlike a variable, Objects are complex bits of information that have defined characteristics and actions (aka methods, which is just a class’ version of functions) that can be referenced and used.

An example of this the Turtle object that I have used quite abundantly in this Thinkcspy series. Turtles have characteristics like position, color, and shape as well and actions like goto, left, and penup. In this and the next chapter, I learned to create something like Turtles.

Discussion of the Solutions

I’m not going to discuss every solution because some of them are fairly self-explanatory. I will however discuss the more interesting and complex problems.

Problems 3 and 4: Points of Failure

For both of these problems, I was asked to consider where my solutions would fail. Because division is involved in both solutions, the obvious point of failure would be division by 0, which would indicate that the slope is either vertical or nonexistent if the two points happen to point to the same location. For some reason, this was not immediately apparent to me for the problem 3. I’m embarrassed to admit that as of time of writing, I just added a check for that in my solution to that problem.

Problem 6: Circumcenter

Mkay…. Do y’all remember having to calculate the radius and center of a circle in high school geometry class? ‘Cause I don’t. Shot out to Khan Academy (and AI) for helping me understand this concept.

The thought process behind finding a circle from three points is that in certain angles, three points can be connected to form a triangle. For any given triangle, there is a single circle that encapsulates said triangles. From there you can surmise that in order to find the center of the circle, you need find the intersection of the lines that run through the midpoints of each side of the triangle. Once the center has been calculated, you can use it to find the distance between itself and one of three points, which just so happens to be the radius.

Phew… math class over. Now how do we do this programmatically? Well first, we need a method to calculate the midpoint. With that, we can find the midpoint of two of the lines and then use those two lines to find the intersection, which also happens to be a midpoint. Now why just two lines instead of three? Well, if we know where the intersection of two lines are, then we know the intersection of all three lines. Adding a third line in the equation would be redundant.

For your convenience, here is the primary method used for this problem:

    def findCenterRadius(self, pnt1, pnt2):
        m1, b1 = self.slopeIntercept(pnt1)
        m2, b2 = self.slopeIntercept(pnt2)
        
        if m1 == m2 and b1 == b2:
            return "Error: Points are on the same line."
        
        mid1X, mid1Y = self.getMid(pnt1)
        mid2X, mid2Y = self.getMid(pnt2)
        center = Point(mid1X, mid1Y).getMid(Point(mid2X, mid2Y))
        
        r = self.distanceFromPoint(Point(center[0], center[1]))
        
        return "Circumcenter: {}; Radius: {}".format(center, r)

In observing this block of code, you’ve probably noticed that I’m not actually using lines to find the midpoints. I’m using points. Even where a line is referenced with calls to slopeIntercept(), that doesn’t figure into the calculation of the center. Let me explain (cue your favorite sappy R&B/pop song here).

The calls to slopeIntercept() are there to make sure all three points don’t sit on the same line. self is used as the central point to test this. This is because if the points form a triangle, then self acts as an apex. If they don’t, self is a simply another point on a straight line. The way I test for this in the program is by comparing the slope and intercepts. If both are the same, that means they represent the same line. In that case, there would be no point in attempting to find a center or radius.

And that’s it! Check out the code below to see the solutions to the other problems as well as the methods that were called in this problem.

The Code

# Runestone.Academy thinkcspy course
# Chapter 17
# Problems 1 - 6

import math

class Point:
    """ Point class for representing and manipulating x,y coordinates. """

    def __init__(self, initX, initY):
        """ Create a new point at the given coordinates. """
        self.x = initX
        self.y = initY

    def getX(self):
        return self.x

    def getY(self):
        return self.y

    def distanceFromOrigin(self):
        return ((self.x ** 2) + (self.y ** 2)) ** 0.5
 
    # Solution to Problem 1 
    def distanceFromPoint(self, pnt):
        dx = self.x - pnt.getX()
        dy = self.y - pnt.getY()
        
        return math.sqrt(dx**2 + dy**2)
    
    # Solution to Problem 2
    def reflect_x(self):
        return (self.x, -self.y)
    
    # Solution to Problem 3
    def slopeFromOrigin(self):
        if self.x == 0:
            return None

        return self.y / self.x
    
    # Solution to Problem 4
    def slopeIntercept(self, pnt):
        dx = self.x - pnt.getX()
        dy = self.y - pnt.getY()
        
        # Accounting for points of failure (divide by 0)
        if dx == 0:
            if dy == 0:
                return "Error: Cannot calculate slope of a single point."
            else:
                return "Error: Cannot calculate slope of a vertical line."
        
        m = dy / dx
        b = self.y - m*self.x
        
        return (m, b)
    
    # Helper function for Problem 6
    def getMid(self, pnt):
        midX = (self.x + pnt.getX()) / 2
        midY = (self.y + pnt.getY()) / 2
        
        return (midX, midY) 
    
    # Solution to Problem 6
    def findCenterRadius(self, pnt1, pnt2):
        m1, b1 = self.slopeIntercept(pnt1)
        m2, b2 = self.slopeIntercept(pnt2)
        
        if m1 == m2 and b1 == b2:
            return "Error: Points are on the same line."
        
        mid1X, mid1Y = self.getMid(pnt1)
        mid2X, mid2Y = self.getMid(pnt2)
        center = Point(mid1X, mid1Y).getMid(Point(mid2X, mid2Y))
        
        r = self.distanceFromPoint(Point(center[0], center[1]))
        
        return "Circumcenter: {}; Radius: {}".format(center, r)
        
    # Solution to Problem 5
    def move(self, pnt):
        self.x += pnt.getX()
        self.y += pnt.getY()
        
        return (self.x, self.y)

    def __str__(self):
        return "x=" + str(self.x) + ", y=" + str(self.y)

Leave a Reply

Your email address will not be published. Required fields are marked *

fourteen − eleven =

Expand ToC