我的网格对齐代码有什么问题?
首先,我觉得让东西对齐到网格上应该是比较简单的,但在这种情况下我遇到了一些奇怪的问题,我的数学水平也不够,搞不清楚具体哪里出错了。
这是我的情况:
我有一个抽象的网格,Y轴的每一步之间的距离是Y_STEP(X轴的部分没问题,暂时忽略)。
这个网格是在一个抽象的坐标空间里,为了让东西对齐,我在里面加了一个神奇的偏移量,我们叫它Y_OFFSET。
为了让东西对齐到网格上,我写了以下代码(用的是Python):
def snapToGrid(originalPos, offset, step):
index = int((originalPos - offset) / step) #truncates the remainder away
return index * gap + offset
我把光标的位置、Y_OFFSET和Y_STEP传入这个函数,它会返回网格上离我最近的Y坐标。
在原来的场景下,这看起来没问题,但当我考虑到视图是可以滚动的时候,事情就变得有点奇怪了。
我把滚动做得尽量简单,创建了一个视口(viewPort),它会记录在Y轴上滚动的距离,然后把所有经过它的东西都进行偏移。
这是光标鼠标移动代码的一部分:
def mouseMotion(self, event):
pixelPos = event.pos[Y]
odePos = Scroll.pixelPosToOdePos(pixelPos)
self.tool.positionChanged(odePos)
这里有两个要关注的地方,首先是滚动模块把像素位置转换成抽象坐标空间的过程,然后是工具的positionChanged函数,它会把抽象坐标空间的值对齐到最近的Y步。
这是相关的滚动代码:
def pixelPosToOdePos(pixelPos):
offsetPixelPos = pixelPos - self.viewPortOffset
return pixelsToOde(offsetPixelPos)
def pixelsToOde(pixels):
return float(pixels) / float(pixels_in_an_ode_unit)
还有工具的更新代码:
def positionChanged(self, newPos):
self.snappedPos = snapToGrid(originalPos, Y_OFFSET, Y_STEP)
最后一个相关的部分是当工具要渲染自己时。它会通过滚动对象,把工具对齐后的坐标空间位置转换成屏幕上的像素位置,代码如下:
#in Tool
def render(self, screen):
Scroll.render(screen, self.image, self.snappedPos)
#in Scroll
def render(self, screen, image, odePos):
pixelPos = self.odePosToPixelPos(odePos)
screen.blit(image, pixelPos) # screen is a surface from pygame for the curious
def odePosToPixelPos(self.odePos):
offsetPos = odePos + self.viewPortOffset
return odeToPixels(offsetPos)
def odeToPixels(odeUnits):
return int(odeUnits * pixels_in_an_ode_unit)
呼,刚才的解释有点长。希望你还在跟着我……
我现在遇到的问题是,当我向上滚动时,绘制的图像和光标失去了对齐。
它开始对齐到光标下方正好1步的Y步。
而且它似乎在对齐和不对齐之间不断变化。
在某些滚动情况下,它偏离了1步,而在其他情况下又正好对齐。
它从来不会偏离超过1步,并且总是对齐到一个有效的网格位置。
我最好的猜测是,我在某个地方错误地截断了一些数据,但我不知道具体在哪里,或者为什么会出现这种情况。
有没有人对坐标空间、滚动和对齐有了解的?
3 个回答
谢谢你的回答,可能有个打字错误,但我看不出来...
不幸的是,改动snapToGrid并没有什么效果,所以我觉得这不是问题所在。
它不是偏差一个像素,而是偏差了Y_STEP。经过进一步的尝试,我发现当屏幕向上滚动时,我无法让它在任何点上都准确,而且这个问题发生在屏幕的顶部,我怀疑这可能是ODE的零位置,所以我猜我的问题可能与小值或负值有关。
你在positionChanged()里是不是写错了什么?
def positionChanged(self, newPos):
self.snappedPos = snapToGrid(newPos, Y_OFFSET, Y_STEP)
我觉得你可能因为浮点数除法的精度问题,导致偏差了一像素。试试把你的snapToGrid()改成这样:
def snapToGrid(originalPos, offset, step):
EPS = 1e-6
index = int((originalPos - offset) / step + EPS) #truncates the remainder away
return index * gap + offset
好的,我在这里回答我自己的问题,正如alexk提到的,使用int来截断是我的错误。
我想要的行为最好用math.floor()来表示。
抱歉,最初的问题没有提供足够的信息来真正解决问题。那时候我没有额外的信息。
关于拼写错误的说明,我觉得我可能在使用上下文时让人困惑……从positionChanged()函数的角度来看,参数是一个新的位置。
而从snapToGrid()函数的角度来看,参数是一个要被改变为对齐位置的原始位置。之所以这样说,是因为部分代码在我的事件处理代码中,另一部分在我的通用服务代码中。我应该为这个例子做些修改。