I am trying to make something along the lines of this: http://instaprint.me/, however I don't have a dedicated Linux machine, ink-less paper and WiFi. I do, on the other hand, have a desktop connected to the internet and a color photo-printer.
So what I had in mind was this -
Set up an Instagram API App that gets all the information of the most recent photo
A PHP script and a server that can produce a static URL for the most recent photo with a header("Content-Type: image/jpeg"), so that every time I take a new photo, the contents of the image on that static page changes to be the most recent photo.
Other pages, like the one mentioned above, that change to reflect the new location and caption of each photo I take, hosted on a static URL.
Some basic knowledge of Processing.
So here's how for I've got so far - I can download the most recent photo ID, and if it has changed since the last time it checked (15 seconds ago), then it proceeds to download the most recent image, caption and location.
I can then arrange these on the canvas to look like an old-fashioned polaroid. Now the only thing I have to do is print the canvas, at 150dpi, on a piece of 6x4in photo paper.
Anyone got any idea how I could do this?
Focusing on the part about bringing up large images in Processing, I did a Google search and came up with this thread. Quoting their problem and results:
The goal is to print a 180 pdi image with a shape depending on the
original canvas. So can be diffrent but it will end up being a
1500x1000mm picture approximately. So a big image. I'm not trying to
display it just to dump it into a file.
Then I setup a 64bits JVM. The latest one from Oracle website. Then I
created different size of picture and push the memory up to 1024MB. 5000x7500 test OK and 6000x9000 test OK
I tried to setup the memory up to 1500MB, but the JVM was not able to start.
So I tried 1200MB 8000x12000 test OK
So it doesn't do the image-printing part but it brings up key information on getting large images in memory via a Java Virtual Machine (JVM).
Lastly, this thread describes a TileSaver class for viewing/printing a large number of pixels using OpenGL. I pasted a large code block below. (This may be unrelated to your question but makes the answer more complete for other uses.)
import processing.opengl.*;
import toxi.geom.*;
Tiler tiler;
void setup() {
size(640,480,OPENGL);
tiler=new Tiler((PGraphics3D)g,NUM_TILES);
}
void draw() {
// see thread
}
/**
* Implements a screen tile exporter with support for FOV,
* clip planes and flexible file formats.
*
* Re-engineered from an older version by Marius Watz:
* http://workshop.evolutionzone.com/unlekkerlib/
*
* #author toxi <info at postspectacular dot com>
*/
class Tiler {
protected PGraphics3D gfx;
protected PImage buffer;
protected Vec2D[] tileOffsets;
protected double top;
protected double bottom;
protected double left;
protected double right;
protected Vec2D tileSize;
protected int numTiles;
protected int tileID;
protected float subTileID;
protected boolean isTiling;
protected String fileName;
protected String format;
protected double cameraFOV;
protected double cameraFar;
protected double cameraNear;
public Tiler(PGraphics3D g, int n) {
gfx = g;
numTiles = n;
}
public void chooseTile(int id) {
Vec2D o = tileOffsets[id];
gfx.frustum(o.x, o.x + tileSize.x, o.y, o.y + tileSize.y,
(float) cameraNear, (float) cameraFar);
}
public void initTiles(float fov, float near, float far) {
tileOffsets = new Vec2D[numTiles * numTiles];
double aspect = (double) gfx.width / gfx.height;
cameraFOV = fov;
cameraNear = near;
cameraFar = far;
top = Math.tan(cameraFOV * 0.5) * cameraNear;
bottom = -top;
left = aspect * bottom;
right = aspect * top;
int idx = 0;
tileSize = new Vec2D((float) (2 * right / numTiles), (float) (2 * top / numTiles));
double y = top - tileSize.y;
while (idx < tileOffsets.length) {
double x = left;
for (int xi = 0; xi < numTiles; xi++) {
tileOffsets[idx++] = new Vec2D((float) x, (float) y);
x += tileSize.x;
}
y -= tileSize.y;
}
}
public void post() {
if (isTiling) {
subTileID += 0.5;
if (subTileID > 1) {
int x = tileID % numTiles;
int y = tileID / numTiles;
gfx.loadPixels();
buffer.set(x * gfx.width, y * gfx.height, gfx);
if (tileID == tileOffsets.length - 1) {
buffer.save(fileName + "_" + buffer.width + "x"
+ buffer.height + "." + format);
buffer = null;
}
subTileID = 0;
isTiling = (++tileID < tileOffsets.length);
}
}
}
public void pre() {
if (isTiling) {
chooseTile(tileID);
}
}
public void save(String path, String baseName, String format) {
(new File(path)).mkdirs();
this.fileName = path + "/" + baseName;
this.format = format;
buffer = new PImage(gfx.width * numTiles, gfx.height * numTiles);
tileID = 0;
subTileID = 0;
isTiling = true;
}
public boolean isTiling() {
return isTiling;
}
}
Related
i am trying to make the Profile screen for my Application and the Requirement is :
I have the Data with me in the Vector and Image in bitmap form.. Only problem is that i am Struggling to figure out how it can be achieved, How to make the Photo Section parallel to the multiple Fields in Right hand Side..
Please help me out..
Thanks in Advance..
You haven't specified everything about how this should work. You may want the profile picture to scale with the screen size. Some of the fields may be editable. I'm not sure if you want backgrounds or borders drawn around the 3 rows of Text/Number. So, I had to make some guesses. But, this should get you started, and help you understand what's needed to create a custom Manager subclass, which is one of the ways to solve this problem.
public class ProfileScreen extends MainScreen {
public ProfileScreen() {
super(MainScreen.NO_VERTICAL_SCROLL | MainScreen.NO_VERTICAL_SCROLLBAR);
add(new ProfileManager());
}
private class ProfileManager extends Manager {
private static final int PADDING = 20; // TODO: might make this depend on screen size!
private static final int ROW_PAD = PADDING / 2;
private static final int NUM_ROWS = 3;
private String _name = "Nate";
private String _place = "USA";
private String _phone = "1-206-123-4567";
private String _email = "nate#stackoverflow.com";
private Bitmap _photo;
private Vector _text;
private Vector _numbers;
private LabelField _nameField;
private LabelField _placeField;
private LabelField _phoneField;
private LabelField _emailField;
private BitmapField _photoField;
private LabelField[] _textFields;
private LabelField[] _numberFields;
private int[] _rowLocations;
public ProfileManager() {
super(Manager.NO_VERTICAL_SCROLL | Manager.NO_VERTICAL_SCROLLBAR);
// Create the data and the fields ... this would probably be more
// dynamic in your production code, so the profile could change.
_nameField = new LabelField(_name);
_placeField = new LabelField(_place);
_phoneField = new LabelField(_phone);
_emailField = new LabelField(_email);
_photo = Bitmap.getBitmapResource("avatar.png");
_photoField = new BitmapField(_photo);
_text = new Vector();
_textFields = new LabelField[NUM_ROWS];
for (int i = 0; i < NUM_ROWS; i++) {
_text.insertElementAt("Text" + (i + 1), i);
_textFields[i] = new LabelField(_text.elementAt(i),
Field.USE_ALL_WIDTH | DrawStyle.HCENTER);
}
_numbers = new Vector();
_numberFields = new LabelField[NUM_ROWS];
for (int i = 0; i < NUM_ROWS; i++) {
_numbers.insertElementAt("Number" + (i + 1), i);
_numberFields[i] = new LabelField(_numbers.elementAt(i),
Field.USE_ALL_WIDTH | DrawStyle.HCENTER);
}
// We will store the bottom 3 row locations for use in paint()
_rowLocations = new int[NUM_ROWS];
// Add the fields to this manager
add(_photoField);
add(_nameField);
add(_placeField);
add(_phoneField);
add(_emailField);
for (int i = 0; i < NUM_ROWS; i++) {
// Add one row of Text and Number fields
add(_textFields[i]);
add(_numberFields[i]);
}
}
public int getPreferredHeight() {
return Display.getHeight(); // full screen
}
public int getPreferredWidth() {
return Display.getWidth(); // full screen
}
protected void sublayout(int width, int height) {
setExtent(width, height);
int x = PADDING;
int y = PADDING;
setPositionChild(_photoField, x, y);
layoutChild(_photoField, _photo.getWidth(), _photo.getHeight());
x += _photo.getWidth() + PADDING;
int widthMinusPhoto = width - 3 * PADDING - _photo.getWidth();
setPositionChild(_nameField, x, y);
layoutChild(_nameField, widthMinusPhoto, _nameField.getPreferredHeight());
y += _nameField.getHeight() + ROW_PAD;
setPositionChild(_placeField, x, y);
layoutChild(_placeField, widthMinusPhoto, _placeField.getPreferredHeight());
y += _placeField.getHeight() + ROW_PAD;
setPositionChild(_phoneField, x, y);
layoutChild(_phoneField, widthMinusPhoto, _phoneField.getPreferredHeight());
y += _phoneField.getHeight() + ROW_PAD;
setPositionChild(_emailField, x, y);
layoutChild(_emailField, widthMinusPhoto, _emailField.getPreferredHeight());
// layout the 3 rows of Text and Numbers (1/3 width for each label field (?)
x = PADDING;
y = PADDING + _photo.getHeight() + PADDING + ROW_PAD;
for (int i = 0; i < NUM_ROWS; i++) {
setPositionChild(_textFields[i], x, y);
// record the y coordinate of this row, to use in paint()
_rowLocations[i] = y;
layoutChild(_textFields[i],
width / 3, _textFields[i].getPreferredHeight());
setPositionChild(_numberFields[i], width - PADDING - width / 3, y);
layoutChild(_numberFields[i],
width / 3, _numberFields[i].getPreferredHeight());
y += _textFields[i].getPreferredHeight() + PADDING + 2 * ROW_PAD;
}
}
// paint overridden to draw gray box behind Text/Number rows
protected void paint(Graphics graphics) {
int oldColor = graphics.getColor();
// paint a light gray background behind each row of Text/Numbers
graphics.setColor(Color.LIGHTGRAY);
for (int i = 0; i < NUM_ROWS; i++) {
// if you want a solid border, use drawRect() instead of fillRect():
graphics.fillRect(PADDING, _rowLocations[i] - ROW_PAD,
getWidth() - 2 * PADDING, _textFields[i].getHeight() + 2 * ROW_PAD);
}
graphics.setColor(oldColor); // reset the color for super.paint()
super.paint(graphics);
}
}
}
It's not always required to override paint() in a custom Manager class. sublayout() is where most of the work is done, to position the fields. In this case, I chose to override paint() in order to provide a custom border around more than one field. This isn't the only way to do that, but there are definitely times when you'll want to be able to add custom drawing code, to improve the look of your UIs.
Results
I've been trying to work on a 3d forward-runner game (like temple run) in XNA 4.0.
I'm hitting a bit of a brick wall, so any help would be much appreciated!
Currently, I'm using my own method for collision detection, which requires the dimensions for each model to be hard-coded into the collision method. I've tried using the code from Microsoft, directly below, but it always returns false:
static bool CheckForCollisions(Entity c1, Entity c2)
{
for (int i = 0; i < c1.body.Meshes.Count; i++)
{
// Check whether the bounding boxes of the two cubes intersect.
BoundingSphere c1BoundingSphere = c1.body.Meshes[i].BoundingSphere;
c1BoundingSphere.Center += c1.position;
for (int j = 0; j < c2.body.Meshes.Count; j++)
{
BoundingSphere c2BoundingSphere = c2.body.Meshes[j].BoundingSphere;
c2BoundingSphere.Center += c2.position;
if (c1BoundingSphere.Intersects(c2BoundingSphere))
{
return true;
}
}
}
return false;
}
This was taken and modified very slightly from, Here My Entity class goes as follows.
Code of mine which I think is relevant would be:
public class Entity
{
public int rowID;
public Model body;
public Vector3 position;
public float rotation = 0f;
public float rotatePerFrame = 0f;
protected internal float toRight = 0;
protected internal float toLeft = 0;
protected internal float forward = 0;
protected internal float back = 0;
protected internal float bottom = 0;
protected internal float top = 0;
public void setDimensions(float right, float left, float front, float back, float top, float bottom)
{
this.toRight = right;
this.toLeft = left;
this.forward = front;
this.back = back;
this.top = top;
this.bottom = bottom;
}
public Entity RotateEnt(Entity e,float degrees)//Psuedo-only accurate to 90 degrees.
{
float actual = MathHelper.ToDegrees(degrees);
switch ((int)actual)
{
case 0:
break;
case 90:
// float temp = e.forward;
// e.forward = e.toLeft;
// e.toLeft =e.back ;
// e.back = e.toRight;
// e.toRight = temp;
float temp = e.forward;
e.forward = e.toRight;
e.toRight = e.back;
e.back = e.toLeft;
e.toLeft = temp;
break;
case 180:
e.forward = e.back;
e.back = e.forward;
e.toRight = e.toLeft;
e.toLeft = e.toRight;
break;
default: //case: 270
e.toRight = e.forward;
e.back = e.toRight;
e.toLeft = e.back;
e.forward = e.toLeft;
break;
}
return e;
}
public bool Collides(Entity e)
{
Entity c1 = RotateEnt(this, this.rotation);
Entity c2 = RotateEnt(e, e.rotation);
float myRightest = c1.position.X + c1.toRight;
float myLeftest = c1.position.X - c1.toLeft;
float hisRightest = c2.position.X + c2.toRight;
float hisLeftest = c2.position.X - c2.toLeft;
if(Collides1D(myLeftest, myRightest, hisLeftest, hisRightest))
{
float myTop = c1.position.Y + c1.top;
float myBottom = c1.position.Y - c1.bottom;
float hisTop = c2.position.Y + c2.top;
float hisBottom = c2.position.Y - c2.bottom;
if (Collides1D(myBottom, myTop, hisBottom, hisTop))
{
float myBack = c1.position.Z - c1.forward;
float myForward = c1.position.Z + c1.back;
float hisBack = c2.position.Z - c2.forward;
float hisForward = c2.position.Z + c2.back;
if (Collides1D(myBack, myForward, hisBack, hisForward))
{
return true;
}
}
}
return false;
}
}
static bool Collides1D(float left1, float right1, float left2, float right2)
{
if (left1 >= left2 && left1 <= right2)
return true;
if (right1 >= left2 && right1 <= right2)
return true;
if (left2 >= left1 && left2 <= right1)
return true;
if (right2 >= left1 && right2 <= right1)
return true;
return false;
}
My own method has been screwing up also, when trying to rotate models.
Ideally, it would be good to know what is wrong with the code from Microsoft, so that I can use it wherever, without worrying about hard-coding in object dimensions.
If anyone can see a fast fix to my own basic collision detection method, that would also be great.
I've looked at the Reimers tutorials, but I'm not getting them at the moment, maybe I've been staring at my own code for too long...
Any other information you want, I can try and supply. I can upload the models also, if that's the problem. I'm using models from Maya, exported as FBX.
I'm using MSVS 2010.
Thanks very much!
Jack
I think the problem may not be in the collision code, but in how you are updating the position of your Entitys. I don't see any update code or MoveForward(), etc, type of code. First of all, though, I think it would be much easier for you to use a Matrix instead of a bunch of float values for rotation. For example, you could have:
public class Entity
{
...
public Matrix RotationMatrix = Matrix.Identity;
public Vector3 position;
Public Entity(Vector3 FaceDirection, Vector3 UpDirection, Vector3 Position)
{
...
position = Position
RotationMatrix = Matrix.CreateWorld(Position, FaceDirection, UpDirection)
}
}
Then if you want to rotate, all you have to do is:
public Void RotateEnt(float Degrees)
{
RotationMatrix *= Matrix.CreateFromAxisAngle(RotationMatrix.Up, MathHelper.ToRadians(Degrees));
}
And if you do all this, then you should easily be able to update your Entity's position, which I think is the problem
public Void Update(GameTime gametime)
{
position += RotationMatrix.Forward * gametime.ElapsedRealTime.TotalMilliseconds * x; //x = some number to scale up or down velocity.
}
If you do this, I think your Entity's position will be updated, which then should fix your collision problem. But, not knowing how you update the position of your Entities, I am just speculating. HTH
I'm experimenting a bit with XNA 4.0, following tutorials and creating very basic stuff (like a triangle and some lines ;-)). While doing this, I noticed that all my applications never run at more than 50-51 fps (with Fraps). It's not that I'm running heavy programs on a slow computer or graphics card (Ati HD4870), it must have something to do with XNA (games run just fine here).
Now, everything I read about XNA says that the default update frequency is 60 times a second, and I'd like to get that.
It's the same in full screen as in windowed
If I set SynchronizeWithVerticalRetrace to false or true: same
If run the program without Visual Studio, I only get 41 fps
When I override the update frequency by using TargetElapsedTime = new TimeSpan(0, 0, 0, 0, 10); the fps does go up significantly. I noticed though that this still isn't correct: the 10 means 10ms, yet I 'only' get 83 fps instead of 100. At 1ms I get 850 fps. So the deviation of what fps I get and what I should get is pretty consistent. It looks to me like there's just something wrong with the timing?
Anyone knows what might be the problem here and/or has suggestions to get a stable 60 fps?
Thanks!
Isn't it possible that FRAPS isn't giving you accurate results? Have you tried adding in your own framerate counter to the game and seeing what those results say? Shawn Hargreaves has a method he coded up on his blog that should be pretty painless to add to a new blank XNA game project.
http://blogs.msdn.com/b/shawnhar/archive/2007/06/08/displaying-the-framerate.aspx
Do you see the same FPS or is reported differently when you do the calculation yourself?
I put together the following code on XNA 4 and it's getting a locked 60 fps. Try it out on your system (You'll just have to add the appropriate sprite font) and see if you get 60 fps too. If so, put the adjustments in your problem code and see if you get the same result.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace _60fps
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
SpriteFont OutputFont;
float Fps = 0f;
private const int NumberSamples = 50; //Update fps timer based on this number of samples
int[] Samples = new int[NumberSamples];
int CurrentSample = 0;
int TicksAggregate = 0;
int SecondSinceStart = 0;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
base.Initialize();
graphics.SynchronizeWithVerticalRetrace = false;
int DesiredFrameRate = 60;
TargetElapsedTime = new TimeSpan(TimeSpan.TicksPerSecond / DesiredFrameRate);
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
OutputFont = Content.Load<SpriteFont>("MessageFont");
}
protected override void UnloadContent()
{/* Nothing to do */}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Escape))
this.Exit();
base.Update(gameTime);
}
private float Sum(int[] Samples)
{
float RetVal = 0f;
for (int i = 0; i < Samples.Length; i++)
{
RetVal += (float)Samples[i];
}
return RetVal;
}
private Color ClearColor = Color.FromNonPremultiplied(20, 20, 40, 255);
protected override void Draw(GameTime gameTime)
{
Samples[CurrentSample++] = (int)gameTime.ElapsedGameTime.Ticks;
TicksAggregate += (int)gameTime.ElapsedGameTime.Ticks;
if (TicksAggregate > TimeSpan.TicksPerSecond)
{
TicksAggregate -= (int)TimeSpan.TicksPerSecond;
SecondSinceStart += 1;
}
if (CurrentSample == NumberSamples) //We are past the end of the array since the array is 0-based and NumberSamples is 1-based
{
float AverageFrameTime = Sum(Samples) / NumberSamples;
Fps = TimeSpan.TicksPerSecond / AverageFrameTime;
CurrentSample = 0;
}
GraphicsDevice.Clear(ClearColor);
spriteBatch.Begin();
if (Fps > 0)
{
spriteBatch.DrawString(OutputFont, string.Format("Current FPS: {0}\r\nTime since startup: {1}", Fps.ToString("000"), TimeSpan.FromSeconds(SecondSinceStart).ToString()), new Vector2(10,10), Color.White);
}
spriteBatch.End();
base.Draw(gameTime);
}
}
}
In HLSL, is there any way to limit the number of constant registers that the compiler uses?
Specifically, if I have something like:
float4 foobar[300];
In a vs_2_0 vertex shader, the compiler will merrily generate the effect with more than 256 constant registers. But a 2.0 vertex shader is only guaranteed to have access to 256 constant registers, so when I try to use the effect, it fails in an obscure and GPU-dependent way at runtime. I would much rather have it fail at compile time.
This problem is especially annoying as the compiler itself allocates constant registers behind the scenes, on top of the ones I am asking for. I have to check the assembly to see if I'm over the limit.
Ideally I'd like to do this in HLSL (I'm using the XNA content pipeline), but if there's a flag that can be passed to the compiler that would also be interesting.
Based on Stringer Bell's pointing out of the Disassemble method, I have whipped up a small post-build utility to parse and check the effect. Be warned that this is not very pretty. It is designed for XNA 3.1 and requires the ServiceContainer and GraphicsDeviceService classes from the XNA WinForms sample. Pass a content directory path on the command line with no trailing slash.
class Program
{
const int maxRegisters = 256; // Sutiable for VS 2.0, not much else
static int retval = 0;
static string root;
static ContentManager content;
static void CheckFile(string path)
{
string name = path.Substring(root.Length+1, path.Length - (root.Length+1) - #".xnb".Length);
Effect effect;
try { effect = content.Load<Effect>(name); }
catch { return; } // probably not an Effect
string effectSource = effect.Disassemble(false);
int highest = -1; // highest register allocated
var matches = Regex.Matches(effectSource, #" c([0-9]+)"); // quick and dirty
foreach(Match match in matches)
{
int register = Int32.Parse(match.Groups[1].ToString());
if(register > highest)
highest = register;
}
var parameters = Regex.Matches(effectSource, #"^ *// *[a-zA-Z_0-9]+ +c([0-9]+) +([0-9]+)", RegexOptions.Multiline);
foreach(Match match in parameters)
{
int register = Int32.Parse(match.Groups[1].ToString()) + Int32.Parse(match.Groups[2].ToString()) - 1;
if(register > highest)
highest = register;
}
if(highest+1 > maxRegisters)
{
Console.WriteLine("Error: Shader \"" + name + "\" uses " + (highest+1).ToString() + " constant registers, which is TOO MANY!");
retval = 1;
}
else
{
Console.WriteLine("Shader \"" + name + "\" uses " + (highest+1).ToString() + " constant registers (OK)");
}
}
static void CheckDirectory(string path)
{
try
{
foreach(string file in Directory.GetFiles(path, #"*.xnb"))
CheckFile(file);
foreach(string dir in Directory.GetDirectories(path))
CheckDirectory(dir);
}
catch { return; } // Don't care
}
static int Main(string[] args)
{
root = args[0];
Form form = new Form(); // Dummy form for creating a graphics device
GraphicsDeviceService gds = GraphicsDeviceService.AddRef(form.Handle,
form.ClientSize.Width, form.ClientSize.Height);
ServiceContainer services = new ServiceContainer();
services.AddService<IGraphicsDeviceService>(gds);
content = new ContentManager(services, root);
CheckDirectory(root);
return retval;
}
}
When my Floating-Point Guide was yesterday published on slashdot, I got a lot of flak for my suggested comparison function, which was indeed inadequate. So I finally did the sensible thing and wrote a test suite to see whether I could get them all to pass. Here is my result so far. And I wonder if this is really as good as one can get with a generic (i.e. not application specific) float comparison function, or whether I still missed some edge cases.
(Code updated to fix error)
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
/**
* Test suite to demonstrate a good method for comparing floating-point values using an epsilon. Run via JUnit 4.
*
* Note: this function attempts a "one size fits all" solution. There may be some edge cases for which it still
* produces unexpected results, and some of the tests it was developed to pass probably specify behaviour that is
* not appropriate for some applications. Before using it, make sure it's appropriate for your application!
*
* From http://floating-point-gui.de
*
* #author Michael Borgwardt
*/
public class NearlyEqualsTest {
public static boolean nearlyEqual(float a, float b, float epsilon) {
final float absA = Math.abs(a);
final float absB = Math.abs(b);
final float diff = Math.abs(a - b);
if (a * b == 0) { // a or b or both are zero
// relative error is not meaningful here
return diff < (epsilon * epsilon);
} else { // use relative error
return diff / (absA + absB) < epsilon;
}
}
public static boolean nearlyEqual(float a, float b) {
return nearlyEqual(a, b, 0.000001f);
}
/** Regular large numbers - generally not problematic */
#Test
public void big() {
assertTrue(nearlyEqual(1000000f, 1000001f));
assertTrue(nearlyEqual(1000001f, 1000000f));
assertFalse(nearlyEqual(10000f, 10001f));
assertFalse(nearlyEqual(10001f, 10000f));
}
/** Negative large numbers */
#Test
public void bigNeg() {
assertTrue(nearlyEqual(-1000000f, -1000001f));
assertTrue(nearlyEqual(-1000001f, -1000000f));
assertFalse(nearlyEqual(-10000f, -10001f));
assertFalse(nearlyEqual(-10001f, -10000f));
}
/** Numbers around 1 */
#Test
public void mid() {
assertTrue(nearlyEqual(1.0000001f, 1.0000002f));
assertTrue(nearlyEqual(1.0000002f, 1.0000001f));
assertFalse(nearlyEqual(1.0002f, 1.0001f));
assertFalse(nearlyEqual(1.0001f, 1.0002f));
}
/** Numbers around -1 */
#Test
public void midNeg() {
assertTrue(nearlyEqual(-1.000001f, -1.000002f));
assertTrue(nearlyEqual(-1.000002f, -1.000001f));
assertFalse(nearlyEqual(-1.0001f, -1.0002f));
assertFalse(nearlyEqual(-1.0002f, -1.0001f));
}
/** Numbers between 1 and 0 */
#Test
public void small() {
assertTrue(nearlyEqual(0.000000001000001f, 0.000000001000002f));
assertTrue(nearlyEqual(0.000000001000002f, 0.000000001000001f));
assertFalse(nearlyEqual(0.000000000001002f, 0.000000000001001f));
assertFalse(nearlyEqual(0.000000000001001f, 0.000000000001002f));
}
/** Numbers between -1 and 0 */
#Test
public void smallNeg() {
assertTrue(nearlyEqual(-0.000000001000001f, -0.000000001000002f));
assertTrue(nearlyEqual(-0.000000001000002f, -0.000000001000001f));
assertFalse(nearlyEqual(-0.000000000001002f, -0.000000000001001f));
assertFalse(nearlyEqual(-0.000000000001001f, -0.000000000001002f));
}
/** Comparisons involving zero */
#Test
public void zero() {
assertTrue(nearlyEqual(0.0f, 0.0f));
assertTrue(nearlyEqual(0.0f, -0.0f));
assertTrue(nearlyEqual(-0.0f, -0.0f));
assertFalse(nearlyEqual(0.00000001f, 0.0f));
assertFalse(nearlyEqual(0.0f, 0.00000001f));
assertFalse(nearlyEqual(-0.00000001f, 0.0f));
assertFalse(nearlyEqual(0.0f, -0.00000001f));
assertTrue(nearlyEqual(0.0f, 0.00000001f, 0.01f));
assertTrue(nearlyEqual(0.00000001f, 0.0f, 0.01f));
assertFalse(nearlyEqual(0.00000001f, 0.0f, 0.000001f));
assertFalse(nearlyEqual(0.0f, 0.00000001f, 0.000001f));
assertTrue(nearlyEqual(0.0f, -0.00000001f, 0.1f));
assertTrue(nearlyEqual(-0.00000001f, 0.0f, 0.1f));
assertFalse(nearlyEqual(-0.00000001f, 0.0f, 0.00000001f));
assertFalse(nearlyEqual(0.0f, -0.00000001f, 0.00000001f));
}
/** Comparisons of numbers on opposite sides of 0 */
#Test
public void opposite() {
assertFalse(nearlyEqual(1.000000001f, -1.0f));
assertFalse(nearlyEqual(-1.0f, 1.000000001f));
assertFalse(nearlyEqual(-1.000000001f, 1.0f));
assertFalse(nearlyEqual(1.0f, -1.000000001f));
assertTrue(nearlyEqual(1e10f * Float.MIN_VALUE, -1e10f * Float.MIN_VALUE));
}
/**
* The really tricky part - comparisons of numbers very close to zero.
*/
#Test
public void ulp() {
assertTrue(nearlyEqual(Float.MIN_VALUE, -Float.MIN_VALUE));
assertTrue(nearlyEqual(-Float.MIN_VALUE, Float.MIN_VALUE));
assertTrue(nearlyEqual(Float.MIN_VALUE, 0));
assertTrue(nearlyEqual(0, Float.MIN_VALUE));
assertTrue(nearlyEqual(-Float.MIN_VALUE, 0));
assertTrue(nearlyEqual(0, -Float.MIN_VALUE));
assertFalse(nearlyEqual(0.000000001f, -Float.MIN_VALUE));
assertFalse(nearlyEqual(0.000000001f, Float.MIN_VALUE));
assertFalse(nearlyEqual(Float.MIN_VALUE, 0.000000001f));
assertFalse(nearlyEqual(-Float.MIN_VALUE, 0.000000001f));
assertFalse(nearlyEqual(1e25f * Float.MIN_VALUE, 0.0f, 1e-12f));
assertFalse(nearlyEqual(0.0f, 1e25f * Float.MIN_VALUE, 1e-12f));
assertFalse(nearlyEqual(1e25f * Float.MIN_VALUE, -1e25f * Float.MIN_VALUE, 1e-12f));
assertTrue(nearlyEqual(1e25f * Float.MIN_VALUE, 0.0f, 1e-5f));
assertTrue(nearlyEqual(0.0f, 1e25f * Float.MIN_VALUE, 1e-5f));
assertTrue(nearlyEqual(1e20f * Float.MIN_VALUE, -1e20f * Float.MIN_VALUE, 1e-5f));
}
}
The main problem I see is you don't allow the user to control epsilon.
Also epsilon changes depending on the order of magnitude of the numbers being compared. Near zero epsilon is small, near the maximum power epsilon is large.
I think whenever you need to talk about such concepts as "close enough" it becomes an application level design decision. You can't write a generic library for that.
After sleeping over it, I've realized that this part was rubbish:
if (a*b==0) {
return diff < Float.MIN_VALUE / epsilon;
This becomes less strict as epsilon gets smaller! A more sensible version:
if (a * b == 0) {
return diff < (epsilon * epsilon);
Still, the two branches of the if are not very consistent with each other. It's much stricter when a or b is very small than when one of them is zero. I'm really starting to think that using integer comparison is an overall better method.