I want to implement insert method for a quadtree. In this quadtree i will insert certain shapes like rectangle, triangle, circle and diamond. All shaoes come with an rectangle border(all shaoes are considered rectangles in this method). My method works for few inputs, but for most of all i get the error mentioned.
void insert(Node parent, GeometricObj shape) {
switch(parent.getType()) {
case EMPTY:
this.setShapeForNode(parent, shape);
break;
case LEAF:
if(needSplit(parent, shape)) {
this.setShapeForNode(parent, shape);
}
else {
this.split(parent);
this.insert(parent, shape);
for (int i = 0; i < parent.Shapes.size(); i++) {
this.insert(parent, parent.Shapes.get(i));
}
}
break;
case PARENT:
for (int i = 0; i < getQuadrantForShape(parent, shape.getX(), shape.getY(),
shape.getWidth(), shape.getHeight()).size(); i++) {
if(getQuadrantForShape(parent, shape.getX(), shape.getY(),
shape.getWidth(), shape.getHeight()).get(i).equals(parent.Quads.get(0))) {
this.insert(parent.Quads.get(0), shape);
}
if(getQuadrantForShape(parent, shape.getX(), shape.getY(),
shape.getWidth(), shape.getHeight()).get(i).equals(parent.Quads.get(1))) {
this.insert(parent.Quads.get(1), shape);
}
if(getQuadrantForShape(parent, shape.getX(), shape.getY(),
shape.getWidth(), shape.getHeight()).get(i).equals(parent.Quads.get(2))) {
this.insert(parent.Quads.get(2), shape);
}
if(getQuadrantForShape(parent, shape.getX(), shape.getY(),
shape.getWidth(), shape.getHeight()).get(i).equals(parent.Quads.get(3))) {
this.insert(parent.Quads.get(3), shape);
}
}
break;
}
}
Split method is this:
void split(Node node) {
node.setType(NodeType.PARENT);
double x = node.getX();
double y = node.getY();
double halfwidth = node.getWidth()/ 2;
double halfheight = node.getHeight()/ 2;
node.setQuad1(new Node(x + halfwidth, y + halfheight, halfwidth, halfheight, node));
node.setQuad2(new Node(x, y + halfheight, halfwidth, halfheight, node));
node.setQuad3(new Node(x, y, halfwidth, halfheight, node));
node.setQuad4(new Node(x + halfwidth, y, halfwidth, halfheight, node));
node.Quads.add(node.quad1);
node.Quads.add(node.quad2);
node.Quads.add(node.quad3);
node.Quads.add(node.quad4);
}
GetQuadrantForShape method returns a list of quadrants for the new shape.
void setShapeForNode(Node node, GeometricObj shape) {
if (node.getType() != NodeType.PARENT) {
node.setType(NodeType.LEAF);
node.Shapes.add(shape);
}
}
If someone has a clue of what the problem could be, or what am I doing wrong, or any hint of what can I improve, please tell me. If you need any other class or method I'm gonna attach it here.
UPDATE:
boolean needSplit(Node node, GeometricObj shape) {
boolean result = false;
for (int i = 0; i < node.Shapes.size(); i++) {
GeometricObj o = node.Shapes.get(i);
result = this.overlaps(shape.getX(), shape.getY(), shape.getWidth(), shape.getHeight(), o);
if(result == false) {
break;
}
}
return result;
}
boolean overlaps (double x, double y, double width, double height, GeometricObj r) {
return x < r.getX() + r.getWidth() && x + width > r.getX() && y < r.getY() + r.getHeight() && y + height > r.getY();
}
Related
Using the mouse, I would like to change the size of the shapes that I have in my code (which is circle and rectangle) by clicking on the corners and dragging it to change the shape's width and height.
Here's my main code:
circleShape [] objs;
int numObjs = 1;
int pickedItem = -1;
rectShape [] objss;
int numObjss = 1;
int itemPicked = -1;
void setup() {
size(600, 600);
objs = new circleShape[numObjs];
for (int i = 0; i < numObjs; i++) {
int radius = (int)(random(20, 70));
color c = (int)(random(0, 255));
objs[i] = new circleShape(radius, c);
}
objss = new rectShape[numObjss];
for (int i = 0; i < numObjss; i++) {
int Radius = (int)(random(20, 70));
color C = (int)(random(0, 255));
objss[i] = new rectShape(Radius, C);
}
}
void draw() {
background(255);
for (int i = 0; i < numObjs; i++) {
objs[i].drawShape();
}
for (int i =0; i < numObjss; i++) {
objss[i].shapeDraw();
}
}
// return the index number of the picked item from the array
// return -1 otherwise
int getPickedObj(int x, int y) {
for (int i = 0; i < numObjs; i++) {
if (objs[i].isOnShape(x, y)) {
return i;
}
}
return -1;
}
int pickedObj(int X, int Y) {
for (int i = 0; i < numObjss; i++) {
if (objss[i].onShape(X, Y)) {
return i;
}
}
return -1;
}
void mousePressed() {
pickedItem = getPickedObj(mouseX, mouseY);
itemPicked = pickedObj(mouseX, mouseY);
}
void mouseDragged() {
if (pickedItem >= 0) {
objs[pickedItem].move(mouseX, mouseY);
objs[pickedItem].drawShape();
}
if (itemPicked >= 0) {
objss[itemPicked].move(mouseX, mouseY);
objss[itemPicked].shapeDraw();
}
}
And here's my shape code:
// a shape is simply as a circle
class circleShape {
int posX, posY, radius;
color c;
circleShape(int radius, color c) {
this.radius = radius;
this.c = c;
posX = (int)random(0, 200);
posY = (int)random(0, 200);
}
// return true if the position x, y is on the shape
boolean isOnShape(int x, int y) {
float d = (posX - x)*(posX - x) + (posY - y)*(posY - y);
d = sqrt(d);
if (d < radius) return true;
else return false;
}
void move (int newPosX, int newPosY) {
this.posX = newPosX;
this.posY = newPosY;
}
void drawShape() {
noStroke();
fill(c);
ellipse(posX, posY, 2*radius, 2*radius);
}
}
//Rectangle Shape
class rectShape {
int xPos, yPos, Radius;
color C;
rectShape(int Radius, color C) {
this.Radius = Radius;
this.C = C;
xPos = (int)random(0, 200);
yPos = (int)random(0, 200);
}
boolean onShape(int X, int Y) {
float D = (xPos - X)*(xPos - X) + (yPos - Y)*(yPos - Y);
D = sqrt(D);
if (D < Radius) return true;
else return false;
}
void move (int posXnew, int posYnew) {
this.xPos = posXnew;
this.yPos = posYnew;
}
void shapeDraw() {
noStroke();
fill(C);
rect(xPos, yPos, 2*Radius+60, 2*Radius);
}
}
My code allows the mouse to move the shapes from one place to another. It just needs to be able to change the size of them.
I'll give you an example for a rectangular shape.
A rectangle is defined by a position, the width and the height. Change the attributes and the constructor of the rectangle:
class rectShape {
int xPos, yPos, Width, Height;
color C;
rectShape(int X, int Y, int Width, int Height, color C) {
this.xPos = X;
this.yPos = Y;
this.Width = Width;
this.Height = Height;
this.C = C;
}
// [...]
void shapeDraw() {
noStroke();
fill(C);
rect(xPos, yPos, this.Width, this.Height);
}
}
Create a random rectangle:
void setup() {
size(600, 600);
// [...]
objss = new rectShape[numObjss];
for (int i = 0; i < numObjss; i++) {
int xPos = (int)random(0, 200);
int yPos = (int)random(0, 200);
int Width = (int)(random(20, 70));
int Height = (int)(random(20, 70));
color C = (int)(random(0, 255));
objss[i] = new rectShape(xPos, yPos, Width, Height, C);
}
}
Create a method in the class rectShape, which determines if the mouse is on the rectangle and if it is on the left, right, top, bottom or center:
String onShape(int X, int Y) {
String where = "";
Boolean isOn = X > xPos && X < xPos + Width && Y > yPos && Y < yPos + Height;
if (isOn) {
int left = abs(X - xPos);
int right = abs(X - (xPos + Width));
int top = abs(Y - yPos);
int bottom = abs(Y - (yPos + Height));
if (min (left, right) < min (top, bottom)) {
if (left < 10)
where = "left";
else if (right < 10)
where = "right";
else
where = "center";
}
else {
if (top < 10)
where = "top";
else if (bottom < 10)
where = "bottom";
else
where = "center";
}
}
return where;
}
Create a method which can change the rectangle dependent on an action command:
void update(String what, int posXnew, int posYnew) {
if (what == "left") {
if (posXnew < this.xPos + this.Width) {
this.Width = this.xPos + this.Width - posXnew;
this.xPos = posXnew;
}
}
else if (what == "right") {
if (posXnew > this.xPos) {
this.Width = posXnew - this.xPos;
}
}
else if (what == "top") {
if (posYnew < this.yPos + this.Height) {
this.Height = this.yPos + this.Height - posYnew;
this.yPos = posYnew;
}
}
else if (what == "bottom") {
if (posYnew > this.yPos) {
this.Height = posYnew - this.yPos;
}
}
else {
this.xPos = posXnew - this.Width / 2;
this.yPos = posYnew - this.Height / 2;
}
}
Determine the action when the rectangle is clicked on:
String itemAction = "";
int pickedObj(int X, int Y) {
for (int i = 0; i < numObjss; i++) {
itemAction = objss[i].onShape(X, Y);
if (itemAction != "") {
return i;
}
}
return -1;
}
void mousePressed() {
// [...]
itemPicked = pickedObj(mouseX, mouseY);
}
Pass the action to the update method when the mouse is dragged:
void mouseDragged() {
if (itemPicked >= 0) {
objss[itemPicked].update(itemAction, mouseX, mouseY);
}
}
Complete class rectShape:
//Rectangle Shape
class rectShape {
int xPos, yPos, Width, Height;
color C;
rectShape(int X, int Y, int Width, int Height, color C) {
this.xPos = X;
this.yPos = Y;
this.Width = Width;
this.Height = Height;
this.C = C;
}
String onShape(int X, int Y) {
String where = "";
Boolean isOn = X > xPos && X < xPos + Width && Y > yPos && Y < yPos + Height;
if (isOn) {
int left = abs(X - xPos);
int right = abs(X - (xPos + Width));
int top = abs(Y - yPos);
int bottom = abs(Y - (yPos + Height));
if (min (left, right) < min (top, bottom)) {
if (left < 10)
where = "left";
else if (right < 10)
where = "right";
else
where = "center";
}
else {
if (top < 10)
where = "top";
else if (bottom < 10)
where = "bottom";
else
where = "center";
}
}
return where;
}
void update(String what, int posXnew, int posYnew) {
if (what == "left") {
if (posXnew < this.xPos + this.Width) {
this.Width = this.xPos + this.Width - posXnew;
this.xPos = posXnew;
}
}
else if (what == "right") {
if (posXnew > this.xPos) {
this.Width = posXnew - this.xPos;
}
}
else if (what == "top") {
if (posYnew < this.yPos + this.Height) {
this.Height = this.yPos + this.Height - posYnew;
this.yPos = posYnew;
}
}
else if (what == "bottom") {
if (posYnew > this.yPos) {
this.Height = posYnew - this.yPos;
}
}
else {
this.xPos = posXnew - this.Width / 2;
this.yPos = posYnew - this.Height / 2;
}
}
void shapeDraw() {
noStroke();
fill(C);
rect(xPos, yPos, this.Width, this.Height);
}
}
I am currently learning Java and working on a class assignment. We're supposed to make a "weird version" of Chess.
Like in original chess, pieces can't move if there is a piece in their way, the obvious exception being the Horse, which can hop over all pieces except Kings.
I have an abstract class Piece that is inherited by all the piece types, each with their own rules of movement. Most of them have this movement restriction, and so I have defined the methods in this class:
public boolean freeWayHorizontally(int xO, int yO, int xD) {
//RIGHT
if (xO < xD) {
for (int x = xO + 1; x < xD; x++) {
CrazyPiece thereIsPiece = Simulador.checkIfTheresPiece(x, yO);
if (thereIsPiece != null){
return false;
}
}
//LEFT
} else if (xO > xD) {
for (int x = xO - 1; x > xD; x--) {
CrazyPiece thereIsPiece = Simulador.checkIfTheresPiece(x, yO);
if (thereIsPiece != null){
return false;
}
}
}
return true;
}
public boolean freeWayVertically(int xO, int yO, int yD) {
//UP
if (yO < yD) {
for (int y = yO + 1; y < yD; y++) {
CrazyPiece thereIsPiece = Simulador.checkIfTheresPiece(xO, y);
if (thereIsPiece != null){
return false;
}
}
//DOWN
} else if (yO > yD) {
for (int y = yO - 1; y > yD; y--) {
CrazyPiece thereIsPiece = Simulador.checkIfThereIsPiece(xO, y);
if (thereIsPiece != null){
return false;
}
}
}
return true;
}
thereIsPiece(int x, int y) is a function from the Chess Simulator class that, given a position on the board, returns the piece in that position.
As is obvious, these two receive the same parameters (origin coordinates and a destination coordinate, where one of the destination coordinates is one of the piece's origin coordinates), so the only thing that really changes is the way thereIsPiece() is called. EDIT: And because of this, they're marked as duplicates, and from what I've been told, that's very bad!
However, I can't seem to figure out a way to solve this problem using only one of these methods; ALSO AN EDIT: I've tried overloading it, but then it'd work only vertically or horizontally (may have done it wrong).
The thing is I need these to be done separately to implement the Horse's movement, that overrides these methods:
public boolean freeWayHorizontally(int xO, int yO, int xD) { //Overriden by the Horse class
//RIGHT
if (xO < xD) {
for (int x = xO + 1; x <= xD; x++) {
CrazyPiece thereIsPiece = Simulador.checkIfTheresPiece(x, yO);
if (thereIsPiece != null && thereIsPiece.isKing){
return false;
}
}
//LEFT
} else if (xO > xD) {
for (int x = xO - 1; x >= xD; x--) {
CrazyPiece thereIsPiece = Simulador.checkIfTheresPiece(x, yO);
if (thereIsPiece != null && thereIsPiece.isKing){
return false;
}
}
}
return true;
}
public boolean freeWayVertically(int xO, int yO, int yD) { //Overriden by the Horse class
//UP
if (yO < yD) {
for (int y = yO + 1; y <= yD; y++) {
CrazyPiece thereIsPiece = Simulador.checkIfTheresPiece(xO, y);
if (thereIsPiece != null && thereIsPiece.isKing){
return false;
}
}
//DOWN
} else if (yO > yD) {
for (int y = yO - 1; y >= yD; y--) {
CrazyPiece thereIsPiece = Simulador.checkIfThereIsPiece(xO, y);
if (thereIsPiece != null && thereIsPiece.isKing){
return false;
}
}
}
return true;
}
And then calls its own type of movement check, also defined in the Piece class:
public boolean freeWayL(int xO, int yO, int xD, int yD) {
boolean fH, fV;
//Horizontal -> Vertical
fH = this.freeWayHorizontally(xO, yO, xD);
if (fH) {
fV = this.freeWayVertically(xD, yO, yD);
if (fV) {
return true;
}
}
//Vertical -> Horizontal
fV = this.freeWayVertically(xO, yO, yD);
if (dV) {
fH = this.freeWayHorizontally(xO, yD, xD);
if (fH) {
return true;
}
}
return false;
}
What can I do to avoid all this duplication, or even to make these validations better?
The first issue with your code is that you have different method on Simulador class to check positions:
// When is checking horizontally
CrazyPiece thereIsPiece = Simulador.pegaPecaPorCoordenada(x, yO);
// When is checking vertically
CrazyPiece thereIsPiece = Simulador.checkIfThereIsPiece(xO, y);
I don't see a reason why this should not be a single method.
I can see 2 different point of improvement here:
rewrite the loop changing start and end in a way to have only one loop.
As an example:
public boolean freeWayHorizontally(int xO, int yO, int xD) {
int destination = xD;
int origin = xO + 1;
if (xO > xD) {
destination = xO - 1;
origin = xD;
}
for (int x = origin; x < destination; x++) {
CrazyPiece thereIsPiece = Simulador.pegaPecaPorCoordenada(x, yO);
if (thereIsPiece != null){
return false;
}
}
return true;
}
When you have the origin is before the destination, you move from origin to destination stight forward.
When the destination is before the origin, you just swap and move from destination to origin.
the second point is the check condition.
I think you should just add a method:
private boolean checkPositionIsFree(int x, int y) {
return Simulador.pegaPecaPorCoordenada(x, y) != null;
}
Now you need to have one for the horizontal and one for the vertical, until you can't merge the 2 methods.
And then you can rewrite your method like so:
public boolean freeWayHorizontally(int xO, int yO, int xD) {
int destination = xD;
int origin = xO + 1;
if (xO > xD) {
destination = xO - 1;
origin = xD;
}
for (int x = origin; x < destination; x++) {
if (checkPositionIsFree(x, yO)){
return false;
}
}
return true;
}
For the Horse, you just #Override the checkPositionIsFree() method with the proper condition (adding the check on the king), and everything should work.
Update
To cover completely the horse case you can have a method that work on the input data:
#Override
public boolean freeWayHorizontally(int xO, int yO, int xD) {
return super.freeWayHorizontally(xO, yO, xD + 1);
}
In such a way you can avoid to rewrite all the code.
By the way, your code here have some typos, maybe you rewrite it. It's better to check those kind of things, and have working code (in your case) or the exact code is failing, to avoid lost vouluntiers time following the wrong bug.
I'm new to programming and I need to make ten or more bouncing objects that will bounce off of each other. Can someone give me an example of a program that will create this?
Edit:Here is the code that I'm currently working on.
//main program
float hue;
float alpha;
float scale = 50;
AnArray HmWd;
void setup(){
size(1000,1000);
colorMode(HSB, 360, 100, 100);
HmWd = new AnArray(scale,3);
}
void display(){
background(0,0,100);
HmWd.display();
}
AnArray class:
class AnArray{
Agent Gems1[];
Agent Gems2[];
Agent Gems3[];
Agent Gems4[];
Agent Gems5[];
float size;
int quant;
int ol = 1;
AnArray(float _size,int _quant){
size = _size;
quant = _quant;
for(int i=0; i<quant;i++){
Gems1[i]=new Agent(size,ol); //I'm getting a "NullPointerException" Error here
ol++;
Gems2[i]=new Agent(size,ol);
ol++;
Gems3[i]=new Agent(size,ol);
ol++;
Gems4[i]=new Agent(size,ol);
ol++;
Gems5[i]=new Agent(size,ol);
ol=1;
}
}
void display(){
for(int i=0; i<quant;i++){
Gems1[i].display();
Gems2[i].display();
Gems3[i].display();
Gems4[i].display();
Gems5[i].display();
}
}
}
Agent class:
class Agent{
PVector position, speed;
float r;
float highlightHue, basicHue;
Agent(float _r,int c){
position = new PVector((random(100,900)),(random(100,900)));
r = _r; ///radius is for test intersection
speed = new PVector(2,2);
highlightHue = 72+(5*c);
basicHue = 190+(5*c);
hue = basicHue;
}
PVector getCenter(){
PVector center = new PVector();
center.x = position.x ;
center.y = position.y ;
return center;
}
void highlight(){
hue = highlightHue;
}
void reverseSpeed(){
speed.x *= -1;
}
void display(){
colorMode(HSB);
fill(hue, 200,200,alpha);
stroke(50, alpha);
ellipse(position.x,position.y, 2*r,2*r);
hue= basicHue;
}
void move(){
if( position.x >width || position.x < 0){
speed.x *= -1;
}
if( position.y > height || position.y < 0){
speed.y *= -1;
}
position.add(speed);
}
void grow(){
if( r < 150){
r++;
alpha = alpha > 50? alpha -= 2: 50; //ternary assignment operator
}
}
void shrink(){
if( r>10){
r--;
alpha = alpha< 200? alpha +=2: 200;
}
}
boolean intersect( Agent a){
boolean isIntersect = false;
float d = PVector.dist(position,a.position);
if(d < this.r + a.r){
isIntersect= true;
}
return isIntersect;
}
}
I am writing a ray tracer.I am currently working on reflections.But the seem not to be reflecting correctly.I keep on getting StackOverflowError.I increased the memory and it runs now but the reflections are not like I thought the would be this.
(source: ageofarmour.com)
I thought it would Reflect the reflections!But it just ends up like this.
Note:This is after moved the normal off the object and changed the color calculations!Check the Cal_Reflection for new color calculation!
Here is my code for my tracer!
public class Tracer {
public boolean Tracing;
public Camera Cam;
public int Width, Height;
public BufferedImage Image;
public Color BackGroundColor;
public int StartX, StartY, EndX, EndY, RowCount, ColCount;
public double AmbientLight;
public double DiffuseLight;
public int MaxReflectionCount;
public ArrayList<GeometricObject> GeoObjects;
public ArrayList<LightObject> LightObjects;
public Tracer(Camera cam, int width, int height, BufferedImage image, Color backGroundColor, int startX, int startY, int endX, int endY, int rowCount, int colCount, double ambientLight, double diffuseLight, int maxReflectionCount, ArrayList<GeometricObject> geoObjects, ArrayList<LightObject> lightObjects) {
super();
Cam = cam;
Width = width;
Height = height;
Image = image;
BackGroundColor = backGroundColor;
StartX = startX;
StartY = startY;
EndX = endX;
EndY = endY;
RowCount = rowCount;
ColCount = colCount;
AmbientLight = ambientLight;
DiffuseLight = diffuseLight;
MaxReflectionCount = maxReflectionCount;
GeoObjects = geoObjects;
LightObjects = lightObjects;
}
public void TracePixelFast(int x, int y) {
Color color = new Color(BackGroundColor.r, BackGroundColor.g, BackGroundColor.b);
for (int o = 0; o < GeoObjects.size(); o++) {
GeometricObject GO = GeoObjects.get(o);
Ray r = new Ray(Cam.GetRayPos(Width, Height, x, y, 1, 1, RowCount, ColCount), Cam.GetRayDir(Width, Height, x, y, 1, 1, RowCount, ColCount));
double hit = GO.hit(r);
if (hit != 0.0) {
color = Cal_Pixel(x, y);
Image.setRGB(x, y, color.toInt());
break;
}
}
}
public void TracePixelSmooth(int x, int y) {
Image.setRGB(x, y, Cal_Pixel(x, y).toInt());
}
public Color Cal_Pixel(int x, int y) {
Color color = new Color(BackGroundColor);
Color colorh = new Color(BackGroundColor);
Color bgc = new Color(BackGroundColor);
int HIT = 0;
int MISS = 0;
for (int row = 0; row < RowCount; row++) {
for (int col = 0; col < ColCount; col++) {
double min = Double.MAX_VALUE;
Ray r = new Ray(Cam.GetRayPos(Width, Height, x, y, row, col, RowCount, ColCount), Cam.GetRayDir(Width, Height, x, y, row, col, RowCount, ColCount));
for (int o = 0; o < GeoObjects.size(); o++) {
GeometricObject GO = GeoObjects.get(o);
double hit = GO.hit(r);
if (hit != 0.0 && hit < min) {
min = hit;
colorh = ShadePixel(0, GO, r, hit);
HIT++;
} else {
double min2 = Double.MAX_VALUE;
for (int o2 = 0; o2 < GeoObjects.size(); o2++) {
if (o != o2) {
GeometricObject GO2 = GeoObjects.get(o2);
double hit2 = GO2.hit(r);
if (hit2 != 0.0 && hit2 < min2) {
min2 = hit2;
bgc = ShadePixel(0, GO2, r, hit2);
}
}
}
MISS++;
}
}
}
}
for (int h = 0; h < HIT; h++) {
color.Add(colorh);
}
for (int m = 0; m < MISS; m++) {
color.Add(bgc);
}
color.Divide(RowCount * ColCount);
return color;
}
public Color ShadePixel(int ReflectionDepthCount, GeometricObject GO, Ray ray, double t) {
Normal normal = GO.Cal_Normal(ray, t);
if (GO.Reflectivity > 0) {
Color GoColor = new Color(Cal_Reflection(GO, ReflectionDepthCount, ray, normal));
Color finalcolor = new Color(Cal_Light(GoColor, normal));
return finalcolor;
} else {
;
Color finalcolor = new Color(Cal_Light(GO.Color, normal));
return finalcolor;
}
}
public Color Cal_Light(Color color, Normal normal) {
ArrayList<Color> PixelShade = new ArrayList<Color>();
Color Final = new Color();
for (int l = 0; l < LightObjects.size(); l++) {
LightObject light = LightObjects.get(l);
Vector3D r_Dir = light.Pos.Sub(normal.Origin);
r_Dir.normalize();
Ray raytolight = new Ray(normal.Origin, r_Dir);
int WAS_HIT = 0;
for (int o = 0; o < GeoObjects.size(); o++) {
GeometricObject NGO = GeoObjects.get(o);
double hit = NGO.hit(raytolight);
if (hit != 0.0) {
WAS_HIT = 1;
}
}
PixelShade.add(light.ShadePixel(WAS_HIT, normal, r_Dir, color, AmbientLight, DiffuseLight));
}
for (int s = 0; s < PixelShade.size(); s++) {
Final.Add(PixelShade.get(s));
}
Final.Divide(PixelShade.size());
return Final;
}
public Color Cal_Reflection(GeometricObject OriginalObject, int ReflectionDepthCount, Ray InRay, Normal normal) {
if (ReflectionDepthCount <= MaxReflectionCount) {
GeometricObject LastGO = null;
Ray LastRay = null;
double LastT = 0.0;
double min = Double.MAX_VALUE;
Vector3D Origin = normal.Origin.Add(normal.Direction.Mul(1E-100));
Vector3D Direction = normal.Direction;
Direction.normalize();
Ray r = new Ray(Origin, Direction);
for (int o = 0; o < GeoObjects.size(); o++) {
GeometricObject GO = GeoObjects.get(o);
double hit = GO.hit(r);
if (hit != 0.0 && hit < min) {
min = hit;
LastGO = GO;
LastRay = r;
LastT = hit;
}
}
if (LastGO != null) {
System.out.println(ReflectionDepthCount);
Color Reflected = new Color(ShadePixel(ReflectionDepthCount++, LastGO, LastRay, LastT));
Color HitColor = new Color(LastGO.Color);
Color FinalColor = new Color(OriginalObject.Color);
Reflected.Mul(OriginalObject.Reflectivity);
HitColor.Mul(OriginalObject.Reflectivity);
FinalColor.Add(HitColor);
FinalColor.Add(Reflected);
FinalColor.Divide(2);
return FinalColor;
}
} else {
return BackGroundColor;
}
return OriginalObject.Color;
}
public void TraceArea(boolean SmoothTracing) {
Tracing = true;
if (SmoothTracing) {
for (int x = StartX; x < EndX; x++) {
for (int y = StartY; y < EndY; y++) {
TracePixelSmooth(x, y);
}
}
} else {
for (int x = StartX; x < EndX; x++) {
for (int y = StartY; y < EndY; y++) {
TracePixelFast(x, y);
}
}
}
}}
Here is my code for my sphere!
public class Sphere extends GeometricObject{
public Vector3D Center;
public double Radius;
public Sphere(Vector3D Center,Color Color,double Radius,double Reflectivity){
this.Center = Center;
this.Radius = Radius;
this.Color = Color;
this.Reflectivity = Reflectivity;
}
public double hit(Ray ray) {
double a = ray.Direction.Dot(ray.Direction);
double b = 2 * ray.Origin.Sub(Center).Dot(ray.Direction);
double c = ray.Origin.Sub(Center).Dot(ray.Origin.Sub(Center))-Radius*Radius;
double discreminant = b*b-4*a*c;
if(discreminant < 0.0f){
return 0.0;
}else{
double t = (-b - Math.sqrt(discreminant))/(2*a);
if(t > 10E-9){
return t;
}else{
return 0.0;
}
}
}
public Normal Cal_Normal(Ray ray,double t) {
Vector3D NPos = new Vector3D(ray.Origin.x + ray.Direction.x*t,ray.Origin.y + ray.Direction.y*t,ray.Origin.z + ray.Direction.z*t);
Vector3D NDir = NPos.Sub(Center).Div(Radius);
NDir.normalize();
return new Normal(NPos,NDir);
}}
And here is my launcher that controls the scene and tracer!
public class Launcher {
public static int Width = 600;
public static int Height = 600;
public static void main(String[] args) {
Scene scene = new Scene(Width, Height, 8, 8, new Color(0, 0, 0), new Camera(new Vector3D(0, 0, 30), new Vector3D(0.3, 0, -1), 1, true, Width, Height, 40), 0.1, 0.2, 2);
// scene.AddObject(new Sphere(new Vector3D(0,0,0),new
// Color(0,255,0),5,1));
// scene.AddObject(new Sphere(new Vector3D(-30,0,0),new
// Color(0,0,255),10,0.5));
// scene.AddObject(new Sphere(new Vector3D(30,0,0),new
// Color(255,0,0),10,0.5));
scene.AddObject(new Sphere(new Vector3D(15, 0, 0), new Color(255, 0, 0), 15, 1));
scene.AddObject(new Sphere(new Vector3D(-15, 0, 0), new Color(0, 0, 255), 15, 1));
scene.AddLight(new NonColoredLight(new Vector3D(0, 0, 20), 0.1));
long Start = System.currentTimeMillis();
BufferedImage Image = scene.Trace(false, 2);
long End = System.currentTimeMillis();
System.out.println("Milli Seconds To Render " + (End - Start));
File ImageFile = new File("TracedImage.png");
try {
ImageIO.write(Image, "PNG", ImageFile);
} catch (IOException e) {
e.printStackTrace();
}
}}
And here is the scene code!
public Scene(int width, int height, int row, int col, Color backGroundColor, Camera cam, double ambientLight, double diffuseLight, int maxReflectionCount) {
super();
Width = width;
Height = height;
Row = row;
Col = col;
BackGroundColor = backGroundColor;
Cam = cam;
AmbientLight = ambientLight;
DiffuseLight = diffuseLight;
MaxReflectionCount = maxReflectionCount;
GeoObjects = new ArrayList<GeometricObject>();
LightObjects = new ArrayList<LightObject>();
if (ambientLight > 1) {
AmbientLight = 1;
} else if (ambientLight < 0) {
AmbientLight = 0;
} else {
AmbientLight = ambientLight;
}
if (diffuseLight > 1) {
DiffuseLight = 1;
} else if (diffuseLight < 0) {
DiffuseLight = 0;
} else {
DiffuseLight = ambientLight;
}
}
public void AddObject(GeometricObject GO) {
GeoObjects.add(GO);
}
public void AddLight(LightObject Light) {
LightObjects.add(Light);
}
public BufferedImage Trace(boolean SmoothTracing, int ThreadCount) {
Image = new BufferedImage(Width, Height, BufferedImage.TYPE_INT_RGB);
Tracer tracer = new Tracer(Cam, Width, Height, Image, BackGroundColor, 0, 0, Width, Height, Row, Col, AmbientLight, DiffuseLight, MaxReflectionCount, GeoObjects, LightObjects);
tracer.TraceArea(SmoothTracing);
return Image;
}}
If you need me to post any more of my code just let me know!
Thanks in advance!
Variables for first reflection!
AmbientLight 0.1
BackGroundColor Color (id=39)
b 0.0
g 0.0
r 0.0
Cam Camera (id=41)
Direction Vector3D (id=104)
x 0.3
y 0.0
z -1.0
Distance 357.526077778263
FOV 40.0
Height 600
Perspective true
PixelSize 1.0
Pos Vector3D (id=115)
x 0.0
y 0.0
z 30.0
u Vector3D (id=116)
x -0.9578262852211514
y 0.0
z -0.28734788556634544
v Vector3D (id=117)
x 0.0
y 1.0
z 0.0
w Vector3D (id=118)
x 0.2873478855663454
y 0.0
z -0.9578262852211513
Width 600
ColCount 8
DiffuseLight 0.1
EndX 600
EndY 600
GeoObjects ArrayList<E> (id=43)
[0] Sphere (id=26)
[1] Sphere (id=34)
Height 600
Image BufferedImage (id=50)
accelerationPriority 0.5
colorModel DirectColorModel (id=120)
imageType 1
osis null
properties null
raster IntegerInterleavedRaster (id=124)
surfaceManager null
LightObjects ArrayList<E> (id=59)
[0] NonColoredLight (id=88)
MaxReflectionCount 2
RowCount 8
StartX 0
StartY 0
Tracing true
Width 600
OriginalObject Sphere (id=26)
Center Vector3D (id=60)
Color Color (id=61)
Radius 15.0
Reflectivity 1.0
ReflectionDepthCount 0
InRay Ray (id=29)
Direction Vector3D (id=62)
Origin Vector3D (id=63)
normal Normal (id=31)
Direction Vector3D (id=38)
Origin Vector3D (id=36)
LastGO Sphere (id=34)
Center Vector3D (id=64)
Color Color (id=65)
Radius 15.0
Reflectivity 1.0
LastRay Ray (id=35)
Direction Vector3D (id=38)
Origin Vector3D (id=36)
LastT 4.2468950498166125
min 4.2468950498166125
Origin Vector3D (id=36)
x 0.5809721247344103
y -0.023188722729640822
z 0.8135930637731328
Direction Vector3D (id=38)
x -0.32494278056317
y -0.02694400302823308
z 0.9453497818589108
r Ray (id=35)
Direction Vector3D (id=38)
Origin Vector3D (id=36)
Project Zip File
You are reflecting your ray exactly on the surface of the object. When checking intersections with the reflected ray, you hit on the same object again. You assume that checking if the distance is equal to 0.0 is enough to avoid this, but FP numbers are trickier than you think...
I am getting strange rings of black on my spheres when I render with lighting. I just added lighting and I cannot figure out why the black rings are being created.
Here is my code for my tracer.
public class Tracer {
public Camera Cam;
public int Width, Height;
public BufferedImage Image;
public Color BackGroundColor;
public int StartX, StartY, EndX, EndY,RowCount,ColCount;
public ArrayList<GeometricObject> GeoObjects;
public ArrayList<LightObject> LightObjects;
public boolean Tracing;
public double AmbientLight;
public Tracer(Camera cam, int width, int height, BufferedImage image, Color backGroundColor, int startX, int startY, int endX, int endY, int rowCount, int colCount, ArrayList<GeometricObject> Geoobjects,ArrayList<LightObject> Lightobjects,double ambientLight) {
super();
Cam = cam;
Width = width;
Height = height;
Image = image;
BackGroundColor = backGroundColor;
StartX = startX;
StartY = startY;
EndX = endX;
EndY = endY;
RowCount = rowCount;
ColCount = colCount;
GeoObjects = Geoobjects;
LightObjects = Lightobjects;
if(ambientLight > 1){
AmbientLight = 1;
}else if(ambientLight < 0){
AmbientLight = 0;
}else{
AmbientLight = ambientLight;
}
}
public void TracePixelFast(int x, int y) {
Color color = new Color(BackGroundColor.r,BackGroundColor.g,BackGroundColor.b);
for(int o = 0;o < GeoObjects.size();o++){
GeometricObject GO = GeoObjects.get(o);
Ray r = new Ray(Cam.GetRayPos(Width, Height, x, y, 1, 1, RowCount, ColCount), Cam.GetRayDir(Width, Height, x, y, 1,1, RowCount, ColCount));
double hit = GO.hit(r);
if (hit != 0.0) {
color = Cal_Pixel(x,y);
Image.setRGB(x, y, color.toInt());
break;
}
}
}
public void TracePixelSmooth(int x, int y) {
Image.setRGB(x, y,Cal_Pixel(x,y).toInt());
}
public Color Cal_Pixel(int x,int y){
Color color = new Color(BackGroundColor);
Color colorh = new Color(BackGroundColor);
Color bgc = new Color(BackGroundColor);
int HIT = 0;
int MISS = 0;
for (int row = 0; row < RowCount; row++) {
for (int col = 0; col < ColCount; col++) {
double min = Double.MAX_VALUE;
for (int o = 0; o < GeoObjects.size(); o++) {
GeometricObject GO = GeoObjects.get(o);
Ray r = new Ray(Cam.GetRayPos(Width, Height, x, y, row, col, RowCount, ColCount),Cam.GetRayDir(Width, Height, x, y, row, col, RowCount, ColCount));
double hit = GO.hit(r);
if (hit != 0.0 && hit < min) {
min = hit;
colorh = ShadePixel(GO, r, hit);
HIT++;
} else {
double min2 = Double.MAX_VALUE;
for (int o2 = 0; o2 < GeoObjects.size(); o2++) {
if(o!=o2){
GeometricObject GO2 = GeoObjects.get(o2);
double hit2 = GO2.hit(r);
if (hit2 != 0.0 && hit2 < min2) {
min2 = hit2;
bgc = ShadePixel(GO2, r, hit2);
}
}
}
MISS++;
}
}
}
}
for(int h = 0;h < HIT;h++){
color.Add(colorh);
}
for(int m = 0;m < MISS;m++){
color.Add(bgc);
}
color.Divide(RowCount * ColCount);
return color;
}
public Color ShadePixel(GeometricObject GO,Ray ray,double t){
ArrayList<Color> PixelShade = new ArrayList<Color>();
Normal normal = GO.Cal_Normal(ray, t);
for(int l = 0;l < LightObjects.size();l++){
LightObject light = LightObjects.get(l);
Vector3D r_Dir = light.Pos.Sub(normal.Origin);
r_Dir.normalize();
Ray raytolight = new Ray(normal.Origin,r_Dir);
int WAS_HIT = 0;
for(int o = 0;o < GeoObjects.size();o++){
GeometricObject NGO = GeoObjects.get(o);
double hit = NGO.hit(raytolight);
if (hit != 0.0) {
WAS_HIT = 1;
}
}
if(WAS_HIT == 0){
double Dot = normal.Direction.Dot(r_Dir);
if(Dot < 0){
Dot = 0;
}
double Diffuse = 1 - AmbientLight;
Color color = new Color(GO.Color);
double Shade = AmbientLight + Diffuse*Dot;
color.Mul(Shade);
PixelShade.add(color);
}else{
Color color = new Color(GO.Color);
double Shade = AmbientLight;
color.Mul(Shade);
PixelShade.add(color);
}
}
Color Final = new Color();
for(int s = 0;s < PixelShade.size();s++){
Final.Add(PixelShade.get(s));
}
Final.Divide(PixelShade.size());
return Final;
}
public void TraceArea(boolean SmoothTracing) {
Tracing = true;
if(SmoothTracing){
for (int x = StartX; x < EndX; x++) {
for (int y = StartY; y < EndY; y++) {
TracePixelSmooth(x,y);
}
}
}else{
for (int x = StartX; x < EndX; x++) {
for (int y = StartY; y < EndY; y++) {
TracePixelFast(x,y);
}
}
}
}
}
And here is the code for the sphere.
public class Sphere extends GeometricObject{
public Vector3D Center;
public double Radius;
public Sphere(Vector3D Center,double Radius,Color Color){
this.Center = Center;
this.Radius = Radius;
this.Color = Color;
}
public double hit(Ray ray) {
double a = ray.Direction.Dot(ray.Direction);
double b = 2 * ray.Origin.Sub(Center).Dot(ray.Direction);
double c = ray.Origin.Sub(Center).Dot(ray.Origin.Sub(Center))-Radius*Radius;
double discreminant = b*b-4*a*c;
if(discreminant < 0.0f){
return 0.0;
}else{
double t = (-b - Math.sqrt(discreminant))/(2*a);
if(t > 10E-9){
return t;
}else{
return 0.0;
}
}
}
public Normal Cal_Normal(Ray ray,double t) {
Vector3D NPos = new Vector3D(ray.Origin.x + ray.Direction.x*t,ray.Origin.y + ray.Direction.y*t,ray.Origin.z + ray.Direction.z*t);
Vector3D NDir = NPos.Sub(Center).Div(Radius);
return new Normal(NPos,NDir);
}
}
I am sure the problem is in shadepixel() but I could be wrong.
I just found out that the more objects that I add the more rings there are:
1 object no rings.
2 objects 1 ring.
3 objects 2 rings.
If you need me to post more of my code.Just ask and I will.
When I get back from school I will post my color class and fix the color problem. I still do not understand why the more objects (spheres) I add, the more rings there are. Can anyone explain to me why this is happening?
Here is my Color code.
public class Color {
public float r,g,b;
public Color(){
r = 0.0f;
g = 0.0f;
b = 0.0f;
}
public Color(float fr,float fg,float fb){
r = fr;
g = fg;
b = fb;
}
public Color(Color color){
r = color.r;
g = color.g;
b = color.b;
}
public void Add(Color color){
r += color.r;
g += color.g;
b += color.b;
}
public void Divide(int scalar){
r /= scalar;
g /= scalar;
b /= scalar;
}
public void Mul(double mul){
r *= mul;
g *= mul;
b *= mul;
}
public int toInt(){
return (int) (r*255)<<16 | (int) (g*255)<<8 | (int) (b*255);
}
There are multiple issues with this code, but the direct reason for the rings is that color component values are overflowing 0-255 range. This in turn is caused by incorrect calculations in what I take to be an attempt at antialiasing in Cal_Pixel(), as well as by no control whatsoever of numeric range in ShadePixel().
Think about how you can visually debug this scene.
First are the normals correct? Display them as the colour to see.
Taking the range for each component [-1..1] to the range [0..255]:
r = 255*(n.x + 1)/2;
g = 255*(n.y + 1)/2;
b = 255*(n.z + 1)/2;
Once you think they look correct move on to the next stage and build it up stage by stage.
e.g. you might look at if your dot product is as expected (again [-1..1] because the vectors are supposedly normalised):
r = 255*(dot + 1)/2;
g = 255*(dot + 1)/2;
b = 255*(dot + 1)/2;