Why does safari merge canvas drawings on mouse move? - html

The video shows the performance problem of safari on canvas drawing.
The drawing takes 100 msec. at average.
When I move cursor point A to B, other browsers draw the canvas at some points between A and B. But on safari, it generally draws on only near A and near B.
If the drawing time is so small, safari behaves like the others.
I think when safari does a drawing job, the mouse events are not triggered to the browser.
Is there a way to fix this problem?
My code:
public void draw() {
long start = System.currentTimeMillis();
canvas.getContext2d().clearRect(Integer.MIN_VALUE / 2,
Integer.MIN_VALUE / 2, Integer.MAX_VALUE, Integer.MAX_VALUE);
canvas.getContext2d().save();
canvas.getContext2d().setFillStyle("red");
for (int i = 0; i < 10000; i++) {
int x = (int)(Math.random() * 1000);
int y = (int)(Math.random() * 600);
canvas.getContext2d().fillRect(x, y, 15, 15);
}
canvas.getContext2d().restore();
logger.finer("Draw Time: " + (System.currentTimeMillis() - start));
}
canvas.addMouseDownHandler(new MouseDownHandler() {
#Override
public void onMouseDown(MouseDownEvent event) {
event.preventDefault();
startX = event.getX();
startY = event.getY();
dragging = true;
}
});
canvas.addMouseMoveHandler(new MouseMoveHandler() {
#Override
public void onMouseMove(MouseMoveEvent event) {
if(dragging)
{
event.preventDefault();
int x = event.getX();
int y = event.getY();
canvas.getContext2d().translate((-startX + x) / startScale,
(-startY + y) / startScale);
draw();
startX = x;
startY = y;
}
}
});
canvas.addMouseUpHandler(new MouseUpHandler() {
#Override
public void onMouseUp(MouseUpEvent event) {
dragging = false;
}
});

Related

Editing a function within mousePressed

I'm a beginner working with Processing trying to create a moving cloud sketch. They are to appear on mouseClick, and horizontally move across the screen.
void mousePressed() {
int newCloud {
xpos: mouseX;
ypos: mouseY;
}
clouds.push(newCloud);
}
Here is the area I'm unable to fix, trying to work out the mousePressed part.
and here is my full code! It seems a simple fix but I've tried a bunch of ways rewriting it without succsess.
int[] clouds;
int cloudx;
int cloudy;
int xpos, ypos;
void setup() {
size(600, 600);
int cloudx=mouseX;
int cloudy=mouseY;
}
void draw() {
background(100);
for (int i = 0; i < clouds.length; i++) {
int[] currentObj = clouds[i];
cloud(currentObj.xpos, currentObj.ypos, currentObj.size);
currentObj.xpos += 0.5;
currentObj.ypos += random(-0.5, 0.5);
if (clouds[i].xpos > width+20) {
clouds.splice(i, 1);
}
}
}
void makeCloud (int x, int y){
fill(250);
noStroke();
ellipse(x, y, 70, 50);
ellipse(x + 10, y + 10, 70, 50);
ellipse(x - 20, y + 10, 70, 50);
}
void mousePressed() {
int newCloud {
xpos: mouseX;
ypos: mouseY;
}
clouds.push(newCloud);
}
I had tried to make a new function, though the clouds wouldnt show, I also tried calling the makeCloud function though i know I need to be updating within this new function. Overall, I need help with how to write this statement for newCloud in the mousePressed function.
Your code is irreparable for many reasons.
int[] clouds; will create a reference for Array of single integers, not objects,
void makeCloud (int x, int y){...}, will just draw some ellipses,
clouds.splice(i, 1); inside an Array will not work at all,
This is a working reconstruction of Your problem:
ArrayList<Cloud> clouds = new ArrayList<Cloud>();
void setup() {
size(600, 600);
}
void draw() {
background(100);
drawClouds(clouds);
removeExcessClouds(clouds);
}
/**
** Cloud class
**/
class Cloud {
float xPos;
float yPos;
Cloud(float x, float y) {
xPos = x;
yPos = y;
}
void draw() {
fill(250);
noStroke();
ellipse(xPos, yPos, 70, 50);
ellipse(xPos + 10, yPos + 10, 70, 50);
ellipse(xPos - 20, yPos + 10, 70, 50);
}
void positionUpdate(float deltaX) {
xPos += deltaX;
yPos += random(-0.5, 0.5);
}
}
void drawClouds(ArrayList<Cloud> cds) {
float wind = 0.5;
for (Cloud cd : clouds) {
cd.draw();
cd.positionUpdate(wind);
}
}
void removeExcessClouds(ArrayList<Cloud> cds) {
int cdAmount = clouds.size();
for (int i = 0; i<cdAmount; i++) {
if (clouds.get(i).xPos > width+20) {
clouds.remove(i);
cdAmount = clouds.size();
}
}
}
void mousePressed() {
clouds.add(new Cloud(mouseX, mouseY));
println(mouseX + ", " + mouseY + " : " + clouds.size());
}
Note:
global List initiation:
ArrayList clouds = new ArrayList();
List proper iteration:
for (Cloud cd : clouds) { foo(cd); }
draw method inside a Cloud,
passing values when calling methods.
So, now You can iterate over a List of Objects, and call a draw method inside each Cloud.
As said in the other answer, you will need to refactor your code to use objects.
This looks like an atempt at a JS object literal - Java doesn't use them.
int newCloud {
xpos: mouseX;
ypos: mouseY;
}
You need to instance a Class:
Cloud myCloud = new Cloud(0,5); // You create a new variable of the Cloud type and initialize it with a new Cloud object (essentially calling the constructor)
class Cloud{
int posX, posY;
Cloud(int px, int py){ // This is called a constuctor and its the way a new instance is created
this.posX = px;
this.posY = py;
}
}
Than for the array of clouds you need an ArrayList of Clouds:
ArrayList<Cloud> clouds = new ArrayList<Cloud>();
in the mousePressed event you than just add the new cloud to the arraylist:
clouds.add(myCloud);

Libgdx : GestureListener - pan not working properly

I am new to libgdx and trying to move a circle vy draging it. But its not working properly.
class deceleration is as follows -
public class gScreen1 extends ScreenAdapter
implements GestureDetector.GestureListener {
private static final float WORLD_WIDTH = 640;
private static final float WORLD_HEIGHT = 480;
...
#Override
public void show() {
super.show();
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera.position.set(WORLD_WIDTH / 2, WORLD_HEIGHT / 2, 0);
camera.update();
viewport = new FitViewport(WORLD_WIDTH, WORLD_HEIGHT, camera);
shapeRenderer = new ShapeRenderer();
batch = new SpriteBatch();
}
private void drawGrid() {
Gdx.gl.glLineWidth(2);
shapeRenderer.setProjectionMatrix(camera.projection);
shapeRenderer.setTransformMatrix(camera.view);
shapeRenderer.setColor(Color.YELLOW);
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
size = points.length;
for (int i = 0; i < size; i++) {
shapeRenderer.circle(pointsr[i].x , pointsr[i].y ,10);
}
shapeRenderer.end();
}
#Override
public void render(float delta) {
super.render(delta);
clearScreen();
drawGrid();
}
#Override
public boolean pan(float x1, float y1, float deltaX, float deltaY) {
Vector3 tmpCoords = new Vector3(x1,y1, 0);
camera.unproject(tmpCoords);
int x = (int)tmpCoords.x;
int y = (int)tmpCoords.y;
for( i = 0; i < size ; i++) {
int x2 = pointsr[i].x;
int y2 = pointsr[i].y;
if( ((x2 -x)*(x2 -x) + (y2 -y)*(y2 -y) ) < 400 )
break;
}
if( i < size ) {
pointsr[i].x += deltaX;
pointsr[i].y += deltaY;
}
}
Circles are not following movement of finger, some time they move a little bit but in opposite y direction of touch movement.

Horizontal only touchpad in libgdx

Im trying to modify the libgdx Touchpad class to only be horizontal movement and not a full circle. So something like this:
But the touch is messed up and doesnt stop the movement within the bounds of the touchpad like it does for the round version. Heres my version, any help on where Im going wrong would be appreciated:
public class HorizontalTouchpad extends Widget {
private HorizontalTouchpadStyle style;
boolean touched;
boolean resetOnTouchUp = true;
private float deadzoneWidth;
private final Rectangle knobBounds = new Rectangle(0, 0, 0,0);
private final Rectangle touchBounds = new Rectangle(0, 0, 0,0);
private final Rectangle deadzoneBounds = new Rectangle(0, 0, 0,0);
private final Vector2 knobPosition = new Vector2();
private final Vector2 knobPercent = new Vector2();
/** #param deadzoneWidth The distance in pixels from the center of the touchpad required for the knob to be moved. */
public HorizontalTouchpad (float deadzoneWidth, Skin skin) {
this(deadzoneWidth, skin.get(HorizontalTouchpadStyle.class));
}
/** #param deadzoneWidth The distance in pixels from the center of the touchpad required for the knob to be moved. */
public HorizontalTouchpad (float deadzoneWidth, Skin skin, String styleName) {
this(deadzoneWidth, skin.get(styleName, HorizontalTouchpadStyle.class));
}
/** #param deadzoneWidth The distance in pixels from the center of the touchpad required for the knob to be moved. */
public HorizontalTouchpad (float deadzoneWidth, HorizontalTouchpadStyle style) {
if (deadzoneWidth < 0) throw new IllegalArgumentException("deadzoneWidth must be > 0");
this.deadzoneWidth = deadzoneWidth;
knobPosition.set(getWidth() / 2f, getHeight() / 2f);
setStyle(style);
setSize(getPrefWidth(), getPrefHeight());
addListener(new InputListener() {
#Override
public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
if (touched) return false;
touched = true;
calculatePositionAndValue(x, y, false);
return true;
}
#Override
public void touchDragged (InputEvent event, float x, float y, int pointer) {
calculatePositionAndValue(x, y, false);
}
#Override
public void touchUp (InputEvent event, float x, float y, int pointer, int button) {
touched = false;
calculatePositionAndValue(x, y, resetOnTouchUp);
}
});
}
void calculatePositionAndValue (float x, float y, boolean isTouchUp) {
float oldPositionX = knobPosition.x;
float oldPositionY = knobPosition.y;
float oldPercentX = knobPercent.x;
float oldPercentY = knobPercent.y;
float centerX = knobBounds.x;
float centerY = knobBounds.y;
knobPosition.set(centerX, centerY);
knobPercent.set(0f, 0f);
if (!isTouchUp) {
if (!deadzoneBounds.contains(x, y)) {
knobPercent.set((x - centerX) / (knobBounds.getWidth()/2), 0);
float length = knobPercent.len();
if (length > 1) knobPercent.scl(1 / length);
if (knobBounds.contains(x, y)) {
knobPosition.set(x, y);
} else {
knobPosition.set(knobPercent).nor().scl(knobBounds.getWidth()/2,0).add(knobBounds.x, knobBounds.y);
}
}
}
if (oldPercentX != knobPercent.x || oldPercentY != knobPercent.y) {
ChangeListener.ChangeEvent changeEvent = Pools.obtain(ChangeListener.ChangeEvent.class);
if (fire(changeEvent)) {
knobPercent.set(oldPercentX, oldPercentY);
knobPosition.set(oldPositionX, oldPositionY);
}
Pools.free(changeEvent);
}
}
public void setStyle (HorizontalTouchpadStyle style) {
if (style == null) throw new IllegalArgumentException("style cannot be null");
this.style = style;
invalidateHierarchy();
}
/** Returns the touchpad's style. Modifying the returned style may not have an effect until {#link #setStyle(HorizontalTouchpadStyle)} is
* called. */
public HorizontalTouchpadStyle getStyle () {
return style;
}
#Override
public Actor hit (float x, float y, boolean touchable) {
if (touchable && this.getTouchable() != Touchable.enabled) return null;
return touchBounds.contains(x, y) ? this : null;
}
#Override
public void layout () {
// Recalc pad and deadzone bounds
float halfWidth = getWidth() / 2;
float halfHeight = getHeight() / 2;
float radius = Math.min(halfWidth, halfHeight);
touchBounds.set(halfWidth, halfHeight, getWidth(),getHeight());
if (style.knob != null) radius -= Math.max(style.knob.getMinWidth(), style.knob.getMinHeight()) / 2;
knobBounds.set(halfWidth, halfHeight, getWidth(),getHeight());
deadzoneBounds.set(halfWidth, halfHeight, deadzoneWidth, getHeight());
// Recalc pad values and knob position
knobPosition.set(halfWidth, halfHeight);
knobPercent.set(0, 0);
}
#Override
public void draw (Batch batch, float parentAlpha) {
validate();
Color c = getColor();
batch.setColor(c.r, c.g, c.b, c.a * parentAlpha);
float x = getX();
float y = getY();
float w = getWidth();
float h = getHeight();
final Drawable bg = style.background;
if (bg != null) bg.draw(batch, x, y, w, h);
final Drawable knob = style.knob;
if (knob != null) {
x += knobPosition.x - knob.getMinWidth() / 2f;
y += knobPosition.y - knob.getMinHeight() / 2f;
knob.draw(batch, x, y, knob.getMinWidth(), knob.getMinHeight());
}
}
#Override
public float getPrefWidth () {
return style.background != null ? style.background.getMinWidth() : 0;
}
#Override
public float getPrefHeight () {
return style.background != null ? style.background.getMinHeight() : 0;
}
public boolean isTouched () {
return touched;
}
public boolean getResetOnTouchUp () {
return resetOnTouchUp;
}
/** #param reset Whether to reset the knob to the center on touch up. */
public void setResetOnTouchUp (boolean reset) {
this.resetOnTouchUp = reset;
}
/** #param deadzoneWidth The distance in pixels from the center of the touchpad required for the knob to be moved. */
public void setDeadzone (float deadzoneWidth) {
if (deadzoneWidth < 0) throw new IllegalArgumentException("deadzoneWidth must be > 0");
this.deadzoneWidth = deadzoneWidth;
invalidate();
}
/** Returns the x-position of the knob relative to the center of the widget. The positive direction is right. */
public float getKnobX () {
return knobPosition.x;
}
/** Returns the y-position of the knob relative to the center of the widget. The positive direction is up. */
public float getKnobY () {
return knobPosition.y;
}
/** Returns the x-position of the knob as a percentage from the center of the touchpad to the edge of the circular movement
* area. The positive direction is right. */
public float getKnobPercentX () {
return knobPercent.x;
}
/** Returns the y-position of the knob as a percentage from the center of the touchpad to the edge of the circular movement
* area. The positive direction is up. */
public float getKnobPercentY () {
return knobPercent.y;
}
/** The style for a {#link HorizontalTouchpad}.
* #author Josh Street */
public static class HorizontalTouchpadStyle {
/** Stretched in both directions. Optional. */
public Drawable background;
/** Optional. */
public Drawable knob;
public HorizontalTouchpadStyle () {
}
public HorizontalTouchpadStyle (Drawable background, Drawable knob) {
this.background = background;
this.knob = knob;
}
public HorizontalTouchpadStyle (HorizontalTouchpadStyle style) {
this.background = style.background;
this.knob = style.knob;
}
}
}
If you only allow horizontal movement it's only x-position that's interesting right? You can set y-position to always be a constant value. To make it stay within the bounds of the touchpad you need a check to make sure it doesn't move outside. So if you define some constants inside the layout method:
#Override
public void layout () {
// Recalc pad and deadzone bounds
float halfWidth = getWidth() / 2;
float halfHeight = getHeight() / 2;
float radius = Math.min(halfWidth, halfHeight);
touchBounds.set(halfWidth, halfHeight, getWidth(),getHeight());
if (style.knob != null) radius -= Math.max(style.knob.getMinWidth(), style.knob.getMinHeight()) / 2;
knobBounds.set(halfWidth, halfHeight, getWidth(),getHeight());
deadzoneBounds.set(halfWidth, halfHeight, deadzoneWidth, getHeight());
yPosition = halfHeight;
minX = style.knob.getMinWidth() / 2;
maxX = getWidth() - style.knob.getMinWidth() / 2;
// Recalc pad values and knob position
knobPosition.set(halfWidth, halfHeight);
knobPercent.set(0, 0);
}
and then when you set the position:
.....
if (knobBounds.contains(x, y)) {
if (x < minX) {
x = minX;
}
if (x > maxX) {
x = maxX;
}
knobPosition.set(x, yPosition);
} else {
knobPosition.set(knobPercent).nor().scl(knobBounds.getWidth()/2,0).add(knobBounds.x, knobBounds.y);
}
.....
Something like that (might need tweeking). You probably have to do a similair check for knobPercent.
Had to adjust the bounds and how it calculates the location and percent based on it being a rectangle versus a circle. Here is the updated layout method and calculate method:
#Override
public void layout () {
// Recalc pad and deadzone bounds
float halfWidth = getWidth() / 2;
float halfHeight = getHeight() / 2;
touchBounds.set(getX(), getY(), getWidth(),getHeight());
knobBounds.set(getX(), getY(), getWidth(),getHeight());
deadzoneBounds.set(halfWidth-deadzoneWidth/2, getY(), deadzoneWidth, getHeight());
yPosition = halfHeight;
// Recalc pad values and knob position
knobPosition.set(halfWidth, halfHeight);
knobPercent.set(0, 0);
}
Added a yPosition variable to make it easy to set the y position everytime
void calculatePositionAndValue (float x, float y, boolean isTouchUp) {
float oldPositionX = knobPosition.x;
float oldPositionY = knobPosition.y;
float oldPercentX = knobPercent.x;
float oldPercentY = knobPercent.y;
float centerX = knobBounds.width/2;
float centerY = knobBounds.height/2;
knobPosition.set(centerX, centerY);
knobPercent.set(0f, 0f);
if (!isTouchUp) {
if (!deadzoneBounds.contains(x, y)) {
knobPercent.set((x - centerX) / (knobBounds.getWidth()/2), 0);
float length = knobPercent.len();
if (length > 1) knobPercent.scl(1 / length);
if (knobBounds.contains(x, y)) {
knobPosition.set(x, yPosition);
} else {
knobPosition.set(knobPercent).scl(knobBounds.getWidth()/2,0).add(knobBounds.width/2, knobBounds.height/2);
}
}
}
if (oldPercentX != knobPercent.x || oldPercentY != knobPercent.y) {
ChangeListener.ChangeEvent changeEvent = Pools.obtain(ChangeListener.ChangeEvent.class);
if (fire(changeEvent)) {
knobPercent.set(oldPercentX, oldPercentY);
knobPosition.set(oldPositionX, oldPositionY);
}
Pools.free(changeEvent);
}
}
The big change here was calculating the percent and changing the position when not within the bounds. It was using getX() for the circle which is the center, but for rectangle it is the bottom left. So had to change it to the center.

Capture Screen in libGdx

I want to capture screen in libGdx . I use this code for my problem. On Desktop it work . But when I run on my android , I can't find image capture screen. How can I fix it? Thanks for reading my question.
public class ScreenshotFactory {
private static int counter = 1;
public static void saveScreenshot() {
try {
FileHandle fh;
do {
if (Gdx.app.getType() == ApplicationType.Desktop)
Infor.linkScreenShot = "D://chupngoc" + counter + ".png";
else
Infor.linkScreenShot = Gdx.files.getExternalStoragePath()
+ counter + ".png";
Infor.nameImage = counter + ".png";
fh = new FileHandle(Infor.linkScreenShot);
counter++;
} while (fh.exists());
Pixmap pixmap = getScreenshot(0, 0, Gdx.graphics.getWidth(),
Gdx.graphics.getHeight(), true);
PixmapIO.writePNG(fh, pixmap);
pixmap.dispose();
} catch (Exception e) {
}
}
private static Pixmap getScreenshot(int x, int y, int w, int h,
boolean yDown) {
Gdx.gl.glPixelStorei(GL20.GL_PACK_ALIGNMENT, 1);
final Pixmap pixmap1 = new Pixmap(w, h, Format.RGBA8888);
ByteBuffer pixels1 = pixmap1.getPixels();
Gdx.gl.glReadPixels(x, y, w, h, GL20.GL_RGBA, GL20.GL_UNSIGNED_BYTE,
pixels1);
Pixmap pixmap = pixmap1;
if (yDown) {
// Flip the pixmap upside down
ByteBuffer pixels = pixmap.getPixels();
int numBytes = w * h * 4;
byte[] lines = new byte[numBytes];
int numBytesPerLine = w * 4;
for (int i = 0; i < h; i++) {
pixels.position((h - i - 1) * numBytesPerLine);
pixels.get(lines, i * numBytesPerLine, numBytesPerLine);
}
pixels.clear();
pixels.put(lines);
}
return pixmap;
}
}
Your problem lies here...
fh = new FileHandle(Infor.linkScreenShot);
This way of creating a FileHandle is for the desktop only and will not work on Android.
You should create a file using Gdx.files.external instead...
fh = Gdx.files.external(Infor.linkScreenShot);

Trying to create a circle with VBO's - LWJGL

Im trying to create a circle in LWJGL , using VBO's and VAO , and move it using an offset , but it seems one vertex is stuck in the center of the screen . I can't figure out how to move it to the new location . Any help is appreciated , thanks !
P.S : I have already tried debugging the program , but I can't locate the faulty vertex in my array
import java.nio.FloatBuffer;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.*;
public class Test {
// Setup variables
private int WIDTH = 800;
private int HEIGHT = 600;
private String title = "Circle";
// Quad variables
private int vbo = 0; // Vertex Buffer Object
private int vao = 0; // Vertex Array Object
int SUBDIVISIONS = 100;
float[] vertex = new float[(SUBDIVISIONS + 1) * 4];
public Test() {
// Initialize
setupOpenGL();
setupQuad();
while (!Display.isCloseRequested()) {
loop();
Display.update();
Display.sync(60);
}
Display.destroy();
}
public void setupOpenGL() {
try {
Display.setDisplayMode(new DisplayMode(WIDTH, HEIGHT));
Display.setTitle(title);
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
System.exit(-1); // If error , exit program
}
GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
}
public void setupQuad() {
float r = 0.2f;
float x;
float y;
float offSetX = 0.3f;
float offSetY = 0.3f;
vertex[0] = (float) Math.sin(Math.PI*2*0/SUBDIVISIONS) * r + offSetX;
vertex[1] = (float) Math.cos(Math.PI*2*1/SUBDIVISIONS) * r + offSetY;
for (int i = 2; i < 360; i = i + 2) {
double angle = Math.PI * 2 * i / SUBDIVISIONS;
x = (float) Math.cos(angle) * r;
vertex[i] = x + offSetX;
}
for (int i = 3; i < 360; i = i + 2) {
double angle = Math.PI * 2 * i / SUBDIVISIONS;
y = (float) Math.sin(angle) * r;
vertex[i] = y + offSetY;
}
FloatBuffer vertexBuffer = BufferUtils.createFloatBuffer(vertex.length);
vertexBuffer.put(vertex);
vertexBuffer.flip();
vao = GL30.glGenVertexArrays();
GL30.glBindVertexArray(vao);
vbo = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER,vertexBuffer,GL15.GL_STATIC_DRAW);
GL20.glVertexAttribPointer(0, 2, GL11.GL_FLOAT, false, 0, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
}
public void loop() {
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
GL30.glBindVertexArray(vao);
GL20.glEnableVertexAttribArray(0);
// Draw the vertices
GL11.glDrawArrays(GL11.GL_TRIANGLE_FAN, 0, vertex.length / 2);
// Put everything back to default (deselect)
GL20.glDisableVertexAttribArray(0);
GL30.glBindVertexArray(0);
}
public static void main(String[] args) {
new Test();
}
}
"I think I've found the problem . I was setting the positions of only 359 vertices out of 404 vertices (nr of subdivisions + 1 times 4) . It seems the rest of the vertices were stuck at 0,0 on the screen . Allowing both FOR statements to cycle up to 404 seems to solve the problem"