# Make bottle script. This is a port of the C++ sample MakeBottle provided with the OpenCASCADE library Qt samples

from OCC.gp import *
from OCC.TopoDS import *
from OCC.GC import *
from OCC.BRepBuilderAPI import *
from OCC.BRepPrimAPI import *
from OCC.BRepFilletAPI import *
from OCC.BRepAlgoAPI import *
from OCC.Utils.Topology import *
from OCC.Geom import *
from OCC.Geom2d import *
from OCC.GCE2d import *
from OCC.BRep import *
from OCC.BRepLib import *
from OCC.BRepOffsetAPI import *
from OCC.TopTools import *

import math

def face_is_plane(face):
    ''' Returns True if the TopoDS_Shape is a plane, False otherwise
    '''
    hs = BRep_Tool_surface(face)
    downcast_result = Handle_Geom_Plane_downcast(hs)
    # the handle is null if downcast failed or is not possible,
    # that is to say the face is not a plane
    if downcast_result.IsNull():
        return False
    else:
        return True
    
myWidth = 50
myHeight = 70
myThickness = 30
 
# Profile : Define Support Points
aPnt1 = gp_Pnt(-myWidth / 2. , 0 , 0)
aPnt2 = gp_Pnt(-myWidth / 2. , -myThickness / 4. , 0)
aPnt3 = gp_Pnt(0 , -myThickness / 2. , 0)
aPnt4 = gp_Pnt(myWidth / 2. , -myThickness / 4. , 0)
aPnt5 = gp_Pnt(myWidth / 2. , 0 , 0)
 
# Profile : Define the Geometry
aArcOfCircle = GC_MakeArcOfCircle(aPnt2,aPnt3 ,aPnt4)
aSegment1 = GC_MakeSegment(aPnt1 , aPnt2)
aSegment2 = GC_MakeSegment(aPnt4 , aPnt5)
 
# Profile : Define the Topology
aEdge1 = BRepBuilderAPI_MakeEdge(aSegment1.Value())
aEdge2 = BRepBuilderAPI_MakeEdge(aArcOfCircle.Value())
aEdge3 = BRepBuilderAPI_MakeEdge(aSegment2.Value())
aWire  = BRepBuilderAPI_MakeWire(aEdge1.Edge() , aEdge2.Edge() ,aEdge3.Edge())

# Complete Profile
xAxis = gp_ox()
aTrsf = gp_Trsf()
aTrsf.SetMirror(xAxis)
aBRepTrsf = BRepBuilderAPI_Transform(aWire.Shape() , aTrsf)
aMirroredShape = aBRepTrsf.Shape()
aMirroredWire = TopoDS_wire(aMirroredShape)
mkWire = BRepBuilderAPI_MakeWire()
mkWire.Add(aWire.Wire())
mkWire.Add(aMirroredWire)
myWireProfile = mkWire.Wire()

# Body : Prism the Profile
myFaceProfile = BRepBuilderAPI_MakeFace(myWireProfile)
aPrismVec = gp_Vec(0 , 0 , myHeight)
myBody = BRepPrimAPI_MakePrism(myFaceProfile.Shape() , aPrismVec)

# Body : Apply Fillets
mkFillet = BRepFilletAPI_MakeFillet(myBody.Shape())
topology_traverser = Topo(myBody.Shape())
for aEdge in topology_traverser.edges():
    mkFillet.Add(myThickness / 12. , aEdge)
myBody = mkFillet.Shape()

# Remove face
myBody_traverser = Topo(myBody)
zMax=-1
for aFace in myBody_traverser.faces():
    if face_is_plane(aFace):
        aPlane = Handle_Geom_Plane_downcast(BRep_Tool_surface(aFace)).GetObject()
        aPnt = aPlane.Location()
        aZ = aPnt.X()
        if aZ>zMax:
            zMax = aZ
            faceToRemove = aFace
facesToRemove = TopTools_ListOfShape()
facesToRemove.Append(faceToRemove)
myBody = BRepOffsetAPI_MakeThickSolid(myBody,facesToRemove,-myThickness/50,0.001)

neckLocation = gp_Pnt(0 , 0 , myHeight)
neckNormal = gp_dz()
neckAx2 = gp_Ax2(neckLocation , neckNormal)
myNeckRadius = myThickness / 4.
myNeckHeight = myHeight / 10.

MKCylinder = BRepPrimAPI_MakeCylinder(neckAx2 , myNeckRadius ,myNeckHeight)

myNeck = MKCylinder.Shape()
myBody = BRepAlgoAPI_Fuse(myBody , myNeck)
 
# Threading : Create Surfaces
neckAx2_bis = gp_Ax3(neckLocation , neckNormal)
aCyl1 = Geom_CylindricalSurface(neckAx2_bis , myNeckRadius * 0.99)
aCyl2 = Geom_CylindricalSurface(neckAx2_bis , myNeckRadius * 1.05)

# Threading : Define 2D Curves
aPnt = gp_Pnt2d(2. * math.pi , myNeckHeight / 2.)
aDir = gp_Dir2d(2. * math.pi , myNeckHeight / 4.)
aAx2d = gp_Ax2d(aPnt , aDir)
aMajor = 2. * math.pi
aMinor = myNeckHeight / 10.
anEllipse1 = Geom2d_Ellipse(aAx2d , aMajor , aMinor)
anEllipse2 = Geom2d_Ellipse(aAx2d , aMajor , aMinor / 4)
aArc1 = Geom2d_TrimmedCurve(Handle_Geom2d_Ellipse(anEllipse1) , 0 ,math.pi)
aArc2 = Geom2d_TrimmedCurve(Handle_Geom2d_Ellipse(anEllipse2) , 0 ,math.pi)
aArc1_handle = Handle_Geom2d_Curve(aArc1)
aArc2_handle = Handle_Geom2d_Curve(aArc2)
anEllipsePnt1 = anEllipse1.Value(0)
anEllipsePnt2 = anEllipse1.Value(math.pi)
aSegment = GCE2d_MakeSegment(anEllipsePnt1 , anEllipsePnt2)

 

# Threading : Build Edges and Wires
aEdge1OnSurf1 = BRepBuilderAPI_MakeEdge( aArc1_handle ,Handle_Geom_Surface(aCyl1))
aEdge2OnSurf1 = BRepBuilderAPI_MakeEdge( aSegment.Value() ,Handle_Geom_Surface(aCyl1))
aEdge1OnSurf2 = BRepBuilderAPI_MakeEdge( aArc2_handle ,Handle_Geom_Surface(aCyl2))
aEdge2OnSurf2 = BRepBuilderAPI_MakeEdge( aSegment.Value() ,Handle_Geom_Surface(aCyl2))

threadingWire1 = BRepBuilderAPI_MakeWire(aEdge1OnSurf1.Edge() ,aEdge2OnSurf1.Edge())
threadingWire2 = BRepBuilderAPI_MakeWire(aEdge1OnSurf2.Edge() ,aEdge2OnSurf2.Edge())
BRepLib().BuildCurves3d(threadingWire1.Shape())
BRepLib().BuildCurves3d(threadingWire2.Shape())

# Create Threading
aTool = BRepOffsetAPI_ThruSections(True)
aTool.AddWire(threadingWire1.Wire())
aTool.AddWire(threadingWire2.Wire())
aTool.CheckCompatibility(False)
 
myThreading = aTool.Shape()

#Building the resulting compund
theBottle = TopoDS_Compound()
aBuilder = BRep_Builder()
aBuilder.MakeCompound(theBottle)
aBuilder.Add(theBottle, myBody.Shape())
aBuilder.Add(theBottle, myThreading)

# Display bottle
from OCC.Display.SimpleGui import init_display
display, start_display, add_menu, add_function_to_menu = init_display()
display.DisplayShape(theBottle)
start_display()
 
