Skip to content

Commit

Permalink
Merge pull request #1915 from midichef/graph_y
Browse files Browse the repository at this point in the history
[graph] fix top margin location and simplify y-coordinate calculation
  • Loading branch information
anjakefala committed Jun 20, 2023
2 parents c1fef18 + abc9aff commit b401b34
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 36 deletions.
63 changes: 41 additions & 22 deletions visidata/canvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,13 +388,10 @@ def statusLine(self):

@property
def canvasMouse(self):
return self.canvasFromPlotterCoord(self.plotterMouse.x, self.plotterMouse.y)

def canvasFromPlotterCoord(self, plotter_x, plotter_y):
return Point(self.visibleBox.xmin + (plotter_x-self.plotviewBox.xmin)/self.xScaler, self.visibleBox.ymin + (plotter_y-self.plotviewBox.ymin)/self.yScaler)

def canvasFromTerminalCoord(self, x, y):
return self.canvasFromPlotterCoord(*self.plotterFromTerminalCoord(x, y))
x = self.plotterMouse.x
y = self.plotterMouse.y
p = Point(self.unscaleX(x), self.unscaleY(y))
return p

def setCursorSize(self, p):
'sets width based on diagonal corner p'
Expand Down Expand Up @@ -446,6 +443,9 @@ def plotterCursorBox(self):
self.scaleX(self.cursorBox.xmax),
self.scaleY(self.cursorBox.ymax))

def startCursor(self):
self.cursorBox = Box(*self.canvasMouse.xy)

def point(self, x, y, attr=0, row=None):
self.polylines.append(([(x, y)], attr, row))

Expand Down Expand Up @@ -606,14 +606,21 @@ def calcVisibleBoxHeight(self):
else:
return h

#could be called canvas_to_plotterX()
def scaleX(self, x):
'returns plotter x coordinate'
return round(self.plotviewBox.xmin+(x-self.visibleBox.xmin)*self.xScaler)
def scaleX(self, canvasX):
'returns a plotter x coordinate'
return round(self.plotviewBox.xmin+(canvasX-self.visibleBox.xmin)*self.xScaler)

def scaleY(self, canvasY):
'returns a plotter y coordinate'
return round(self.plotviewBox.ymin+(canvasY-self.visibleBox.ymin)*self.yScaler)

def scaleY(self, y):
'returns plotter y coordinate'
return round(self.plotviewBox.ymin+(y-self.visibleBox.ymin)*self.yScaler)
def unscaleX(self, plotterX):
'performs the inverse of scaleX, returns a canvas x coordinate'
return (plotterX-self.plotviewBox.xmin)/self.xScaler + self.visibleBox.xmin

def unscaleY(self, plotterY):
'performs the inverse of scaleY, returns a canvas y coordinate'
return (plotterY-self.plotviewBox.ymin)/self.yScaler + self.visibleBox.ymin

def canvasW(self, plotter_width):
'plotter X units to canvas units'
Expand All @@ -637,25 +644,33 @@ def render(self, h, w):

@asyncthread
def render_async(self):
self.render_sync()
self.plot_elements()

def render_sync(self):
'plots points and lines and text onto the Plotter'
def plot_elements(self, invert_y=False):
'plots points and lines and text onto the plotter'

self.resetBounds()

bb = self.visibleBox
xmin, ymin, xmax, ymax = bb.xmin, bb.ymin, bb.xmax, bb.ymax
xfactor, yfactor = self.xScaler, self.yScaler
plotxmin, plotymin = self.plotviewBox.xmin, self.plotviewBox.ymin
plotxmin = self.plotviewBox.xmin
if invert_y:
plotymax = self.plotviewBox.ymax
else:
plotymin = self.plotviewBox.ymin

for vertexes, attr, row in Progress(self.polylines, 'rendering'):
if len(vertexes) == 1: # single point
x1, y1 = vertexes[0]
x1, y1 = float(x1), float(y1)
if xmin <= x1 <= xmax and ymin <= y1 <= ymax:
# equivalent to self.scaleX(x1) and self.scaleY(y1), inlined for speed
x = plotxmin+(x1-xmin)*xfactor
y = plotymin+(y1-ymin)*yfactor
if invert_y:
y = plotymax-(y1-ymin)*yfactor
else:
y = plotymin+(y1-ymin)*yfactor
self.plotpixel(round(x), round(y), attr, row)
continue

Expand All @@ -665,9 +680,13 @@ def render_sync(self):
if r:
x1, y1, x2, y2 = r
x1 = plotxmin+float(x1-xmin)*xfactor
y1 = plotymin+float(y1-ymin)*yfactor
x2 = plotxmin+float(x2-xmin)*xfactor
y2 = plotymin+float(y2-ymin)*yfactor
if invert_y:
y1 = plotymax-float(y1-ymin)*yfactor
y2 = plotymax-float(y2-ymin)*yfactor
else:
y1 = plotymin+float(y1-ymin)*yfactor
y2 = plotymin+float(y2-ymin)*yfactor
self.plotline(x1, y1, x2, y2, attr, row)
prev_x, prev_y = x, y

Expand Down Expand Up @@ -718,7 +737,7 @@ def deleteSourceRows(self, rows):
Canvas.addCommand('z_', 'set-aspect', 'sheet.aspectRatio = float(input("aspect ratio=", value=aspectRatio)); refresh()', 'set aspect ratio')

# set cursor box with left click
Canvas.addCommand('BUTTON1_PRESSED', 'start-cursor', 'sheet.cursorBox = Box(*canvasMouse.xy)', 'start cursor box with left mouse button press')
Canvas.addCommand('BUTTON1_PRESSED', 'start-cursor', 'startCursor()', 'start cursor box with left mouse button press')
Canvas.addCommand('BUTTON1_RELEASED', 'end-cursor', 'setCursorSize(canvasMouse)', 'end cursor box with left mouse button release')

Canvas.addCommand('BUTTON3_PRESSED', 'start-move', 'sheet.anchorPoint = canvasMouse', 'mark grid point to move')
Expand Down
35 changes: 21 additions & 14 deletions visidata/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,44 @@ def numericCols(vd, cols):


class InvertedCanvas(Canvas):
@asyncthread
def render_async(self):
self.plot_elements(invert_y=True)

def zoomTo(self, bbox):
super().zoomTo(bbox)
self.fixPoint(Point(self.plotviewBox.xmin, self.plotviewBox.ymax), bbox.xymin)

def plotpixel(self, x, y, attr, row=None):
y = self.plotviewBox.ymax-y
self.pixels[y][x][attr].append(row)
self.fixPoint(Point(self.plotviewBox.xmin, self.plotviewBox.ymax),
Point(bbox.xmin, bbox.ymax + 1/4*self.canvasCharHeight))

def scaleY(self, canvasY):
'returns plotter y coordinate, with y-axis inverted'
plotterY = super().scaleY(canvasY)
return (self.plotviewBox.ymax-plotterY+4)
'returns a plotter y coordinate for a canvas y coordinate, with the y direction inverted'
return self.plotviewBox.ymax-round((canvasY-self.visibleBox.ymin)*self.yScaler)

def canvasH(self, plotterY):
return (self.plotviewBox.ymax-plotterY)/self.yScaler
def unscaleY(self, plotterY_inverted):
'performs the inverse of scaleY, returns a canvas y coordinate'
return (self.plotviewBox.ymax-plotterY_inverted)/self.yScaler + self.visibleBox.ymin

@property
def canvasMouse(self):
p = super().canvasMouse
p.y = self.visibleBox.ymin + (self.plotviewBox.ymax-self.plotterMouse.y)/self.yScaler
p.y = self.unscaleY(self.plotterMouse.y)
return p

def calcTopCursorY(self):
'ymin for the cursor that will align its top with the top edge of the graph'
return self.visibleBox.ymax + self.canvasCharHeight - self.cursorBox.h
return self.visibleBox.ymax - self.cursorBox.h

def calcBottomCursorY(self):
'ymin for the cursor that will align its bottom with the bottom edge of the graph'
# Shift by 1 plotter pixel, like with goTopCursorY for Canvas. But shift in the
# opposite direction, because the y-coordinate system is inverted.
return self.visibleBox.ymin + self.canvasCharHeight - (1/4 * self.canvasCharHeight)
'ymin for the cursor that will align its bottom with the bottom edge of the graph'
return self.visibleBox.ymin - (1/4 * self.canvasCharHeight)

def startCursor(self):
super().startCursor()
# Since the y coordinates for plotting increase in the opposite
# direction from Canvas, the cursor has to be shifted.
self.cursorBox.ymin -= self.canvasCharHeight

# provides axis labels, legend
class GraphSheet(InvertedCanvas):
Expand Down

0 comments on commit b401b34

Please sign in to comment.