Advertisement
Guest User

Untitled

a guest
Oct 17th, 2023
192
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.43 KB | None | 0 0
  1.  
  2. # Draper: Drapes objects onto terrain, setting them down on two feet and rotating about an axis.
  3. # To use: Define the things in user_settings. Note that negative_clearance is not yet functioning.
  4. # Patrick
  5. # 23-10-11
  6. # v4, works on red axis, not tested on green
  7.  
  8. $debug_print = false
  9. puts "\n\n\nDEBUG TEXT WILL BE PRINTED" if $debug_print
  10.  
  11. def user_settings
  12. # Define the axis of rotation as either :red or :green
  13. $axis = :red # You can change this to :green if you prefer
  14.  
  15. # Define the allowed negative clearance in inches (SketchUp uses inches internally)
  16. # DOES NOT CURRENTLY WORK
  17. $allowed_neg_clearance = 18.0 # INCHES
  18.  
  19. # Define the distance from the edge to the "feet" in inches
  20. $feet_from_edge = 189 # INCHES from outside of component to middle of first pile
  21.  
  22.  
  23.  
  24.  
  25.  
  26. return $axis, $allowed_neg_clearance, $feet_from_edge
  27. end
  28.  
  29.  
  30.  
  31. def identify_feet(entity, axis)
  32. bounds = entity.bounds
  33. if $axis == :red
  34. x_mid = (bounds.max.x + bounds.min.x) / 2.0
  35. y_foot_a = bounds.min.y + $feet_from_edge
  36. y_foot_b = bounds.max.y - $feet_from_edge
  37. foot_a = [x_mid, y_foot_a, bounds.min.z]
  38. foot_b = [x_mid, y_foot_b, bounds.min.z]
  39. else # Axis is :green
  40. y_mid = (bounds.max.y + bounds.min.y) / 2.0
  41. x_foot_a = bounds.min.x + $feet_from_edge
  42. x_foot_b = bounds.max.x - $feet_from_edge
  43. foot_a = [x_foot_a, y_mid, bounds.min.z]
  44. foot_b = [x_foot_b, y_mid, bounds.min.z]
  45. end
  46.  
  47. return foot_a, foot_b
  48. end
  49.  
  50.  
  51.  
  52.  
  53.  
  54. def query_terrain(foot_a, foot_b, entity)
  55. model = Sketchup.active_model
  56.  
  57. # Convert local coordinates to global if entity has a parent
  58. #global_foot_a = foot_a.transform(entity.transformation)
  59. #global_foot_b = foot_b.transform(entity.transformation)
  60.  
  61. global_foot_a = Geom::Point3d.new(foot_a)
  62. global_foot_b = Geom::Point3d.new(foot_b)
  63.  
  64. puts "> QT() > global_foot_a: #{global_foot_a}" if $debug_print
  65. puts "> QT() > global_foot_b: #{global_foot_b}" if $debug_print
  66.  
  67. # Initialize ray for foot_a and foot_b
  68. ray_a = [global_foot_a, [0, 0, -1]]
  69. ray_b = [global_foot_b, [0, 0, -1]]
  70.  
  71. puts "> QT() > ray_a: #{ray_a}" if $debug_print
  72. puts "> QT() > ray_b: #{ray_b}" if $debug_print
  73.  
  74. # Perform raycasting for foot_a and foot_b
  75. terrain_intersections_a = model.raytest(ray_a, false)
  76. terrain_intersections_b = model.raytest(ray_b, false)
  77.  
  78. puts "> QT() > terrain_intersections_a: #{terrain_intersections_a}" if $debug_print
  79. puts "> QT() > terrain_intersections_b: #{terrain_intersections_b}" if $debug_print
  80.  
  81. if terrain_intersections_a.nil? || terrain_intersections_b.nil?
  82. puts "No terrain intersection found for one or both feet."
  83. return nil, nil
  84. end
  85.  
  86. z_a = terrain_intersections_a[0].z
  87. z_b = terrain_intersections_b[0].z
  88.  
  89. puts "> QT() > How high is the terrain is at foot_a's intersection is: #{z_a}" if $debug_print
  90. puts "> QT() > How high is the terrain is at foot_b's intersection is: #{z_b}" if $debug_print
  91.  
  92. # Calculate the distance between the foot and the terrain
  93. z_a = (foot_a.z - z_a).abs
  94. z_b = (foot_b.z - z_b).abs
  95.  
  96. puts "> QT() > Foot_a's height above terrain is z_a: #{z_a}" if $debug_print
  97. puts "> QT() > Foot_b's height above terrain is z_b: #{z_b}" if $debug_print
  98.  
  99. return z_a, z_b
  100. end
  101.  
  102.  
  103.  
  104.  
  105. def calculate_rotation(z_a, z_b, foot_a, foot_b)
  106. # Create Geom::Point3d objects for the feet
  107. point_a = Geom::Point3d.new(foot_a)
  108. point_b = Geom::Point3d.new(foot_b)
  109.  
  110. # Calculate the distance between foot_a and foot_b in the plane parallel to the terrain
  111. distance = point_a.distance(point_b)
  112.  
  113. puts "> Calculate_rotation() > Distance between feet - #{distance}" if $debug_print
  114.  
  115. # Calculate the height difference between the two feet above the terrain
  116. #delta_z = (z_a - z_b).abs
  117. delta_z = (z_a - z_b)
  118.  
  119. puts "> Calculate_rotation() > Height difference above terrain - #{delta_z}" if $debug_print
  120.  
  121. # Calculate the angle needed for rotation in radians
  122. # angle = arctan(delta_z / distance)
  123. rotation_angle = Math.atan2(delta_z, distance)
  124.  
  125. # Convert the angle to degrees for better readability
  126. rotation_angle_deg = rotation_angle * (180.0 / Math::PI)
  127.  
  128. puts "> Calculate_rotation() > Rotation angle in radians - #{rotation_angle}" if $debug_print
  129. puts "> Calculate_rotation() > Rotation angle in degrees - #{rotation_angle_deg}" if $debug_print
  130.  
  131. return rotation_angle
  132. end
  133.  
  134.  
  135.  
  136.  
  137.  
  138.  
  139. def do_transformation(entity, z_a, z_b, rotation_angle, axis)
  140. # Debug Output
  141. puts "> DO_T() > Starting do_transformation function" if $debug_print
  142.  
  143. # Get the bounding box of the entity
  144. bounds = entity.bounds
  145. # Calculate the bottom center for rotation
  146. bottom_center = [(bounds.min.x + bounds.max.x) / 2.0, (bounds.min.y + bounds.max.y) / 2.0, bounds.min.z]
  147. puts "> DO_T() > Initial Bottom Center: #{bottom_center}" if $debug_print
  148.  
  149. # If the entity is part of a group or component, get the transformation to global coordinates
  150. if entity.parent.is_a?(Sketchup::Group) || entity.parent.is_a?(Sketchup::ComponentInstance)
  151. transformation_to_global = entity.parent.transformation
  152. bottom_center = transformation_to_global * bottom_center
  153. end
  154. puts "> DO_T() > Transformed Bottom Center (if applicable): #{bottom_center}" if $debug_print
  155.  
  156. # calculate the middle height between the two delta z'see
  157. #z_mid = ((z_a - z_b).abs) / 2.0
  158. z_mid = (z_a + z_b) / 2.0
  159. puts "> DO_T() > Z-values for Foot A and Foot B: #{z_a}, #{z_b}" if $debug_print
  160. puts "> DO_T() > Calculated dZ Midpoint: #{z_mid}" if $debug_print
  161.  
  162. # Calculate the midpoint between the two feet along the Z-axis
  163. #z_mid = (z_a + z_b) / 2.0
  164. #puts "> DO_T() > Z-values for Foot A and Foot B: #{z_a}, #{z_b}"
  165. #puts "> DO_T() > Calculated Z Midpoint: #{z_mid}"
  166.  
  167. # Define the axis of rotation based on user input
  168. rotation_axis = axis == :red ? X_AXIS : Y_AXIS
  169. puts "> DO_T() > Axis of Rotation: #{rotation_axis}" if $debug_print
  170.  
  171. puts "> DO_T() > Angle of Rotation: #{rotation_angle}" if $debug_print
  172.  
  173. puts "> DO_T() > rotation_transform = Geom::Transformation.rotation(#{bottom_center}, #{rotation_axis}, #{rotation_angle})" if $debug_print
  174.  
  175. # Define the rotation transformation based on the calculated rotation_angle and bottom_center
  176. rotation_transform = Geom::Transformation.rotation(bottom_center, rotation_axis, rotation_angle)
  177. puts "> DO_T() > Rotation Transformation: #{rotation_transform.to_a}" if $debug_print
  178.  
  179. # Define the Z-axis translation based on z_mid
  180. #z_translation = Geom::Transformation.translation([0, 0, z_mid - bottom_center.z])
  181. z_translation = Geom::Transformation.translation([0, 0, -z_mid])
  182. puts "> DO_T() > Z-Axis Translation: #{z_translation.to_a}" if $debug_print
  183. puts "> DO_T() > Z-Axis Translation: #{z_translation}" if $debug_print
  184.  
  185. #z_move = Geom::Transformation.translation([0, 0, -240])
  186. #entity.transform!(z_move)
  187.  
  188. entity_transformation = entity.transformation
  189. puts "> DO_T() > Debug: Entity's Initial Global Transformation: #{entity_transformation.to_a}" if $debug_print
  190.  
  191.  
  192. # Apply the transformations
  193. puts "Entity's Initial Position: #{entity.bounds.center}" if $debug_print
  194. entity.transform!(rotation_transform)
  195. puts "> DO_T() > Debug: Entity's Transformation After Rotation: #{entity.transformation.to_a}" if $debug_print
  196. puts "Entity's Position After Rotation: #{entity.bounds.center}" if $debug_print
  197.  
  198. entity.transform!(z_translation)
  199. puts "> DO_T() > Debug: Entity's Final Transformation: #{entity.transformation.to_a}" if $debug_print
  200. puts "Entity's Final Position: #{entity.bounds.center}" if $debug_print
  201.  
  202. Sketchup.active_model.active_view.refresh
  203. #entity.transform!(rotation_transform * z_translation)
  204.  
  205. #z_move = Geom::Transformation.translation([0, 0, -240])
  206. #entity.transform!(z_move)
  207.  
  208. puts "> DO_T() > Transformations applied" if $debug_print
  209. end
  210.  
  211.  
  212.  
  213.  
  214.  
  215. def check_clearance(entity, foot_a, foot_b)
  216. # Transform to global coordinates if part of a group or component instance
  217. if entity.parent.is_a?(Sketchup::Group) || entity.parent.is_a?(Sketchup::ComponentInstance)
  218. transformation_to_global = entity.parent.transformation
  219. foot_a = transformation_to_global * foot_a
  220. foot_b = transformation_to_global * foot_b
  221. end
  222.  
  223. # Calculate 10 equally spaced points between foot_a and foot_b
  224. check_points = []
  225. for i in 0..9
  226. x = foot_a.x + i * (foot_b.x - foot_a.x) / 9.0
  227. y = foot_a.y + i * (foot_b.y - foot_a.y) / 9.0
  228. z = foot_a.z + i * (foot_b.z - foot_a.z) / 9.0
  229. check_points.push(Geom::Point3d.new(x, y, z))
  230. end
  231.  
  232. # Initialize max_intrusion to 0
  233. max_intrusion = 0.0
  234.  
  235. # For each check point, raytest upward to find the distance to the terrain
  236. model = Sketchup.active_model
  237. check_points.each do |point|
  238. hit_point = model.raytest([point, [0, 0, 1]])
  239. if hit_point
  240. intrusion = hit_point[0].z - point.z
  241. max_intrusion = [max_intrusion, intrusion].max
  242. end
  243. end
  244.  
  245. # Compare max_intrusion to allowed negative clearance
  246. if max_intrusion > $allowed_negative_clearance
  247. return max_intrusion - $allowed_negative_clearance
  248. else
  249. return 0.0
  250. end
  251. end
  252.  
  253.  
  254.  
  255.  
  256. def apply_z_transformation(entity, clearance_flag)
  257. # Check if Z-axis adjustment is needed
  258. if clearance_flag > 0.0
  259. # Define the Z-axis translation transformation based on clearance_flag
  260. z_translation = Geom::Transformation.translation([0, 0, clearance_flag])
  261.  
  262. # If part of a group or component instance, transform to global coordinates
  263. if entity.parent.is_a?(Sketchup::Group) || entity.parent.is_a?(Sketchup::ComponentInstance)
  264. z_translation = entity.parent.transformation * z_translation
  265. end
  266.  
  267. # Apply the Z-axis translation transformation
  268. entity.transform!(z_translation)
  269. end
  270. end
  271.  
  272.  
  273.  
  274. # Add blank lines for better console readability
  275. puts "\n\n\n"
  276.  
  277. # Initialize the counter for the number of transformed components
  278. $num_transformed = 0
  279. $total_components = 0
  280.  
  281. # Start the timer
  282. $start_time = Time.now
  283.  
  284. # Function to count total number of components in the selection
  285. def count_components(entities)
  286. entities.each do |entity|
  287. case entity
  288. when Sketchup::ComponentInstance
  289. $total_components += 1
  290. when Sketchup::Group
  291. # Recursively count components within the group
  292. count_components(entity.entities)
  293. end
  294. end
  295. end
  296.  
  297.  
  298.  
  299. # Function to actually apply the transformation to an entity
  300. def apply_transformation(entity)
  301. # # Calculate the center of the bounding box of the entity
  302. # center = entity.bounds.center
  303. # # Define the rotation transformation (30 degrees about the red axis, around the center of the bounding box)
  304. # transformation = Geom::Transformation.rotation(center, X_AXIS, 30.degrees)
  305. # entity.transform!(transformation)
  306.  
  307. axis_of_rotation, allowed_neg_clearance, feet_from_edge = user_settings()
  308.  
  309. puts "> Starting transformation for entity..." if $debug_print
  310.  
  311. # Step 1: Identify the axis of rotation based on user settings
  312. #axis_of_rotation = $user_settings[:axis_of_rotation]
  313. puts "> Axis of rotation: #{axis_of_rotation}" if $debug_print
  314.  
  315. # Step 2: Identify the feet of the component
  316. foot_a, foot_b = identify_feet(entity, axis_of_rotation)
  317. puts "> Identified feet: Foot A = #{foot_a}, Foot B = #{foot_b}" if $debug_print
  318.  
  319. # Step 3: Query the terrain to get the Z-axis values for the feet
  320. z_a_initial, z_b_initial = query_terrain(foot_a, foot_b, entity)
  321. puts "> Height above terrain: Foot A dZ = #{z_a_initial}, Foot B dZ = #{z_b_initial}" if $debug_print
  322.  
  323. # Step 4: Calculate the Rotation Angle
  324. rotation_angle = calculate_rotation(z_a_initial, z_b_initial, foot_a, foot_b)
  325. puts "> Calculated rotation angle: #{rotation_angle}" if $debug_print
  326.  
  327. # Step 5: Apply the Rotation Transformation
  328. #do_transformation(entity, rotation_angle, axis_of_rotation)
  329. do_transformation(entity, z_a_initial, z_b_initial, rotation_angle, axis_of_rotation)
  330. puts "> Applied rotation transformation." if $debug_print
  331.  
  332. # Step 6: Move Component Down to Ground
  333. #move_to_ground(entity, z_a_initial, z_b_initial)
  334. # puts "> Moved component down to ground."
  335.  
  336.  
  337. # UNCOMMENT AND FIX, but for now just test the above
  338. # # Step 7: Check for Clearance Issues
  339. # clearance_flag = check_clearance(entity, foot_a, foot_b)
  340. # puts "> Checked for clearance issues. Clearance flag: #{clearance_flag}"
  341.  
  342. # # Step 8: Apply Z-Axis Transformation If Needed
  343. # apply_z_transformation(entity, clearance_flag)
  344. # puts "> Applied Z-axis transformation if needed."
  345.  
  346. puts "> Transformation for entity completed." if $debug_print
  347.  
  348. end
  349.  
  350.  
  351.  
  352. # Function to perform the transformation operation and provide user feedback
  353. def perform_transformation_operation(entity)
  354. apply_transformation(entity)
  355. $num_transformed += 1
  356.  
  357. # Calculate and display elapsed time and remaining time estimates
  358. elapsed_time = Time.now - $start_time
  359. avg_time_per_component = elapsed_time / $num_transformed
  360. remaining_components = $total_components - $num_transformed
  361. estimated_time_remaining = avg_time_per_component * remaining_components
  362.  
  363. puts "Transforming component #{$num_transformed} of #{$total_components}. Elapsed Time: #{elapsed_time.round(2)}s. Estimated Time Remaining: #{estimated_time_remaining.round(2)}s."
  364. end
  365.  
  366.  
  367.  
  368. # Function to iterate through entities and perform transformation
  369. def iterate_through_transforming_components(entities)
  370. entities.each do |entity|
  371. case entity
  372. when Sketchup::ComponentInstance
  373. perform_transformation_operation(entity)
  374. when Sketchup::Group
  375. # Recursively apply rotation to nested entities within the group
  376. iterate_through_transforming_components(entity.entities)
  377. end
  378. end
  379. end
  380.  
  381.  
  382. # This works in conjunction with the new apply_transformation to pass it a transformation, but we'll see.
  383. # def iterate_through_transforming_components(entities, transformation = nil)
  384. # entities.each do |entity|
  385. # # Clone the transformation
  386. # global_transformation = transformation ? transformation.clone : nil
  387.  
  388. # # Apply the parent group's transformation to the entity
  389. # global_transformation *= entity.transformation if global_transformation
  390.  
  391. # case entity
  392. # when Sketchup::ComponentInstance
  393. # apply_transformation(entity, global_transformation)
  394. # when Sketchup::Group
  395. # # Clone the transformation for the recursive call
  396. # iterate_through_transforming_components(entity.entities, global_transformation.clone)
  397. # end
  398. # end
  399. # end
  400.  
  401.  
  402.  
  403. # Get the current selection
  404. selection = Sketchup.active_model.selection
  405.  
  406. # Count total number of components in the selection
  407. count_components(selection)
  408. puts "Total number of components to be transformed: #{$total_components}"
  409.  
  410. # Start an operation, allowing for undo later
  411. Sketchup.active_model.start_operation('Transform Components', true)
  412.  
  413. # Perform the transformation
  414. iterate_through_transforming_components(selection)
  415.  
  416. # Commit the operation to make it live
  417. Sketchup.active_model.commit_operation
  418.  
  419. # Output the number of transformed components and total elapsed time
  420. puts "Number of transformed components: #{$num_transformed}"
  421. total_elapsed_time = Time.now - $start_time
  422. puts "Total elapsed time: #{total_elapsed_time.round(2)}s"
  423.  
  424. # Show a popup to decide whether to keep or undo the transformations
  425. result = UI.messagebox('Keep the changes?', MB_YESNO)
  426. if result == IDNO
  427. # If the user chooses "No", then initiate SketchUp's native undo action.
  428. Sketchup.send_action("editUndo:")
  429. end
  430.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement