I'm working on an ASCII graphics engine in Java, to emulate terminal graphics without some of the headaches of dealing with actual terminals.
One key element is coloring the background of characters. So at every position, I put a rectangle, which serves as the background color, and a Text object, which represents a character (using monospace fonts).
I've tried using FlowPanes, TilePanes, GridPanes, but regular Panes seem to work best (least spacing achieved). Here is an image that shows the issue at hand: Screenshot
I'm trying to make all the rectangles align in a way that there is no visible space to see through to the background. In the image linked above, there should be no black ridges between the colored rectangles.
Here is the code I have that adds the Rectangle and Text for each "pixel" (which is simply a class called Char that holds a Rectangle and a Text object).
for (int x = 0; x < COLUMNS; x++)
for (int y = 0; y < ROWS; y++)
pixels[x][y] = new Char(pane, paddingX + x * width, paddingY + y * height, width, height);
The height and width are calculated before this block, and are determined based on the font used. They represent the width and height of any character, since the font used is monospace. The padding is just a number used to center the "pixels", and is also determined before the nested loop.
Char class:
private Text ch;
private Rectangle background;
Char(Pane pane, double x, double y, double w, double h) {
ch = new Text();
ch.relocate(x, y);
ch.setFont(View.font);
ch.setFill(Color.WHITE);
background = new Rectangle(w, h, Color.BLACK);
background.relocate(x, y);
ch.setBoundsType(TextBoundsType.VISUAL);
pane.getChildren().addAll(background, ch);
}
This is a rounding issue. It can be fixed by making sure you're using integral locations and sizes.
In the following code you replacing the assignments for x, y, nx and ny to the ones in the comments results in visible gaps but changing w and h to integral values, e.g. 10, also prevents visible gaps:
#Override
public void start(Stage primaryStage) {
double w = 10.5;
double h = 10.5;
Pane pane = new Pane();
for (int i = 0; i < 57; i++) {
for (int j = 0; j < 57; j++) {
double x = Math.floor(i * w);
double y = Math.floor(j * h);
double nx = Math.floor((i + 1) * w);
double ny = Math.floor((j + 1) * h);
// double x = i * w;
// double y = j * h;
// double nx = (i + 1) * w;
// double ny = (j + 1) * h;
Rectangle rect = new Rectangle(x, y, nx - x, ny - y);
rect.setFill(Color.BLACK);
pane.getChildren().add(rect);
}
}
primaryStage.setScene(new Scene(pane, 600, 600));
primaryStage.show();
}
Related
I am trying to make a program where there are lines in a grid pointing towards the mouse like magnets. I am a beginner in Processing, can someone point me towards a tutorial on how to do that or give me some code and explain what it does?
int x1 = 0;
int x2 = 0;
int y1 = 0;
int y2 = 0;
void setup() {
size(200, 200);
}
void draw() {
background(255, 255, 0);
x1 = (mouseX + 100) / 2;
y1 = (mouseY + 100) / 2;
x2 = -1 * x1 + 200;
y2 = -1 * y1 + 200;
line(x1, y1, x2, y2);
}
There's plenty of solutions for this project. One of the easiest is to use Processing's PVector class.
The PVector class can be used for two or three dimensional vectors. A vector is an entity that has both magnitude and direction. The PVector class, however, stores the components of the vector (x,y for 2D, and x,y,z for 3D). The magnitude and direction are calculated from the components and can be accessed via the methods mag() and heading().
A two dimensional vector in Processing is defined through x and y components:
PVector v = new PVector(xComponent, yComponent);
With some mathematical formulae, you can determine magnitude and direction using the x- and y-components. But we don't need to determine these.
Below, I've attached completed solution code. Most of it should make sense to you. But it's worth understanding what is going on with PVector.
A nested for loop within void draw() contains x and y variables that represent the coordinates of each grid vertex.
We first define PVector v as a vector given by an x-component of mouseX - x, or the difference between the x-positions of the mouse and each grid point. Similarly, the y-component given by mouseY - y has the same difference.
Creating a variable PVector u initialized from v.setMag(15) holds a PVector that has the same direction as v, but with a length of just 15.
Now to draw the lines. Vectors represent an offset, not a position (in this case), so drawing a line from a grid point to an offset of a grid point is key.
Hence line(x, y, x + u.x, y + u.y), where u.x and u.y are the x- and y-components of the vector u.
void setup() {
size(600, 600); // Set the size of the canvas to 600x600.
}
void draw() {
background(255);
stroke(200); // Set the stroke color to black
int distVertLine = width / 10; // This variable defines the distance between each subsequent vertical line.
for(int i = 0; i < width; i += distVertLine) {
line(i, 0, i, height); // Draw a line at x=i starting at the top of the canvas (y=0) and going to the bottom (y=height)
}
int distHorizLine = height / 10; // This variable defines the distance between each subsequent vertical line.
for(int i = 0; i < width; i += distHorizLine) {
line(0, i, width, i); // Draw a line at y=i starting at the left of the canvas (x=0) and going to the right (x=width)
}
stroke(0); // Set the stroke to black.
// Use a nested for loop to iterate through all grid vertices.
for(int x = 0; x <= width; x += width/10) {
for(int y = 0; y <= height; y += height/10) {
PVector v = new PVector(mouseX - x, mouseY - y); // Define a vector that points in the direction of the mouse from each grid point.
PVector u = v.setMag(15); // Make the vector have a length of 15 units.
line(x, y, x + u.x, y + u.y); // Draw a line from the grid vertex to the terminal point given by the vector.
}
}
}
The answer already given by Ben Myers is excellent! The code below has a few small modifications:
the two for loops for the grid lines have been combined (since width and height are equal);
the construction of the vector is combined with setting the magnitude;
some minor changes to colors and comments.
Modified code:
void setup() {
// Set the size of the canvas to 600x600 pixels.
size(600, 600);
}
void draw() {
// There are 10x10 grid cells that each have a size of 60x60 pixels.
int gridSize = width / 10;
// Set the background color to anthracite and the stroke color to orange.
background(56, 62, 66);
stroke(235, 113, 52);
// Draw vertical and horizontal grid lines.
for (int lineIndex = 0; lineIndex < gridSize; lineIndex++) {
line(lineIndex * gridSize, 0, lineIndex * gridSize, height);
line(0, lineIndex * gridSize, width, lineIndex * gridSize);
}
// Set the stroke color to blue.
stroke(0, 139, 225);
// Use a nested for loop to iterate through all grid cells.
for (int x = 0; x <= width; x += gridSize) {
for (int y = 0; y <= height; y += gridSize) {
// Define a vector that points in the direction of the mouse from
// each grid point and set the vector length to 15 units.
PVector vector = new PVector(mouseX - x, mouseY - y).setMag(15);
// Draw a line from the grid point to the end point using the vector.
line(x, y, x + vector.x, y + vector.y);
}
}
}
I am having issues figuring out three things. (Created using Drawing Panel: http://www.buildingjavaprograms.com/DrawingPanel.java)
Problem #1: Drawing the polygon so it's centered and not crooked. It's unnoticeable with more points drawn.
Problem #2: Connecting all points of the star together so it's a giant circle (dotted). I don't see why it's happening unless maybe the method is not the best.
Problem #3: When drawn with low amounts of points, I notice that it doesn't draw a point correctly, and it looks like a square.
I'd really appreciate the help!
import java.awt.*;
public class StarSampler {
public static void main(String[] args)
{
DrawingPanel panel = new DrawingPanel(500, 500);
Graphics2D g = panel.getGraphics();
g.setColor(Color.BLUE);
fillStar(g, 250, 250, 150, 5, 1);
}
public static void fillStar(Graphics2D g, int ctrX, int ctrY, int radius, int nPoints, double spikiness)
{
double xDouble[] = new double[2*nPoints];
double yDouble[] = new double[2*nPoints];
int xPoint[] = new int[100];
int yPoint[] = new int[100];
for (int i = 0; i < 2*nPoints; i++)
{
double iRadius = (i % 2 == 0) ? radius : (radius * spikiness);
double angle = (i * 720.0) / (2*nPoints);
xDouble[i] = ctrX + iRadius * Math.cos(Math.toRadians(angle));
yDouble[i] = ctrY + iRadius * Math.sin(Math.toRadians(angle));
for (int j = 0; j < nPoints; j++) // Casts for ints and doubles
{
xPoint[j] = (int) xDouble[j];
yPoint[j] = (int) yDouble[j];
}
}
g.fillPolygon(xPoint, yPoint, nPoints); // Creates polygon
// Polygon gets drawn crookedly
g.drawPolyline(xPoint, yPoint, nPoints); // Draws lines to connect points
// Two lines go straight to (0,0) when nPonts*2 and nothing without *2?
}
}
My Output:
My Target Output (Without labeled points, two stars just for example):
The issues with your code are of logical nature or due to a sloppy coding style:
for (int j = 0; j < nPoints; j++) // Casts for ints and doubles
{
xPoint[j] = (int) xDouble[j];
yPoint[j] = (int) yDouble[j];
}
This piece of code is supposed to transform all portions of the polygon into integers. There are several issues with this piece of code:
It doesn't cover all points. The loop produces a total of 2 * nPoints points, but only half of them is converted. This is where the missing spikes come from
Why do this in a inner loop? This shouldn't be done in the loop that generates the values. It's just an enormous number of redundant copies and casts.
Why keep two separate arrays at all? Just convert them directly on creation. Since no value will be reused, there's no point in keeping a value with full precision anyways.
A circle is 360 degrees, not 720. This code:
double angle = (i * 720.0) / (2*nPoints);
Will alter the angle between created points. This means you either only generate half of the spikes, if the number is even, or generate a lot of crossing lines (doesn't look bad either, but not what you want, I guess).
The unit-circle (relevant for the trignometry-part) is defined in a way such that (1, 0) is the point with an angle of 0° to the center. This is also where your first spike will be created. Simply substract 90° of the angle to rotate the circle by 90° counter-clockwise.
Here's working solution based on your code. The main-method only holds the code to manage a simple testing-UI:
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
public class StarSampler
{
private static final int WIDTH = 500,
HEIGHT = 500,
RADIUS = 200;
private static final double SPIKINESS = 0.5;
public static void main(String[] args)
{
BufferedImage bi = new BufferedImage(500, 500, BufferedImage.TYPE_4BYTE_ABGR);
updateImage(5, bi);
JFrame frame = new JFrame("Some Test");
frame.setLayout(new BorderLayout());
frame.add(new JLabel(new ImageIcon(bi)), BorderLayout.CENTER);
//menu to update number of spikes
JPanel sub = new JPanel();
sub.setLayout(new BoxLayout(sub, BoxLayout.X_AXIS));
sub.add(new JLabel("Spikes: "));
JSpinner spikeSpinner = new JSpinner(new SpinnerNumberModel(5, 1, 500, 1));
spikeSpinner.addChangeListener(e -> {
updateImage((Integer) spikeSpinner.getModel().getValue(), bi);
SwingUtilities.invokeLater(()->frame.repaint());
});
sub.add(spikeSpinner);
frame.add(sub, BorderLayout.SOUTH);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static void updateImage(int nSpikes, BufferedImage bi)
{
int ctrX = WIDTH / 2, ctrY = HEIGHT / 2;
int nPoints = nSpikes * 2 + 1;
int xPoint[] = new int[nPoints];
int yPoint[] = new int[nPoints];
//generate star
for (int i = 0; i < nPoints; i++)
{
double iRadius = (i % 2 == 0) ? RADIUS : (RADIUS * SPIKINESS);
double angle = (i * 360.0) / (2*nSpikes);
xPoint[i] = (int) (ctrX + iRadius * Math.cos(Math.toRadians(angle - 90)));
yPoint[i] = (int) (ctrY + iRadius * Math.sin(Math.toRadians(angle - 90)));
}
//paint the star
Graphics2D g2 = (Graphics2D) bi.getGraphics();
g2.setColor(Color.blue);
g2.fillRect(0, 0, WIDTH, HEIGHT);
g2.setStroke(new BasicStroke(4.f));
g2.setColor(Color.yellow);
g2.drawPolyline(xPoint, yPoint, nPoints);
//insert control lines
g2.setStroke(new BasicStroke(1.f));
g2.setColor(Color.black);
for(int i = 0; i < nSpikes * 2; i++)
g2.drawLine(ctrX, ctrY, xPoint[i], yPoint[i]);
int w1 = RADIUS,
w2 = (int) (RADIUS * SPIKINESS);
g2.drawOval(ctrX - w1, ctrY - w1, w1 * 2, w1 * 2);
g2.drawOval(ctrX - w2, ctrY - w2, w2 * 2, w2 * 2);
}
}
This is an extension to my previous question posted here -- Java Swing GUI for equation 5((θ/β) - cos(2πθ/β))
I have implemented the Java program based on the answers provided in the post an here is my program:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class DisplacementFunctionNew extends JFrame {
public DisplacementFunctionNew() {
setLayout(new BorderLayout());
add(new CosGraph(), BorderLayout.CENTER);
}
public static void main(String[] args) {
DisplacementFunctionNew frame = new DisplacementFunctionNew();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(6000, 6000);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
frame.setTitle("SineWave");
}
class CosGraph extends JPanel {
public void paintComponent(Graphics g) {
int graphHeight = 5; // Declared this to set the height of graph based on the value given here.
super.paintComponent(g);
int xBase = 100;
int top = 100;
int yScale = 100;
int xAxis = 360;
int yBase = top + yScale;
g.drawLine(xBase, top, xBase, top + 2 * yScale);
g.drawLine(xBase, yBase, xBase + xAxis, yBase);
g.setColor(Color.red);
double maxY = 0;
for (int i = 0; i < 360; i++) {
maxY = Math.max(maxY, Math.abs(getValue(i)));
}
int x, y;
for (int i = 0; i < 360; i++) {
x = xBase + i;
y = yBase - (int) (getValue(i)*graphHeight / maxY * yScale);
g.drawLine(x, y, x, y);
}
}
private double getValue(int theta) {
int beta = 45;
double b = (theta / (double) beta);
double angle = 2 * Math.PI * (b);
double c = Math.cos(angle);
double s = (b - c);
return s;
}
}
}
Now in this program I want to have a variable called graphHeight that helps to increase the height of the graph. If I give the value of the variable as 1 then I can see the output like this:
Now if I try to increase the height to 5 then I get the graph but it is not shown smoothly or continuous curve, I get the output like this:
Can someone please help me how to get the output as smooth continuous curve?
You are drawing a curve using points. You are placing one point at each location on the x axis -- this makes sense to do.
When the graph is small, it looks fine, because the y separation of these points is relatively small. However, as you increase the graph size, this flaw becomes more noticeable.
The solution here is to fill in the vertical space with lines. You have a few options for the exact implementation of this:
Draw a line from [x(i), y(i)] to [x(i+1),y(i+1)] -- this is easy, but may not look the way you want.
Draw a line from [x(i), y(i)] to [x(i),y(i+1)] -- this is still pretty easy, but it won't be quite correct: you're continuing up so that you could be an entire pixel off.
Draw a line from [x(i), y(i)] to [x(i),(y(i)+y(i+1))/2], and then from [x(i+1), (y(i)+y(i+1))/2] to [x(i+1),y(i+1)] -- this is what 1 should do (neglecting anti-aliasing), and will be the most correct of your possible options.
I would suggest number 3. Note that you can implement this with a loop of the form:
int lastY = yBase - (int) (getValue(0)*graphHeight / maxY * yScale);
for (int i = 1; i < 360; i++) {
x = xBase + i;
y = yBase - (int) (getValue(i)*graphHeight / maxY * yScale);
g.drawLine(x-1, lastY, x-1, (y+lastY)/2);
g.drawLine(x, (y+lastY)/2, x, y);
}
If the one pixel overlap bothers you, you can make it a bit more complex such that the second line starts at +/- 1 pixel (depending on if the function is increasing or decreasing).
Alternatively, you can implement number 3 by manually drawing the line, using a for loop, and basically write a special-case version of Bresenham's line algorithm.
You use graphHeight to define the y of the next point to be painted with g.drawLine(x, y, x, y);. The distance between drawn points will be related to the graphHeight variable
this question is continue of GPS coordinates to pixel.
I need to draw a several polygons. I can draw each polygon alone, but cant all polygons on right position.
I load information about polygon from this file:
GPS
I have class Kraj, which represent each polygon.
public class Kraj {
String name;
Point2D.Double points[];
Point2D.Double transPoints[];
Point2D.Double max;
Point2D.Double min;
// polygon
Path2D.Double polygon;
ArrayList<Kraj> kraje;
public Kraj(String name, Point2D.Double body[])
{
this.name = name;
this.body = Arrays.copyOf(body, body.length);
// calculate a bounding box
zjistiLimity();
this.transPoints = new Point2D.Double[points.length];
}
private void transformToWindow(int width, int height)
{
// convert to window
double convertX = width / (max.x - min.x);
double convertY = height / (max.y - min.y);
// calculate polygon to fit in window with right aspect ratio
double convert = convertX > convertY ? convertY : convertX;
// min = 0, convert to interval <0: infinity> and multiply by convert,
for (int j = 0; j < points.length; j++) {
double transX = (points[j].x - min.x) * convert;
double transY = height - (points[j].y - min.y) * convert;
transPoints[j] = new Point2D.Double(transX, transY);
}
this.polygon = new Path2D.Double();
this.polygon.moveTo(transBody[0].x, transBody[0].y);
for (int i = 1; i < body.length; i++)
this.polygon.lineTo(transPoints[i].x, transPoints[i].y);
this.polygon.closePath();
}
private void drawKraj(Graphics2D g2, int width, int height) {
g2.setStroke(new BasicStroke(2));
g2.fill(polygon);
// vykreslime obrys
g2.setColor(Color.black);
g2.draw(polygon);
}
public void draw(Graphics2D g2,
int contextWidth, int contextHeight)
{
// fit to window size
int sirkaSOdsazenim = contextWidth;
int vyskaSOdsazenim = contextHeight;
this.transformujToWindow(sirkaSOdsazenim, vyskaSOdsazenim);
this.drawKraj(g2, sirkaSOdsazenim, vyskaSOdsazenim);
}
/**
* Set min and max
*/
private void zjistiLimity() {
max = new Point2D.Double(-Double.MAX_VALUE, -Double.MAX_VALUE);
min = new Point2D.Double(Double.MAX_VALUE, Double.MAX_VALUE);
for(int j = 0; j < 10; j++)
{
for (int i = 0; i < body.length; i++)
{
if (points[i].getX() < min.getX()) min.x = points[i].getX();
if (points[i].getY() < min.getY()) min.y = points[i].getY();
if (points[i].getX() > max.getX())max.x = points[i].getX();
if (points[i].getY() > max.getY()) max.y = points[i].getY();
}
}
}
With this code I can draw polygon, which fit to window. But I need to draw all polygons to fit to window (calculate coordinates to create this map):
What I need to edit or add? Thanks for all answers.
You can translate the entire polygon by using:
g2.translate(x, y);
g2.draw(polygon);
g2.translate(-x, -y)
Determining the appropriate x/y translation for each polygon is something you will need to do.
I need to draw a several polygons. I can draw each polygon alone, but I can't draw all polygons in the right position.
If you can draw each polygon alone, then your polygons are correct.
You need to add an origin point to your Kraj class. Then your draw method would transform the polygon points from the polygon origin to the map origin. Assuming your polygon origin is (10,10) and a particular polygon needs to be drawn at (20,30), then you would add 10 to the x and add 20 to the y of each point in the polygon before you draw it.
You can do this my making a copy of the polygon in the draw routine before you adjust the X and Y values of each point.
Edited to add: Here's your own code modified to transform the origin. I've not tested these changes.
private void transformToWindow(Point2D windowOrigin, int width, int height)
{
// convert to window
double convertX = width / (max.x - min.x);
double convertY = height / (max.y - min.y);
// calculate polygon to fit in window with right aspect ratio
double convert = convertX > convertY ? convertY : convertX;
// min = 0, convert to interval <0: infinity> and multiply by convert,
for (int j = 0; j < points.length; j++) {
double transX = (points[j].x - min.x) * convert;
double transY = height - (points[j].y - min.y) * convert;
transPoints[j] = new Point2D.Double(transX, transY);
}
this.polygon = new Path2D.Double();
double xShift = windowOrigin.x - transBody[0].x;
double yShift = windowOrigin.y - transBody[0].y;
this.polygon.moveTo(windowOrigin.x, windowOrigin.y);
for (int i = 1; i < body.length; i++)
this.polygon.lineTo(transPoints[i].x + xShift,
transPoints[i].y + yShift);
this.polygon.closePath();
}
I'm attempting to take a picture as input, then manipulate said picture (I specifically want to make it greyscale) and then output the new image. This is a snippet of the code that I'm editing in order to do so but I'm getting stuck. Any ideas of what I can change/do next. Greatly appreciated!
public boolean recieveFrame (Image frame) {
int width = frame.width();
int height = frame.height();
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
Color c1 = frame.get(i, j);
double greyScale = (double) ((Color.red *.3) + (Color.green *.59) + (Color.blue * .11));
Color newGrey = Color.greyScale(greyScale);
frame.set(i, j, newGrey);
}
}
boolean shouldStop = displayImage(frame);
return shouldStop;
}
I'm going to try to stick as close as possible to what you already have. So, I'll assume that you are looking for how to do pixel-level processing on an Image, rather than just looking for a technique that happens to work for converting to greyscale.
The first step is that you need the image to be a BufferedImage. This is what you get by default from ImageIO, but if you have some other type of image, you can create a BufferedImage and paint the other image into it first:
BufferedImage buffer = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g = buffer.createGraphics();
g.drawImage(image, 0, 0);
g.dispose()
Then, you can operate on the pixels like this:
public void makeGrey(BufferedImage image) {
for(int x = 0; x < image.getWidth(); ++x) {
for(int y = 0; y < image.getHeight(); ++y) {
Color c1 = new Color(image.getRGB(x, y));
int grey = (int)(c1.getRed() * 0.3
+ c1.getGreen() * 0.59
+ c1.getBlue() * .11
+ .5);
Color newGrey = new Color(grey, grey, grey);
image.setRGB(x, y, newGrey.getRGB());
}
}
}
Note that this code is horribly slow. A much faster option is to extract all the pixels from the BufferedImage into an int[], operate on that, and then set it back into the image. This uses the other versions of the setRGB()/getRGB() methods that you'll find in the javadoc.