Package dwlab.shapes.sprites

Source Code of dwlab.shapes.sprites.Sprite$SpriteMapInserter

/* Digital Wizard's Lab - game development framework
* Copyright (C) 2013, Matt Merkulov
*
* All rights reserved. Use of this code is allowed under the
* Artistic License 2.0 terms, as specified in the license.txt
* file distributed with this code, or available from
* http://www.opensource.org/licenses/artistic-license-2.0.php
*/

package dwlab.shapes.sprites;

import dwlab.base.Obj;
import dwlab.base.Project;
import dwlab.base.XMLObject;
import dwlab.base.service.Service;
import dwlab.base.service.Vector;
import static dwlab.platform.Functions.*;
import dwlab.shapes.Line;
import dwlab.shapes.Shape;
import dwlab.shapes.layers.Layer;
import dwlab.shapes.line_segments.LineSegment;
import dwlab.shapes.line_segments.collision.CollisionWithLineSegment;
import dwlab.shapes.maps.SpriteMap;
import dwlab.shapes.maps.tilemaps.TileMap;
import dwlab.shapes.sprites.shape_types.ShapeType;
import dwlab.shapes.sprites.shape_types.collisions.SpritesCollision;
import dwlab.shapes.sprites.shape_types.drawing_shape.DrawingShape;
import dwlab.shapes.sprites.shape_types.overlapping.SpritesOverlapping;
import dwlab.shapes.sprites.shape_types.wedging_off.WedgingOffSprites;
import dwlab.visualizers.Color;
import dwlab.visualizers.Visualizer;
import java.util.Iterator;

/**
* Sprite is the main basic shape of the framework to draw, move and check collisions.
* @see #lTVectorSprite
*/
public class Sprite extends Shape {
  private static final Vector serviceVector = new Vector();
  private static final Sprite serviceSprite = new Sprite();

 
  /**
   * Type of the sprite shape.
   * @see #pivot, #oval, #rectangle
   */
  public ShapeType shapeType = ShapeType.rectangle;


  /**
   * Direction of the sprite
   * @see #moveForward, #moveTowards
   */
  public double angle;

  /**
   * Angle of displaying image.
   * Displaying angle is relative to sprite's direction if visualizer's rotating flag is set to True.
   */
  public double displayingAngle;

  /**
   * Velocity of the sprite in units per second.
   * @see #moveForward, #moveTowards
   */
  public double velocity;

  /**
   * Frame of the sprite image.
   * Can only be used with visualizer which have images.
   */
  public int frame;

  public SpriteMap spriteMap;

  public static int checkNum = 0;
  public int checkValue = 0;

  @Override
  public String getClassTitle() {
    return "Sprite";
  }

  // ==================== Creating =================== 

  public Sprite() {
    this.shapeType = ShapeType.pivot;
  }

  /**
   * Creates sprite using given shape type.
   * @return Created sprite.
   */
  public Sprite( ShapeType shapeType ) {
    this.shapeType = shapeType;
    this.x = 0d;
    this.y = 0d;
    if( shapeType == ShapeType.pivot ) {
      this.width = 0d;
      this.height = 0d;
    } else {
      this.width = 1d;
      this.height = 1d;
    }
  }
 
  public Sprite( double x, double y ) {
    this.x = x;
    this.y = y;
    this.shapeType = ShapeType.pivot;
  }

  public Sprite( double x, double y, double radius ) {
    this.x = x;
    this.y = y;
    this.width = radius;
    this.height = radius;
    this.shapeType = ShapeType.oval;
  }

  public Sprite( double x, double y, double width, double height ) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    this.shapeType = ShapeType.rectangle;
  }

  public Sprite( ShapeType shapeType, double width, double height ) {
    this.width = width;
    this.height = height;
    this.shapeType = shapeType;
  }
 
  /**
   * Creates sprite using given coordinates, size, shape type, angle and velocity.
   * @return Created sprite.
   * See also #overlaps example.
   */
  public Sprite( ShapeType shapeType, double x, double y, double width, double height ) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    this.shapeType = shapeType;
  }
 
  public Sprite( ShapeType shapeType, double x, double y, double width, double height, double angle, double velocity ) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    this.shapeType = shapeType;
    this.angle = angle;
    this.velocity = velocity;
  }

  public Sprite( Shape shape ) {
    this.x = shape.getX();
    this.y = shape.getY();
    this.width = shape.getWidth();
    this.height = shape.getHeight();
  }
 
  // ==================== Drawing =================== 

  @Override
  public void draw( Color drawingColor ) {
    visualizer.drawUsingSprite( this, this, drawingColor );
  }


  @Override
  public void drawUsingVisualizer( Visualizer vis, Color drawingColor ) {
    vis.drawUsingSprite( this, this, drawingColor );
  }
 

  public void drawShape( Color drawingColor, boolean contour ) {
    DrawingShape.handlers[ shapeType.getNum() ].perform( this, drawingColor, contour );
  }

  // ==================== Collisions ===================


  /**
   * Checks if this sprite collides with given sprite.
   * @return True if the sprite collides with given sprite, False otherwise.
   */
  public boolean collidesWith( Sprite sprite ) {
    if( debug ) Project.collisionChecks += 1;
    int num1 = shapeType.getNum();
    int num2 = sprite.shapeType.getNum();
    if( num1 <= num2 ) {
      return SpritesCollision.handlers[ num1 ][ num2 ].check( this, sprite );
    } else {
      return SpritesCollision.handlers[ num2 ][ num1 ].check( sprite, this );
    }
  }


  /**
   * Checks if the sprite collides with given line.
   * @return True if the sprite collides with given line, otherwise false.
   * Only collision of line and Oval is yet implemented.
   */
  public boolean collidesWith( LineSegment lineSegment ) {
    if( debug ) Project.collisionChecks += 1;
    return CollisionWithLineSegment.handlers[ shapeType.getNum() ].check( this, lineSegment );
  }


  /**
   * Checks if the sprite overlaps given sprite.
   * @return True if the sprite overlaps given sprite, otherwise false.
   * Pivot overlapping is not supported.
   */
  public boolean overlaps( Sprite sprite ) {
    if( debug ) Project.collisionChecks += 1;
    return SpritesOverlapping.handlers[ shapeType.getNum() ][ sprite.shapeType.getNum() ].check( this, sprite );
  }


  /**
   * Searches the layer for first sprite which collides with given.
   * @return First found sprite which collides with given.
   * Included layers will be also checked.
   *
   * @see #clone example
   */ 
  public Sprite lastCollidedSpriteOf( Layer layer ) {
    for ( Iterator<Shape> it = layer.children.descendingIterator(); it.hasNext(); ) {
      Shape shape = it.next();
      if( shape != this ) {
        Sprite collided = shape.layerLastSpriteCollision( this );
        if( collided != null ) return collided;
      }
    }
    return null;
  }


  @Override
  public Sprite layerLastSpriteCollision( Sprite sprite ) {
    if( collidesWith( sprite ) ) return this; else return null;
  }


  /**
   * Executes given collision handler for collision of sprite with given sprite.
   * If sprites collide then HandleCollisionWithSprite() method will be executed and given sprite will be passed to this method.
   * You can specify collision type which will be passed to this method too.
   *
   * @see #collisionsWithLayer, #collisionsWithTileMap, #collisionsWithLine, #collisionsWithSpriteMap, #horizontal, #vertical
   */
  public void collisionsWith( Sprite sprite, SpriteCollisionHandler handler ) {
    if( collidesWith( sprite ) ) handler.handleCollision( this, sprite );
  }


  /**
   * Executes given collision handler for collision of sprite with shapes in given group.
   * For every collided shape collision handling method will be executed and corresponding parameters will be passed to this method.
   * You can specify collision type which will be passed to this method too.
   *
   * @see #collisionsWithSprite, #collisionsWithTileMap, #collisionsWithLine, #collisionsWithSpriteMap, #horizontal, #vertical
   */
  public void collisionsWith( Layer layer, SpriteCollisionHandler handler ) {
    for( Shape shape: layer.children ) {
      if( shape != this ) shape.spriteLayerCollisions( this, handler );
    }
  }


  @Override
  public void spriteLayerCollisions( Sprite sprite, SpriteCollisionHandler handler ) {
    if( sprite.collidesWith( this ) ) handler.handleCollision( sprite, this );
  }


  /**
   * Executes given collision handler for collision of sprite with given line.
   * If sprite collides with line then HandleCollisionWithLine() method will be executed and line will be passed to this method.
   * You can specify collision type which will be passed to this method too.
   *
   * @see #collisionsWithLayer, #collisionsWithSprite, #collisionsWithTileMap, #collisionsWithSpriteMap, #horizontal, #vertical
   */
  public void collisionsWith( LineSegment lineSegment, SpriteAndLineSegmentCollisionHandler handler ) {
    if( collidesWith( lineSegment ) ) handler.handleCollision( this, lineSegment );
  }


  public static final double smallNum = 0.00000000001;
 
  /**
   * Executes given collision handler for collision of sprite with tiles in given tilemap.
   * For every collided tile HandleCollisionWithTile() method will be executed and tilemap with tile indexes will be passed to this method.
   * You can specify collision type which will be passed to this method too.
   *
   * @see #collisionsWithLayer, #collisionsWithSprite, #collisionsWithLine, #collisionsWithSpriteMap, #horizontal, #vertical, #lTVectorSprite example
   */
  public void collisionsWith( TileMap tileMap, SpriteAndTileCollisionHandler handler, int[] tileNums ) {
    double x0 = tileMap.leftX();
    double y0 = tileMap.topY();
    double cellWidth = tileMap.getTileWidth();
    double cellHeight = tileMap.getTileHeight();
    int xQuantity = tileMap.xQuantity;
    int yQuantity = tileMap.yQuantity;
    Sprite[][] collisionSprites = tileMap.tileSet.collisionSprites;

    if( shapeType == ShapeType.pivot ) {
      int tileX = Service.floor( ( x - x0 ) / cellWidth );
      int tileY = Service.floor( ( y - y0 ) / cellHeight );

      if( tileX >= 0 && tileY >= 0 && tileX < xQuantity && tileY < yQuantity ) {
        int tileNum = tileMap.value[ tileY ][ tileX ];
        Sprite[] spriteArray = collisionSprites[ tileNum ];
        if( spriteArray != null ) {
          if( !Service.inArray( tileNums, tileNum ) ) return;
          for( int n = 0; n < spriteArray.length; n++ ) {
            spriteArray[ n ].transformFrom( x0, y0, cellWidth, cellHeight, tileX, tileY );
            if( collidesWith( serviceSprite ) ) handler.handleCollision( this, tileMap, tileX, tileY, serviceSprite );
          }
        }
      }
    } else if( shapeType != ShapeType.ray ) {
      int x1 = Service.floor( ( x - 0.5d * width - x0 ) / cellWidth );
      int y1 = Service.floor( ( y - 0.5d * height - y0 ) / cellHeight );
      int x2 = Service.floor( ( x + 0.5d * width - x0 - smallNum ) / cellWidth );
      int y2 = Service.floor( ( y + 0.5d * height - y0 - smallNum ) / cellHeight );

      if( x2 >= 0 && y2 >= 0 && x1 < xQuantity && y1 < yQuantity ) {
        x1 = Service.limit( x1, 0, xQuantity - 1 );
        y1 = Service.limit( y1, 0, yQuantity - 1 );
        x2 = Service.limit( x2, 0, xQuantity - 1 );
        y2 = Service.limit( y2, 0, yQuantity - 1 );

        for( int tileY = y1; tileY <= y2; tileY++ ) {
          for( int tileX = x1; tileX <= x2; tileX++ ) {
            int tileNum = tileMap.value[ tileY ][ tileX ];
            Sprite[] spriteArray = collisionSprites[ tileNum ];
            if( spriteArray != null ) {
              if( !Service.inArray( tileNums, tileNum ) ) continue;
              for( int n = 0; n < spriteArray.length; n++ ) {
                spriteArray[ n ].transformFrom( x0, y0, cellWidth, cellHeight, tileX, tileY );
                if( collidesWith( serviceSprite ) ) handler.handleCollision( this, tileMap, tileX, tileY, serviceSprite );
              }
            }
          }
        }
      }
    }
  }
 
  public void collisionsWith( TileMap tileMap, SpriteAndTileCollisionHandler handler ) {
    collisionsWith( tileMap, handler, null );
  }


  /**
   * Executes reaction for collision of sprite with sprites in sprite map.
   * For every collided sprite HandleCollisionWithSprite() method will be executed and collided srite will be passed to this method.
   * You can specify collision type which will be passed to this method too.
   * Map parameter allows you to specify map to where collided sprites will be added as keys.
   *
   * @see #collisionsWithGroup, #collisionsWithSprite, #collisionsWithTileMap, #collisionsWithLine, #horizontal, #vertical, #lTSpriteMap example
   */
  public void collisionsWith( SpriteMap spriteMap, SpriteCollisionHandler handler ) {
    checkNum++;
    if( shapeType == ShapeType.pivot ) {
      int wrappedCellX = ( (int) Service.floor( x / spriteMap.cellWidth ) ) & spriteMap.xMask;
      int wrappedCellY = ( (int) Service.floor( y / spriteMap.cellHeight ) ) & spriteMap.yMask;
      Sprite[] sprites = spriteMap.lists[ wrappedCellY ][ wrappedCellX ];
      int quantity = spriteMap.listSize[ wrappedCellY ][ wrappedCellX ];
      for( int n = 0; n < quantity; n++ ) {
        Sprite mapSprite = sprites[ n ];
        if( this == mapSprite ) continue;
        if( collidesWith( mapSprite ) ) {
          if( mapSprite.checkValue != checkNum ) {
            mapSprite.checkValue = checkNum;
            handler.handleCollision( this, mapSprite );
          }
        }
      }
    } else if( shapeType != ShapeType.ray ) {
      int mapX1 = Service.floor( ( x - 0.5d * width ) / spriteMap.cellWidth );
      int mapY1 = Service.floor( ( y - 0.5d * height ) / spriteMap.cellHeight );
      int mapX2 = Service.floor( ( x + 0.5d * width - smallNum ) / spriteMap.cellWidth );
      int mapY2 = Service.floor( ( y + 0.5d * height - smallNum ) / spriteMap.cellHeight );

      for( int cellY = mapY1; cellY <= mapY2; cellY++ ) {
        int wrappedCellY = cellY & spriteMap.yMask;
        for( int cellX = mapX1; cellX <= mapX2; cellX++ ) {
          int wrappedCellX = cellX & spriteMap.xMask;
          Sprite[] sprites = spriteMap.lists[ wrappedCellY ][ wrappedCellX ];
          int quantity = spriteMap.listSize[ wrappedCellY ][ wrappedCellX ];
          for( int n = 0; n < quantity; n++ ) {
            Sprite mapSprite = sprites[ n ];
            if( this == mapSprite ) continue;
            if( collidesWith( mapSprite ) ) {
              if( mapSprite.checkValue != checkNum ) {
                mapSprite.checkValue = checkNum;
                handler.handleCollision( this, mapSprite );
              }
            }
          }
        }
      }
    }
  }

  // ==================== Wedging off ====================

  /**
   * Wedges off sprite with given sprite.
   * Pushes sprites from each other until they stops colliding. More the moving resistance, less the sprite will be moved.
   * <ul>
   * <li> If each sprite's moving resistance is zero, or each sprite's moving resistance is less than 0 then sprites will be moved on same distance.
   * <li> If one of the sprite has zero moving resistance and other's moving resistance is non-zero, only zero-moving-resistance sprite will be moved
   * <li> If one of the sprite has moving resistance less than 0 and other has moving resistance more or equal to 0, then only zero-or-more-moving-resistance sprite will be moved.
   * </ul>
   */
  public void wedgeOffWith( Sprite sprite, double selfMovingResistance, double spriteMovingResistance ) {
    int num1 = shapeType.getNum();
    int num2 = sprite.shapeType.getNum();
    if( num1 <= num2 ) {
      WedgingOffSprites.handlers[ num1 ][ num2 ].calculateVector( this, sprite, serviceVector );
      WedgingOffSprites.separate( this, sprite, serviceVector, selfMovingResistance, spriteMovingResistance );
    } else {
      WedgingOffSprites.handlers[ num2 ][ num1 ].calculateVector( sprite, this, serviceVector );
      WedgingOffSprites.separate( sprite, this, serviceVector, spriteMovingResistance, selfMovingResistance );
    }
  }
 
  public void wedgeOffWith( Sprite sprite ) {
    wedgeOffWith( sprite, 0.5d, 0.5d );
  }


  /**
   * Pushes sprite from given sprite.
   * See also : #wedgeOffWithSprite, #pushFromTile
   */
  public void pushFrom( Sprite sprite ) {
    wedgeOffWith( sprite, 0.0, 1.0 );
  }


  /**
   * Pushes sprite from given tile.
   * See also : #pushFromSprite
   */
  public void pushFrom( TileMap tileMap, int tileX, int tileY ) {
    double x0 = tileMap.leftX();
    double y0 = tileMap.topY();
    double cellWidth = tileMap.getTileWidth();
    double cellHeight = tileMap.getTileHeight();
    Sprite[] spriteArray = tileMap.tileSet.collisionSprites[ tileMap.value[ tileY ][ tileX ] ];
    if( spriteArray != null ) {
      for( int n = 0; n < spriteArray.length; n++ ) {
        spriteArray[ n ].transformFrom( x0, y0, cellWidth, cellHeight, tileX, tileY );
        pushFrom( serviceSprite );
      }
    }
  }


  /**
   * Forces sprite to bounce off the inner bounds of the shape.
   * @see #active example
   */
  public Sprite bounceInside( Sprite sprite, boolean leftSide, boolean topSide, boolean rightSide, boolean bottomSide, SpriteCollisionHandler handler ) {
    if( leftSide ) {
      if( leftX() < sprite.leftX() ) {
        x = sprite.leftX() + 0.5 * width;
        angle = 180 - angle;
        if( handler != null ) handler.handleCollision( this, sprite );
      }
    }
    if( topSide ) {
      if( topY() < sprite.topY() ) {
        y = sprite.topY() + 0.5 * height;
        angle = -angle;
        if( handler != null ) handler.handleCollision( this, sprite );
      }
    }
    if( rightSide ) {
      if( rightX() > sprite.rightX() ) {
        x = sprite.rightX() - 0.5 * width;
        angle = 180 - angle;
        if( handler != null ) handler.handleCollision( this, sprite );
      }
    }
    if( bottomSide ) {
      if( bottomY() > sprite.bottomY() ) {
        y = sprite.bottomY() - 0.5 * height;
        angle = -angle;
        if( handler != null ) handler.handleCollision( this, sprite );
      }
    }
    return this;
  }
 
  public Sprite bounceInside( Sprite sprite, SpriteCollisionHandler handler ) {
    bounceInside( sprite, true, true, true, true, handler );
    return this;
  }
 
  public Sprite bounceInside( Sprite sprite ) {
    bounceInside( sprite, true, true, true, true, null );
    return this;
  }

  // ==================== Position and size ====================

  @Override
  public Shape setCoords( double newX, double newY ) {
    if( spriteMap != null ) spriteMap.removeSprite( this, false, false );

    x = newX;
    y = newY;

    update();
    if( spriteMap != null ) spriteMap.insertSprite( this, false, false );
    return this;
  }


  @Override
  public Shape setCoordsAndSize( double x1, double y1, double x2, double y2 ) {
    if( spriteMap != null ) spriteMap.removeSprite( this, false, false );

    x = 0.5d * ( x1 + x2 );
    y = 0.5d * ( y1 + y2 );
    width = x2 - x1;
    height = y2 - y1;

    update();
    if( spriteMap != null ) spriteMap.insertSprite( this, false, false );
    return this;
  }


  /**
   * Moves sprite forward.
   * @see #move, #moveBackward, #turn example
   */
  public Sprite moveForward() {
    setCoords( x + Math.cos( angle ) * velocity * Project.deltaTime, y + Math.sin( angle ) * velocity * Project.deltaTime );
    return this;
  }


  /**
   * Moves sprite backward.
   * @see #move, #moveForward, #turn example
   */
  public Sprite moveBackward() {
    setCoords( x - Math.cos( angle ) * velocity * Project.deltaTime, y - Math.sin( angle ) * velocity * Project.deltaTime );
    return this;
  }


  @Override
  public Shape setSize( double newWidth, double newHeight ) {
    if( spriteMap != null ) spriteMap.removeSprite( this, false, false );

    width = newWidth;
    height = newHeight;

    update();
    if( spriteMap != null ) spriteMap.insertSprite( this, false, false );
    return this;
  }


  /**
   * Sets the sprite as a tile.
   * Position, size, visualizer and frame will be changed. This method can be used to cover other shapes with the tile or voluntary moving the tile.
   *
   * @see #getTileForPoint example
   */
  public Sprite setAsTile( TileMap tileMap, int tileX, int tileY ) {
    width = tileMap.getTileWidth();
    height = tileMap.getTileHeight();
    x = tileMap.leftX() + width * ( 0.5 + tileX );
    y = tileMap.topY() + height * ( 0.5 + tileY );
    visualizer = tileMap.visualizer.clone();
    visualizer.image = tileMap.tileSet.image;
    frame = tileMap.getTile( tileX, tileY );
    return this;
  }

  // ==================== Limiting ====================

  @Override
  public Shape limitLeftWith( Shape rectangle, SpriteCollisionHandler handler ) {
    double rectLeftX = rectangle.leftX();
    if( leftX() < rectLeftX ) {
      setX( rectLeftX + 0.5 * width );
      if( handler != null ) handler.handleCollision( this, null );
    }
    return this;
  }


  @Override
  public Shape limitTopWith( Shape rectangle, SpriteCollisionHandler handler ) {
    double rectTopY = rectangle.topY();
    if( topY() < rectTopY ) {
      setY( rectTopY + 0.5 * height );
      if( handler != null ) handler.handleCollision( this, null );
    }
    return this;
  }


  @Override
  public Shape limitRightWith( Shape rectangle, SpriteCollisionHandler handler ) {
    double rectRightX = rectangle.rightX();
    if( rightX() > rectRightX ) {
      setX( rectRightX - 0.5 * width );
      if( handler != null ) handler.handleCollision( this, null );
    }
    return this;
  }


  @Override
  public Shape limitBottomWith( Shape rectangle, SpriteCollisionHandler handler ) {
    double rectBottomY = rectangle.bottomY();
    if( bottomY() > rectBottomY ) {
      setY( rectBottomY - 0.5 * height );
      if( handler != null ) handler.handleCollision( this, null );
    }
    return this;
  }

  // ==================== Angle ====================

  /**
   * Directs sprite as given angular sprite.
   * @see #directTo
   */
  public Sprite directAs( Sprite sprite ) {
    angle = sprite.angle;
    return this;
  }


  /**
   * Turns the sprite.
   * Turns the sprite with given speed per second.
   */
  public Sprite turn( double turningSpeed ) {
    angle += Project.deltaTime * turningSpeed;
    return this;
  }


  /**
   * Direct the sprite to center of the given shape.
   * @see #directAs
   */
  public Sprite directTo( Shape shape ) {
    angle = Math.atan2( shape.getY() - y, shape.getX() - x );
    return this;
  }


  public Sprite reverseDirection() {
    angle = angle + 180;
    return this;
  }


  /**
   * Alters angle by given value.
   * @see #clone example
   */
  public Sprite alterAngle( double dAngle ) {
    angle += dAngle;
    return this;
  }

  // ==================== Animation ====================

  /**
   * Animates the sprite.
   */
  public Sprite animate( double speed, int framesQuantity, int frameStart, double startingTime, boolean pingPong ) {
    if( framesQuantity == 0 ) framesQuantity = visualizer.getImage().framesQuantity();
    int modFactor = framesQuantity;
    if( pingPong ) modFactor = framesQuantity * 2 - 2;
    frame =  ( int ) Math.floor( ( Project.current.time - startingTime ) / speed ) % modFactor;
    if( pingPong && frame >= framesQuantity ) frame = modFactor - frame;
    frame += frameStart;
    return this;
  }

  // ==================== Methods for oval ==================== 

  public Sprite toCircle( Sprite pivot1, Sprite circleSprite ) {
    if( width == height ) return this;
    if( circleSprite !=null ) circleSprite = new Sprite( ShapeType.oval );
    if( width > height ) {
      circleSprite.x = Service.limit( pivot1.getX(), x - 0.5 * ( width - height ), x + 0.5 * ( width - height ) );
      circleSprite.y = y;
      circleSprite.width = height;
      circleSprite.height = height;
    } else {
      circleSprite.x = x;
      circleSprite.y = Service.limit( pivot1.getY(), y - 0.5 * ( height - width ), y + 0.5 * ( height - width ) );
      circleSprite.width = width;
      circleSprite.height = width;
    }
    return circleSprite;
  }


  public Sprite toCircleUsingLine( Line line, Sprite circleSprite ) {
    if( width == height ) return circleSprite;
    if( width > height ) {
      double dWidth = 0.5 * ( width - height );
      double o1 = line.a * ( x - dWidth ) + line.b * y + line.c;
      double o2 = line.a * ( x + dWidth ) + line.b * y + line.c;
      if( Math.signum( o1 ) != Math.signum( o2 ) ) {
        circleSprite.x = -( line.b * y + line.c ) / line.a;
      } else if( Math.abs( o1 ) < Math.abs( o2 ) ) {
        circleSprite.x = x - dWidth;
      } else {
        circleSprite.x = x + dWidth;
      }
      circleSprite.y = y;
    } else {
      double dHeight = 0.5 * ( height - width );
      double o1 = line.a * x + line.b * ( y - dHeight ) + line.c;
      double o2 = line.a * x + line.b * ( y + dHeight ) + line.c;
      if( Math.signum( o1 ) != Math.signum( o2 ) ) {
        circleSprite.y = -( line.a * x + line.c ) / line.b;
      } else if( Math.abs( o1 ) < Math.abs( o2 ) ) {
        circleSprite.y = y - dHeight;
      } else {
        circleSprite.y = y + dHeight;
      }
      circleSprite.x = x;
    }
    return circleSprite;
  }
 
  public Sprite toCircleUsingLine( Line line ) {
    return toCircleUsingLine( line, new Sprite( ShapeType.oval ) );
  }

  // ==================== Methods for ray ==================== 

  public void toLine( Line line ) {
    line.usePoints( x, y, x + Math.cos( angle ), y + Math.sin( angle ) );
  }
 
  public Line toLine() {
    Line line = new Line();
    toLine( line );
    return line;
  }


  public boolean hasPoint( double x1, double y1 ) {
    double ang = Service.wrap( angle, 360.0 );
    if( ang < 45.0 || ang >= 315.0 ) {
      return x1 >= x;
    } else if( ang < 135.0 ) {
      return y1 >= y;
    } else if( ang < 225.0 ) {
      return x1 <= x;
    } else {
      return y1 <= y;
    }
  }
 

  public boolean hasPivot( Sprite pivot ) {
    return hasPoint( pivot.x, pivot.y );
  }

  // ==================== Methods for triangle ==================== 

  public static void getMedium( Sprite pivot1, Sprite pivot2, Sprite medium ) {
    medium.setCoords( 0.5 * ( pivot1.x + pivot2.x ), 0.5 * ( pivot1.y + pivot2.y ) );
  }

  public static Sprite getMedium( Sprite pivot1, Sprite pivot2 ) {
    Sprite medium = new Sprite();
    getMedium( pivot1, pivot2, medium );
    return medium;
  }


  public void getHypotenuse( Line line ) {
    if( shapeType == ShapeType.topLeftTriangle || shapeType == ShapeType.bottomRightTriangle ) {
      line.usePoints( x, y, x - width, y + height );
    } else if( shapeType == ShapeType.topRightTriangle || shapeType == ShapeType.bottomLeftTriangle ) {
      line.usePoints( x, y, x + width, y + height );
    }
  }
 
  public Line getHypotenuse() {
    Line line = new Line();
    getHypotenuse( line );
    return line;
  }


  public void getRightAngleVertex( Sprite vertex ) {
    if( shapeType == ShapeType.topLeftTriangle || shapeType == ShapeType.bottomLeftTriangle ) {
      vertex.setX( x - 0.5 * width );
    } else if( shapeType == ShapeType.topRightTriangle || shapeType == ShapeType.bottomRightTriangle ) {
      vertex.setX( x + 0.5 * width );
    }
    if( shapeType == ShapeType.topLeftTriangle || shapeType == ShapeType.topRightTriangle ) {
      vertex.setY( y - 0.5 * height );
    } else if( shapeType == ShapeType.bottomLeftTriangle || shapeType == ShapeType.bottomRightTriangle ) {
      vertex.setY( y + 0.5 * height );
    }
  }
 
  public Sprite getRightAngleVertex() {
    Sprite vertex = new Sprite();
    getRightAngleVertex( vertex );
    return vertex;
  }


  public void getOtherVertices( Sprite pivot1, Sprite pivot2 ) {
    if( shapeType == ShapeType.topRightTriangle || shapeType == ShapeType.bottomLeftTriangle ) {
      getPivots( pivot1, null, pivot2, null );
    } else {
      getPivots( null, pivot1, null, pivot2 );
    }
  }

  // ==================== Cloning =================== 

  @Override
  public Sprite clone() {
    Sprite newSprite = new Sprite();
    copySpriteTo( newSprite );
    return newSprite;
  }


  public void copySpriteTo( Sprite sprite ) {
    copyShapeTo( sprite );

    sprite.shapeType = shapeType;
    sprite.angle = angle;
    sprite.displayingAngle = displayingAngle;
    sprite.velocity = velocity;
    sprite.frame = frame;
    sprite.updateFromAngularModel();
  }


  @Override
  public void copyTo( Shape shape ) {
    copySpriteTo( shape.toSprite() );
  }
 

  @Override
  public Sprite toSprite() {
    return this;
  }

  // ==================== Other ====================

  private class SpriteMapRemover extends Obj {
    SpriteMap map;
    Sprite sprite;
   
    @Override
    public void act() {
      map.removeSprite( sprite, true, true );
    }
  }
 
  public void removeFrom( SpriteMap map ) {
    SpriteMapRemover remover = new SpriteMapRemover();
    remover.map = map;
    remover.sprite = this;
    Project.managers.add( remover );
  }
 
 
  private class SpriteMapInserter extends Obj {
    SpriteMap map;
    Sprite sprite;
   
    @Override
    public void act() {
      map.insertSprite( sprite, true, true );
    }
  } 
 
  public void insertTo( SpriteMap map ) {
    SpriteMapInserter inserter = new SpriteMapInserter();
    inserter.map = map;
    inserter.sprite = this;
    Project.managers.add( inserter );
  }
 
 
  public void updateFromAngularModel() {
  }
 

  private void transformFrom( double x0, double y0, double cellWidth, double cellHeight, int tileX, int tileY ) {
    serviceSprite.setCoords( x0 + ( x + tileX ) * cellWidth, y0 + ( y + tileY ) * cellHeight );
    serviceSprite.setSize( width * cellWidth, height * cellHeight );
    serviceSprite.shapeType = shapeType;
  }


  @Override
  public void xMLIO( XMLObject xMLObject ) {
    super.xMLIO( xMLObject );

    if( XMLObject.xMLGetMode() ) {
      if( xMLObject.fieldExists( "shape-type" ) ) {
        shapeType = xMLObject.manageObjectField( "shape-type", shapeType );
      } else {
        shapeType = ShapeType.getByNum( xMLObject.getIntegerAttribute( "shape" ) );
      }
    } else if( shapeType.singleton() ) {
      xMLObject.setAttribute( "shape", String.valueOf( shapeType.getNum() ) );
    } else {
      shapeType = xMLObject.manageObjectField( "shape-type", shapeType );
    }
   
    angle = xMLObject.manageDoubleAttribute( "angle", angle );
    displayingAngle = xMLObject.manageDoubleAttribute( "disp_angle", displayingAngle );
    velocity = xMLObject.manageDoubleAttribute( "velocity", velocity, 1.0 );
    frame = xMLObject.manageIntAttribute( "frame", frame );
  }
}
TOP

Related Classes of dwlab.shapes.sprites.Sprite$SpriteMapInserter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.