Python: A more Pythonic way to loop through grid classifications - python

I have an 11-by-11 size grid that is placed over a scatter plot. The scatter plot is of 100 randomly generated pairs. Within each grid space, is a classification type, where:
Type A is greater than 0, but less than 0.5 in both the X and Y axes,
Type B is greater than 0.5, but less than 1.5 in both the X and Y axes
ect...
I want to know how many points are within each grid space, as well as the pairs that exist in that grid space. This part isn't a problem, I just want to know if there is a more pythonic way to write my loop, since I don't want to have to write an if statement for each grid space.
My script is as follows:
TypeA = []
TypeB = []
fig = plt.figure()
ax = fig.gca()
ax.set_xticks(np.arange(0.5, 10.5, 1))
ax.set_yticks(np.arange(0.5, 10.5, 1))
for ii in range(100):
RNumX = randint(0, 10)
RNumY = randint(0, 10)
print RNumX, RNumY
hold(True)
plot1 = plt.scatter(RNumX, RNumY)
if RNumX >= 0 and RNumX < 0.5:
if RNumY >= 0 and RNumY < 0.5:
PairA = (RNumX, RNumY)
TypeA.append(PairA)
elif RNumY >= 0.5 and RNumY < 1.5:
PairB = (RNumX, RNumY)
TypeB.append(PairB)
SumA = len(TypeA)
SumB = len(TypeB)
print TypeA, SumA
print TypeB, SumB
plt.grid()
plt.show()

You could make Type a matrix and round the values to find your indices:
from random import random
# An 11 x 11 matrix of lists
Type = 11 * (11 * ([],),)
fig = plt.figure()
ax = fig.gca()
ax.set_xticks(np.arange(0.5, 10.5, 1))
ax.set_yticks(np.arange(0.5, 10.5, 1))
for ii in range(100):
# If you want to use floats in stead of ints
RNumX = 0.5 + 10 * random()
RNumY = 0.5 + 10 * random()
print RNumX, RNumY
hold(True)
plot1 = plt.scatter(RNumX, RNumY)
# Round the coordinates to find the indices
Type[int(RNumX + 0.5)][int(RNumY + 0.5)].append((RNumX, RNumY))
# Print all buckets as your snippet implies
for x in Type:
for y in x:
print y, len(y)
# Print only buckets with both values in the same range as your question implies
for x in range(11):
print Type[x][x], len(Type[x][x])
plt.grid()
plt.show()

you could use the bisect module to avoid those if statements. Here is a quick-and-dirty example.
from functools import total_ordering
import bisect
#total_ordering
class Point:
def __init__(self, X, Y):
self.X = X
self.Y = Y
def __eq__(self, other):
return self.X == other.X and self.Y == other.Y
def __lt__(self, other):
return self.X < other.X and self.Y < other.Y
print [(float(x)/2, float(x)/2) for x in xrange(0, 23)]
[(0.0, 0.0), (0.5, 0.5), (1.0, 1.0), (1.5, 1.5), (2.0, 2.0), (2.5, 2.5), (3.0, 3.0), (3.5, 3.5), (4.0, 4.0), (4.5, 4.5), (5.0, 5.0), (5.5, 5.5), (6.0, 6.0), (6.5, 6.5), (7.0, 7.0), (7.5, 7.5), (8.0, 8.0), (8.5, 8.5), (9.0, 9.0), (9.5, 9.5), (10.0, 10.0), (10.5, 10.5), (11.0, 11.0)]
points = [Point(float(x)/2, float(x)/2) for x in xrange(0, 23)]
types = [chr(x) for x in xrange(65, 91)]
print types
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
print types[bisect.bisect(points, Point(0.1, 0.1)) - 1]
A
print types[bisect.bisect(points, Point(0.6, 0.6)) - 1]
B

Related

Python - how to round up to specified decimal

I want to round up numbers in Python to a specified decimal.
For example, if I choose
(2.1, 0.2) --> I want to get 2.2
(2.2, 0.2) --> I want to get 2.2
(3.6, 0.5) --> I want to get 4.0
(3.5, 0.5) --> I want to get 3.5
probably not the most efficient, but working with numpy
I am assuming that you are working with not too much decimals
import numpy as np
def rounder(num, round_):
int_p = np.floor(num)
float_p = num - int_p
print(float_p) # this print is meant to show the floating point problem
diff = float_p - round_
if diff <= 0 or -0.00001 <diff< 0.00001:
return int_p + round_
else:
return np.ceil(num)
list_ = [(2.1, 0.2), (2.2, 0.2), (3.6, 0.5), (3.5, 0.5)]
for num, round_ in list_:
print("({}, {})".format(num, round_), rounder(num, round_))
# (2.1, 0.2) 2.2
# (2.2, 0.2) 2.2
# (3.6, 0.5) 4.0
# (3.5, 0.5) 3.5
unfortunately there are problems with the representation of the floating point that is not so precise. That's the reason why I wrote another condition in the if else -0.00001 <diff< 0.00001 that means, if the difference is not so high (near zero) than it is basically zero
New algorithm as requested from op
import numpy as np
def rounder(num, round_):
int_p = np.floor(num)
new_value = int_p
while new_value < num:
new_value = round(new_value + round_, 4)
return new_value
list_ = [(2.1, 0.2), (2.2, 0.2), (3.6, 0.5), (3.5, 0.5), (0.178, 0.1)]
for num, round_ in list_:
print("({}, {})".format(num, round_), rounder(num, round_))
# (2.1, 0.2) 2.2
# (2.2, 0.2) 2.2
# (3.6, 0.5) 4.0
# (3.5, 0.5) 3.5
# (0.178, 0.1) 0.2
What I wanted was:
def rounder(num, round_):
val = num // round_
if val*round_ == num:
return num
else:
return val*round_ + round_
list_ = [(9.3, 0.4), (2.2, 0.2), (3.6, 0.5), (3.5, 0.5), (0.178, 0.1)]
for num, round_ in list_:
print("({}, {})".format(num, round_), rounder(num, round_))
Assume that rounder(x, y) is some multiple of y that is closest to x. If there are two such multiples, choose the one that has a higher absolute value.
Python has an issue of rounding halves:
>>> round(10.5)
10
>>> np.round(10.5)
10.0
Iterable case
Borrowing from this simple fix, I'll extend it for OP's question
import math
def rounder(x, y):
r = x/y
if (r - int(r) >= 0.5):
return y * math.ceil(r)
else:
return y * math.floor(r)
Sample run:
>>> rounder(2.1, 0.2)
2.2
>>> rounder(2.2, 0.2)
2.2
>>> rounder(3.6, 0.5)
3.5
>>> rounder(3.5, 0.5)
3.5
This is designed to call it multiple times on items, usually in a process of some iterations.
Vectorised case
It's also possible to rewrite it in order to work in vectorised way:
def rounder(x, y):
r = x/y
np.where(r-r//1 >= 0.5, y * np.ceil(r), y * np.floor(r))
>>> rounder(np.array([2.1,2.2,3.6,3.5]), np.array([0.2, 0.2, 0.5, 0.5]))
array([2.2, 2.2, 3.5, 3.5])

Finding clusters of line segments in a list

I have a list of line segments represented as x and y coordinates and I'm trying to find groups of lines such that all angles within the group are within 20 degrees. But my problem is that math.degrees(math.atan2(-0.1,-1)) (=-174.29) and math.degrees(math.atan2(0.1,-1)) (=174.29) should make two points in the same group, but the difference in face value is greater than 20. I wonder if my code should do what I want and avoids the 180/-180 problem:
import math
endpoints = [((2, 11), (2, 8)), ((11, 3), (2, 5)), ((13, 7), (9, 12)), ((5, 5), (15, 12)), ((15, 4), (8, 1)), ((15, 14), (14, 3)), ((2, 4), (6, 5)), ((1, 13), (13, 11)), ((8, 11), (12, 15)), ((12, 4), (7, 1))]
def find_angle(p1,p2):
x1 = p1[0]
y1 = p1[1]
x2 = p2[0]
y2 = p2[1]
dx = max(x2-x1,x1-x2)
if dx == x2-x1:
dy = y2-y1
else:
dy = y1-y2
return math.degrees(math.atan2(dy,dx))
endpointsbyangle = sorted([(find_angle(p1,p2), (p1,p2)) for p1, p2 in endpoints], key=lambda x: x[0])
prev = -190
group = []
allgroups = []
for (theta, (p1, p2)) in endpointsbyangle:
if prev == -190:
group.append((p1,p2))
prev = theta
else:
if abs(prev - theta) < 20:
group.append((p1,p2))
else:
allgroups.append(group)
group = [(p1,p2)]
prev = theta
print dict(enumerate(allgroups))
Any thought appreciated.
One way is to replace your line
if abs(prev - theta) < 20:
with
if abs(prev - theta) < 20 or abs(prev - theta) > 340:
This then captures the situations where the calculated angle is near 360 degrees.
However, if I understand you, you have another problem. If the angle ABC is exactly 5 degrees (for example), and angle CAB is also 5 degrees, then angle ACB is 170 degrees and would fail your test. In other words, it is not possible for "all angles within the group are within 20 degrees" for a group of 3 points. You should also allow an angle to be within 20 degrees of 180 degrees. So perhaps you should use the line
if (abs(prev - theta) < 20
or abs(prev - theta) > 340
or abs(prev - theta - 180) < 20):
That depends on exactly what you mean by your requirement "all angles within the group are within 20 degrees."

Find the shortest distance between a point and line segments (not line)

I have set of line segments (not lines), (A1, B1), (A2, B2), (A3, B3), where A,B are ending points of the line segment. Each A and B has (x,y) coordinates.
QUESTION:
I need to know the shortest distance between point O and line segments as shown in the shown figure implemented in line of codes. The code I can really understand is either pseudo-code or Python.
CODE: I tried to solve the problem with this code, unfortunately, it does not work properly.
def dist(A, B, O):
A_ = complex(*A)
B_ = complex(*B)
O_= complex(*O)
OA = O_ - A_
OB = O_ - B_
return min(OA, OB)
# coordinates are given
A1, B1 = [1, 8], [6,4]
A2, B2 = [3,1], [5,2]
A3, B3 = [2,3], [2, 1]
O = [2, 5]
A = [A1, A2, A3]
B = [B1, B2, B3]
print [ dist(i, j, O) for i, j in zip(A, B)]
Thanks in advance.
Here is the answer. This code belongs to Malcolm Kesson, the source is here. I provided it before with just link itself and it was deleted by the moderator. I assume that the reason for that is because of not providing the code (as an answer).
import math
def dot(v,w):
x,y,z = v
X,Y,Z = w
return x*X + y*Y + z*Z
def length(v):
x,y,z = v
return math.sqrt(x*x + y*y + z*z)
def vector(b,e):
x,y,z = b
X,Y,Z = e
return (X-x, Y-y, Z-z)
def unit(v):
x,y,z = v
mag = length(v)
return (x/mag, y/mag, z/mag)
def distance(p0,p1):
return length(vector(p0,p1))
def scale(v,sc):
x,y,z = v
return (x * sc, y * sc, z * sc)
def add(v,w):
x,y,z = v
X,Y,Z = w
return (x+X, y+Y, z+Z)
# Given a line with coordinates 'start' and 'end' and the
# coordinates of a point 'pnt' the proc returns the shortest
# distance from pnt to the line and the coordinates of the
# nearest point on the line.
#
# 1 Convert the line segment to a vector ('line_vec').
# 2 Create a vector connecting start to pnt ('pnt_vec').
# 3 Find the length of the line vector ('line_len').
# 4 Convert line_vec to a unit vector ('line_unitvec').
# 5 Scale pnt_vec by line_len ('pnt_vec_scaled').
# 6 Get the dot product of line_unitvec and pnt_vec_scaled ('t').
# 7 Ensure t is in the range 0 to 1.
# 8 Use t to get the nearest location on the line to the end
# of vector pnt_vec_scaled ('nearest').
# 9 Calculate the distance from nearest to pnt_vec_scaled.
# 10 Translate nearest back to the start/end line.
# Malcolm Kesson 16 Dec 2012
def pnt2line(pnt, start, end):
line_vec = vector(start, end)
pnt_vec = vector(start, pnt)
line_len = length(line_vec)
line_unitvec = unit(line_vec)
pnt_vec_scaled = scale(pnt_vec, 1.0/line_len)
t = dot(line_unitvec, pnt_vec_scaled)
if t < 0.0:
t = 0.0
elif t > 1.0:
t = 1.0
nearest = scale(line_vec, t)
dist = distance(nearest, pnt_vec)
nearest = add(nearest, start)
return (dist, nearest)
Rather than using a for loop, you can vectorize these operations and get much better performance. Here is my solution that allows you to compute the distance from a single point to multiple line segments with vectorized computation.
def lineseg_dists(p, a, b):
"""Cartesian distance from point to line segment
Edited to support arguments as series, from:
https://stackoverflow.com/a/54442561/11208892
Args:
- p: np.array of single point, shape (2,) or 2D array, shape (x, 2)
- a: np.array of shape (x, 2)
- b: np.array of shape (x, 2)
"""
# normalized tangent vectors
d_ba = b - a
d = np.divide(d_ba, (np.hypot(d_ba[:, 0], d_ba[:, 1])
.reshape(-1, 1)))
# signed parallel distance components
# rowwise dot products of 2D vectors
s = np.multiply(a - p, d).sum(axis=1)
t = np.multiply(p - b, d).sum(axis=1)
# clamped parallel distance
h = np.maximum.reduce([s, t, np.zeros(len(s))])
# perpendicular distance component
# rowwise cross products of 2D vectors
d_pa = p - a
c = d_pa[:, 0] * d[:, 1] - d_pa[:, 1] * d[:, 0]
return np.hypot(h, c)
And some tests:
p = np.array([0, 0])
a = np.array([[ 1, 1],
[-1, 0],
[-1, -1]])
b = np.array([[ 2, 2],
[ 1, 0],
[ 1, -1]])
print(lineseg_dists(p, a, b))
p = np.array([[0, 0],
[1, 1],
[0, 2]])
print(lineseg_dists(p, a, b))
>>> [1.41421356 0. 1. ]
[1.41421356 1. 3. ]
The explanation is in the docstring of this function:
def point_to_line_dist(point, line):
"""Calculate the distance between a point and a line segment.
To calculate the closest distance to a line segment, we first need to check
if the point projects onto the line segment. If it does, then we calculate
the orthogonal distance from the point to the line.
If the point does not project to the line segment, we calculate the
distance to both endpoints and take the shortest distance.
:param point: Numpy array of form [x,y], describing the point.
:type point: numpy.core.multiarray.ndarray
:param line: list of endpoint arrays of form [P1, P2]
:type line: list of numpy.core.multiarray.ndarray
:return: The minimum distance to a point.
:rtype: float
"""
# unit vector
unit_line = line[1] - line[0]
norm_unit_line = unit_line / np.linalg.norm(unit_line)
# compute the perpendicular distance to the theoretical infinite line
segment_dist = (
np.linalg.norm(np.cross(line[1] - line[0], line[0] - point)) /
np.linalg.norm(unit_line)
)
diff = (
(norm_unit_line[0] * (point[0] - line[0][0])) +
(norm_unit_line[1] * (point[1] - line[0][1]))
)
x_seg = (norm_unit_line[0] * diff) + line[0][0]
y_seg = (norm_unit_line[1] * diff) + line[0][1]
endpoint_dist = min(
np.linalg.norm(line[0] - point),
np.linalg.norm(line[1] - point)
)
# decide if the intersection point falls on the line segment
lp1_x = line[0][0] # line point 1 x
lp1_y = line[0][1] # line point 1 y
lp2_x = line[1][0] # line point 2 x
lp2_y = line[1][1] # line point 2 y
is_betw_x = lp1_x <= x_seg <= lp2_x or lp2_x <= x_seg <= lp1_x
is_betw_y = lp1_y <= y_seg <= lp2_y or lp2_y <= y_seg <= lp1_y
if is_betw_x and is_betw_y:
return segment_dist
else:
# if not, then return the minimum distance to the segment endpoints
return endpoint_dist
Basic algorithm: pretend that you have lines, so oriented that A lies to the left of B when O lies above the line (mentally rotate the picture to match as needed).
Find closest point as normal. If the point is between A and B, you're done. If it's to the left of A, the closest point is A. If the point is to the right of B, the closest point is B.
The case when A, B, and O all lie on the same line may or may not need special attention. Be sure to include a few tests of this position.
On my side, I've found that the two other answers were broken, especially when the line is purely vertical or horizontal. Here is what I did to properly solve the problem.
Python code:
def sq_shortest_dist_to_point(self, other_point):
dx = self.b.x - self.a.x
dy = self.b.y - self.a.y
dr2 = float(dx ** 2 + dy ** 2)
lerp = ((other_point.x - self.a.x) * dx + (other_point.y - self.a.y) * dy) / dr2
if lerp < 0:
lerp = 0
elif lerp > 1:
lerp = 1
x = lerp * dx + self.a.x
y = lerp * dy + self.a.y
_dx = x - other_point.x
_dy = y - other_point.y
square_dist = _dx ** 2 + _dy ** 2
return square_dist
def shortest_dist_to_point(self, other_point):
return math.sqrt(self.sq_shortest_dist_to_point(other_point))
A test case:
def test_distance_to_other_point(self):
# Parametrize test with multiple cases:
segments_and_point_and_answer = [
[Segment(Point(1.0, 1.0), Point(1.0, 3.0)), Point(2.0, 4.0), math.sqrt(2.0)],
[Segment(Point(1.0, 1.0), Point(1.0, 3.0)), Point(2.0, 3.0), 1.0],
[Segment(Point(0.0, 0.0), Point(0.0, 3.0)), Point(1.0, 1.0), 1.0],
[Segment(Point(1.0, 1.0), Point(3.0, 3.0)), Point(2.0, 2.0), 0.0],
[Segment(Point(-1.0, -1.0), Point(3.0, 3.0)), Point(2.0, 2.0), 0.0],
[Segment(Point(1.0, 1.0), Point(1.0, 3.0)), Point(2.0, 3.0), 1.0],
[Segment(Point(1.0, 1.0), Point(1.0, 3.0)), Point(2.0, 4.0), math.sqrt(2.0)],
[Segment(Point(1.0, 1.0), Point(-3.0, -3.0)), Point(-3.0, -4.0), 1],
[Segment(Point(1.0, 1.0), Point(-3.0, -3.0)), Point(-4.0, -3.0), 1],
[Segment(Point(1.0, 1.0), Point(-3.0, -3.0)), Point(1, 2), 1],
[Segment(Point(1.0, 1.0), Point(-3.0, -3.0)), Point(2, 1), 1],
[Segment(Point(1.0, 1.0), Point(-3.0, -3.0)), Point(-3, -1), math.sqrt(2.0)],
[Segment(Point(1.0, 1.0), Point(-3.0, -3.0)), Point(-1, -3), math.sqrt(2.0)],
[Segment(Point(-1.0, -1.0), Point(3.0, 3.0)), Point(3, 1), math.sqrt(2.0)],
[Segment(Point(-1.0, -1.0), Point(3.0, 3.0)), Point(1, 3), math.sqrt(2.0)],
[Segment(Point(1.0, 1.0), Point(3.0, 3.0)), Point(3, 1), math.sqrt(2.0)],
[Segment(Point(1.0, 1.0), Point(3.0, 3.0)), Point(1, 3), math.sqrt(2.0)]
]
for i, (segment, point, answer) in enumerate(segments_and_point_and_answer):
result = segment.shortest_dist_to_point(point)
self.assertAlmostEqual(result, answer, delta=0.001, msg=str((i, segment, point, answer)))
Note: I assume this function is inside a Segment class.
In case your line is infinite, don't limit the lerp from 0 to 1 only, but still at least provide two distinct a and b points.
I had to solve this problem as well, so for the sake of availability I'll post my code here. I did some cursory validation but nothing particularly serious. Your question actually helped me identify a bug in mine where a vertical or horizontal line segment would have broken the code and bypassed the intersection point on segment logic.
from math import sqrt
def dist_to_segment(ax, ay, bx, by, cx, cy):
"""
Computes the minimum distance between a point (cx, cy) and a line segment with endpoints (ax, ay) and (bx, by).
:param ax: endpoint 1, x-coordinate
:param ay: endpoint 1, y-coordinate
:param bx: endpoint 2, x-coordinate
:param by: endpoint 2, y-coordinate
:param cx: point, x-coordinate
:param cy: point, x-coordinate
:return: minimum distance between point and line segment
"""
# avoid divide by zero error
a = max(by - ay, 0.00001)
b = max(ax - bx, 0.00001)
# compute the perpendicular distance to the theoretical infinite line
dl = abs(a * cx + b * cy - b * ay - a * ax) / sqrt(a**2 + b**2)
# compute the intersection point
x = ((a / b) * ax + ay + (b / a) * cx - cy) / ((b / a) + (a / b))
y = -1 * (a / b) * (x - ax) + ay
# decide if the intersection point falls on the line segment
if (ax <= x <= bx or bx <= x <= ax) and (ay <= y <= by or by <= y <= ay):
return dl
else:
# if it does not, then return the minimum distance to the segment endpoints
return min(sqrt((ax - cx)**2 + (ay - cy)**2), sqrt((bx - cx)**2 + (by - cy)**2))

Get x1,x2 provided that x belongs to [x1,x2]

Given:
xvalues = [0.0, 1829.0, 3658.0, 5487.0]
and
nodesF = [[1, 0, 0, 0], [2, 0.5, 0, 0], [3, 5487, 0, 0]]
I want to loop over nodesF and return x1 and x2 values which every x of nodesF lie between, i.e. x belongs to [x1,x2] with x1<x2.
My code is:
for nodeID, x, y, z in nodesF:
x2= min(value for value in xvalues if value >= x)
x1= max(value for value in xvalues if value <= x)
if x1==x2:
x1=None
x2=None
x2= min(value for value in xvalues if value > x)
x1= max(value for value in xvalues if value <= x)
if x2==None or x2<=x1:
x2= min(value for value in xvalues if value >= x)
x1= max(value for value in xvalues if value < x)
elif x1==None or x2<=x1:
print "Error"
For x=5487 I get:
x2= min(value for value in xvalues if value > x) ValueError: min() arg
is an empty sequence.
So my question is how do get pass this error? if I could just set x2=None when min() is empty it would be OK! Thanks!
You're on the right track, but you'll need to do the check the list yourself:
valid_values = [value for value in xvalues if value > x] #changed >= by >
if valid_values:
x2 = min(valid_values)
else:
x2 = None
Or you could catch the error after the fact:
try:
x2 = min(value for value in xvalues if value > x) #changed >= by >
except ValueError:
x2 = None
So two lines might seem a little terse, but I hope you like it:
r = zip([float('-inf')] + xvalues, xvalues + [float('inf')])
which produces this:
In [104]: print r
Out[104]: [(-inf, 0.0), (0.0, 1829.0), (1829.0, 3658.0), (3658.0, 5487.0), (5487.0, inf)]
Then get your ranges like this:
[[filter(lambda l: l[0] < n <= l[1], r)[0] for n in m] for m in nodesF]
which produces this:
Out[102]:
[[(0.0, 1829.0), (-inf, 0.0), (-inf, 0.0), (-inf, 0.0)],
[(0.0, 1829.0), (0.0, 1829.0), (-inf, 0.0), (-inf, 0.0)],
[(0.0, 1829.0), (3658.0, 5487.0), (-inf, 0.0), (-inf, 0.0)]]

divide ways on a map in parts of length one

I have these coordinates:
coord = [(10,10), (13,10), (13,13)]
Now i need new coordinates.
The way between two coordinates is always one.
For example:
(10,10)
(11,10)
(12,10)
(13,10)
(13,11)
(13,12)
(13,13)
Any ideas?
#
I found the solution.
for n in range(len(coord)-1):
lengthx = coord[n+1][0] - coord[n][0]
lengthy = coord[n+1][1] - coord[n][1]
length = (lengthx**2 + lengthy**2)**.5
for m in range(length):
print coord[n][0]+lengthx/length*m, coord[n][1]+lengthy/length*m
A simple variation on Bresenham's line algorithm will achieve what you want using integer arithmetic only (so it should be noticeably faster):
def steps(path):
if len(path) > 0:
for i in range(1, len(path)):
for step in steps_between(path[i - 1], path[i]):
yield step
yield path[-1]
def steps_between(start, end):
x0, y0 = start
x1, y1 = end
steep = abs(y1 - y0) > abs(x1 - x0)
if steep:
x0, y0 = y0, x0
x1, y1 = y1, x1
if y0 > y1:
x0, x1 = x1, x0
y0, y1 = y1, y0
if y0 < y1:
ystep = 1
else:
ystep = -1
deltax = x1 - x0
deltay = abs(y1 - y0)
error = -deltax / 2
y = y0
for x in range(x0, x1):
if steep:
yield (y, x)
else:
yield (x, y)
error += deltay
if error > 0:
y += ystep
error -= deltax
if steep:
yield (y, x)
else:
yield (x, y)
coords = [(10, 10), (13, 10), (13, 13)]
print "\n".join(str(step) for step in steps(coords))
The above prints:
(10, 10)
(11, 10)
(12, 10)
(13, 10)
(13, 11)
(13, 12)
(13, 13)
Of course, Bresenham works as expected when both x and y change between two points on the path:
coords = [(10, 10), (13, 12), (15, 13)]
print "\n".join(str(step) for step in steps(coords))
That prints:
(10, 10)
(11, 10)
(11, 11)
(12, 11)
(12, 12)
(13, 12)
(14, 12)
(14, 13)
(15, 13)

Categories

Resources