<commit_before>
import rhinoscriptsyntax as rs
import random
import math

# this method returns the component of vec1 along vec2
def component(vec1, vec2):
	dotProduct = rs.VectorDotProduct(vec1, vec2)
	modVec2 = rs.VectorLength(vec2)
	unitVec2 = rs.VectorUnitize(vec2)
	
	component = rs.VectorScale(unitVec2, dotProduct/modVec2)
	
	return component

class brick:
	#initializes a new brick of dimensions - dim, with the center of its base positioned at 'pos'
	#dim is a list of the form [length, width, height]
	# in counter clockwise order, bottomplane first
	
	def __init__(self, pos, dimension, typeOfBrick = 0):
		self.type = typeOfBrick
		
		#Now determining setting the true dimensions of the brick based on its type
		if self.type == 1: # this is a halfBrick
			dimension[0] = dimension[0]/2
		elif self.type == 2: # this is a Quarter brick
			dimension[0] = dimension[0]/4
		elif self.type == 3: # this is a 3/4ths of a brick
			dimension[0] = dimension[0]*3/4
		
		self.dim = dimension
		self.base = pos
		
		u = [self.dim[0], 0, 0]
		v = [0, self.dim[1], 0]
		w = [0, 0, self.dim[2]]
		
		vert = []
		vert.append(pos)
		p = rs.VectorAdd(pos,u)
		vert.append(p)
		p = rs.VectorAdd(p,v)
		vert.append(p)
		p = rs.VectorSubtract(p,u)
		vert.append(p)
		
		i = 0
		while i < 4:
			p1 = rs.VectorAdd(vert[i], w)
			vert.append(p1)
			i += 1
		
		self.id = rs.AddBox(vert)
		clearIsoCurves = rs.SurfaceIsocurveDensity(self.id, 0)
		self.vertex = vert
		self.direction = u
	
	def rotateBrick(self, angle, axis):
		done = rs.RotateObject(self.id, self.base, angle, axis)
		if done:
			self.direction = rs.VectorRotate(self.direction, angle, axis)
			return True
		else:
			return False
	
	#this function aligns the brick with the given vector
	def alignWith(self, vec):
		axis = rs.VectorCrossProduct(self.direction, vec)
		angle = rs.VectorAngle(vec, self.direction)
		
		done = rs.RotateObject(self.id, self.base, angle, axis)
		if done:
			self.direction = rs.VectorRotate(self.direction, angle, axis)
			return True
		else:
			return False
	
	#moves the brick to newPos - base position
	def moveTo(self, newPos):
		translation = rs.VectorSubtract(newPos, self.base)
		self.id = rs.MoveObject(self.id, translation)
		if self.id:
			self.base = newPos
		
	# calculated the intercept of the brick in the direction vec
	def intercept(self, vec):
		aspectAng = math.atan(self.dim[1]/self.dim[0])
		vecAng = math.pi*rs.VectorAngle(self.direction, vec)/180
		
		if vecAng < aspectAng or vecAng > (math.pi-aspectAng):
			return abs(self.dim[0]/math.cos(vecAng))
		elif vecAng > aspectAng and vecAng < (math.pi - aspectAng):
			return self.dim[1]/math.sin(vecAng)

def distRatio(curID, param):
	domain = rs.CurveDomain(curID)
	sample = (domain[1] - domain[0])/1000000
	
	pt = rs.EvaluateCurve(curID, param)
	param2 = param + sample
	pt2 = rs.EvaluateCurve(curID, param2)
	dist = rs.Distance(pt, pt2)
	
	return (dist/sample)
	
def makeCurves(curveID, layerNum, spacing):
	dom = rs.CurveDomain(curveID)
	tan = rs.CurveTangent(curveID, dom[0])
	normal = rs.CurveNormal(curveID)
	dir = rs.VectorRotate(normal,90,tan)
	
	layerEven = False
	if layerNum%2:
		offsetBound = spacing*(layerNum-1)/2
	else:
		layerEven = True
		offsetBound = spacing*((layerNum/2)-0.5)
	
	curves = []
	i = -offsetBound
	while i <= offsetBound:
		newCurve = rs.OffsetCurve(curveID, dir, i)
		if newCurve:
			curves.append(newCurve[0])
		else:
			duplicate = rs.CopyObject(curveID, [0,0,0])
			curves.append(duplicate)
		
		i += spacing
	
	if layerEven:
		rs.DeleteObject(curveID)
		
	return curves
	
def craziness(iterNum):
	return 0
	#return 20*math.sin(45*iterNum*math.pi/180)
	#return 15*iterNum
	#return iterNum
	#return random.uniform(-10,10)+90
	#return random.uniform(-20,20)
	#return 20
	
	num = random.randint(0,4)
	if num == 0:
		return 90
	else:
		return 0

class Wall:
	def __init__(self, curveID, lNum, ht):
		self.height = ht
		self.mortarThickness = 10
		self.centerLine = curveID
		courseNum = ht/(brickDim[2]+self.mortarThickness)
		self.id = rs.AddGroup()
		curveList = makeCurves(curveID, lNum, (brickDim[1]+self.mortarThickness))
		
		self.brickCount = 0
		
		isEven = False
		c = 0 # iterating variable representing the current course Number
		while c < courseNum:
			
			layer = 0
			layerEven = False
			while layer < len(curveList):
				curID = rs.CopyObject(curveList[layer], [0,0,0])
				dom = rs.CurveDomain(curID)
				p = dom[0]
				pShiftLayer = (brickDim[0]/2)/distRatio(curID, p)
				if layerEven:
					p += pShiftLayer
				
				if isEven:
					pShift = (brickDim[0]/4)/distRatio(curID, p)
					p += pShift
			
				n = 0
				nLimit = 8
				while p <= dom[1]:
					pt = rs.EvaluateCurve(curID, p)
					block = brick(pt, brickDim)
					self.brickCount += 1
					
					tangent = rs.CurveTangent(curID, p)
					axis = rs.VectorCrossProduct(block.direction, tangent)
					brickDir = rs.VectorRotate(tangent, craziness(n), axis)
					aligned = block.alignWith(brickDir)
					if not aligned:
						print('Something went wrong with the alignment')
					
					#print(block.intercept(brickDir))
					step = (block.intercept(tangent)/2)/distRatio(curID, p)
					p += step
					pt = rs.EvaluateCurve(curID, p)
					block.moveTo(pt)
					
					# checking of the angle between the two vectors is almost 90
					if abs(rs.VectorAngle(tangent, brickDir)-90) < 0.1:
						#vector joining current point to closes point on the center line curve
						unitNormal = rs.VectorUnitize(brickDir)
						
						moveVec = rs.VectorScale(unitNormal, block.dim[0]/4)
						finalPt = rs.VectorAdd(block.base, moveVec)
						
						block.moveTo(finalPt)
					
					rs.AddObjectToGroup(block.id, self.id)
					
					step = ((block.intercept(tangent)/2)+self.mortarThickness)/distRatio(curID, p)
					p += step
					n += 1
				
				layer += 1
				layerEven = not layerEven
				rs.DeleteObject(curID)
				
			c += 1
			isEven = not isEven
			
			zStep = brickDim[2]+self.mortarThickness
			curveList = rs.MoveObjects(curveList, [0,0,zStep])
		
		rs.DeleteObjects(curveList)
		
brickDim = [230,115,80]
curve = rs.GetCurveObject('Select Curve')
layers = rs.GetInteger('Enter Number of Layers',1)
height = rs.GetInteger('Enter Wall height', 2000)

rs.EnableRedraw(False)
newWall = Wall(curve[0], layers, height)
#newBrick = brick([0,0,0], brickDim, 1)
rs.EnableRedraw(True)<commit_msg>Improved it further... added a build random method which builds the wall randomly by choosing from the bond options<commit_after>
import rhinoscriptsyntax as rs
import random
import math

# this method returns the component of vec1 along vec2
def component(vec1, vec2):
	dotProduct = rs.VectorDotProduct(vec1, vec2)
	modVec2 = rs.VectorLength(vec2)
	unitVec2 = rs.VectorUnitize(vec2)
	
	component = rs.VectorScale(unitVec2, dotProduct/modVec2)
	
	return component

class brick:
	#initializes a new brick of dimensions - dim, with the center of its base positioned at 'pos'
	#dim is a list of the form [length, width, height]
	# in counter clockwise order, bottomplane first
	
	def __init__(self, pos, dimension, typeOfBrick = 0):
		self.type = typeOfBrick
		
		#Now determining setting the true dimensions of the brick based on its type
		if self.type == 1: # this is a halfBrick
			dimension[0] = dimension[0]/2
		elif self.type == 2: # this is a Quarter brick
			dimension[0] = dimension[0]/4
		elif self.type == 3: # this is a 3/4ths of a brick
			dimension[0] = dimension[0]*3/4
		
		self.dim = dimension
		self.base = pos
		
		u = [self.dim[0], 0, 0]
		v = [0, self.dim[1], 0]
		w = [0, 0, self.dim[2]]
		
		vert = []
		vert.append(pos)
		p = rs.VectorAdd(pos,u)
		vert.append(p)
		p = rs.VectorAdd(p,v)
		vert.append(p)
		p = rs.VectorSubtract(p,u)
		vert.append(p)
		
		i = 0
		while i < 4:
			p1 = rs.VectorAdd(vert[i], w)
			vert.append(p1)
			i += 1
		
		self.id = rs.AddBox(vert)
		clearIsoCurves = rs.SurfaceIsocurveDensity(self.id, 0)
		self.vertex = vert
		self.direction = u
	
	def rotateBrick(self, angle, axis):
		done = rs.RotateObject(self.id, self.base, angle, axis)
		if done:
			self.direction = rs.VectorRotate(self.direction, angle, axis)
			return True
		else:
			return False
	
	#this function aligns the brick with the given vector
	def alignWith(self, vec):
		axis = rs.VectorCrossProduct(self.direction, vec)
		angle = rs.VectorAngle(vec, self.direction)
		
		done = rs.RotateObject(self.id, self.base, angle, axis)
		if done:
			self.direction = rs.VectorRotate(self.direction, angle, axis)
			return True
		else:
			return False
	
	#moves the brick to newPos - base position
	def moveTo(self, newPos):
		translation = rs.VectorSubtract(newPos, self.base)
		self.id = rs.MoveObject(self.id, translation)
		if self.id:
			self.base = newPos
		
	# calculated the intercept of the brick in the direction vec
	def intercept(self, vec):
		aspectAng = math.atan(self.dim[1]/self.dim[0])
		vecAng = math.pi*rs.VectorAngle(self.direction, vec)/180
		
		if vecAng < aspectAng or vecAng > (math.pi-aspectAng):
			return abs(self.dim[0]/math.cos(vecAng))
		elif vecAng > aspectAng and vecAng < (math.pi - aspectAng):
			return self.dim[1]/math.sin(vecAng)

#returns length per unit parameter atany point on a curve
def distRatio(curID, param):
	domain = rs.CurveDomain(curID)
	sample = (domain[1] - domain[0])/1000000
	
	pt = rs.EvaluateCurve(curID, param)
	param2 = param + sample
	pt2 = rs.EvaluateCurve(curID, param2)
	dist = rs.Distance(pt, pt2)
	
	return (dist/sample)
	
def makeCurves(curveID, layerNum, spacing):
	dom = rs.CurveDomain(curveID)
	tan = rs.CurveTangent(curveID, dom[0])
	normal = rs.CurveNormal(curveID)
	dir = rs.VectorRotate(normal,90,tan)
	
	layerEven = False
	if layerNum%2:
		offsetBound = spacing*(layerNum-1)/2
	else:
		layerEven = True
		offsetBound = spacing*((layerNum/2)-0.5)
	
	curves = []
	i = -offsetBound
	while i <= offsetBound:
		newCurve = rs.OffsetCurve(curveID, dir, i)
		if newCurve:
			curves.append(newCurve[0])
		else:
			duplicate = rs.CopyObject(curveID, [0,0,0])
			curves.append(duplicate)
		
		i += spacing
	
	if layerEven:
		rs.DeleteObject(curveID)
		
	return curves
	
def craziness(iterNum):
	return 0
	#return 20*math.sin(45*iterNum*math.pi/180)
	#return 15*iterNum
	#return iterNum
	#return random.uniform(-10,10)+90
	#return random.uniform(-20,20)
	#return 20
	
	num = random.randint(0,4)
	if num == 0:
		return 90
	else:
		return 0

class bond:
	def __init__(self, courseList, unitLength, unitHeight, dimension):
		#this list contains all the details of all courses
		self.course = courseList
		self.unitL = unitLength
		self.unitH = unitHeight
		self.dim = dimension
	
	def placeSampleUnit(self, pos, vec):
		u = rs.VectorUnitize(vec)
		v = rs.VectorUnitize(rs.VectorRotate(vec,90,[0,0,1]))
		
		c = 0
		while c < len(self.course):
			b = 0
			while b < len(self.course[c]):
				p = self.course[c][b][0]
				pVec = rs.VectorAdd(rs.VectorScale(u,p[0]*self.unitL), rs.VectorScale(v,p[1]*self.unitL))
				newPos = rs.VectorAdd(pos,pVec)
				newPos[2] = c*self.unitH
				
				type = self.course[c][b][1]
				orientation = self.course[c][b][2]
				
				newBrick = brick(newPos, brickDim, type)
				brickDir = rs.VectorRotate(u,orientation,[0,0,1])
				newBrick.alignWith(brickDir)
				
				b += 1
			c += 1
		
class Wall:
	def __init__(self, curveID, bondConfig, ht, mortarThickness = 10):
		self.height = ht
		self.mortarT = mortarThickness
		self.pathID = curveID
		courseNum = ht/(brickDim[2]+self.mortarT)
		self.id = rs.AddGroup()
		
		self.bond = bondConfig
		
		self.courseNum = self.height/self.bond.unitH
		
		self.brickCount = 0
		
		self.bondOptions = []
	
	def build(self):
		dom = rs.CurveDomain(self.pathID)
		uL = self.bond.unitL
		
		def traverseTo(uv,unitD, curID, startParam, morT):
			dom = rs.CurveDomain(curID)
			p1 = startParam
			uCur = 0
			while uCur < uv[0]:
				pStep = 0.5*unitD/distRatio(curID, startParam)
				p1 += pStep
				
				uCur += 0.5
			
			if p1 > dom[1]:
				return [None, dom[1]]
			
			tan = rs.CurveTangent(curID, p1)
			u = rs.VectorUnitize(tan)
			v = rs.VectorRotate(u, 90, [0,0,1])
			
			pos = rs.EvaluateCurve(self.pathID, p1)
			# now add the vcomponent to the pos
			pos = rs.VectorAdd(pos, rs.VectorScale(v, (uv[1]*unitD)))
			return [pos,p1]
		
		def decideCourse(cNum, bondCourses):
			return (cNum-1)%bondCourses
		
		cN = 1
		while cN <= self.courseNum:
			c = decideCourse(cN, len(self.bond.course))
			param = dom[0]
			while param < dom[1]:
				b = 0
				while b < len(self.bond.course[c]):
					#lay this particular brick
					posParam = self.bond.course[c][b][0]
					type = self.bond.course[c][b][1]
					angle = self.bond.course[c][b][2]
					
					brickPos = traverseTo(posParam, uL, self.pathID, param,self.mortarT)[0]
					zShift = [0,0,(cN-1)* self.bond.unitH]
					
					if brickPos is None:
						b += 1
					else:
						#print(zShift, brickPos)
						brickPos = rs.VectorAdd(brickPos, zShift)
						
						tangent = rs.CurveTangent(self.pathID, param)
						u = rs.VectorUnitize(tangent)
						v = rs.VectorRotate(u, 90, [0,0,1])
						
						brickDir = rs.VectorRotate(u, angle, [0,0,1])
						
						newBrick = brick(brickPos, [230,115,80], type)
						rs.AddObjectToGroup(newBrick.id, self.id)
						self.brickCount += 1
						newBrick.alignWith(brickDir)
							
						b += 1
				
				reset = self.bond.dim
				reset[1] = 0
				pNew = traverseTo(reset, uL, self.pathID, param,self.mortarT)[1]
				param = pNew
				#rs.AddPoint(rs.EvaluateCurve(self.pathID, param))
			cN += 1
	
	def buildRandom(self):
		dom = rs.CurveDomain(self.pathID)
		uL = self.bond.unitL
		
		def traverseTo(uv,unitD, curID, startParam, morT):
			dom = rs.CurveDomain(curID)
			p1 = startParam
			uCur = 0
			while uCur < uv[0]:
				pStep = 0.5*unitD/distRatio(curID, startParam)
				p1 += pStep
				
				uCur += 0.5
			
			if p1 > dom[1]:
				return [None, dom[1]]
			
			tan = rs.CurveTangent(curID, p1)
			u = rs.VectorUnitize(tan)
			v = rs.VectorRotate(u, 90, [0,0,1])
			
			pos = rs.EvaluateCurve(self.pathID, p1)
			# now add the vcomponent to the pos
			pos = rs.VectorAdd(pos, rs.VectorScale(v, (uv[1]*unitD)))
			return [pos,p1]
		
		def decideCourse(cNum, bondCourses):
			return (cNum-1)%bondCourses
		
		cN = 1
		while cN <= self.courseNum:
			c = decideCourse(cN, len(self.bond.course))
			param = dom[0]
			while param < dom[1]:
				b = 0
				randNum = random.randint(1,len(self.bondOptions)) - 1
				curBond = self.bondOptions[randNum]
				while b < len(curBond .course[c]):
					#lay this particular brick
					
					posParam = curBond .course[c][b][0]
					type = curBond .course[c][b][1]
					angle = curBond .course[c][b][2]
					
					brickPos = traverseTo(posParam, uL, self.pathID, param,self.mortarT)[0]
					zShift = [0,0,(cN-1)* curBond .unitH]
					
					if brickPos is None:
						b += 1
					else:
						#print(zShift, brickPos)
						brickPos = rs.VectorAdd(brickPos, zShift)
						
						tangent = rs.CurveTangent(self.pathID, param)
						u = rs.VectorUnitize(tangent)
						v = rs.VectorRotate(u, 90, [0,0,1])
						
						brickDir = rs.VectorRotate(u, angle, [0,0,1])
						
						newBrick = brick(brickPos, [230,115,80], type)
						rs.AddObjectToGroup(newBrick.id, self.id)
						self.brickCount += 1
						newBrick.alignWith(brickDir)
							
						b += 1
				
				reset = self.bond.dim
				reset[1] = 0
				pNew = traverseTo(reset, uL, self.pathID, param,self.mortarT)[1]
				param = pNew
				#rs.AddPoint(rs.EvaluateCurve(self.pathID, param))
			cN += 1

englishBondCourses = [
					[#course1
						[[0,0],0,0],
						[[0,1],0,0]
						],
					[#course2
						[[1.5,0,0,0],0,90],
						[[2.5,0,0.5,0],0,90]
						]
					]

flemishBondCourses = [
					[#course1
						[[0,0],0,0],
						[[0,1],0,0],
						[[3,0],0,90]
						],
					[#course2
						[[1.5,0],0,90],
						[[1.5,0],0,0],
						[[1.5,1],0,0]
						]
					]

bond1Courses = [
				[#course1
					[[0,0],0,0],
					[[0,1],0,0]
					],
				[#course2
					#[[1.5,0],0,90],
					[[2.5,0],0,90]
					]
			]

bond2Courses = [
				[#course1
					[[0,0],0,0]
					,[[0,1],0,0]
					],
				[#course2
					[[1.5,0],3,90]
					,[[2.5,0],0,90]
					,[[1.5,1.5],1,90]
					]
			]

bond3Courses = [
				[#course1
					[[0,0],0,0]
					,[[0,1],0,0]
					],
				[#course2
					[[1.5,0],3,90]
					,[[2.5,0],0,90]
					#,[[1.5,1.5],1,0]
					]
			]

englishBond = bond(englishBondCourses, 115, 80, [2,2])
flemishBond = bond(flemishBondCourses, 115 , 80, [3,2])

# this is an english bond with holes
WallBond1 = bond(bond1Courses, 115, 80, [2,2])

# this is an english bond with projections
WallBond2 = bond(bond2Courses, 115, 80, [2,2])

# this is an english bond with depressions
WallBond3 = bond(bond3Courses, 115, 80, [2,2])

brickDim = [230,115,80]
curve = rs.GetCurveObject('Select Curve')
height = rs.GetInteger('Enter Wall height', 2000)

rs.EnableRedraw(False)
newWall = Wall(curve[0], WallBond3, height)
newWall.bondOptions.append(WallBond1)
newWall.bondOptions.append(WallBond2)
newWall.bondOptions.append(WallBond3)
newWall.bondOptions.append(englishBond)
newWall.buildRandom()
#newWall.build()
print(newWall.brickCount)

rs.EnableRedraw(True)

#course =  []
#course[i] = []
#course[i][j] = [pos, type, orientation]
{'fraction_non_alphanumeric': 0.1370357433927116, 'fraction_numerical': 0.03452089882407731, 'mean_word_length': 3.163596703829375, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '41873', 'n_tokens_mistral': 8065, 'n_tokens_neox': 6486, 'n_words': 1486}