newmen/versatile-diamond

View on GitHub
analyzer/lib/concepts/surface_linker.rb

Summary

Maintainability
A
0 mins
Test Coverage
module VersatileDiamond
  module Concepts

    # Provides methods for linking two surface atoms
    module SurfaceLinker
    protected

      # Links together atoms of surface spec. Surface spec must have at least
      # one atom belonging to the lattice. Obtaining the inverse relation
      # between linking atoms is occured by the crystal lattice.
      #
      # @param [Atom] first the first of two linking atoms
      # @param [Atom] second the second of two linking atoms
      # @param [Bond] relation the instance of relation
      # @option [Boolean] :check_possible is flag that another positions should be
      #   checked
      # @raise [Lattices::Base::UndefinedRelation] if used relation instance is
      #   wrong for current lattice
      # @raise [Position::Duplicate] if same position already exist
      def link_together(first, second, relation, check_possible: true)
        orel = opposite_relation(first, second, relation)

        if !relation.bond? && has_relations?(first, second, relation, orel)
          raise Position::Duplicate, relation
        end

        if check_possible && !(relation.exist? || position_presented?)
          raise NonPosition::Impossible
        end

        link_with_other(first, second, relation, orel)
      end

    private

      # Checks that any position relation is presented
      # @return [Boolean] is position presented in links graph or not
      def position_presented?
        links.any? do |_, rels|
          rels.any? { |_, r| r.relation? && !r.bond? && r.exist? }
        end
      end

      # If so, must have relations in both directions
      # @param [Atom] first the first atom
      # @param [Atom] second the second atom
      # @param [Array] relations the array with two relations
      # @return [Boolean] has or not
      def has_relations?(first, second, *relations)
        a = has_relation?(first, second, relations.first)
        b = has_relation?(second, first, relations.last)

        if a && b
          true
        elsif a || b
          raise 'Checking relations ERROR'
        else
          false
        end
      end

      # Check availability of passed relation between atoms
      # @param [Atom] first the first atom
      # @param [Atom] second the second atom
      # @param [Bond] relation the relation from first atom to second atom
      # @return [Boolean] has or not
      def has_relation?(first, second, relation)
        !!links[first].find do |atom, link|
          atom == second && link.it?(relation.params)
        end
      end
    end

  end
end