PyGame: Viewport click & Drag - grid

I'm trying to duplicate the behavior of some grid viewports (like 3ds Max's Orthometric view)
or map viewers like (GoogleMaps) where we have the map or grid, which is a whole lot bigger
than the screen, and we navigate by clicking somewhere in the viewport and dragging.
So far, i've managed to create a pretty big grid, draw it and make the viewport show only the tiles it should.
Here is my code so far:
import pygame, sys, math
from pygame.locals import *
FPS = 30
WINDOWWIDTH = 640
WINDOWHEIGHT = 480
GRIDWIDTH = 256
GRIDHEIGHT = 256
GRIDSIZE = 256
TILESIZE = 40
BGCOLOR = (128, 128, 128)
FGCOLOR = (64, 64, 64)
GRID = []
FPSCLOCK = pygame.time.Clock()
indexX = None
indexY = None
minVPcoordX = 0
minVPcoordY = 0
maxVPcoordX = (TILESIZE*GRIDSIZE)-WINDOWWIDTH
maxVPcoordY = (TILESIZE*GRIDSIZE)-WINDOWHEIGHT
viewportOffset = (0, 0)
vpStartXTile = 0
vpStartYTile = 0
viewportCoord = (0, 0)
coordX = 0
coordY = 0
movedDistanceX = 0
movedDistanceY = 0
speed = 4
def main():
global FPSCLOCK, DISPLAYSURF
global coordX, coordY
global offsetX, offsetY, negativeOffsetX, negativeOffsetY
global movedDistanceX, movedDistanceY
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
mouseX = 0
mouseY = 0
generateGrid(GRIDSIZE, GRIDSIZE)
LeftButton = False
mousePos = (0, 0)
dragStart = (0,0)
dragEnd = (0,0)
pygame.font.init()
arialFnt = pygame.font.SysFont('Arial', 16)
while True:
DISPLAYSURF.fill(BGCOLOR)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
#X
if coordX < maxVPcoordX:
coordX += speed
elif coordX < minVPcoordX:
coordX = 0
else:
coordX = maxVPcoordX
#Y
if coordY < maxVPcoordY:
coordY += speed
elif coordY < minVPcoordY:
coordY = 0
else:
coordY = maxVPcoordY
#-------------
viewportCoord = (coordX, coordY)
print(coordX, coordY)
vpStartXTile = math.floor(float(viewportCoord[0]/TILESIZE))
vpStartYTile = math.floor(float(viewportCoord[1]/TILESIZE))
GRIDstartTile = GRID[vpStartXTile][vpStartYTile]
negativeOffsetX = viewportCoord[0] - GRID[vpStartXTile][vpStartYTile][0]
negativeOffsetY = viewportCoord[1] - GRID[vpStartXTile][vpStartYTile][1]
offsetX = TILESIZE - negativeOffsetX
offsetY = TILESIZE - negativeOffsetY
repeatX = math.floor(WINDOWWIDTH/TILESIZE)
repeatY = math.floor(WINDOWHEIGHT/TILESIZE)
drawGrid(repeatX, repeatY)
outputLabel = arialFnt.render('(Top-Left)Coordinates: x%s - y%s' % (coordX, coordY), 1, (255,255,255))
DISPLAYSURF.blit(outputLabel, (10, 10))
# frame draw
pygame.display.set_caption("Memory Game - FPS: %.0f" % FPSCLOCK.get_fps())
pygame.display.flip()
pygame.display.update()
FPSCLOCK.tick(FPS)
def generateGrid(xTiles=None, yTiles=None):
global GRID
GRID = []
for i in range(xTiles):
GRID.append([None] * yTiles)
ix = 0
iy = 0
posX = -40
for x in range(len(GRID[ix])):
posX += TILESIZE
posY = -40
iy = 0
for y in range(xTiles):
posY += TILESIZE
position = (posX, posY)
GRID[ix][iy] = position
iy += 1
if ix < xTiles:
ix += 1
else:
return
def drawGrid(x=None, y=None):
lineWidth = 1
xPos = 0
yPos = 0
for i in range(x):
xStart = (xPos + offsetX, 0)
xEnd = (xPos + offsetX, WINDOWHEIGHT + negativeOffsetY)
pygame.draw.line(DISPLAYSURF, FGCOLOR, xStart, xEnd, lineWidth)
xPos += TILESIZE
for i in range(y):
yStart = (0, yPos + offsetY)
yEnd = (WINDOWWIDTH + negativeOffsetX, yPos + offsetY)
pygame.draw.line(DISPLAYSURF, FGCOLOR, yStart, yEnd, lineWidth)
yPos += TILESIZE
def moveGrid():
pass
def zoomIn():
pass
def zoomOut():
pass
main()
As you can see, it works as expected (i haven't implemented any form of click&drag
in this sample).
It seems that pygame doesn't have an event for this, so it must be a combination of
MOUSEBUTTONDOWN and MOUSEMOTION.
I've tried storing the positions of the previous frame with the get_pos() and subtracting the current frame's positions, but i can't figure out the next.
It accelerates way too fast..
I've also tried this with the get_rel() method of the mouse, with no success.
(Though i'm pretty sure i wasn't supposed to ++ the mouse position to the screen position)
I researched around to see if anyone else did this but i found only how to drag around something
on a fixed screen. I need the opposite - drag the screen around on a fixed grid.
So, if anyone has any idea or advice on how to make this mechanic, or any link that i could study about it, i would be grateful!
ps: I found something similar but it's written in JS and it's a pain to translate)

I got it working!
It still has some issues on the zoomIn/zoomOut additions but the main problem of dragging
the grid around is fixed.
import pygame, sys, math
from pygame.locals import *
FPS = 30
WINDOWWIDTH = 640
WINDOWHEIGHT = 480
GRIDSIZE = 256
TILESIZE = 40
BGCOLOR = (128, 128, 128)
FGCOLOR = (64, 64, 64)
GRID = []
FPSCLOCK = pygame.time.Clock()
indexX = None
indexY = None
minVPcoordX = 0
minVPcoordY = 0
maxVPcoordX = (TILESIZE*GRIDSIZE)-WINDOWWIDTH
maxVPcoordY = (TILESIZE*GRIDSIZE)-WINDOWHEIGHT
viewportOffset = (0, 0)
vpStartXTile = 0
vpStartYTile = 0
viewportCoord = (0, 0)
coordX = 0
coordY = 0
def main():
global FPSCLOCK, DISPLAYSURF
global coordX, coordY
global offsetX, offsetY, negativeOffsetX, negativeOffsetY
global movedDistanceX, movedDistanceY
global isDragging
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
mouseX = 0
mouseY = 0
generateGrid(GRIDSIZE, GRIDSIZE)
isDragging = False
mousePos = (0, 0)
dragStart = (0,0)
dragEnd = (0,0)
pygame.font.init()
arialFnt = pygame.font.SysFont('Arial', 16)
while True:
DISPLAYSURF.fill(BGCOLOR)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == MOUSEBUTTONDOWN:
if event.button == 2:
isDragging = True
elif event.button == 4:
zoomIn()
elif event.button == 5:
zoomOut()
elif event.type == MOUSEMOTION:
mouseRel = pygame.mouse.get_rel()
moveGrid(mouseRel)
elif event.type == MOUSEBUTTONUP:
isDragging = False
viewportCoord = (coordX, coordY)
vpStartXTile = math.floor(float(viewportCoord[0]/TILESIZE))
vpStartYTile = math.floor(float(viewportCoord[1]/TILESIZE))
GRIDstartTile = GRID[vpStartXTile][vpStartYTile]
negativeOffsetX = viewportCoord[0] - GRID[vpStartXTile][vpStartYTile][0]
negativeOffsetY = viewportCoord[1] - GRID[vpStartXTile][vpStartYTile][1]
offsetX = TILESIZE - negativeOffsetX
offsetY = TILESIZE - negativeOffsetY
repeatX = math.floor(WINDOWWIDTH/TILESIZE)
repeatY = math.floor(WINDOWHEIGHT/TILESIZE)
drawGrid(repeatX, repeatY)
outputLabel = arialFnt.render('(Top-Left)Coordinates: x%s - y%s' % (coordX, coordY), 1, (255,255,255))
DISPLAYSURF.blit(outputLabel, (10, 10))
# frame draw
pygame.display.set_caption("Memory Game - FPS: %.0f" % FPSCLOCK.get_fps())
pygame.display.flip()
pygame.display.update()
FPSCLOCK.tick(FPS)
def generateGrid(xTiles=None, yTiles=None):
global GRID
GRID = []
for i in range(xTiles):
GRID.append([None] * yTiles)
ix = 0
iy = 0
posX = -40
for x in range(len(GRID[ix])):
posX += TILESIZE
posY = -40
iy = 0
for y in range(xTiles):
posY += TILESIZE
position = (posX, posY)
GRID[ix][iy] = position
iy += 1
if ix < xTiles:
ix += 1
else:
return
def drawGrid(x=None, y=None):
lineWidth = 1
xPos = 0
yPos = 0
for i in range(x):
xStart = (xPos + offsetX, 0)
xEnd = (xPos + offsetX, WINDOWHEIGHT + negativeOffsetY)
pygame.draw.line(DISPLAYSURF, FGCOLOR, xStart, xEnd, lineWidth)
xPos += TILESIZE
for i in range(y):
yStart = (0, yPos + offsetY)
yEnd = (WINDOWWIDTH + negativeOffsetX, yPos + offsetY)
pygame.draw.line(DISPLAYSURF, FGCOLOR, yStart, yEnd, lineWidth)
yPos += TILESIZE
def moveGrid(rel):
global coordX, coordY, isDragging
if isDragging == True:
#X
if coordX <= maxVPcoordX and coordX >= minVPcoordX:
coordX = coordX - rel[0]
if coordX > maxVPcoordX:
coordX = maxVPcoordX
if coordX < minVPcoordX:
coordX = 0
#Y
if coordY <= maxVPcoordY and coordY >= minVPcoordY:
coordY = coordY - rel[1]
if coordY > maxVPcoordY:
coordY = maxVPcoordY
elif coordY < minVPcoordY:
coordY = 0
#-------------
def zoomIn():
global TILESIZE, maxVPcoordX, maxVPcoordY
TILESIZE += 4
print("Tile size: ", TILESIZE)
generateGrid(GRIDSIZE, GRIDSIZE)
maxVPcoordX = (TILESIZE*GRIDSIZE)-WINDOWWIDTH
maxVPcoordY = (TILESIZE*GRIDSIZE)-WINDOWHEIGHT
def zoomOut():
global TILESIZE, maxVPcoordX, maxVPcoordY
TILESIZE -= 4
if TILESIZE <= 0:
TILESIZE = 4
print("Tile size: ", TILESIZE)
generateGrid(GRIDSIZE, GRIDSIZE)
maxVPcoordX = (TILESIZE*GRIDSIZE)-WINDOWWIDTH
maxVPcoordY = (TILESIZE*GRIDSIZE)-WINDOWHEIGHT
main()

Related

Pygame : Pyscroll & Pytmx working on Tiled - Isometric map [duplicate]

I am facing some issues while trying to create a staggered isomteric game map with pygame.
So far I can draw a rectangular one but I have no clue on how to rotate it.
Here is the code:
from pygame.locals import *
import pygame
green = (40,255,30)
brown = (40,60,90)
grass = 0
dirt = 1
colours = {
grass: green,
dirt: brown,
}
tilemap = [
[grass,dirt,dirt,dirt, grass],
[dirt,grass,dirt,dirt, dirt],
[grass, grass,dirt,dirt, grass],
[grass, grass,dirt,dirt, grass],
[dirt,dirt,dirt,dirt,grass]
]
TILESIZE = 50
MAPWIDTH = 5
MAPHEIGHT = 5
pygame.init()
DISPLAYMAP = pygame.display.set_mode((MAPWIDTH*TILESIZE,MAPHEIGHT*TILESIZE))
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
DISPLAYMAP.fill((0, 0, 0))
for row in range(MAPWIDTH):
print
for column in range(MAPHEIGHT):
color = colours[tilemap[row][column]]
pygame.draw.rect(DISPLAYMAP, color, (column * TILESIZE, row * TILESIZE, TILESIZE, TILESIZE))
pygame.display.update()
Here is the result:
And I would like to have something looking like this:
Any help would be really appreciated.
Thank you
Use pygame.transform.rotate() and pygame.transform.scale() to create an isometric pygame.Surface object for each kind of tile:
isometric_tiles = {}
for key, color in colours.items():
tile_surf = pygame.Surface((TILESIZE, TILESIZE), pygame.SRCALPHA)
tile_surf.fill(color)
tile_surf = pygame.transform.rotate(tile_surf, 45)
isometric_size = tile_surf.get_width()
tile_surf = pygame.transform.scale(tile_surf, (isometric_size, isometric_size//2))
isometric_tiles[key] = tile_surf
blit the tiles instead of drawing rectangles:
for column in range(MAPWIDTH):
for row in range(MAPHEIGHT):
tile_surf = isometric_tiles[tilemap[row][column]]
x = (column + (MAPHEIGHT - row)) * isometric_size // 2
y = 20 + (column + row) * isometric_size // 4
DISPLAYMAP.blit(tile_surf, (x, y))
Complete example:
from pygame.locals import *
import pygame
green = (40,255,30)
brown = (40,60,90)
grass = 0
dirt = 1
colours = {
grass: green,
dirt: brown,
}
tilemap = [
[grass,dirt,dirt,dirt, grass],
[dirt,grass,dirt,dirt, dirt],
[grass, grass,dirt,dirt, grass],
[grass, grass,dirt,dirt, grass],
[dirt,dirt,dirt,dirt,grass]
]
TILESIZE = 50
MAPWIDTH = 5
MAPHEIGHT = 5
isometric_tiles = {}
for key, color in colours.items():
tile_surf = pygame.Surface((TILESIZE, TILESIZE), pygame.SRCALPHA)
tile_surf.fill(color)
tile_surf = pygame.transform.rotate(tile_surf, 45)
isometric_size = tile_surf.get_width()
tile_surf = pygame.transform.scale(tile_surf, (isometric_size, isometric_size//2))
isometric_tiles[key] = tile_surf
pygame.init()
DISPLAYMAP = pygame.display.set_mode((MAPWIDTH*TILESIZE*2,MAPHEIGHT*TILESIZE))
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
DISPLAYMAP.fill((0, 0, 0))
for column in range(MAPWIDTH):
for row in range(MAPHEIGHT):
tile_surf = isometric_tiles[tilemap[row][column]]
x = (column + (MAPHEIGHT - row)) * isometric_size // 2
y = 20 + (column + row) * isometric_size // 4
DISPLAYMAP.blit(tile_surf, (x, y))
pygame.display.update()
For the staggered representation you have to change the arrangement of the tiles:
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
DISPLAYMAP.fill((0, 0, 0))
for column in range(MAPWIDTH):
for row in range(MAPHEIGHT):
tile_surf = isometric_tiles[tilemap[row][column]]
x = 20 + column * isometric_size + (row % 2) * isometric_size // 2
y = 20 + row * isometric_size // 4
DISPLAYMAP.blit(tile_surf, (x, y))
pygame.display.update()

Angular 7 lava effect animation

I have an Angular 7 app with a home page containing a large coloured block (enough to fill the page) at the top with a header and some images. I want to put some lava effect animations into the background similar to this
code in case link is removed:
HTML:
<canvas id="lamp-anim" class="lamp-anim" width="1034" height="613"></canvas>
CSS:
body {
background: #f857a6; /* fallback for old browsers */
background: -webkit-linear-gradient(to top, #ff5858, #f857a6); /* Chrome
10-25, Safari 5.1-6 */
background: linear-gradient(to top, #ff5858, #f857a6); /* W3C, IE 10+/
Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
}
JS:
window.lavaAnimation = function() {
"use strict";
var t, i = {
screen: {
elem: null,
callback: null,
ctx: null,
width: 0,
height: 0,
left: 0,
top: 0,
init: function(t, i, s) {
return this.elem = document.getElementById(t), this.callback = i || null, "CANVAS" == this.elem.tagName && (this.ctx = this.elem.getContext("2d")), window.addEventListener("resize", function() {
this.resize()
}.bind(this), !1), this.elem.onselectstart = function() {
return !1
}, this.elem.ondrag = function() {
return !1
}, s && this.resize(), this
},
resize: function() {
var t = this.elem;
for (this.width = t.offsetWidth, this.height = t.offsetHeight, this.left = 0, this.top = 0; null != t; t = t.offsetParent) this.left += t.offsetLeft, this.top += t.offsetTop;
this.ctx && (this.elem.width = this.width, this.elem.height = this.height), this.callback && this.callback()
}
}
},
s = function(t, i) {
this.x = t, this.y = i, this.magnitude = t * t + i * i, this.computed = 0, this.force = 0
};
s.prototype.add = function(t) {
return new s(this.x + t.x, this.y + t.y)
};
var h = function(t) {
var i = .1,
h = 1.5;
this.vel = new s((Math.random() > .5 ? 1 : -1) * (.2 + .25 * Math.random()), (Math.random() > .5 ? 1 : -1) * (.2 + Math.random())), this.pos = new s(.2 * t.width + Math.random() * t.width * .6, .2 * t.height + Math.random() * t.height * .6), this.size = t.wh / 15 + (Math.random() * (h - i) + i) * (t.wh / 15), this.width = t.width, this.height = t.height
};
h.prototype.move = function() {
this.pos.x >= this.width - this.size ? (this.vel.x > 0 && (this.vel.x = -this.vel.x), this.pos.x = this.width - this.size) : this.pos.x <= this.size && (this.vel.x < 0 && (this.vel.x = -this.vel.x), this.pos.x = this.size), this.pos.y >= this.height - this.size ? (this.vel.y > 0 && (this.vel.y = -this.vel.y), this.pos.y = this.height - this.size) : this.pos.y <= this.size && (this.vel.y < 0 && (this.vel.y = -this.vel.y), this.pos.y = this.size), this.pos = this.pos.add(this.vel)
};
var e = function(t, i, e, n, a) {
this.step = 5, this.width = t, this.height = i, this.wh = Math.min(t, i), this.sx = Math.floor(this.width / this.step), this.sy = Math.floor(this.height / this.step), this.paint = !1, this.metaFill = r(t, i, t, n, a), this.plx = [0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0], this.ply = [0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1], this.mscases = [0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 0, 2, 1, 1, 0], this.ix = [1, 0, -1, 0, 0, 1, 0, -1, -1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1], this.grid = [], this.balls = [], this.iter = 0, this.sign = 1;
for (var o = 0; o < (this.sx + 2) * (this.sy + 2); o++) this.grid[o] = new s(o % (this.sx + 2) * this.step, Math.floor(o / (this.sx + 2)) * this.step);
for (var l = 0; e > l; l++) this.balls[l] = new h(this)
};
e.prototype.computeForce = function(t, i, s) {
var h, e = s || t + i * (this.sx + 2);
if (0 === t || 0 === i || t === this.sx || i === this.sy) h = .6 * this.sign;
else {
h = 0;
for (var r, n = this.grid[e], a = 0; r = this.balls[a++];) h += r.size * r.size / (-2 * n.x * r.pos.x - 2 * n.y * r.pos.y + r.pos.magnitude + n.magnitude);
h *= this.sign
}
return this.grid[e].force = h, h
}, e.prototype.marchingSquares = function(t) {
var i = t[0],
s = t[1],
h = t[2],
e = i + s * (this.sx + 2);
if (this.grid[e].computed === this.iter) return !1;
for (var r, n = 0, a = 0; 4 > a; a++) {
var l = i + this.ix[a + 12] + (s + this.ix[a + 16]) * (this.sx + 2),
d = this.grid[l].force;
(d > 0 && this.sign < 0 || 0 > d && this.sign > 0 || !d) && (d = this.computeForce(i + this.ix[a + 12], s + this.ix[a + 16], l)), Math.abs(d) > 1 && (n += Math.pow(2, a))
}
if (15 === n) return [i, s - 1, !1];
5 === n ? r = 2 === h ? 3 : 1 : 10 === n ? r = 3 === h ? 0 : 2 : (r = this.mscases[n], this.grid[e].computed = this.iter);
var p = this.step / (Math.abs(Math.abs(this.grid[i + this.plx[4 * r + 2] + (s + this.ply[4 * r + 2]) * (this.sx + 2)].force) - 1) / Math.abs(Math.abs(this.grid[i + this.plx[4 * r + 3] + (s + this.ply[4 * r + 3]) * (this.sx + 2)].force) - 1) + 1);
return o.lineTo(this.grid[i + this.plx[4 * r] + (s + this.ply[4 * r]) * (this.sx + 2)].x + this.ix[r] * p, this.grid[i + this.plx[4 * r + 1] + (s + this.ply[4 * r + 1]) * (this.sx + 2)].y + this.ix[r + 4] * p), this.paint = !0, [i + this.ix[r + 4], s + this.ix[r + 8], r]
}, e.prototype.renderMetaballs = function() {
for (var t, i = 0; t = this.balls[i++];) t.move();
for (this.iter++, this.sign = -this.sign, this.paint = !1, o.fillStyle = this.metaFill, o.beginPath(), i = 0; t = this.balls[i++];) {
var s = [Math.round(t.pos.x / this.step), Math.round(t.pos.y / this.step), !1];
do s = this.marchingSquares(s); while (s);
this.paint && (o.fill(), o.closePath(), o.beginPath(), this.paint = !1)
}
};
var r = function(t, i, s, h, e) {
var r = o.createRadialGradient(t / 1, i / 1, 0, t / 1, i / 1, s);
return r.addColorStop(0, h), r.addColorStop(1, e), r
};
if (document.getElementById("lamp-anim")) {
var n = function() {
requestAnimationFrame(n), o.clearRect(0, 0, a.width, a.height), t.renderMetaballs()
},
a = i.screen.init("lamp-anim", null, !0),
o = a.ctx;
a.resize(), t = new e(a.width, a.height, 6, "#3494E6", "#EC6EAD")
}
return {
run: n
}
}();
if (document.getElementById('lamp-anim')) {
lavaAnimation.run();
}
setTimeout(function() {
$('.js-works-d-list').addClass('is-loaded');
}, 150);
Is it possible to convert/do this in angular animations? Are they flexible enough to do this sort of (what id call advanced) animation?
I think the question of 'can I convert this to Angular' is a bit off because Angular runs on Typescript, which is a language built from javascript. So, yes you can do all this in Angular or rather using Typescript within an Angular app.
We're always here to help once you get some code written in an Angular app! But in general, we are here to help you were you get stuck in code and help you solve the problem. It's a bit more challenging to say 'yes it will work' without seeing how you implement it in your project and can't really guide or help you until we see how your angular components are written.
Short answer: Yeah, I think it can work. But it also depends how you implement this code into your Angular app.

How can I get rotated rectangle corner points from side points

Say I have a set of four or more points that are on the perimeter of a rectangle, and that the rectangle is rotated by some unknown amount. I know that at least one point is on each side of the rectangle. One arbitrary side point is designated (0, 0), and the other points are the distance from this starting point. How can I get the non-rotated corner points of this rectangle?
assuming you're not trying to find a unique solution:
rotate your points around 0,0 until the top-most, bottom-most,
left-most, and right-most points are all different points
draw horizontal lines through the top-most and bottom-most, and vertical lines through the left-most and right-most
you're done
var points = [];
var bs = document.body.style;
var ds = document.documentElement.style;
bs.height = bs.width = ds.height = ds.width = "100%";
bs.border = bs.margin = bs.padding = 0;
var c = document.createElement("canvas");
c.style.display = "block";
c.addEventListener("mousedown", addPoint, false);
document.body.appendChild(c);
var ctx = c.getContext("2d");
var interval;
function addPoint(e) {
if (points.length >= 4) points = [];
points.push({
x: e.x - c.offsetLeft,
y: e.y - c.offsetTop
});
while (points.length > 4) points.shift();
redraw();
}
function rotateAround(a, b, r) {
d = {x:a.x - b.x, y:a.y - b.y};
return {
x: b.x + Math.cos(r) * d.x - Math.sin(r) * d.y,
y: b.y + Math.cos(r) * d.y + Math.sin(r) * d.x
}
}
function drawPoint(p) {
ctx.strokeStyle = "rgb(0,0,0)";
ctx.beginPath();
ctx.arc(p.x, p.y, 10, 0, 2 * Math.PI, true);
ctx.closePath();
ctx.stroke();
}
var last_few = [];
function redraw() {
if (interval) clearInterval(interval);
last_few = [];
c.width = window.innerWidth;
c.height = window.innerHeight;
ctx.clearRect(0, 0, c.width, c.height);
ctx.fillStyle = "rgb(200, 200, 200)";
ctx.font = "40px serif";
if (points.length < 4) {
ctx.fillText("click " + (4 - points.length) + " times", 20, 40);
points.forEach(drawPoint);
} else {
var average = {x:0, y:0};
points.forEach(function (p) {
average.x += p.x / 4;
average.y += p.y / 4;
});
var step = 0;
interval = setInterval(function () {
ctx.clearRect(0, 0, c.width, c.height);
ctx.fillText("click anywhere to start over", 20, 40);
last_few.forEach(function(r) {
ctx.strokeStyle = "rgb(200,255,200)";
ctx.save();
ctx.translate(average.x, average.y);
ctx.rotate((step -r.step) * Math.PI / 180);
ctx.strokeRect(r.lm - average.x, r.tm - average.y, (r.rm - r.lm), (r.bm - r.tm));
ctx.restore();
});
var tm = Infinity;
var bm = -Infinity;
var lm = Infinity;
var rm = -Infinity;
points.forEach(function (p) {
p = rotateAround(p, average, step * Math.PI / 180);
drawPoint(p);
tm = Math.min(p.y, tm);
bm = Math.max(p.y, bm);
lm = Math.min(p.x, lm);
rm = Math.max(p.x, rm);
});
if (points.every(function (p) {
p = rotateAround(p, average, step * Math.PI / 180);
return (p.x == lm) || (p.x == rm) || (p.y == tm) || (p.y == bm);
})) {
ctx.strokeStyle = "rgb(0,255,0)";
ctx.strokeRect(lm, tm, (rm - lm), (bm - tm));
last_few.push({tm:tm, bm:bm, lm:lm, rm:rm, step:step});
while(last_few.length > 30) last_few.shift();
} else {
ctx.strokeStyle = "rgb(255,0,0)";
ctx.strokeRect(lm, tm, (rm - lm), (bm - tm));
}
step++;
}, 30);
}
}
window.onresize = redraw;
redraw();

Update canvas without delete the existing objects

I have a QCanvas with multiple objects on it. How could I update the canvas and put new object onto it without delete the existing ones?
I want the canvas to draw every existing objects, even if I draw a new one. A new object is generated on every mouse event.
class CANVAS(QtGui.QWidget):
def __init__(self , parent):
super(CANVAS , self).__init__(parent)
self.setGeometry( 0 , 30 , 530 , 530 )
self.frame = QtGui.QFrame(self)
self.CLICKED = 1
self.FUNCTION = 0
self.x =""
self.y =""
def paintEvent(self, e):
qp = QtGui.QPainter()
qp.begin(self)
self.drawPoints(qp)
qp.end()
def drawPoints(self, qp):
qp.setPen(QtCore.Qt.red)
t = points.point()
print self.x
if self.CLICKED == 1:
qp.drawRect(int(self.x), int(self.y), 10, 10)
self.CLICKED = 0
if self.FUNCTION == 1:
for k in range(0,360,1):
radius = 50
a = float(self.x) + radius * np.cos(k)
b = float(self.y) + radius * np.sin(k)
qp.drawPoint(a,b)
qp.drawPoint(int(self.x), int(self.y))
print "circle done"
self.FUNCTION = 0
elif self.FUNCTION == 2:
start_P = points.point(int(self.x), int(self.y))
a = 15
b = 20
upperL = points.point((int(self.x) + (10 * a)), int(self.y))
P = [start_P, upperL]
dummy1 = LinInt(P, qp)
leftL = points.point((int(self.x)), ((int(self.y))+(10*b)))
P = [start_P, leftL]
dummy2 = LinInt(P, qp)
tmp = dummy1.pop()
rightL = points.point((tmp.getX()),((int(self.y))+(10*b)))
P = [tmp, rightL]
LinInt(P, qp)
P = [leftL, rightL]
LinInt(P, qp)
self.FUNCTION = 0
print "rectangle done"
elif self.FUNCTION == 3:
print "curve"
def mousePressEvent(self, event):
self.x = ""
self.y = ""
coordinates = event.x()
for i in str(coordinates):
if i.isdigit():
self.x+=i
coordinates = event.y()
for i in str(coordinates):
if i.isdigit():
self.y+=i
self.CLICKED = 1
self.update()
In the constructor, create an empty list. In your drawPoints, do:
if self.CLICKED == 1:
rect = QRect(int(self.x), int(self.y), 10, 10)
self.rects.append(rect)
qp.drawRects(self.rects)

How can i convert x-y position to tile x-y for isometric tile?

I can draw my map according to the formula on Drawing Isometric game worlds . But how can i find the x-y of one tile according to it's position on map layer div?
For being clear, my tile's left style is 1036px and top style is 865px. According to those css properties how can find the tile's x and y position according to map?
Tile width is 200 and height is 100.
Many Thanks.
My javascript code is like that:
this.drawTiles = function(min_x, max_x, min_y, max_y) {
if (min_x < 0) min_x = 0;
if (min_y < 0) min_y = 0;
if (max_x < 0) max_x = thisObj.MAXSIZE;
if (max_y < 0) max_y = thisObj.MAXSIZE;
if (max_x > this.mapSize - 1) max_x = this.mapSize - 1;
if (max_y > this.mapSize - 1) max_y = this.mapSize - 1;
if (min_x < this.mapSize - 1) min_x = max_x - thisObj.MAXSIZE;
if (min_y < this.mapSize - 1) min_y = max_y - thisObj.MAXSIZE;
var appendLayer = thisObj.maplayer;
this.capMapDataObj.getTiles(min_x, max_x, min_y, max_y, function(JSONResp) {
var tiles = JSON.parse(JSONResp);
for (var x=min_x; x<max_x+1; x++) {
for (var y=min_y; y<max_y+1; y++) {
var tile = tiles[x][y];
var xpos = (y * thisObj.tile_width / 2) + (x * thisObj.tile_width / 2);
var ypos = (x * thisObj.tile_height / 2) - (y * thisObj.tile_height / 2);
var zin1 = 100 + parseInt(x) + parseInt(y);
var elem = '<div style="position:absolute;top:'+ypos+'px;left:'+xpos+'px;z-index:'+zin1+';width:200px;height:200px;background-image:url(images/'+tile[4]+');background-position:bottom;background-repeat:no-repeat;bottom:0px;text-align:center;" id="'+x+'_'+y+'"><div style="padding-top:140px;">'+x+' - '+y+'</div></div>\n';
if (document.getElementById(x+'_'+y) == undefined)
$(elem).appendTo(appendLayer);
}
}
});
}
So xpos and ypos variables are the css positions of each tile layer. When i click the Map Layer, I wanted to calculate the tile's isometric x-y coordinates.
I've solved the problem.
tileX = (yPos / tile_height) + (xPos / tile_width);
tileY = (xPos / tile_width) - (yPos / tile_height);

Resources