Draw an Arc and gradient it - java

I would like to know if its possible to draw a Arc on a graphics Panel using a gradient and how I would go about it.
My end goal would be to rotate the arc in a full circle so it would be similar to a rotating loading circle. However it is not a loading bar. It would be a background of a custom JButton.
Any suggestions to alternatives that would create a similar effect would be appreciated.
This is similar to what oi want to draw. Keep in mind that it will be "rotating"

public class TestArc {
public static void main(String[] args) {
new TestArc();
}
public TestArc() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int radius = Math.min(getWidth(), getHeight());
int x = (getWidth() - radius) / 2;
int y = (getHeight() - radius) / 2;
RadialGradientPaint rgp = new RadialGradientPaint(
new Point(getWidth() / 2, getHeight() / 2),
radius,
new float[]{0f, 1f},
new Color[]{Color.RED, Color.YELLOW}
);
g2d.setPaint(rgp);
g2d.fill(new Arc2D.Float(x, y, radius, radius, 0, 45, Arc2D.PIE));
g2d.dispose();
}
}
}
You might like to have a look at 2D Graphics for more info
Updated after additional input
So you want a conical fill effect then...
The implementation I have comes from Harmonic Code, but I can't find a direct reference to it (I think it's part of his (excellent) series), but you can see the source code here
Now. I had issues with the angles as it appears that 0 starts at the top point (not the left) and it doesn't like negative angles...you might have better luck, but what I did was create a basic buffer at a position I could easily get working and then rotate the graphics context using an AffineTransformation...
public class TestArc {
public static void main(String[] args) {
new TestArc();
}
public TestArc() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private float angle = 0;
private float extent = 270;
private BufferedImage buffer;
public TestPane() {
Timer timer = new Timer(125, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
angle -= 5;
if (angle > 360) {
angle = 0;
}
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(false);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected BufferedImage getBuffer() {
if (buffer == null) {
int radius = Math.min(getWidth(), getHeight());
int x = (getWidth() - radius) / 2;
int y = (getHeight() - radius) / 2;
buffer = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = buffer.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
float startAngle = 0;
Color start = new Color(0, 128, 0, 128);
Color end = new Color(0, 128, 0, 0);
ConicalGradientPaint rgp = new ConicalGradientPaint(
true,
new Point(getWidth() / 2, getHeight() / 2),
0.5f,
new float[]{startAngle, extent},
new Color[]{start, end});
g2d.setPaint(rgp);
g2d.fill(new Arc2D.Float(x, y, radius, radius, startAngle + 90, -extent, Arc2D.PIE));
// g2d.fill(new Ellipse2D.Float(0, 0, radius, radius));
g2d.dispose();
g2d.dispose();
}
return buffer;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int radius = Math.min(getWidth(), getHeight());
int x = (getWidth()) / 2;
int y = (getHeight()) / 2;
BufferedImage buffer = getBuffer();
g2d.setTransform(AffineTransform.getRotateInstance(Math.toRadians(angle), x, y));
x = (getWidth() - buffer.getWidth()) / 2;
y = (getHeight() - buffer.getHeight()) / 2;
g2d.drawImage(buffer, x, y, this);
g2d.dispose();
}
}
public final class ConicalGradientPaint implements java.awt.Paint {
private final java.awt.geom.Point2D CENTER;
private final double[] FRACTION_ANGLES;
private final double[] RED_STEP_LOOKUP;
private final double[] GREEN_STEP_LOOKUP;
private final double[] BLUE_STEP_LOOKUP;
private final double[] ALPHA_STEP_LOOKUP;
private final java.awt.Color[] COLORS;
private static final float INT_TO_FLOAT_CONST = 1f / 255f;
/**
* Standard constructor which takes the FRACTIONS in values from 0.0f to
* 1.0f
*
* #param CENTER
* #param GIVEN_FRACTIONS
* #param GIVEN_COLORS
* #throws IllegalArgumentException
*/
public ConicalGradientPaint(final java.awt.geom.Point2D CENTER, final float[] GIVEN_FRACTIONS, final java.awt.Color[] GIVEN_COLORS) throws IllegalArgumentException {
this(false, CENTER, 0.0f, GIVEN_FRACTIONS, GIVEN_COLORS);
}
/**
* Enhanced constructor which takes the FRACTIONS in degress from 0.0f to
* 360.0f and also an GIVEN_OFFSET in degrees around the rotation CENTER
*
* #param USE_DEGREES
* #param CENTER
* #param GIVEN_OFFSET
* #param GIVEN_FRACTIONS
* #param GIVEN_COLORS
* #throws IllegalArgumentException
*/
public ConicalGradientPaint(final boolean USE_DEGREES, final java.awt.geom.Point2D CENTER, final float GIVEN_OFFSET, final float[] GIVEN_FRACTIONS, final java.awt.Color[] GIVEN_COLORS) throws IllegalArgumentException {
// Check that fractions and colors are of the same size
if (GIVEN_FRACTIONS.length != GIVEN_COLORS.length) {
throw new IllegalArgumentException("Fractions and colors must be equal in size");
}
final java.util.ArrayList<Float> FRACTION_LIST = new java.util.ArrayList<Float>(GIVEN_FRACTIONS.length);
final float OFFSET;
if (USE_DEGREES) {
final double DEG_FRACTION = 1f / 360f;
if (Double.compare((GIVEN_OFFSET * DEG_FRACTION), -0.5) == 0) {
OFFSET = -0.5f;
} else if (Double.compare((GIVEN_OFFSET * DEG_FRACTION), 0.5) == 0) {
OFFSET = 0.5f;
} else {
OFFSET = (float) (GIVEN_OFFSET * DEG_FRACTION);
}
for (float fraction : GIVEN_FRACTIONS) {
FRACTION_LIST.add((float) (fraction * DEG_FRACTION));
}
} else {
// Now it seems to work with rotation of 0.5f, below is the old code to correct the problem
// if (GIVEN_OFFSET == -0.5)
// {
// // This is needed because of problems in the creation of the Raster
// // with a angle offset of exactly -0.5
// OFFSET = -0.49999f;
// }
// else if (GIVEN_OFFSET == 0.5)
// {
// // This is needed because of problems in the creation of the Raster
// // with a angle offset of exactly +0.5
// OFFSET = 0.499999f;
// }
// else
{
OFFSET = GIVEN_OFFSET;
}
for (float fraction : GIVEN_FRACTIONS) {
FRACTION_LIST.add(fraction);
}
}
// Check for valid offset
if (OFFSET > 0.5f || OFFSET < -0.5f) {
throw new IllegalArgumentException("Offset has to be in the range of -0.5 to 0.5");
}
// Adjust fractions and colors array in the case where startvalue != 0.0f and/or endvalue != 1.0f
final java.util.List<java.awt.Color> COLOR_LIST = new java.util.ArrayList<java.awt.Color>(GIVEN_COLORS.length);
COLOR_LIST.addAll(java.util.Arrays.asList(GIVEN_COLORS));
// Assure that fractions start with 0.0f
if (FRACTION_LIST.get(0) != 0.0f) {
FRACTION_LIST.add(0, 0.0f);
final java.awt.Color TMP_COLOR = COLOR_LIST.get(0);
COLOR_LIST.add(0, TMP_COLOR);
}
// Assure that fractions end with 1.0f
if (FRACTION_LIST.get(FRACTION_LIST.size() - 1) != 1.0f) {
FRACTION_LIST.add(1.0f);
COLOR_LIST.add(GIVEN_COLORS[0]);
}
// Recalculate the fractions and colors with the given offset
final java.util.Map<Float, java.awt.Color> FRACTION_COLORS = recalculate(FRACTION_LIST, COLOR_LIST, OFFSET);
// Clear the original FRACTION_LIST and COLOR_LIST
FRACTION_LIST.clear();
COLOR_LIST.clear();
// Sort the hashmap by fraction and add the values to the FRACION_LIST and COLOR_LIST
final java.util.SortedSet<Float> SORTED_FRACTIONS = new java.util.TreeSet<Float>(FRACTION_COLORS.keySet());
final java.util.Iterator<Float> ITERATOR = SORTED_FRACTIONS.iterator();
while (ITERATOR.hasNext()) {
final float CURRENT_FRACTION = ITERATOR.next();
FRACTION_LIST.add(CURRENT_FRACTION);
COLOR_LIST.add(FRACTION_COLORS.get(CURRENT_FRACTION));
}
// Set the values
this.CENTER = CENTER;
COLORS = COLOR_LIST.toArray(new java.awt.Color[]{});
// Prepare lookup table for the angles of each fraction
final int MAX_FRACTIONS = FRACTION_LIST.size();
this.FRACTION_ANGLES = new double[MAX_FRACTIONS];
for (int i = 0; i < MAX_FRACTIONS; i++) {
FRACTION_ANGLES[i] = FRACTION_LIST.get(i) * 360;
}
// Prepare lookup tables for the color stepsize of each color
RED_STEP_LOOKUP = new double[COLORS.length];
GREEN_STEP_LOOKUP = new double[COLORS.length];
BLUE_STEP_LOOKUP = new double[COLORS.length];
ALPHA_STEP_LOOKUP = new double[COLORS.length];
for (int i = 0; i < (COLORS.length - 1); i++) {
RED_STEP_LOOKUP[i] = ((COLORS[i + 1].getRed() - COLORS[i].getRed()) * INT_TO_FLOAT_CONST) / (FRACTION_ANGLES[i + 1] - FRACTION_ANGLES[i]);
GREEN_STEP_LOOKUP[i] = ((COLORS[i + 1].getGreen() - COLORS[i].getGreen()) * INT_TO_FLOAT_CONST) / (FRACTION_ANGLES[i + 1] - FRACTION_ANGLES[i]);
BLUE_STEP_LOOKUP[i] = ((COLORS[i + 1].getBlue() - COLORS[i].getBlue()) * INT_TO_FLOAT_CONST) / (FRACTION_ANGLES[i + 1] - FRACTION_ANGLES[i]);
ALPHA_STEP_LOOKUP[i] = ((COLORS[i + 1].getAlpha() - COLORS[i].getAlpha()) * INT_TO_FLOAT_CONST) / (FRACTION_ANGLES[i + 1] - FRACTION_ANGLES[i]);
}
}
/**
* Recalculates the fractions in the FRACTION_LIST and their associated
* colors in the COLOR_LIST with a given OFFSET. Because the conical
* gradients always starts with 0 at the top and clockwise direction you
* could rotate the defined conical gradient from -180 to 180 degrees which
* equals values from -0.5 to +0.5
*
* #param FRACTION_LIST
* #param COLOR_LIST
* #param OFFSET
* #return Hashmap that contains the recalculated fractions and colors after
* a given rotation
*/
private java.util.HashMap<Float, java.awt.Color> recalculate(final java.util.List<Float> FRACTION_LIST, final java.util.List<java.awt.Color> COLOR_LIST, final float OFFSET) {
// Recalculate the fractions and colors with the given offset
final int MAX_FRACTIONS = FRACTION_LIST.size();
final java.util.HashMap<Float, java.awt.Color> FRACTION_COLORS = new java.util.HashMap<Float, java.awt.Color>(MAX_FRACTIONS);
for (int i = 0; i < MAX_FRACTIONS; i++) {
// Add offset to fraction
final float TMP_FRACTION = FRACTION_LIST.get(i) + OFFSET;
// Color related to current fraction
final java.awt.Color TMP_COLOR = COLOR_LIST.get(i);
// Check each fraction for limits (0...1)
if (TMP_FRACTION <= 0) {
FRACTION_COLORS.put(1.0f + TMP_FRACTION + 0.0001f, TMP_COLOR);
final float NEXT_FRACTION;
final java.awt.Color NEXT_COLOR;
if (i < MAX_FRACTIONS - 1) {
NEXT_FRACTION = FRACTION_LIST.get(i + 1) + OFFSET;
NEXT_COLOR = COLOR_LIST.get(i + 1);
} else {
NEXT_FRACTION = 1 - FRACTION_LIST.get(0) + OFFSET;
NEXT_COLOR = COLOR_LIST.get(0);
}
if (NEXT_FRACTION > 0) {
final java.awt.Color NEW_FRACTION_COLOR = getColorFromFraction(TMP_COLOR, NEXT_COLOR, (int) ((NEXT_FRACTION - TMP_FRACTION) * 10000), (int) ((-TMP_FRACTION) * 10000));
FRACTION_COLORS.put(0.0f, NEW_FRACTION_COLOR);
FRACTION_COLORS.put(1.0f, NEW_FRACTION_COLOR);
}
} else if (TMP_FRACTION >= 1) {
FRACTION_COLORS.put(TMP_FRACTION - 1.0f - 0.0001f, TMP_COLOR);
final float PREVIOUS_FRACTION;
final java.awt.Color PREVIOUS_COLOR;
if (i > 0) {
PREVIOUS_FRACTION = FRACTION_LIST.get(i - 1) + OFFSET;
PREVIOUS_COLOR = COLOR_LIST.get(i - 1);
} else {
PREVIOUS_FRACTION = FRACTION_LIST.get(MAX_FRACTIONS - 1) + OFFSET;
PREVIOUS_COLOR = COLOR_LIST.get(MAX_FRACTIONS - 1);
}
if (PREVIOUS_FRACTION < 1) {
final java.awt.Color NEW_FRACTION_COLOR = getColorFromFraction(TMP_COLOR, PREVIOUS_COLOR, (int) ((TMP_FRACTION - PREVIOUS_FRACTION) * 10000), (int) (TMP_FRACTION - 1.0f) * 10000);
FRACTION_COLORS.put(1.0f, NEW_FRACTION_COLOR);
FRACTION_COLORS.put(0.0f, NEW_FRACTION_COLOR);
}
} else {
FRACTION_COLORS.put(TMP_FRACTION, TMP_COLOR);
}
}
// Clear the original FRACTION_LIST and COLOR_LIST
FRACTION_LIST.clear();
COLOR_LIST.clear();
return FRACTION_COLORS;
}
/**
* With the START_COLOR at the beginning and the DESTINATION_COLOR at the
* end of the given RANGE the method will calculate and return the color
* that equals the given VALUE. e.g. a START_COLOR of BLACK (R:0, G:0, B:0,
* A:255) and a DESTINATION_COLOR of WHITE(R:255, G:255, B:255, A:255) with
* a given RANGE of 100 and a given VALUE of 50 will return the color that
* is exactly in the middle of the gradient between black and white which is
* gray(R:128, G:128, B:128, A:255) So this method is really useful to
* calculate colors in gradients between two given colors.
*
* #param START_COLOR
* #param DESTINATION_COLOR
* #param RANGE
* #param VALUE
* #return Color calculated from a range of values by given value
*/
public java.awt.Color getColorFromFraction(final java.awt.Color START_COLOR, final java.awt.Color DESTINATION_COLOR, final int RANGE, final int VALUE) {
final float SOURCE_RED = START_COLOR.getRed() * INT_TO_FLOAT_CONST;
final float SOURCE_GREEN = START_COLOR.getGreen() * INT_TO_FLOAT_CONST;
final float SOURCE_BLUE = START_COLOR.getBlue() * INT_TO_FLOAT_CONST;
final float SOURCE_ALPHA = START_COLOR.getAlpha() * INT_TO_FLOAT_CONST;
final float DESTINATION_RED = DESTINATION_COLOR.getRed() * INT_TO_FLOAT_CONST;
final float DESTINATION_GREEN = DESTINATION_COLOR.getGreen() * INT_TO_FLOAT_CONST;
final float DESTINATION_BLUE = DESTINATION_COLOR.getBlue() * INT_TO_FLOAT_CONST;
final float DESTINATION_ALPHA = DESTINATION_COLOR.getAlpha() * INT_TO_FLOAT_CONST;
final float RED_DELTA = DESTINATION_RED - SOURCE_RED;
final float GREEN_DELTA = DESTINATION_GREEN - SOURCE_GREEN;
final float BLUE_DELTA = DESTINATION_BLUE - SOURCE_BLUE;
final float ALPHA_DELTA = DESTINATION_ALPHA - SOURCE_ALPHA;
final float RED_FRACTION = RED_DELTA / RANGE;
final float GREEN_FRACTION = GREEN_DELTA / RANGE;
final float BLUE_FRACTION = BLUE_DELTA / RANGE;
final float ALPHA_FRACTION = ALPHA_DELTA / RANGE;
//System.out.println(DISTANCE + " " + CURRENT_FRACTION);
return new java.awt.Color(SOURCE_RED + RED_FRACTION * VALUE, SOURCE_GREEN + GREEN_FRACTION * VALUE, SOURCE_BLUE + BLUE_FRACTION * VALUE, SOURCE_ALPHA + ALPHA_FRACTION * VALUE);
}
#Override
public java.awt.PaintContext createContext(final java.awt.image.ColorModel COLOR_MODEL, final java.awt.Rectangle DEVICE_BOUNDS, final java.awt.geom.Rectangle2D USER_BOUNDS, final java.awt.geom.AffineTransform TRANSFORM, final java.awt.RenderingHints HINTS) {
final java.awt.geom.Point2D TRANSFORMED_CENTER = TRANSFORM.transform(CENTER, null);
return new ConicalGradientPaintContext(TRANSFORMED_CENTER);
}
#Override
public int getTransparency() {
return java.awt.Transparency.TRANSLUCENT;
}
private final class ConicalGradientPaintContext implements java.awt.PaintContext {
final private java.awt.geom.Point2D CENTER;
public ConicalGradientPaintContext(final java.awt.geom.Point2D CENTER) {
this.CENTER = new java.awt.geom.Point2D.Double(CENTER.getX(), CENTER.getY());
}
#Override
public void dispose() {
}
#Override
public java.awt.image.ColorModel getColorModel() {
return java.awt.image.ColorModel.getRGBdefault();
}
#Override
public java.awt.image.Raster getRaster(final int X, final int Y, final int TILE_WIDTH, final int TILE_HEIGHT) {
final double ROTATION_CENTER_X = -X + CENTER.getX();
final double ROTATION_CENTER_Y = -Y + CENTER.getY();
final int MAX = FRACTION_ANGLES.length;
// Create raster for given colormodel
final java.awt.image.WritableRaster RASTER = getColorModel().createCompatibleWritableRaster(TILE_WIDTH, TILE_HEIGHT);
// Create data array with place for red, green, blue and alpha values
int[] data = new int[(TILE_WIDTH * TILE_HEIGHT * 4)];
double dx;
double dy;
double distance;
double angle;
double currentRed = 0;
double currentGreen = 0;
double currentBlue = 0;
double currentAlpha = 0;
for (int py = 0; py < TILE_HEIGHT; py++) {
for (int px = 0; px < TILE_WIDTH; px++) {
// Calculate the distance between the current position and the rotation angle
dx = px - ROTATION_CENTER_X;
dy = py - ROTATION_CENTER_Y;
distance = Math.sqrt(dx * dx + dy * dy);
// Avoid division by zero
if (distance == 0) {
distance = 1;
}
// 0 degree on top
angle = Math.abs(Math.toDegrees(Math.acos(dx / distance)));
if (dx >= 0 && dy <= 0) {
angle = 90.0 - angle;
} else if (dx >= 0 && dy >= 0) {
angle += 90.0;
} else if (dx <= 0 && dy >= 0) {
angle += 90.0;
} else if (dx <= 0 && dy <= 0) {
angle = 450.0 - angle;
}
// Check for each angle in fractionAngles array
for (int i = 0; i < (MAX - 1); i++) {
if ((angle >= FRACTION_ANGLES[i])) {
currentRed = COLORS[i].getRed() * INT_TO_FLOAT_CONST + (angle - FRACTION_ANGLES[i]) * RED_STEP_LOOKUP[i];
currentGreen = COLORS[i].getGreen() * INT_TO_FLOAT_CONST + (angle - FRACTION_ANGLES[i]) * GREEN_STEP_LOOKUP[i];
currentBlue = COLORS[i].getBlue() * INT_TO_FLOAT_CONST + (angle - FRACTION_ANGLES[i]) * BLUE_STEP_LOOKUP[i];
currentAlpha = COLORS[i].getAlpha() * INT_TO_FLOAT_CONST + (angle - FRACTION_ANGLES[i]) * ALPHA_STEP_LOOKUP[i];
continue;
}
}
// Fill data array with calculated color values
final int BASE = (py * TILE_WIDTH + px) * 4;
data[BASE + 0] = (int) (currentRed * 255);
data[BASE + 1] = (int) (currentGreen * 255);
data[BASE + 2] = (int) (currentBlue * 255);
data[BASE + 3] = (int) (currentAlpha * 255);
}
}
// Fill the raster with the data
RASTER.setPixels(0, 0, TILE_WIDTH, TILE_HEIGHT, data);
return RASTER;
}
}
}
}

Related

Java tile map using Libgdx: finding tile at mouse position

I've looked at several examples of people creating tile maps, and I am unable to get the tile position where my mouse is pointed at.
I am using a spritebatch and GameTile[][] to create the map. Keep in mind that the tiles themselves are isometric and not actually a square.
The method renderMap() is where the map is actually is being rendered. createMap() just sets the initial GameTiles for an empty map.
The map is able to be dragged and zoomed in and out using Ortho camera.
Zooming out gives me an issue as well, the tiles seem to be shifted over on click
public class MapEditor implements GameScene {
private GameContext context;
private SpriteBatch batch;
private OrthographicCamera camera;
public static GameTile[][] tiles; //GameTile.WIDTH = 64 & GameTile.HEIGHT =48
public static final int MAP_WIDTH = 20;
public static final int MAP_HEIGHT = 36;
public MapEditor(GameContext context) {
this.context = context;
tiles = new GameTile[MAP_WIDTH][MAP_HEIGHT];
}
#Override
public void create() {
renderer = new ShapeRenderer();
this.batch = new SpriteBatch();
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
}
public void createMap() {
// Create the sea tiles
for (int x = 0; x < MAP_WIDTH; x++) {
for (int y = 0; y < MAP_HEIGHT; y++) {
if (y < 3 || y > 32) {
if(tiles[x][y] == null) {
tiles[x][y] = safezone;
}
}
else {
if(tiles[x][y] == null) {
tiles[x][y] = cell;
}
}
}
}
}
#Override
public void update(){
// update the camera
camera.update();
}
#Override
public void render() {
batch.setProjectionMatrix(camera.combined);
batch.begin();
Gdx.gl.glViewport(0,0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
renderMap();
batch.end();
}
public int getTileX(float x, float y) {
/*
* getRegionWidth() = TILE_WIDTH_HALF
* getRegionHeight() = TILE_HEIGHT_HALF
* these are the ones being added to worldCoords.x/y
*/
Vector3 worldCoords = camera.unproject(new Vector3(x, y, 0));
return (int)((TILE_WIDTH_HALF * ((-TILE_HEIGHT_HALF + (worldCoords.y + TILE_HEIGHT_HALF)) /
TILE_HEIGHT_HALF) + (worldCoords.x + TILE_WIDTH_HALF)) / TILE_WIDTH_HALF) / 2;
}
public int getTileY(float x, float y) {
/*
* getRegionWidth() = TILE_WIDTH_HALF
* getRegionHeight() = TILE_HEIGHT_HALF
* these are the ones being added to worldCoords.x/y
*/
Vector3 worldCoords = camera.unproject(new Vector3(x, y, 0));
return (int)(((-TILE_HEIGHT_HALF * (TILE_WIDTH_HALF + (worldCoords.x + TILE_WIDTH_HALF)) /
TILE_WIDTH_HALF) + (worldCoords.y + TILE_HEIGHT_HALF)) / TILE_HEIGHT_HALF) / 2;
}
#Override
public boolean handleClick(float x, float y, int button) {
int tileX = getTileX(x,y);
int tileY = getTileY(x,y);
System.out.println("Tile:"+tileX + ","+tileY);
}
private void renderMap() {
for (int i = 0; i < tiles.length; i++) {
for(int j = 0; j < tiles[i].length; j++) {
TextureRegion region = tiles[i][j].getRegion();
int x = (i * GameTile.TILE_WIDTH / 2) - (j * GameTile.TILE_WIDTH / 2) - region.getRegionWidth() / 2;
int y = (i * GameTile.TILE_HEIGHT / 2) + (j * GameTile.TILE_HEIGHT / 2) - region.getRegionHeight() / 2;
if (canDraw(x, y, GameTile.TILE_WIDTH, GameTile.TILE_HEIGHT)) {
batch.draw(region, x, y);
}
}
}
}
Actual tile before doing anything to it;
Actual:
Desired:
Converting Cartesian coordinates to isometric is (sort of) done like this:
float isometricX = cartesianX - cartesianY;
float isometricY = (cartesianX + cartesianY) * 0.5f;
The formula needs to be scaled by the height-to-width ratio of the tiles as well and I think that is where it's going wrong in your code.
Given an unprojected worldMousePosition you can get the coordinates and tile coordinates like this:
float r = (float) TILE_HEIGHT / (float) TILE_WIDTH;
float mapx = (worldMousePosition.x / TILE_HEIGHT + worldMousePosition.y / (TILE_HEIGHT * r)) * r;
float mapy = (worldMousePosition.y / (TILE_HEIGHT * r) - (worldMousePosition.x / TILE_HEIGHT)) * r;
worldPosition = new Vector2(mapx - 0.5f, mapy + 0.5f); // -.5/+.5 because the drawing isn't aligned to the tile, it's aligned to the image
int tileX = (int) worldPosition.x;
int tileY = (int) worldPosition.y;
Full source code for the example above:
import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
public class SandboxGame extends Game {
public static final int TILE_NONE = -1;
public static final int MAP_WIDTH = 20;
public static final int MAP_HEIGHT = 36;
public static final int TILE_WIDTH = 64;
public static final int TILE_HEIGHT = 48;
private SpriteBatch batch;
private OrthographicCamera camera;
private BitmapFont font;
private Vector3 unprojectVector = new Vector3();
private Vector2 worldMousePosition = new Vector2();
private Vector2 worldPosition = new Vector2();
private Texture[] textures;
private int[][] tiles = new int[MAP_WIDTH][MAP_HEIGHT];
#Override
public void create() {
batch = new SpriteBatch();
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
font = new BitmapFont(Gdx.files.internal("default.fnt"), Gdx.files.internal("default.png"), false);
textures = new Texture[] {
new Texture(Gdx.files.internal("tile.png"))
};
for(int x = 0; x < MAP_WIDTH; ++x) {
for(int y = 0; y < MAP_HEIGHT; ++y) {
int rnd = MathUtils.random(10);
if (rnd < 1)
tiles[x][y] = TILE_NONE;
else
tiles[x][y] = 0;
}
}
}
#Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
float scrollSpeed = 64;
float zoomSpeed = 2;
float delta = Gdx.graphics.getDeltaTime();
if (Gdx.input.isKeyPressed(Input.Keys.A))
camera.position.x -= delta * scrollSpeed;
if (Gdx.input.isKeyPressed(Input.Keys.D))
camera.position.x += delta * scrollSpeed;
if (Gdx.input.isKeyPressed(Input.Keys.W))
camera.position.y += delta * scrollSpeed;
if (Gdx.input.isKeyPressed(Input.Keys.S))
camera.position.y -= delta * scrollSpeed;
if (Gdx.input.isKeyPressed(Input.Keys.Q))
camera.zoom = Math.min(camera.zoom + zoomSpeed * delta, 8.0f);
if (Gdx.input.isKeyPressed(Input.Keys.E))
camera.zoom = Math.max(camera.zoom - zoomSpeed * delta, 0.5f);
camera.update();
int mx = Gdx.input.getX();
int my = Gdx.input.getY();
camera.unproject(unprojectVector.set(mx, my, 0.0f));
worldMousePosition.set(unprojectVector.x, unprojectVector.y);
float r = (float) TILE_HEIGHT / (float) TILE_WIDTH;
float mapx = (worldMousePosition.x / TILE_HEIGHT + worldMousePosition.y / (TILE_HEIGHT * r)) * r;
float mapy = (worldMousePosition.y / (TILE_HEIGHT * r) - (worldMousePosition.x / TILE_HEIGHT)) * r;
worldPosition = new Vector2(mapx - 0.5f, mapy + 0.5f); // -.5/+.5 because the drawing isn't aligned to the tile, it's aligned to the image
int tileX = (int) worldPosition.x;
int tileY = (int) worldPosition.y;
batch.setProjectionMatrix(camera.combined);
batch.begin();
for (int col = MAP_WIDTH - 1; col >= 0; --col) {
for (int row = MAP_HEIGHT - 1; row >= 0; --row) {
if (tiles[col][row] != TILE_NONE) {
Texture texture = textures[tiles[col][row]];
int x = (col * TILE_WIDTH / 2) - (row * TILE_WIDTH / 2);
int y = (col * TILE_HEIGHT / 2) + (row * TILE_HEIGHT / 2);
batch.setColor(col == tileX && row == tileY ? Color.GRAY : Color.WHITE);
batch.draw(texture, x, y);
}
}
}
if (Gdx.input.isKeyPressed(Input.Keys.SPACE)) {
for (int col = MAP_WIDTH - 1; col >= 0; --col) {
for (int row = MAP_HEIGHT - 1; row >= 0; --row) {
int x = (col * TILE_WIDTH / 2) - (row * TILE_WIDTH / 2);
int y = (col * TILE_HEIGHT / 2) + (row * TILE_HEIGHT / 2);
font.draw(batch, String.format("(%d, %d)", col, row), x, y);
}
}
}
String str = String.format("World position (%.2f, %.2f), Tile (%d, %d)", worldPosition.x, worldPosition.y, (int)worldPosition.x, (int)worldPosition.y);
font.draw(batch, str, worldMousePosition.x, worldMousePosition.y);
batch.end();
}
}
I cant respond to bornander's post, but my tweak would be at
int tileX = (int) Math.Floor(worldPosition.x);
int tileY = (int) Math.Floor(worldPosition.y);
Where simple (int) cast will provide wrong position around 0 with negative values, if there are tiles, while using Math.Floor will work as intended.

LWJGL 2 - Always show front of 3D font

I'm trying to always show the front of a 3D font to the user. I tried rotating the font when rotating the camera, but never could get it to work.
I currently have this:
I'm trying to do this (font always faces front):
TrueTypeFont.java
package com.displee.render.font;
import lombok.AllArgsConstructor;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* A TrueType font implementation originally for Slick, edited for Bobjob's Engine
* #original author James Chambers (Jimmy)
* #original author Jeremy Adams (elias4444)
* #original author Kevin Glass (kevglass)
* #original author Peter Korzuszek (genail)
* #new version edited by David Aaron Muhar (bobjob)
*/
public class TrueTypeFont {
public final static int ALIGN_LEFT = 0, ALIGN_RIGHT = 1, ALIGN_CENTER = 2;
/**
* Array that holds necessary information about the font characters
*/
private IntObject[] charArray = new IntObject[256];
/**
* Map of user defined font characters (Character <-> IntObject)
*/
private Map customChars = new HashMap();
/**
* Boolean flag on whether AntiAliasing is enabled or not
*/
private boolean antiAlias;
/**
* Font's size
*/
private int fontSize = 0;
/**
* Font's height
*/
private int fontHeight = 0;
/**
* Texture used to cache the font 0-255 characters
*/
private int fontTextureID;
/**
* Default font texture width
*/
private int textureWidth = 512;
/**
* Default font texture height
*/
private int textureHeight = 512;
/**
* A reference to Java's AWT Font that we create our font texture from
*/
private Font font;
/**
* The font metrics for our Java AWT font
*/
private FontMetrics fontMetrics;
private int correctL = 9, correctR = 8;
private class IntObject {
/**
* Character's width
*/
public int width;
/**
* Character's height
*/
public int height;
/**
* Character's stored x position
*/
public int storedX;
/**
* Character's stored y position
*/
public int storedY;
}
public TrueTypeFont(Font font, boolean antiAlias, char[] additionalChars) {
this.font = font;
this.fontSize = font.getSize() + 3;
this.antiAlias = antiAlias;
createSet(additionalChars);
fontHeight -= 1;
if (fontHeight <= 0) {
fontHeight = 1;
}
}
public TrueTypeFont(Font font, boolean antiAlias) {
this(font, antiAlias, null);
}
public void setCorrection(boolean on) {
if (on) {
correctL = 2;
correctR = 1;
} else {
correctL = 0;
correctR = 0;
}
}
private BufferedImage getFontImage(char ch) {
// Create a temporary image to extract the character's size
BufferedImage tempfontImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D) tempfontImage.getGraphics();
if (antiAlias == true) {
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
g.setFont(font);
fontMetrics = g.getFontMetrics();
int charwidth = fontMetrics.charWidth(ch) + 8;
if (charwidth <= 0) {
charwidth = 7;
}
int charheight = fontMetrics.getHeight() + 3;
if (charheight <= 0) {
charheight = fontSize;
}
// Create another image holding the character we are creating
BufferedImage fontImage;
fontImage = new BufferedImage(charwidth, charheight, BufferedImage.TYPE_INT_ARGB);
Graphics2D gt = (Graphics2D) fontImage.getGraphics();
if (antiAlias == true) {
gt.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
gt.setFont(font);
gt.setColor(Color.WHITE);
int charx = 3;
int chary = 1;
gt.drawString(String.valueOf(ch), (charx), (chary) + fontMetrics.getAscent());
//fontImage = ImageUtils.flipVertically(fontImage);
//fontImage = ImageUtils.flipHorizontally(fontImage);
//fontImage = ImageUtils.flipHorizontallyAndVertically(fontImage);
return fontImage;
}
private void createSet(char[] customCharsArray) {
// If there are custom chars then I expand the font texture twice
if (customCharsArray != null && customCharsArray.length > 0) {
textureWidth *= 2;
}
// In any case this should be done in other way. Texture with size 512x512
// can maintain only 256 characters with resolution of 32x32. The texture
// size should be calculated dynamicaly by looking at character sizes.
try {
BufferedImage imgTemp = new BufferedImage(textureWidth, textureHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D) imgTemp.getGraphics();
g.setColor(new Color(0, 0, 0, 1));
g.fillRect(0, 0, textureWidth, textureHeight);
int rowHeight = 0;
int positionX = 0;
int positionY = 0;
int customCharsLength = (customCharsArray != null) ? customCharsArray.length : 0;
for (int i = 0; i < 256 + customCharsLength; i++) {
// get 0-255 characters and then custom characters
char ch = (i < 256) ? (char) i : customCharsArray[i - 256];
BufferedImage fontImage = getFontImage(ch);
IntObject newIntObject = new IntObject();
newIntObject.width = fontImage.getWidth();
newIntObject.height = fontImage.getHeight();
if (positionX + newIntObject.width >= textureWidth) {
positionX = 0;
positionY += rowHeight;
rowHeight = 0;
}
newIntObject.storedX = positionX;
newIntObject.storedY = positionY;
if (newIntObject.height > fontHeight) {
fontHeight = newIntObject.height;
}
if (newIntObject.height > rowHeight) {
rowHeight = newIntObject.height;
}
// Draw it here
g.drawImage(fontImage, positionX, positionY, null);
positionX += newIntObject.width;
if (i < 256) { // standard characters
charArray[i] = newIntObject;
} else { // custom characters
customChars.put(new Character(ch), newIntObject);
}
fontImage = null;
}
fontTextureID = loadImage(imgTemp);
//.getTexture(font.toString(), imgTemp);
} catch (Exception e) {
System.err.println("Failed to create font.");
e.printStackTrace();
}
}
private void drawQuad(float drawX, float drawY, float drawX2, float drawY2, float srcX, float srcY, float srcX2, float srcY2, float z) {
float DrawWidth = drawX2 - drawX;
float DrawHeight = drawY2 - drawY;
float TextureSrcX = srcX / textureWidth;
float TextureSrcY = srcY / textureHeight;
float SrcWidth = srcX2 - srcX;
float SrcHeight = srcY2 - srcY;
float RenderWidth = (SrcWidth / textureWidth);
float RenderHeight = (SrcHeight / textureHeight);
GL11.glTexCoord2f(TextureSrcX, TextureSrcY);
GL11.glVertex3f(drawX, drawY, z);
GL11.glTexCoord2f(TextureSrcX, TextureSrcY + RenderHeight);
GL11.glVertex3f(drawX, drawY + DrawHeight, z);
GL11.glTexCoord2f(TextureSrcX + RenderWidth, TextureSrcY + RenderHeight);
GL11.glVertex3f(drawX + DrawWidth, drawY + DrawHeight, z);
GL11.glTexCoord2f(TextureSrcX + RenderWidth, TextureSrcY);
GL11.glVertex3f(drawX + DrawWidth, drawY, z);
}
public int getWidth(String whatchars) {
int totalwidth = 0;
IntObject intObject = null;
int currentChar = 0;
for (int i = 0; i < whatchars.length(); i++) {
currentChar = whatchars.charAt(i);
if (currentChar < 256) {
intObject = charArray[currentChar];
} else {
intObject = (IntObject) customChars.get(new Character((char) currentChar));
}
if (intObject != null) {
totalwidth += intObject.width;
}
}
return totalwidth;
}
public int getHeight() {
return fontHeight;
}
public int getHeight(String HeightString) {
return fontHeight;
}
public int getLineHeight() {
return fontHeight;
}
public void drawString(float x, float y, float z, String whatchars, float scaleX, float scaleY) {
drawString(x, y, z, whatchars, 0, whatchars.length() - 1, scaleX, scaleY, ALIGN_LEFT);
}
public void drawString(float x, float y, float z, String whatchars, float scaleX, float scaleY, int format) {
drawString(x, y, z, whatchars, 0, whatchars.length() - 1, scaleX, scaleY, format);
}
public void drawString(float x, float y, float z, String whatchars, int startIndex, int endIndex, float scaleX, float scaleY, int format) {
IntObject intObject = null;
int charCurrent;
int totalwidth = 0;
int i = startIndex, d, c;
float startY = 0;
switch (format) {
case ALIGN_RIGHT: {
d = -1;
c = correctR;
while (i < endIndex) {
if (whatchars.charAt(i) == '\n') {
startY -= fontHeight;
}
i++;
}
break;
}
case ALIGN_CENTER: {
for (int l = startIndex; l <= endIndex; l++) {
charCurrent = whatchars.charAt(l);
if (charCurrent == '\n') {
break;
}
if (charCurrent < 256) {
intObject = charArray[charCurrent];
} else {
intObject = (IntObject) customChars.get(new Character((char) charCurrent));
}
totalwidth += intObject.width - correctL;
}
totalwidth /= -2;
}
case ALIGN_LEFT:
default: {
d = 1;
c = correctL;
break;
}
}
java.util.List<QuadObject> list = new ArrayList<>(endIndex - startIndex);
while (i >= startIndex && i <= endIndex) {
charCurrent = whatchars.charAt(i);
if (charCurrent < 256) {
intObject = charArray[charCurrent];
} else {
intObject = (IntObject) customChars.get(new Character((char) charCurrent));
}
if (intObject != null) {
if (d < 0) {
totalwidth += (intObject.width - c) * d;
}
if (charCurrent == '\n') {
startY -= fontHeight * d;
totalwidth = 0;
if (format == ALIGN_CENTER) {
for (int l = i + 1; l <= endIndex; l++) {
charCurrent = whatchars.charAt(l);
if (charCurrent == '\n') {
break;
}
if (charCurrent < 256) {
intObject = charArray[charCurrent];
} else {
intObject = (IntObject) customChars.get(new Character((char) charCurrent));
}
totalwidth += intObject.width - correctL;
}
totalwidth /= -2;
}
//if center get next lines total width/2;
} else {
QuadObject quad = new QuadObject((totalwidth + intObject.width) * scaleX + x, startY * scaleY + y, totalwidth * scaleX + x, (startY + intObject.height) * scaleY + y, intObject.storedX + intObject.width, intObject.storedY + intObject.height, intObject.storedX, intObject.storedY, z);
list.add(quad);
if (d > 0) {
totalwidth += (intObject.width - c) * d;
}
}
i += d;
}
}
float centerX = 0;
for(QuadObject quad : list) {
centerX += quad.drawX + (quad.drawX2 - quad.drawX);
}
centerX /= 2.0f;
float centerY = 0;
for(QuadObject quad : list) {
centerY += quad.drawY + (quad.drawY2 - quad.drawY);
}
centerY /= 2.0f;
//GL11.glTranslatef( -centerX, -centerY, -z);
//GL11.glTranslatef(0, 0, -z);
GL11.glRotatef(-Test3DFont.rotation.x, 0.0f, 0.0f, 0.0f);
GL11.glRotatef(-Test3DFont.rotation.y, 0.0f, 1.0f, 0.0f);
//GL11.glRotatef(-Test3DFont.rotation.z, 0.0f, 0.0f, 1.0f);
//GL11.glTranslatef(0, 0, z);
//GL11.glTranslatef(centerX, centerY, z); // M1 - 2nd translation
GL11.glEnable(GL11.GL_TEXTURE_2D); // Enable Texture Mapping
GL11.glBindTexture(GL11.GL_TEXTURE_2D, fontTextureID);
GL11.glBegin(GL11.GL_QUADS);
for(QuadObject quad : list) {
drawQuad(quad.drawX, quad.drawY, quad.drawX2, quad.drawY2, quad.srcX, quad.srcY, quad.srcX2, quad.srcY2, quad.z);
}
GL11.glDisable(GL11.GL_TEXTURE_2D);
GL11.glEnd();
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
}
public static int loadImage(BufferedImage bufferedImage) {
try {
short width = (short) bufferedImage.getWidth();
short height = (short) bufferedImage.getHeight();
//textureLoader.bpp = bufferedImage.getColorModel().hasAlpha() ? (byte)32 : (byte)24;
int bpp = (byte) bufferedImage.getColorModel().getPixelSize();
ByteBuffer byteBuffer;
DataBuffer db = bufferedImage.getData().getDataBuffer();
if (db instanceof DataBufferInt) {
int intI[] = ((DataBufferInt) (bufferedImage.getData().getDataBuffer())).getData();
byte newI[] = new byte[intI.length * 4];
for (int i = 0; i < intI.length; i++) {
byte b[] = intToByteArray(intI[i]);
int newIndex = i * 4;
newI[newIndex] = b[1];
newI[newIndex + 1] = b[2];
newI[newIndex + 2] = b[3];
newI[newIndex + 3] = b[0];
}
byteBuffer = ByteBuffer.allocateDirect(width * height * (bpp / 8)).order(ByteOrder.nativeOrder()).put(newI);
} else {
byteBuffer = ByteBuffer.allocateDirect(width * height * (bpp / 8)).order(ByteOrder.nativeOrder()).put(((DataBufferByte) (bufferedImage.getData().getDataBuffer())).getData());
}
byteBuffer.flip();
int internalFormat = GL11.GL_RGBA8, format = GL11.GL_RGBA;
IntBuffer textureId = BufferUtils.createIntBuffer(1);
;
GL11.glGenTextures(textureId);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureId.get(0));
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL11.GL_UNSIGNED_BYTE, byteBuffer.order(ByteOrder.nativeOrder()));
return textureId.get(0);
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
return -1;
}
public static boolean isSupported(String fontname) {
Font font[] = getFonts();
for (int i = font.length - 1; i >= 0; i--) {
if (font[i].getName().equalsIgnoreCase(fontname)) {
return true;
}
}
return false;
}
public static Font[] getFonts() {
return GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
}
public static byte[] intToByteArray(int value) {
return new byte[]{(byte) (value >>> 24), (byte) (value >>> 16), (byte) (value >>> 8), (byte) value};
}
public void destroy() {
IntBuffer scratch = BufferUtils.createIntBuffer(1);
scratch.put(0, fontTextureID);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
GL11.glDeleteTextures(scratch);
}
#AllArgsConstructor
private class QuadObject {
private float drawX;
private float drawY;
private float drawX2;
private float drawY2;
private float srcX;
private float srcY;
private float srcX2;
private float srcY2;
private float z;
}
}
Test3DFont.java
package com.displee.render.font;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.GLU;
import org.lwjgl.util.vector.Vector3f;
import java.awt.*;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.util.glu.GLU.gluPerspective;
public class Test3DFont {
private static final int WIDTH = 800;
private static final int HEIGHT = 600;
private static final float FOV = 45f;
private static final float NEAR = 0.1f;
private static final float FAR = 1000f;
private static boolean mousePressed;
private static Vector3f startCoordinations = new Vector3f();
private static float scale = 0.05f;
public static Vector3f rotation = new Vector3f(0, 0, 0);
private static Vector3f startRotation = new Vector3f();
private static TrueTypeFont font;
private static boolean running = true;
public static void main(String[] args) throws Exception {
initializeDisplay();
font = new TrueTypeFont(new Font("serif", Font.PLAIN, 30), true);
initializeGL();
while(running) {
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
handleMouse();
font.drawString(0, 0, 0, "Test", 0.1f, 0.1f);
loadDefaultRotation();
setViewport();
drawGrid();
Display.sync(60);
Display.update();
if (Display.isCloseRequested()) {
break;
}
}
font.destroy();
Display.destroy();
}
private static void initializeDisplay() throws LWJGLException {
Display.setDisplayMode(new DisplayMode(WIDTH, HEIGHT));
Display.create();
setViewport();
}
public static void set2DMode() {
//GL11.glDisable(GL11.GL_DEPTH_TEST);
GL11.glMatrixMode(GL11.GL_PROJECTION); // Select The Projection Matrix
GL11.glPushMatrix(); // Store The Projection Matrix
GL11.glLoadIdentity(); // Reset The Projection Matrix
GL11.glOrtho(0, WIDTH, 0, HEIGHT, -1, 1); // Set Up An Ortho Screen
GL11.glMatrixMode(GL11.GL_MODELVIEW); // Select The Modelview Matrix
GL11.glPushMatrix(); // Store The Modelview Matrix
GL11.glLoadIdentity(); // Reset The Modelview Matrix
}
public static void set3DMode() {
GL11.glMatrixMode(GL11.GL_PROJECTION); // Select The Projection Matrix
GL11.glPopMatrix(); // Restore The Old Projection Matrix
GL11.glMatrixMode(GL11.GL_MODELVIEW); // Select The Modelview Matrix
GL11.glPopMatrix(); // Restore The Old Projection Matrix
//GL11.glEnable(GL11.GL_DEPTH_TEST);
}
private static void setViewport() {
glViewport(0, 0, WIDTH, HEIGHT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(FOV, (float) WIDTH / (float) HEIGHT, NEAR, FAR);
glMatrixMode(GL_MODELVIEW);
}
private static void initializeGL() {
glShadeModel(GL_SMOOTH);
glEnable(GL_NORMALIZE);
glEnable(GL_BLEND);
glCullFace(GL_BACK);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
private static void handleMouse() {
scale += Mouse.getDWheel() > 0 ? 0.005f : Mouse.getDWheel() < 0 ? -0.005f : 0;
int x = Mouse.getY();
int y = Mouse.getX();
if (!mousePressed) {
mousePressed = Mouse.isButtonDown(0);
if (mousePressed) {
startCoordinations.set((float) x, (float) y, 0.0f);
startRotation = new Vector3f(rotation);
}
} else if (!Mouse.isButtonDown(0)) {
mousePressed = false;
}
if (!mousePressed) {
return;
}
float differenceX = x - startCoordinations.x;
float differenceY = y - startCoordinations.y;
rotation.set(startRotation.x - (differenceX * 0.5F), startRotation.y + (differenceY * 0.5F), 0);
}
private static void loadDefaultRotation() {
glLoadIdentity();
Vector3f cameraPosition = new Vector3f();
glTranslatef(cameraPosition.x, cameraPosition.y, -10);
glRotatef(rotation.x, 1.0F, 0.0F, 0.0F);
glRotatef(rotation.y, 0.0F, 1.0F, 0.0F);
glRotatef(rotation.z, 0.0F, 0.0F, 1.0F);
glScalef(scale, scale, scale);
}
private static void drawGrid() {
glColor4f(0.7176471f, 0.7176471f, 0.7176471f, 1.0f);
glBegin(GL_LINES);
float size = 50;
float step = 10;
for (float i = -size; i <= size; i += step) {
glVertex3f(i, 0, size);
glVertex3f(i, 0, -size);
glVertex3f(size, 0, i);
glVertex3f(-size, 0, i);
}
glEnd();
}
public static int[] getScreenCoords(double x, double y, double z) {
FloatBuffer screenCoords = BufferUtils.createFloatBuffer(4);
IntBuffer viewport = BufferUtils.createIntBuffer(16);
FloatBuffer modelView = BufferUtils.createFloatBuffer(16);
FloatBuffer projection = BufferUtils.createFloatBuffer(16);
GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, modelView);
GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, projection);
GL11.glGetInteger(GL11.GL_VIEWPORT, viewport);
boolean result = GLU.gluProject((float) x, (float) y, (float) z, modelView, projection, viewport, screenCoords);
if (result) {
return new int[] { (int) screenCoords.get(0), (int) screenCoords.get(1) };
}
return null;
}
}
Can anyone help me with this? How can I make it so I always see the front of the font?
Update 1:
I've got it almost working by rotating the quads. I've added the following code in the drawString method before enabling texture 2D:
GL11.glRotatef(-Test3DFont.rotation.x, 0.0f, 0.0f, 0.0f);
GL11.glRotatef(-Test3DFont.rotation.y, 0.0f, 1.0f, 0.0f);
I've updated the code. It currently looks like this:
I finally fixed it by pushing a matrix and using the original x, y and z coords in the translation. I also had to subtract 180 from the rotation because that's my starting rotation. Ended up with the following code:
GL11.glPushMatrix();
GL11.glTranslatef(x, y, z);
GL11.glRotatef(180 - rotation.y, 0.0f, 1.0f, 0.0f);
GL11.glTranslatef(-x, -y, -z);
GL11.glEnable(GL11.GL_TEXTURE_2D); // Enable Texture Mapping
GL11.glBindTexture(GL11.GL_TEXTURE_2D, fontTextureID);
GL11.glBegin(GL11.GL_QUADS);
for(QuadObject quad : list) {
drawQuad(quad.drawX, quad.drawY, quad.drawX2, quad.drawY2, quad.srcX, quad.srcY, quad.srcX2, quad.srcY2, quad.z);
}
GL11.glDisable(GL11.GL_TEXTURE_2D);
GL11.glEnd();
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
GL11.glPopMatrix();

fillArc() in Java not blending color as expected

I'm trying to blend all the colors into a circle using arcs.
However, the arc comes as one solid color and not a blend of color as I thought.
Is it possible to?
public static void main(String[] args) {
DrawingPanel panel = new DrawingPanel(512,512);
Graphics g = panel.getGraphics();
int width = 100;
int height = 100;
g.drawOval(0,0,width, height);
//yellow
for( int i = 0; i < 100 ; i++){
Color c = new Color(255/100*i,255,0);
g.setColor(c);
g.fillArc(0,0,width,height,95,11);
}
g.fillArc(0,0,width,height,95,11);
You need to change the arc angle for every iteration and the arc size should be fixed at a certain value. I'm not sure what the value would be because I would expect you should iterate 360 times (in which case the size would be 1), not 100.
You can use the HSL Color class to do this simply.
An HSL color allows you to change the "hue" of the color in degrees. So you just need a simple loop to set/paint the color in a 1 degree arc:
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import javax.swing.*;
public class ColorWheel extends JPanel
{
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
HSLColor color = new HSLColor( Color.YELLOW );
for (int i = 0; i < 360; i++)
{
g.setColor( color.adjustHue(i) );
g.fillArc( 25, 25, 200, 200, i, 1);
}
}
#Override
public Dimension getPreferredSize()
{
return new Dimension(250, 250);
}
private static void createAndShowGUI()
{
JComponent wheel = new ColorWheel();
JFrame frame = new JFrame("Color Wheel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane( wheel );
frame.setLocationByPlatform( true );
frame.pack();
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
I used the HSL Color because it has a simple API to change the hue.
If you don't want to use that class then you can use Color.getHSBColor(...) method to get the color for each degree of change. Again the saturation and brightness would be fixed values and then you just change the hue.
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
float hueDegree = 1 / 360.0f;
for (int i = 0; i < 360; i++)
{
Color color = Color.getHSBColor(i * hueDegree, 1.0f, 1.0f);
g.setColor( color );
g.fillArc( 25, 25, 200, 200, i, 1);
}
}
Along time ago, I stumbled across a ConicalGradientPaint from Harmonic Code (there source is there somewhere, I just can't seem to find it again, but I've included my copy of it in the example).
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ColorWheel {
public static void main(String[] args) {
new ColorWheel();
}
public ColorWheel() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
ConicalGradientPaint rgp = new ConicalGradientPaint(
true,
new Point(getWidth() / 2, getHeight() / 2),
0.5f,
new float[]{0, 60, 120, 180, 240, 300, 360},
new Color[]{Color.RED, Color.YELLOW, Color.GREEN, Color.CYAN, Color.BLUE, Color.MAGENTA, Color.RED});
g2d.setPaint(rgp);
int radius = Math.min(getWidth(), getHeight());
int x = (getWidth() - radius) / 2;
int y = (getHeight() - radius) / 2;
g2d.fillOval(x, y, radius, radius);
g2d.dispose();
}
}
public final class ConicalGradientPaint implements java.awt.Paint {
private final java.awt.geom.Point2D CENTER;
private final double[] FRACTION_ANGLES;
private final double[] RED_STEP_LOOKUP;
private final double[] GREEN_STEP_LOOKUP;
private final double[] BLUE_STEP_LOOKUP;
private final double[] ALPHA_STEP_LOOKUP;
private final java.awt.Color[] COLORS;
private static final float INT_TO_FLOAT_CONST = 1f / 255f;
/**
* Standard constructor which takes the FRACTIONS in values from 0.0f to
* 1.0f
*
* #param CENTER
* #param GIVEN_FRACTIONS
* #param GIVEN_COLORS
* #throws IllegalArgumentException
*/
public ConicalGradientPaint(final java.awt.geom.Point2D CENTER, final float[] GIVEN_FRACTIONS, final java.awt.Color[] GIVEN_COLORS) throws IllegalArgumentException {
this(false, CENTER, 0.0f, GIVEN_FRACTIONS, GIVEN_COLORS);
}
/**
* Enhanced constructor which takes the FRACTIONS in degress from 0.0f to
* 360.0f and also an GIVEN_OFFSET in degrees around the rotation CENTER
*
* #param USE_DEGREES
* #param CENTER
* #param GIVEN_OFFSET
* #param GIVEN_FRACTIONS
* #param GIVEN_COLORS
* #throws IllegalArgumentException
*/
public ConicalGradientPaint(final boolean USE_DEGREES, final java.awt.geom.Point2D CENTER, final float GIVEN_OFFSET, final float[] GIVEN_FRACTIONS, final java.awt.Color[] GIVEN_COLORS) throws IllegalArgumentException {
// Check that fractions and colors are of the same size
if (GIVEN_FRACTIONS.length != GIVEN_COLORS.length) {
throw new IllegalArgumentException("Fractions and colors must be equal in size");
}
final java.util.ArrayList<Float> FRACTION_LIST = new java.util.ArrayList<Float>(GIVEN_FRACTIONS.length);
final float OFFSET;
if (USE_DEGREES) {
final double DEG_FRACTION = 1f / 360f;
if (Double.compare((GIVEN_OFFSET * DEG_FRACTION), -0.5) == 0) {
OFFSET = -0.5f;
} else if (Double.compare((GIVEN_OFFSET * DEG_FRACTION), 0.5) == 0) {
OFFSET = 0.5f;
} else {
OFFSET = (float) (GIVEN_OFFSET * DEG_FRACTION);
}
for (float fraction : GIVEN_FRACTIONS) {
FRACTION_LIST.add((float) (fraction * DEG_FRACTION));
}
} else {
// Now it seems to work with rotation of 0.5f, below is the old code to correct the problem
// if (GIVEN_OFFSET == -0.5)
// {
// // This is needed because of problems in the creation of the Raster
// // with a angle offset of exactly -0.5
// OFFSET = -0.49999f;
// }
// else if (GIVEN_OFFSET == 0.5)
// {
// // This is needed because of problems in the creation of the Raster
// // with a angle offset of exactly +0.5
// OFFSET = 0.499999f;
// }
// else
{
OFFSET = GIVEN_OFFSET;
}
for (float fraction : GIVEN_FRACTIONS) {
FRACTION_LIST.add(fraction);
}
}
// Check for valid offset
if (OFFSET > 0.5f || OFFSET < -0.5f) {
throw new IllegalArgumentException("Offset has to be in the range of -0.5 to 0.5");
}
// Adjust fractions and colors array in the case where startvalue != 0.0f and/or endvalue != 1.0f
final java.util.List<java.awt.Color> COLOR_LIST = new java.util.ArrayList<java.awt.Color>(GIVEN_COLORS.length);
COLOR_LIST.addAll(java.util.Arrays.asList(GIVEN_COLORS));
// Assure that fractions start with 0.0f
if (FRACTION_LIST.get(0) != 0.0f) {
FRACTION_LIST.add(0, 0.0f);
final java.awt.Color TMP_COLOR = COLOR_LIST.get(0);
COLOR_LIST.add(0, TMP_COLOR);
}
// Assure that fractions end with 1.0f
if (FRACTION_LIST.get(FRACTION_LIST.size() - 1) != 1.0f) {
FRACTION_LIST.add(1.0f);
COLOR_LIST.add(GIVEN_COLORS[0]);
}
// Recalculate the fractions and colors with the given offset
final java.util.Map<Float, java.awt.Color> FRACTION_COLORS = recalculate(FRACTION_LIST, COLOR_LIST, OFFSET);
// Clear the original FRACTION_LIST and COLOR_LIST
FRACTION_LIST.clear();
COLOR_LIST.clear();
// Sort the hashmap by fraction and add the values to the FRACION_LIST and COLOR_LIST
final java.util.SortedSet<Float> SORTED_FRACTIONS = new java.util.TreeSet<Float>(FRACTION_COLORS.keySet());
final java.util.Iterator<Float> ITERATOR = SORTED_FRACTIONS.iterator();
while (ITERATOR.hasNext()) {
final float CURRENT_FRACTION = ITERATOR.next();
FRACTION_LIST.add(CURRENT_FRACTION);
COLOR_LIST.add(FRACTION_COLORS.get(CURRENT_FRACTION));
}
// Set the values
this.CENTER = CENTER;
COLORS = COLOR_LIST.toArray(new java.awt.Color[]{});
// Prepare lookup table for the angles of each fraction
final int MAX_FRACTIONS = FRACTION_LIST.size();
this.FRACTION_ANGLES = new double[MAX_FRACTIONS];
for (int i = 0; i < MAX_FRACTIONS; i++) {
FRACTION_ANGLES[i] = FRACTION_LIST.get(i) * 360;
}
// Prepare lookup tables for the color stepsize of each color
RED_STEP_LOOKUP = new double[COLORS.length];
GREEN_STEP_LOOKUP = new double[COLORS.length];
BLUE_STEP_LOOKUP = new double[COLORS.length];
ALPHA_STEP_LOOKUP = new double[COLORS.length];
for (int i = 0; i < (COLORS.length - 1); i++) {
RED_STEP_LOOKUP[i] = ((COLORS[i + 1].getRed() - COLORS[i].getRed()) * INT_TO_FLOAT_CONST) / (FRACTION_ANGLES[i + 1] - FRACTION_ANGLES[i]);
GREEN_STEP_LOOKUP[i] = ((COLORS[i + 1].getGreen() - COLORS[i].getGreen()) * INT_TO_FLOAT_CONST) / (FRACTION_ANGLES[i + 1] - FRACTION_ANGLES[i]);
BLUE_STEP_LOOKUP[i] = ((COLORS[i + 1].getBlue() - COLORS[i].getBlue()) * INT_TO_FLOAT_CONST) / (FRACTION_ANGLES[i + 1] - FRACTION_ANGLES[i]);
ALPHA_STEP_LOOKUP[i] = ((COLORS[i + 1].getAlpha() - COLORS[i].getAlpha()) * INT_TO_FLOAT_CONST) / (FRACTION_ANGLES[i + 1] - FRACTION_ANGLES[i]);
}
}
/**
* Recalculates the fractions in the FRACTION_LIST and their associated
* colors in the COLOR_LIST with a given OFFSET. Because the conical
* gradients always starts with 0 at the top and clockwise direction you
* could rotate the defined conical gradient from -180 to 180 degrees which
* equals values from -0.5 to +0.5
*
* #param FRACTION_LIST
* #param COLOR_LIST
* #param OFFSET
* #return Hashmap that contains the recalculated fractions and colors after
* a given rotation
*/
private java.util.HashMap<Float, java.awt.Color> recalculate(final java.util.List<Float> FRACTION_LIST, final java.util.List<java.awt.Color> COLOR_LIST, final float OFFSET) {
// Recalculate the fractions and colors with the given offset
final int MAX_FRACTIONS = FRACTION_LIST.size();
final java.util.HashMap<Float, java.awt.Color> FRACTION_COLORS = new java.util.HashMap<Float, java.awt.Color>(MAX_FRACTIONS);
for (int i = 0; i < MAX_FRACTIONS; i++) {
// Add offset to fraction
final float TMP_FRACTION = FRACTION_LIST.get(i) + OFFSET;
// Color related to current fraction
final java.awt.Color TMP_COLOR = COLOR_LIST.get(i);
// Check each fraction for limits (0...1)
if (TMP_FRACTION <= 0) {
FRACTION_COLORS.put(1.0f + TMP_FRACTION + 0.0001f, TMP_COLOR);
final float NEXT_FRACTION;
final java.awt.Color NEXT_COLOR;
if (i < MAX_FRACTIONS - 1) {
NEXT_FRACTION = FRACTION_LIST.get(i + 1) + OFFSET;
NEXT_COLOR = COLOR_LIST.get(i + 1);
} else {
NEXT_FRACTION = 1 - FRACTION_LIST.get(0) + OFFSET;
NEXT_COLOR = COLOR_LIST.get(0);
}
if (NEXT_FRACTION > 0) {
final java.awt.Color NEW_FRACTION_COLOR = getColorFromFraction(TMP_COLOR, NEXT_COLOR, (int) ((NEXT_FRACTION - TMP_FRACTION) * 10000), (int) ((-TMP_FRACTION) * 10000));
FRACTION_COLORS.put(0.0f, NEW_FRACTION_COLOR);
FRACTION_COLORS.put(1.0f, NEW_FRACTION_COLOR);
}
} else if (TMP_FRACTION >= 1) {
FRACTION_COLORS.put(TMP_FRACTION - 1.0f - 0.0001f, TMP_COLOR);
final float PREVIOUS_FRACTION;
final java.awt.Color PREVIOUS_COLOR;
if (i > 0) {
PREVIOUS_FRACTION = FRACTION_LIST.get(i - 1) + OFFSET;
PREVIOUS_COLOR = COLOR_LIST.get(i - 1);
} else {
PREVIOUS_FRACTION = FRACTION_LIST.get(MAX_FRACTIONS - 1) + OFFSET;
PREVIOUS_COLOR = COLOR_LIST.get(MAX_FRACTIONS - 1);
}
if (PREVIOUS_FRACTION < 1) {
final java.awt.Color NEW_FRACTION_COLOR = getColorFromFraction(TMP_COLOR, PREVIOUS_COLOR, (int) ((TMP_FRACTION - PREVIOUS_FRACTION) * 10000), (int) (TMP_FRACTION - 1.0f) * 10000);
FRACTION_COLORS.put(1.0f, NEW_FRACTION_COLOR);
FRACTION_COLORS.put(0.0f, NEW_FRACTION_COLOR);
}
} else {
FRACTION_COLORS.put(TMP_FRACTION, TMP_COLOR);
}
}
// Clear the original FRACTION_LIST and COLOR_LIST
FRACTION_LIST.clear();
COLOR_LIST.clear();
return FRACTION_COLORS;
}
/**
* With the START_COLOR at the beginning and the DESTINATION_COLOR at the
* end of the given RANGE the method will calculate and return the color
* that equals the given VALUE. e.g. a START_COLOR of BLACK (R:0, G:0, B:0,
* A:255) and a DESTINATION_COLOR of WHITE(R:255, G:255, B:255, A:255) with
* a given RANGE of 100 and a given VALUE of 50 will return the color that
* is exactly in the middle of the gradient between black and white which is
* gray(R:128, G:128, B:128, A:255) So this method is really useful to
* calculate colors in gradients between two given colors.
*
* #param START_COLOR
* #param DESTINATION_COLOR
* #param RANGE
* #param VALUE
* #return Color calculated from a range of values by given value
*/
public java.awt.Color getColorFromFraction(final java.awt.Color START_COLOR, final java.awt.Color DESTINATION_COLOR, final int RANGE, final int VALUE) {
final float SOURCE_RED = START_COLOR.getRed() * INT_TO_FLOAT_CONST;
final float SOURCE_GREEN = START_COLOR.getGreen() * INT_TO_FLOAT_CONST;
final float SOURCE_BLUE = START_COLOR.getBlue() * INT_TO_FLOAT_CONST;
final float SOURCE_ALPHA = START_COLOR.getAlpha() * INT_TO_FLOAT_CONST;
final float DESTINATION_RED = DESTINATION_COLOR.getRed() * INT_TO_FLOAT_CONST;
final float DESTINATION_GREEN = DESTINATION_COLOR.getGreen() * INT_TO_FLOAT_CONST;
final float DESTINATION_BLUE = DESTINATION_COLOR.getBlue() * INT_TO_FLOAT_CONST;
final float DESTINATION_ALPHA = DESTINATION_COLOR.getAlpha() * INT_TO_FLOAT_CONST;
final float RED_DELTA = DESTINATION_RED - SOURCE_RED;
final float GREEN_DELTA = DESTINATION_GREEN - SOURCE_GREEN;
final float BLUE_DELTA = DESTINATION_BLUE - SOURCE_BLUE;
final float ALPHA_DELTA = DESTINATION_ALPHA - SOURCE_ALPHA;
final float RED_FRACTION = RED_DELTA / RANGE;
final float GREEN_FRACTION = GREEN_DELTA / RANGE;
final float BLUE_FRACTION = BLUE_DELTA / RANGE;
final float ALPHA_FRACTION = ALPHA_DELTA / RANGE;
//System.out.println(DISTANCE + " " + CURRENT_FRACTION);
return new java.awt.Color(SOURCE_RED + RED_FRACTION * VALUE, SOURCE_GREEN + GREEN_FRACTION * VALUE, SOURCE_BLUE + BLUE_FRACTION * VALUE, SOURCE_ALPHA + ALPHA_FRACTION * VALUE);
}
#Override
public java.awt.PaintContext createContext(final java.awt.image.ColorModel COLOR_MODEL, final java.awt.Rectangle DEVICE_BOUNDS, final java.awt.geom.Rectangle2D USER_BOUNDS, final java.awt.geom.AffineTransform TRANSFORM, final java.awt.RenderingHints HINTS) {
final java.awt.geom.Point2D TRANSFORMED_CENTER = TRANSFORM.transform(CENTER, null);
return new ConicalGradientPaintContext(TRANSFORMED_CENTER);
}
#Override
public int getTransparency() {
return java.awt.Transparency.TRANSLUCENT;
}
private final class ConicalGradientPaintContext implements java.awt.PaintContext {
final private java.awt.geom.Point2D CENTER;
public ConicalGradientPaintContext(final java.awt.geom.Point2D CENTER) {
this.CENTER = new java.awt.geom.Point2D.Double(CENTER.getX(), CENTER.getY());
}
#Override
public void dispose() {
}
#Override
public java.awt.image.ColorModel getColorModel() {
return java.awt.image.ColorModel.getRGBdefault();
}
#Override
public java.awt.image.Raster getRaster(final int X, final int Y, final int TILE_WIDTH, final int TILE_HEIGHT) {
final double ROTATION_CENTER_X = -X + CENTER.getX();
final double ROTATION_CENTER_Y = -Y + CENTER.getY();
final int MAX = FRACTION_ANGLES.length;
// Create raster for given colormodel
final java.awt.image.WritableRaster RASTER = getColorModel().createCompatibleWritableRaster(TILE_WIDTH, TILE_HEIGHT);
// Create data array with place for red, green, blue and alpha values
int[] data = new int[(TILE_WIDTH * TILE_HEIGHT * 4)];
double dx;
double dy;
double distance;
double angle;
double currentRed = 0;
double currentGreen = 0;
double currentBlue = 0;
double currentAlpha = 0;
for (int py = 0; py < TILE_HEIGHT; py++) {
for (int px = 0; px < TILE_WIDTH; px++) {
// Calculate the distance between the current position and the rotation angle
dx = px - ROTATION_CENTER_X;
dy = py - ROTATION_CENTER_Y;
distance = Math.sqrt(dx * dx + dy * dy);
// Avoid division by zero
if (distance == 0) {
distance = 1;
}
// 0 degree on top
angle = Math.abs(Math.toDegrees(Math.acos(dx / distance)));
if (dx >= 0 && dy <= 0) {
angle = 90.0 - angle;
} else if (dx >= 0 && dy >= 0) {
angle += 90.0;
} else if (dx <= 0 && dy >= 0) {
angle += 90.0;
} else if (dx <= 0 && dy <= 0) {
angle = 450.0 - angle;
}
// Check for each angle in fractionAngles array
for (int i = 0; i < (MAX - 1); i++) {
if ((angle >= FRACTION_ANGLES[i])) {
currentRed = COLORS[i].getRed() * INT_TO_FLOAT_CONST + (angle - FRACTION_ANGLES[i]) * RED_STEP_LOOKUP[i];
currentGreen = COLORS[i].getGreen() * INT_TO_FLOAT_CONST + (angle - FRACTION_ANGLES[i]) * GREEN_STEP_LOOKUP[i];
currentBlue = COLORS[i].getBlue() * INT_TO_FLOAT_CONST + (angle - FRACTION_ANGLES[i]) * BLUE_STEP_LOOKUP[i];
currentAlpha = COLORS[i].getAlpha() * INT_TO_FLOAT_CONST + (angle - FRACTION_ANGLES[i]) * ALPHA_STEP_LOOKUP[i];
continue;
}
}
// Fill data array with calculated color values
final int BASE = (py * TILE_WIDTH + px) * 4;
data[BASE + 0] = (int) (currentRed * 255);
data[BASE + 1] = (int) (currentGreen * 255);
data[BASE + 2] = (int) (currentBlue * 255);
data[BASE + 3] = (int) (currentAlpha * 255);
}
}
// Fill the raster with the data
RASTER.setPixels(0, 0, TILE_WIDTH, TILE_HEIGHT, data);
return RASTER;
}
}
}
}
To try and do what you're doing with a pie slices, you need to slow alter the color from one point to another, using very small slices. So, while you might start with Color.RED and want to blend to Color.YELLOW, you would actually need to generate each color between them (based on the distance)
For example...
import core.ui.ColorUtilities;
import static core.ui.ColorUtilities.blend;
import static core.ui.ColorUtilities.getFractionIndicies;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ColorWheel {
public static void main(String[] args) {
new ColorWheel();
}
public ColorWheel() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
float fractions[] = new float[]{0, 0.16f, 0.33f, 0.5f, 0.66f, 0.83f, 1f};
Color colors[] = new Color[]{Color.RED, Color.YELLOW, Color.GREEN, Color.CYAN, Color.BLUE, Color.MAGENTA, Color.RED};
for (int index = 0; index < getWidth(); index++) {
float progress = (float) index / getWidth();
System.out.println(progress);
Color color = blendColors(fractions, colors, progress);
g2d.setColor(color);
g2d.drawLine(index, 0, index, getHeight());
}
g2d.dispose();
}
}
/**
* This will attempt to blend two colors that site between the supplied
* progress value, based on the distance of the progress value...
*
* For example, if you have a series of fractions of {0f, 0.5f, 1f} and a
* progress of 25%, the resulting color will be a 50% blend of the first and
* second color (as the progress is half between those two points)
*
* #param fractions
* #param colors
* #param progress
* #return
*/
public static Color blendColors(float[] fractions, Color[] colors, float progress) {
Color color = null;
if (fractions != null) {
if (colors != null) {
if (fractions.length == colors.length) {
int[] indicies = getFractionIndicies(fractions, progress);
float[] range = new float[]{fractions[indicies[0]], fractions[indicies[1]]};
Color[] colorRange = new Color[]{colors[indicies[0]], colors[indicies[1]]};
float max = range[1] - range[0];
float value = progress - range[0];
float weight = value / max;
color = blend(colorRange[0], colorRange[1], 1f - weight);
} else {
throw new IllegalArgumentException("Fractions and colours must have equal number of elements");
}
} else {
throw new IllegalArgumentException("Colours can't be null");
}
} else {
throw new IllegalArgumentException("Fractions can't be null");
}
return color;
}
}
I know, it's note a wheel, but i was demonstrating the blending algorithm, I'll leave the pie slicing up to you ;)
Just proving to myself it could work...
int dimeter = Math.min(getWidth(), getHeight());
int x = (getWidth() - dimeter) / 2;
int y = (getHeight() - dimeter) / 2;
for (int angle = 0; angle < 360; angle++) {
float progress = (float) angle / 360;
System.out.println(progress);
Color color = blendColors(fractions, colors, progress);
g2d.setColor(color);
g2d.fillArc(x, y, dimeter, dimeter, angle + 90, 2);
}

How to draw hexagons in android?

I used the hexagon code in this tutorial and created a createHex class (Should I post the code?). The linked web page has used the following code to actually draw the hexagons using the math in createHex:
#Override
public void paint(Graphics g){
for(int j = 0; int j < BOARD_HEIGHT; j++){
for(int i = 0; i < BOARD_HEIGHT; I++){
mCellMetrics.setCellIndex(i, j);
if(mCells[j][i] != 0){
mCellMetrics.computeCorners(mCornersX, mCornersY);
g.setColor((mCells[j][i] == L_ON) ? COLOR.ORANGE):COLOR.GRAY;
g.fillPolygon(mCornersX, mCornersY, NUM_HEX_CORNERS);
g.setColor(COLOR.BLACK)
g.drawPolygon(mCornersX, mCornersY, NUM_HEX_CORNERS);
}
}
}
}
The problem I encountered is that Android does not have a Graphics class that contains all the required methods. I did about an hour and a half of fishing around the android documentation and the closest thing I found was the Path class but it doesn't have the methods I need. I want to use the hexagon code in the linked article at the top but I can't find the equivalent of the graphics class. If there isn't an equivalent, can someone show me how to get the results I want using the linked code?
My question: How can I port the code in the linked article to android?
EDIT:
I decided that it might be helpful to others (and potential answerers) if I included the hexagon code that calculates the sides and whatnot of a hexagon, so here is createHex.java:
package com.rush;
/**
* Uniform hexagonal grid cell's metrics utility class.
*/
public class HexGridCell {
private static final int[] NEIGHBORS_DI = { 0, 1, 1, 0, -1, -1 };
private static final int[][] NEIGHBORS_DJ = {
{ -1, -1, 0, 1, 0, -1 }, { -1, 0, 1, 1, 1, 0 } };
private final int[] CORNERS_DX; // array of horizontal offsets of the cell's corners
private final int[] CORNERS_DY; // array of
vertical offsets of the cell's corners
private final int SIDE;
private int mX = 0; // cell's left coordinate
private int mY = 0; // cell's top coordinate
private int mI = 0; // cell's horizontal grid coordinate
private int mJ = 0; // cell's vertical grid coordinate
/**
* Cell radius (distance from center to one of the corners)
*/
public final int RADIUS;
/**
* Cell height
*/
public final int HEIGHT;
/**
* Cell width
*/
public final int WIDTH;
public static final int NUM_NEIGHBORS = 6;
/**
* #param radius Cell radius (distance from the center to one of the corners)
*/
public HexGridCell(int radius) {
RADIUS = radius;
WIDTH = radius * 2;
HEIGHT = (int) (((float) radius) * Math.sqrt(3));
SIDE = radius * 3 / 2;
int cdx[] = { RADIUS / 2, SIDE, WIDTH, SIDE, RADIUS / 2, 0 };
CORNERS_DX = cdx;
int cdy[] = { 0, 0, HEIGHT / 2, HEIGHT, HEIGHT, HEIGHT / 2 };
CORNERS_DY = cdy;
}
/**
* #return X coordinate of the cell's top left corner.
*/
public int getLeft() {
return mX;
}
/**
* #return Y coordinate of the cell's top left corner.
*/
public int getTop() {
return mY;
}
/**
* #return X coordinate of the cell's center
*/
public int getCenterX() {
return mX + RADIUS;
}
/**
* #return Y coordinate of the cell's center
*/
public int getCenterY() {
return mY + HEIGHT / 2;
}
/**
* #return Horizontal grid coordinate for the cell.
*/
public int getIndexI() {
return mI;
}
/**
* #return Vertical grid coordinate for the cell.
*/
public int getIndexJ() {
return mJ;
}
/**
* #return Horizontal grid coordinate for the given neighbor.
*/
public int getNeighborI(int neighborIdx) {
return mI + NEIGHBORS_DI[neighborIdx];
}
/**
* #return Vertical grid coordinate for the given neighbor.
*/
public int getNeighborJ(int neighborIdx) {
return mJ + NEIGHBORS_DJ[mI % 2][neighborIdx];
}
/**
* Computes X and Y coordinates for all of the cell's 6 corners, clockwise,
* starting from the top left.
*
* #param cornersX Array to fill in with X coordinates of the cell's corners
* #param cornersX Array to fill in with Y coordinates of the cell's corners
*/
public void computeCorners(int[] cornersX, int[] cornersY) {
for (int k = 0; k < NUM_NEIGHBORS; k++) {
cornersX[k] = mX + CORNERS_DX[k];
cornersY[k] = mY + CORNERS_DY[k];
}
}
/**
* Sets the cell's horizontal and vertical grid coordinates.
*/
public void setCellIndex(int i, int j) {
mI = i;
mJ = j;
mX = i * SIDE;
mY = HEIGHT * (2 * j + (i % 2)) / 2;
}
/**
* Sets the cell as corresponding to some point inside it (can be used for
* e.g. mouse picking).
*/
public void setCellByPoint(int x, int y) {
int ci = (int)Math.floor((float)x/(float)SIDE);
int cx = x - SIDE*ci;
int ty = y - (ci % 2) * HEIGHT / 2;
int cj = (int)Math.floor((float)ty/(float)HEIGHT);
int cy = ty - HEIGHT*cj;
if (cx > Math.abs(RADIUS / 2 - RADIUS * cy / HEIGHT)) {
setCellIndex(ci, cj);
} else {
setCellIndex(ci - 1, cj + (ci % 2) - ((cy < HEIGHT / 2) ? 1 :
0));
}
}
}
For an explanation of how the code works please refer to the linked article.
Try this:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Path;
import android.graphics.Region;
import android.util.AttributeSet;
import android.view.View;
public class HexagonMaskView extends View {
private Path hexagonPath;
private Path hexagonBorderPath;
private float radius;
private float width, height;
private int maskColor;
public HexagonMaskView(Context context) {
super(context);
init();
}
public HexagonMaskView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public HexagonMaskView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
hexagonPath = new Path();
hexagonBorderPath = new Path();
maskColor = 0xFF01FF77;
}
public void setRadius(float r) {
this.radius = r;
calculatePath();
}
public void setMaskColor(int color) {
this.maskColor = color;
invalidate();
}
private void calculatePath() {
float triangleHeight = (float) (Math.sqrt(3) * radius / 2);
float centerX = width/2;
float centerY = height/2;
hexagonPath.moveTo(centerX, centerY + radius);
hexagonPath.lineTo(centerX - triangleHeight, centerY + radius/2);
hexagonPath.lineTo(centerX - triangleHeight, centerY - radius/2);
hexagonPath.lineTo(centerX, centerY - radius);
hexagonPath.lineTo(centerX + triangleHeight, centerY - radius/2);
hexagonPath.lineTo(centerX + triangleHeight, centerY + radius/2);
hexagonPath.moveTo(centerX, centerY + radius);
float radiusBorder = radius - 5;
float triangleBorderHeight = (float) (Math.sqrt(3) * radiusBorder / 2);
hexagonBorderPath.moveTo(centerX, centerY + radiusBorder);
hexagonBorderPath.lineTo(centerX - triangleBorderHeight, centerY + radiusBorder/2);
hexagonBorderPath.lineTo(centerX - triangleBorderHeight, centerY - radiusBorder/2);
hexagonBorderPath.lineTo(centerX, centerY - radiusBorder);
hexagonBorderPath.lineTo(centerX + triangleBorderHeight, centerY - radiusBorder/2);
hexagonBorderPath.lineTo(centerX + triangleBorderHeight, centerY + radiusBorder/2);
hexagonBorderPath.moveTo(centerX, centerY + radiusBorder);
invalidate();
}
#Override
public void onDraw(Canvas c){
super.onDraw(c);
c.clipPath(hexagonBorderPath, Region.Op.DIFFERENCE);
c.drawColor(Color.WHITE);
c.save();
c.clipPath(hexagonPath, Region.Op.DIFFERENCE);
c.drawColor(maskColor);
c.save();
}
// getting the view size and default radius
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = MeasureSpec.getSize(widthMeasureSpec);
height = MeasureSpec.getSize(heightMeasureSpec);
radius = height / 2 - 10;
calculatePath();
}
}
Try this or download demo example.
public static Bitmap getHexagonalCroppedBitmap(Bitmap bitmap, int radius) {
Bitmap finalBitmap;
if (bitmap.getWidth() != radius || bitmap.getHeight() != radius)
finalBitmap = Bitmap.createScaledBitmap(bitmap, radius, radius,
false);
else
finalBitmap = bitmap;
Bitmap output = Bitmap.createBitmap(finalBitmap.getWidth(),
finalBitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
Paint paint = new Paint();
final Rect rect = new Rect(0, 0, finalBitmap.getWidth(),
finalBitmap.getHeight());
Point point1_draw = new Point(75, 0);
Point point2_draw = new Point(0, 50);
Point point3_draw = new Point(0, 100);
Point point4_draw = new Point(75, 150);
Point point5_draw = new Point(150, 100);
Point point6_draw = new Point(150, 50);
Path path = new Path();
path.moveTo(point1_draw.x, point1_draw.y);
path.lineTo(point2_draw.x, point2_draw.y);
path.lineTo(point3_draw.x, point3_draw.y);
path.lineTo(point4_draw.x, point4_draw.y);
path.lineTo(point5_draw.x, point5_draw.y);
path.lineTo(point6_draw.x, point6_draw.y);
path.close();
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(Color.parseColor("#BAB399"));
canvas.drawPath(path, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(finalBitmap, rect, rect, paint);
return output;
}
pass your image to be cropped to hexagon as bitmap to this function
Bitmap myhexagon = getHexagonalCroppedBitmap(Myimage, yourHexagonalRadius);

Reduce lag in JFrame Painting

I am trying to paint a cube on a JFrame.
Sounds simple, but lags a lot. The 7th and 8th lines usually flash pretty bad.
here is the code:
http://pastebin.com/ncDasST6
if someone can give me a hint or two on how to stop this lag from occurring, that would be great :D.
Originally was for Applet, but i wanted it to execute through a .jar file.
Also, any way to add an Applet to a JFrame?
I tried doing: add(new Rotational()); //name of JApplet it is based off of.
Thanks, Fire
Does this variant work to your expectation? There are a number of changes which I did not bother to document (as I was 'just playing' with the code). Do a diff. to reveal the extent and nature of the changes.
It shows no lag or rendering artifacts here at 700x700.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.GroupLayout.Alignment;
import javax.swing.border.EmptyBorder;
public class Square extends JPanel implements MouseListener,
MouseMotionListener {
private static final long serialVersionUID = 1L;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
JFrame f = new JFrame("Cube Rotational");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Square square = new Square();
square.setBorder(new EmptyBorder(5,5,5,5));
f.setContentPane(square);
f.pack();
f.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public Square() {
init();
setPreferredSize(new Dimension(700,700));
}
class Point3D {
public int x, y, z;
public Point3D(int X, int Y, int Z) {
x = X;
y = Y;
z = Z;
}
}
class Edge {
public int a, b;
public Edge(int A, int B) {
a = A;
b = B;
}
}
static int width, height;
static int mx, my;
static int azimuth = 45, elevation = 45;
static Point3D[] vertices;
static Edge[] edges;
public void init() {
width = 500;
height = 500;
vertices = new Point3D[8];
vertices[0] = new Point3D(-1, -1, -1);
vertices[1] = new Point3D(-1, -1, 1);
vertices[2] = new Point3D(-1, 1, -1);
vertices[3] = new Point3D(-1, 1, 1);
vertices[4] = new Point3D(1, 1, -1);
vertices[5] = new Point3D(1, 1, 1);
vertices[6] = new Point3D(1, -1, -1);
vertices[7] = new Point3D(1, -1, 1);
edges = new Edge[12];
edges[0] = new Edge(0, 1);
edges[1] = new Edge(0, 2);
edges[2] = new Edge(0, 6);
edges[3] = new Edge(1, 3);
edges[4] = new Edge(1, 7);
edges[5] = new Edge(2, 3);
edges[6] = new Edge(2, 4);
edges[7] = new Edge(3, 5);
edges[8] = new Edge(4, 5);
edges[9] = new Edge(4, 6);
edges[10] = new Edge(5, 7);
edges[11] = new Edge(6, 7);
setCursor(new Cursor(Cursor.HAND_CURSOR));
addMouseListener(this);
addMouseMotionListener(this);
setVisible(true);
}
void drawWireframe(Graphics g) {
double theta = Math.PI * azimuth / 180.0;
double phi = Math.PI * elevation / 180.0;
float cosT = (float) Math.cos(theta);
float sinT = (float) Math.sin(theta);
float cosP = (float) Math.cos(phi);
float sinP = (float) Math.sin(phi);
float cosTcosP = cosT * cosP;
float cosTsinP = cosT * sinP;
float sinTcosP = sinT * cosP;
float sinTsinP = sinT * sinP;
Point[] points;
points = new Point[vertices.length];
float scaleFactor = (getWidth() + getHeight()) / 8;
float near = (float) 6;
float nearToObj = 1.5f;
for (int j = 0; j < vertices.length; ++j) {
int x0 = vertices[j].x;
int y0 = vertices[j].y;
int z0 = vertices[j].z;
float x1 = cosT * x0 + sinT * z0;
float y1 = -sinTsinP * x0 + cosP * y0 + cosTsinP * z0;
float z1 = cosTcosP * z0 - sinTcosP * x0 - sinP * y0;
x1 = x1 * near / (z1 + near + nearToObj);
y1 = y1 * near / (z1 + near + nearToObj);
points[j] = new Point(
(int) (getWidth() / 2 + scaleFactor * x1 + 0.5),
(int) (getHeight() / 2 - scaleFactor * y1 + 0.5));
}
g.setColor(Color.black);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.white);
for (int j = 0; j < edges.length; ++j) {
int x1 = points[edges[j].a].x;
int x2 = points[edges[j].b].x;
int y1 = points[edges[j].a].y;
int y2 = points[edges[j].b].y;
((Graphics2D) g).setStroke(new BasicStroke(5));
g.drawLine(x1, y1, x2, y2);
}
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mouseClicked(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
mx = e.getX();
my = e.getY();
e.consume();
}
public void mouseReleased(MouseEvent e) {
}
public void mouseMoved(MouseEvent e) {
}
public void mouseDragged(MouseEvent e) {
int new_mx = e.getX();
int new_my = e.getY();
azimuth -= new_mx - mx;
azimuth %= 360;
elevation += new_my - my;
elevation %= 360;
repaint();
mx = new_mx;
my = new_my;
repaint();
e.consume();
}
#Override
public void paintComponent(Graphics g) {
drawWireframe(g);
}
}
Originally was for Applet, but i wanted it to execute through a .jar file.
Good idea converting an applet to something more sensible, but note that an applet can (and usually should) be packed into a Jar.
Also, any way to add an Applet to a JFrame?
This is possible, relatively easy with this code (barring mixing Swing (JFrame) & AWT (Applet) components), but not the best way to go. It is better to create a hybrid like (for example) the subway applet/application.
By moving the custom rendering from the frame to a JPanel, the code has been partially transformed into a hybrid, since the panel can be added to a frame or applet (or window or dialog, or another panel or..).

Categories

Resources