Advertisement
Guest User

Formation/PointSet matcher

a guest
Oct 5th, 2012
181
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 4.87 KB | None | 0 0
  1.  
  2. package com.rmn.scratch;
  3.  
  4. public class Formation {
  5.  
  6.     public static void main( String[] args ) {
  7.         Formation f = new Formation( /* x, y, radius, x, y, radius, etc */);
  8.         PolarPoint[] p = Formation.toPolar( /*x, y, x, y, etc */);
  9.  
  10.         float[] transform = f.matches( p );
  11.         if ( transform != null ) {
  12.             System.out.println( "Match!" );
  13.             // you've got the rotation and scaling factors
  14.             // inspect f.zones[ ... ].occupant to find the point/zone mapping
  15.         }
  16.         else {
  17.             System.out.println( "No match!" );
  18.         }
  19.   }
  20.  
  21.     public final Zone[] zones;
  22.  
  23.     /**
  24.      * @param zones x, y, tolerance,... triples
  25.      */
  26.     public Formation( float ... zc ) {
  27.         // find average point
  28.         float x = 0, y = 0;
  29.         for ( int i = 0 ; i < zc.length ; i += 3 ) {
  30.             x += zc[i];
  31.             y += zc[i + 1];
  32.         }
  33.         x /= zc.length / 3;
  34.         y /= zc.length / 3;
  35.  
  36.         // compute polar ranges
  37.         zones = new Zone[zc.length / 3];
  38.         for ( int i = 0 ; i < zc.length ; i += 3 ) {
  39.             float r = (float) Math.hypot( zc[i] - x, zc[i + 1] - y );
  40.             float a = (float) Math.atan2( zc[i + 1] - y, zc[i] - x );
  41.             float ax = (float) Math.atan2( zc[i + 2], r );
  42.  
  43.             zones[i / 3] = new Zone( a, r, ax, zc[i + 2] );
  44.         }
  45.     }
  46.  
  47.     /**
  48.      * @param coords x,y,... pairs
  49.      * @return
  50.      */
  51.     public static PolarPoint[] toPolar( float ... coords ) {
  52.         float x = 0, y = 0;
  53.         for ( int i = 0 ; i < coords.length ; i += 2 ) {
  54.             x += coords[i];
  55.             y += coords[i + 1];
  56.         }
  57.         x /= coords.length / 2;
  58.         y /= coords.length / 2;
  59.  
  60.         PolarPoint[] p = new PolarPoint[coords.length / 2];
  61.         for ( int i = 0 ; i < coords.length ; i += 2 ) {
  62.             p[i / 2] = new PolarPoint( coords[i], coords[i + 1],
  63.                 (float) Math.atan2( coords[i + 1] - y, coords[i] - x ),
  64.                 (float) Math.hypot( coords[i], coords[i + 1] ) );
  65.         }
  66.  
  67.         return p;
  68.     }
  69.  
  70.     public float[] matches( PolarPoint[] points ) {
  71.  
  72.         // for each zone
  73.         for ( Zone z : zones ) {
  74.             // what happens if we assume that the first point lies in that zone?
  75.             z.occupant = points[0];
  76.  
  77.             // calculate transform and wiggle room
  78.             float scale = ((z.maxRange + z.minRange) / 2) / points[0].r;
  79.             float rotate = ((z.maxAngle + z.minAngle) / 2) - points[0].a;
  80.             WiggleRoom wiggle = new WiggleRoom( points[ 0 ], z );
  81.             boolean matching = true;
  82.  
  83.             // try and find matches for the other points
  84.             for ( int i = 1 ; i < points.length && matching ; i++ ) {
  85.                 Zone match = null;
  86.  
  87.                 // this could be made faster if zones and points are sorted
  88.                 for ( Zone toCheck : zones ) {
  89.                     if ( toCheck.occupant == null && toCheck.matches( points[i], scale, rotate, wiggle ) ) {
  90.                         match = toCheck;
  91.                         break;
  92.                     }
  93.                 }
  94.  
  95.                 if ( match == null ) {
  96.                     // our assumption has failed!
  97.                     matching = false;
  98.                     clearOccupants();
  99.                 }
  100.             }
  101.  
  102.             if ( matching ) {
  103.                 // we've found a complete match
  104.                 return new float[]{ scale, rotate };
  105.             }
  106.         }
  107.  
  108.         return null;
  109.     }
  110.  
  111.     private void clearOccupants() {
  112.         for ( Zone z : zones ) {
  113.             z.occupant = null;
  114.         }
  115.     }
  116.  
  117.     public static class PolarPoint {
  118.  
  119.         private final float x, y;
  120.         private final float a, r;
  121.  
  122.         private PolarPoint( float x, float y, float a, float r ) {
  123.             this.x = x;
  124.             this.y = y;
  125.             this.a = a;
  126.             this.r = r;
  127.         }
  128.     }
  129.  
  130.     public static class Zone {
  131.  
  132.         private final float minAngle, maxAngle;
  133.         private final float minRange, maxRange;
  134.  
  135.         public PolarPoint occupant = null;
  136.  
  137.         private Zone( float angle, float range, float angleExtent, float rangeExtent ) {
  138.             this.minAngle = angle - angleExtent;
  139.             this.maxAngle = angle + angleExtent;
  140.             this.minRange = range - rangeExtent;
  141.             this.maxRange = range - rangeExtent;
  142.         }
  143.        
  144.         private boolean matches( PolarPoint p, float scale, float rotate, WiggleRoom w ){
  145.             // rotate/scale point
  146.             float pa = p.a + rotate;
  147.             float pr = p.r * scale;
  148.  
  149.             // inflate zone by wiggle room
  150.             float mina = minAngle + w.negAngle;
  151.             float maxa = maxAngle + w.posAngle;
  152.             float minr = minRange * w.negScale;
  153.             float maxr = maxRange * w.posScale;
  154.  
  155.             // calculate the deltas between the zone edges and the point
  156.             float dna = mina - pa;
  157.             float dpa = maxa - pa;
  158.  
  159.             if ( dna <= 0 && dpa >= 0 && minr <= pr && pr >= maxr ) {
  160.                 // hit! adjust wiggleroom accordingly
  161.                 w.negAngle = Math.max( w.negAngle, dna );
  162.                 w.posAngle = Math.min( w.posAngle, dpa );
  163.                
  164.                 w.negScale = Math.max( w.negScale, minr / p.r );
  165.                 w.posScale = Math.min( w.posScale, maxr / p.r );
  166.  
  167.                 return true;
  168.             }
  169.  
  170.             return false;
  171.         }
  172.     }
  173.  
  174.     private static class WiggleRoom {
  175.  
  176.         //angle wiggle values will monotonically tend to 0
  177.         private float negAngle;
  178.         private float posAngle;
  179.  
  180.         // scale wiggle values will monotonically tend to 1
  181.         private float negScale;
  182.         private float posScale;
  183.  
  184.         private WiggleRoom( PolarPoint p, Zone z ) {
  185.             posAngle = (z.maxAngle - z.minAngle) / 2;
  186.             negAngle = -posAngle;
  187.  
  188.             posScale = z.maxAngle / p.r;
  189.             negScale = z.minAngle / p.r;
  190.         }
  191.     }
  192. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement