# -*- coding: utf-8 -*-
# Author: Ruslan Krenzler.
# Date: 2 December 2017
# Create a pipe elbow.

import math

import FreeCAD
import Spreadsheet
import Sketcher
import Part

# Here are dimensions used to create a elbow.
alpha = "60 deg"
r1 = "1 in"
r2 = "2 in"
r3 = "3 in"
len1 = "4 in"
len2 = "5 in"

tu = FreeCAD.Units.parseQuantity

# Add sketches for swept part (circle) into X-Y Plane.
document = App.activeDocument()
class Elbow:
	def __init__ (self, document):
		self.document = document
		# Set default values.
		self.alpha = "60 deg"
		self.r1 = "1 in"
		self.r2 = "2 in"
		self.r3 = "3 in"
		self.len1 = "4 in"
		self.len2 = "5 in"
	@staticmethod
	def createBentCylinder(document, group, r_cyl, r_bent, alpha):
		"""Create a cylinder with a radius r_cyl1 with a base on X-Y plane
		and bent it by angle alpha along radius r_bent

		Add all new created objects to a group.
		This should simplify clean up later.
		"""
		base_sketch = document.addObject('Sketcher::SketchObject','BaseSketch')
		base_sketch.Placement = App.Placement(App.Vector(0.000000,0.000000,0.000000),App.Rotation(0.000000,0.000000,0.000000,1.000000))
		# When adding a radius, do not forget to convert the units.
		base_sketch.addGeometry(Part.Circle(App.Vector(0.000000,0.000000,0),App.Vector(0,0,1),tu(r_cyl)),False)
		# Add sweeping part into X-Z plane.
		path_sketch = document.addObject('Sketcher::SketchObject','PathSketch')
		path_sketch.Placement = App.Placement(App.Vector(0.000000,0.000000,0.000000),App.Rotation(-0.707107,0.000000,0.000000,-0.707107))
		# The arc is created counterwise.
		start = tu("pi rad").getValueAs("rad") - tu(alpha).getValueAs("rad")
		stop = tu("pi rad").getValueAs("rad")
		path_sketch.addGeometry(Part.ArcOfCircle(Part.Circle(App.Vector(tu(r_bent),0,0),App.Vector(0,0,1),tu(r_bent)),start, stop),False)
		# Sweep the parts
		sweep = document.addObject('Part::Sweep','InnerSweep')
		sweep.Sections=[base_sketch, ]
		sweep.Spine=(path_sketch,["Edge1"])
		sweep.Solid=True
		sweep.Frenet=False
		# add all the objects to the group.
		group.addObject(base_sketch)
		group.addObject(path_sketch,)
		group.addObject(sweep)
		return sweep
	@staticmethod
	def createBentPart(document, group, r1, r3, alpha):
		outer_sweep = Elbow.createBentCylinder(document, group, r3, r3, alpha)
		inner_sweep = Elbow.createBentCylinder(document, group, r1, r3, alpha)
		bent_cut = document.addObject("Part::Cut","BentCut")
		bent_cut.Base = outer_sweep
		bent_cut.Tool = inner_sweep
		group.addObject(bent_cut)
		return bent_cut
	# What is the name of the part where the pipe comes in.
	# I call now it pipe too, but it should be changed later.
	@staticmethod
	def createPipePart(document, outer_r, inner_r, length):
		outer_cylinder = document.addObject("Part::Cylinder","OuterCylinder")
		outer_cylinder.Radius = outer_r
		outer_cylinder.Height = length
		inner_cylinder = document.addObject("Part::Cylinder","InnerCylinder")
		inner_cylinder.Radius = inner_r
		inner_cylinder.Height = length
		cut = document.addObject("Part::Cut","PipeCut") # This is no pipe. Change to proper name later.
		cut.Base = outer_cylinder
		cut.Tool = inner_cylinder
		return cut

	@staticmethod
	def toSolid(document, part, name):
		"""Convert object to a solid.
			Basically those are commands, which FreeCAD runs when you convert a part to a solid.

			Important!: recompute document. Without this step. The faces may not yet exists.
			(Is my interpretation correct?)
			document.recompute()
		"""
		s = part.Shape.Faces
		s = Part.Solid(Part.Shell(s))
		o = document.addObject("Part::Feature", name)
		o.Label=name
		o.Shape=s
		return o
	@staticmethod
	def NestedObjects(group):
		res = []
		if group.OutList == []:
			res.append(group)
		else:
			# append children first
			for o in group.OutList:
				res += NestedObjects(o)
			res.append(group)
		return res

	def create(self, convertToSolid = True):
		"""Create elbow."""
		# Create new group to put all the temporal data.
		group = self.document.addObject("App::DocumentObjectGroup", "elbow group")
		# Create the bent part.
		bent_part = self.createBentPart(self.document, group, self.r1, self.r3, self.alpha)
		pipe1 = self.createPipePart(self.document, self.r3, self.r2, self.len1)
		# Move pipe1 to the bottom such that its upper part lies in X-Y plane.
		pipe1.Placement.Base = (0,0,-tu(self.len1))
		# Rotate and move the second pipe, such that it touches with its base the bent part.
		pipe2 = self.createPipePart(self.document, self.r3, self.r2, self.len2)
		x = (1-math.cos(tu(self.alpha).getValueAs("rad"))) *tu(self.r3)
		z = math.sin(tu(self.alpha).getValueAs("rad"))*tu(self.r3)
		pipe2.Placement = App.Placement(App.Vector(x,0,z),App.Rotation(App.Vector(0,1,0),tu(self.alpha)))

		# Add pipes to a group.
		group.addObject(pipe1)
		group.addObject(pipe2)

		if convertToSolid:
			# Remove objects of the pipes separately.

			# Create all faces.
			document.recompute()
			# Now convert all parts to solid, and remove intermediate data.
			pipe1_solid = self.toSolid(document, pipe1, "pipe1 (solid)")
			bent_solid = self.toSolid(document, bent_part, "bent part (solid)")
			pipe2_solid = self.toSolid(document, pipe2, "pipe2 (solid)")
			parts = NestedObjects(group)
			# Document.removeObjects can remove many objects, when use
			# parts directly. Therefore use the name list instead.
			names_to_remove = []
			for part in parts:
				if part.Name not in names_to_remove:
					names_to_remove.append(part.Name)
			for name in names_to_remove:
				print("Deleting temporary objects %s."%name)
				document.removeObject(name)

	
ell = Elbow(document)
ell.create()
document.recompute()


