#
# Name :          GetCentroid 1.0
# Description :   Draws a construction point at the centroid of a shape that is
#                 drawn on the X-Y plane (the ground). Also calculates
#                 area and Moments of Inertia of face (Ix, Iy, Ixy)
# Usage :         1. Draw shapes on the X-Y (ground) plane and select at least
#                    one face, make sure top of te face is up!
#                 2. Select "Get Face Centroid" from the Tools menu
#                 3. A construction point will be placed at the centroid and
#                    the area properties will be displayed
#                 PLEASE NOTE: Since SketchUp approximates curved shapes with
#                 polygons, this calculation will only be as good as the
#                 approximation. To increase accuracy, increase the number
#                 of polygons.
#
# Author :        Alexander Schreyer, www.alexschreyer.net
# E-Mail:         mail@alexschreyer.net
# Date :          October 11, 2008
# Type :          Tool
# Disclaimer:     THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
#                 IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
#                 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#                 PURPOSE.
#
# History:        1.0 (10/11/2008) - first version
# To-Do List:     - Implement translate so that this works on arbitrarily
#                   oriented surfaces
#                 - Get angle of strong axis and draw it. Also get Section
#                   Modulus and radius of gyration
#


require 'sketchup.rb'


# =========================================


def face_selected
# Check if face is in selection set - for context menu
  lookup = false
  selection_set = Sketchup.active_model.selection
  selection_set.each {|entity|
    if entity.typename == "Face"
      lookup = true
    end
  }
  return lookup
end


# =========================================


def calculate_centroid (a_face)
# Do the math and display the results - returns the centroid

  # Get all the vertices for the current face
  vertices = a_face.vertices
  
  if (vertices.length < 3)
  
    UI.messagebox "Awfully sorry. Can't calculate centroid.\nPlease select at least one valid face."
    return false
  
  else
  
    # First calculate centroid:

    # Loop the first vertex around and create a point for the centroid
    vertices[vertices.length] = vertices[0]
    centroid = Geom::Point3d.new
    a_sum = 0.0
    x_sum = 0.0
    y_sum = 0.0

    for i in (0...vertices.length-1)
      temp = vertices[i].position.x * vertices[i+1].position.y - vertices[i+1].position.x * vertices[i].position.y
      a_sum += temp
      x_sum += (vertices[i+1].position.x + vertices[i].position.x) * temp
      y_sum += (vertices[i+1].position.y + vertices[i].position.y) * temp
    end

    area = a_sum / 2
    centroid.x = x_sum / (3 * a_sum)
    centroid.y = y_sum / (3 * a_sum)
    centroid.z = 0
    
    # Now calculate moments:
    
    adjusted_points_x = []
    adjusted_points_y = []
    i_x = 0.0
    i_y = 0.0
    i_xy = 0.0
    
    # Get all the vertices for the current face and wrap the first one again
    for i in (0...vertices.length)
      adjusted_points_x[i] = vertices[i].position.x - centroid.x
      adjusted_points_y[i] = vertices[i].position.y - centroid.y
    end

    for i in (0...adjusted_points_x.length-1)
      j = i+1
      temp = 0.5 * (adjusted_points_x[i] * adjusted_points_y[j] - adjusted_points_x[j] * adjusted_points_y[i]);
      i_x += (adjusted_points_y[i] * adjusted_points_y[i] + adjusted_points_y[i] * adjusted_points_y[j] + adjusted_points_y[j] * adjusted_points_y[j]) / 6 * temp;
      i_y += (adjusted_points_x[i] * adjusted_points_x[i] + adjusted_points_x[i] * adjusted_points_x[j] + adjusted_points_x[j] * adjusted_points_x[j]) / 6 * temp;
      i_xy += (2 * adjusted_points_x[i] * adjusted_points_y[i] + adjusted_points_x[i] * adjusted_points_y[j] + adjusted_points_x[j] * adjusted_points_y[i] + 2 * adjusted_points_x[j] * adjusted_points_y[j]) / 12 * temp;
    end
    
    # Show results - better in inputbox, can copy values
    # if larger than 5 ft^2 go with larger units
    if (area > 3600)
      prompts = ["Area (ft^2)", "Ix (ft^4)", "Iy (ft^4)", "Ixy (ft^4)"]
      values = [sprintf("%.4f",area/144), sprintf("%.4f",i_x/20736), sprintf("%.4f",i_y/20736), sprintf("%.4f",i_xy/20736)]
    else
      prompts = ["Area (in^2)", "Ix (in^4)", "Iy (in^4)", "Ixy (in^4)"]
      values = [sprintf("%.4f",area), sprintf("%.4f",i_x), sprintf("%.4f",i_y), sprintf("%.4f",i_xy)]
    end
    inputbox prompts, values, "Face Area Properties"

    # Send the centroid back as a point
    return centroid

  end
  
end # calculate_centroid


# =========================================


def get_centroid

  # Get the active model
  model = Sketchup.active_model

  # Get currently selected objects
  selection_set = model.selection
  vector = Geom::Vector3d.new
  
  if selection_set.empty?
    UI.messagebox 'Please select at least one face.'
  else
    # Do this for each face in the selection set seperately
    selection_set.each {|entity|
      if entity.typename == "Face"
        if entity.normal.samedirection? [0,0,-1]
          UI.messagebox "I need to flip this face (white side up) for a correct calculation."
          entity.reverse!
        end
        # Calculate centroid
        centroid = calculate_centroid (entity)
        # Draw a construction point and axis lines at centroid
        if (centroid != false)
          model.entities.add_cpoint(centroid)
          model.entities.add_cline centroid.offset(X_AXIS, -2.feet), centroid.offset(X_AXIS, 2.feet)
          model.entities.add_cline centroid.offset(Y_AXIS, -2.feet), centroid.offset(Y_AXIS, 2.feet)
        end
      end
    }
  end

end # get_centroid


# =========================================


# Load plugin at startup and add menu items

filename = "_GetCentroid.rb"

if !file_loaded?(filename)

  # Add to the SketchUp tools menu
  tools_menu = UI.menu("Tools")
  tools_menu.add_item($exStrings.GetString("Get Face Centroid")) { get_centroid }
  
  # Add to the context menu
  UI.add_context_menu_handler do |menu|
    if( face_selected )
      menu.add_separator
      menu.add_item($exStrings.GetString("Get Face Centroid")) { get_centroid }
    end
  end
  
  # Let Ruby know we have loaded this file
  file_loaded(filename)

end

# =========================================

