Add router data structure, blockage parser, pin parser, initial unit tests

This commit is contained in:
Matt Guthaus 2016-11-16 15:02:07 -08:00
parent d0782df9fe
commit b947989970
14 changed files with 483 additions and 46 deletions

View File

@ -602,9 +602,9 @@ class VlsiLayout:
for boundary in self.structures[self.rootStructureName].boundaries: for boundary in self.structures[self.rootStructureName].boundaries:
if boundary.drawingLayer==borderlayer: if boundary.drawingLayer==borderlayer:
debug.info(debug_level,"Find border "+str(boundary.coordinates)) debug.info(debug_level,"Find border "+str(boundary.coordinates))
left_button=boundary.coordinates[0] left_bottom=boundary.coordinates[0]
right_top=boundary.coordinates[2] right_top=boundary.coordinates[2]
cellSize=[right_top[0]-left_button[0],right_top[1]-left_button[1]] cellSize=[right_top[0]-left_bottom[0],right_top[1]-left_bottom[1]]
cellSizeMicron=[cellSize[0]*self.units[0],cellSize[1]*self.units[0]] cellSizeMicron=[cellSize[0]*self.units[0],cellSize[1]*self.units[0]]
if not(cellSizeMicron): if not(cellSizeMicron):
debug.error("Error: "+str(self.rootStructureName)+".cell_size information not found yet") debug.error("Error: "+str(self.rootStructureName)+".cell_size information not found yet")
@ -620,6 +620,15 @@ class VlsiLayout:
cellSizeMicron=[cellSize[0]*self.units[0],cellSize[1]*self.units[0]] cellSizeMicron=[cellSize[0]*self.units[0],cellSize[1]*self.units[0]]
return cellSizeMicron return cellSizeMicron
def measureBoundary(self,startStructure):
self.rootStructureName=startStructure
self.populateCoordinateMap()
cellBoundary = [None, None, None, None]
for TreeUnit in self.xyTree:
cellBoundary=self.measureSizeInStruture(TreeUnit,cellBoundary)
return [[self.units[0]*cellBoundary[0],self.units[0]*cellBoundary[1]],
[self.units[0]*cellBoundary[2],self.units[0]*cellBoundary[3]]]
def measureSizeInStruture(self,Struture,cellBoundary): def measureSizeInStruture(self,Struture,cellBoundary):
StrutureName=Struture[0] StrutureName=Struture[0]
StrutureOrgin=[Struture[1][0],Struture[1][1]] StrutureOrgin=[Struture[1][0],Struture[1][1]]
@ -631,9 +640,9 @@ class VlsiLayout:
debug.info(debug_level,"-Structure direction: vVector["+str(StruturevVector)+"]") debug.info(debug_level,"-Structure direction: vVector["+str(StruturevVector)+"]")
for boundary in self.structures[str(StrutureName)].boundaries: for boundary in self.structures[str(StrutureName)].boundaries:
left_button=boundary.coordinates[0] left_bottom=boundary.coordinates[0]
right_top=boundary.coordinates[2] right_top=boundary.coordinates[2]
thisBoundary=[left_button[0],left_button[1],right_top[0],right_top[1]] thisBoundary=[left_bottom[0],left_bottom[1],right_top[0],right_top[1]]
thisBoundary=self.tranformRectangle(thisBoundary,StrutureuVector,StruturevVector) thisBoundary=self.tranformRectangle(thisBoundary,StrutureuVector,StruturevVector)
thisBoundary=[thisBoundary[0]+StrutureOrgin[0],thisBoundary[1]+StrutureOrgin[1], thisBoundary=[thisBoundary[0]+StrutureOrgin[0],thisBoundary[1]+StrutureOrgin[1],
thisBoundary[2]+StrutureOrgin[0],thisBoundary[3]+StrutureOrgin[1]] thisBoundary[2]+StrutureOrgin[0],thisBoundary[3]+StrutureOrgin[1]]
@ -670,7 +679,8 @@ class VlsiLayout:
pin_boundary=self.readPinInStructureList(label_coordinate, label_layer) pin_boundary=self.readPinInStructureList(label_coordinate, label_layer)
debug.info(debug_level, "Find pin covers "+str(label_name)+" at "+str(pin_boundary)) debug.info(debug_level, "Find pin covers "+str(label_name)+" at "+str(pin_boundary))
pin_boundary=[pin_boundary[0]*self.units[0],pin_boundary[1]*self.units[0],pin_boundary[2]*self.units[0],pin_boundary[3]*self.units[0]] pin_boundary=[pin_boundary[0]*self.units[0],pin_boundary[1]*self.units[0],
pin_boundary[2]*self.units[0],pin_boundary[3]*self.units[0]]
return [label_name, label_layer, pin_boundary] return [label_name, label_layer, pin_boundary]
def readPinInStructureList(self,label_coordinates,layer): def readPinInStructureList(self,label_coordinates,layer):
@ -692,9 +702,9 @@ class VlsiLayout:
for boundary in self.structures[str(StrutureName)].boundaries: for boundary in self.structures[str(StrutureName)].boundaries:
if layer==boundary.drawingLayer: if layer==boundary.drawingLayer:
left_button=boundary.coordinates[0] left_bottom=boundary.coordinates[0]
right_top=boundary.coordinates[2] right_top=boundary.coordinates[2]
MetalBoundary=[left_button[0],left_button[1],right_top[0],right_top[1]] MetalBoundary=[left_bottom[0],left_bottom[1],right_top[0],right_top[1]]
MetalBoundary=self.tranformRectangle(MetalBoundary,StrutureuVector,StruturevVector) MetalBoundary=self.tranformRectangle(MetalBoundary,StrutureuVector,StruturevVector)
MetalBoundary=[MetalBoundary[0]+StrutureOrgin[0],MetalBoundary[1]+StrutureOrgin[1], MetalBoundary=[MetalBoundary[0]+StrutureOrgin[0],MetalBoundary[1]+StrutureOrgin[1],
MetalBoundary[2]+StrutureOrgin[0],MetalBoundary[3]+StrutureOrgin[1]] MetalBoundary[2]+StrutureOrgin[0],MetalBoundary[3]+StrutureOrgin[1]]
@ -707,18 +717,18 @@ class VlsiLayout:
return label_boundary return label_boundary
def tranformRectangle(self,orignalRectangle,uVector,vVector): def tranformRectangle(self,orignalRectangle,uVector,vVector):
LeftButton=mpmath.matrix([orignalRectangle[0],orignalRectangle[1]]) LeftBottom=mpmath.matrix([orignalRectangle[0],orignalRectangle[1]])
LeftButton=self.tranformCoordinate(LeftButton,uVector,vVector) LeftBottom=self.tranformCoordinate(LeftBottom,uVector,vVector)
RightUp=mpmath.matrix([orignalRectangle[2],orignalRectangle[3]]) RightUp=mpmath.matrix([orignalRectangle[2],orignalRectangle[3]])
RightUp=self.tranformCoordinate(RightUp,uVector,vVector) RightUp=self.tranformCoordinate(RightUp,uVector,vVector)
Left=min(LeftButton[0],RightUp[0]) Left=min(LeftBottom[0],RightUp[0])
Button=min(LeftButton[1],RightUp[1]) Bottom=min(LeftBottom[1],RightUp[1])
Right=max(LeftButton[0],RightUp[0]) Right=max(LeftBottom[0],RightUp[0])
Up=max(LeftButton[1],RightUp[1]) Up=max(LeftBottom[1],RightUp[1])
return [Left,Button,Right,Up] return [Left,Bottom,Right,Up]
def tranformCoordinate(self,Coordinate,uVector,vVector): def tranformCoordinate(self,Coordinate,uVector,vVector):
x=Coordinate[0]*uVector[0]+Coordinate[1]*uVector[1] x=Coordinate[0]*uVector[0]+Coordinate[1]*uVector[1]

33
compiler/router/cell.py Normal file
View File

@ -0,0 +1,33 @@
from PIL import ImageColor
class cell:
"""
A single cell that can be occupied in a given layer, blocked,
visited, etc.
"""
scale=1
def __init__(self):
self.visited = 0
self.blocked = False
self.is_source = False
self.is_target = False
def get_color(self):
# Blues are horizontal
if self.blocked:
return ImageColor.getrgb("Blue")
# Reds are source/sink
if self.is_source or self.is_target:
return ImageColor.getrgb("Red")
if self.visited>0:
return [255-min(int(self.visited/cell.scale * 255),255)] * 3
return [255,255,255]

68
compiler/router/grid.py Normal file
View File

@ -0,0 +1,68 @@
import numpy as np
from PIL import Image
import debug
from cell import cell
class grid:
"""A two layer routing map. Each cell can be blocked in the vertical
or horizontal layer.
"""
def __init__(self, width, height):
""" Create a routing map of width x height cells and 2 in the z-axis. """
self.width=width
self.height=height
self.map={}
for x in range(width):
for y in range(height):
for z in range(2):
self.map[x,y,z]=cell()
def view(self,):
"""
View the data by creating an RGB array and mapping the data
structure to the RGB color palette.
"""
v_map = np.zeros((self.width,self.height,3), 'uint8')
mid_map = np.ones((25,self.height,3), 'uint8')
h_map = np.ones((self.width,self.height,3), 'uint8')
# We shouldn't have a path greater than 50% the HPWL
# so scale all visited indices by this value for colorization
cell.scale = 1.5 * (self.width+self.height)
for x in range(self.width):
for y in range(self.height):
h_map[x,y] = self.map[x,y,0].get_color()
v_map[x,y] = self.map[x,y,1].get_color()
v_img = Image.fromarray(v_map, 'RGB').rotate(90)
mid_img = Image.fromarray(mid_map, 'RGB').rotate(90)
h_img = Image.fromarray(h_map, 'RGB').rotate(90)
# concatenate them into a plot with the two layers
img = Image.new('RGB', (2*self.width+25, self.height))
img.paste(h_img, (0,0))
img.paste(mid_img, (self.width,0))
img.paste(v_img, (self.width+25,0))
img.show()
def set_property(self,ll,ur,z,name,value=True):
for x in range(int(ll[0]),int(ur[0])):
for y in range(int(ll[1]),int(ur[1])):
setattr (self.map[x,y,z], name, True)
def add_blockage(self,ll,ur,z):
debug.info(1,"Adding blockage ll={0} ur={1} z={2}".format(str(ll),str(ur),z))
self.set_property(ll,ur,z,"blocked")
def set_source(self,ll,ur,z):
debug.info(1,"Adding source ll={0} ur={1} z={2}".format(str(ll),str(ur),z))
self.set_property(ll,ur,z,"is_source")
def set_target(self,ll,ur,z):
debug.info(1,"Adding target ll={0} ur={1} z={2}".format(str(ll),str(ur),z))
self.set_property(ll,ur,z,"is_target")

View File

@ -1,40 +1,120 @@
import gdsMill import gdsMill
import tech import tech
from contact import contact
import math import math
import debug import debug
from collections import defaultdict from vector import vector
import grid
class router: class router:
"""A router class to read an obstruction map from a gds and plan a """A router class to read an obstruction map from a gds and plan a
route on a given layer. route on a given layer. This is limited to two layer routes.
""" """
def __init__(self, gdsName, topName, layers): def __init__(self, gds_name):
"""Use the gds file for the blockages with the top module topName and """Use the gds file for the blockages with the top module topName and
layers for the layers to route on layers for the layers to route on
""" """
self.topName = topName self.gds_name = gds_name
self.gdsName = gdsName self.layout = gdsMill.VlsiLayout()
self.layout = gdsMill.VlsiLayout(units=tech.GDS["unit"])
self.reader = gdsMill.Gds2reader(self.layout) self.reader = gdsMill.Gds2reader(self.layout)
self.reader.loadFromFile(gdsName) self.reader.loadFromFile(gds_name)
self.top_name = self.layout.rootStructureName
self.unit = float(self.layout.info['units'][0]) self.unit = float(self.layout.info['units'][0])
self.layers = layers print "Units:",self.unit
self.find_blockages() self.pin_names = []
def create_map(self): self.pin_shapes = {}
pass self.pin_layers = {}
self.boundary = self.layout.measureBoundary(self.top_name)
self.ll = vector(self.boundary[0])
self.ur = vector(self.boundary[1])
self.size = self.ur - self.ll
self.width = self.size.x
self.height = self.size.y
print "Boundary: ",self.boundary
print "Size: ", self.width,self.height
# to scale coordinates by units
self.unit_factor = [self.unit] * 2
# We will offset so ll is at (0,0)
self.offset = self.ll
print "Offset: ",self.offset
def set_top(self,top_name):
""" If we want to route something besides the top-level cell."""
self.top_name = top_name
def set_layers(self, layers):
""" Allows us to change the layers that we are routing on. """
self.layers = layers
(horiz_layer, via_layer, vert_layer) = self.layers
if (via_layer != None):
self.via_layer_name = via_layer
else:
self.via_layer_name = None
self.vert_layer_name = vert_layer
self.vert_layer_width = tech.drc["minwidth_{0}".format(vert_layer)]
self.vert_layer_number = tech.layer[vert_layer]
self.horiz_layer_name = horiz_layer
self.horiz_layer_width = tech.drc["minwidth_{0}".format(horiz_layer)]
self.horiz_layer_number = tech.layer[horiz_layer]
# contacted track spacing
via_connect = contact(self.layers, (1, 1))
self.horiz_track_width = tech.drc[str(self.horiz_layer_name)+"_to_"+str(self.horiz_layer_name)] + via_connect.width
self.vert_track_width = tech.drc[str(self.vert_layer_name)+"_to_"+str(self.vert_layer_name)] + via_connect.width
# This is so we can use a single resolution grid for both layers
self.track_width = max(self.horiz_track_width,self.vert_track_width)
print "Track width:",self.track_width
# to scale coordinates to tracks
self.track_factor = [1/self.track_width] * 2
def create_routing_grid(self):
""" Create a routing grid that spans given area. Wires cannot exist outside region. """
self.width_in_tracks = int(math.ceil(self.width/self.track_width))
self.height_in_tracks = int(math.ceil(self.height/self.track_width))
print "Size (in tracks): ", self.width_in_tracks, self.height_in_tracks
self.rg = grid.grid(self.width_in_tracks,self.height_in_tracks)
def find_pin(self,pin):
""" Finds the offsets to the gds pins """
(pin_name,pin_layer,pin_shape) = self.layout.readPin(str(pin))
# repack the shape as a pair of vectors rather than four values
new_shape = self.convert_to_tracks([vector(pin_shape[0],pin_shape[1]),vector(pin_shape[2],pin_shape[3])])
self.pin_names.append(pin_name)
self.pin_shapes[str(pin)] = new_shape
self.pin_layers[str(pin)] = pin_layer
return new_shape
def find_blockages(self): def find_blockages(self):
for layer in self.layer: if len(self.pin_names)!=2:
debug.info("Layer: " + layer) debug.error("Must set pins before creating blockages.",-1)
self.writeObstruct(self.topName, layer)
for layer in self.layers:
self.write_obstacle(self.top_name)
def add_route(self,start, end, layerstack): def add_route(self,start, end, layerstack):
""" Add a wire route from the start to the end point""" """ Add a wire route from the start to the end point"""
pass pass
def create_steiner_routes(self,pins): def create_steiner_routes(self,pins):
@ -46,30 +126,115 @@ class router:
""" Find the set of steiner points and return them.""" """ Find the set of steiner points and return them."""
pass pass
def translate_coordinates(self, coord, mirr, angle, xyShift):
"""Calculate coordinates after flip, rotate, and shift"""
coordinate = []
for item in coord:
x = (item[0]*math.cos(angle)-item[1]*mirr*math.sin(angle)+xyShift[0])
y = (item[0]*math.sin(angle)+item[1]*mirr*math.cos(angle)+xyShift[1])
coordinate += [(x, y)]
return coordinate
def writeObstruct(self, sr, lay, mirr = 1, angle = math.radians(float(0)), xyShift = (0, 0)): def min_max_coord(self, coordTrans):
"""Find the lowest and highest conner of a Rectangle"""
coordinate = []
minx = min(coordTrans[0][0], coordTrans[1][0], coordTrans[2][0], coordTrans[3][0])
maxx = max(coordTrans[0][0], coordTrans[1][0], coordTrans[2][0], coordTrans[3][0])
miny = min(coordTrans[0][1], coordTrans[1][1], coordTrans[2][1], coordTrans[3][1])
maxy = max(coordTrans[0][1], coordTrans[1][1], coordTrans[2][1], coordTrans[3][1])
coordinate += [vector(minx, miny)]
coordinate += [vector(maxx, maxy)]
return coordinate
def set_source(self,name):
shape = self.find_pin(name)
zindex = 0 if self.pin_layers[name]==self.horiz_layer_number else 1
debug.info(0,"Set source: " + str(name) + " " + str(shape) + " z=" + str(zindex))
self.rg.set_source(shape[0],shape[1],zindex)
def set_target(self,name):
shape = self.find_pin(name)
zindex = 0 if self.pin_layers[name]==self.horiz_layer_number else 1
debug.info(0,"Set target: " + str(name) + " " + str(shape) + " z=" + str(zindex))
self.rg.set_target(shape[0],shape[1],zindex)
def write_obstacle(self, sref, mirr = 1, angle = math.radians(float(0)), xyShift = (0, 0)):
"""Recursive write boundaries on each Structure in GDS file to LEF""" """Recursive write boundaries on each Structure in GDS file to LEF"""
for boundary in self.layout.structures[sr].boundaries:
coordTrans = self.coordinatesTranslate(boundary.coordinates, mirr, angle, xyShift)
rect = self.minMaxCoord(coordTrans)
lay_convert = tech.layer[lay]
if boundary.drawingLayer == lay_convert:
text = " RECT "
for item in rect:
text += " {0} {1}".format(item[0]*self.unit, item[1]*self.unit)
debug.info(text)
for sref in self.layout.structures[sr].srefs: for boundary in self.layout.structures[sref].boundaries:
coordTrans = self.translate_coordinates(boundary.coordinates, mirr, angle, xyShift)
shape = self.min_max_coord(coordTrans)
if boundary.drawingLayer in [self.vert_layer_number,self.horiz_layer_number]:
ll_microns=shape[0].scale(self.unit_factor)
ur_microns=shape[1].scale(self.unit_factor)
shape_tracks=self.convert_to_tracks([ll_microns,ur_microns])
if shape_tracks not in self.pin_shapes.values():
# inflate the ll and ur by 1 track in each direction
[ll,ur]=shape_tracks
ll = vector(0,0).max(ll + vector(-1,-1))
ur = vector(self.width_in_tracks-1,self.height_in_tracks-1).min(ur + vector(1,1))
zlayer = 0 if boundary.drawingLayer==self.horiz_layer_number else 1
debug.info(1,"Blockage: "+str([ll,ur])+" z="+str(zlayer))
self.rg.add_blockage(ll,ur,zlayer)
else:
debug.info(2,"Skip: "+str(shape_tracks))
# recurse given the mirror, angle, etc.
for cur_sref in self.layout.structures[sref].srefs:
sMirr = 1 sMirr = 1
if sref.transFlags[0] == True: if sref.transFlags[0] == True:
sMirr = -1 sMirr = -1
sAngle = math.radians(float(0)) sAngle = math.radians(float(0))
if sref.rotateAngle: if sref.rotateAngle:
sAngle = math.radians(float(sref.rotateAngle)) sAngle = math.radians(float(cur_sref.rotateAngle))
sAngle += angle sAngle += angle
x = sref.coordinates[0] x = cur_sref.coordinates[0]
y = sref.coordinates[1] y = cur_sref.coordinates[1]
newX = (x)*math.cos(angle) - mirr*(y)*math.sin(angle) + xyShift[0] newX = (x)*math.cos(angle) - mirr*(y)*math.sin(angle) + xyShift[0]
newY = (x)*math.sin(angle) + mirr*(y)*math.cos(angle) + xyShift[1] newY = (x)*math.sin(angle) + mirr*(y)*math.cos(angle) + xyShift[1]
sxyShift = (newX, newY) sxyShift = (newX, newY)
self.writeObstruct(sref.sName, lay,sMirr, sAngle, sxyShift)
self.write_obstacle(cur_sref.sName, layer,sMirr, sAngle, sxyShift)
def inflate_obstacle(self,shape):
# TODO: inflate by the layer design rules
return shape
def convert_to_tracks(self,shape):
"""
Convert a rectangular shape into track units.
"""
[ll,ur] = shape
# fix offset
ll = snap_to_grid(ll-self.offset)
ur = snap_to_grid(ur-self.offset)
# always round down, because we will add a track
# to inflate each object later
ll = ll.scale(self.track_factor).ceil()
ur = ur.scale(self.track_factor).floor()
return [ll,ur]
# FIXME: This should be replaced with vector.snap_to_grid at some point
def snap_to_grid(offset):
"""
Changes the coodrinate to match the grid settings
"""
grid = tech.drc["grid"]
x = offset[0]
y = offset[1]
# this gets the nearest integer value
xgrid = int(round(round((x / grid), 2), 0))
ygrid = int(round(round((y / grid), 2), 0))
xoff = xgrid * grid
yoff = ygrid * grid
return vector(xoff, yoff)

View File

@ -0,0 +1,54 @@
#!/usr/bin/env python2.7
"Run a regresion test the library cells for DRC"
import unittest
from testutils import header
import sys,os
sys.path.append(os.path.join(sys.path[0],"../.."))
sys.path.append(os.path.join(sys.path[0],".."))
import globals
import debug
import calibre
import vector
class no_blockages_test(unittest.TestCase):
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
import router
#r=router.router("A_to_B_no_blockages.gds")
r=router.router("A_to_B_m1m2_blockages.gds")
r.set_layers(("metal1","via1","metal2"))
r.create_routing_grid()
r.set_source("A")
r.set_target("B")
r.find_blockages()
r.rg.view()
#drc_errors = calibre.run_drc(name, gds_name)
drc_errors = 1
# fails if there are any DRC errors on any cells
self.assertEqual(drc_errors, 0)
globals.end_openram()
# instantiate a copy of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,23 @@
word_size = 1
num_words = 16
num_banks = 1
tech_name = "freepdk45"
decoder = "hierarchical_decoder"
ms_flop = "ms_flop"
ms_flop_array = "ms_flop_array"
control_logic = "control_logic"
bitcell_array = "bitcell_array"
sense_amp = "sense_amp"
sense_amp_array = "sense_amp_array"
precharge_array = "precharge_array"
column_mux_array = "single_level_column_mux_array"
write_driver = "write_driver"
write_driver_array = "write_driver_array"
tri_gate = "tri_gate"
tri_gate_array = "tri_gate_array"
wordline_driver = "wordline_driver"
replica_bitcell = "replica_bitcell"
bitcell = "bitcell"
delay_chain = "logic_effort_dc"

View File

@ -0,0 +1,23 @@
word_size = 1
num_words = 16
num_banks = 1
tech_name = "scn3me_subm"
decoder = "hierarchical_decoder"
ms_flop = "ms_flop"
ms_flop_array = "ms_flop_array"
control_logic = "control_logic"
bitcell_array = "bitcell_array"
sense_amp = "sense_amp"
sense_amp_array = "sense_amp_array"
precharge_array = "precharge_array"
column_mux_array = "single_level_column_mux_array"
write_driver = "write_driver"
write_driver_array = "write_driver_array"
tri_gate = "tri_gate"
tri_gate_array = "tri_gate_array"
wordline_driver = "wordline_driver"
replica_bitcell = "replica_bitcell"
bitcell = "bitcell"
delay_chain = "logic_effort_dc"

View File

@ -0,0 +1,30 @@
#!/usr/bin/env python2.7
import re
import unittest
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
from testutils import header
header(__file__, OPTS.tech_name)
# get a list of all files in the tests directory
files = os.listdir(sys.path[0])
# assume any file that ends in "test.py" in it is a regression test
nametest = re.compile("test\.py$", re.IGNORECASE)
tests = filter(nametest.search, files)
tests.sort()
# import all of the modules
filenameToModuleName = lambda f: os.path.splitext(f)[0]
moduleNames = map(filenameToModuleName, tests)
modules = map(__import__, moduleNames)
suite = unittest.TestSuite()
load = unittest.defaultTestLoader.loadTestsFromModule
suite.addTests(map(load, modules))
unittest.TextTestRunner(verbosity=2).run(suite)

View File

@ -1,5 +1,5 @@
import debug import debug
import math
class vector(): class vector():
""" """
@ -61,6 +61,7 @@ class vector():
""" """
return vector(self.x + other[0], self.y + other[1]) return vector(self.x + other[0], self.y + other[1])
def __radd__(self, other): def __radd__(self, other):
""" """
Override + function (right add) Override + function (right add)
@ -99,3 +100,33 @@ class vector():
y_factor=x_factor[1] y_factor=x_factor[1]
x_factor=x_factor[0] x_factor=x_factor[0]
return vector(self.y*x_factor,self.x*y_factor) return vector(self.y*x_factor,self.x*y_factor)
def floor(self):
"""
Override floor function
"""
return vector(int(math.floor(self.x)),int(math.floor(self.y)))
def ceil(self):
"""
Override ceil function
"""
return vector(int(math.ceil(self.x)),int(math.ceil(self.y)))
def __eq__(self, other):
"""Override the default Equals behavior"""
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
return False
def __ne__(self, other):
"""Override the default non-equality behavior"""
return not self.__eq__(other)
def max(self, other):
""" Max of both values """
return vector(max(self.x,other.x),max(self.y,other.y))
def min(self, other):
""" Min of both values """
return vector(min(self.x,other.x),min(self.y,other.y))