Draw a circle on an iPad map from three points in Pythonista
I happened across a copy of 57 Practical Programs & Games in BASIC while traveling a month ago, and was once again fascinated by the simple toolchests we used back in the seventies and eighties. I fooled around with it a bit, typing up the programs in HotPaw BASIC on the iPad. I now have the BASIC code in HotPaw necessary to tell what day of the week any date, post 1752, fell on, as well as a ChiSquare evaluator that I’ll probably never use.
While reading through it, I came across the code for Circle determined by three points and thought about how cool it would be to use that simple code on modern mobile tools. It should be a snap to write an onthefly app in HotPaw BASIC or Pythonista. Take a snapshot of a map, tap three points, and see what the circle is.
HotPaw BASIC does not appear to have access to the iPad’s photo library, but Pythonista does. It has a photos module that allows you to pick_image and several methods in the scene module for simple manipulations and display.
The code itself is pretty simple. Create a scene, override touch_end to capture points (touch_end is similar to onClick in JavaScript), and the BASIC code from 57 Programs converted to Python, to determine the center and radius of a circle given three points on the screen.
[toggle code]
 from scene import *
 import photos

class MyScene(Scene):
 touch_radius = 10

def __init__(self, mapimage):
 #scene.image can only display RGBA images
 self.mapimage = mapimage.convert('RGBA')
 self.points = []
 super(MyScene, self).__init__()

def setup(self):
 # scale image to fit screen
 width = self.mapimage.size[0]
 height = self.mapimage.size[1]
 widthratio = width/self.size.w
 heightratio = height/self.size.h
 if widthratio > heightratio:
 scale = widthratio
 else:
 scale = heightratio
 if scale != 1:
 width = int(width/scale)
 height = int(height/scale)
 self.mapimage = self.mapimage.resize((width, height))
 # center the image on the screen
 self.imagelocation = [(self.size.wwidth)/2, (self.size.hheight)/2]
 # load image for display
 self.mapimage = load_pil_image(self.mapimage)

def draw(self):
 background(1, 1, 1)
 image(self.mapimage, *self.imagelocation)
 #if there are three points, draw a circle
 if len(self.points) == 3:
 self.makeCircle(self.points)
 #if there are any points, show them
 if self.points:
 self.showPoints(self.points)
 #override touch_ended to store/remove touches

def touch_ended(self, touch):
 #if any of the touches are in a previous touch, remove them
 if self.remove_point(touch):
 return
 #if there are fewer than three touches, add this one
 if len(self.points) < 3:
 self.points.append(touch)

def remove_point(self, point):
 for existingPoint in self.points:
 #determine distance between tap and existing point
 x1 = existingPoint.location.x
 y1 = existingPoint.location.y
 x2 = point.location.x
 y2 = point.location.y
 distance = sqrt((x2x1)**2 + (y2y1)**2)
 #if the tap is in the circle of an existing point, delete it
 if distance <= self.touch_radius:
 self.points.remove(existingPoint)
 return True
 return False

def showPoints(self, points):
 radius = self.touch_radius
 fill(.5, 0, 0)
 for point in points:
 x = point.location.x
 y = point.location.y
 ellipse(xradius, yradius, radius*2, radius*2)

def makeCircle(self, points):
 #determine center and radius of circle from three points
 x1 = points[0].location.x
 x2 = points[1].location.x
 x3 = points[2].location.x
 y1 = points[0].location.y
 y2 = points[1].location.y
 y3 = points[2].location.y
 n1 = (y2y1)/(x2x1)
 n2 = (y3y1)/(x3x1)
 k1numerator = (x2x1)*(x2+x1) + (y2y1)*(y2+y1)
 k1 = k1numerator/(2*(x2x1))
 k2numerator = (x3x1)*(x3+x1) + (y3y1)*(y3+y1)
 k2 = k2numerator/(2*(x3x1))
 y = (k2k1)/(n2n1)
 x = k2(n2*y)
 radius = sqrt((x3x)**2 + (y3y)**2)
 # ellipse draws in a rectangle, so x/y need to be the lower left corner
 x = xradius
 y = yradius
 fill(0, .5, 0, .5)
 ellipse(x, y, radius*2, radius*2)
 mapimage = photos.pick_image(show_albums=True)

if mapimage:
 scene = MyScene(mapimage)
 #the smaller the frame_interval, the more responsive it will be
 #and the faster the battery will drain
 run(scene, frame_interval=5)

else:
 print 'Canceled or invalid image.'
This code asks the user to pick an image from any album. If you remove show_albums=True, it will only display images from the camera roll.
It then instantiates a MyScene instance given the chosen “mapimage”.
Inside, the current version of Pythonista as I write this requires that load_pil_image be in the setup method; future versions will allow it in the init method as well.^{1} So setup resizes the image either horizontally or vertically as necessary to fill the screen without distortion, then loads it for display using scene.image.
On every tap, the code checks to see if the user was tapping on an existing point; if so, that point is removed. Otherwise, as long as there are fewer than three points currently stored, it stores that point.
The draw method displays all of the points, and if there are three of them, displays the circle using the formula from 57 Programs.
Mostly worthless, but it seems like the kind of thing that might show up in a fraught race to find a criminal in a modern crime show. It should be possible to do a lot of cool programming on the fly on mobile devices using tools like Pythonista.
Thanks to mmontague and JonB on the Pythonista forums for help tracking down where load_pil_image needed to be.
↑
 57 Practical Programs & Games in BASIC: Ken Tracton (paperback)
 This collection of routines from the dawn of personal computers collects what, nowadays, is likely covered in a library in whatever programming language you use. Unless, of course, you still use BASIC. It’s a fascinating look at what we all were doing back in the late seventies and early eighties if we wanted our computers to do anything useful at all.
 57 Practical Programs & Games in BASIC: Ken Tracton at Internet Archive (ebook)
 Archive.org has 57 Practical Programs in several ebook formats. So that you can copy and paste into your TRS80’s command line…
 HotPaw Basic on iOS
 Looks like there’s a minor renaissance in programming languages on the iPhone and iPad. HotPaw BASIC is one of the first.
 Pythagoras Theorem: Distance Between Two Points
 “We use the Pythagoras Theorem to derive a formula for finding the distance between two points in 2 and 3 dimensional space. Let P = (x 1, y 1) and Q = (x 2, y 2) be two points on the Cartesian plane; Then from the Pythagoras Theorem we find that the distance between P and Q is PQ = sqrt((x2x1)^{2} + (y2y1)^{2}).”
 Pythonista
 “Bring the Zen of Python to iOS.”