from 당구대 import plt,ax,Config import 당구대 import matplotlib.patches as patches import numpy as np import math # 포인트 좌표계 (0,0) - (8,4) ball_radius/한칸 # 당구대좌표계 (0,0) - (8*한칸,4*한칸) def 당구대좌표계로(n_list): # 각 포인트 수치에 '한칸' 길이를 곱하여 실제 mm 좌표로 변환 return [val * Config.한칸 for val in n_list] ball_radius=Config.ball_radius/Config.한칸 class Ball: def __init__(self,x, y, r=1,facecolor="white", edgecolor=None, linewidth=1): self.x=x; self.y=y; self.r=r self.facecolor=facecolor; self.edgecolor=edgecolor self.linewidth=linewidth def draw(self): #당구대 좌표계로 변환 x,y=당구대좌표계로([self.x,self.y]) r=self.r*Config.ball_radius 당구대.draw_ball(x,y,r,facecolor=self.facecolor,edgecolor=self.edgecolor,linewidth=self.linewidth) def draw_circle(self): x,y=당구대좌표계로([self.x,self.y]) r=self.r*Config.ball_radius 당구대.draw_ball(x,y,r,facecolor='none',edgecolor=self.edgecolor,linewidth=self.linewidth) class Vector: def __init__(self, x, y): self.x = x self.y = y # 더하기: V1 + V2 def __add__(self, other): return Vector(self.x + other.x, self.y + other.y) # 빼기: V1 - V2 (벡터 계산의 핵심: 목적지 - 출발지) def __sub__(self, other): return Vector(self.x - other.x, self.y - other.y) # 스칼라 곱: V1 * k def __mul__(self, scalar): return Vector(self.x * scalar, self.y * scalar) # 벡터의 길이 (크기) def length(self): return math.sqrt(self.x**2 + self.y**2) # 단위 벡터 만들기 (방향은 같고 길이는 1) def normalize(self): mag = self.length() if mag == 0: return Vector(0, 0) return Vector(self.x / mag, self.y / mag) # 리스트나 튜플 형태로 변환 (Matplotlib 입력용) def to_tuple(self): return (self.x, self.y) def __repr__(self): return f"Vector({self.x:.2f}, {self.y:.2f})" def draw(self,V1, V2, color="blue"): x1,y1,x2,y2=당구대좌표계로([V1.x,V1.y,V2.x,V2.y]) 당구대.draw_arrow(x1,y1,x2,y2,color) def drawto(self,V2, color="blue",alpha=1,head_width=20): #x=self.x*당구대.한칸; y=self.y*당구대.한칸; r=self.r*당구대.ball_radius; x1,y1,x2,y2=당구대좌표계로([self.x,self.y,V2.x,V2.y]) 당구대.draw_arrow(x1,y1,x2,y2,color=color,alpha=alpha,head_width=head_width) def get_intersection(v1,v2, q1,q2): p1=v1.x,v1.y; p2=v2.x,v2.y p3=q1.x,q1.y; p4=q2.x,q2.y """ (x1, y1)-(x2, y2) 선분과 (x3, y3)-(x4, y4) 선분의 교점을 반환 """ x1, y1 = p1; x2, y2 = p2 x3, y3 = p3; x4, y4 = p4 # 분모 계산 (평행 여부 확인) denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4) if denom == 0: return None # 평행하거나 일치함 # 행렬식을 이용한 분자 계산 intersect_x = ((x1*y2 - y1*x2)*(x3 - x4) - (x1 - x2)*(x3*y4 - y3*x4)) / denom intersect_y = ((x1*y2 - y1*x2)*(y3 - y4) - (y1 - y2)*(x3*y4 - y3*x4)) / denom return (intersect_x, intersect_y) ######################################################### # 넣어 치기 : 수구 W가 이미지 볼 C로 출발하고 # 반사하여 적구R로 향하는 진로를 볼의 중심을 계산하고 도시 ######################################################### def 넣어치기(R=Ball(x=1,y=3,r=1,facecolor='red'),W=Ball(x=1,y=2,r=1,facecolor='white')): # R 적구, :수구 R.draw() W.draw() B0=Vector(ball_radius,0) B1=Vector(ball_radius,4) B0.drawto(B1,color='white',alpha=0.5,head_width=0) def reflect(BallW,BallC): W=Vector(BallW.x,BallW.y) C=Vector(BallC.x,BallC.y) W.drawto(C,color='red') #수구W부터 이미지볼 C 까지 x,y=Vector.get_intersection(W,C,B0,B1) #수구 W가 날 B에 퉁돌하는 교점 x,y qx=W.x; qy=2*y-W.y #교점에서 반사되는 점(qx,qy) Vector(x,y).drawto(Vector(qx,qy),color="white") #교점(x,y)부터 반사점(qx,qy)까지 #적구 R의 인접 이미지원 v1=Ball(x=R.x-ball_radius*2,y=R.y,r=R.r,facecolor='None',edgecolor='white') v1.draw_circle() #적구 내접점 거리 d=R.x- ball_radius #이미지 볼 C1=Ball(x=-d-ball_radius, y=R.y,r=R.r,facecolor='None',edgecolor='red') #적구의 대칭원 C1 C2=Ball(x=C1.x+ball_radius*2,y=R.y,r=R.r,facecolor='None',edgecolor='red') #C1의 내점원 C2 if C1.x<0: C1.draw_circle() # C1의 원 그리기 reflect(W,C1) # 이미지볼 C1로 향하는 수구W의 반사 궈적 reflect(W,Vector(C1.x+ball_radius,C1.y)) # 이미지볼 C1의 내접점으로 향하는 수구W의 반사 궈적 if C2.x<0: C2.draw_circle() # C2의 원 그리기 reflect(W,C2) # 이미지볼 C2로 향하는 수구W의 반사 궈적 reflect(W,Vector(C2.x+ball_radius,C2.y)) # 이미지볼 C2의 내접점으로 향하는 수구W의 반사 궈적 당구대.draw_당구대() r=ball_radius# d=r+r #넣어치기(R=Ball(d+r,3,facecolor='red'),W=Ball(1,1)) #공1개 구멍 수구적구2칸 #넣어치기(R=Ball(d+r+r,3,facecolor='red'),W=Ball(1,1)) #공1개반구멍 수구적구2칸 #넣어치기(R=Ball(d*2+r,3,facecolor='red'),W=Ball(1,1)) #공2개 구멍 수구적구2칸 넣어치기(R=Ball(d+r,3,facecolor='red'),W=Ball(1,2)) #공1개 구멍 수구적구1칸 #넣어치기(R=Ball(d+r+r,3,facecolor='red'),W=Ball(1,2)) #공1개반구멍 수구적구1칸 #넣어치기(R=Ball(d*2+r,3,facecolor='red'),W=Ball(1,2)) #공2개 구멍 수구적구1칸 ax.axis('off') plt.title("Billiards Table Coordinate System (mm)") plt.show()