The client-side code remains virtually unchanged from the version of the code between Javascript and PHP. Exactly one line needs changing. The route is now mapped to a function in the Python code.
Writing the server using Flask
In contrast with monolithic back-end libraries, Flask lets developers make many decisions, such as choosing the structure of the system and the extensions to use.
We create an instance of the Flask
object and define the routes.
We serve the route route ('/'
) by responding with the function
index
.
from geom import Point, Segment, Line, intersect
from flask import Flask, render_template, request, jsonify, abort
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
The app is launched using app.run()
.
if __name__=="__main__":
app.run(host='0.0.0.0')
We also respond at the URL /calculate_intersection
by running the
function with the same name. Both the request and the response are
JSON strings. We construct two segments, compute their intersection,
if any, and respond with the point of intersection.
# Both request and response are JSON.
@app.route('/calculate_intersection', methods=['POST'])
def calculate_intersection():
if not request.json:
abort(400)
seg1 = Segment(request.json['seg1'])
seg2 = Segment(request.json['seg2'])
result = intersect(seg1, seg2)
if result is None:
return jsonify({'intersection': False})
else:
return jsonify({'intersection': True, 'x': result.x, 'y': result.y})
Geometry
Here also we do not aim to build a comprehensive geometry library, but only those functions needed, starting with the humble determinant, the cornerstone of geometric calculations.
def determinant(a, b, c, d):
return a * d - b * c
A Point
object can take either one or two arguments. If one, we
expect a dict
, and if two, we expect the two coordinates.
class Point(object):
def __init__(self, *args):
if len(args) == 1 and type(args[0]) is dict:
self.x, self.y = float(args[0]['x']), float(args[0]['y'])
elif len(args) == 2:
self.x, self.y = args[0], args[1]
else:
raise TypeError('Unable to construct Point.')
A Segment
is initialized by a dict, as received in the JSON message,
and a Line
is initialized by a segment.
class Segment(object):
def __init__(self, msg):
self.src = Point(msg['src'])
self.tgt = Point(msg['tgt'])
class Line(object):
def __init__(self, seg):
self.a = + determinant(seg.src.y, 1.0, seg.tgt.y, 1.0)
self.b = - determinant(seg.src.x, 1.0, seg.tgt.x, 1.0)
self.c = + determinant(seg.src.x, seg.src.y, seg.tgt.x, seg.tgt.y)
To compute the intersection, we first determine whether the endpoints of each segment lie on opposite sides of the other.
class Side(object):
def __init__(self, v):
self.v = +1 if v > 0 else (0 if v == 0 else -1)
def oriented_side(line, point):
return Side(line.a * point.x + line.b * point.y + line.c)
def straddle(line, segment):
src_side = oriented_side(line, segment.src)
tgt_side = oriented_side(line, segment.tgt)
s1 = src_side.v >= 0 and tgt_side.v < 0
s2 = src_side.v < 0 and tgt_side.v >= 0
return s1 or s2
If both do, we find the intersection point.
def intersect(seg1, seg2):
l1 = Line(seg1)
l2 = Line(seg2)
if straddle(l1, seg2) and straddle(l2, seg1):
denom = determinant(l1.a, l1.b, l2.a, l2.b);
if denom != 0:
detx = + determinant(l1.b, l1.c, l2.b, l2.c);
dety = - determinant(l1.a, l1.c, l2.a, l2.c);
return Point(detx/denom, dety/denom);
return None
The source code is here.
Back to CodingNirvana.ca