mirror of https://github.com/rusefi/jzy3d-api.git
Allow rotating text with native painter
This commit is contained in:
parent
80a3e89cf6
commit
aeabc19238
|
@ -7,6 +7,9 @@ import com.diogonunes.jcolor.Attribute;
|
|||
/**
|
||||
* A console output helper able to add coloring in console.
|
||||
*
|
||||
* It require the console to have an ANSI interpreter, e.g. Eclipse needs something like
|
||||
* <a href="https://marketplace.eclipse.org/content/ansi-escape-console">this</a>.
|
||||
*
|
||||
* @author Martin Pernollet
|
||||
*
|
||||
*/
|
||||
|
@ -28,6 +31,7 @@ public class Console {
|
|||
}
|
||||
|
||||
public static Attribute toBackground(Color background) {
|
||||
return Attribute.BACK_COLOR((int)(background.r*255), (int)(background.g*255), (int)(background.b*255));
|
||||
return Attribute.BACK_COLOR((int) (background.r * 255), (int) (background.g * 255),
|
||||
(int) (background.b * 255));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -708,6 +708,13 @@ public class EmulGLPainter extends AbstractPainter implements IPainter {
|
|||
color.b, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render 2D text at the given 3D position.
|
||||
*
|
||||
* The {@link Font} can be any font name and size supported by AWT.
|
||||
*
|
||||
* Rotation is in radian and is applied at the center of the text to avoid messing up text layout.
|
||||
*/
|
||||
@Override
|
||||
public void drawText(Font font, String label, Coord3d position, Color color, float rotation) {
|
||||
glut.glutBitmapString(toAWT(font), label, position.x, position.y, position.z, color.r, color.g,
|
||||
|
|
|
@ -30,292 +30,293 @@ import java.awt.geom.AffineTransform;
|
|||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ImageObserver;
|
||||
import java.awt.image.MemoryImageSource;
|
||||
import java.awt.image.RenderedImage;
|
||||
|
||||
import jgl.ImageToDraw;
|
||||
import jgl.TextToDraw;
|
||||
import jgl.context.gl_util;
|
||||
|
||||
public final class GL extends jgl.GL<BufferedImage, Font> {
|
||||
|
||||
protected Component canvas;
|
||||
protected BufferedImage glImage;
|
||||
protected boolean renderedOnce = false;
|
||||
protected Component canvas;
|
||||
protected BufferedImage glImage;
|
||||
protected boolean renderedOnce = false;
|
||||
|
||||
/**
|
||||
* Draws the image buffer that was built by {@link GL#glFlush()} with the caller {@link Graphics}
|
||||
* context
|
||||
*
|
||||
* OpenGL equivalent:
|
||||
*
|
||||
* <code>void glXSwapBuffers (Display *dpy, GLXDrawable drawable)</code>
|
||||
*/
|
||||
public void glXSwapBuffers(Graphics g, ImageObserver o) {
|
||||
/**
|
||||
* Draws the image buffer that was built by {@link GL#glFlush()} with the caller {@link Graphics}
|
||||
* context
|
||||
*
|
||||
* OpenGL equivalent:
|
||||
*
|
||||
* <code>void glXSwapBuffers (Display *dpy, GLXDrawable drawable)</code>
|
||||
*/
|
||||
public void glXSwapBuffers(Graphics g, ImageObserver o) {
|
||||
g.drawImage(glImage, StartX, StartY, desiredWidth, desiredHeight, o);
|
||||
}
|
||||
|
||||
g.drawImage(glImage, StartX, StartY, desiredWidth, desiredHeight, o);
|
||||
}
|
||||
public void glXSwapBuffers(Graphics g, Applet o) {
|
||||
glXSwapBuffers(g, (ImageObserver) o);
|
||||
}
|
||||
|
||||
public void glXSwapBuffers(Graphics g, Applet o) {
|
||||
@Override
|
||||
public void glFlush() {
|
||||
|
||||
glXSwapBuffers(g, (ImageObserver)o);
|
||||
}
|
||||
if (Context.RenderMode != GL_RENDER) {
|
||||
return;
|
||||
}
|
||||
// DEBUG
|
||||
// checkColorBuffer();
|
||||
|
||||
@Override
|
||||
public void glFlush() {
|
||||
// ------------------------------------------
|
||||
// Create an image producer based on
|
||||
// colorbuffer into which GL draws
|
||||
MemoryImageSource producer = new MemoryImageSource(Context.Viewport.Width,
|
||||
Context.Viewport.Height, Context.ColorBuffer.Buffer, 0, Context.Viewport.Width);
|
||||
// producer.setAnimated(true);
|
||||
// producer.setFullBufferUpdates(true);
|
||||
// Generates an image from the toolkit to use this producer
|
||||
|
||||
if(Context.RenderMode != GL_RENDER) {
|
||||
return;
|
||||
}
|
||||
// DEBUG
|
||||
// checkColorBuffer();
|
||||
|
||||
// ------------------------------------------
|
||||
// Create an image producer based on
|
||||
// colorbuffer into which GL draws
|
||||
MemoryImageSource producer = new MemoryImageSource(Context.Viewport.Width, Context.Viewport.Height, Context.ColorBuffer.Buffer, 0, Context.Viewport.Width);
|
||||
// producer.setAnimated(true);
|
||||
// producer.setFullBufferUpdates(true);
|
||||
// Generates an image from the toolkit to use this producer
|
||||
|
||||
Image jGLColorBuffer = canvas.createImage(producer);
|
||||
|
||||
// ------------------------------------------
|
||||
// Write GL content in a temporary image
|
||||
// and then to the image returned to Canvas
|
||||
glImage = new BufferedImage(jGLColorBuffer.getWidth(null), jGLColorBuffer.getHeight(null), BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g2d = (Graphics2D)glImage.getGraphics();
|
||||
configureRenderingHints(g2d);
|
||||
|
||||
// Hack background
|
||||
if(clearBackgroundWithG2d)
|
||||
hackClearColorWithG2DfillRect(g2d);
|
||||
|
||||
// Text that should appear BEHIND the scene's polygons
|
||||
drawTexts(g2d);
|
||||
|
||||
// Images that should appear BEHIND the scene's polygons
|
||||
// drawImagesAndClearBuffer(g2d);
|
||||
drawImages(g2d, ImageLayer.BACKGROUND);
|
||||
|
||||
// Color buffer
|
||||
g2d.drawImage(jGLColorBuffer, shiftHorizontally, 0, null);
|
||||
// Text that should appear ON TOP of the scene's polygons
|
||||
// ...
|
||||
|
||||
// Images that should appear ON TOP of the scene's polygons
|
||||
drawImages(g2d, ImageLayer.FOREGROUND);
|
||||
clearImagesBuffer();
|
||||
// debugWriteImageTo("target/jGL.glFlush.png", (RenderedImage)JavaImage);
|
||||
}
|
||||
Image jGLColorBuffer = canvas.createImage(producer);
|
||||
|
||||
public BufferedImage getRenderedImage() {
|
||||
// ------------------------------------------
|
||||
// Write GL content in a temporary image
|
||||
// and then to the image returned to Canvas
|
||||
glImage = new BufferedImage(jGLColorBuffer.getWidth(null), jGLColorBuffer.getHeight(null),
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g2d = (Graphics2D) glImage.getGraphics();
|
||||
configureRenderingHints(g2d);
|
||||
|
||||
return glImage;
|
||||
}
|
||||
// Hack background
|
||||
if (clearBackgroundWithG2d)
|
||||
hackClearColorWithG2DfillRect(g2d);
|
||||
|
||||
@Override
|
||||
public void applyViewport() {
|
||||
// Text that should appear BEHIND the scene's polygons
|
||||
drawTexts(g2d);
|
||||
|
||||
// Update pixel scale to guess if HiDPI
|
||||
if(canvas != null && canvas.getGraphics() != null)
|
||||
updatePixelScale(canvas.getGraphics());
|
||||
super.applyViewport();
|
||||
}
|
||||
// Images that should appear BEHIND the scene's polygons
|
||||
// drawImagesAndClearBuffer(g2d);
|
||||
drawImages(g2d, ImageLayer.BACKGROUND);
|
||||
|
||||
/** Because this is for Java, use true color and double buffer default */
|
||||
/** Bool glXMakeCurrent (Display *dpy, GLXDrawable drawable, GLXcontext ctx) */
|
||||
// public boolean glXMakeCurrent (Applet o, int x, int y) {
|
||||
public boolean glXMakeCurrent(Component o, int x, int y) {
|
||||
// Color buffer
|
||||
g2d.drawImage(jGLColorBuffer, shiftHorizontally, 0, null);
|
||||
// Text that should appear ON TOP of the scene's polygons
|
||||
// ...
|
||||
|
||||
// JavaApplet = o;
|
||||
canvas = o;
|
||||
StartX = x;
|
||||
StartY = y;
|
||||
// Context.gl_initialize_context (o.getSize().width, o.getSize().height);
|
||||
glViewport(x, y, o.getSize().width, o.getSize().height);
|
||||
Context.gl_initialize_context();
|
||||
return GL_TRUE;
|
||||
}
|
||||
// Images that should appear ON TOP of the scene's polygons
|
||||
drawImages(g2d, ImageLayer.FOREGROUND);
|
||||
clearImagesBuffer();
|
||||
// debugWriteImageTo("target/jGL.glFlush.png", (RenderedImage)JavaImage);
|
||||
}
|
||||
|
||||
public Component glJGetComponent() {
|
||||
public BufferedImage getRenderedImage() {
|
||||
|
||||
return canvas;
|
||||
}
|
||||
return glImage;
|
||||
}
|
||||
|
||||
public void updatePixelScale(Graphics g) {
|
||||
@Override
|
||||
public void applyViewport() {
|
||||
|
||||
Graphics2D g2d = (Graphics2D)g;
|
||||
// printGlobalScale(g2d);
|
||||
// produce 2.0 factory on MacOS with Retina
|
||||
// produce 1.5 factor on Win10 HiDPI on the same Apple hardware as above
|
||||
// We will read pixel scale from G2D while swapping images. This means
|
||||
// we may be late of 1 image to adapt to an HiDPI change.
|
||||
if(autoAdaptToHiDPI) {
|
||||
getPixelScaleFromG2D(g2d);
|
||||
if(!renderedOnce) {
|
||||
// no event was sent has the default values
|
||||
// are 1,1 so we force an event
|
||||
if(pixelScaleX == 1 && pixelScaleY == 1)
|
||||
firePixelScaleChanged(1, 1);
|
||||
renderedOnce = true;
|
||||
}
|
||||
} else {
|
||||
resetPixelScale();
|
||||
if(!renderedOnce) {
|
||||
firePixelScaleChanged(1, 1);
|
||||
renderedOnce = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update pixel scale to guess if HiDPI
|
||||
if (canvas != null && canvas.getGraphics() != null)
|
||||
updatePixelScale(canvas.getGraphics());
|
||||
super.applyViewport();
|
||||
}
|
||||
|
||||
/** Pixel scale is used to model the pixel ratio introduced by HiDPI */
|
||||
protected void getPixelScaleFromG2D(Graphics2D g2d) {
|
||||
/** Because this is for Java, use true color and double buffer default */
|
||||
/** Bool glXMakeCurrent (Display *dpy, GLXDrawable drawable, GLXcontext ctx) */
|
||||
// public boolean glXMakeCurrent (Applet o, int x, int y) {
|
||||
public boolean glXMakeCurrent(Component o, int x, int y) {
|
||||
canvas = o;
|
||||
StartX = x;
|
||||
StartY = y;
|
||||
|
||||
AffineTransform globalTransform = g2d.getTransform();
|
||||
double oldPixelScaleX = pixelScaleX;
|
||||
double oldPixelScaleY = pixelScaleY;
|
||||
double newPixelScaleX = globalTransform.getScaleX();
|
||||
double newPixelScaleY = globalTransform.getScaleY();
|
||||
setPixelScaleX(newPixelScaleX);
|
||||
setPixelScaleY(newPixelScaleY);
|
||||
if(newPixelScaleX != oldPixelScaleX || newPixelScaleY != oldPixelScaleY) {
|
||||
firePixelScaleChanged(newPixelScaleX, newPixelScaleY);
|
||||
}
|
||||
}
|
||||
glViewport(x, y, o.getSize().width, o.getSize().height);
|
||||
Context.gl_initialize_context();
|
||||
|
||||
return GL_TRUE;
|
||||
}
|
||||
|
||||
protected void printGlobalScale(Graphics2D g2d) {
|
||||
public Component glJGetComponent() {
|
||||
return canvas;
|
||||
}
|
||||
|
||||
AffineTransform globalTransform = g2d.getTransform();
|
||||
double globalScaleX = globalTransform.getScaleX();
|
||||
double globalScaleY = globalTransform.getScaleY();
|
||||
System.out.println("globalScaleX:" + globalScaleX + " globalScaleY:" + globalScaleY);
|
||||
}
|
||||
public void updatePixelScale(Graphics g) {
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
// printGlobalScale(g2d);
|
||||
// produce 2.0 factory on MacOS with Retina
|
||||
// produce 1.5 factor on Win10 HiDPI on the same Apple hardware as above
|
||||
// We will read pixel scale from G2D while swapping images. This means
|
||||
// we may be late of 1 image to adapt to an HiDPI change.
|
||||
if (autoAdaptToHiDPI) {
|
||||
getPixelScaleFromG2D(g2d);
|
||||
if (!renderedOnce) {
|
||||
// no event was sent has the default values
|
||||
// are 1,1 so we force an event
|
||||
if (pixelScaleX == 1 && pixelScaleY == 1)
|
||||
firePixelScaleChanged(1, 1);
|
||||
renderedOnce = true;
|
||||
}
|
||||
} else {
|
||||
resetPixelScale();
|
||||
if (!renderedOnce) {
|
||||
firePixelScaleChanged(1, 1);
|
||||
renderedOnce = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void configureRenderingHints(Graphics2D g2d) {
|
||||
/** Pixel scale is used to model the pixel ratio introduced by HiDPI */
|
||||
protected void getPixelScaleFromG2D(Graphics2D g2d) {
|
||||
AffineTransform globalTransform = g2d.getTransform();
|
||||
double oldPixelScaleX = pixelScaleX;
|
||||
double oldPixelScaleY = pixelScaleY;
|
||||
double newPixelScaleX = globalTransform.getScaleX();
|
||||
double newPixelScaleY = globalTransform.getScaleY();
|
||||
setPixelScaleX(newPixelScaleX);
|
||||
setPixelScaleY(newPixelScaleY);
|
||||
if (newPixelScaleX != oldPixelScaleX || newPixelScaleY != oldPixelScaleY) {
|
||||
firePixelScaleChanged(newPixelScaleX, newPixelScaleY);
|
||||
}
|
||||
}
|
||||
|
||||
RenderingHints rh = new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
g2d.setRenderingHints(rh);
|
||||
}
|
||||
protected void printGlobalScale(Graphics2D g2d) {
|
||||
AffineTransform globalTransform = g2d.getTransform();
|
||||
double globalScaleX = globalTransform.getScaleX();
|
||||
double globalScaleY = globalTransform.getScaleY();
|
||||
System.out.println("globalScaleX:" + globalScaleX + " globalScaleY:" + globalScaleY);
|
||||
}
|
||||
|
||||
protected void hackClearColorWithG2DfillRect(Graphics2D g2d) {
|
||||
protected void configureRenderingHints(Graphics2D g2d) {
|
||||
RenderingHints rh = new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING,
|
||||
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
g2d.setRenderingHints(rh);
|
||||
}
|
||||
|
||||
Color backgroundColor = getClearColorAWT();
|
||||
g2d.setColor(backgroundColor);
|
||||
g2d.fillRect(0, 0, Context.Viewport.Width, Context.Viewport.Height);
|
||||
}
|
||||
protected void hackClearColorWithG2DfillRect(Graphics2D g2d) {
|
||||
Color backgroundColor = getClearColorAWT();
|
||||
g2d.setColor(backgroundColor);
|
||||
g2d.fillRect(0, 0, Context.Viewport.Width, Context.Viewport.Height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a color given as an integer to an AWT Color.
|
||||
*/
|
||||
public static Color glIntToColor(int color) {
|
||||
/**
|
||||
* Convert a color given as an integer to an AWT Color.
|
||||
*/
|
||||
public static Color glIntToColor(int color) {
|
||||
float r = gl_util.ItoR(color);
|
||||
float g = gl_util.ItoG(color);
|
||||
float b = gl_util.ItoB(color);
|
||||
// float a = gl_util.ItoA(color);
|
||||
return new Color(r / 255, g / 255, b / 255);
|
||||
}
|
||||
|
||||
float r = gl_util.ItoR(color);
|
||||
float g = gl_util.ItoG(color);
|
||||
float b = gl_util.ItoB(color);
|
||||
// float a = gl_util.ItoA(color);
|
||||
return new Color(r / 255, g / 255, b / 255);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the clear color as an AWT Color.
|
||||
*/
|
||||
public Color getClearColorAWT() {
|
||||
|
||||
int clearColor = getContext().ColorBuffer.IntClearColor;
|
||||
return glIntToColor(clearColor);
|
||||
}
|
||||
/**
|
||||
* @return the clear color as an AWT Color.
|
||||
*/
|
||||
public Color getClearColorAWT() {
|
||||
int clearColor = getContext().ColorBuffer.IntClearColor;
|
||||
return glIntToColor(clearColor);
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected void drawImagesAndClearBuffer(Graphics2D g2d) {
|
||||
protected void drawImagesAndClearBuffer(Graphics2D g2d) {
|
||||
synchronized (imageToDraw) {
|
||||
for (ImageToDraw<BufferedImage> img : imageToDraw) {
|
||||
g2d.drawImage(img.image, img.x + shiftHorizontally, img.y, null);
|
||||
}
|
||||
imageToDraw.clear(); // empty image buffer
|
||||
}
|
||||
}
|
||||
|
||||
synchronized(imageToDraw) {
|
||||
for(ImageToDraw<BufferedImage> img : imageToDraw) {
|
||||
g2d.drawImage(img.image, img.x + shiftHorizontally, img.y, null);
|
||||
}
|
||||
imageToDraw.clear(); // empty image buffer
|
||||
}
|
||||
}
|
||||
protected void drawImages(Graphics2D g2d, ImageLayer layer) {
|
||||
synchronized (imageToDraw) {
|
||||
for (ImageToDraw<BufferedImage> img : imageToDraw) {
|
||||
if (img.layer == null || img.layer.equals(layer)) {
|
||||
g2d.drawImage(img.image, img.x + shiftHorizontally, img.y, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void drawImages(Graphics2D g2d, ImageLayer layer) {
|
||||
/**
|
||||
* Renders appended text to given {@link Graphics2D} context.
|
||||
*
|
||||
* @param g2d
|
||||
*/
|
||||
protected void drawTexts(Graphics2D g2d) {
|
||||
synchronized (textsToDraw) {
|
||||
for (TextToDraw<Font> text : textsToDraw) {
|
||||
doDrawString(g2d, text);
|
||||
}
|
||||
textsToDraw.clear(); // empty text buffer
|
||||
}
|
||||
}
|
||||
|
||||
synchronized(imageToDraw) {
|
||||
for(ImageToDraw<BufferedImage> img : imageToDraw) {
|
||||
if(img.layer == null || img.layer.equals(layer)) {
|
||||
g2d.drawImage(img.image, img.x + shiftHorizontally, img.y, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
protected void doDrawString(Graphics2D g2d, TextToDraw<Font> text) {
|
||||
|
||||
// Coloring
|
||||
if (text.r >= 0) {
|
||||
g2d.setColor(new Color(text.r, text.g, text.b));
|
||||
} else {
|
||||
g2d.setColor(Color.BLACK);
|
||||
}
|
||||
|
||||
// Position
|
||||
int x = text.x + shiftHorizontally;
|
||||
int y = text.y;
|
||||
|
||||
// Get text width
|
||||
int textWidth = 0;
|
||||
g2d.setFont(text.font);
|
||||
FontMetrics fm = g2d.getFontMetrics();
|
||||
if (fm != null) {
|
||||
textWidth = fm.stringWidth(text.string);
|
||||
}
|
||||
|
||||
// Configure context for rotation
|
||||
preRotateFromLeftPoint(g2d, x, y, text.rotate, textWidth);
|
||||
// rotation point / position of text
|
||||
// g2d.fillRect(-1, -1, 2, 2);
|
||||
|
||||
// Render text
|
||||
if (useOSFontRendering) {
|
||||
g2d.drawString(text.string, 0, 0);
|
||||
} else {
|
||||
FontRenderContext frc = g2d.getFontRenderContext();
|
||||
GlyphVector gv = text.font.createGlyphVector(frc, text.string);
|
||||
g2d.drawGlyphVector(gv, 0, 0);
|
||||
}
|
||||
|
||||
// Reset rotation from context
|
||||
postRotateFromLeftPoint(g2d, x, y, text.rotate, textWidth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders appended text to given {@link Graphics2D} context.
|
||||
*
|
||||
* @param g2d
|
||||
*/
|
||||
protected void drawTexts(Graphics2D g2d) {
|
||||
/**
|
||||
* Configure graphics to rotate text and shift the text horizontally
|
||||
*/
|
||||
protected void preRotateFromLeftPoint(Graphics2D g2d, int x, int y, float rotate, int textWidth) {
|
||||
|
||||
synchronized(textsToDraw) {
|
||||
for(TextToDraw<Font> text : textsToDraw) {
|
||||
doDrawString(g2d, text);
|
||||
}
|
||||
textsToDraw.clear(); // empty text buffer
|
||||
}
|
||||
}
|
||||
if (rotate != 0) {
|
||||
g2d.translate(textWidth / 2, 0);
|
||||
}
|
||||
g2d.translate(x, y);
|
||||
if (rotate != 0) {
|
||||
g2d.rotate(rotate);
|
||||
g2d.translate(-textWidth / 2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
protected void doDrawString(Graphics2D g2d, TextToDraw<Font> text) {
|
||||
/**
|
||||
* Apply reverse rotation and translation to reset graphics context.
|
||||
*/
|
||||
protected void postRotateFromLeftPoint(Graphics2D g2d, int x, int y, float rotate,
|
||||
int textWidth) {
|
||||
if (rotate != 0) {
|
||||
g2d.translate(textWidth / 2, 0);
|
||||
g2d.rotate(-rotate);
|
||||
}
|
||||
g2d.translate(-x, -y);
|
||||
if (rotate != 0) {
|
||||
g2d.translate(-textWidth / 2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// AffineTransform orig = g2d.getTransform();
|
||||
// g2d.rotate(Math.PI/2);
|
||||
if(text.r >= 0) {
|
||||
g2d.setColor(new Color(text.r, text.g, text.b));
|
||||
} else {
|
||||
g2d.setColor(Color.BLACK);
|
||||
}
|
||||
int x = text.x + shiftHorizontally;
|
||||
int y = text.y;
|
||||
float rotate = text.rotate;
|
||||
int textWidth = 0;
|
||||
g2d.setFont(text.font);
|
||||
FontMetrics fm = g2d.getFontMetrics();
|
||||
if(fm != null) {
|
||||
textWidth = fm.stringWidth(text.string);
|
||||
}
|
||||
preRotateFromLeftPoint(g2d, x, y, rotate, textWidth);
|
||||
// rotation point / position of text
|
||||
// g2d.fillRect(-1, -1, 2, 2);
|
||||
if(useOSFontRendering) {
|
||||
g2d.drawString(text.string, 0, 0);
|
||||
} else {
|
||||
FontRenderContext frc = g2d.getFontRenderContext();
|
||||
GlyphVector gv = text.font.createGlyphVector(frc, text.string);
|
||||
g2d.drawGlyphVector(gv, 0, 0);
|
||||
}
|
||||
postRotateFromLeftPoint(g2d, x, y, rotate, textWidth);
|
||||
}
|
||||
|
||||
protected void preRotateFromLeftPoint(Graphics2D g2d, int x, int y, float rotate, int textWidth) {
|
||||
|
||||
if(rotate != 0) {
|
||||
g2d.translate(textWidth / 2, 0);
|
||||
}
|
||||
g2d.translate(x, y);
|
||||
if(rotate != 0) {
|
||||
g2d.rotate(rotate);
|
||||
g2d.translate(-textWidth / 2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
protected void postRotateFromLeftPoint(Graphics2D g2d, int x, int y, float rotate, int textWidth) {
|
||||
|
||||
if(rotate != 0) {
|
||||
g2d.translate(textWidth / 2, 0);
|
||||
g2d.rotate(-rotate);
|
||||
}
|
||||
g2d.translate(-x, -y);
|
||||
if(rotate != 0) {
|
||||
g2d.translate(-textWidth / 2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import org.jzy3d.maths.Coord3d;
|
|||
import org.jzy3d.painters.AWTFont;
|
||||
import org.jzy3d.painters.Font;
|
||||
import org.jzy3d.painters.IPainter;
|
||||
import org.jzy3d.painters.NativeDesktopPainter;
|
||||
import org.jzy3d.plot3d.primitives.PolygonFill;
|
||||
import org.jzy3d.plot3d.primitives.PolygonMode;
|
||||
import org.jzy3d.plot3d.text.AbstractTextRenderer;
|
||||
|
@ -16,6 +17,7 @@ import org.jzy3d.plot3d.text.align.AWTTextLayout;
|
|||
import org.jzy3d.plot3d.text.align.Horizontal;
|
||||
import org.jzy3d.plot3d.text.align.Vertical;
|
||||
import org.jzy3d.plot3d.text.renderers.jogl.style.DefaultTextStyle;
|
||||
import com.jogamp.opengl.GL2;
|
||||
import com.jogamp.opengl.util.awt.TextRenderer;
|
||||
import com.jogamp.opengl.util.awt.TextRenderer.RenderDelegate;
|
||||
|
||||
|
@ -72,14 +74,14 @@ public class JOGLTextRenderer2d extends AbstractTextRenderer implements ITextRen
|
|||
// Reset to a polygon mode suitable for rendering the texture handling the text
|
||||
painter.glPolygonMode(PolygonMode.FRONT_AND_BACK, PolygonFill.FILL);
|
||||
|
||||
drawText2D(painter, font, s, position, color, horizontal, vertical);
|
||||
drawText2D(painter, font, s, position, color, rotation, horizontal, vertical);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Draws a 2D text (facing camera) at the specified 3D position. */
|
||||
protected void drawText2D(IPainter painter, Font font, String text, Coord3d position,
|
||||
Color color, Horizontal horizontal, Vertical vertical) {
|
||||
Color color, float rotation, Horizontal horizontal, Vertical vertical) {
|
||||
|
||||
// Canvas size
|
||||
int width = painter.getView().getCanvas().getRendererWidth();
|
||||
|
@ -90,14 +92,42 @@ public class JOGLTextRenderer2d extends AbstractTextRenderer implements ITextRen
|
|||
Coord2d textSize = layout.getBounds(text, awtFont, renderer.getFontRenderContext());
|
||||
screen = layout.align(textSize.x, textSize.y, horizontal, vertical, screen);
|
||||
|
||||
|
||||
GL2 gl = ((NativeDesktopPainter)painter).getGL().getGL2();
|
||||
|
||||
|
||||
|
||||
// Render text
|
||||
renderer.setColor(color.r, color.g, color.b, color.a);
|
||||
renderer.beginRendering(width, height);
|
||||
|
||||
|
||||
gl.glMatrixMode(GL2.GL_MODELVIEW);
|
||||
gl.glPushMatrix();
|
||||
|
||||
renderer.draw(text, (int) screen.x, (int) screen.y);
|
||||
renderer.flush();
|
||||
rotation = (float)(360*rotation / (2*Math.PI));
|
||||
//rotation = 90;
|
||||
gl.glTranslatef(screen.x, screen.y, 0);
|
||||
gl.glRotatef(rotation, 0, 0, 1);
|
||||
|
||||
|
||||
|
||||
//System.out.println(rotation);
|
||||
|
||||
int x =0, y = 0;
|
||||
|
||||
/*if(Math.abs(rotation)==90) {
|
||||
x = (int)(textSize.y / 2);
|
||||
y = (int)(textSize.x / 2);
|
||||
}*/
|
||||
|
||||
renderer.draw(text, x, y);
|
||||
//renderer.draw(text, (int) screen.x, (int) screen.y);
|
||||
renderer.endRendering();
|
||||
renderer.flush();
|
||||
|
||||
gl.glPopMatrix();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ import java.nio.Buffer;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jzy3d.colors.Color;
|
||||
|
@ -30,11 +32,12 @@ import com.jogamp.opengl.fixedfunc.GLLightingFunc;
|
|||
import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
|
||||
import com.jogamp.opengl.glu.GLU;
|
||||
import com.jogamp.opengl.glu.GLUquadric;
|
||||
import com.jogamp.opengl.util.awt.TextRenderer;
|
||||
import com.jogamp.opengl.util.gl2.GLUT;
|
||||
|
||||
public class NativeDesktopPainter extends AbstractPainter implements IPainter {
|
||||
static Logger LOGGER = LogManager.getLogger(NativeDesktopPainter.class);
|
||||
|
||||
|
||||
protected GL gl;
|
||||
protected GLU glu = new GLU();
|
||||
protected GLUT glut = new GLUT();
|
||||
|
@ -43,6 +46,10 @@ public class NativeDesktopPainter extends AbstractPainter implements IPainter {
|
|||
return gl;
|
||||
}
|
||||
|
||||
public GL2 getGL2() {
|
||||
return getGL().getGL2();
|
||||
}
|
||||
|
||||
public void setGL(GL gl) {
|
||||
this.gl = gl;
|
||||
}
|
||||
|
@ -141,23 +148,22 @@ public class NativeDesktopPainter extends AbstractPainter implements IPainter {
|
|||
} else {
|
||||
gl.glDisable(GL2.GL_ALPHA_TEST);
|
||||
}
|
||||
|
||||
|
||||
// Make smooth colors for polygons (interpolate color between points)
|
||||
if(gl.getGLProfile().isGL2()) {
|
||||
if (quality.isSmoothColor())
|
||||
if (gl.getGLProfile().isGL2()) {
|
||||
if (quality.isSmoothColor())
|
||||
gl.getGL2().glShadeModel(GLLightingFunc.GL_SMOOTH);
|
||||
else
|
||||
gl.getGL2().glShadeModel(GLLightingFunc.GL_FLAT);
|
||||
} else {
|
||||
LOGGER.warn(
|
||||
"Did not configured shade model as we don t have a GL2 context : " + gl.getGLProfile());
|
||||
}
|
||||
else {
|
||||
LOGGER.warn("Did not configured shade model as we don t have a GL2 context : " + gl.getGLProfile());
|
||||
}
|
||||
/*else if(gl.getGLProfile().isGL4()) {
|
||||
if (quality.isSmoothColor())
|
||||
gl.getGL4().glShadeModel(GLLightingFunc.GL_SMOOTH);
|
||||
else
|
||||
gl.getGL2().glShadeModel(GLLightingFunc.GL_FLAT);
|
||||
}*/
|
||||
/*
|
||||
* else if(gl.getGLProfile().isGL4()) { if (quality.isSmoothColor())
|
||||
* gl.getGL4().glShadeModel(GLLightingFunc.GL_SMOOTH); else
|
||||
* gl.getGL2().glShadeModel(GLLightingFunc.GL_FLAT); }
|
||||
*/
|
||||
|
||||
// Make smoothing setting
|
||||
if (quality.isSmoothPolygon()) {
|
||||
|
@ -448,11 +454,97 @@ public class NativeDesktopPainter extends AbstractPainter implements IPainter {
|
|||
glut.glutBitmapString(font, string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render 2D text at the given 3D position.
|
||||
*
|
||||
* The {@link Font} can be any font name and size supported by AWT.
|
||||
*
|
||||
* Rotation is in radian and is applied at the center of the text to avoid messing up text layout.
|
||||
*
|
||||
* @see {@link #glutBitmapString(int, String)}, an alternative way of rendering text with simpler
|
||||
* parameters and smaller font name and size set.
|
||||
*/
|
||||
@Override
|
||||
public void drawText(Font font, String label, Coord3d position, Color color, float rotation) {
|
||||
glutBitmapString(font, label, position, color);
|
||||
// OLD WAY
|
||||
//glutBitmapString(font, label, position, color);
|
||||
|
||||
|
||||
// Reset to a polygon mode suitable for rendering the texture handling the text
|
||||
glPolygonMode(PolygonMode.FRONT_AND_BACK, PolygonFill.FILL);
|
||||
|
||||
// Geometric processing for text layout
|
||||
int width = getCanvas().getRendererWidth();
|
||||
int height = getCanvas().getRendererHeight();
|
||||
float rotationD = -(float) (360 * rotation / (2 * Math.PI));
|
||||
Coord3d screen = modelToScreen(position);
|
||||
|
||||
|
||||
TextRenderer renderer = getOrCreateTextRenderer(font);
|
||||
renderer.setColor(color.r, color.g, color.b, color.a);
|
||||
renderer.beginRendering(width, height);
|
||||
|
||||
// System.out.println(label + "\t" + screen);
|
||||
// Console.print(label + "\t" + screen, color);
|
||||
|
||||
int xPreShift = 0, yPreShift = 0;
|
||||
|
||||
|
||||
if (rotation != 0) {
|
||||
// xPreShift = (int)(Math.cos(rotation)*getTextLengthInPixels(font, label));
|
||||
// yPreShift = (int)(Math.sin(rotation)*font.getHeight());
|
||||
xPreShift = getTextLengthInPixels(font, label) / 2;
|
||||
yPreShift = 0;
|
||||
}
|
||||
|
||||
|
||||
GL2 gl = getGL2();
|
||||
gl.glMatrixMode(GL2.GL_MODELVIEW);
|
||||
gl.glPushMatrix();
|
||||
gl.glTranslatef(screen.x + xPreShift, screen.y + yPreShift, 0);
|
||||
gl.glRotatef(rotationD, 0, 0, 1);
|
||||
|
||||
// Shifting text to deal with rotation
|
||||
int xPostShift = 0, yPostShift = 0;
|
||||
|
||||
if (rotation != 0) {
|
||||
xPostShift = (int) (Math.cos(rotation) * getTextLengthInPixels(font, label));
|
||||
yPostShift = (int) (Math.sin(rotation) * font.getHeight());
|
||||
xPostShift = -xPreShift;// getTextLengthInPixels(font, label)/2;
|
||||
yPostShift = -yPreShift;
|
||||
}
|
||||
|
||||
renderer.draw(label, xPostShift, yPostShift);
|
||||
renderer.endRendering();
|
||||
renderer.flush();
|
||||
|
||||
gl.glPopMatrix();
|
||||
|
||||
}
|
||||
|
||||
protected TextRenderer getOrCreateTextRenderer(Font font) {
|
||||
TextRenderer renderer = txtRendererMap.get(font);
|
||||
|
||||
if (renderer == null) {
|
||||
renderer = new TextRenderer(toAWT(font), true, true, null);
|
||||
// renderer.setSmoothing(false);// some GPU do not handle smoothing well
|
||||
// renderer.setUseVertexArrays(false); // some GPU do not handle VBO properly
|
||||
txtRendererMap.put(font, renderer);
|
||||
}
|
||||
return renderer;
|
||||
}
|
||||
|
||||
protected Map<Font, TextRenderer> txtRendererMap = new HashMap<>();
|
||||
|
||||
// protected TextLayout layout = new TextLayout();
|
||||
|
||||
|
||||
/**
|
||||
* Render 2D text at the given 3D position using a font as supported by
|
||||
* {@link #glutBitmapString(int, String)}.
|
||||
*
|
||||
* @see {@link Font} to know the set of font name and size supported by this method.
|
||||
*/
|
||||
@Override
|
||||
public void glutBitmapString(Font axisFont, String label, Coord3d p, Color c) {
|
||||
color(c);
|
||||
|
@ -655,11 +747,11 @@ public class NativeDesktopPainter extends AbstractPainter implements IPainter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void glStencilMask_False(){
|
||||
gl.glStencilMask(GL.GL_FALSE);
|
||||
public void glStencilMask_False() {
|
||||
gl.glStencilMask(GL.GL_FALSE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void glStencilOp(StencilOp fail, StencilOp zfail, StencilOp zpass) {
|
||||
gl.glStencilOp(toInt(fail), toInt(zfail), toInt(zpass));
|
||||
|
@ -722,7 +814,7 @@ public class NativeDesktopPainter extends AbstractPainter implements IPainter {
|
|||
|
||||
@Override
|
||||
public void glClipPlane(int plane, double[] equation) {
|
||||
//Array.print("NativePainter : glClipPlane : " + plane + " : ", equation);
|
||||
// Array.print("NativePainter : glClipPlane : " + plane + " : ", equation);
|
||||
gl.getGL2().glClipPlane(plane, equation, 0);
|
||||
}
|
||||
|
||||
|
@ -730,8 +822,8 @@ public class NativeDesktopPainter extends AbstractPainter implements IPainter {
|
|||
public void glEnable_ClipPlane(int plane) {
|
||||
gl.glEnable(clipPlaneId(plane));
|
||||
}
|
||||
|
||||
/** Return the GL clip plane ID according to an ID in [0;5]*/
|
||||
|
||||
/** Return the GL clip plane ID according to an ID in [0;5] */
|
||||
@Override
|
||||
public int clipPlaneId(int id) {
|
||||
switch (id) {
|
||||
|
@ -1250,12 +1342,12 @@ public class NativeDesktopPainter extends AbstractPainter implements IPainter {
|
|||
public void glDisable_DepthTest() {
|
||||
gl.glDisable(GL.GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void glEnable_Stencil() {
|
||||
gl.glEnable(GL2.GL_STENCIL_TEST);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void glDisable_Stencil() {
|
||||
gl.glDisable(GL2.GL_STENCIL_TEST);
|
||||
|
|
Loading…
Reference in New Issue