Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # Draper: Drapes objects onto terrain, setting them down on two feet and rotating about an axis.
- # To use: Define the things in user_settings. Note that negative_clearance is not yet functioning.
- # Patrick
- # 23-10-11
- # v4, works on red axis, not tested on green
- $debug_print = false
- puts "\n\n\nDEBUG TEXT WILL BE PRINTED" if $debug_print
- def user_settings
- # Define the axis of rotation as either :red or :green
- $axis = :red # You can change this to :green if you prefer
- # Define the allowed negative clearance in inches (SketchUp uses inches internally)
- # DOES NOT CURRENTLY WORK
- $allowed_neg_clearance = 18.0 # INCHES
- # Define the distance from the edge to the "feet" in inches
- $feet_from_edge = 189 # INCHES from outside of component to middle of first pile
- return $axis, $allowed_neg_clearance, $feet_from_edge
- end
- def identify_feet(entity, axis)
- bounds = entity.bounds
- if $axis == :red
- x_mid = (bounds.max.x + bounds.min.x) / 2.0
- y_foot_a = bounds.min.y + $feet_from_edge
- y_foot_b = bounds.max.y - $feet_from_edge
- foot_a = [x_mid, y_foot_a, bounds.min.z]
- foot_b = [x_mid, y_foot_b, bounds.min.z]
- else # Axis is :green
- y_mid = (bounds.max.y + bounds.min.y) / 2.0
- x_foot_a = bounds.min.x + $feet_from_edge
- x_foot_b = bounds.max.x - $feet_from_edge
- foot_a = [x_foot_a, y_mid, bounds.min.z]
- foot_b = [x_foot_b, y_mid, bounds.min.z]
- end
- return foot_a, foot_b
- end
- def query_terrain(foot_a, foot_b, entity)
- model = Sketchup.active_model
- # Convert local coordinates to global if entity has a parent
- #global_foot_a = foot_a.transform(entity.transformation)
- #global_foot_b = foot_b.transform(entity.transformation)
- global_foot_a = Geom::Point3d.new(foot_a)
- global_foot_b = Geom::Point3d.new(foot_b)
- puts "> QT() > global_foot_a: #{global_foot_a}" if $debug_print
- puts "> QT() > global_foot_b: #{global_foot_b}" if $debug_print
- # Initialize ray for foot_a and foot_b
- ray_a = [global_foot_a, [0, 0, -1]]
- ray_b = [global_foot_b, [0, 0, -1]]
- puts "> QT() > ray_a: #{ray_a}" if $debug_print
- puts "> QT() > ray_b: #{ray_b}" if $debug_print
- # Perform raycasting for foot_a and foot_b
- terrain_intersections_a = model.raytest(ray_a, false)
- terrain_intersections_b = model.raytest(ray_b, false)
- puts "> QT() > terrain_intersections_a: #{terrain_intersections_a}" if $debug_print
- puts "> QT() > terrain_intersections_b: #{terrain_intersections_b}" if $debug_print
- if terrain_intersections_a.nil? || terrain_intersections_b.nil?
- puts "No terrain intersection found for one or both feet."
- return nil, nil
- end
- z_a = terrain_intersections_a[0].z
- z_b = terrain_intersections_b[0].z
- puts "> QT() > How high is the terrain is at foot_a's intersection is: #{z_a}" if $debug_print
- puts "> QT() > How high is the terrain is at foot_b's intersection is: #{z_b}" if $debug_print
- # Calculate the distance between the foot and the terrain
- z_a = (foot_a.z - z_a).abs
- z_b = (foot_b.z - z_b).abs
- puts "> QT() > Foot_a's height above terrain is z_a: #{z_a}" if $debug_print
- puts "> QT() > Foot_b's height above terrain is z_b: #{z_b}" if $debug_print
- return z_a, z_b
- end
- def calculate_rotation(z_a, z_b, foot_a, foot_b)
- # Create Geom::Point3d objects for the feet
- point_a = Geom::Point3d.new(foot_a)
- point_b = Geom::Point3d.new(foot_b)
- # Calculate the distance between foot_a and foot_b in the plane parallel to the terrain
- distance = point_a.distance(point_b)
- puts "> Calculate_rotation() > Distance between feet - #{distance}" if $debug_print
- # Calculate the height difference between the two feet above the terrain
- #delta_z = (z_a - z_b).abs
- delta_z = (z_a - z_b)
- puts "> Calculate_rotation() > Height difference above terrain - #{delta_z}" if $debug_print
- # Calculate the angle needed for rotation in radians
- # angle = arctan(delta_z / distance)
- rotation_angle = Math.atan2(delta_z, distance)
- # Convert the angle to degrees for better readability
- rotation_angle_deg = rotation_angle * (180.0 / Math::PI)
- puts "> Calculate_rotation() > Rotation angle in radians - #{rotation_angle}" if $debug_print
- puts "> Calculate_rotation() > Rotation angle in degrees - #{rotation_angle_deg}" if $debug_print
- return rotation_angle
- end
- def do_transformation(entity, z_a, z_b, rotation_angle, axis)
- # Debug Output
- puts "> DO_T() > Starting do_transformation function" if $debug_print
- # Get the bounding box of the entity
- bounds = entity.bounds
- # Calculate the bottom center for rotation
- bottom_center = [(bounds.min.x + bounds.max.x) / 2.0, (bounds.min.y + bounds.max.y) / 2.0, bounds.min.z]
- puts "> DO_T() > Initial Bottom Center: #{bottom_center}" if $debug_print
- # If the entity is part of a group or component, get the transformation to global coordinates
- if entity.parent.is_a?(Sketchup::Group) || entity.parent.is_a?(Sketchup::ComponentInstance)
- transformation_to_global = entity.parent.transformation
- bottom_center = transformation_to_global * bottom_center
- end
- puts "> DO_T() > Transformed Bottom Center (if applicable): #{bottom_center}" if $debug_print
- # calculate the middle height between the two delta z'see
- #z_mid = ((z_a - z_b).abs) / 2.0
- z_mid = (z_a + z_b) / 2.0
- puts "> DO_T() > Z-values for Foot A and Foot B: #{z_a}, #{z_b}" if $debug_print
- puts "> DO_T() > Calculated dZ Midpoint: #{z_mid}" if $debug_print
- # Calculate the midpoint between the two feet along the Z-axis
- #z_mid = (z_a + z_b) / 2.0
- #puts "> DO_T() > Z-values for Foot A and Foot B: #{z_a}, #{z_b}"
- #puts "> DO_T() > Calculated Z Midpoint: #{z_mid}"
- # Define the axis of rotation based on user input
- rotation_axis = axis == :red ? X_AXIS : Y_AXIS
- puts "> DO_T() > Axis of Rotation: #{rotation_axis}" if $debug_print
- puts "> DO_T() > Angle of Rotation: #{rotation_angle}" if $debug_print
- puts "> DO_T() > rotation_transform = Geom::Transformation.rotation(#{bottom_center}, #{rotation_axis}, #{rotation_angle})" if $debug_print
- # Define the rotation transformation based on the calculated rotation_angle and bottom_center
- rotation_transform = Geom::Transformation.rotation(bottom_center, rotation_axis, rotation_angle)
- puts "> DO_T() > Rotation Transformation: #{rotation_transform.to_a}" if $debug_print
- # Define the Z-axis translation based on z_mid
- #z_translation = Geom::Transformation.translation([0, 0, z_mid - bottom_center.z])
- z_translation = Geom::Transformation.translation([0, 0, -z_mid])
- puts "> DO_T() > Z-Axis Translation: #{z_translation.to_a}" if $debug_print
- puts "> DO_T() > Z-Axis Translation: #{z_translation}" if $debug_print
- #z_move = Geom::Transformation.translation([0, 0, -240])
- #entity.transform!(z_move)
- entity_transformation = entity.transformation
- puts "> DO_T() > Debug: Entity's Initial Global Transformation: #{entity_transformation.to_a}" if $debug_print
- # Apply the transformations
- puts "Entity's Initial Position: #{entity.bounds.center}" if $debug_print
- entity.transform!(rotation_transform)
- puts "> DO_T() > Debug: Entity's Transformation After Rotation: #{entity.transformation.to_a}" if $debug_print
- puts "Entity's Position After Rotation: #{entity.bounds.center}" if $debug_print
- entity.transform!(z_translation)
- puts "> DO_T() > Debug: Entity's Final Transformation: #{entity.transformation.to_a}" if $debug_print
- puts "Entity's Final Position: #{entity.bounds.center}" if $debug_print
- Sketchup.active_model.active_view.refresh
- #entity.transform!(rotation_transform * z_translation)
- #z_move = Geom::Transformation.translation([0, 0, -240])
- #entity.transform!(z_move)
- puts "> DO_T() > Transformations applied" if $debug_print
- end
- def check_clearance(entity, foot_a, foot_b)
- # Transform to global coordinates if part of a group or component instance
- if entity.parent.is_a?(Sketchup::Group) || entity.parent.is_a?(Sketchup::ComponentInstance)
- transformation_to_global = entity.parent.transformation
- foot_a = transformation_to_global * foot_a
- foot_b = transformation_to_global * foot_b
- end
- # Calculate 10 equally spaced points between foot_a and foot_b
- check_points = []
- for i in 0..9
- x = foot_a.x + i * (foot_b.x - foot_a.x) / 9.0
- y = foot_a.y + i * (foot_b.y - foot_a.y) / 9.0
- z = foot_a.z + i * (foot_b.z - foot_a.z) / 9.0
- check_points.push(Geom::Point3d.new(x, y, z))
- end
- # Initialize max_intrusion to 0
- max_intrusion = 0.0
- # For each check point, raytest upward to find the distance to the terrain
- model = Sketchup.active_model
- check_points.each do |point|
- hit_point = model.raytest([point, [0, 0, 1]])
- if hit_point
- intrusion = hit_point[0].z - point.z
- max_intrusion = [max_intrusion, intrusion].max
- end
- end
- # Compare max_intrusion to allowed negative clearance
- if max_intrusion > $allowed_negative_clearance
- return max_intrusion - $allowed_negative_clearance
- else
- return 0.0
- end
- end
- def apply_z_transformation(entity, clearance_flag)
- # Check if Z-axis adjustment is needed
- if clearance_flag > 0.0
- # Define the Z-axis translation transformation based on clearance_flag
- z_translation = Geom::Transformation.translation([0, 0, clearance_flag])
- # If part of a group or component instance, transform to global coordinates
- if entity.parent.is_a?(Sketchup::Group) || entity.parent.is_a?(Sketchup::ComponentInstance)
- z_translation = entity.parent.transformation * z_translation
- end
- # Apply the Z-axis translation transformation
- entity.transform!(z_translation)
- end
- end
- # Add blank lines for better console readability
- puts "\n\n\n"
- # Initialize the counter for the number of transformed components
- $num_transformed = 0
- $total_components = 0
- # Start the timer
- $start_time = Time.now
- # Function to count total number of components in the selection
- def count_components(entities)
- entities.each do |entity|
- case entity
- when Sketchup::ComponentInstance
- $total_components += 1
- when Sketchup::Group
- # Recursively count components within the group
- count_components(entity.entities)
- end
- end
- end
- # Function to actually apply the transformation to an entity
- def apply_transformation(entity)
- # # Calculate the center of the bounding box of the entity
- # center = entity.bounds.center
- # # Define the rotation transformation (30 degrees about the red axis, around the center of the bounding box)
- # transformation = Geom::Transformation.rotation(center, X_AXIS, 30.degrees)
- # entity.transform!(transformation)
- axis_of_rotation, allowed_neg_clearance, feet_from_edge = user_settings()
- puts "> Starting transformation for entity..." if $debug_print
- # Step 1: Identify the axis of rotation based on user settings
- #axis_of_rotation = $user_settings[:axis_of_rotation]
- puts "> Axis of rotation: #{axis_of_rotation}" if $debug_print
- # Step 2: Identify the feet of the component
- foot_a, foot_b = identify_feet(entity, axis_of_rotation)
- puts "> Identified feet: Foot A = #{foot_a}, Foot B = #{foot_b}" if $debug_print
- # Step 3: Query the terrain to get the Z-axis values for the feet
- z_a_initial, z_b_initial = query_terrain(foot_a, foot_b, entity)
- puts "> Height above terrain: Foot A dZ = #{z_a_initial}, Foot B dZ = #{z_b_initial}" if $debug_print
- # Step 4: Calculate the Rotation Angle
- rotation_angle = calculate_rotation(z_a_initial, z_b_initial, foot_a, foot_b)
- puts "> Calculated rotation angle: #{rotation_angle}" if $debug_print
- # Step 5: Apply the Rotation Transformation
- #do_transformation(entity, rotation_angle, axis_of_rotation)
- do_transformation(entity, z_a_initial, z_b_initial, rotation_angle, axis_of_rotation)
- puts "> Applied rotation transformation." if $debug_print
- # Step 6: Move Component Down to Ground
- #move_to_ground(entity, z_a_initial, z_b_initial)
- # puts "> Moved component down to ground."
- # UNCOMMENT AND FIX, but for now just test the above
- # # Step 7: Check for Clearance Issues
- # clearance_flag = check_clearance(entity, foot_a, foot_b)
- # puts "> Checked for clearance issues. Clearance flag: #{clearance_flag}"
- # # Step 8: Apply Z-Axis Transformation If Needed
- # apply_z_transformation(entity, clearance_flag)
- # puts "> Applied Z-axis transformation if needed."
- puts "> Transformation for entity completed." if $debug_print
- end
- # Function to perform the transformation operation and provide user feedback
- def perform_transformation_operation(entity)
- apply_transformation(entity)
- $num_transformed += 1
- # Calculate and display elapsed time and remaining time estimates
- elapsed_time = Time.now - $start_time
- avg_time_per_component = elapsed_time / $num_transformed
- remaining_components = $total_components - $num_transformed
- estimated_time_remaining = avg_time_per_component * remaining_components
- puts "Transforming component #{$num_transformed} of #{$total_components}. Elapsed Time: #{elapsed_time.round(2)}s. Estimated Time Remaining: #{estimated_time_remaining.round(2)}s."
- end
- # Function to iterate through entities and perform transformation
- def iterate_through_transforming_components(entities)
- entities.each do |entity|
- case entity
- when Sketchup::ComponentInstance
- perform_transformation_operation(entity)
- when Sketchup::Group
- # Recursively apply rotation to nested entities within the group
- iterate_through_transforming_components(entity.entities)
- end
- end
- end
- # This works in conjunction with the new apply_transformation to pass it a transformation, but we'll see.
- # def iterate_through_transforming_components(entities, transformation = nil)
- # entities.each do |entity|
- # # Clone the transformation
- # global_transformation = transformation ? transformation.clone : nil
- # # Apply the parent group's transformation to the entity
- # global_transformation *= entity.transformation if global_transformation
- # case entity
- # when Sketchup::ComponentInstance
- # apply_transformation(entity, global_transformation)
- # when Sketchup::Group
- # # Clone the transformation for the recursive call
- # iterate_through_transforming_components(entity.entities, global_transformation.clone)
- # end
- # end
- # end
- # Get the current selection
- selection = Sketchup.active_model.selection
- # Count total number of components in the selection
- count_components(selection)
- puts "Total number of components to be transformed: #{$total_components}"
- # Start an operation, allowing for undo later
- Sketchup.active_model.start_operation('Transform Components', true)
- # Perform the transformation
- iterate_through_transforming_components(selection)
- # Commit the operation to make it live
- Sketchup.active_model.commit_operation
- # Output the number of transformed components and total elapsed time
- puts "Number of transformed components: #{$num_transformed}"
- total_elapsed_time = Time.now - $start_time
- puts "Total elapsed time: #{total_elapsed_time.round(2)}s"
- # Show a popup to decide whether to keep or undo the transformations
- result = UI.messagebox('Keep the changes?', MB_YESNO)
- if result == IDNO
- # If the user chooses "No", then initiate SketchUp's native undo action.
- Sketchup.send_action("editUndo:")
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement