1

I decided to create a visualization of the second problem on the 2011 Math Olympiad competition. I need to create a line which passes through a point, and once it intersects a different point, I need it to rotate about it.

More info: https://artofproblemsolving.com/wiki/index.php?title=2011_IMO_Problems/Problem_2

I currently have two lines starting at the last point in my array of points, but once the line starts rotating, both lines will need to be shifted, and I don't know how to work that out.

import random
import math
from graphics import *

winWidth=300
winHeight=240

def show():
    pointSetX = []
    pointSetY = []
    totalSet = []

    '''Displaying all of the points on the screen'''
    for i in range(random.randrange(2, 10)):
        x = math.floor(random.randrange(0, winWidth))
        y = math.floor(random.randrange(0, winWidth))
        pt = Point(x, y)
        circleForRotation = Circle(pt, 4)
        circleForRotation.setFill('white')
        circleForRotation.draw(win)
        pointSetX.append(x)
        pointSetY.append(y)
        totalSet = zip(pointSetX, pointSetY)
    '''Printing the totalSet to see where all of the points lie'''
    print(totalSet)

    '''Displaying the line(s) on the screen'''
    ln = Line(pt, Point(pt.x, winHeight))
    ln2 = Line(pt, Point(pt.x, 0))
    ln.setFill('red')
    ln2.setFill('blue')
    ln2.draw(win)
    ln.draw(win)

win = GraphWin('IMO 20011 P2', winWidth, winHeight)
show()

Everything in the code works as expected, but the two lines have the point as an endpoint, and I don't know how to continue with the problem without having one line pass through the point instead of two.

cdlane
  • 40,441
  • 5
  • 32
  • 81
JamesC
  • 11
  • 3
  • What about defining a method specifically for creating the circle based on the point? Use it both initially and after the new point is encountered. There is also an answer for [rotating points in python](https://stackoverflow.com/questions/34372480/rotate-point-about-another-point-in-degrees-python?rq=1) that may help – Brennan Aug 13 '19 at 21:33
  • Where do you get Line, Point and Circle from? Include the relevant import statements in your sample code. You might also want to add a question tag if the graphics library you are using has its own tag. – Håken Lid Aug 13 '19 at 21:36
  • I thought of a solution to the problem. I just need to create a new line which begins at the end point of ln and has the other end point at the end of ln2. Not very elegant, but it gets the job done. Thank you both for the feedback. (P.S. I got the lines, points, and circles from the graphics.py library) – JamesC Aug 13 '19 at 21:45

1 Answers1

0

Below is my attempt at a visualization of your problem using Zelle graphics. My solution was to make the line twice the size of the window and, every time it hits a point, readjust its center:

from random import randrange, choice
from math import sin, cos
from graphics import *

NONCOLLINEAR_POINTS = 6
CIRCLE_RADIUS = 4
WINDOW_SIZE = 500
DELTA = -0.0001  # in radians

def rotateLine(line, theta):
    cosine, sine = cos(theta), sin(theta)

    points = [line.getP1(), line.getP2()]  # use copies, not originals!
    center = line.getCenter()

    for point in points:
        dx, dy = point.x - center.x, point.y - center.y
        point.x = center.x + dx * cosine + dy * sine
        point.y = center.y + dy * cosine - dx * sine

    return Line(*points)

def collinear(a, b, c):
    return (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y) < 0

def distance(a, b):
    return ((b.x - a.x) ** 2 + (b.y - a.y) ** 2) ** 0.5

points = []

while len(points) < NONCOLLINEAR_POINTS:

    c = Point(randrange(WINDOW_SIZE), randrange(WINDOW_SIZE))

    for a in range(len(points) - 1):
        if distance(points[a], c) < CIRCLE_RADIUS*2:
            c = None  # circles are touching
            break

        for b in range(a + 1, len(points)):
            if distance(points[b], c) < CIRCLE_RADIUS*2:
                c = None
                break

            if collinear(points[a], points[b], c):
                c = None
                break
        else:  # no break
            continue

        break

    if c is None:
        continue

    points.append(c)

win = GraphWin("Visualization", WINDOW_SIZE, WINDOW_SIZE)

circles = []

for point in points:
    circle = Circle(point, CIRCLE_RADIUS)
    circle.setFill('black')
    circle.draw(win)

    circles.append(circle)

origin = choice(circles)
origin.setFill('red')
center = origin.getCenter()

line = Line(Point(-WINDOW_SIZE, WINDOW_SIZE/2), Point(WINDOW_SIZE * 2, WINDOW_SIZE/2))
line_center = line.getCenter()
line.move(center.x - line_center.x, center.y - line_center.y)
line.draw(win)

angle = 0

while True:
    angle += DELTA

    line.undraw()
    line = rotateLine(line, angle)
    line.draw(win)

    for circle in circles:
        if circle == origin:
            continue

        center = circle.getCenter()

        if collinear(line.p1, line.p2, center):
            origin.setFill('black')
            origin = circle
            origin.setFill('red')

            line_center = line.getCenter()
            line.move(center.x - line_center.x, center.y - line_center.y)

            break

The real problem was trying to randomly generate noncollinear points! That part of the code would need to be improved before increasing the number of points.

enter image description here

cdlane
  • 40,441
  • 5
  • 32
  • 81