001 package com.croftsoft.core.animation.updater;
002
003 import java.awt.Rectangle;
004 import java.awt.Shape;
005 import javax.swing.JComponent;
006
007 import com.croftsoft.core.animation.Clock;
008 import com.croftsoft.core.animation.ComponentUpdater;
009 import com.croftsoft.core.animation.Sprite;
010 import com.croftsoft.core.lang.NullArgumentException;
011 import com.croftsoft.core.math.MathConstants;
012
013 /*********************************************************************
014 * Bounces a Sprite off the walls of a Rectangle.
015 *
016 * @version
017 * 2003-07-11
018 * @since
019 * 2002-02-15
020 * @author
021 * <a href="https://www.croftsoft.com/">David Wallace Croft</a>
022 *********************************************************************/
023
024 public final class BounceUpdater
025 implements ComponentUpdater
026 //////////////////////////////////////////////////////////////////////
027 //////////////////////////////////////////////////////////////////////
028 {
029
030 private final Sprite sprite;
031
032 private final Rectangle bounds;
033
034 private final Clock clock;
035
036 private final Rectangle collisionBounds;
037
038 private final Rectangle newPaintBounds;
039
040 private final Rectangle oldPaintBounds;
041
042 //
043
044 private long lastUpdateTimeNanos;
045
046 //////////////////////////////////////////////////////////////////////
047 //////////////////////////////////////////////////////////////////////
048
049 public BounceUpdater (
050 Sprite sprite,
051 Rectangle bounds,
052 Clock clock )
053 //////////////////////////////////////////////////////////////////////
054 {
055 NullArgumentException.check ( this.sprite = sprite );
056
057 NullArgumentException.check ( this.bounds = bounds );
058
059 NullArgumentException.check ( this.clock = clock );
060
061 collisionBounds = new Rectangle ( );
062
063 newPaintBounds = new Rectangle ( );
064
065 oldPaintBounds = new Rectangle ( );
066 }
067
068 //////////////////////////////////////////////////////////////////////
069 //////////////////////////////////////////////////////////////////////
070
071 public void update ( JComponent component )
072 //////////////////////////////////////////////////////////////////////
073 {
074 long updateTimeNanos = clock.currentTimeNanos ( );
075
076 if ( updateTimeNanos == lastUpdateTimeNanos )
077 {
078 return;
079 }
080
081 double timeDelta
082 = ( updateTimeNanos - lastUpdateTimeNanos )
083 / ( double ) MathConstants.NANOSECONDS_PER_SECOND;
084
085 lastUpdateTimeNanos = updateTimeNanos;
086
087 double x = sprite.getX ( );
088
089 double y = sprite.getY ( );
090
091 double heading = sprite.getHeading ( );
092
093 double velocity = sprite.getVelocity ( );
094
095 double delta_x = Math.cos ( heading ) * velocity * timeDelta;
096
097 double delta_y = Math.sin ( heading ) * velocity * timeDelta;
098
099 x += delta_x;
100
101 y += delta_y;
102
103 int minX = bounds.x;
104
105 int minY = bounds.y;
106
107 sprite.getCollisionBounds ( collisionBounds );
108
109 int maxX = bounds.x + bounds.width - collisionBounds.width;
110
111 int maxY = bounds.y + bounds.height - collisionBounds.height;
112
113 boolean headingAlreadyAdjusted = false;
114
115 if ( x < minX )
116 {
117 x = minX;
118
119 if ( delta_x < 0.0 )
120 {
121 if ( heading > Math.PI )
122 {
123 heading = 3.0 * Math.PI - heading;
124 }
125 else
126 {
127 heading = Math.PI - heading;
128 }
129
130 headingAlreadyAdjusted = true;
131 }
132 }
133 else if ( x > maxX )
134 {
135 x = maxX;
136
137 if ( delta_x > 0.0 )
138 {
139 if ( heading < Math.PI )
140 {
141 heading = Math.PI - heading;
142 }
143 else
144 {
145 heading = 3.0 * Math.PI - heading;
146 }
147
148 headingAlreadyAdjusted = true;
149 }
150 }
151
152 if ( y < minY )
153 {
154 y = minY;
155
156 if ( delta_y < 0.0 )
157 {
158 if ( !headingAlreadyAdjusted )
159 {
160 heading = 2.0 * Math.PI - heading;
161 }
162 }
163 }
164 else if ( y > maxY )
165 {
166 y = maxY;
167
168 if ( delta_y > 0.0 )
169 {
170 if ( !headingAlreadyAdjusted )
171 {
172 heading = 2.0 * Math.PI - heading;
173 }
174 }
175 }
176
177 sprite.getPaintBounds ( oldPaintBounds );
178
179 sprite.setX ( x );
180
181 sprite.setY ( y );
182
183 sprite.setHeading ( heading );
184
185 sprite.getPaintBounds ( newPaintBounds );
186
187 oldPaintBounds.add ( newPaintBounds );
188
189 component.repaint ( oldPaintBounds );
190 }
191
192 //////////////////////////////////////////////////////////////////////
193 //////////////////////////////////////////////////////////////////////
194 }