Is line drawing working correct?

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
Muris
Party member
Posts: 131
Joined: Fri May 23, 2014 9:18 am

Is line drawing working correct?

Post by Muris »

I am using love version: 0.91

I was in middle of trying to do paint like program for pixel drawing. When I tried to add grid drawing on top of my picture using rough line draw the picture came as following:

Image

But when I set line width to 2 in the createGrid() -function, the grid is being drawn normally.

Image

edit: Also if I draw the lines as "smooth", the lines will be shown correctly even with line width of 1

Here is the code:

Code: Select all

io.stdout:setvbuf("no")

local MIXSCALE = 1
local CANVAS_X, CANVAS_Y = 64, 64

local _lg = love.graphics

local _width = CANVAS_X -- _lg.getWidth()
local _height = CANVAS_Y -- _lg.getHeight()

local _gridSize = 1

local _paintCanvas = null 
local _gridCanvas = null

local _gridColor = { 255,255,255, 64 }
local _color = { 0, 0, 255, 255 }
local _whiteColor = {255,255,255,255}

_lg.setNewFont(60)

local _paintObjects = {}

local _undos = {}
local _undoIndex = 0
local _undoCount = 0

local _panning = false
local _lastPanX, _lastPanY = 0, 0

local _lastMouseX, _lastMouseY = 0,0
local _leftDown = false
local _ctrlDown = false

local _scaler = 8
local _zoomValue = _scaler - 1
local _scalePointX = math.floor( ( _lg.getWidth()  - CANVAS_X * _scaler ) / 2 )
local _scalePointY = math.floor( ( _lg.getHeight() - CANVAS_Y * _scaler ) / 2 )

local function createGrid()
	if( _gridSize <= 0 ) then return end

	_gridCanvas:clear()
	_lg.setCanvas( _gridCanvas )
	_lg.setColor( _gridColor )
	_lg.setLineStyle('rough')
	_lg.setLineWidth(2)
	
	
	local startX = _scalePointX
	local startY = _scalePointY
	
	local endX, endY = math.floor( startX + CANVAS_X * _scaler ), math.floor( startY + CANVAS_Y * _scaler )
	local tileSize = math.floor( _gridSize * _scaler )
	
	local i, j = math.floor( startX ), math.floor( startY )
	
	if( startX < 0 ) 			then 
		startX = 0
		i = _scalePointX % tileSize
	elseif( startY < 0 ) 	then 
		startY = 0 
		j = _scalePointY % tileSize
	end
	
	if( endX > _lg.getWidth()  ) 			then endX = _lg.getWidth()
	elseif( endY > _lg.getHeight() ) 	then endY = _lg.getHeight() end 
	
	for i = i, endX, tileSize do
		_lg.line(  i, startY, i, endY )			
	end
	for j = j, endY, tileSize do
		_lg.line( startX, j, endX, j )
	end
	
	
	
	_lg.setLineStyle('rough')
	_lg.setCanvas()
	_lg.setLineWidth(1)
end

local function drawOnCanvas(x, y)
	_lg.setCanvas(_paintCanvas)

	_lg.setColor( _color )
	
	--_lg.rectangle('fill', x, y, 10, 10)	
	if( x == _lastMouseX and y == _lastMouseY ) then _lg.point( x, y ) 
 	else _lg.line(x, y, _lastMouseX, _lastMouseY)	end
  _lg.setCanvas()
	_undoCount = 0
end

local function drawFromCanvasToCanvas( source, dest )
	dest:clear()
	dest:renderTo( function()	
		dest:setFilter("nearest", "nearest")
	  _lg.setColor(255,255,255)
    _lg.draw(source, 0, 0)
		end
	)

end

local function createUndo()
	_undoIndex = _undoIndex + 1
	local newCanvas = _undos[ _undoIndex ]
	
	
	if( not newCanvas ) then
		newCanvas = _lg.newCanvas( _width, _height ) 
		_undos[_undoIndex] = newCanvas
	end
	
	drawFromCanvasToCanvas(  _paintCanvas, newCanvas )

end



local function doUndo()
	if( _undoIndex < 1 ) then return end
	

	_undoIndex = _undoIndex - 1
	_undoCount = _undoCount + 1
	
	if( _undoIndex == 0 ) then 
		_paintCanvas:clear()
		-- just to make the canvas actually clear, flushes the gpu
		_paintCanvas:getPixel(0,0)
		return
	end

	
	drawFromCanvasToCanvas( _undos[ _undoIndex ], _paintCanvas )
end

local function doRedo()
	if( _undoIndex >= #_undos or _undoCount <= 0 ) then return end
	_undoCount = _undoCount - 1
	_undoIndex = _undoIndex + 1
	drawFromCanvasToCanvas( _undos[ _undoIndex ], _paintCanvas )

end


local function 	zoom( zoomValue )
	_zoomValue = _zoomValue + zoomValue
	local lastScale = _scaler
	
	if( _zoomValue < 0 ) then _scaler = -1.0 / _zoomValue
	else _scaler =  _zoomValue + 1 end
	--if( _scaler < MIXSCALE ) then _scaler = MIXSCALE end
	
	local x, y, scaleValue = love.mouse.getX(), love.mouse.getY(), _scaler / lastScale
	
	
	_scalePointX = math.floor( x - ( x - _scalePointX ) * scaleValue )
	_scalePointY = math.floor( y - ( y - _scalePointY ) * scaleValue )
	createGrid()
end

local function getScaledPosition()
	return ( love.mouse.getX() - _scalePointX ) / _scaler,
				 ( love.mouse.getY() - _scalePointY ) / _scaler
end


function love.load()
	--if arg[#arg] == "-debug" then require("mobdebug").start() end 
	_lg.setLineStyle('rough')
	_lg.setPointStyle('rough')
	_paintCanvas = _lg.newCanvas( _width, _height ) 
	_paintCanvas:setFilter("nearest", "nearest")
	_paintCanvas:clear()
	
	_gridCanvas = _lg.newCanvas( _lg.getWidth(), _lg.getHeight() ) 
	_gridCanvas:clear()
	_gridCanvas:setFilter("nearest", "nearest")
	createGrid()
	
end


function love.update(dt)
	if _leftDown then
		local x, y = getScaledPosition()
		drawOnCanvas( x, y )
		_lastMouseX, _lastMouseY = x, y
	elseif _panning then
		local x, y = love.mouse.getPosition()
		_scalePointX, _scalePointY = _scalePointX + ( x - _lastPanX ), _scalePointY + ( y - _lastPanY )
		_lastPanX, _lastPanY = x, y
		createGrid()
	end
end


function love.draw()
	_lg.setColor( _whiteColor )
  _lg.draw( _paintCanvas, _scalePointX, _scalePointY ,0, _scaler, _scaler )
	_lg.draw(_gridCanvas, 0, 0)
end

function love.resize(w, h)
	_gridCanvas = _lg.newCanvas( _lg.getWidth(), _lg.getHeight() ) 
	_gridCanvas:setFilter("nearest", "nearest")
	_gridCanvas:clear()
	createGrid()
	
end


function love.mousepressed(x, y, button)

	if button == 'l' then
		_leftDown = true
		_lastMouseX, _lastMouseY = getScaledPosition()
	elseif button == "m" then
		_lastPanX, _lastPanY = love.mouse.getPosition()
		_panning = true
	elseif button == "wu" then
		zoom(1)
	elseif button == "wd" then
		zoom(-1)
	end
	
end

function love.mousereleased( x, y, button )
	if button == 'l' then
		createUndo()
		_leftDown = false
	elseif button == "m" then
		_panning = false
	end
end

function love.keypressed(key)
	if key == "escape" then
		love.event.quit() 
	elseif key == "z" and love.keyboard.isDown("lctrl","rctrl") then
		doUndo()
	elseif key == "y" and love.keyboard.isDown("lctrl","rctrl") then
		doRedo()
	elseif key == "1" then
		zoom( 1 )
	elseif key == "2" then		
		zoom( -1 )
		
	elseif key == "q" then
		_gridSize = _gridSize + 1
		createGrid()
	elseif key == "e" then
		_gridSize = _gridSize - 1
		if( _gridSize < 1) then _gridSize = 1 end
		createGrid()
		
	elseif key == "a" then
		_paintCanvas:clear(0,0,0)
		-- just to make the canvas actually clear, flushes the gpu
		_paintCanvas:getPixel(0,0)
	end
	
end

Also another point I noticed is, that calling canvas:clear() isn't actually enough to clear the canvas, but you also need to do something to the canvas before it actually clears it. Since I am using a canvas as a buffer to copy it to the screen, until I started drawing or calling getPixel() the canvas never got cleared.
Last edited by Muris on Mon Dec 15, 2014 10:00 am, edited 1 time in total.
User avatar
Azhukar
Party member
Posts: 478
Joined: Fri Oct 26, 2012 11:54 am

Re: Is line drawing working correct?

Post by Azhukar »

Muris wrote:the picture came as following
That happens when you draw on non-integer coordinates and is a result of your scaling.
Muris
Party member
Posts: 131
Joined: Fri May 23, 2014 9:18 am

Re: Is line drawing working correct?

Post by Muris »

Azhukar wrote:
Muris wrote:the picture came as following
That happens when you draw on non-integer coordinates and is a result of your scaling.
The gridcanvas is same size as the screen. I am drawing it on 0,0 without any scaling. On the line drawing i am using math.floor to set the line values as integer. A side note on android this actually does work correctly with 'rough' and linewidth = 1. Maybe i am missing a float value somewhere. Also the vertical lines do seem to work fine, its just the horizontal for some reason.

edit: You are probably right, because if I set the color to full white, its not complete white, so there is some rounding at somewhere of the code
User avatar
bartbes
Sex machine
Posts: 4946
Joined: Fri Aug 29, 2008 10:35 am
Location: The Netherlands
Contact:

Re: Is line drawing working correct?

Post by bartbes »

Sadly, OpenGL's definition of coordinates is a bit weird when it comes to lines and points. For the best results you generally want (rough) lines with a width of 1 to be drawn midway between pixel coordinates. So instead of drawing at (2, 3), try drawing at (2.5, 3.5).
Muris
Party member
Posts: 131
Joined: Fri May 23, 2014 9:18 am

Re: Is line drawing working correct?

Post by Muris »

bartbes wrote:Sadly, OpenGL's definition of coordinates is a bit weird when it comes to lines and points. For the best results you generally want (rough) lines with a width of 1 to be drawn midway between pixel coordinates. So instead of drawing at (2, 3), try drawing at (2.5, 3.5).
Thank you, this actually seems to fix the issue. Not that I wouldn't be able to use smooth lines or lines with width of 2, but this info might come in handy if/when I have to do some drawing algorithms.
Post Reply

Who is online

Users browsing this forum: Bing [Bot] and 7 guests