Mergeprog

#!/usr/bin/ruby
# Bjorn Bergqvist 20090324
# solved: 	replacement table for point numbers
# 		find which bc that coincide
#	 	neighbour for merged bc line
#		assemble "mesh"
# 		make "partial" merge possible (just delete coincided faces and points )
# methods
def translate( x, y, movx, movy )
   xres, yres = Array.new, Array.new
   x.each { |x| xres.push( x + movx ) }
   y.each { |y| yres.push( y + movy ) }
   return xres, yres
end
# cell definition [ p0, p1, p2, p3 ]
def makecells( xysize, newcol )
   startpoint = 0
   newcellcol = newcol - 2
   celldef = Array.new
   (0..xysize/newcol - 2).each do |col|
      (0..newcol - 2).each do |row|
         celldef.push( [row + (1+col)*newcol + startpoint,
                        row + (1+col)*newcol + 1 + startpoint,
                        row + col*newcol + 1 + startpoint,
                        row + col*newcol + startpoint])
      end
   end
   return celldef
end
def cellshift(celldef, startpoint)
   result = Array.new
   celldef.each { |cell| result.push( [ cell[0] + startpoint, cell[1] + startpoint, cell[2] + startpoint, cell[3] + startpoint ] ) }
   return result
end
# Mesh Export class
class MeshExport
   # class variables
   @@celldef = Array.new
   @@pointx = Array.new
   @@pointy = Array.new
   @@ilines = Array.new
   @@owner = Array.new
   @@neighbour = Array.new
   @@bno = Array.new
   @@faceno = 0
   def initialize  
   end
   def add( celldef, newcol, pointx, pointy )
      firstpoint = @@pointx.size
      @@celldef[ @@faceno ] = cellshift( celldef, firstpoint )
      @@pointx += pointx
      @@pointy += pointy
      firstcell = 0
      if @@neighbour.last != nil 
         @@neighbour.last.flatten.each { |cell| firstcell = cell if ( cell > firstcell ) }
         firstcell += 1
      end
     #puts "#{firstpoint} ####################################################################################################"
      @@ilines[ @@faceno], @@owner[ @@faceno ], @@neighbour[ @@faceno ], @@bno[ @@faceno ] = makelines( @@celldef[@@faceno], newcol, firstcell)
      @@faceno += 1
   end
   #
   # bcranges calculates:
   # @xranges[ face number ][ bondary number ]
   # @yranges[ face number ][ bondary number ]
   #
   def bcranges
      @xranges = Array.new
      @yranges = Array.new
      (0..@@faceno - 1).each do |fno|
         @xranges[fno] = Array.new
         @yranges[fno] = Array.new
         (1..@@ilines[fno].size - 1).each do |bno|
            @xranges[fno][bno] = Array.new
            @yranges[fno][bno] = Array.new
            (0..@@ilines[fno][bno].size - 1).each do |lno|
               (0..@@ilines[fno][bno][lno].size - 1).each do |pno|
                  #@xranges[fno][bno][pno] = pno
                  pointno = @@ilines[fno][bno][lno][pno]
                  #puts "faceno #{fno}, bcno #{bno}, pointno #{ pointno }, x #{@@pointx[pointno]}, y #{@@pointy[pointno]}"
                  @xranges[fno][bno][pointno] = @@pointx[ pointno ] 
                  @yranges[fno][bno][pointno] = @@pointy[ pointno ] 
               end
            end
            @xranges[fno][bno].compact!
            @yranges[fno][bno].compact!
            #puts "xranges #{@xranges[fno][bno].join(", ")}"
            #puts "yranges #{@yranges[fno][bno].join(", ")}"
            #puts "face no #{fno}, bondary no #{bno}, xmin #{@xranges[fno][bno].min}, xmax #{@xranges[fno][bno].max
            #}, ymin #{@yranges[fno][bno].min}, ymax #{@yranges[fno][bno].max}" 
         end
      end
   end
   # see if ranges might coincide
   def testranges( x1, y1, x2, y2)
      tolerance = 1e-9
      if x1.max + tolerance  < x2.min
         return false
      end
      if y1.max + tolerance < y2.min
         return false
      end
      if x2.max + tolerance  < x1.min
         return false
      end
      if y2.max + tolerance < y1.min
         return false
      end
      return true
   end
   def duplicatesubset( list1, list2)
      tolerance = 1e-9
      keepidx = Array.new
      removeidx = Array.new
      list1.each do |p1|
         list2.each do |p2|
            if ( (@@pointx[p1] - @@pointx[p2]).abs < tolerance )  && ( (@@pointy[p1] - @@pointy[p2]).abs < tolerance ) 
               keepidx.push( p1 )
               removeidx.push( p2 )
            end
         end
      end
      return removeidx, keepidx
   end
   def deletepoints
      @exportx, @exporty = @@pointx.clone, @@pointy.clone
      @delpoints.each { |delp| @exportx[delp], @exporty[delp] = nil, nil }
      @exportx.compact!
      @exporty.compact!
   end
   def replacetable
      # mergecandidates has to be run first
      @oldpointno = (0..@@pointx.size - 1).to_a
      puts "old index #{@oldpointno.join(", ")}"
      @newpointno = @oldpointno
      @delpoints.each_with_index do |delp, idx|
         (delp..@@pointx.size - 1).each do |del1|
            @newpointno[del1] -= 1 
         end
      end
      #@newpointno.compact!
      @delpoints.each_with_index do |delp, idx|
         @newpointno[delp] = @keeppoints[idx]
      end
      puts "new index #{@newpointno.join(", ")}"
   end
   def newno( oldno )
      if oldno.class == Fixnum
         return @newpointno[oldno]
      elsif oldno.class == Array
         if oldno[0].class == Array
            result = Array.new
            oldno.each_with_index do |olda,idx|
               result[idx] = Array.new
               olda.each { |oldp| result[idx].push( newno(oldp) ) }
            end
            return result
         else
            result = Array.new
            oldno.each { |oldp| result.push( newno(oldp) ) }
            return result
         end
      end
   end
   # find out which faces that might coincide
   def mergecandidates
      bcranges
      @delpoints = Array.new
      @keeppoints = Array.new
      @removebc = Array.new
      @internalbc = Array.new
      @checkbc = Array.new
      (0..@@faceno - 2).each do |face1|
         (face1 + 1..@@faceno - 1).each do |face2|
            # puts "face1 #{face1}, face2 #{face2}"
               (1..4).each do |b1|
                  (1..4).each do |b2|
                     coincide = testranges( @xranges[face1][b1], @yranges[face1][b1], @xranges[face2][b2], @yranges[face2][b2] )
                     # puts "compare face #{face1}, bno #{b1} with face #{face2}, bno #{b2} result #{coincide}" if coincide
                     if coincide
                        remove, keep = duplicatesubset( @@ilines[face1][b1].flatten.uniq, @@ilines[face2][b2].flatten.uniq )
                        #puts "keep #{keep.join(", ")}, remove #{remove.join(", ")}"
                        @delpoints.push(remove) 
                        @keeppoints.push(keep)
                        # remove bc if more than 1 point is duplicate
                        if remove.size > 1
                           @internalbc.push( [ face1, b1 ] )
                           @removebc.push( [ face2, b2 ] )
                        # check bc if one point is duplicate
			else
                           @checkbc.push( [ face2, b2 ] )
                        end
                        puts "delete boundary face#{face2} bno #{b2}" if remove.size > 1
                     end
                  end
               end
         end
      end
      @delpoints.flatten!
      @delpoints.uniq!
      @keeppoints.flatten!
      @keeppoints.uniq!
      puts "delete points \t#{@delpoints.join(", ")}"
      puts "keep points \t#{@keeppoints.join(", ")}"
      @removebc.each do |rbc|
         puts "remove bc #{rbc[1]} of face #{rbc[0]}"
      end
      @checkbc.each do |rbc|
         puts "check bc #{rbc[1]} of face #{rbc[0]}"
      end
   end
   def createneighbour
      @removeafter = Array.new
      (0..@internalbc.size - 1).each do |line| 
         puts "neighbour for face #{@internalbc[line][0]} line #{@internalbc[line][1]} remove face #{@removebc[line][0]} line #{@removebc[line][1]}"
         internal = @internalbc[line]
         remove = @removebc[line]
	 intface, intline = internal[0], internal[1]
	 remface, remline = remove[0], remove[1]
         # puts "lines with neighbour #{@@ilines[intface][intline].join(", ")}"
         @@ilines[intface][intline].each_with_index do |line1,idx1|
            @@ilines[remface][remline].each_with_index do |line2,idx2|
               if line1.sort == newno(line2).sort
                  # puts "line1 #{line1.join(", ")}, idx1 #{idx1}, line2 #{line2.join(", ")}, idx2 #{idx2}"
                  # puts "2line1 #{line1.join(", ")}, idx1 #{idx1}, line2 #{newno(line2).join(", ")}, idx2 #{idx2}"
                  @@ilines[intface][5].push( @@ilines[intface][intline][idx1] )
                  @@neighbour[intface][5].push( @@owner[remface][remline][idx2] )
                  @@owner[intface][5].push( @@owner[intface][intline][idx1] )
                  @@bno[intface][5].push( @@bno[intface][intline][idx1] )  # @@bno[intface][0].push( -1 ) give new boundary idx?
                  @removeafter.push( [intface, intline, idx1] )
                  @removeafter.push( [remface, remline, idx2] )
               end
            end
         end
      end
      #@removeafter.each { |rem| @@ilines[ rem[0] ][ rem[1] ][ rem[2] ] = nil }
      #@removeafter.each { |rem| @@neighbour[ rem[0] ][ rem[1] ][ rem[2] ] = nil }
      #@removeafter.each { |rem| @@owner[ rem[0] ][ rem[1] ][ rem[2] ] = nil }
      #@removeafter.each { |rem| @@bno[ rem[0] ][ rem[1] ][ rem[2] ] = nil }
   end
   def deletebc
      createneighbour
=begin
      @removebc.each do |delbc|
         @@ilines[ delbc[0] ][ delbc[1] ] = nil
         @@owner[ delbc[0] ][ delbc[1] ] = nil
         @@neighbour[ delbc[0] ][ delbc[1] ] = nil
         @@bno[ delbc[0] ][ delbc[1] ] = nil
      end
      @internalbc.each do |intbc|
         @@owner[ intbc[0] ][ 0 ] += @@owner[ intbc[0] ][ intbc[1] ]
         @@owner[ intbc[0] ][ intbc[1] ] = nil
         @@neighbour[ intbc[0] ][ 0 ] += @@neighbour[ intbc[0] ][ intbc[1] ]
         @@neighbour[ intbc[0] ][ intbc[1] ] = nil
         @@ilines[ intbc[0] ][ 0 ] += @@ilines[ intbc[0] ][ intbc[1] ]
         @@ilines[ intbc[0] ][ intbc[1] ] = nil
      end
=end
   end
   def print
      (0..@@ilines.size - 1).each do |idx|
         puts "\nface #{idx}"
         printlines( @@ilines[ idx ] , @@owner[ idx ], @@neighbour[ idx], @@bno[ idx ] )
      end
      puts "\npoints"
      puts @@pointx.join(", ")
      puts @@pointy.join(", ")
   end
   def merge
      removep, keepp = duplicatepoints( @@pointx, @@pointy )
      puts "duplicate points, remove #{removep.join(", ")}"
      puts "duplicate points, keep #{keepp.join(", ")}"
   end
   def export
      (0..@@ilines.size - 1).each { |faceno| @@ilines[faceno][5] = Array.new }
      (0..@@neighbour.size - 1).each { |faceno| @@neighbour[faceno][5] = Array.new }
      (0..@@owner.size - 1).each { |faceno| @@owner[faceno][5] = Array.new }
      (0..@@bno.size - 1).each { |faceno| @@bno[faceno][5] = Array.new }
      mergecandidates
      replacetable
      deletepoints
      createneighbour
      pointsfile = File.open( "points", "w" )
      pointxy = Array.new
      (0..@exportx.size - 1).each do |pidx|
         pointxy.push( "#{@exportx[pidx]} #{@exporty[pidx]}")
         #pointxy.push( "#{pidx} #{@@pointx[pidx]} #{@@pointy[pidx]}")
      end
      pointsfile.write( pointxy.join("\n") + "\n" )
      pointsfile.close
      puts "points file written"
      ownerfile = File.open( "owner", "w" )
      ownerarray = Array.new
      @@owner.each { |face| ownerarray.push( face[0] ) }
      @@owner.each { |face| ownerarray.push( face[5] ) if face[5].size > 0 }
      #@@owner.each { |face| face[1..4].flatten.each { |bc| ownerarray.push( bc ) if bc != nil } }
      @@owner.each_with_index do |face,fidx|
         (1..4).each do |bc|
            face[bc].each_with_index do |bcline,bidx|
               if !@removeafter.member?( [fidx, bc, bidx] )
                  ownerarray.push( bcline )
                  #puts "a member #{@@owner[fidx][bc][bidx]}"
               end
            end
         end
      end
      #@@owner.each { |face| face[0..4].flatten.each { |bc| puts "1 #{bc}"} }
      #@@owner.each do |face|
      #   (1..4).each do |bcno|
      #      puts "2 #{face[bcno].join(", ")}" if face[bcno].class == Array
      #   end
      #end
      ownerfile.write( ownerarray.compact.join("\n") + "\n" )
      ownerfile.close
      puts "owner file written"
      neighbourfile = File.open( "neighbour", "w" )
      neighbourarray = Array.new
      @@neighbour.each { |face| neighbourarray.push( face[0] ) }
      @@neighbour.each { |face| neighbourarray.push( face[5] ) if face[5].size >  0  }
      #@@neighbour.each { |face| face[1..4].flatten.each { |bc| neighbourarray.push( bc ) if bc !=nil } }
      @@neighbour.each_with_index do |face,fidx|
         (1..4).each do |bc|
            face[bc].each_with_index do |bcline,bidx|
               if !@removeafter.member?( [fidx, bc, bidx] )
                  neighbourarray.push( bcline ) && bcline != nil
                  #puts "a member #{@@neighbour[fidx][bc][bidx]}"
               end
            end
         end
      end
      neighbourfile.write( neighbourarray.compact.join("\n") + "\n" )
      neighbourfile.close
      puts "neighbour file written"
      bnofile = File.open( "bno", "w" )
      bnoarray = Array.new
      @@bno.each { |face| bnoarray.push( face[0] ) }
      @@bno.each { |face| bnoarray.push( face[5] ) if face[5].size > 0 }
      #@@bno.each { |face| face[1..4].flatten.each { |bc| bnoarray.push( bc ) if bc != nil } }
      @@bno.each_with_index do |face,fidx|
         (1..4).each do |bc|
            face[bc].each_with_index do |bcline,bidx|
               if !@removeafter.member?( [fidx, bc, bidx] ) && bcline != nil
                  bnoarray.push( bcline )
               end
            end
         end
      end
      bnofile.write( bnoarray.compact.join("\n") + "\n" )
      bnofile.close
      puts "bno file written"
      linesfile = File.open( "lines", "w" )
      linesarray = Array.new
      @@ilines.each { |face| face[0].each { |line| linesarray.push(newno(line).join(" ")) } }
      @@ilines.each { |face| face[5].each { |line| linesarray.push(newno(line).join(" ")) } }
      #@@ilines.each { |lines| [lines[1], lines[2],lines[3], lines[4]].each { |line| linesarray.push( line.join(" ")) if line.class == Array  } }
      #@@ilines.each { |lines| lines[1..4].each { |line|   } }
      @@ilines.each_with_index do |face,fidx| 
         (1..4).each do |bno|
            (0..face[bno].size - 1).each do |lineidx|
               linesarray.push( newno(face[bno][lineidx]).join(" ") ) if !@removeafter.member?([fidx,bno,lineidx]) if face[bno][lineidx] != nil
            end
         end
      end
      linesfile.write( linesarray.compact.join("\n") + "\n" )
      linesfile.close
      puts "lines file written"
   end
end
# line definition xi = internal line in  xdir, yi = internal line in ydir, xmin, xmax, ymin, ymax = boundary lines
def makelines( celldef, newcol, startcell )
   #ymin 
   ymin = ( 0.. newcol - 1 ).to_a
   #ymax 
   ymax = ( celldef.size - newcol..celldef.size - 1 ).to_a
   xmin, xmax = Array.new, Array.new
   # xmin
   (0..celldef.size - 1).each { |cellno| xmin.push( cellno ) if cellno%newcol == 0 }
   # xmax
   (0..celldef.size - 1).each { |cellno| xmax.push( cellno ) if ( (cellno + 1)%newcol == 0 &&  cellno != 0 ) ||  (newcol == 1 && cellno == 0 )   }
   ilines = Array.new
   owner, neighbour = Array.new, Array.new
   # bno, boundary number. Internal -1, negx 0, posx 1, negy 2, posy 3
   bno = Array.new
   # 5 groups of bc: internal and 4 edge lines
   (0..4).each { |idx| ilines[idx], owner[idx], neighbour[idx], bno[idx] = Array.new, Array.new, Array.new, Array.new }
   (0..celldef.size - 1).each do |cellno| 
      # x direction
      if !xmax.member?cellno
         ilines[0].push( [ celldef[cellno][1], celldef[cellno][2] ] )
         owner[0].push( cellno + startcell )
         neighbour[0].push( cellno + 1 + startcell )
         bno[0].push( -1 )
      end
      # y direction
      if !ymax.member?cellno
         ilines[0].push( [ celldef[cellno][0], celldef[cellno][1] ] )
         owner[0].push( cellno + startcell )
         neighbour[0].push( cellno + newcol + startcell )
         bno[0].push( -1 )
      end
   end
   # bondary lines
   # outer lines
   xmin.each do |cellno| 
      ilines[1].push( [ celldef[cellno][3], celldef[cellno][0] ] )
      owner[1].push( cellno + startcell )
      neighbour[1].push( -1 )
      bno[1].push( 0 )
   end
   xmax.each do |cellno| 
      ilines[2].push( [ celldef[cellno][1], celldef[cellno][2] ] )
      owner[2].push( cellno + startcell )
      neighbour[2].push( -1 )
      bno[2].push( 1 )
   end
   ymin.each do |cellno| 
      ilines[3].push( [ celldef[cellno][2], celldef[cellno][3] ] )
      owner[3].push( cellno + startcell )
      neighbour[3].push( -1 )
      bno[3].push( 2 )
   end
   ymax.each do |cellno| 
      ilines[4].push( [ celldef[cellno][0], celldef[cellno][1] ] )
      owner[4].push( cellno + startcell )
      neighbour[4].push( -1 )
      bno[4].push( 3 )
   end
   return ilines, owner, neighbour, bno
end
# find duplicate points and return index of the duplicate points
def duplicatepoints( px, py )
   tolerance = 1e-9
   maxidx = [ px.size, py.size ].min - 1
   keepidx = Array.new
   removeidx = Array.new
   (0..maxidx).each do |idx1|
      (idx1 + 1..maxidx).each do |idx2|
         #if px[idx1] == px[idx2] && py[idx1] == py[idx2] 
         if ( (px[idx1] - px[idx2]).abs < tolerance )  && ( (py[idx1] - py[idx2]).abs < tolerance ) 
            # puts "idx1 #{idx1}, idx2 #{idx2}, px[idx1] #{px[idx1]}, py[idx1] #{py[idx1]}, px[idx2] #{px[idx2]}, py[idy2] #{py[idx2]}"
            keepidx.push( idx1 )
            removeidx.push( idx2 )
         end
      end
   end
   return removeidx, keepidx
end
def printlines( ilines, owner, neighbour, bno)
   (0..4).each do |idx|
      puts "owner#{idx} #{owner[idx].join(", ")}" if owner[idx] != nil
      puts "neighbour#{idx} #{neighbour[idx].join(", ")}" if neighbour[idx] != nil
      puts "bno#{idx} #{bno[idx].join(", ")}" if bno[idx] != nil
      #puts "lines #{ilines.join(", ")}"
      string = "lines#{idx} "
      ilines[idx].each { |face| string += "[#{face.join(",")}] " if face != nil } if ilines[idx] != nil
      string += "\n"
      puts string
   end
end
def printcells(celldef, newrow )
   puts "cell printout, size #{celldef.size}"
   celldef.each_with_index do |cell, idx|
      print "[ #{cell[0]}, #{cell[1]}, #{cell[2]}, #{cell[3]} ], "
      print "\n" if (idx+1.0)%newrow.to_f == 0.0 
   end
   print "\n"
end
#
# example usage
#

# face 1
# 24 points, breakpoint 3
celldef1 = makecells( 24, 3 )
f1x = [ 0.0, 1.0, 2.0, 0.0, 1.0, 2.0, 0.0, 1.0, 2.0 ,0.0, 1.0, 2.0 ,0.0, 1.0, 2.0, 0.0, 1.0, 2.0, 0.0, 1.0, 2.0, 0.0, 1.0, 2.0 ] 
f1y = [ 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, 4.0, 4.0, 4.0, 5.0, 5.0, 5.0, 6.0, 6.0, 6.0, 7.0, 7.0, 7.0 ]

# face 2
# 6 points, breakpoint 3
celldef2 = makecells( 6, 3 )
f2x = [ 2.0, 3.0, 4.0, 2.0, 3.0, 4.0 ]
f2y = [ 0.0, 0.0, 0.0, 1.0, 1.0, 1.0 ]
celldef3 = makecells( 6, 3 )
f3x, f3y = translate( f2x, f2y, 0.0, 3.0 )

# possible usage of export class
export = MeshExport.new
# breakcell 2 = breakpoint 3: point1 -- cell1 -- point2 -- cell2 -- point3
export.add( celldef1, 2, f1x, f1y)
export.add( celldef2, 2, f2x, f2y)
export.add( celldef3, 2, f3x, f3y)
export.export