I would like to Draw an Regular Polygon inscrit , and I have wrote some code with the usually formula angle = 2*Math.Pi/numside ecc etc.
The Poylgon it's drawn with the circle dynamically with mouse movement in this way:
public void Set_beahvior(Pane DrawPane, BottomPane bottompane )
{
this.selectedProperty().addListener(new ChangeListener<Boolean>()
{
private final EventHandler<MouseEvent> pressedHandler = (event) -> {
cerchio = new MyCircle(event.getX(), event.getY(), 0.0, DrawPane ,bottompane1);
cerchio.setCenterX(event.getX());
cerchio.setCenterY(event.getY());
cerchio.setStrokeType(StrokeType.OUTSIDE);
cerchio.setStroke(Color.RED);
cerchio.setFill(Color.TRANSPARENT);
cerchio.setStrokeWidth(1);
currentPolygon= new MyPolygon2(DrawPane,bottompane);
DrawPane.getChildren().addAll(cerchio);
DrawPane.getChildren().addAll(currentPolygon);
};
private final EventHandler<MouseEvent> draggedHandler = (event) -> {
cerchio.setRadius((event.getX()-cerchio.getCenterX()));
currentPolygon.setPolygon(cerchio.getCenterX(), cerchio.getCenterY(), cerchio.getRadius(),cerchio);
};
private final EventHandler<MouseEvent> releasedHandler = (event) -> {
cerchio.setRadius((event.getX()-cerchio.getCenterX()));
cerchio.setStroke(Color.TRANSPARENT);
};
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
// add/remove event handlers based on toggle state of the button
if (newValue) {
DrawPane.addEventHandler(MouseEvent.MOUSE_PRESSED, pressedHandler);
DrawPane.addEventHandler(MouseEvent.MOUSE_DRAGGED, draggedHandler);
DrawPane.addEventHandler(MouseEvent.MOUSE_RELEASED, releasedHandler);
} else {
DrawPane.removeEventHandler(MouseEvent.MOUSE_PRESSED, pressedHandler);
DrawPane.removeEventHandler(MouseEvent.MOUSE_DRAGGED, draggedHandler);
DrawPane.removeEventHandler(MouseEvent.MOUSE_RELEASED, releasedHandler);
}
}
});
And another pieace of code :
public void setPolygon(Double x, Double y, Double radius, MyCircle circle){
DoubleProperty raggio = new SimpleDoubleProperty(circle.getRadius());
double section = 2.0 * Math.PI/Numside;
this.getPoints().addAll((x + raggio.doubleValue() * Math.cos(0)), (y + raggio.doubleValue() * Math.sin(0)));
for(int i=1; i<Numside; i++){
this.getPoints().addAll((x + raggio.doubleValue() * Math.cos(section * i)), (y + raggio.doubleValue() * Math.sin(section * i)));
}
}
THE PROBLEM: I started with 3 side for then I'll write the code for a number of side generic, but All going fine if I increase dynamically the radius of circle (increment the radius -> triangle increase ).
But if I decrease the radius of circle, the polygon doesn't follow the behaviuor of the circle, and I can't understand why, any suggestion please?
PS: please ignore the Doubleproperty and some code written only for a test, I know that if I pass the circle object I don't need the other parameter. At the moment I'm looking at the reason why the polygon does not follow the circumference.
You never clear any of the points from the Polygon. You need to make sure the coordinates in the points list remains twice the number of sides. Furthermore the parameters of setPolygon are not defined well: You pass the same info though the first 3 and the last parameter; also you're using the reference type Double for no obvious reason. The following code improves on the code a bit:
private final int numside;
private final double[] points;
public MyPolygon2(int numside) {
this.numside = numside;
this.points = new double[numside * 2];
double step = 2 * Math.PI / numside;
for (int i = 0; i < numside; i++) {
int index = 2 * i;
double angle = i * step;
points[index] = Math.cos(angle);
points[index + 1] = Math.sin(angle);
}
}
public void setPolygon(double x, double y, double radius) {
getPoints().clear();
for (int i = 0; i < points.length;) {
getPoints().addAll(x + radius * points[i++], y + radius * points[i++]);
}
}
Related
i have made a game with a spaceship but i have a problem with moving of it.
this is the script:
public class spaceship {
private final local_client local_client;
private final int startX;
private final int startY;
private final int startR;
private double x,y,r;
public static double rotation;
public static double velo;
public static AffineTransform at;
public spaceship(local_client local_client,int startX,int startY,int startR) {
this.local_client = local_client;
this.startX = startX;
this.startY = startY;
this.startR = startR;
x=200;
y=200;
r=90;
}
public void drawImage(Graphics2D g2d) {
g2d.drawImage(local_client.spaceship_img,200,200, local_client.game);
at = g2d.getTransform();
at.translate(x , y);
at.rotate(Math.toRadians(r));
at.translate(-(local_client.spaceship_img.getWidth()/2),-(local_client.spaceship_img.getHeight()/2));
System.out.println(at.getTranslateX() + " - " + at.getTranslateY());
g2d.setTransform(at);
g2d.drawImage(local_client.spaceship_img,0,0, local_client.game);
}
So with this script i can rotate image (see the screen):
http://gyazo.com/c982b17afbba8cdc65e7786bd1cbea47
My problem is that if i increment X or Y move on the normal axis (screen) :
http://gyazo.com/1fb2efb5aff9e95c56e7dddb6a30df4a
and not in diagonal
at.translate(x , y);
This line of code moves your ship around, correct? Incrementing x and y will only move them up and down according to the axis.
You will have to use trigonometry like you have with your rotations to calculate the actual x and y to move if you want to move along the ship's path.
The following is the approximate pseudocode that should get you in the right direction. May or may not be accurate depending on your r.
//drive along the ship's nose line
void moveAlongShipAxis(int magnitude) {
x += magnitude * Math.cos(Math.toRadians(r));
y += magnitude * Math.sin(Math.toRadians(r));
}
//strafe left and right, if you have that, otherwise you can skip this one
void moveYAlongTransverseAxis(int magnitude) {
y += magnitude * Math.cos(Math.toRadians(r));
x += magnitude * Math.sin(Math.toRadians(r));
}
So I'm trying to map the pixel on the rgbMap to one on the depthMap so I can get the depth from a color tracked object.
I've noticed the depth map is smaller than the rgb map in addition to the different POV from being on a different spot on the kinect itself.
Is there an efficient way to do this that I'm unaware of? Currently I'm thinking of trying to line up the example pixels and then try to figure out an equation for the offset as you get farther away.
Code below, please not that the color tracking is far from done, and that I'm using mouse pressed for testing the pixel position.
/**
* ControlP5 Controlframe
* with controlP5 2.0 all java.awt dependencies have been removed
* as a consequence the option to display controllers in a separate
* window had to be removed as well.
* this example shows you how to create a java.awt.frame and use controlP5
*
* by Andreas Schlegel, 2012
* www.sojamo.de/libraries/controlp5
*
*/
import java.awt.Frame;
import java.awt.BorderLayout;
import controlP5.*;
import SimpleOpenNI.*;
private ControlP5 cp5;
ControlFrame cf;
SimpleOpenNI context;
Ktracker p,o,b;
float rx,ry,dx,dy;
int def;
void setup() {
size((2 * 640),480);
cp5 = new ControlP5(this);
o = new Ktracker(188,57,49);
// by calling function addControlFrame() a
// new frame is created and an instance of class
// ControlFrame is instanziated.
cf = addControlFrame("Output", 640,480);
// add Controllers to the 'extra' Frame inside
// the ControlFrame class setup() method below.
context = new SimpleOpenNI(this);
context.enableRGB();
context.enableDepth();
smooth();
rx=context.rgbWidth()/2;
ry=height/2;
}
void draw() {
context.update();
background(0,150,0);
image(context.depthImage(),context.rgbWidth(),0);
image(context.rgbImage(), 0,0);
int[] dm = context.depthMap();
o.run();
rectMode(CENTER);
noStroke();
fill(255,255,0);
rect(rx,ry,5,5);
dx=rx;
dy=ry;
fill(0,255,255);
rect(dx+context.rgbWidth(),dy,5,5);
rectMode(CORNER);
if(o.wr<10){
fill(0,0,255);
noStroke();
ellipse(o.currentPoints[0]+context.rgbWidth(),o.currentPoints[1],10,10);
cf.z = dm[int(o.currentPoints[0]*o.currentPoints[1])];
} else {
cf.z=0;
}
}
void mousePressed(){
rx=mouseX;
ry=mouseY;
}
ControlFrame addControlFrame(String theName, int theWidth, int theHeight) {
Frame f = new Frame(theName);
ControlFrame p = new ControlFrame(this, theWidth, theHeight);
f.add(p);
p.init();
f.setTitle(theName);
f.setSize(p.w, p.h);
f.setLocation(100, 100);
f.setResizable(false);
f.setVisible(true);
return p;
}
// the ControlFrame class extends PApplet, so we
// are creating a new processing applet inside a
// new frame with a controlP5 object loaded
public class ControlFrame extends PApplet {
int w, h;
float z;
int abc = 100;
public void setup() {
size(w, h, P3D);
frameRate(25);
z=0;
}
public void draw() {
background(abc);
translate(width/2,height/2,z/100);
sphere(250);
}
private ControlFrame() {
}
public ControlFrame(Object theParent, int theWidth, int theHeight) {
parent = theParent;
w = theWidth;
h = theHeight;
}
public ControlP5 control() {
return cp5;
}
ControlP5 cp5;
Object parent;
}
class Ktracker{
float mx,my,wr;
// A variable for the color we are searching for.
color trackColor;
float[] currentPoints, prevPoints;
Ktracker(int r,int g, int b){
trackColor = color(r,g,b);
currentPoints = new float[2];
prevPoints = new float[2];
prevPoints[0]=0;
prevPoints[1]=0;
}
void run(){
loadPixels();
// Before we begin searching, the "world record" for closest color is set to a high number that is easy for the first pixel to beat.
float worldRecord = 500;
// XY coordinate of closest color
int closestX = 0;
int closestY = 0;
// Begin loop to walk through every pixel
for (int x = 0; x < width/2; x ++ ) {
for (int y = 0; y < height; y ++ ) {
int loc = x + y*width;
// What is current color
color currentColor = pixels[loc];
float r1 = red(currentColor);
float g1 = green(currentColor);
float b1 = blue(currentColor);
float r2 = red(trackColor);
float g2 = green(trackColor);
float b2 = blue(trackColor);
// Using euclidean distance to compare colors
float d = dist(r1,g1,b1,r2,g2,b2); // We are using the dist( ) function to compare the current color with the color we are tracking.
// If current color is more similar to tracked color than
// closest color, save current location and current difference
if (d < worldRecord) {
worldRecord = d;
closestX = x;
closestY = y;
currentPoints[0] = closestX;
currentPoints[1] = closestY;
}
}
}
wr=worldRecord;
// We only consider the color found if its color distance is less than 10.
// This threshold of 10 is arbitrary and you can adjust this number depending on how accurate you require the tracking to be.
if (worldRecord < 10) {
// Draw a circle at the tracked pixel
fill(255,0,0);
//strokeWeight(4.0);
stroke(0,0);
ellipse(currentPoints[0],currentPoints[1],16,16);
}
println("wr: "+wr);
println("worldRecord: "+worldRecord);
}
}
If you want to align the depth map with the RGB map you need to simply tell OpenNI to do so fo you in setup:
context.alternativeViewPointDepthToImage();
Have a look at the AlternativeViewpoint3d sketch in Examples > SimpleOpenNI > OpenNI
OpenNI has the registration done already so all you need to do is enable it when you need it.
My final goal is to have a method, lets say:
Rectangle snapRects(Rectangle rec1, Rectangle rec2);
Imagine a Rectangle having info on position, size and angle.
Dragging and dropping the ABDE rectangle close to the BCGF rectangle would call the method with ABDE as first argument and BCGF as second argument, and the resulting rectangle is a rectangle lined up with BCGF's edge.
The vertices do not have to match (and preferrably won't so the snapping isn't so restrictive).
I can only understand easily how to give the same angle, but the position change is quite confusing to me. Also, i believe even if i reached a solution it would be quite badly optimized (excessive resource cost), so I would appreciate guidance on this.
(This has already been asked but no satisfatory answer was given and the question forgotten.)
------------------------------------------------------------------
Edit: It seems my explanation was insufficient so I will try to clarify my wishes:
The following image shows the goal of the method in a nutshell:
Forget about "closest rectangle", imagine there are just two rectangles. The lines inside the rectangles represent the direction they are facing (visual aid for the angle).
There is a static rectangle, which is not to be moved and has an angle (0->360), and a rectangle (also with an angle) which I want to Snap to the closest edge of the static rectangle. By this, i mean, i want the least transformations possible for the "snap to edge" to happen.
This brings many possible cases, depending on the rotation of the rectangles and their position relative to each other.
The next image shows the static rectangle and how the position of the "To Snap" rectangle changes the snapping result:
The final rotations might not be perfect since it was done by eye, but you get the point, it matters the relative position and also both angles.
Now, in my point of view, which may be completely naive, I see this problem solved on two important and distinct steps on transforming the "To Snap" rectangle: Positioning and Rotation
Position: The objective of the new position is to stick to the closest edge, but since we want it to stick paralell to the static rectangle, the angle of the static rectangle matters. The next image shows examples of positioning:
In this case, the static rectangle has no angle, so its easy to determine up, down, left and right. But with angle, there are alot more possibilities:
As for the rotation, the goal is for the "to snap" rectangle to rotate the minimum needed to become paralell with the static rectangle:
As a final note, in regard of implementation input, the goal is to actually drag the "to snap" rectangle to wherever position i wish around the static rectangle and by pressing a keyboard key, the snap happens.
Also, it appears i have exagerated a little when i asked for optimization, to be honest i do not need or require optimization, I do prefer an easy to read, step by step clear code (if its the case), rather than any optimization at all.
I hope i was clear this time, sorry for the lack of clarity in the first place, if you have any more doubts please do ask.
The problem is obviously underspecified: What does "line up" for the edges mean? A common start point (but not necessarily a common end point)? A common center point for both edges? (That's what I assumed now). Should ALL edges match? What is the criterion for deciding WHICH edge of the first rectangle should be "matched" with WHICH edge of the second rectangle? That is, imagine one square consists exactly of the center points of the edges of the other square - how should it be aligned then?
(Secondary question: In how far is optimization (or "low resource cost") important?)
However, I wrote a few first lines, maybe this can be used to point out more clearly what the intended behavior should be - namely by saying in how far the intended behavior differs from the actual behavior:
EDIT: Old code omitted, update based on the clarification:
The conditions for the "snapping" are still not unambiguous. For example, it is not clear whether the change in position or the change in the angle should be preferred. But admittedly, I did not figure out in detail all possible cases where this question could arise. In any case, based on the updated question, this might be closer to what you are looking for.
NOTE: This code is neither "clean" nor particularly elegant or efficient. The goal until now was to find a method that delivers "satisfactory" results. Optimizations and beautifications are possible.
The basic idea:
Given are the static rectangle r1, and the rectangle to be snapped, r0
Compute the edges that should be snapped together. This is divided in two steps:
The method computeCandidateEdgeIndices1 computes the "candidate edges" (resp. their indices) of the static rectangle that the moving rectangle may be snapped to. This is based on the folowing criterion: It checks how many vertices (corners) of the moving rectangle are right of the particular edge. For example, if all 4 vertices of the moving rectangle are right of edge 2, then edge 2 will be a candidate for snapping the rectangle to.
Since there may be multiple edges for which the same number of vertices may be "right", the method computeBestEdgeIndices computes the candidate edge whose center has the least distance to the center of any edge of the moving rectangle. The indices of the respective edges are returned
Given the indices of the edges to be snapped, the angle between these edges is computed. The resulting rectangle will be the original rectangle, rotated by this angle.
The rotated rectangle will be moved so that the centers of the snapped edges are at the same point
I tested this with several configurations, and the results at least seem "feasible" for me. Of course, this does not mean that it works satisfactory in all cases, but maybe it can serve as a starting point.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class RectangleSnap
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
RectangleSnapPanel panel = new RectangleSnapPanel();
f.getContentPane().add(panel);
f.setSize(1000,1000);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class SnapRectangle
{
private Point2D position;
private double sizeX;
private double sizeY;
private double angleRad;
private AffineTransform at;
SnapRectangle(
double x, double y,
double sizeX, double sizeY, double angleRad)
{
this.position = new Point2D.Double(x,y);
this.sizeX = sizeX;
this.sizeY = sizeY;
this.angleRad = angleRad;
at = AffineTransform.getRotateInstance(
angleRad, position.getX(), position.getY());
}
double getAngleRad()
{
return angleRad;
}
double getSizeX()
{
return sizeX;
}
double getSizeY()
{
return sizeY;
}
Point2D getPosition()
{
return position;
}
void draw(Graphics2D g)
{
Color oldColor = g.getColor();
Rectangle2D r = new Rectangle2D.Double(
position.getX(), position.getY(), sizeX, sizeY);
AffineTransform at = AffineTransform.getRotateInstance(
angleRad, position.getX(), position.getY());
g.draw(at.createTransformedShape(r));
g.setColor(Color.RED);
for (int i=0; i<4; i++)
{
Point2D c = getCorner(i);
Ellipse2D e = new Ellipse2D.Double(c.getX()-3, c.getY()-3, 6, 6);
g.fill(e);
g.drawString(""+i, (int)c.getX(), (int)c.getY()+15);
}
g.setColor(Color.GREEN);
for (int i=0; i<4; i++)
{
Point2D c = getEdgeCenter(i);
Ellipse2D e = new Ellipse2D.Double(c.getX()-3, c.getY()-3, 6, 6);
g.fill(e);
g.drawString(""+i, (int)c.getX(), (int)c.getY()+15);
}
g.setColor(oldColor);
}
Point2D getCorner(int i)
{
switch (i)
{
case 0:
return new Point2D.Double(position.getX(), position.getY());
case 1:
{
Point2D.Double result = new Point2D.Double(
position.getX(), position.getY()+sizeY);
return at.transform(result, null);
}
case 2:
{
Point2D.Double result = new Point2D.Double
(position.getX()+sizeX, position.getY()+sizeY);
return at.transform(result, null);
}
case 3:
{
Point2D.Double result = new Point2D.Double(
position.getX()+sizeX, position.getY());
return at.transform(result, null);
}
}
return null;
}
Line2D getEdge(int i)
{
Point2D p0 = getCorner(i);
Point2D p1 = getCorner((i+1)%4);
return new Line2D.Double(p0, p1);
}
Point2D getEdgeCenter(int i)
{
Point2D p0 = getCorner(i);
Point2D p1 = getCorner((i+1)%4);
Point2D c = new Point2D.Double(
p0.getX() + 0.5 * (p1.getX() - p0.getX()),
p0.getY() + 0.5 * (p1.getY() - p0.getY()));
return c;
}
void setPosition(double x, double y)
{
this.position.setLocation(x, y);
at = AffineTransform.getRotateInstance(
angleRad, position.getX(), position.getY());
}
}
class RectangleSnapPanel extends JPanel implements MouseMotionListener
{
private final SnapRectangle rectangle0;
private final SnapRectangle rectangle1;
private SnapRectangle snappedRectangle0;
RectangleSnapPanel()
{
this.rectangle0 = new SnapRectangle(
200, 300, 250, 200, Math.toRadians(-21));
this.rectangle1 = new SnapRectangle(
500, 300, 200, 150, Math.toRadians(36));
addMouseMotionListener(this);
}
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setColor(Color.BLACK);
rectangle0.draw(g);
rectangle1.draw(g);
if (snappedRectangle0 != null)
{
g.setColor(Color.BLUE);
snappedRectangle0.draw(g);
}
}
#Override
public void mouseDragged(MouseEvent e)
{
rectangle0.setPosition(e.getX(), e.getY());
snappedRectangle0 = snapRects(rectangle0, rectangle1);
repaint();
}
#Override
public void mouseMoved(MouseEvent e)
{
}
private static SnapRectangle snapRects(
SnapRectangle r0, SnapRectangle r1)
{
List<Integer> candidateEdgeIndices1 =
computeCandidateEdgeIndices1(r0, r1);
int bestEdgeIndices[] = computeBestEdgeIndices(
r0, r1, candidateEdgeIndices1);
int bestEdgeIndex0 = bestEdgeIndices[0];
int bestEdgeIndex1 = bestEdgeIndices[1];
System.out.println("Best to snap "+bestEdgeIndex0+" to "+bestEdgeIndex1);
Line2D bestEdge0 = r0.getEdge(bestEdgeIndex0);
Line2D bestEdge1 = r1.getEdge(bestEdgeIndex1);
double edgeAngle = angleRad(bestEdge0, bestEdge1);
double rotationAngle = edgeAngle;
if (rotationAngle <= Math.PI)
{
rotationAngle = Math.PI + rotationAngle;
}
else if (rotationAngle <= -Math.PI / 2)
{
rotationAngle = Math.PI + rotationAngle;
}
else if (rotationAngle >= Math.PI)
{
rotationAngle = -Math.PI + rotationAngle;
}
SnapRectangle result = new SnapRectangle(
r0.getPosition().getX(), r0.getPosition().getY(),
r0.getSizeX(), r0.getSizeY(), r0.getAngleRad()-rotationAngle);
Point2D edgeCenter0 = result.getEdgeCenter(bestEdgeIndex0);
Point2D edgeCenter1 = r1.getEdgeCenter(bestEdgeIndex1);
double dx = edgeCenter1.getX() - edgeCenter0.getX();
double dy = edgeCenter1.getY() - edgeCenter0.getY();
result.setPosition(
r0.getPosition().getX()+dx,
r0.getPosition().getY()+dy);
return result;
}
// Compute for the edge indices for r1 in the given list
// the one that has the smallest distance to any edge
// of r0, and return this pair of indices
private static int[] computeBestEdgeIndices(
SnapRectangle r0, SnapRectangle r1,
List<Integer> candidateEdgeIndices1)
{
int bestEdgeIndex0 = -1;
int bestEdgeIndex1 = -1;
double minCenterDistance = Double.MAX_VALUE;
for (int i=0; i<candidateEdgeIndices1.size(); i++)
{
int edgeIndex1 = candidateEdgeIndices1.get(i);
for (int edgeIndex0=0; edgeIndex0<4; edgeIndex0++)
{
Point2D p0 = r0.getEdgeCenter(edgeIndex0);
Point2D p1 = r1.getEdgeCenter(edgeIndex1);
double distance = p0.distance(p1);
if (distance < minCenterDistance)
{
minCenterDistance = distance;
bestEdgeIndex0 = edgeIndex0;
bestEdgeIndex1 = edgeIndex1;
}
}
}
return new int[]{ bestEdgeIndex0, bestEdgeIndex1 };
}
// Compute the angle, in radians, between the given lines,
// in the range (-2*PI, 2*PI)
private static double angleRad(Line2D line0, Line2D line1)
{
double dx0 = line0.getX2() - line0.getX1();
double dy0 = line0.getY2() - line0.getY1();
double dx1 = line1.getX2() - line1.getX1();
double dy1 = line1.getY2() - line1.getY1();
double a0 = Math.atan2(dy0, dx0);
double a1 = Math.atan2(dy1, dx1);
return (a0 - a1) % (2 * Math.PI);
}
// In these methods, "right" refers to screen coordinates, which
// unfortunately are upside down in Swing. Mathematically,
// these relation is "left"
// Compute the "candidate" edges of r1 to which r0 may
// be snapped. These are the edges to which the maximum
// number of corners of r0 are right of
private static List<Integer> computeCandidateEdgeIndices1(
SnapRectangle r0, SnapRectangle r1)
{
List<Integer> bestEdgeIndices = new ArrayList<Integer>();
int maxRight = 0;
for (int i=0; i<4; i++)
{
Line2D e1 = r1.getEdge(i);
int right = countRightOf(e1, r0);
if (right > maxRight)
{
maxRight = right;
bestEdgeIndices.clear();
bestEdgeIndices.add(i);
}
else if (right == maxRight)
{
bestEdgeIndices.add(i);
}
}
//System.out.println("Candidate edges "+bestEdgeIndices);
return bestEdgeIndices;
}
// Count the number of corners of the given rectangle
// that are right of the given line
private static int countRightOf(Line2D line, SnapRectangle r)
{
int count = 0;
for (int i=0; i<4; i++)
{
if (isRightOf(line, r.getCorner(i)))
{
count++;
}
}
return count;
}
// Returns whether the given point is right of the given line
// (referring to the actual line *direction* - not in terms
// of coordinates in 2D!)
private static boolean isRightOf(Line2D line, Point2D point)
{
double d00 = line.getX1() - point.getX();
double d01 = line.getY1() - point.getY();
double d10 = line.getX2() - point.getX();
double d11 = line.getY2() - point.getY();
return d00 * d11 - d10 * d01 > 0;
}
}
I have a image with blackened circles.
The image is a scanned copy of an survey sheet pretty much like an OMR questionnaire sheet.
I want to detect the circles that have been blackened using the JUI(if any other api required)
I have a few examples while searching, but they dont give me accurate result.
I tried..UDAI,Moodle...etc...
Then I decided to make my own. I am able to detect the black pixels but as follows.
BufferedImage mapa = BMPDecoder.read(new File("testjui.bmp"));
final int xmin = mapa.getMinX();
final int ymin = mapa.getMinY();
final int ymax = ymin + mapa.getHeight();
final int xmax = xmin + mapa.getWidth();
for (int i = xmin;i<xmax;i++)
{
for (int j = ymin;j<ymax;j++)
{
int pixel = mapa.getRGB(i, j);
if ((pixel & 0x00FFFFFF) == 0)
{
System.out.println("("+i+","+j+")");
}
}
}
This gives me the co-ordinates of all the black pixels but i cannot make out if its a circle or not.
How can I identify if its a circle.
2] Also I want to know if the image scanned is tilted....I know that the Udai api takes care of that, but for some reason I am not able to get my survey template to run with that code.
So if I understood correctly, you have code that picks out the black pixels so now you have the coordinates of all black pixels and you want to determine all of those that fall on a circle.
The way I would approach this is in 2 steps.
1) Cluster the pixels. Create a class called Cluster, that contains a list of points and use your clustering algorithm to put all the points in the right cluster.
2) Determine which clusters are circles. To do this find the midpoint of all of the points in each cluster (just take the mean of all the points). Then find the minimum and maximum distances from the center, The difference between these should be less than the maximum thickness for a circle in your file. These will give you the radii for the innermost and outermost circles contained within the circle. Now use the equation of a circle x^2 + y^2 = radius, with the radius set to a value between the maximum and minimum found previously to find the points that your cluster should contain. If your cluster contains these it is a circle.
Of course other considerations to consider is whether the shapes you have approximate ellipses rather than circles, in which case you should use the equation of an ellipse. Furthermore, if your file contains circle-like shapes you will need to write additional code to exclude these. On the other hand if all of your circles are exactly the same size you can cut the work that needs to be done by having your algorithm search for circles of that size only.
I hope I could be of some help, good luck!
To answer your first question, I created a class that checks weather an image contains a single non black filled black outlined circle.
This class is experimental, it does not provide exact results all the time, feel free to edit it and to correct the bugs you might encounter.
The setters do not check for nulls or out of range values.
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
/**
* Checks weather an image contains a single non black filled black outlined circle<br />
* This class is experimental, it does not provide exact results all the time, feel free to edit it and to correct
* the bugs you might encounter.
* #author Ahmed KRAIEM
* #version 0.9 alpha
* #since 2013-04-03
*/
public class CircleChecker {
private BufferedImage image;
/**
* Points that are equal to the calculated radiusĀ±<code>radiusesErrorMargin%</code> are not considered rogue points.<br />
* <code>radiusesErrorMargin</code> must be <code>>0 && <1</code>
*/
private double radiusesErrorMargin = 0.2;
/**
* A shape that has fewer than roguePointSensitivity% of rogue points is considered a circle.<br />
* <code>roguePointSensitivity</code> must be <code>>0 && <1</code>
*/
private double roguePointSensitivity = 0.05;
/**
* The presumed circle is divided into <code>angleCompartimentPrecision</code> parts,<br />
* each part must have <code>minPointsPerCompartiment</code> points
* <code>angleCompartimentPrecision</code> must be <code>> 0</code>
*/
private int angleCompartimentPrecision = 50;
/**
* The minimum number of points requiered to declare a part valid.<br />
* <code>minPointsPerCompartiment</code> must be <code>> 0</code>
*/
private int minPointsPerCompartiment = 20;
public CircleChecker(BufferedImage image) {
super();
this.image = image;
}
public CircleChecker(BufferedImage image, double radiusesErrorMargin,
int minPointsPerCompartiment, double roguePointSensitivity,
int angleCompartimentPrecision) {
this(image);
this.radiusesErrorMargin = radiusesErrorMargin;
this.minPointsPerCompartiment = minPointsPerCompartiment;
this.roguePointSensitivity = roguePointSensitivity;
this.angleCompartimentPrecision = angleCompartimentPrecision;
}
public BufferedImage getImage() {
return image;
}
public void setImage(BufferedImage image) {
this.image = image;
}
public double getRadiusesErrorMargin() {
return radiusesErrorMargin;
}
public void setRadiusesErrorMargin(double radiusesErrorMargin) {
this.radiusesErrorMargin = radiusesErrorMargin;
}
public double getMinPointsPerCompartiment() {
return minPointsPerCompartiment;
}
public void setMinPointsPerCompartiment(int minPointsPerCompartiment) {
this.minPointsPerCompartiment = minPointsPerCompartiment;
}
public double getRoguePointSensitivity() {
return roguePointSensitivity;
}
public void setRoguePointSensitivity(double roguePointSensitivity) {
this.roguePointSensitivity = roguePointSensitivity;
}
public int getAngleCompartimentPrecision() {
return angleCompartimentPrecision;
}
public void setAngleCompartimentPrecision(int angleCompartimentPrecision) {
this.angleCompartimentPrecision = angleCompartimentPrecision;
}
/**
*
* #return true if the image contains no more than <code>roguePointSensitivity%</code> rogue points
* and all the parts contain at least <code>minPointsPerCompartiment</code> points.
*/
public boolean isCircle() {
List<Point> list = new ArrayList<>();
final int xmin = image.getMinX();
final int ymin = image.getMinY();
final int ymax = ymin + image.getHeight();
final int xmax = xmin + image.getWidth();
for (int i = xmin; i < xmax; i++) {
for (int j = ymin; j < ymax; j++) {
int pixel = image.getRGB(i, j);
if ((pixel & 0x00FFFFFF) == 0) {
list.add(new Point(i, j));
}
}
}
if (list.size() == 0)
return false;
double diameter = -1;
Point p1 = list.get(0);
Point across = null;
for (Point p2 : list) {
double d = distance(p1, p2);
if (d > diameter) {
diameter = d;
across = p2;
}
}
double radius = diameter / 2;
Point center = center(p1, across);
int diffs = 0;
int diffsUntilError = (int) (list.size() * roguePointSensitivity);
double minRadius = radius - radius * radiusesErrorMargin;
double maxRadius = radius + radius * radiusesErrorMargin;
int[] compartiments = new int[angleCompartimentPrecision];
for (int i=0; i<list.size(); i++) {
Point p = list.get(i);
double calRadius = distance(p, center);
if (calRadius>maxRadius || calRadius < minRadius)
diffs++;
else{
//Angle
double angle = Math.atan2(p.y -center.y,p.x-center.x);
//angle is between -pi and pi
int index = (int) ((angle + Math.PI)/(Math.PI * 2 / angleCompartimentPrecision));
compartiments[index]++;
}
if (diffs >= diffsUntilError){
return false;
}
}
int sumCompartiments = list.size() - diffs;
for(int comp : compartiments){
if (comp < minPointsPerCompartiment){
return false;
}
}
return true;
}
private double distance(Point p1, Point p2) {
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
}
private Point center(Point p1, Point p2) {
return new Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
}
public static void main(String[] args) throws IOException {
BufferedImage image = ImageIO.read(new File("image.bmp"));
CircleChecker cc = new CircleChecker(image);
System.out.println(cc.isCircle());
}
}
You'll need to program in a template of what a circle would look like, and then make it scalable to suit the different circle sizes.
For example circle of radius 3 would be:
o
ooo
o
This assumes you have a finite set of circles you need to find, maybe up to 5x5 or 6x6 this would be feasible.
or you could use: Midpoint circle algorithm
This would involve finding all black pixel groups and then selecting the middle pixel for each one.
Apply this algorithm using the outer pixels as a guid to how big the circle could be.
Finding the difference between black /expected black pixels.
If the black to expected black ratio is high enough, its a black circle and you can delete / whiten it.
I am making a simulation of a pendulum, but it only performs one swing before sending close to random positions for the bob to be at. Essentially, it does not go backwards.
I have tried to change the direction using the goingForward boolean, but it still doesnt work.
public class AnimationPane extends JPanel {
// START CHANGEABLE VARIABLES
private double startAngle = -60.0; // degrees
private double mass = 1; // kilogrammes
private int radius = 10; // m
private double gravity = 9.80665; // m/s^2 // on earth: 9.80665
// END CHANGEABLE VARIABLEs
private BufferedImage ball;
private BufferedImage rope;
private int pointX = 180;
private int pointY = 50;
private double endAngle = Math.abs(startAngle); // absolute value of startAngle
private double angle = startAngle; // current angle
private double circum = (2 * Math.PI * radius); // m
private double distance = 0; // m
private double velocity = 0; // m/s
private double totalEnergy = ((radius) - (Math.cos(Math.toRadians(angle)) * radius)) * gravity * mass + 0.00001;
private double previousE;
private int xPos = 0; // for program
private int yPos = 0; // for program
private boolean goingForward = true;
private double height = 0;
public AnimationPane() {
try {
ball = ImageIO.read(new File("rsz_black-circle-mask-to-fill-compass-outline.png"));
Timer timer = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
double angleRad = Math.toRadians(Math.abs(angle));
double potentialE = ((radius) - (Math.cos(angleRad) * radius)) * gravity * mass;
Double pE = new Double(potentialE);
height = (radius - (Math.cos(angleRad) * radius));
double kineticE = totalEnergy - pE;
if (kineticE <= 0 || angle >= endAngle) {
if (goingForward == true) {
goingForward = false;
}
else
{
goingForward = true;
}
kineticE = 0.1;
angle = 60;
}
velocity = Math.sqrt(2 * kineticE / mass);
double ratio = distance / circum;
if (goingForward == true) {
distance = distance + (velocity / 10);
angle = startAngle + (360 * ratio);
}
else {
distance = distance - (velocity / 10);
angle = startAngle - (360 * ratio);
}
double angles = Math.toRadians(angle);
double xDouble = Math.sin(angles) * (radius * 10);
Double x = new Double(xDouble);
xPos = x.intValue() + 150;
double yDouble = Math.cos(angles) * (radius * 10);
Double y = new Double(yDouble);
yPos = y.intValue() + 50;
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
} catch (IOException ex) {
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(xPos + 20, yPos + 20, pointX, pointY);
g.drawImage(ball, xPos, yPos, this);
}
}
I would really appreciate some help getting this to work.
Thank you
I could not debug your code, which was uneasy to work with and sometimes to understand (you use a lot of integer literals in your code, which hides their semantic, I have no idea what was your intention on some statements).
Therefore, I rewrote it using the solution of the differential equation for small oscillations. It works, you can take it as a clean base to implement it again the way you wanted. Note that as Andy Turner pointed it, you should not have to worry about the fact of going forward or backward. You have an equation, you solve it, it gives you the position of the ball at any time. If you want something which is accurate for large angles, I suggest you go on Wikipedia to see the movement equation in this case. Last option, you could numerically solve the differential equation although I would personally don't know how to do it at first glance.
package stackoverflow;
public class AnimationPane extends JPanel {
private static final long serialVersionUID = 1L;
private static final double GRAVITY = 9.80665;
private BufferedImage ball;
private final Point fixedCordPoint;
private final int cordLength;
private final double startAngle;
private double currentAngle;
private final double pulsation;
private final Point ballPos = new Point();
private int time = 1;
public AnimationPane(Point fixedCordPoint, int cordLength, double startAngleRadians) {
this.fixedCordPoint = new Point(fixedCordPoint);
this.cordLength = cordLength;
this.pulsation = Math.sqrt(GRAVITY / cordLength);
this.startAngle = startAngleRadians;
this.currentAngle = startAngleRadians;
this.ball = loadImage(new File("ball.jpg"));
}
private BufferedImage loadImage(File file) {
try {
return ImageIO.read(file);
} catch (IOException e) {
throw new RuntimeException("Could not load file : " + file, e);
}
}
public void start() {
Timer timer = new Timer(100, event -> {
ballPos.x = fixedCordPoint.x + (int) Math.round(Math.sin(currentAngle) * cordLength);
ballPos.y = fixedCordPoint.y + (int) Math.round(Math.cos(currentAngle) * cordLength);
repaint();
currentAngle = startAngle * Math.cos(pulsation * time);
time++;
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(ballPos.x, ballPos.y, fixedCordPoint.x, fixedCordPoint.y);
g.drawImage(ball, ballPos.x - ball.getWidth() / 2, ballPos.y - ball.getHeight() / 2, this);
}
public static void main(String[] args) {
JFrame frame = new JFrame();
AnimationPane pendulumAnimationPane = new AnimationPane(new Point(160, 25), 180, - Math.PI / 10);
frame.setContentPane(pendulumAnimationPane);
frame.setSize(400,400);
frame.setVisible(true);
pendulumAnimationPane.start();
}
}
I really can't follow your code. If I had to say where I thought the problem is, I would say that it is the distance variable, and its consequent use in ratio: this seems to be defined as being zero when the pendulum starts. When you switch the direction of the pendulum, I think that it needs to be rebased to zero... Somehow.
You should also separate out your GUI code from your simulation. You can just make it print out the x and y coordinates while you are debugging. For instance, you can write the values to a CSV file, so you can visualize them in Matlab (or whatever) to ensure that your simulation looks right over time.
I would make two suggestions to help you with the simulation:
Don't model it in terms of energy, which is a scalar quantity, meaning that you have to use additional state like a "direction flag" to model which way it is going;
You are going to need an additional state variable. Storing x and y is redundant, since these can be derived directly from the angle (and R, although that is constant). You are modelling a second-order system, so you need that second state variable which models the movement of the system at an instant, as well as its position. For example, you could model the angle and angular velocity over time.
And, of course, don't mess around with degrees - stick to radians :)
(On the matter of velocity, your variable of that name is actually speed - you don't know which direction it moves in, and that information is highly relevant to the dynamics of the system).
The derivation of the method using angle and angular velocity is quite straightforward, although my maths is rusty enough to caution its use without checking carefully!
The rotational form of Newton's second law of motion is:
Torque = moment of inertia * angular acceleration
The torque on the bob is given by -mgR sin angle (m is mass, g is gravity, R is length of pendulum, angle is the angular displacement of the pendulum from vertical). The minus sign is because the force tends to return the bob to vertical;
The moment of inertia of a point mass is mR^2 - this is a reasonable approximation if the pendulum is very long compared to the radius of the bob.
Therefore the angular acceleration is -g sin theta / R. Hopefully this should look like simple harmonic motion - an acceleration proportional to the distance from an equilibrium (for small theta, sin theta is approximately theta) and directed towards it.
You can now put this together into a numerical scheme to simulate the pendulum. For example, using Euler's method:
New angle = old angle + dt * old angular velocity
New angular velocity = old angular velocity vel - dt * g * sin(angle) / R
where dt is your time step.
You can, of course, use higher-order methods like Runge-Kutta to reduce the numerical errors in the simulation, but it is (slightly) more involved.