import importDAE, Part, ObjectsFem, Fem, FemGmshTools, FemGui, FemToolsCcx

PRESSURE_MODE = 1
MAX_DEPTH = 8.0
HEIGHT = 6.48
GROUND_DENSITY = 1500.0
GMSH_RES_METERS = 2.0
PRESSURE_LAYERS = 100

importDAE.open(u"/tmp/tmp.dae")
doc = FreeCAD.getDocument("tmp")
gui_doc = Gui.getDocument("tmp")
mesh = doc.getObject("Mesh")
ground = doc.getObject("Mesh001")
Gui.SendMsgToActiveView("ViewFit")

print "Object loaded.  Making shape..."
part = doc.addObject("Part::Feature", "Hús part")
shape_tmp = Part.Shape()
shape_tmp.makeShapeFromMesh(mesh.Mesh.Topology, 0.100000)
part.Shape = shape_tmp
part.purgeTouched()
delete shape_tmp

# solidify
print "Solidifying..."
solid_tmp = part.Shape
solid_tmp = Part.Solid(solid_tmp)
solid_part = doc.addObject("Part::Feature", "Hús solid part")
solid_part.Label = "Hús solid part"
solid_part.Shape = solid_tmp
delete solid_tmp

# refining
print "Refining..."
refined_part = doc.addObject("Part::Feature", "Hús refined part")
refined_part.Shape = solid_part.Shape.removeSplitter()

# analysis
analysis_object = ObjectsFem.makeAnalysis("Analysis")
FemGui.setActiveAnalysis(doc.Analysis)

# gmsh
print "Computing gmsh..."
gmsh = ObjectsFem.makeMeshGmsh('Hús FEM')
gmsh.Part = refined_part
gmsh.CharacteristicLengthMax = 1000.0 * GMSH_RES_METERS
gmsh.CharacteristicLengthMin = 0.0
gmsh_mesh = FemGmshTools.FemGmshTools(gmsh)
error = gmsh_mesh.create_mesh()
print error
doc.Analysis.Member += [gmsh]
doc.recompute()

# solver
print "Creating solver..."
solver_object = ObjectsFem.makeSolverCalculix("CalculiX")
solver_object.GeometricalNonlinearity = 'linear'
solver_object.ThermoMechSteadyState = True
solver_object.MatrixSolverType = 'default'
solver_object.IterationsControlParameterTimeUse = False
doc.Analysis.Member = doc.Analysis.Member + [solver_object]

# material
print "Adding material..."
material_object = ObjectsFem.makeMaterialSolid("SolidMaterial")
mat = material_object.Material
mat['Name'] = "Pumicecrete"
mat['YoungsModulus'] = "5000 MPa"
mat['PoissonRatio'] = "0.17"
mat['Density'] = "1000 kg/m^3"
material_object.Material = mat
doc.Analysis.Member = doc.Analysis.Member + [material_object]

# bounding
print "Bounding..."
MIN_X =  9999999999999.9
MAX_X = -9999999999999.9
MIN_Y =  9999999999999.9
MAX_Y = -9999999999999.9
MIN_Z =  9999999999999.9
MAX_Z = -9999999999999.9
for vertex in refined_part.Shape.Vertexes:
  if vertex.X > MAX_X:
    MAX_X = vertex.X
  if vertex.X < MIN_X:
    MIN_X = vertex.X
  if vertex.Y > MAX_Y:
    MAX_Y = vertex.Y
  if vertex.Y < MIN_Y:
    MIN_Y = vertex.Y
  if vertex.Z > MAX_Z:
    MAX_Z = vertex.Z
  if vertex.Z < MIN_Z:
    MIN_Z = vertex.Z

SCALAR_X = MAX_X - MIN_X
SCALAR_Y = MAX_Y - MIN_Y
SCALAR_Z = (MAX_Z - MIN_Z) / HEIGHT


# fixed_constraint
print "Adding anchors..."
fixed_constraint = ObjectsFem.makeConstraintFixed("Anchors")
keys = []
for face_id in range(len(refined_part.Shape.Faces)):
  face = refined_part.Shape.Faces[face_id]
  z = (face.CenterOfMass.z - MIN_Z) / SCALAR_Z
  if z < 2.7 and face.normalAt(0, 0)[2] < -0.9:
    keys.append( (refined_part, "Face%d" % (face_id + 1)) )

fixed_constraint.References = keys
doc.Analysis.Member = doc.Analysis.Member + [fixed_constraint]

def sign(p0, p1, p2):
  return (p0[0] - p2[0]) * (p1[1] - p2[1]) - (p1[0] - p2[0]) * (p0[1] - p2[1])

def point_in_triangle(p0, p1, p2, x, y):
  pt = (x, y)
  b1 = sign(pt, p0, p1) < 0.0
  b2 = sign(pt, p1, p2) < 0.0
  b3 = sign(pt, p2, p0) < 0.0
  return (b1 == b2) & (b2 == b3)

def distance(x0, y0, x1, y1):
  return ((x0-x1)**2 + (y0-y1)**2)**0.5

def ground_height(x, y):
  for facet in ground.Mesh.Facets:
    p = []
    for i in facet.Points:
      p.append( ((i[0] - MIN_X) / SCALAR_X, (i[1] - MIN_Y) / SCALAR_Y, (i[2] - MIN_Z) / SCALAR_Z) )
    if point_in_triangle(p[0], p[1], p[2], x, y):
      break
  d0 = distance(x, y, p[0][0], p[0][1])
  d1 = distance(x, y, p[1][0], p[1][1])
  d2 = distance(x, y, p[2][0], p[2][1])
  sum = (d0 + d1 + d2)
  z = (d0 * p[0][2] + d1 * p[1][2] + d2 * p[2][2]) / sum
#  print x, y, " : ", p[0][2], p[1][2], p[2][2], " : ", d0, d1, d2, " : ", z
  return z

print "Adding pressures..."

if PRESSURE_MODE:
  # pressure_constraint
  keys = []
  for i in range(PRESSURE_LAYERS):
    keys.append([])
  for face_id in range(len(refined_part.Shape.Faces)):
    face = refined_part.Shape.Faces[face_id]
    average_depth = 0.0
    for vertex in face.Vertexes:
      x = (vertex.X - MIN_X) / SCALAR_X
      y = (vertex.Y - MIN_Y) / SCALAR_Y
      z = (vertex.Z - MIN_Z) / SCALAR_Z
      ground_z = ground_height(x, y)
      depth = ground_z - z
#      print ground_z, z, depth
      if depth > 0:
        average_depth += depth
    average_depth /= len(face.Vertexes)
    if average_depth > 0.0:
#      print "Adding key: %d %f to %d" % (face_id + 1, average_depth, int(round(average_depth / MAX_DEPTH * len(keys))))
      keys[int(round(average_depth / MAX_DEPTH * len(keys)))].append( (refined_part, "Face%d" % (face_id + 1)) )

  for key_id in range(len(keys)):
    if len(keys[key_id]) == 0:
      continue
    depth = key_id * MAX_DEPTH / len(keys)
#    print "%d) Adding pressures for %f: " % (key_id, depth), keys[key_id]
    pressure = (9.81 * GROUND_DENSITY * depth) / 1000000.0
    pressure_constraint = ObjectsFem.makeConstraintPressure("Pressure %0.4f" % pressure)
    pressure_constraint.References = keys[key_id]
    pressure_constraint.Pressure = pressure
    pressure_constraint.Reversed = False
    doc.Analysis.Member = doc.Analysis.Member + [pressure_constraint]
    #break

else:
  # ATH: ekki tilbúið!
  for key_id in range(len(keys)):
    if len(keys[key_id]) == 0:
      continue
    depth = key_id * MAX_DEPTH / len(keys)
    force_per_m2 = (9.81 * GROUND_DENSITY * depth)
    m2_on_z = 0# ATH
    force = force_per_m2 / m2_on_z
    force_constraint = ObjectsFem.makeConstraintForce("Force %0.0f" % force)
    force_constraint.References = keys[key_id]
    force_constraint.Force = force
    force_constraint.Reversed = False
    force_constraint.Direction = (refined_part, [nafn]) # ATH
    doc.Analysis.Member = doc.Analysis.Member + [force_constraint]

print "Pressures added."

## View object
#femmesh_obj = doc.addObject('Fem::FemMeshObject', 'FEM view')
#femmesh_obj.FemMesh = gmsh
#doc.Analysis.Member = doc.Analysis.Member + [femmesh_obj]

# run the analysis
FemGui.setActiveAnalysis(doc.Analysis)
fea = FemToolsCcx.FemToolsCcx()
fea.update_objects()
message = fea.check_prerequisites()
if not message:
  fea.reset_all()
  fea.run()
  fea.load_results()
else:
  FreeCAD.Console.PrintError("Houston, we have a problem! {}\n".format(message))  # in report view
  print("Houston, we have a problem! {}\n".format(message))  # in python console

# run the analysis
for m in analysis_object.Member:
  if m.isDerivedFrom('Fem::FemResultObject'):
    result_object = m
    break

gui_doc.getObject("Mesh001").Visibility = False
gui_doc.getObject("Mesh").Visibility = False
gui_doc.getObject("Hús part").Visibility = False
gui_doc.getObject("Hús solid part").Visibility = False
gui_doc.getObject("Hús refined part").Visibility = False

gmsh.ViewObject.setNodeDisplacementByVectors(result_object.NodeNumbers, result_object.DisplacementVectors)
gmsh.ViewObject.applyDisplacement(10)



