def __init__(self, position, mass, charge, timeInterval):
		"""Takes position as list [x, y], mass, charge (sign indicates positive or negative), and the
		amount of time that passes between value calculations (the time between frames shown)"""
		self.moveState = False
		self.pos = position
		self.mass = mass
		self.velocity = [0, 0]
		self.charge = charge
		self.timeInterval = timeInterval
		#setting radius to 20x the mass of particle, can change later
		if self.charge < 0:
			self.graphicalObject = GraphicalParticle(self.pos, self.mass*20, Charge.NEGATIVE, (0, 0, 255))
		else: 
			self.graphicalObject = GraphicalParticle(self.pos, self.mass*20, Charge.POSITIVE, (255, 0, 0))
class Particle:
	def __init__(self, position, mass, charge, timeInterval):
		"""Takes position as list [x, y], mass, charge (sign indicates positive or negative), and the
		amount of time that passes between value calculations (the time between frames shown)"""
		self.moveState = False
		self.pos = position
		self.mass = mass
		self.velocity = [0, 0]
		self.charge = charge
		self.timeInterval = timeInterval
		#setting radius to 20x the mass of particle, can change later
		if self.charge < 0:
			self.graphicalObject = GraphicalParticle(self.pos, self.mass*20, Charge.NEGATIVE, (0, 0, 255))
		else: 
			self.graphicalObject = GraphicalParticle(self.pos, self.mass*20, Charge.POSITIVE, (255, 0, 0))
		


	def getPos(self):
		"""returns position of particle as list [x, y]"""
		return [self.pos[0], self.pos[1]]

	def getMass(self):
		"""returns mass of particle"""
		return self.mass

	def getVelocity(self):
		"""return velocity of partice as list [x component, y component]"""
		return [self.velocity[0], self.velocity[1]]

	def getCharge(self):
		"""returns charge of particle"""
		return self.charge

	@staticmethod
	def sinatan(x):
		"""Internally used function, returns sine of the arc tangent of the parameter"""
		return x/math.sqrt((x**2)+1)

	@staticmethod
	def cosatan(x):
		"""Internally used function, returns cosine of the arc tangent of the parameter"""
		return 1 / math.sqrt((x ** 2) + 1)


	@staticmethod
	def distance(pos1, pos2):
		"""Internally used function, takes 2 position tuples (x, y) and returns the distance between them"""
		return ((pos1[0]-pos2[0])**2 + (pos1[1]-pos2[1])**2)**0.5

	@staticmethod
	def arcTanWithChecks(x, y):
		"""Performs the arctangent function on the given fraction (y/x)and handles division by zero"""
		if x == 0 and y > 0:
			return math.pi / 2
		elif x == 0 and y < 0:
			return 3 * math.pi / 4
		elif x == 0 and y == 0:
			return 0
		else:
			return math.atan(y/x)

	@classmethod
	def crossProduct(cls, vec1, vec2):
		"""Internally used function, takes 2 2-D vectors as tuples and returns the magnitude (with sign)
		of the resulting cross product"""
		theta1 = cls.arcTanWithChecks(vec1[0], vec1[1])
		theta2 = cls.arcTanWithChecks(vec2[0], vec2[1])
		mag1 = cls.distance((0, 0), vec1)
		mag2 = cls.distance((0,0),  vec2)
		return math.sin(theta1-theta2)*mag1*mag2

	def calculateNetMagneticFieldForce(self, particleList):
		"""Internally used method, returns force due to magnetic field effects
		as tuple (x component, y component)"""
		netMagField = 0
		for p in particleList:
			if not(p is self):
				displacementVec = (p.getPos()[0] - self.pos[0], p.getPos()[1] - self.pos[1])
				cross = self.crossProduct(p.getVelocity(), displacementVec)
				distance = self.distance((0, 0), displacementVec)
				netMagField += (10**-7) * p.getCharge() * cross/distance
		magnitudeV = self.distance((0, 0), self.velocity)
		magnitudeF = magnitudeV * netMagField
		#subtract pi/2 from angle of v to get angle of f
		angleV = self.arcTanWithChecks(self.velocity[0], self.velocity[1])
		angleF = angleV - math.pi/2
		return magnitudeF*math.cos(angleF), magnitudeF*math.sin(angleF)

	def calculateNetCoulombsLawForce(self, particleList):
		"""Internally used method, returns force due to electric interaction with other charged particles
		as tuple (x component, y component)"""
		force = [0, 0]
		for p in particleList:
			if not(p is self):
				magnitude = 8.99*(10**9) * self.charge * p.getCharge() / (self.distance(self.pos, p.getPos())**2)
				#special case so that we don't get division by zero
				if self.pos[0] - p.getPos()[0] == 0:
					force[0] += 0
					force[1] += magnitude
				else:
					angleSlope = (self.pos[1]-p.getPos()[1])/(self.pos[0]-p.getPos()[0])
					force[0] += self.cosatan(angleSlope)*magnitude * math.copysign(1.0, self.pos[0]-p.getPos()[0])
					force[1] += self.sinatan(angleSlope)*magnitude * math.copysign(1.0, self.pos[0]-p.getPos()[0])
		return force[0], force[1]


	def isInside(self, staticField):
		"""Internally used method that checks if particle is inside a given electric field"""
		if self.pos[0] > staticField.topLeft[0] and self.pos[0] < staticField.bottomRight[0]:
			if self.pos[1] > staticField.topLeft[1] and self.pos[1] < staticField.bottomRight[1]:
				return True
		return False

	def calculateNetElectricFieldForce(self, staticFieldList):
		"""Internally used method, returns force due to influence of static electric field on particle
		as tuple (x component, y component)"""
		force = [0, 0]
		for ef in staticFieldList:
			if self.isInside(ef):
				magnitude = self.charge * ef.getStrength()
				force[0] += magnitude*math.cos(ef.getDirection())
				force[1] += magnitude*math.sin(ef.getDirection())
		return force[0], -force[1]


	def calculateDisplacement(self, particleList, staticFieldList):
		"""takes list of other particles and static fields in the system,
		updates internal dx and dy variables"""
		if self.moveState:
			self.newVelocity = (0, 0)
			self.dx = 0
			self.dy = 0
		else:
			mff = self.calculateNetMagneticFieldForce(particleList)
			clf = self.calculateNetCoulombsLawForce(particleList)
			eff = self.calculateNetElectricFieldForce(staticFieldList)
			acceleration = ((mff[0]+clf[0]+eff[0])/self.mass, (mff[1]+clf[1]+eff[1])/self.mass)
			#change in velocity is acceleration*time
			self.newVelocity = (self.velocity[0] + acceleration[0]*self.timeInterval,
								self.velocity[1] + acceleration[1]*self.timeInterval)
			self.dx = self.velocity[0]*self.timeInterval + 0.5*acceleration[0]*self.timeInterval
			self.dy = self.velocity[1]*self.timeInterval + 0.5*acceleration[1]*self.timeInterval


	def finalizeValues(self):
		"""Indicates to particle object that all other particle object calculations are done,
		so that internal values can be updated and used for rendering"""
		self.pos = [self.pos[0] + self.dx, self.pos[1] + self.dy]
		self.velocity = self.newVelocity
		self.graphicalObject.update(self.pos, self.mass*20, Charge.NEGATIVE if self.charge < 0 else Charge.POSITIVE, (0, 0, 255) if self.charge < 0 else (255, 0, 0))

	def draw(self, screen):
		"""Draws objects using GraphicalParticle"""
		self.graphicalObject.draw(screen)