Detectar quando os vértices de um componente específico colide com algum cubo

Preciso encontrar uma solução para que consiga encontrar os limites de cada componente de forma global para que e compare se existe intersecção de usinagens dentro do me cubo, considerando que minhas usinagem podem estar agrupadas ou não. Alguém pode me ajudar para melhorar me script para que ele consiga transformar e trazer as cordenadas globais independente da hierarquia que cada componente está?

# Função para detectar componentes com atributo "int_cat" e valor "usi_col"
def find_usi_col_components(entities)
  usi_col_components = []
  entities.each do |entity|
    if entity.is_a?(Sketchup::ComponentInstance) || entity.is_a?(Sketchup::Group)
      if entity.definition.get_attribute("dynamic_attributes", "int_cat") == "usi_col"
        usi_col_components << entity
      end
      # Recursivamente procurar em grupos e componentes aninhados
      usi_col_components.concat(find_usi_col_components(entity.definition.entities))
    end
  end
  usi_col_components
end

# Função para verificar se o grupo pai tem o atributo "int_cat" com valor "usi_group"
def parent_has_usi_group_attribute?(component_instance)
  parent = component_instance.parent
  return false unless parent.is_a?(Sketchup::Model) || parent.is_a?(Sketchup::ComponentDefinition)

  parent_instance = parent.instances.first
  return false unless parent_instance

  parent_instance.definition.get_attribute("dynamic_attributes", "int_cat") == "usi_group"
end

# Função para obter as coordenadas globais exatas do ponto de origem da usinagem
def get_usinagem_origin(component)
  origin = component.transformation.origin
  
  {
    x: (origin.x * 25.4).round(1),  # Convertendo de polegadas para milímetros
    y: (origin.y * 25.4).round(1),
    z: (origin.z * 25.4).round(1)
  }
end

# Função para obter as coordenadas mínimas e máximas do BoundingBox do MDF (em milímetros)
def get_bounding_box_coordinates(component)
  bounds = component.bounds

  # Obter os limites mínimos e máximos diretamente sem transformação
  {
    min_x: (bounds.min.x * 25.4).round(1),  # Convertendo de polegadas para milímetros
    min_y: (bounds.min.y * 25.4).round(1),
    min_z: (bounds.min.z * 25.4).round(1),
    max_x: (bounds.max.x * 25.4).round(1),
    max_y: (bounds.max.y * 25.4).round(1),
    max_z: (bounds.max.z * 25.4).round(1)
  }
end

# Função para formatar as coordenadas de forma clara
def format_origin_coordinates(description, coordinates)
  "#{description} {x=#{coordinates[:x]}, y=#{coordinates[:y]}, z=#{coordinates[:z]}}"
end

# Função para formatar o BoundingBox de forma clara
def format_bounding_box(description, coordinates)
  "#{description} {min_x=#{coordinates[:min_x]}, min_y=#{coordinates[:min_y]}, min_z=#{coordinates[:min_z]}, max_x=#{coordinates[:max_x]}, max_y=#{coordinates[:max_y]}, max_z=#{coordinates[:max_z]}}"
end

# Função principal para verificar se os pontos dos eixos globais estão dentro de algum BoundingBox
def check_usinagem_within_components(usi_col_component, entities)
  detected_intersections = []

  # Verifica se o grupo pai tem o atributo "int_cat" com valor "usi_group"
  return false, detected_intersections if parent_has_usi_group_attribute?(usi_col_component)

  usinagem_origin = get_usinagem_origin(usi_col_component)

  entities.each do |entity|
    next unless entity.is_a?(Sketchup::ComponentInstance) || entity.is_a?(Sketchup::Group)
    next if entity == usi_col_component

    # Verifica se o componente tem o atributo "int_cat" com valor "MDF"
    next unless entity.definition.get_attribute("dynamic_attributes", "int_cat") == "MDF"

    bounding_box_coords = get_bounding_box_coordinates(entity)

    # Verifica se os pontos de origem da usinagem (x, y, z) estão dentro dos intervalos do BoundingBox do componente MDF
    colisao_x = usinagem_origin[:x] >= bounding_box_coords[:min_x] && usinagem_origin[:x] <= bounding_box_coords[:max_x]
    colisao_y = usinagem_origin[:y] >= bounding_box_coords[:min_y] && usinagem_origin[:y] <= bounding_box_coords[:max_y]
    colisao_z = usinagem_origin[:z] >= bounding_box_coords[:min_z] && usinagem_origin[:z] <= bounding_box_coords[:max_z]

    if colisao_x && colisao_y && colisao_z
      detected_intersections << {
        usi_component_desc: usi_col_component.definition.get_attribute("dynamic_attributes", "int_desc"),
        intersecting_component_desc: entity.definition.get_attribute("dynamic_attributes", "int_desc"),
        usinagem_origin: usinagem_origin,
        component_bounds: bounding_box_coords
      }
    else
      puts "Colisão não detectada: X=#{colisao_x}, Y=#{colisao_y}, Z=#{colisao_z}"
    end
  end

  [!detected_intersections.empty?, detected_intersections]
end

# Função principal para executar o script
def run_collision_detection
  model = Sketchup.active_model
  entities = model.entities
  usi_col_components = find_usi_col_components(entities)
  
  collision_found = false
  all_intersections = []

  usi_col_components.each do |usi_col_component|
    found_collision, detected_intersections = check_usinagem_within_components(usi_col_component, entities)
    all_intersections.concat(detected_intersections) if found_collision
    collision_found ||= found_collision
  end
  
  if collision_found
    intersection_details = all_intersections.map do |intersection|
      "Colisão detectada!\n" \
      "Usinagem: #{format_origin_coordinates(intersection[:usi_component_desc], intersection[:usinagem_origin])}\n" \
      "Colidiu com: #{format_bounding_box(intersection[:intersecting_component_desc], intersection[:component_bounds])}"
    end.join("\n\n")
    UI.messagebox("Colisões Detectadas:\n\n#{intersection_details}")
  else
    usinagem_details = usi_col_components.map do |usi|
      usinagem_origin = get_usinagem_origin(usi)
      format_origin_coordinates(usi.definition.get_attribute("dynamic_attributes", "int_desc"), usinagem_origin)
    end.join("\n")
    components_details = entities.grep(Sketchup::ComponentInstance).map do |comp|
      next unless comp.definition.get_attribute("dynamic_attributes", "int_cat") == "MDF"
      bounding_box_coords = get_bounding_box_coordinates(comp)
      format_bounding_box(comp.definition.get_attribute("dynamic_attributes", "int_desc"), bounding_box_coords)
    end.compact.join("\n")
    
    UI.messagebox("Nenhuma colisão detectada.\n\nUsinagens encontradas:\n#{usinagem_details}\n\nComponentes analisados:\n#{components_details}")
  end
  
  model.commit_operation
end

# Iniciar a detecção
Sketchup.active_model.start_operation('Detect Usinagem Collisions', true)
run_collision_detection

Procurei por artigos com o mesmo tópico mais nenhum deles tiveram esclarecimento.

Obrigado.

When you get information about an object you need to find that object’s transformation.
And also the transformation info for its parent [container if applicable].
From these you can manipulate the info so that a shared coordinate system is found etc…

Quando você obtém informações sobre um objeto, você precisa encontrar a transformação desse objeto.
E também as informações de transformação para seu pai [contêiner, se aplicável].
A partir delas, você pode manipular as informações para que um sistema de coordenadas compartilhado seja encontrado, etc…

See the API guide…
Veja o guia da API…

Dynamic attribute dictionaries can be attached to both the instance and the definition IF the object is a component. BUT if the object is a nested dynamic group, then the dynamic dictionary is only attached at the instance level.

Dynamic groups must be nested within a dynamic component.

Dicionários de atributos dinâmicos podem ser anexados tanto à instância quanto à definição SE o objeto for um componente. MAS se o objeto for um grupo dinâmico aninhado, o dicionário dinâmico será anexado somente no nível da instância.

Grupos dinâmicos devem ser aninhados dentro de um componente dinâmico.

See the Numeric superclass for methods the SketchUp API added for easy conversion.
These methods are inherited by all numeric subclasses and Length.

Veja a superclasse Numeric para métodos que a API do SketchUp adicionou para conversão fácil.
Esses métodos são herdados por todas as subclasses numéricas e Length.

Example / Exemplo:

    min_y: (bounds.min.y.mm.round(1),

REF: Numeric#mm()

Since you are using bounding boxes, why not just leverage the API’s Geom::BoundingBox#intersect method ?

As your find_usi_col_components() method drills down into the geometric hierarchy, you must build an InstancePath array and apply it’s compound #transformation to each entity’s corner points. This means creating a new Geom::BoundingBox object with the transformed corners.
Note that point objects have a #transform method.

# Return a transformed bounding box from another bounding box.
#
# @param bounds [Geom::BoundingBox]
# @param transformation [Geom::Transformation]
#
# @return [Geom::BoundingBox] The new bounding box.
def transform_bounds(bounds, transformation)
  corners = (0..7).map { |i| bounds.corner(i).transform(transformation) }
  Geom::BoundingBox.new.add(corners)
end

This then also means that you’ll want to use a Hash instead of an Array so you can store changing bounding boxes with each entity reference as the key.


Já que você está usando caixas delimitadoras, por que não aproveitar o método Geom::BoundingBox#intersect da API?

Conforme seu método find_usi_col_components() aprofunda-se na hierarquia geométrica, você deve construir um array InstancePath e aplicar seu composto #transformation aos pontos de canto de cada entidade. Isso significa criar um novo objeto Geom::BoundingBox com os cantos transformados.
Observe que os objetos de ponto têm um método #transform.

# Retorna uma caixa delimitadora transformada de outra caixa delimitadora.
#
# @param bounds [Geom::BoundingBox]
# @param transformation [Geom::Transformation]
#
# @return [Geom::BoundingBox] A nova caixa delimitadora.
def transform_bounds(bounds, transformation)
  corners = (0..7).map { |i| bounds.corner(i).transform(transformation) }
  Geom::BoundingBox.new.add(corners)
end

Isso também significa que você vai querer usar um Hash em vez de um Array para que você possa armazenar caixas delimitadoras em mudança com cada referência de entidade como a chave.

1 Like