I've been trying to figure this out for a while, and it's driving me mad. As most people know, if you draw 10 rectangles next to each other, ranging from white to black in equal steps of HSV brightness, they will not be perceived equal to the eye. Here's an example of that:
And the code in Processing:
void setup()
{
size(600, 150);
colorMode(HSB, 360, 100, 100);
background(0, 0, 100);
translate(50, 50);
noStroke();
for(int i = 0; i < 10; i++)
{
fill(0, 0, i * 10);
rect(i * 50, 0, 50, 50);
}
}
As you can see, the contrast between some of the darker tiles is perceived much bigger than with some of the white tiles.
Many people have pointed this out. In his book The Art of Color, Josef Albers describes (based on the Weber-Fechner law) that you should instead increase the brightness in exponential steps. It was later proved that Albers did some nasty miscalculations, and the idea of using a constant logarithmic increase in brightness proved true only within very limited bounds. There has been a lot of papers on this, but many of them are very hard for me to read, and most of them ties to physical aspects of the retina.
So my question is:
Given any color, how do I calculate the perceived equal steps in brightness from HSV brightness 0 to 100?
Even better, how do I calculate the perceived equal steps in brightness from any one color to any other color?
I'm generating files for print via code, and I need to do this in Processing. Any example in any language will do though.
For other people looking to do this in Processing, here's the answer. The Toxiclibs TColor class ships with LAB -> RGB conversion, so it wasn't hard. As you can see in the screenshot, the difference is clear.
import toxi.color.*;
import toxi.geom.*;
void setup()
{
size(600, 250);
colorMode(RGB, 1, 1, 1);
background(1);
noStroke();
translate(50, 50);
// RGB: 10 rects where perceived contrast is NOT equal in all squares
for(float i = 0; i < 10; i++)
{
fill(i / 10.0, i / 10.0, i / 10.0);
rect(i * 50, 0, 50, 50);
}
// LAB: 10 rects where perceived contrast IS equal in all squares
translate(0, 50);
for(int i = 0; i < 10; i++)
{
float[] rgb = TColor.labToRGB(i * 10, 0, 0);
TColor col = TColor.newRandom().setRGB(rgb);
fill(col.toARGB());
rect(i * 50, 0, 50, 50);
}
}
And here's the output:
Related
I'm prototyping a script to plot equally spaced points around a rotating plane and Processing is producing unexpected results?
This is my code:
int WHITE = 255;
int BLACK = 0;
void setup() {
size(500, 500);
}
void draw() {
background(WHITE);
translate(width/2, height/2); // move origin to center of window
// center ellipse
noStroke();
fill(255, 0, 0);
ellipse(0, 0, 10, 10); // center point, red
// satellite ellipses
fill(BLACK);
int points = 4;
for (int i = 0; i < points; i++) {
rotate(i * (TWO_PI / points));
ellipse(0, 100, 10, 10); // after plane rotation, plot point of size(10, 10), 100 points above y axis
}
}
When points = 4 I get the output I would expect, but when points = 5 // also when points = 3 or > 4, I get an output that is missing plotted points but still spaced correctly.
Why is this happening?
You're rotating too much: you don't want to rotate by i * angle at every iteration, because if we do we end up rotating so much that points end up overlapping. For example, with the code as is, with 3 points we want to place them at 0, 120, and 240 degrees (or, 120, 240, 360). But that's not what happens:
when i=0 we rotate by 0 degrees. So far so good.
when i=1 we rotate by 120 degrees on top of 0. Still good.
when i=2 we rotate by 240 degrees on top of 120. That's 120 degrees too far!
That's clearly not what we want, so just rotate by the fixed angle TAU / points and things'll work as expected:
for (int i = 0; i < points; i++) {
rotate(TAU / points);
ellipse(0, 100, 10, 10);
}
Alternatively, keep the incrementing angle, but then place the points without using rotate(), by using trigonometry to compute the placement:
float x = 0, y = 100, nx, ny, angle;
for (int i = 0; i < points; i++) {
angle = i * TAU / points;
nx = x * cos(a) - y * sin(a);
ny = x * sin(a) + y * cos(a);
ellipse(nx, ny, 10, 10);
}
Maybe this is only my problem, but I simply can't find this while searching on Google, and it shouldn't be that hard.
I'm looking for a Class/API for 2D plotting.
I need a method in which I give a series of int or double values, and it plots them in a 2-coordinate plane, and draws the plane on a JFrame or JPanel.
Here's a method:
public void plot(String ints, Graphics g) {
ints = "put all nums here (e.g. 4,3;9,1;1.1,2)";
String[] Part1 = ints.split(";");
String coor1 = Arrays.(Part1[0]);
String coor2 = Arrays.(Part1[2]);
g.drawLine(50, 0, 2, heightOfFrame);
g.drawLine(0, 50, widthOfFrame, 2);
g.drawLine(45, 40, 10, 2);
g.drawLine(40, 45, 2, 10);
int coord1 = Integer.parseInt(coor1) * 10;
int coord2 = Integer.parseInt(coor2) * 10;
g.drawOval(coord1-1, coord2-1, 2, 2);
}
In theory, this should work - though I haven't tested it - so please tell me about any bugs in this and I'll fix it.
BTW: this only covers 0 and 1 x and y; but it's the general idea to get you started.
I am currently working on a game that requires a hexagon grid to be rendered in order to provide a place for the game to take place. I am using offset coordinates to store the hex grid in memory. When I attempt to render the grid, I get a result like the following with space between the hexes. I want the hexes to perfectly align so that all of the neccessary are at the exact same points, and so that their lines overlap and there is no space between the hexes
The coordinates inside each hex represent the offset coordinates of the hex.
I am using the distances shown in the following image (source) to decide where to render each hex.
The only StackExchange question I was able to find that addresses a problem that sounds like this one is here. It, however, only talks about a data structure to store the hexes in and not how to render them from said structure. I am attempting to render them as described here, however, I get the buggy result shown above.
Here is the portion of my code that handles rendering (it should be easy to infer what the various custom methods do, if there is any confusion please let me know):
#Override
public void paint(Graphics g){
int quarterHexSize = 32; //using non static final member variables because I want to make this dynamically determine the largest size of the hexagons that will fit on the screen later
int halfHexSize = quarterHexSize * 2;
int hexSize = halfHexSize * 2;
int cx, cy, renderX, renderY;
g.setColor(Color.DARK_GRAY);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.WHITE);
for(cx=0;cx<5;cx++){
for(cy=0;cy<5;cy++){
Hex hex = board.getHexAt(cx, cy);
if(hex != null){
renderX = cx * 2; //multiplying by 2 rather than using floats to represent half offset to simplify code and because IIRC integers are faster, I got the same error when using floats
renderY = cy * 2;
if(renderY % 4 != 0){
renderX++;
}
//converts the somewhat arbitrary units into the actual display size units using the values from the image in the question
renderX *= hexSize;
renderY *= quarterHexSize * 3f;
//the numbers are divided by 2 to make up for the offset
renderX /= 2;
renderY /= 2;
//64 is added to both dimensions to shift the grid inside the window
renderX += 64;
renderY += 64;
drawHex(new RenderPoint(renderX, renderY), halfHexSize, g);
g.drawString(cx + ", " + cy, renderX, renderY);
}
}
}
}
private void drawHex(RenderPoint center, int hexSize, Graphics g){
drawHexLine(center, hexSize, 0, 1, g);
drawHexLine(center, hexSize, 1, 2, g);
drawHexLine(center, hexSize, 2, 3, g);
drawHexLine(center, hexSize, 3, 4, g);
drawHexLine(center, hexSize, 4, 5, g);
drawHexLine(center, hexSize, 5, 0, g);
}
private void drawHexLine(RenderPoint center, int hexSize, int firstCornerNum, int secondCornerNum, Graphics g){
RenderPoint firstCornerNumHexPoint = getHexCorner(center, hexSize, firstCornerNum);
RenderPoint secondCornerNumHexPoint = getHexCorner(center, hexSize, secondCornerNum);
g.drawLine(
firstCornerNumHexPoint.getX(), firstCornerNumHexPoint.getY(),
secondCornerNumHexPoint.getX(), secondCornerNumHexPoint.getY()
);
//g.drawString(String.valueOf(firstCornerNum), firstCornerNumHexPoint.getX(), firstCornerNumHexPoint.getY());
}
private RenderPoint getHexCorner(RenderPoint center, int hexSize, int cornerNum){
return RenderPoint.doublesToInts( //simply rounds the provided doubles and creates a RenderPoint object with these new rounded values
center.getX() + hexSize * Math.sin(cornerNum * 60 * 0.0174533), //decimal number converts from degrees to radians
center.getY() + hexSize * Math.cos(cornerNum * 60 * 0.0174533)
);
}
I have determined what the error was. I had missed a specific detail in the webpage when I assumed that the diagram I posted in the question completely explained the placement of the hexagons.
I have changed the variable renderX to:
renderX *= Math.round(HALF_SQRT_THREE * hexSize);
HALF_SQRT_THREE is a constant I defined in the variable to avoid recalculating it each time a hexagon is rendered. It is defined as Math.sqrt(3)/2.0.
Here is the quote from the webpage:
The width of a hexagon is width = sqrt(3)/2 * height. The horizontal distance between adjacent hexes is horiz = width.
I am trying to draw multiple rectangles using separate integer values.
BufferedImage rectImage = bimage.myBImage;
BufferedImage pointImage = bimage.myBImage;
Graphics g = rectImage.createGraphics();
Graphics h = pointImage.createGraphics();
Color rectangle = Color.CYAN;
g.setColor(rectangle);
int alon = -118;
int alat = 34;
int x = (int) Math.round((alon-UL_Lon)/dXLon);
int y = (int) Math.round((UL_Lat-alat)/dYLat);
g.drawRect(x - 5, y - 5, 10, 10);
Color point = Color.BLUE;
h.setColor(point);
h.drawLine(x, y, x, y);
I have multiple values for alon and alat that I would like to be able to input without having to rewrite the code over and over again.
How should I go about doing something like this?
This question is pretty vague and not worded very well. I'm presuming from what I've gathered you want to render multiple rectangles at different places with different sizes. You should first define how many rectangles you want:
int numberOfRectangles = 5;
Then you should create an array that can hold the number of rectangles you defined:
Rectangle rectangles[] = new Rectangle[numberOfRectangles];
Now, you must define the rectangles:
rectangles[0] = new Rectangle(7, 64, 32, 32);
rectangles[1] = new Rectangle(64, 18, 4, 32);
You appear to also want to set colors? So you can do that by making an array:
Color rectangleColors[] = new Color[numberOfRectangles];
Then also defining them:
rectangleColors[0] = new Color(255, 48, 128);
rectangleColors[1] = Color.GREEN;
Because of the system you don't have to define all the rectangles at once. Now, you must cycle through the rectangles array and render each of the defined rectangles using the render method:
for (int i = 0; i < numberOfRectangles; i++)
{
g.setColor(rectangleColors[i]);
g.fillRect(rectangles[i].getX(), rectangles[i].getY(), rectangles[i].getWidth(), rectangles[i].getHeight());
}
This should work, though I haven't tested it out in Java.
If the values that you want to give your Rectangles completely random, then you'll have to copy-paste the code and change the values. However, if the values are a sequence (like you want to create 20 rectangle adjacent to each other for instance) You can use a for loop.
for(int i=1; i<11; i++){
int x = (int) Math.round((alon-UL_Lon + i)/dXLon);
int y = (int) Math.round((UL_Lat-alat + i)/dYLat);
g.drawRect(x - 5, y - 5, 10, 10);
}
Like this
I am trying to get some circles drawn onscreen using OpenGL ES 1.5 for android. They draw, but I want to be able to input x=300, y=500, and it will draw the circle centered at that coordinate (e.g. at the (300,500) pixel on the screen). Currently, I draw and translate the circles, but its not precise, I don't know how to get it exactly where i want it: here's some broken code from my last attempt:
//doesn't take w/h ratio into consideration, not sure how to implement that
gl.glViewport(0, 0, windowWidth, windowHeight);
gl.glOrthof(0,windowWidth, 0, windowHeight, 1, 2);
GLU.gluLookAt(gl, 0, 0, 5, 0, 0, 0, 0, 1, 0);
//And for drawing a circle, with the desired x and y coordinates:
for (int j = 0; j < number_Triangles; j++) {
x = Math.cos(theta) + xCoor;
y = Math.sin(theta) + yCoor;
z = 1;
theta += 2 * Math.PI / (number_Triangles);
}
If you are doing 2D graphics, I'd recommend gluOrtho2D(left,right,bottom,top). That way you have exact control over what coordinates will map to each edge of your screen.
So, for example, you could have:
gl.glViewport(0,0,windowWidth,windowHeight);
GLU.gluOrtho2D(-2.0f, 2.0f, -2.0f, 2.0f);
for (int j = .....