/* 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;
import dwlab.base.service.Service;
import dwlab.base.service.Vector;
import static dwlab.platform.Functions.*;
import dwlab.shapes.sprites.Camera;
import dwlab.shapes.sprites.Sprite;
import dwlab.visualizers.Color;
/**
* Line is represented by A B and C values in Ax + Bx + C = 0 equation.
*/
public class Line extends Shape {
public double a, b, c;
public double s, s2;
public static final Line serviceLine = new Line();
public Line( double x1, double y1, double x2, double y2 ) {
usePoints( x1, y1, x2, y2 );
}
public Line() {
a = 1.0;
}
public Line( Shape pivot1, Shape pivot2 ) {
usePivots( pivot1, pivot2 );
}
public final void usePoints( double x1, double y1, double x2, double y2 ) {
if( debug ) if( x1 == x2 && y1 == y2 ) error( "Line cannot be formed from two equal points" );
a = y2 - y1;
b = x1 - x2;
c = -a * x1 - b * y1;
calculateS();
}
public final void usePivots( Shape pivot1, Shape pivot2 ) {
if( debug ) if( pivot1.x == pivot2.x && pivot1.y == pivot2.y ) error( "Line cannot be formed from two equal pivots" );
a = pivot2.y - pivot1.y;
b = pivot1.x - pivot2.x;
c = -a * pivot1.x - b * pivot1.y;
calculateS();
}
public void calculateS() {
s2 = a * a + b * b;
s = Math.sqrt( s2 );
}
public double getX( double y ) {
return ( -b * y - c ) / a;
}
public double getY( double x ) {
return ( -a * x - c ) / b;
}
@Override
public double distanceTo( Shape shape ) {
return Math.abs( a * shape.x + b * shape.y ) / s;
}
@Override
public double distanceTo( double pointX, double pointY ) {
return Math.abs( a * pointX + b * pointY ) / s;
}
public Sprite pivotProjection( Sprite pivot, Sprite projection ) {
projection.y = ( ( a * pivot.y - b * pivot.x ) * a - c * b ) / s2;
projection.x = ( -c - b * projection.y ) / a;
return projection;
}
public Sprite pivotProjection( Sprite pivot ) {
return pivotProjection( pivot, new Sprite() );
}
public Sprite intersectionWithLine( Line line, Sprite pivot ) {
double k = b * line.a - a * line.b;
if( k == 0.0 ) return null;
pivot.y = ( line.c * a - c * line.a ) / k;
pivot.x = ( c - b * pivot.y ) / a;
return pivot;
}
public Sprite intersectionWithLine( Line line ) {
return intersectionWithLine( line, new Sprite() );
}
public Sprite intersectionWithLineSegment( Sprite lSPivot1, Sprite lSPivot2, Sprite pivot ) {
if( pivotOrientation( lSPivot1 ) != pivotOrientation( lSPivot2 ) ) {
serviceLine.usePivots( lSPivot1, lSPivot2 );
return intersectionWithLine( serviceLine, pivot );
}
return null;
}
public Sprite intersectionWithLineSegment( Sprite lSPivot1, Sprite lSPivot2 ) {
return intersectionWithLineSegment( lSPivot1, lSPivot2, new Sprite() );
}
public int pointOrientation( double x, double y ) {
return Service.signum( a * x + b * y + c );
}
public int pivotOrientation( Shape pivot ) {
return Service.signum( a * pivot.x + b * pivot.y + c );
}
public boolean collisionPointsWithCircle( Sprite circle, Sprite pivot1, Sprite pivot2 ) {
double d = a * circle.x + b * circle.y + c;
double k = 0.25 * circle.width * circle.width * s2 - d * d;
if( k < 0 ) return false;
k = Math.sqrt( k ) * a;
pivot1.y = ( -b * d - k ) / s2 + circle.y;
pivot1.x = getX( pivot1.y );
pivot2.y = ( -b * d + k ) / s2 + circle.y;
pivot2.x = getX( pivot2.y );
return true;
}
private static Vector serviceVector1 = new Vector();
private static Vector serviceVector2 = new Vector();
private static Vector serviceVector3 = new Vector();
private static Vector serviceVector4 = new Vector();
@Override
public void draw( Color drawingColor ) {
Camera.current.screenToField( Camera.current.viewport.leftX(), Camera.current.viewport.topY(), serviceVector1 );
Camera.current.screenToField( Camera.current.viewport.rightX(), Camera.current.viewport.bottomY(), serviceVector2 );
if( Math.abs( a ) <= Math.abs( b ) ) {
Camera.current.fieldToScreen( serviceVector1.x, ( -a * serviceVector1.x - c ) / b, serviceVector3 );
Camera.current.fieldToScreen( serviceVector2.x, ( -a * serviceVector2.x - c ) / b, serviceVector4 );
} else {
Camera.current.fieldToScreen( ( -b * serviceVector1.y - c ) / a, serviceVector1.y, serviceVector3 );
Camera.current.fieldToScreen( ( -b * serviceVector2.y - c ) / a, serviceVector2.y, serviceVector4 );
}
drawLine( serviceVector3.x, serviceVector3.y, serviceVector4.x, serviceVector4.y, lineWidth, drawingColor );
}
}